feat(NanoDSLAgent): Add validation/retry mechanism and UI rendering integration#500
feat(NanoDSLAgent): Add validation/retry mechanism and UI rendering integration#500
Conversation
Changed the run task in IDE configuration to :mpp-idea:runIde.
Replaces Jewel's CircularProgressIndicator with a custom implementation using Swing Timer to prevent ClassLoader issues with IntelliJ's bundled Compose runtime.
…ntegration Implements GitHub Issue #499 ## Summary - Add NanoDSL validation with retry mechanism (up to 3 attempts) - Integrate NanoDSL rendering into CodingAgentRenderer ## Part 1: Validation & Retry Mechanism - Create NanoDSLValidator expect/actual classes for cross-platform validation - JVM implementation uses xuiper-ui's NanoParser via reflection when available - Non-JVM platforms (JS, WASM, iOS, Android) use basic syntax validation - NanoDSLAgent now validates generated code and retries with error feedback ## Part 2: Rendering Integration - Add renderNanoDSL() to CodingAgentRenderer interface - Add NanoDSLItem to TimelineItem sealed class - Implement rendering in all platforms: - TypeScript: CliRenderer, TuiRenderer, ServerRenderer with syntax highlighting - Compose: ComposeRenderer with NanoDSLTimelineItem component - IntelliJ: JewelRenderer with IdeaNanoDSLBubble component - VSCode: VSCodeRenderer with nanodsl message type ## Files Changed ### mpp-core - src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (new) - src/*/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.*.kt (platform actuals) - src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt - src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt - src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt - src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt ### mpp-ui - src/commonMain/kotlin/.../compose/agent/ComposeRenderer.kt - src/commonMain/kotlin/.../compose/agent/AgentMessageList.kt - src/jsMain/typescript/agents/render/*.ts ### mpp-idea - src/main/kotlin/.../renderer/JewelRenderer.kt - src/main/kotlin/.../components/timeline/IdeaNanoDSLBubble.kt (new) - src/main/kotlin/.../components/timeline/IdeaTimelineContent.kt - src/main/kotlin/.../toolwindow/knowledge/IdeaKnowledgeContent.kt ### mpp-vscode - src/bridge/mpp-core.ts Closes #499
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a cross-platform NanoDSL validation and parsing API with platform-specific actuals, integrates a retry-enabled generation loop into NanoDSLAgent, and wires NanoDSL rendering across Compose, IntelliJ, TypeScript, CLI/TUI, and VS Code frontends with a new timeline item model and UI components. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (18)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt (1)
182-227: IdeaCircularProgressIndicator implementation is lifecycle-safe and matches the CL-avoidance goalUsing
DisposableEffect(Unit)to manage a SwingTimerand driving a simpleCanvasarc viarotationstate is a reasonable, coroutine-free way to implement a spinner here. Defaulting the color toJewelTheme.globalColors.text.infokeeps it within the design system, and thesize/strokeWidthparameters give enough flexibility for the call sites you’ve added below. No functional issues from this block.If you later see performance/log spam in large UIs, you could consider a slightly lower tick rate (e.g., 30–40 fps) or a smaller sweep angle, but it’s not required now.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (1)
28-31: MCP config dialog spinners correctly switched to IdeaCircularProgressIndicatorEach loading path now uses
IdeaCircularProgressIndicatorwith appropriate sizing (16.dpin the tools list row,14.dpin compact headers/buttons), and the visibility conditions are unchanged (isLoading,serverState?.isLoading,isReloading). This keeps the UX identical while aligning with the new, safer indicator implementation.You could slightly simplify a few call sites by omitting explicit
modifier = Modifierwhen you don’t chain any modifiers, but that’s purely cosmetic.Also applies to: 639-647, 712-715, 858-871, 974-990
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/knowledge/IdeaKnowledgeContent.kt (1)
801-845: NanoDSL bubble works but ignores validation metadata that’s already availableThe NanoDSL branch renders source and basic header correctly, but it drops
isValid/warningsfromTimelineItem.NanoDSLItem. Consider optionally surfacing validity (e.g., ✓/⚠) and warnings here to align withNanoDSLTimelineItem/IdeaNanoDSLBubble behavior and make NanoDSL issues visible in this view as well.mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.kt (1)
229-238: Clean NanoDSL wiring, but composable has unused preview state and mismatched KDoc
RenderMessageItemcorrectly forwards NanoDSL fields, butNanoDSLTimelineItemcurrently has:
- An unused
showPreviewstate and anirJsonparameter that is never read.- A KDoc that promises an “optional preview toggle” which doesn’t exist yet.
- A small micro-inefficiency from calling
source.lines()again in the footer.Either (a) implement the preview toggle using
irJson/showPreview, or (b) drop the unused state, unused param, and adjust the KDoc until preview is actually wired. You can also reuse the existinglineslist for the footer count to avoid recomputing.Example minimal clean‑up:
-/** - * NanoDSL Timeline Item - displays generated NanoDSL code with optional preview toggle - */ +/** + * NanoDSL Timeline Item - displays generated NanoDSL code + */ @Composable fun NanoDSLTimelineItem( source: String, - irJson: String?, + irJson: String?, // keep only if a preview will be added soon @@ - modifier: Modifier = Modifier -) { - var showPreview by remember { mutableStateOf(false) } - + modifier: Modifier = Modifier +) { @@ - val lines = source.lines() - val maxLineNumWidth = lines.size.toString().length + val lines = source.lines() + val maxLineNumWidth = lines.size.toString().length @@ - Text( - text = "${source.lines().size} lines of NanoDSL code", + Text( + text = "${lines.size} lines of NanoDSL code", fontSize = 10.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f) )Also applies to: 380-501
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt (1)
539-566: IntelliJ NanoDSL timeline integration is consistent with the shared modelThe override correctly maps
source,irJson,componentName,attempts,isValid, andwarningsintoTimelineItem.NanoDSLItem, so Jewel‑based UIs will see NanoDSL entries like other platforms. If NanoDSL component names can include non‑\wcharacters (e.g. dashes), you might later want a slightly broader regex for extraction, but the current approach is fine as a first pass.mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.ts (1)
16-18: TUI NanoDSL rendering is wired correctly and follows existing message patternsThe new
renderNanoDSLcorrectly consumesJsNanoDSLData, surfaces name/attempts/validity/warnings, and wraps the source in a fencednanodslblock. For very large NanoDSL outputs you might consider reusingtruncateText(similar to other tool outputs) to avoid flooding the TUI, but the current behavior is acceptable.Also applies to: 299-325
mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts (1)
647-683: Static analysis ReDoS warning is a false positive.The
keywordsandcomponentsarrays on lines 649-655 are hardcoded string literals, not user-controlled input. The generated regex patterns like\b(VStack)\bare safe and linear-time.For a minor performance optimization, you could pre-compile these regexes outside the method to avoid recreating them on each call, but this is optional given the typical usage frequency.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt (2)
30-30: Unused state variableshowPreview.The
showPreviewstate is declared but never read or modified. If live preview toggle is planned for future implementation, consider adding a TODO comment. Otherwise, remove it to avoid confusion.- var showPreview by remember { mutableStateOf(false) } - + // TODO: Add live preview toggle when NanoRenderer integration is ready + // var showPreview by remember { mutableStateOf(false) }
149-157: Reuse already-calculatedlinesvariable.
item.source.lines()is called again on line 152 whenlineswas already computed on line 123.// Footer with line count Spacer(modifier = Modifier.height(4.dp)) Text( - text = "${item.source.lines().size} lines of NanoDSL code", + text = "${lines.size} lines of NanoDSL code", style = JewelTheme.defaultTextStyle.copy( fontSize = 10.sp, color = JewelTheme.globalColors.text.info.copy(alpha = 0.5f) ) )mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt (1)
214-228: Manual JSON escaping is acceptable but fragile.The current implementation handles common escape sequences. For improved robustness, consider using
kotlinx.serialization.jsonif it's available in the module, as it would handle all edge cases (Unicode escapes, control characters, etc.).mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (1)
151-165: Exception handling doesn't updatelastErrorsfor retry prompt.If an exception occurs on attempt 1 or 2, the loop continues but
lastErrorsremains from the previous validation (or empty on first attempt). The next retry prompt won't include meaningful error context.Consider capturing the exception message as a pseudo-error for retry context:
} catch (e: Exception) { logger.error(e) { "NanoDSL generation failed on attempt $attempt" } + lastErrors = listOf(ValidationError("Generation error: ${e.message}", 0)) if (attempt == maxRetries) { onProgress("❌ Generation failed: ${e.message}") return ToolResult.AgentResult(mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt (1)
32-87: Inconsistent validation depth compared to JS implementation.The WASM implementation lacks the indentation-multiple-of-4 warnings and known-component checks that the JS implementation includes. Since both target browser environments, consider aligning the validation logic for consistent behavior.
Compare with
NanoDSLValidator.js.ktlines 50-95 which includes:
- Indentation multiple-of-4 check (line 66-68)
- Known components validation (lines 71-95)
mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt (1)
32-87: Consider aligning validation depth with JS implementation.The iOS implementation lacks indentation-multiple-of-4 warnings and known-component checks present in the JS implementation. While this works, users on different platforms may receive different warning levels for the same code.
This is optional since the core error detection (component def, tabs, brackets) is present.
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.kt (1)
1-152: Consider extracting shared validation logic to commonMain.The
performBasicValidationandcreateSourceOnlyIRfunctions are nearly identical across iOS, JS, WASM, and JVM fallback implementations. Extracting common logic to a shared helper incommonMainwould:
- Reduce ~300 lines of duplicated code
- Ensure consistent validation behavior across platforms
- Simplify maintenance when adding new validation rules
Example structure:
// In commonMain internal object NanoDSLValidationHelper { fun performBasicValidation(source: String, includeWarnings: Boolean = true): NanoDSLValidationResult fun createSourceOnlyIR(source: String): String }mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.kt (4)
16-35: Reflection-based NanoDSL detection is sound; ensure keep rules for Android shrinkers.The lazy detection of
cc.unitmesh.xuiper.dsl.NanoDSLvia reflection and thefullParserAvailableflag look correct and failure-safe. On Android release builds, though, R8/Proguard may rename or strip this class/methods, which would makeClass.forNamefail even when the dependency is present. Consider documenting or adding keep rules forcc.unitmesh.xuiper.dsl.NanoDSL#validate(String)and#toJson(String, boolean)so full parsing remains available in optimized builds.
81-104: Reflection-based result conversion duplicates JVM logic; consider a shared helper.
convertValidationResultis essentially the same as the JVM implementation, including the “assume valid if conversion fails” behavior. That’s fine functionally, but it’s a clear duplication acrossjvmMainandandroidMain. If you expect this to evolve (e.g., additional fields on the xuiper result), consider extracting a common JVM helper (or at least a small shared utility) to avoid divergence between Android and pure JVM.
106-161: Basic validation is consistent with iOS but lags behind wasmJs checks.This
performBasicValidationmirrors the iOS version (component presence, tabs, balanced parens/braces), but the wasmJs implementation also warns on non‑4‑space indentation and unknown component names. For more consistent behavior across non‑xuiper platforms—including Android when the full parser is missing—you might want to:
- Reuse the richer wasmJs rules (indentation multiple‑of‑4, known component warnings), or
- Factor a shared
performBasicValidationinto common code used by Android, iOS, and wasmJs, with platform-specific extensions if needed.Right now the discrepancy is harmless but could surprise users moving between targets.
163-176: Manual JSON construction works but is brittle; consider centralizing escaping.
createMinimalIRhand‑escapes backslashes, quotes, and control characters into a JSON string. That’s acceptable for a fallback path, but it’s easy to miss edge cases or changes over time. If you already have a lightweight JSON utility in common code, consider reusing it here (or centralizing a shared “escape NanoDSL source to JSON string” helper) to avoid subtle encoding bugs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
.idea/runConfigurations/RunIDEA.xml(1 hunks)mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.kt(1 hunks)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt(1 hunks)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt(1 hunks)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt(1 hunks)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt(3 hunks)mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt(1 hunks)mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt(3 hunks)mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.kt(1 hunks)mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt(1 hunks)mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt(3 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt(5 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.kt(2 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.kt(4 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.kt(3 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/knowledge/IdeaKnowledgeContent.kt(1 hunks)mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.kt(2 hunks)mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.kt(3 hunks)mpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts(2 hunks)mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts(2 hunks)mpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.ts(2 hunks)mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.ts(2 hunks)mpp-vscode/src/bridge/mpp-core.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/knowledge/IdeaKnowledgeContent.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ktmpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.ktmpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.ktmpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt
**/*.{kt,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never use hardcoded colors - always use design tokens from the design system
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/knowledge/IdeaKnowledgeContent.ktmpp-vscode/src/bridge/mpp-core.tsmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.ktmpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ktmpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.ktmpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.ktmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.ktmpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.ktmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.tsmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
When modifying
CodingAgentRenderer, update ALL TypeScript implementations:BaseRenderer.ts,CliRenderer.ts,ServerRenderer.ts, andTuiRenderer.ts
Files:
mpp-vscode/src/bridge/mpp-core.tsmpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts
mpp-vscode/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
When modifying
CodingAgentRenderer, update VSCode implementations:mpp-vscode/src/bridge/mpp-core.tsandmpp-vscode/src/providers/chat-view.ts
Files:
mpp-vscode/src/bridge/mpp-core.ts
mpp-ui/src/jsMain/typescript/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use
semanticInk/semanticChalkdesign tokens frommpp-ui/src/jsMain/typescript/design-system/for CLI/TUI styling
Files:
mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Run `./gradlew :mpp-ui:generateI18n4kFiles` command when updating i18n resources
Applied to files:
.idea/runConfigurations/RunIDEA.xml
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`
Applied to files:
mpp-vscode/src/bridge/mpp-core.tsmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktmpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.ktmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`
Applied to files:
mpp-vscode/src/bridge/mpp-core.tsmpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
Applied to files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.ktmpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.ktmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.tsmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`
Applied to files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.ktmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.ktmpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.tsmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.ts : Use `semanticInk`/`semanticChalk` design tokens from `mpp-ui/src/jsMain/typescript/design-system/` for CLI/TUI styling
Applied to files:
mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/CliRenderer.tsmpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.ts
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : Use concrete classes (not interfaces) and `Promise` (not `Flow`) for JsExport annotations
Applied to files:
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.kt
🧬 Code graph analysis (13)
mpp-vscode/src/bridge/mpp-core.ts (3)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt (1)
source(201-221)mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt (1)
source(90-98)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
irJson(24-37)irJson(28-28)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt (1)
IdeaCircularProgressIndicator(191-227)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt (1)
IdeaCircularProgressIndicator(191-227)
mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.ts (3)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
isValid(6-10)message(15-19)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt (3)
message(83-108)message(135-139)source(201-221)mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt (1)
source(90-98)
mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
validate(55-75)validate(62-62)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt (1)
IdeaCircularProgressIndicator(191-227)
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.kt (4)
mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt (3)
performBasicValidation(32-87)validate(9-102)validate(11-20)mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt (2)
performBasicValidation(111-209)validate(36-56)mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt (3)
performBasicValidation(32-87)validate(9-102)validate(11-20)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
validate(55-75)validate(62-62)
mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts (1)
mpp-ui/src/jsMain/typescript/design-system/theme-helpers.ts (2)
semanticChalk(15-42)dividers(125-130)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt (3)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanStep.kt (1)
generateId(116-118)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/AgentPlan.kt (1)
generateId(135-137)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/plan/PlanTask.kt (1)
generateId(151-153)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt (1)
IdeaCircularProgressIndicator(191-227)
mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.kt (4)
mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt (3)
convertValidationResult(83-106)performBasicValidation(111-209)validate(36-56)mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt (3)
performBasicValidation(32-87)validate(9-102)validate(11-20)mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt (3)
performBasicValidation(32-87)validate(9-102)validate(11-20)mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
validate(55-75)validate(62-62)
mpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.ts (2)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt (1)
source(201-221)mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt (1)
source(90-98)
mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (2)
validate(55-75)validate(62-62)
🪛 ast-grep (0.40.0)
mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts
[warning] 663-663: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${keyword})\\b, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 669-669: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(\\b(${comp})\\b, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test mpp-core (KMP)
- GitHub Check: Build
🔇 Additional comments (30)
.idea/runConfigurations/RunIDEA.xml (1)
13-13: The:mpp-idea:runIdeGradle task is properly defined inmpp-idea/build.gradle.kts. The change correctly references the new module's run configuration without issues.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.kt (1)
11-12: Mermaid loading overlay correctly migrated to IdeaCircularProgressIndicatorThe swap to
IdeaCircularProgressIndicator()inLoadingOverlaypreserves the centered spinner UX while using the new, coroutine-free indicator. Import wiring is correct and no other behavior changes are introduced.Also applies to: 95-101
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaAnalysisComponents.kt (1)
28-29: Progress indicators in analysis header and fix section are wired correctlyReplacing the old progress indicator with
IdeaCircularProgressIndicator()in the header status pill and the Fix Generation section keeps the same visibility conditions (only while active/generating) and fits visually in the existing layouts. All usages rely on sensible defaults, and there’s no change to control flow.Also applies to: 79-96, 190-207
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCommitComponents.kt (1)
19-20: Commit loading states migrated cleanly to IdeaCircularProgressIndicatorBoth the full-panel loading state and the inline
IdeaIssueIndicatornow useIdeaCircularProgressIndicator, with a largersize = 20.dpfor the inline chip where needed. The behavior and conditions mirror the old implementation, so this is a safe drop-in replacement.Also applies to: 54-57, 275-278
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.kt (1)
89-91: NanoDSL timeline dispatch looks correctDelegating
TimelineItem.NanoDSLItemtoIdeaNanoDSLBubble(item, project)cleanly integrates NanoDSL into the existing timeline dispatcher without affecting other cases.mpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts (1)
15-15: BaseRenderer NanoDSL hook is a safe, backwards‑compatible extensionImporting
JsNanoDSLDataand adding a non‑abstractrenderNanoDSL(data: JsNanoDSLData)with a no‑op body cleanly extends the JS renderer API so derived renderers can opt into NanoDSL without impacting existing implementations.Also applies to: 149-157
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt (1)
132-149: New renderNanoDSL hook is well‑shaped; verify all renderer implementations are updatedThe
renderNanoDSL(source, irJson, metadata)addition matches the NanoDSL use case and is safely backwards‑compatible thanks to the default no‑op implementation. Please double‑check that all expected renderers now either override this or intentionally rely on the default, including:
- Kotlin:
DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer,JsRendererAdapter,CodingCliRenderer,ConsoleRenderer- JS/TS:
BaseRendererand its concrete implementations (CliRenderer,ServerRenderer,TuiRenderer)- VSCode bridge/renderer (
VSCodeRendererand its Kotlin adapter object)This keeps the behavior consistent with the AGENTS.md guidelines about updating all renderer implementations when
CodingAgentRendererchanges. Based on learnings, this cross‑renderer alignment is required.mpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.ts (2)
505-542: LGTM! Clean implementation ofrenderNanoDSLfor server-side rendering.The method correctly uses
semanticChalkdesign tokens as per coding guidelines, and the structure (header, validity indicator, warnings, line-numbered code, footer) aligns well with other renderer implementations.
11-12: Imports are correct.The
JsNanoDSLDatatype alias import follows the same pattern used inCliRenderer.ts.mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts (2)
599-642: Well-implementedrenderNanoDSLwith syntax highlighting.The method properly uses
dividers.solid()andsemanticChalktokens from the design system, and provides helpful syntax highlighting for NanoDSL code.
16-16: Import correctly added.mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt (1)
193-221: Well-designedNanoDSLItemdata class.The implementation follows the established pattern of other
TimelineItemsubclasses with:
- Comprehensive documentation explaining JVM vs non-JVM platform behavior
- Sensible default values (
generationAttempts = 1,isValid = true)- Useful
hasLivePreview()helper for UI conditional renderingmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt (1)
24-161: Good use of design tokens.The component correctly uses
AutoDevColorsandJewelThemethroughout, adhering to the coding guidelines that prohibit hardcoded colors.mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt (1)
1-76: Well-designed cross-platform validation API.The abstractions are clean and well-documented:
NanoDSLParseResultsealed class withSuccess/Failurevariants and convenient helper methodsValidationErrorincludes optionalsuggestionfield for actionable error messages- Clear documentation of platform-specific behavior (JVM full parsing vs non-JVM lightweight checks)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.kt (2)
562-589: Good implementation ofrenderNanoDSLfor Compose.The method correctly extracts metadata and creates a
NanoDSLItemfor the timeline. The fallback regex for component name extraction is a nice touch.One consideration: the warnings are split by
;(line 577). If this is a convention, it should be documented somewhere (e.g., in the metadata handling code orNanoDSLAgent).
884-888: Appropriate decision to not persistNanoDSLItem.The reasoning that NanoDSL items are runtime-only and can be regenerated is sound. This is consistent with the treatment of
LiveTerminalItem.mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt (2)
111-209: Solid basic validation implementation.The validation covers essential checks:
- Component definition presence
- Indentation consistency (4-space rule)
- Tab detection
- Known component warnings
- Bracket balance
The bracket counting could be enhanced to report the approximate location of imbalance, but the current implementation is acceptable for basic validation.
21-34: Well-implemented optional dependency loading.The reflection-based approach for optional xuiper-ui integration is clean:
- Proper exception handling
- Good logging for debugging
- Graceful fallback to basic validation
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (3)
38-46: LGTM! Well-structured constructor with validation support.The new
maxRetriesparameter with a sensible default and the validator instance are properly initialized. The default of 3 retries aligns with theRunConfig(maxTurns = 3)in the companion object.
215-235: LGTM! Clean metadata construction.The
buildMapidiom with conditionalputfor optional fields is idiomatic Kotlin. All relevant metadata is captured including attempts, validity, and warnings.
183-210: LGTM! Clear retry prompt structure.The retry prompt effectively combines the original request with structured error feedback, enabling the LLM to self-correct based on specific validation failures.
mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt (1)
89-101: LGTM! IR creation is consistent with other platforms.The JSON escaping and structure match the iOS and JS implementations, ensuring cross-platform consistency for downstream consumers.
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt (3)
87-98: LGTM! Well-designed JS-friendly data class.
JsNanoDSLDatacorrectly usesArray<String>instead ofListfor JS interoperability and follows the pattern of other JS data classes in this file.
203-213: LGTM! Clean adapter implementation.The metadata parsing correctly handles optional values with sensible defaults. The warnings splitting aligns with how they're joined in
NanoDSLAgent.buildMetadata.
129-131: TherenderNanoDSLmethod is properly defined in theCodingAgentRendererinterface with a default no-op implementation, so all implementations automatically inherit it without requiring explicit overrides. Implementations that need NanoDSL support (Compose, Jewel, JavaScript renderers) override it; those that don't (CLI, Console, Server renderers) correctly rely on the default. The design is complete and requires no changes.mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt (1)
9-30: LGTM! Standard validation flow for iOS platform.The validate/parse pattern correctly follows the expect/actual contract, with appropriate fallback to source-only IR since full AST parsing isn't available on iOS.
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.kt (2)
35-132: LGTM! Comprehensive validation for JS platform.This implementation provides the most thorough non-JVM validation including:
- Component definition check
- Indentation consistency (tabs forbidden, multiple-of-4 warnings)
- Known components validation
- Bracket balance verification
The logic aligns well with the JVM basic validation fallback.
137-151: LGTM! Consistent IR generation.The
createSourceOnlyIRmethod properly escapes all control characters and produces valid JSON structure matching other platform implementations.mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.kt (2)
37-57: Validation flow and empty‑source handling look correct and robust.The early check for
source.isBlank()returning an explicitValidationError("Empty source code", 0)and the subsequent preference for the full parser with a clean fallback toperformBasicValidationgive a clear, predictable contract for callers, including when the xuiper parser is present but fails. No issues here.
59-79: Parse behavior aligns with design and uses a sensible minimal‑IR fallback.
parsecorrectly:
- Reuses
validateand short‑circuits toFailurewhen invalid.- Delegates to the full parser when available, surfacing parser exceptions as
Failurewith a useful error message.- Falls back to
createMinimalIR(source)only when the full parser is unavailable, which keeps JVM‑like strictness when xuiper is present while still enabling non‑xuiper Android builds to render something.This matches the expected cross‑platform contract for Android’s hybrid JVM/non‑JVM role.
| } catch (e: Exception) { | ||
| logger.warn(e) { "Failed to convert validation result" } | ||
| NanoDSLValidationResult(isValid = true) // Assume valid if conversion fails | ||
| } |
There was a problem hiding this comment.
Reconsider fallback behavior when conversion fails.
Returning isValid = true when the conversion from xuiper-ui's result fails (line 104) could mask validation errors. Consider returning isValid = false with a warning, or propagating the conversion failure.
} catch (e: Exception) {
logger.warn(e) { "Failed to convert validation result" }
- NanoDSLValidationResult(isValid = true) // Assume valid if conversion fails
+ NanoDSLValidationResult(
+ isValid = false,
+ errors = listOf(ValidationError("Internal error: failed to process validation result", 0)),
+ warnings = listOf("Validation result conversion failed: ${e.message}")
+ )
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt
around lines 102 to 105, the current catch block returns
NanoDSLValidationResult(isValid = true) which masks potential validation
failures; change the fallback to return isValid = false (or rethrow the
exception) and include the caught exception details in the log message so
failures are visible; specifically, replace the return with
NanoDSLValidationResult(isValid = false) and update logger.warn to include e and
a clear message, or rethrow/wrap the exception if upstream should handle
conversion failures.
| override fun renderNanoDSL( | ||
| source: String, | ||
| irJson: String?, | ||
| metadata: Map<String, String> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all renderNanoDSL implementations
echo "=== All renderNanoDSL implementations in .kt files ==="
rg "renderNanoDSL" --type kt -B 2 | head -100
echo ""
echo "=== Search for all CodingAgentRenderer implementations ==="
rg "class.*:\s*.*CodingAgentRenderer" --type kt
echo ""
echo "=== Check CodingCliRenderer ==="
fd -type f -name "*CodingCliRenderer*" --type kt 2>/dev/null || echo "Searching for CodingCliRenderer content..."
rg "CodingCliRenderer" --type kt -l
echo ""
echo "=== Check ConsoleRenderer ==="
rg "ConsoleRenderer" --type kt -l
echo ""
echo "=== Check all renderer files ending with Renderer.kt ==="
fd -type f -name "*Renderer.kt" | sortRepository: phodal/auto-dev
Length of output: 634
🏁 Script executed:
#!/bin/bash
# Find all renderNanoDSL implementations in Kotlin files
echo "=== All renderNanoDSL in Kotlin files ==="
rg "renderNanoDSL" --glob "*.kt" -B 2 -A 3 | head -150
echo ""
echo "=== Search for CodingAgentRenderer implementations ==="
rg "class\s+\w+.*:\s*.*CodingAgentRenderer" --glob "*.kt" -B 1
echo ""
echo "=== Find all Renderer.kt files ==="
find . -type f -name "*Renderer.kt" | head -20
echo ""
echo "=== Check if CodingCliRenderer or ConsoleRenderer exist ==="
find . -type f -name "*.kt" | xargs grep -l "CodingCliRenderer\|ConsoleRenderer" 2>/dev/null | head -20
echo ""
echo "=== Check file structure around ComposeRenderer ==="
find . -type f -path "*compose*renderer*" -name "*.kt" 2>/dev/nullRepository: phodal/auto-dev
Length of output: 7012
🏁 Script executed:
#!/bin/bash
echo "=== CodingCliRenderer content ==="
cat -n mpp-ui/src/jvmMain/kotlin/cc/unitmesh/server/cli/CodingCli.kt | head -100
echo ""
echo "=== ConsoleRenderer content ==="
cat -n mpp-ui/src/jvmMain/kotlin/cc/unitmesh/server/cli/DocumentCli.kt | head -100
echo ""
echo "=== DefaultCodingAgentRenderer ==="
cat -n mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/DefaultCodingAgentRenderer.kt 2>/dev/null || echo "File not found"
echo ""
echo "=== ServerSideRenderer ==="
cat -n mpp-server/src/main/kotlin/cc/unitmesh/server/render/ServerSideRenderer.kt | head -80Repository: phodal/auto-dev
Length of output: 18326
🏁 Script executed:
#!/bin/bash
echo "=== Search for CodingCliRenderer class definition ==="
rg "class\s+CodingCliRenderer" --glob "*.kt" -B 2 -A 10
echo ""
echo "=== Search for ConsoleRenderer class definition ==="
rg "class\s+ConsoleRenderer" --glob "*.kt" -B 2 -A 10
echo ""
echo "=== Check ReviewCli.kt ==="
cat -n mpp-ui/src/jvmMain/kotlin/cc/unitmesh/server/cli/ReviewCli.kt 2>/dev/null | head -100 || echo "File not found"
echo ""
echo "=== Check BaseRenderer.kt for renderNanoDSL ==="
cat -n mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/BaseRenderer.kt | grep -A 5 "renderNanoDSL" || echo "No renderNanoDSL in BaseRenderer"
echo ""
echo "=== Full BaseRenderer.kt content ==="
cat -n mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/BaseRenderer.kt | head -150Repository: phodal/auto-dev
Length of output: 12030
Update JVM CLI and server-side renderer implementations to include renderNanoDSL.
Per coding guidelines, when modifying CodingAgentRenderer, all implementations must be updated. Currently, CodingCliRenderer, ConsoleRenderer, DefaultCodingAgentRenderer, and ServerSideRenderer are missing the renderNanoDSL method that was added to ComposeRenderer and JewelRenderer in this PR.
🤖 Prompt for AI Agents
In
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.kt
around lines 566-569, you added renderNanoDSL but did not update other
CodingAgentRenderer implementations; add the missing override fun
renderNanoDSL(source: String, irJson: String?, metadata: Map<String, String>) to
CodingCliRenderer, ConsoleRenderer, DefaultCodingAgentRenderer, and
ServerSideRenderer with the same signature and visibility, and implement it
consistently (e.g., delegate to the class's existing render logic or to the
shared renderer method used by ComposeRenderer/JewelRenderer), ensuring it
returns the same type and behavior as the other render methods so the code
compiles and follows the established contract.
| /** | ||
| * Render generated NanoDSL UI code | ||
| * Posts a nanodsl message to the webview for rendering | ||
| */ | ||
| renderNanoDSL(source: string, irJson?: string | null, metadata?: Record<string, string>): void { | ||
| this.chatProvider.postMessage({ | ||
| type: 'nanodsl', | ||
| data: { | ||
| source, | ||
| irJson, | ||
| componentName: metadata?.componentName, | ||
| attempts: metadata?.attempts ? parseInt(metadata.attempts) : 1, | ||
| isValid: metadata?.isValid === 'true', | ||
| warnings: metadata?.warnings?.split(';').filter(Boolean) || [] | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd /repo && wc -l mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 114
🏁 Script executed:
cd /repo && sed -n '498,520p' mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 114
🏁 Script executed:
cd /repo && rg "interface.*CodingAgentRenderer|class.*CodingAgentRenderer" -A 10 --type tsRepository: phodal/auto-dev
Length of output: 114
🏁 Script executed:
pwd && git ls-files | head -20Repository: phodal/auto-dev
Length of output: 693
🏁 Script executed:
git ls-files | grep -E "mpp-core\.ts|CodingAgentRenderer"Repository: phodal/auto-dev
Length of output: 257
🏁 Script executed:
git ls-files | grep -E "mpp-vscode.*\.ts"Repository: phodal/auto-dev
Length of output: 2507
🏁 Script executed:
wc -l mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 96
🏁 Script executed:
sed -n '424,440p' mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 628
🏁 Script executed:
sed -n '498,520p' mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 1450
🏁 Script executed:
cat mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.ktRepository: phodal/auto-dev
Length of output: 5664
🏁 Script executed:
sed -n '1,50p' mpp-vscode/src/bridge/mpp-core.tsRepository: phodal/auto-dev
Length of output: 1167
🏁 Script executed:
rg "class.*VSCodeRenderer|createKotlinRenderer" mpp-vscode/src/bridge/mpp-core.ts -B 3 -A 1Repository: phodal/auto-dev
Length of output: 558
Forward renderNanoDSL through the Kotlin renderer adapter
VSCodeRenderer.renderNanoDSL(...) posts nanodsl messages to the webview, but createKotlinRenderer() does not expose this method on the adapter object passed to JsCodingAgent. If the Kotlin side calls renderNanoDSL(...) on its renderer, the call will be undefined and VSCode will not receive NanoDSL events.
Add the forwarding:
private createKotlinRenderer(): any {
const renderer = this.renderer;
return {
renderIterationHeader: (c: number, m: number) => renderer.renderIterationHeader(c, m),
renderLLMResponseStart: () => renderer.renderLLMResponseStart(),
renderLLMResponseChunk: (chunk: string) => renderer.renderLLMResponseChunk(chunk),
renderLLMResponseEnd: () => renderer.renderLLMResponseEnd(),
renderToolCall: (name: string, params: string) => renderer.renderToolCall(name, params),
renderToolResult: (name: string, success: boolean, output: string | null, full: string | null) =>
renderer.renderToolResult(name, success, output, full),
renderTaskComplete: (executionTimeMs?: number, toolsUsedCount?: number) => renderer.renderTaskComplete(executionTimeMs, toolsUsedCount),
renderFinalResult: (success: boolean, msg: string, iters: number) =>
renderer.renderFinalResult(success, msg, iters),
renderError: (msg: string) => renderer.renderError(msg),
renderRepeatWarning: (name: string, count: number) => renderer.renderRepeatWarning(name, count),
renderRecoveryAdvice: (advice: string) => renderer.renderRecoveryAdvice(advice),
+ renderNanoDSL: (source: string, irJson?: string | null, metadata?: Record<string, string>) =>
+ renderer.renderNanoDSL(source, irJson, metadata),
renderUserConfirmationRequest: (name: string, params: any) =>
renderer.renderUserConfirmationRequest(name, params),
forceStop: () => renderer.forceStop()
};
}This keeps the JS bridge synchronized with the CodingAgentRenderer interface so VSCode can render NanoDSL items. This is required per the coding guidelines for VSCode implementations whenever CodingAgentRenderer is extended.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In mpp-vscode/src/bridge/mpp-core.ts around lines 424 to 440, the Kotlin
renderer adapter returned by createKotlinRenderer does not forward renderNanoDSL
to the underlying VSCodeRenderer, so calls from the Kotlin side will be
undefined; add a renderNanoDSL(source, irJson?, metadata?) method on the adapter
that simply delegates to this.chatProvider.renderNanoDSL /
this.chatProvider.postMessage equivalent (i.e., call the existing VSCode
renderer's renderNanoDSL implementation or post the same 'nanodsl' message
structure used elsewhere), preserving the metadata parsing (attempts, isValid,
warnings) exactly as in the current renderNanoDSL implementation so the adapter
matches the CodingAgentRenderer interface.
There was a problem hiding this comment.
Pull request overview
This PR implements validation/retry mechanisms and UI rendering integration for the NanoDSLAgent, enabling AI-generated UI code validation with automatic error correction and comprehensive cross-platform rendering support.
Key Changes:
- Validation Infrastructure: Multi-platform NanoDSLValidator with JVM full parsing (via reflection to xuiper-ui) and basic syntax validation for JS/WASM/iOS/Android
- Retry Mechanism: NanoDSLAgent now validates generated code and retries up to 3 times with error feedback for LLM self-correction
- Rendering Integration: New
renderNanoDSL()method added to CodingAgentRenderer interface with implementations across 6 platforms (VSCode, CLI, TUI, Server, Compose, IntelliJ)
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt |
Defines expect class with validation/parse interfaces and result types |
mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.jvm.kt |
JVM implementation using reflection to access xuiper-ui's NanoParser with fallback to basic validation |
mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.android.kt |
Android implementation mirroring JVM approach with reflection-based parsing |
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.js.kt |
JavaScript implementation with basic syntax validation (no AST parsing) |
mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.wasmJs.kt |
WASM implementation with basic syntax validation |
mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.ios.kt |
iOS implementation with basic syntax validation |
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt |
Enhanced with retry loop, validation integration, error feedback prompts, and metadata generation |
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt |
Added renderNanoDSL() interface method with default no-op implementation |
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/RendererModels.kt |
Added TimelineItem.NanoDSLItem data class for timeline integration |
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt |
Added JsNanoDSLData export class and JS renderer adapter integration |
mpp-vscode/src/bridge/mpp-core.ts |
VSCode renderer implementation posting nanodsl messages to webview |
mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts |
CLI renderer with syntax-highlighted NanoDSL code display |
mpp-ui/src/jsMain/typescript/agents/render/TuiRenderer.ts |
TUI renderer displaying NanoDSL in markdown code blocks |
mpp-ui/src/jsMain/typescript/agents/render/ServerRenderer.ts |
Server renderer with formatted code and line numbers |
mpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts |
Added base renderNanoDSL() method for inheritance |
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.kt |
Compose renderer creating NanoDSLItem timeline entries |
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.kt |
Added NanoDSLTimelineItem composable component for UI display |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt |
IntelliJ Jewel renderer adding NanoDSLItem to timeline |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt |
New IntelliJ bubble component for displaying NanoDSL code with Jewel theming |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.kt |
Timeline integration routing NanoDSLItem to bubble component |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/knowledge/IdeaKnowledgeContent.kt |
Knowledge panel display for NanoDSL items |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/compose/IdeaComposeEffects.kt |
Added IdeaCircularProgressIndicator to avoid ClassLoader conflicts |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/*.kt |
Replaced Jewel CircularProgressIndicator with custom implementation |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/MermaidDiagramView.kt |
Updated to use IdeaCircularProgressIndicator |
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt |
Updated progress indicators to avoid conflicts |
.idea/runConfigurations/RunIDEA.xml |
Fixed Gradle task path for IDEA run configuration |
Files not reviewed (1)
- .idea/runConfigurations/RunIDEA.xml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (attempt in 1..maxRetries) { | ||
| try { | ||
| val prompt = if (attempt == 1) { | ||
| buildPrompt(input) | ||
| } else { | ||
| // Include previous errors in retry prompt | ||
| buildRetryPrompt(input, lastGeneratedCode, lastErrors) | ||
| } | ||
|
|
||
| onProgress(if (attempt == 1) { | ||
| "Calling LLM for code generation..." | ||
| } else { | ||
| "Retry attempt $attempt/$maxRetries - fixing validation errors..." | ||
| }) | ||
|
|
||
| val responseBuilder = StringBuilder() | ||
| llmService.streamPrompt( | ||
| userPrompt = prompt, | ||
| compileDevIns = false | ||
| ).toList().forEach { chunk -> | ||
| responseBuilder.append(chunk) | ||
| } | ||
|
|
||
| val llmResponse = responseBuilder.toString() | ||
|
|
||
| // Extract NanoDSL code from markdown code fence | ||
| val codeFence = CodeFence.parse(llmResponse) | ||
| val generatedCode = if (codeFence.text.isNotBlank()) { | ||
| codeFence.text.trim() | ||
| } else { | ||
| llmResponse.trim() | ||
| } | ||
|
|
||
| lastGeneratedCode = generatedCode | ||
|
|
||
| // Validate the generated code | ||
| val validationResult = validator.validate(generatedCode) | ||
|
|
||
| if (validationResult.isValid) { | ||
| // Try to parse and get IR JSON | ||
| val parseResult = validator.parse(generatedCode) | ||
| if (parseResult is NanoDSLParseResult.Success) { | ||
| irJson = parseResult.irJson | ||
| } | ||
|
|
||
| onProgress("✅ Generated ${generatedCode.lines().size} lines of valid NanoDSL code" + | ||
| (if (attempt > 1) " (after $attempt attempts)" else "")) | ||
|
|
||
| // Log warnings if any | ||
| if (validationResult.warnings.isNotEmpty()) { | ||
| validationResult.warnings.forEach { warning -> | ||
| onProgress("⚠️ $warning") | ||
| } | ||
| } | ||
|
|
||
| return ToolResult.AgentResult( | ||
| success = true, | ||
| content = generatedCode, | ||
| metadata = buildMetadata(input, generatedCode, attempt, irJson, validationResult) | ||
| ) | ||
| } else { | ||
| // Validation failed, prepare for retry | ||
| lastErrors = validationResult.errors | ||
| val errorMessages = validationResult.errors.joinToString("\n") { | ||
| "Line ${it.line}: ${it.message}" + (it.suggestion?.let { s -> " ($s)" } ?: "") | ||
| } | ||
|
|
||
| if (attempt < maxRetries) { | ||
| onProgress("⚠️ Validation failed, will retry: $errorMessages") | ||
| logger.warn { "NanoDSL validation failed (attempt $attempt): $errorMessages" } | ||
| } else { | ||
| onProgress("❌ Validation failed after $maxRetries attempts: $errorMessages") | ||
| logger.error { "NanoDSL validation failed after all retries: $errorMessages" } | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| logger.error(e) { "NanoDSL generation failed on attempt $attempt" } | ||
| if (attempt == maxRetries) { | ||
| onProgress("❌ Generation failed: ${e.message}") | ||
| return ToolResult.AgentResult( | ||
| success = false, | ||
| content = "Failed to generate NanoDSL: ${e.message}", | ||
| metadata = mapOf( | ||
| "error" to (e.message ?: "Unknown error"), | ||
| "attempts" to attempt.toString() | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
[nitpick] The retry loop doesn't include a delay between attempts. When the LLM generates invalid code, immediately retrying without any backoff can:
- Waste resources on rapid repeated failures
- Not give the LLM's internal state time to "reset" if it's in a failure mode
- Result in identical errors if the issue is deterministic
Consider adding a small delay (e.g., 500ms-1s) between retry attempts, or at least documenting why immediate retry is preferred.
| private fun buildRetryPrompt( | ||
| input: NanoDSLContext, | ||
| previousCode: String, | ||
| errors: List<ValidationError> | ||
| ): String { | ||
| val errorFeedback = buildString { | ||
| appendLine("## Previous Attempt (INVALID)") | ||
| appendLine("```nanodsl") | ||
| appendLine(previousCode) | ||
| appendLine("```") | ||
| appendLine() | ||
| appendLine("## Validation Errors to Fix:") | ||
| errors.forEach { error -> | ||
| appendLine("- Line ${error.line}: ${error.message}") | ||
| error.suggestion?.let { appendLine(" Suggestion: $it") } | ||
| } | ||
| appendLine() | ||
| appendLine("## Instructions:") | ||
| appendLine("Please fix the above errors and generate a corrected version.") | ||
| appendLine("Output ONLY the corrected NanoDSL code, no explanations.") | ||
| } | ||
|
|
||
| onProgress("✅ Generated ${generatedCode.lines().size} lines of NanoDSL code") | ||
|
|
||
| ToolResult.AgentResult( | ||
| success = true, | ||
| content = generatedCode, | ||
| metadata = mapOf( | ||
| "description" to input.description, | ||
| "componentType" to (input.componentType ?: "auto"), | ||
| "linesOfCode" to generatedCode.lines().size.toString(), | ||
| "includesState" to input.includeState.toString(), | ||
| "includesHttp" to input.includeHttp.toString() | ||
| ) | ||
| ) | ||
| } catch (e: Exception) { | ||
| logger.error(e) { "NanoDSL generation failed" } | ||
| onProgress("❌ Generation failed: ${e.message}") | ||
|
|
||
| ToolResult.AgentResult( | ||
| success = false, | ||
| content = "Failed to generate NanoDSL: ${e.message}", | ||
| metadata = mapOf("error" to (e.message ?: "Unknown error")) | ||
| ) | ||
| return """ | ||
| ${buildPrompt(input)} | ||
|
|
||
| $errorFeedback | ||
| """.trim() |
There was a problem hiding this comment.
The retry prompt includes the full previous invalid code and all errors, which could consume significant token budget for large generated code blocks or multiple retries. Consider:
- Including only the most critical errors (e.g., first 3-5)
- Truncating very long code blocks in the retry prompt
- Using a more concise error format
For example, if the first attempt generates 500 lines of code with 20 errors, the second attempt's prompt would be extremely long, and the third attempt even longer.
There was a problem hiding this comment.
The showPreview state variable is declared but never used. The NanoDSL bubble component doesn't implement any preview toggle functionality despite the state management being in place.
Either:
- Remove the unused variable if preview functionality isn't planned
- Implement the preview toggle UI if it's intended for future use
- Add a TODO comment explaining the planned preview feature
| var showPreview by remember { mutableStateOf(false) } |
| for (line in lines) { | ||
| for (char in line) { | ||
| when (char) { | ||
| '(' -> parenCount++ | ||
| ')' -> parenCount-- | ||
| '{' -> braceCount++ | ||
| '}' -> braceCount-- |
There was a problem hiding this comment.
Negative bracket/parenthesis counts are not detected as errors. The current logic only checks if counts are non-zero at the end, but doesn't catch cases where closing brackets appear before opening ones (e.g., "text)" or "}value").
Consider tracking and reporting when counts go negative:
if (parenCount < 0) {
errors.add(ValidationError("Unexpected closing parenthesis", lineNum))
parenCount = 0 // Reset to continue checking
}| for (line in lines) { | |
| for (char in line) { | |
| when (char) { | |
| '(' -> parenCount++ | |
| ')' -> parenCount-- | |
| '{' -> braceCount++ | |
| '}' -> braceCount-- | |
| for ((lineNum, line) in lines.withIndex()) { | |
| for (char in line) { | |
| when (char) { | |
| '(' -> parenCount++ | |
| ')' -> { | |
| parenCount-- | |
| if (parenCount < 0) { | |
| errors.add(ValidationError( | |
| "Unexpected closing parenthesis", | |
| lineNum + 1, | |
| "Found ')' before matching '('" | |
| )) | |
| parenCount = 0 // Reset to continue checking | |
| } | |
| } | |
| '{' -> braceCount++ | |
| '}' -> { | |
| braceCount-- | |
| if (braceCount < 0) { | |
| errors.add(ValidationError( | |
| "Unexpected closing brace", | |
| lineNum + 1, | |
| "Found '}' before matching '{'" | |
| )) | |
| braceCount = 0 // Reset to continue checking | |
| } | |
| } |
| */ | ||
| private fun createSourceOnlyIR(source: String): String { | ||
| // Extract component name from source | ||
| val componentNameMatch = Regex("""component\s+(\w+):""").find(source) |
There was a problem hiding this comment.
The regex pattern for extracting the component name uses \w+ which matches word characters including underscores and digits. However, component names starting with digits (e.g., component 123Test:) would be matched but are likely invalid identifiers in most languages.
Consider using a more restrictive pattern like [A-Z][a-zA-Z0-9]* to match typical PascalCase component names, or document that the validation accepts any word characters.
| val componentNameMatch = Regex("""component\s+(\w+):""").find(source) | |
| val componentNameMatch = Regex("""component\s+([A-Z][a-zA-Z0-9]*):""").find(source) |
| } | ||
|
|
||
| is TimelineItem.NanoDSLItem -> { | ||
| // NanoDSL items are not persisted (they're runtime-only, can be regenerated) |
There was a problem hiding this comment.
[nitpick] The documentation comment states "NanoDSL items are not persisted (they're runtime-only, can be regenerated)" but doesn't explain the reasoning. This decision could lead to data loss if users expect their generated UI code to persist across sessions.
Consider:
- Adding a more detailed explanation of why NanoDSL items aren't persisted
- Documenting the expected workflow for users who want to save the generated code
- Adding a user-facing warning or export option if persistence is important
| // NanoDSL items are not persisted (they're runtime-only, can be regenerated) | |
| /** | |
| * NanoDSL items are not persisted. | |
| * | |
| * Reasoning: NanoDSL items represent generated UI code that is derived from other timeline/context data. | |
| * Persisting them could lead to inconsistencies if the underlying data changes, and they can always be regenerated | |
| * from the source information. This design avoids storing potentially stale or redundant UI code. | |
| * | |
| * Expected workflow: If users want to save generated UI code, they should explicitly export or save the code | |
| * at the time of generation. NanoDSL items will not be available after a session ends unless exported. | |
| * | |
| * If persistence of generated UI code is important, consider providing a user-facing warning or an export option | |
| * in the UI to prevent accidental data loss. | |
| */ |
| text = item.source.take(300) + if (item.source.length > 300) "..." else "", | ||
| style = JewelTheme.defaultTextStyle.copy( | ||
| fontSize = 11.sp, | ||
| fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace | ||
| ) | ||
| ) |
There was a problem hiding this comment.
The NanoDSL code display in the Knowledge Content truncates at 300 characters with "..." but doesn't provide any way to view the full code. This could be frustrating for users who want to see their complete generated UI code.
Consider adding:
- A "Show More" button or expandable section
- A "Copy Code" button
- A modal/dialog to view the full code
- Or increase the character limit to a more reasonable value like 1000
| private fun performBasicValidation(source: String): NanoDSLValidationResult { | ||
| val errors = mutableListOf<ValidationError>() | ||
| val warnings = mutableListOf<String>() | ||
| val lines = source.lines() | ||
|
|
||
| // Check for component definition | ||
| val hasComponentDef = lines.any { it.trim().startsWith("component ") && it.trim().endsWith(":") } | ||
| if (!hasComponentDef) { | ||
| errors.add(ValidationError( | ||
| "Missing component definition. Expected 'component Name:'", | ||
| 0, | ||
| "Add 'component YourComponentName:' at the start" | ||
| )) | ||
| } | ||
|
|
||
| // Check indentation consistency | ||
| for ((lineNum, line) in lines.withIndex()) { | ||
| if (line.isBlank()) continue | ||
|
|
||
| val indent = line.takeWhile { it == ' ' }.length | ||
|
|
||
| // Check for tabs (not allowed) | ||
| if (line.contains('\t')) { | ||
| errors.add(ValidationError( | ||
| "Tabs are not allowed. Use 4 spaces for indentation.", | ||
| lineNum, | ||
| "Replace tabs with 4 spaces" | ||
| )) | ||
| } | ||
|
|
||
| // Check for odd indentation (should be multiple of 4) | ||
| if (indent % 4 != 0) { | ||
| warnings.add("Line ${lineNum + 1}: Indentation should be a multiple of 4 spaces") | ||
| } | ||
| } | ||
|
|
||
| // Check for known components | ||
| val knownComponents = setOf( | ||
| "VStack", "HStack", "Card", "Text", "Button", "Input", "Image", | ||
| "Badge", "Checkbox", "Toggle", "Select", "List", "Grid", | ||
| "Spacer", "Divider", "Form", "Section" | ||
| ) | ||
|
|
||
| for ((lineNum, line) in lines.withIndex()) { | ||
| val trimmed = line.trim() | ||
|
|
||
| // Check for component-like patterns | ||
| val componentMatch = Regex("""^(\w+)(?:\(.*\))?:\s*$""").find(trimmed) | ||
| if (componentMatch != null) { | ||
| val componentName = componentMatch.groupValues[1] | ||
| if (componentName !in knownComponents && | ||
| componentName != "component" && | ||
| componentName != "state" && | ||
| componentName != "if" && | ||
| componentName != "for" && | ||
| componentName != "on_click" && | ||
| componentName != "content") { | ||
| warnings.add("Line ${lineNum + 1}: Unknown component '$componentName'") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Check for unclosed blocks | ||
| var parenCount = 0 | ||
| var braceCount = 0 | ||
| for (line in lines) { | ||
| for (char in line) { | ||
| when (char) { | ||
| '(' -> parenCount++ | ||
| ')' -> parenCount-- | ||
| '{' -> braceCount++ | ||
| '}' -> braceCount-- | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (parenCount != 0) { | ||
| errors.add(ValidationError( | ||
| "Unbalanced parentheses", | ||
| 0, | ||
| "Check for missing '(' or ')'" | ||
| )) | ||
| } | ||
|
|
||
| if (braceCount != 0) { | ||
| errors.add(ValidationError( | ||
| "Unbalanced braces", | ||
| 0, | ||
| "Check for missing '{' or '}'" | ||
| )) | ||
| } | ||
|
|
||
| return NanoDSLValidationResult( | ||
| isValid = errors.isEmpty(), | ||
| errors = errors, | ||
| warnings = warnings | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Create a source-only IR JSON for non-JVM platforms | ||
| */ | ||
| private fun createSourceOnlyIR(source: String): String { | ||
| // Extract component name from source | ||
| val componentNameMatch = Regex("""component\s+(\w+):""").find(source) | ||
| val componentName = componentNameMatch?.groupValues?.get(1) ?: "UnknownComponent" | ||
|
|
||
| // Escape the source for JSON | ||
| val escapedSource = source | ||
| .replace("\\", "\\\\") | ||
| .replace("\"", "\\\"") | ||
| .replace("\n", "\\n") | ||
| .replace("\r", "\\r") | ||
| .replace("\t", "\\t") | ||
|
|
||
| return """{"type":"Component","props":{"name":"$componentName"},"source":"$escapedSource"}""" | ||
| } |
There was a problem hiding this comment.
The code validation across platform implementations (JS, WASM, iOS, Android, JVM) contains significant duplication. Consider extracting the common performBasicValidation and createSourceOnlyIR/createMinimalIR functions into a shared common module source set.
This duplication makes maintenance harder - a bug fix or feature addition would need to be replicated across 5 files. The validation logic appears identical across platforms, making this an ideal candidate for code reuse.
| // Check for unclosed blocks | ||
| var parenCount = 0 | ||
| var braceCount = 0 | ||
| for (line in lines) { | ||
| for (char in line) { | ||
| when (char) { | ||
| '(' -> parenCount++ | ||
| ')' -> parenCount-- | ||
| '{' -> braceCount++ | ||
| '}' -> braceCount-- | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (parenCount != 0) { | ||
| errors.add(ValidationError( | ||
| "Unbalanced parentheses", | ||
| 0, | ||
| "Check for missing '(' or ')'" | ||
| )) | ||
| } | ||
|
|
||
| if (braceCount != 0) { | ||
| errors.add(ValidationError( | ||
| "Unbalanced braces", | ||
| 0, | ||
| "Check for missing '{' or '}'" | ||
| )) | ||
| } |
There was a problem hiding this comment.
The bracket validation logic doesn't account for brackets inside string literals. For example, Text("Hello (world)") would be incorrectly flagged for unbalanced parentheses even though they're inside a string.
While full string parsing is complex, at minimum this should be documented as a known limitation in the function's documentation comment.
| let result = line; | ||
|
|
||
| // Highlight strings (quoted text) | ||
| result = result.replace(/"([^"]*)"/g, (match) => semanticChalk.success(match)); | ||
|
|
||
| // Highlight keywords | ||
| for (const keyword of keywords) { | ||
| const regex = new RegExp(`\\b(${keyword})\\b`, 'g'); | ||
| result = result.replace(regex, (match) => semanticChalk.primary(match)); | ||
| } | ||
|
|
||
| // Highlight components | ||
| for (const comp of components) { | ||
| const regex = new RegExp(`\\b(${comp})\\b`, 'g'); | ||
| result = result.replace(regex, (match) => semanticChalk.accent(match)); | ||
| } | ||
|
|
||
| // Highlight types (int, str, bool) | ||
| result = result.replace(/\b(int|str|bool|float)\b/g, (match) => semanticChalk.warning(match)); | ||
|
|
||
| // Highlight state references | ||
| result = result.replace(/\bstate\.(\w+)/g, (match, varName) => | ||
| semanticChalk.muted('state.') + chalk.cyan(varName) | ||
| ); | ||
|
|
||
| return result; |
There was a problem hiding this comment.
The highlightNanoDSL method applies multiple regex replacements sequentially to the same string, which can lead to double-highlighting. For example, if a string contains "state.count", the string highlighting will first convert it to colored text, then the state reference regex will try to match and replace within that already-colored text, potentially breaking the ANSI codes.
Consider applying all regex patterns in a single pass, or ensure that later patterns don't match within already-replaced segments. Alternatively, document this as a known limitation.
| let result = line; | |
| // Highlight strings (quoted text) | |
| result = result.replace(/"([^"]*)"/g, (match) => semanticChalk.success(match)); | |
| // Highlight keywords | |
| for (const keyword of keywords) { | |
| const regex = new RegExp(`\\b(${keyword})\\b`, 'g'); | |
| result = result.replace(regex, (match) => semanticChalk.primary(match)); | |
| } | |
| // Highlight components | |
| for (const comp of components) { | |
| const regex = new RegExp(`\\b(${comp})\\b`, 'g'); | |
| result = result.replace(regex, (match) => semanticChalk.accent(match)); | |
| } | |
| // Highlight types (int, str, bool) | |
| result = result.replace(/\b(int|str|bool|float)\b/g, (match) => semanticChalk.warning(match)); | |
| // Highlight state references | |
| result = result.replace(/\bstate\.(\w+)/g, (match, varName) => | |
| semanticChalk.muted('state.') + chalk.cyan(varName) | |
| ); | |
| return result; | |
| // Split line into quoted and non-quoted segments | |
| const segments: { text: string, quoted: boolean }[] = []; | |
| let lastIndex = 0; | |
| const regex = /"([^"]*)"/g; | |
| let match: RegExpExecArray | null; | |
| while ((match = regex.exec(line)) !== null) { | |
| if (match.index > lastIndex) { | |
| segments.push({ text: line.slice(lastIndex, match.index), quoted: false }); | |
| } | |
| segments.push({ text: match[0], quoted: true }); | |
| lastIndex = regex.lastIndex; | |
| } | |
| if (lastIndex < line.length) { | |
| segments.push({ text: line.slice(lastIndex), quoted: false }); | |
| } | |
| // Highlight each segment appropriately | |
| const highlighted = segments.map(seg => { | |
| if (seg.quoted) { | |
| // Only apply string highlighting | |
| return semanticChalk.success(seg.text); | |
| } else { | |
| let result = seg.text; | |
| // Highlight state references | |
| result = result.replace(/\bstate\.(\w+)/g, (match, varName) => | |
| semanticChalk.muted('state.') + chalk.cyan(varName) | |
| ); | |
| // Highlight keywords | |
| for (const keyword of keywords) { | |
| const regex = new RegExp(`\\b(${keyword})\\b`, 'g'); | |
| result = result.replace(regex, (match) => semanticChalk.primary(match)); | |
| } | |
| // Highlight components | |
| for (const comp of components) { | |
| const regex = new RegExp(`\\b(${comp})\\b`, 'g'); | |
| result = result.replace(regex, (match) => semanticChalk.accent(match)); | |
| } | |
| // Highlight types (int, str, bool, float) | |
| result = result.replace(/\b(int|str|bool|float)\b/g, (match) => semanticChalk.warning(match)); | |
| return result; | |
| } | |
| }); | |
| return highlighted.join(''); |
- IdeaNanoDSLBubble.kt: Remove unused showPreview state, hoist lines variable for reuse in footer - AgentMessageList.kt: Remove unused showPreview state, suppress unused irJson param, reuse lines variable - NanoDSLAgent.kt: Capture exception message as pseudo-error for retry prompt context
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (1)
186-212: Token budget concern already noted.The retry prompt construction embeds full previous code and all errors. Per previous review feedback, consider truncating large code blocks or limiting error count for token efficiency.
🧹 Nitpick comments (2)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2)
91-97: Inefficient flow collection: usecollectinstead oftoList().forEach.Collecting the entire flow into a list before iterating defeats the streaming benefit and buffers the full response in memory unnecessarily.
- llmService.streamPrompt( - userPrompt = prompt, - compileDevIns = false - ).toList().forEach { chunk -> - responseBuilder.append(chunk) - } + llmService.streamPrompt( + userPrompt = prompt, + compileDevIns = false + ).collect { chunk -> + responseBuilder.append(chunk) + }
114-119: Consider logging parse failures for debugging.When validation passes but parsing fails, the failure is silently ignored. While acceptable since IR is optional, logging the failure would aid debugging.
if (validationResult.isValid) { // Try to parse and get IR JSON val parseResult = validator.parse(generatedCode) if (parseResult is NanoDSLParseResult.Success) { irJson = parseResult.irJson + } else if (parseResult is NanoDSLParseResult.Failure) { + logger.debug { "IR parse failed despite valid syntax: ${parseResult.message}" } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt(3 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt(1 hunks)mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.kt(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/AgentMessageList.kt
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaNanoDSLBubble.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
**/*.{kt,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never use hardcoded colors - always use design tokens from the design system
Files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
🧠 Learnings (2)
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`
Applied to files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
📚 Learning: 2025-12-07T08:40:10.850Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.850Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
Applied to files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build
- GitHub Check: Test mpp-core (KMP)
🔇 Additional comments (4)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (4)
38-46: LGTM!The constructor additions are clean -
maxRetrieswith a sensible default and validator instantiation follow good practices for configurability.
151-167: LGTM!Exception handling correctly distinguishes between final and non-final attempts, with appropriate logging and error reporting.
218-238: LGTM!Clean metadata construction using
buildMapwith appropriate conditional inclusion of optional fields.
170-180: LGTM!The best-effort fallback after exhausting retries is reasonable, returning whatever code was generated along with error context.
Summary
Implements GitHub Issue #499: Enhance
NanoDSLAgentwith two critical features:CodingAgentRendererPart 1: Validation & Retry Mechanism
New Files Created:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/parser/NanoDSLValidator.kt- expect classactualimplementations for JVM, Android, JS, WASM, iOSFeatures:
Part 2: Rendering Integration
Interface Changes:
renderNanoDSL(source, irJson?, metadata)toCodingAgentRendererTimelineItem.NanoDSLItemwith fields: source, irJson, componentName, generationAttempts, isValid, warningsJsNanoDSLDataand updated JS exportsRenderer Implementations:
CliRenderer.tsTuiRenderer.tsServerRenderer.tsComposeRenderer.ktNanoDSLTimelineItemcomponentJewelRenderer.ktIdeaNanoDSLBubblecomponentmpp-core.tsnanodslmessage typeTesting
Build verification:
Checklist
Closes #499
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.