-
Notifications
You must be signed in to change notification settings - Fork 480
feat: Add LLM-based Run Config Analysis with IDE-like Run Button #531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Implements dynamic run configuration generation using AI analysis, addressing #529. Features: - LLM-based project analysis with streaming output - Floating Run Button in top-right corner (IDE-like UX) - Real-time command execution with live streaming output - Stop/Cancel functionality for running commands - RunOutputDock for displaying analysis and execution logs Key Components: - LLMRunConfigAnalyzer: Streams AI analysis of project structure - RunConfigService: Manages run configs with live execution support - FloatingRunButton: IDE-style floating action button - RunOutputDock: Terminal-like output display for analysis and execution The system automatically analyzes project files (package.json, build.gradle, etc.) and uses LLM to intelligently generate run configurations, making it easy to discover and execute project commands without manual configuration. Closes #529
|
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. 📝 WalkthroughWalkthroughAdds a run-configuration discovery and execution system: core models and storage, an LLM-driven streaming analyzer, orchestration service with execution and persistence, platform-specific process readers, Compose UI components and view model, a /gen-actions slash command, and cross-platform tooltip/tooltip-wrapper implementations. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI / ViewModel
participant Service as RunConfigService
participant Analyzer as LLMRunConfigAnalyzer
participant LLM as LLM Service
participant Storage as File Storage
participant Executor as Shell Executor
rect rgb(235, 245, 255)
Note over UI, Storage: Analysis flow (streaming)
UI->>Service: analyzeProject()
Service->>Analyzer: analyzeStreaming()
Analyzer->>Analyzer: gatherProjectContext()
Analyzer->>LLM: send prompt / stream responses
LLM-->>Analyzer: streaming text (progress + JSON block)
Analyzer->>Analyzer: extract JSON block -> parseRunConfigs()
Analyzer-->>Service: emit Complete(configs)
Service->>Storage: saveConfigs()
Service-->>UI: update state & configs
end
rect rgb(235, 255, 235)
Note over UI, Executor: Execution flow
UI->>Service: execute(configId)
Service->>Executor: start process (live or blocking)
Executor->>Service: stream stdout/stderr
Service-->>UI: forward output events
Executor-->>Service: exitCode/result
Service-->>UI: emit RunConfigResult
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 Augment PR SummarySummary: This PR adds AI-driven run configuration discovery and an IDE-like Run UX to AutoDev. Changes:
Technical Notes: Uses Kotlin Flow streaming for LLM reasoning/output and StateFlow for UI state; shell execution can use live sessions when supported. 🤖 Was this summary useful? React with 👍 or 👎 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| // Parse final response | ||
| val fullResponse = responseBuffer.toString() | ||
| logger.debug { "LLM response:\n$fullResponse" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fullResponse can contain project config contents (and potentially secrets), so logging the entire LLM response risks sensitive data exposure. (Guideline: no_sensitive_logging)
🤖 Was this useful? React with 👍 or 👎
| emit(AnalysisEvent.Error("Could not parse run configurations from AI response")) | ||
| } | ||
|
|
||
| } catch (e: Exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This catch (e: Exception) will also catch CancellationException, which can prevent proper coroutine cancellation and streaming cleanup. (Guideline: streaming_cleanup)
🤖 Was this useful? React with 👍 or 👎
|
|
||
| // Get top-level file list | ||
| try { | ||
| val topFiles = fileSystem.listFiles(projectPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| stdoutReader.join(2000) | ||
| stderrReader.join(2000) | ||
|
|
||
| if (!completed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| var cancelled = false | ||
|
|
||
| // Read stdout in a separate thread | ||
| val stdoutReader = Thread { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spawning raw Threads for stdout/stderr here can leak threads for long-running commands (they’re non-daemon and not reliably stopped), and cancelled is shared across threads without synchronization. (Guideline: thread_safety)
🤖 Was this useful? React with 👍 or 👎
| projectPath: String, | ||
| llmService: KoogLLMService? = null | ||
| ) { | ||
| private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ViewModel owns a CoroutineScope but there’s no cancellation/dispose hook; if CodingAgentPage recreates the VM (workspace/LLM change), the old scope may keep running and leak work/resources. (Guideline: no_memory_leaks)
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (8)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/chat/TopBarMenu.kt (1)
66-92: Consider usingexpect/actualpattern for platform-specific implementations.The platform detection logic using
Platform.isAndroidandPlatform.isWasmworks but violates the coding guidelines, which specify using theexpect/actualpattern for platform-specific code. Refactoring to separate platform-specific implementations would improve type safety and compile-time verification.The clarifying comment on line 67 is helpful, but the underlying structure would benefit from this pattern.
🔎 Suggested refactoring approach
Instead of runtime platform checks, create platform-specific expect/actual declarations:
Common (commonMain):
@Composable expect fun TopBarMenu( hasHistory: Boolean, hasDebugInfo: Boolean, // ... other parameters )Android (androidMain):
@Composable actual fun TopBarMenu( // ... parameters ) { TopBarMenuMobile( // ... pass parameters ) }WASM (wasmJsMain):
@Composable actual fun TopBarMenu( // ... parameters ) { TopBarMenuDesktop( // ... pass parameters ) }This approach provides compile-time guarantees and improves code organization.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.kt (1)
48-58: String-based type detection works but could be more explicit.Using
title.contains("Analysis")is pragmatic but couples the behavior to the title string. Consider accepting aisAnalysis: Booleanparameter directly if this distinction becomes more important in the future.mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (1)
145-209: Well-implemented run configuration menu with proper state handling.The menu correctly displays all configurations with appropriate icons and colors based on running/default state. The toggle between run and stop actions per configuration works as expected.
Consider replacing
printlnat line 182 with a proper logger for production code.mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/LLMRunConfigAnalyzer.kt (1)
56-85: JSON block detection could be refined but is functionally correct.The
inJsonBlocktracking filters JSON from display output. The end detection at lines 82-84 doesn't resetinJsonBlockto false, meaning once JSON starts, all subsequent content is hidden. This is likely intentional since JSON typically comes at the end, but if the LLM adds text after JSON, it won't be displayed.🔎 Potential refinement if post-JSON text display is needed
// Check if JSON block ended if (inJsonBlock && chunk.contains("```") && !chunk.contains("```json")) { - // JSON block might have ended, but keep inJsonBlock true to avoid further text + inJsonBlock = false }mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt (4)
187-188: Consider documenting the job capture pattern.Capturing the job via
currentCoroutineContext()[Job]insidecoroutineScopeworks but is non-obvious. A brief comment explaining this is forstopRunning()coordination would improve maintainability.
271-273: Mutable vars accessed without synchronization.
currentSession,currentProcess, andexecutionJobare accessed from bothexecute()andstopRunning()potentially on different threads. While the cleanup operations are idempotent, consider usingAtomicReferenceor ensuring access happens on a single dispatcher for cleaner concurrency semantics.
432-439: Returningsuccess=trueon exception may be misleading.When an exception occurs (e.g., timeout), returning
success=truecould mislead callers. Consider using a distinct result state (e.g.,RunConfigResultwithstatus = TIMEOUTorRUNNING) to differentiate from actual successful completion.
265-268: Redundant cleanup in nested finally blocks.Both
executeWithLiveStreaming()(lines 265-268) and the outerexecute()(lines 230-232) null outcurrentSessionandcurrentProcess. The outer finally is sufficient.🔎 Proposed simplification
} - } finally { - currentSession = null - currentProcess = null - } + } }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/LLMRunConfigAnalyzer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfig.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/chat/TopBarMenu.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/icons/AutoDevComposeIcons.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunButton.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunConfigViewModel.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.ktmpp-ui/src/jsMain/typescript/processors/SlashCommandProcessor.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/chat/TopBarMenu.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfig.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunButton.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/icons/AutoDevComposeIcons.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunConfigViewModel.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/LLMRunConfigAnalyzer.kt
**/*.{kt,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never use hardcoded colors - always use design tokens from the design system
Files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/chat/TopBarMenu.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfig.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunButton.ktmpp-ui/src/jsMain/typescript/processors/SlashCommandProcessor.tsmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/icons/AutoDevComposeIcons.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunConfigViewModel.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/LLMRunConfigAnalyzer.kt
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
When modifying
CodingAgentRenderer, update ALL TypeScript implementations:BaseRenderer.ts,CliRenderer.ts,ServerRenderer.ts, andTuiRenderer.ts
Files:
mpp-ui/src/jsMain/typescript/processors/SlashCommandProcessor.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/processors/SlashCommandProcessor.ts
🧠 Learnings (3)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
Applied to files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`
Applied to files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
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-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.kt
🧬 Code graph analysis (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.kt (2)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.kt (1)
RunOutputDock(37-141)mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (1)
FloatingRunButton(49-212)
⏰ 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). (3)
- GitHub Check: Agent
- GitHub Check: Build
- GitHub Check: Test mpp-core (KMP)
🔇 Additional comments (34)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/icons/AutoDevComposeIcons.kt (1)
117-119: Code implementation is correct; no changes needed.The additions follow the established pattern correctly, and the inconsistency flagged in the AI summary is valid—no duplicate declarations exist. Both
Icons.Default.ScienceandIcons.Default.Extensionare available in the Material Icons Extended library, which the project already includes as a dependency (compose.materialIconsExtended).mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunOutputDock.kt (3)
37-47: Well-structured component signature with appropriate defaults.The composable signature follows Compose best practices with sensible defaults for optional parameters and clear callback naming.
87-113: Correct conditional rendering for Stop button.The logic properly hides the Stop button during analysis and when
onStopcallback is null, which aligns with the PR's requirement to support both analysis and run output modes.
116-137: LGTM!Dynamic height based on content type and appropriate placeholder messages provide good UX. The PlatformTerminalDisplay integration enables cross-platform compatibility as documented.
mpp-ui/src/jsMain/typescript/processors/SlashCommandProcessor.ts (5)
114-120: LGTM!Command registration follows the established pattern with appropriate description and useful aliases.
504-538: Consistent error handling and argument parsing.The handler follows the same validation pattern as other commands in this file, with proper error returns for invalid project paths and missing LLM configuration.
575-616: Well-structured action generation with informative output.The implementation provides clear progress feedback, formatted action listings with category icons, and helpful guidance for the no-actions case.
618-625: LGTM!Error handling follows the established pattern with proper logging and user-friendly error messages.
628-644: Clean helper function with sensible defaults.The category-to-icon mapping is comprehensive and the fallback ensures graceful handling of unknown categories.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (2)
49-62: LGTM!The composable signature provides all necessary state and callbacks for the floating run button functionality, with appropriate defaults.
80-130: Good use of animation and tooltip for analyzing state.The spinning animation provides clear visual feedback, and the tooltip with scrollable analysis log gives users insight into the LLM reasoning process without cluttering the UI.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunConfigViewModel.kt (3)
74-106: Analysis method handles streaming progress appropriately.The method correctly clears previous state, streams progress updates, and handles errors from the service. The summary of discovered configurations at the end provides useful feedback.
115-150: LGTM!The run and stop methods provide proper lifecycle management with clear status messages for success, failure, and cancellation states.
152-209: LGTM!Utility methods properly delegate to the service layer and maintain consistent state management.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunButton.kt (4)
60-165: Well-organized dropdown with type grouping.The CONFIGURED state provides a clean UX with the main run button for quick access and a dropdown with configs grouped by type. The dividers and headers help users navigate multiple configurations.
216-251: LGTM!The analyzing state animation is consistent with the FloatingRunButton implementation, providing a unified visual experience.
302-372: LGTM!The menu item provides comprehensive information with proper visual hierarchy, overflow handling, and state indicators.
374-408: LGTM!Type-to-icon and type-to-color mappings are comprehensive and use the design system tokens (AutoDevColors) as per coding guidelines.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/CodingAgentPage.kt (5)
18-20: LGTM!New imports for run configuration components are correctly added.
142-157: ViewModel state collection is comprehensive.All necessary state flows are properly collected for UI consumption. However, note that the ViewModel's coroutine scope should be cancelled when no longer needed (flagged in RunConfigViewModel.kt review).
167-171: LGTM!The auto-open logic correctly triggers for any relevant activity (running, analyzing, or having output), providing immediate feedback to users.
263-279: Correct dock configuration with appropriate callbacks.The dock correctly displays either analysis log or run output based on state, and the onStop callback is properly conditioned to only appear during run execution (not analysis).
569-585: Well-integrated floating run button with proper conditional visibility.The button is appropriately shown only for CODING agent mode and correctly wired to the ViewModel actions. The positioning with
Alignment.TopEndaligns with the PR objective of an IDE-like floating Run button.mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfig.kt (3)
16-50: LGTM!Well-designed data class with appropriate fields and sensible defaults. The separation of required vs optional fields with defaults follows Kotlin best practices.
52-119: LGTM!The enum types are comprehensive, well-documented, and cover the expected use cases for run configuration management.
121-164: LGTM!RunConfigResult captures execution outcomes comprehensively, and RunConfigStorage is well-designed for persistence with version compatibility.
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/LLMRunConfigAnalyzer.kt (5)
25-35: LGTM!The class setup with appropriate JSON configuration for lenient parsing handles the variability of LLM responses well.
126-177: LGTM!Context gathering is thorough with appropriate filtering and truncation to manage LLM context limits. The list of config files covers common project types (Node.js, Gradle, Maven, Cargo, Go, Python, etc.).
182-228: Well-crafted LLM prompt with clear instructions.The prompt provides comprehensive context and explicit output format requirements. The practical guidance (max 10 commands, package manager detection, gradlew preference) helps generate useful configurations.
233-276: LGTM!JSON parsing with fallback for raw arrays is robust. The ID generation and default assignment logic are sensible for typical project workflows.
279-311: LGTM!The sealed class for events provides type-safe handling, and the supporting types are appropriately encapsulated as private implementation details.
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt (3)
48-66: LGTM!State management using
MutableStateFlowwith read-onlyStateFlowexposure follows Kotlin best practices for observable state.
574-579:reset()doesn't clear persisted configs.This method clears in-memory state but leaves
.xiuper/run-configs.jsonintact. Callinginitialize()afterward will reload the old configs. Is this the intended behavior, or shouldreset()also delete the storage file?
530-569: LGTM!Good persistence implementation with proper directory creation, null safety for missing files, and exception handling that doesn't crash the application.
| LLMRunConfigAnalyzer(projectPath, fileSystem, it) | ||
| } | ||
|
|
||
| private val configStoragePath = "$projectPath/.xiuper/run-configs.json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the file to see the actual code
cat -n mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt | head -60Repository: phodal/auto-dev
Length of output: 2854
🏁 Script executed:
# Check if this is a multiplatform project and what platforms are targeted
find . -name "build.gradle.kts" -o -name "build.gradle" -o -name "gradle.properties" | head -5Repository: phodal/auto-dev
Length of output: 186
🏁 Script executed:
# Search for existing path handling patterns in the codebase
rg "File\.separator|Paths\.get|Path\.of|normalize|replaceBackslashes" --type=kt --max-count=20 -C1Repository: phodal/auto-dev
Length of output: 85
🏁 Script executed:
# Check if there's any platform-specific code in the project
rg "expect|actual" --type=kt mpp-core/ --max-count=10 -C1Repository: phodal/auto-dev
Length of output: 85
🏁 Script executed:
# Check the project structure to understand target platforms
cat -n mpp-core/build.gradle.kts 2>/dev/null | head -80Repository: phodal/auto-dev
Length of output: 2540
🏁 Script executed:
# Search for ToolFileSystem interface/class definition and usage
find . -name "*.kt" -type f | xargs grep -l "ToolFileSystem" | head -10Repository: phodal/auto-dev
Length of output: 897
🏁 Script executed:
# Look at DefaultToolFileSystem implementation
find . -name "DefaultToolFileSystem.kt" -type f -exec cat {} \;Repository: phodal/auto-dev
Length of output: 41
🏁 Script executed:
# Search for how paths are constructed elsewhere in the codebase
find . -name "*.kt" -type f | xargs grep -n "\.xiuper\|projectPath.*/" | head -20Repository: phodal/auto-dev
Length of output: 2938
🏁 Script executed:
# Check if there are other instances of path joining in RunConfigService
find . -name "RunConfigService.kt" -exec cat -n {} \;Repository: phodal/auto-dev
Length of output: 24971
🏁 Script executed:
# Check ToolFileSystem interface for available methods
cat mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/filesystem/ToolFileSystem.ktRepository: phodal/auto-dev
Length of output: 9519
🏁 Script executed:
# Look at how DefaultToolFileSystem implements path handling
cat mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/filesystem/DefaultToolFileSystem.ktRepository: phodal/auto-dev
Length of output: 180
🏁 Script executed:
# Check if there are platform-specific implementations of path joining
find . -name "*.kt" -type f | xargs grep -l "resolve\|joinPath\|File\.separator" | head -15Repository: phodal/auto-dev
Length of output: 1358
🏁 Script executed:
# Check how Path is being used in resolvePath and if it normalizes separators correctly
grep -A5 "Path(" mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/filesystem/ToolFileSystem.kt | head -20Repository: phodal/auto-dev
Length of output: 655
🏁 Script executed:
# Verify that the issue affects the actual file operations
grep -B2 -A2 "fileSystem\." mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt | head -30Repository: phodal/auto-dev
Length of output: 925
Use platform-independent path joining instead of hardcoded "/" separator.
Lines 46, 194, and 533 use hardcoded "/" for path construction. Since this is multiplatform code (commonMain) targeting JVM, Android, iOS, and other platforms, paths should use the available fileSystem.resolvePath() method which handles platform separators correctly via kotlinx.io.
Replace:
- Line 46:
"$projectPath/.xiuper/run-configs.json"withfileSystem.resolvePath(".xiuper/run-configs.json") - Line 533:
"$projectPath/.xiuper"withfileSystem.resolvePath(".xiuper") - Line 194:
"$projectPath/${config.workingDir}"withfileSystem.resolvePath(config.workingDir)
🤖 Prompt for AI Agents
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt
lines 46, 194, 533: replace hardcoded path string interpolation that uses "/"
separators with platform-independent fileSystem.resolvePath(...) calls — change
the configStoragePath to use fileSystem.resolvePath(".xiuper/run-configs.json"),
change any "$projectPath/${config.workingDir}" usage to
fileSystem.resolvePath(config.workingDir), and change "$projectPath/.xiuper" to
fileSystem.resolvePath(".xiuper"); ensure the existing fileSystem instance is in
scope (or inject/rename it consistently) so these calls compile across targets.
| when (event) { | ||
| is AnalysisEvent.Progress -> onProgress(event.message) | ||
| is AnalysisEvent.Complete -> discoveredConfigs = event.configs | ||
| is AnalysisEvent.Error -> { | ||
| logger.warn { "LLM analysis failed: ${event.message}" } | ||
| _errorMessage.value = event.message | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
State not updated to ERROR on analysis failure.
When AnalysisEvent.Error is received, only _errorMessage is set. The _state remains ANALYZING because no exception is thrown. This can leave the UI stuck in the analyzing state indefinitely.
🔎 Proposed fix
is AnalysisEvent.Error -> {
logger.warn { "LLM analysis failed: ${event.message}" }
_errorMessage.value = event.message
+ _state.value = RunConfigState.ERROR
}🤖 Prompt for AI Agents
In
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt
around lines 109 to 117, the handler for AnalysisEvent.Error only sets
_errorMessage and leaves _state as ANALYZING; update the state to the
appropriate error state so the UI won't remain stuck. In the AnalysisEvent.Error
branch set _state.value = RunConfigState.ERROR (or the project's equivalent
error enum/state) in addition to logging and setting _errorMessage, ensuring any
necessary cleanup or flags are updated so analysis is no longer considered
ongoing.
| var cancelled = false | ||
|
|
||
| // Read stdout in a separate thread | ||
| val stdoutReader = Thread { | ||
| try { | ||
| process.inputStream.bufferedReader().use { reader -> | ||
| val buffer = CharArray(1024) | ||
| while (!cancelled) { | ||
| val charsRead = reader.read(buffer) | ||
| if (charsRead == -1) break | ||
| val chunk = String(buffer, 0, charsRead) | ||
| outputBuilder.append(chunk) | ||
| onOutput(chunk) | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Stream closed, ignore | ||
| } | ||
| }.apply { start() } | ||
|
|
||
| // Read stderr in a separate thread | ||
| val stderrReader = Thread { | ||
| try { | ||
| process.errorStream.bufferedReader().use { reader -> | ||
| val buffer = CharArray(1024) | ||
| while (!cancelled) { | ||
| val charsRead = reader.read(buffer) | ||
| if (charsRead == -1) break | ||
| val chunk = String(buffer, 0, charsRead) | ||
| outputBuilder.append(chunk) | ||
| onOutput(chunk) | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Stream closed, ignore | ||
| } | ||
| }.apply { start() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thread visibility issue with cancelled flag.
The cancelled variable is accessed from multiple threads without synchronization. Changes made by the main thread may not be visible to the reader threads due to CPU caching.
🔎 Proposed fix - use @volatile or AtomicBoolean
- var cancelled = false
+ val cancelled = java.util.concurrent.atomic.AtomicBoolean(false)
// Read stdout in a separate thread
val stdoutReader = Thread {
try {
process.inputStream.bufferedReader().use { reader ->
val buffer = CharArray(1024)
- while (!cancelled) {
+ while (!cancelled.get()) {
val charsRead = reader.read(buffer)
if (charsRead == -1) break
val chunk = String(buffer, 0, charsRead)
outputBuilder.append(chunk)
onOutput(chunk)
}
}
} catch (e: Exception) {
// Stream closed, ignore
}
}.apply { start() }
// Read stderr in a separate thread
val stderrReader = Thread {
try {
process.errorStream.bufferedReader().use { reader ->
val buffer = CharArray(1024)
- while (!cancelled) {
+ while (!cancelled.get()) {
val charsRead = reader.read(buffer)
if (charsRead == -1) break
...
}
}
} catch (e: Exception) {
// Stream closed, ignore
}
}.apply { start() }
// ... later in catch block:
} catch (e: kotlinx.coroutines.CancellationException) {
- cancelled = true
+ cancelled.set(true)
// Interrupt readersCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt
around lines 330 to 366, the local var `cancelled` is accessed by multiple
threads without synchronization which can cause visibility issues; change `var
cancelled = false` to a thread-safe flag (either declare it as `@Volatile
private var cancelled = false` or replace it with an `AtomicBoolean(false)`),
update reader loops to check `cancelled.get()` if using AtomicBoolean (or keep
`while (!cancelled)` if volatile), and ensure any code that sets cancellation
uses `cancelled = true` for volatile or `cancelled.set(true)` for AtomicBoolean
so the reader threads reliably observe the change.
| fun setDefaultConfig(configId: String) { | ||
| val config = _configs.value.find { it.id == configId } | ||
| if (config != null) { | ||
| // Update default flags | ||
| _configs.value = _configs.value.map { c -> | ||
| c.copy(isDefault = c.id == configId) | ||
| } | ||
| _defaultConfig.value = config.copy(isDefault = true) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Add a custom run configuration | ||
| */ | ||
| fun addConfig(config: RunConfig) { | ||
| _configs.value = _configs.value + config.copy(source = RunConfigSource.USER_DEFINED) | ||
| if (config.isDefault || _defaultConfig.value == null) { | ||
| _defaultConfig.value = config | ||
| } | ||
| _state.value = RunConfigState.CONFIGURED | ||
| } | ||
|
|
||
| /** | ||
| * Remove a run configuration | ||
| */ | ||
| fun removeConfig(configId: String) { | ||
| _configs.value = _configs.value.filter { it.id != configId } | ||
| if (_defaultConfig.value?.id == configId) { | ||
| _defaultConfig.value = _configs.value.firstOrNull { it.isDefault } | ||
| ?: _configs.value.firstOrNull() | ||
| } | ||
| if (_configs.value.isEmpty()) { | ||
| _state.value = RunConfigState.NOT_CONFIGURED | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config modifications are not persisted.
setDefaultConfig(), addConfig(), and removeConfig() update in-memory state but don't call saveConfigs(). User changes will be lost on application restart.
🔎 Proposed fix - persist changes
Since saveConfigs() is a suspend function, these methods need to be converted to suspend functions or trigger persistence asynchronously:
- fun setDefaultConfig(configId: String) {
+ suspend fun setDefaultConfig(configId: String) {
val config = _configs.value.find { it.id == configId }
if (config != null) {
// Update default flags
_configs.value = _configs.value.map { c ->
c.copy(isDefault = c.id == configId)
}
_defaultConfig.value = config.copy(isDefault = true)
+ saveConfigs()
}
}
- fun addConfig(config: RunConfig) {
+ suspend fun addConfig(config: RunConfig) {
_configs.value = _configs.value + config.copy(source = RunConfigSource.USER_DEFINED)
if (config.isDefault || _defaultConfig.value == null) {
_defaultConfig.value = config
}
_state.value = RunConfigState.CONFIGURED
+ saveConfigs()
}
- fun removeConfig(configId: String) {
+ suspend fun removeConfig(configId: String) {
_configs.value = _configs.value.filter { it.id != configId }
if (_defaultConfig.value?.id == configId) {
_defaultConfig.value = _configs.value.firstOrNull { it.isDefault }
?: _configs.value.firstOrNull()
}
if (_configs.value.isEmpty()) {
_state.value = RunConfigState.NOT_CONFIGURED
}
+ saveConfigs()
}🤖 Prompt for AI Agents
In
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt
around lines 484 to 518, the methods setDefaultConfig, addConfig, and
removeConfig mutate in-memory state but do not persist changes via
saveConfigs(), so user changes are lost; convert each method to a suspend
function (or alternatively launch a coroutine) and invoke saveConfigs() after
updating _configs/_defaultConfig/_state, and update all call sites to handle the
suspend signature (or ensure the coroutine is launched on the appropriate scope
and error-handled) so modifications are persisted.
| class RunConfigViewModel( | ||
| projectPath: String, | ||
| llmService: KoogLLMService? = null | ||
| ) { | ||
| private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) | ||
|
|
||
| private val service = RunConfigService( | ||
| projectPath = projectPath, | ||
| fileSystem = DefaultToolFileSystem(projectPath = projectPath), | ||
| shellExecutor = DefaultShellExecutor(), | ||
| llmService = llmService | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing CoroutineScope cancellation may cause resource leaks.
The scope CoroutineScope is created with SupervisorJob() but there's no mechanism to cancel it when the ViewModel is no longer needed. In a Compose context, consider using rememberCoroutineScope() at the call site or implementing a dispose()/close() method that cancels the scope.
🔎 Proposed fix: Add cleanup method
class RunConfigViewModel(
projectPath: String,
llmService: KoogLLMService? = null
) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ /**
+ * Cancel all ongoing operations and cleanup resources.
+ * Should be called when the ViewModel is no longer needed.
+ */
+ fun dispose() {
+ scope.cancel()
+ }🤖 Prompt for AI Agents
In
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/RunConfigViewModel.kt
around lines 28–39, the CoroutineScope named scope is created with
SupervisorJob() but never cancelled; add a public cleanup method (e.g.,
close()/dispose()/onCleared()) that calls scope.cancel() (and any
Service/Executor cleanup if available) to avoid leaking coroutines, or
alternatively accept an external CoroutineScope at construction so lifecycle is
managed by the caller; ensure the new method is called from the Compose host or
where the ViewModel is disposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements dynamic run configuration generation using AI analysis, introducing an IDE-like run button interface for executing project commands. The implementation includes LLM-based project analysis with streaming output, real-time command execution with live output streaming, and process lifecycle management with stop/cancel functionality.
Key Changes:
- LLM-based run configuration analysis with streaming output to provide real-time feedback
- IDE-style floating run button with state management (Configure, Analyzing, Run/Stop, Error)
- Real-time command execution supporting long-running processes with incremental output streaming
- RunOutputDock component for displaying both AI analysis logs and command execution output
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| RunConfig.kt | Data models for run configurations including types, sources, states, and storage format |
| LLMRunConfigAnalyzer.kt | LLM-based analyzer for discovering run configurations with streaming support |
| RunConfigService.kt | Service layer managing config lifecycle, execution, and process control |
| RunConfigViewModel.kt | ViewModel exposing state flows and handling user interactions |
| RunOutputDock.kt | Terminal-like UI component for displaying output with platform-specific rendering |
| RunButton.kt | Dropdown button component showing run configurations with grouping and state |
| FloatingRunButton.kt | Floating action button providing quick access to run configurations |
| CodingAgentPage.kt | Integration of run config features into the main coding agent UI |
| SlashCommandProcessor.ts | Added /gen-actions command for generating IDE actions |
| AutoDevComposeIcons.kt | Added Science and Extension icons for GenAction support |
| TopBarMenu.kt | Updated comment clarifying WASM-only usage |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| val suggestions = json.decodeFromString<List<LLMRunConfigSuggestion>>(jsonStr) | ||
| suggestions.mapIndexed { index, suggestion -> | ||
| RunConfig( | ||
| id = "ai-${suggestion.name.lowercase().replace(Regex("[^a-z0-9]"), "-")}-$index", |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ID generation uses a simple concatenation with the index, which could lead to collisions if the same name is analyzed multiple times or if names are very similar. Consider using a more robust ID generation strategy, such as including a timestamp or UUID component to ensure uniqueness across multiple analysis runs.
|
|
||
| val shellConfig = ShellExecutionConfig( | ||
| workingDirectory = workDir, | ||
| timeoutMs = 600000L, // 10 minutes for long-running tasks |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout is set to 10 minutes (600000ms) for all commands. However, some long-running server processes like Spring Boot's bootRun may need to run indefinitely. The timeout should either be configurable per RunConfig, or there should be a way to specify that certain commands should not have a timeout. Consider adding a timeout field to RunConfig or using a special value (like -1 or 0) to indicate no timeout.
| val shellConfig = ShellExecutionConfig( | |
| workingDirectory = workDir, | |
| timeoutMs = 600000L, // 10 minutes for long-running tasks | |
| // Determine timeout per run config, defaulting to 10 minutes. | |
| // If RUN_CONFIG_TIMEOUT_MS is set to 0 or a negative value, it will be | |
| // passed through to ShellExecutionConfig to indicate no timeout. | |
| val timeoutMs = config.env["RUN_CONFIG_TIMEOUT_MS"]?.toLongOrNull() | |
| ?: 600000L | |
| val shellConfig = ShellExecutionConfig( | |
| workingDirectory = workDir, | |
| timeoutMs = timeoutMs, |
| if (inJsonBlock && chunk.contains("```") && !chunk.contains("```json")) { | ||
| // JSON block might have ended, but keep inJsonBlock true to avoid further text | ||
| } |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic has a potential issue where if the JSON block contains nested backticks or the pattern "```" appears in comments or strings, it may incorrectly mark the end of the JSON block. This could cause JSON parsing to fail. Consider using a more robust JSON extraction approach, such as counting opening and closing braces to identify complete JSON objects.
| val jsonPattern = Regex("```json\\s*([\\s\\S]*?)```", RegexOption.IGNORE_CASE) | ||
| val match = jsonPattern.find(response) | ||
|
|
||
| val jsonStr = match?.groupValues?.get(1)?.trim() | ||
| ?: run { | ||
| // Try to find raw JSON array | ||
| val arrayPattern = Regex("\\[\\s*\\{[\\s\\S]*?}\\s*]") | ||
| arrayPattern.find(response)?.value |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex pattern will only match if there's no closing backticks after the JSON block, which is inconsistent with the JSON extraction logic. The pattern should consistently handle the markdown code fence format. Consider revising to handle both complete and incomplete JSON blocks more reliably.
| val jsonPattern = Regex("```json\\s*([\\s\\S]*?)```", RegexOption.IGNORE_CASE) | |
| val match = jsonPattern.find(response) | |
| val jsonStr = match?.groupValues?.get(1)?.trim() | |
| ?: run { | |
| // Try to find raw JSON array | |
| val arrayPattern = Regex("\\[\\s*\\{[\\s\\S]*?}\\s*]") | |
| arrayPattern.find(response)?.value | |
| // Match a ```json fenced block, allowing for a missing closing fence by stopping at ``` or end of input. | |
| val jsonPattern = Regex("```json\\s*([\\s\\S]*?)(```|$)", RegexOption.IGNORE_CASE) | |
| val match = jsonPattern.find(response) | |
| val jsonStr = match?.groupValues?.get(1)?.trim() | |
| ?: run { | |
| // Try to find raw JSON array, either bare or inside a (possibly incomplete) ```json fence | |
| val arrayPattern = Regex( | |
| "```json\\s*(\\[\\s*\\{[\\s\\S]*?}\\s*])(```|$)|\\[\\s*\\{[\\s\\S]*?}\\s*]", | |
| RegexOption.IGNORE_CASE | |
| ) | |
| val arrayMatch = arrayPattern.find(response) | |
| // Prefer the captured array (group 1, fenced case); otherwise fall back to the full match | |
| arrayMatch?.groups?.get(1)?.value?.trim() | |
| ?: arrayMatch?.value?.trim() |
| modifier: Modifier = Modifier | ||
| ) { | ||
| val isAnalysis = title.contains("Analysis", ignoreCase = true) |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This determines whether the title represents analysis output or run output by checking if the title contains "Analysis" (case-insensitive). This is fragile and could break if the title is changed or localized. Consider passing an explicit boolean flag or enum parameter to indicate the type of output being displayed instead of relying on string matching.
| modifier: Modifier = Modifier | |
| ) { | |
| val isAnalysis = title.contains("Analysis", ignoreCase = true) | |
| modifier: Modifier = Modifier, | |
| isAnalysis: Boolean = title.contains("Analysis", ignoreCase = true), | |
| ) { |
| private var currentProcess: Process? = null | ||
| private var executionJob: kotlinx.coroutines.Job? = null |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These mutable properties are used to manage the lifecycle of running processes. However, they are not thread-safe and could lead to race conditions in concurrent scenarios. Consider using atomic references or synchronization mechanisms to ensure thread safety, especially since the execution and stopping of processes may happen from different coroutine contexts.
| val stdoutReader = Thread { | ||
| try { | ||
| process.inputStream.bufferedReader().use { reader -> | ||
| val buffer = CharArray(1024) | ||
| while (!cancelled) { | ||
| val charsRead = reader.read(buffer) | ||
| if (charsRead == -1) break | ||
| val chunk = String(buffer, 0, charsRead) | ||
| outputBuilder.append(chunk) | ||
| onOutput(chunk) | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Stream closed, ignore | ||
| } | ||
| }.apply { start() } | ||
|
|
||
| // Read stderr in a separate thread | ||
| val stderrReader = Thread { | ||
| try { | ||
| process.errorStream.bufferedReader().use { reader -> | ||
| val buffer = CharArray(1024) | ||
| while (!cancelled) { | ||
| val charsRead = reader.read(buffer) | ||
| if (charsRead == -1) break | ||
| val chunk = String(buffer, 0, charsRead) | ||
| outputBuilder.append(chunk) | ||
| onOutput(chunk) | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Stream closed, ignore | ||
| } | ||
| }.apply { start() } |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Threads are created but there's no error handling if the threads fail to start or encounter issues during execution. Additionally, using daemon threads or properly managing thread lifecycle would be more appropriate here. Consider using Kotlin's coroutine-based approach with Dispatchers.IO instead of creating raw threads, which would provide better resource management and error handling.
| success = true, | ||
| message = "Process started (may still be running)", |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout handling here is potentially confusing. When the timeout is reached, the method returns success=true with a message "Process started (may still be running)", but this doesn't actually indicate success of the command execution. This could mislead users into thinking the command completed successfully when it actually timed out. Consider returning a different status or adding a field to RunConfigResult to distinguish between completed, timed out, and still running states.
| success = true, | |
| message = "Process started (may still be running)", | |
| success = false, | |
| message = "Process did not complete within timeout (may still be running)", |
| println("[RunConfigViewModel] Starting project analysis...") | ||
|
|
||
| service.analyzeProject { progress -> | ||
| progressMessage = progress.take(50) |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The progress message is truncated to 50 characters, which may result in incomplete or misleading information being displayed to the user. This arbitrary limit could cut off important context. Consider either increasing this limit to a more reasonable value, or using a different truncation strategy that preserves complete words or sentences.
| } | ||
| HorizontalDivider() | ||
| // Dynamic height based on content type | ||
| val dockHeight = if (isAnalysis) 220.dp else 180.dp |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fixed height values (220.dp for analysis, 180.dp for run output) may not be appropriate for all screen sizes or content volumes. Consider making the height responsive or configurable, or using a percentage of the available space. This would provide better flexibility across different device sizes and user preferences.
- Implement platform-specific ProcessOutputReader for JVM, Android, iOS, JS, and WASM - Add TooltipWrapper component with desktop tooltip support and fallback on other platforms - Refactor RunConfigService to use platform-specific process handling - Replace direct Process usage with platform-agnostic processHandle - Add process timeout handling and cancellation support across platforms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (9)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt (5)
46-46: Use platform-independent path joining instead of hardcoded "/" separator.This is multiplatform code targeting JVM, Android, iOS, JS, and WASM. The hardcoded "/" separator may cause issues on platforms with different path conventions. Use
fileSystem.resolvePath()or a similar platform-agnostic approach.Also applies to line 194 (
"$projectPath/${config.workingDir}") and line 441 ("$projectPath/.xiuper").
109-117: State not updated to ERROR on analysis failure.When
AnalysisEvent.Erroris received, only_errorMessageis set but_stateremainsANALYZING. This leaves the UI stuck in the analyzing state indefinitely.🔎 Proposed fix
is AnalysisEvent.Error -> { logger.warn { "LLM analysis failed: ${event.message}" } _errorMessage.value = event.message + _state.value = RunConfigState.ERROR }
392-426: Config modifications are not persisted.
setDefaultConfig(),addConfig(), andremoveConfig()update in-memory state but don't callsaveConfigs(). User changes will be lost on application restart.🔎 Proposed fix - launch coroutine for persistence
+ private val serviceScope = kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Default) + fun setDefaultConfig(configId: String) { val config = _configs.value.find { it.id == configId } if (config != null) { _configs.value = _configs.value.map { c -> c.copy(isDefault = c.id == configId) } _defaultConfig.value = config.copy(isDefault = true) + serviceScope.launch { saveConfigs() } } } fun addConfig(config: RunConfig) { _configs.value = _configs.value + config.copy(source = RunConfigSource.USER_DEFINED) if (config.isDefault || _defaultConfig.value == null) { _defaultConfig.value = config } _state.value = RunConfigState.CONFIGURED + serviceScope.launch { saveConfigs() } } fun removeConfig(configId: String) { _configs.value = _configs.value.filter { it.id != configId } if (_defaultConfig.value?.id == configId) { _defaultConfig.value = _configs.value.firstOrNull { it.isDefault } ?: _configs.value.firstOrNull() } if (_configs.value.isEmpty()) { _state.value = RunConfigState.NOT_CONFIGURED } + serviceScope.launch { saveConfigs() } }
197-201: Consider making timeout configurable per RunConfig.The hardcoded 10-minute timeout may not be appropriate for all commands. Long-running server processes like Spring Boot's
bootRunmay need to run indefinitely. Consider adding atimeoutMsfield toRunConfigor using an environment variable to allow customization.
340-347: Returningsuccess = trueon exception is misleading.When an exception occurs (timeout or other error), returning
success = truewith "Process may still be running" doesn't accurately convey the result. This can mislead callers into thinking the operation succeeded when it actually failed or timed out.🔎 Proposed fix
} catch (e: Exception) { // Timeout or other error - process may still be running onOutput("\n[INFO] ${e.message}\n") return RunConfigResult( - success = true, - message = "Process may still be running" + success = false, + message = "Process may still be running: ${e.message}", + error = e.message ) }mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.jvm.kt (2)
21-39: Thread visibility issue withcancelledflag.The
cancelledvariable is accessed from multiple threads (main coroutine and reader threads) without synchronization. Changes made by the main thread may not be visible to the reader threads due to CPU caching.🔎 Proposed fix - use AtomicBoolean
- var cancelled = false + val cancelled = java.util.concurrent.atomic.AtomicBoolean(false) // Read stdout in a separate thread val stdoutReader = Thread { try { process.inputStream.bufferedReader().use { reader -> val buffer = CharArray(1024) - while (!cancelled) { + while (!cancelled.get()) { val charsRead = reader.read(buffer) // ... } } } catch (_: Exception) { // Stream closed, ignore } - }.apply { start() } + }.apply { + isDaemon = true // Prevent thread from blocking JVM shutdown + start() + }
67-76: Returningsuccess = trueon timeout is misleading.When the process doesn't complete within the timeout, returning
success = truewith "may still be running" doesn't accurately convey the result. The caller cannot distinguish between a successful completion and a timeout.🔎 Proposed fix
if (!completed) { // Process still running after timeout - report but don't kill onOutput("\n[INFO] Process still running after ${timeoutMs / 1000}s timeout.\n") onProcessStarted(process.pid().toInt()) return@withContext RunConfigResult( - success = true, + success = false, - message = "Process started (may still be running)", + message = "Process did not complete within timeout (may still be running)", + error = "Timeout after ${timeoutMs}ms", pid = process.pid().toInt() ) }mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.kt (2)
21-39: Same thread visibility issue withcancelledflag as JVM implementation.The
cancelledvariable is accessed from multiple threads without synchronization. Consider usingAtomicBooleanfor thread-safe access, and marking threads as daemon to prevent leaks.
67-81: Same timeout handling issue - returningsuccess = trueon timeout.Similar to the JVM implementation, returning
success = truewhen the process times out is misleading. The reflective pid lookup (lines 70-74) is a good approach for Android API compatibility.
🧹 Nitpick comments (4)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.kt (1)
7-19: LGTM! Well-documented cross-platform API.The expect declaration correctly establishes the cross-platform API surface, and the documentation clearly explains the platform-specific behavior differences.
Optional: Consider default parameter values for better ergonomics
The API could be more ergonomic with default values:
@Composable expect fun TooltipWrapper( tooltip: @Composable () -> Unit, - tooltipOffset: DpOffset, - delayMillis: Int, - modifier: Modifier, + tooltipOffset: DpOffset = DpOffset.Zero, + delayMillis: Int = 500, + modifier: Modifier = Modifier, content: @Composable () -> Unit )This would reduce boilerplate for callers on platforms where tooltip parameters are ignored.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (1)
77-128: Consider conditionally rendering TooltipWrapper.On JVM, the
TooltipAreamay display an empty tooltip whenanalysisLogis blank. Consider wrapping theTooltipWrapperconditionally or passing an empty composable when there's no log.🔎 Proposed conditional tooltip rendering
- TooltipWrapper( - tooltip = { - if (analysisLog.isNotBlank()) { + if (analysisLog.isNotBlank()) { + TooltipWrapper( + tooltip = { Box( modifier = Modifier .widthIn(max = 320.dp) @@ -104,10 +104,7 @@ color = MaterialTheme.colorScheme.inverseOnSurface ) } - } - }, - tooltipOffset = DpOffset(0.dp, 16.dp), - delayMillis = 300, + }, + tooltipOffset = DpOffset(0.dp, 16.dp), + delayMillis = 300, modifier = Modifier - ) { + ) { SmallFloatingActionButton( onClick = {}, @@ -121,5 +118,17 @@ ) } } + } else { + SmallFloatingActionButton( + onClick = {}, + containerColor = AutoDevColors.Signal.info.copy(alpha = 0.15f) + ) { + Icon( + imageVector = AutoDevComposeIcons.Sync, + contentDescription = "Analyzing with AI...", + tint = AutoDevColors.Signal.info, + modifier = Modifier.rotate(rotation) + ) + } + }mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.kt (1)
270-272: Mutable state fields accessed from multiple coroutine contexts without synchronization.
currentSession,currentProcessHandle, andexecutionJobare modified inexecute()and read/cleared instopRunning(). Without synchronization, there's a risk of race conditions ifstopRunning()is called whileexecute()is in progress.Consider using
AtomicReferenceorMutex-protected access for thread safety.mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.kt (1)
7-101: Consider extracting shared logic with JVM implementation.The
readProcessOutputPlatformimplementation is nearly identical to the JVM version (only pid reflection differs). Consider extracting shared logic to reduce duplication, though this may complicate theexpect/actualstructure.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-core/src/iosMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ios.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.js.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.jvm.ktmpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.wasmJs.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.android.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ios.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.js.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.jvm.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.wasmJs.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-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.wasmJs.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.js.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.jvm.ktmpp-core/src/iosMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ios.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ios.ktmpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.wasmJs.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.jvm.ktmpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.js.kt
**/*.{kt,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never use hardcoded colors - always use design tokens from the design system
Files:
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.wasmJs.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.js.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.jvm.ktmpp-core/src/iosMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ios.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ios.ktmpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.wasmJs.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.jvm.ktmpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/RunConfigService.ktmpp-core/src/jsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.js.kt
🧠 Learnings (1)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `expect`/`actual` pattern for platform-specific code (for example: `Platform` implementations)
Applied to files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.kt
🧬 Code graph analysis (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (6)
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.android.kt (1)
TooltipWrapper(8-20)mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.kt (1)
TooltipWrapper(12-19)mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ios.kt (1)
TooltipWrapper(8-20)mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.js.kt (1)
TooltipWrapper(8-20)mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.jvm.kt (1)
TooltipWrapper(10-26)mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.wasmJs.kt (1)
TooltipWrapper(8-20)
⏰ 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 (17)
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.js.kt (1)
8-20: LGTM! Correct JS platform implementation.The implementation correctly provides a no-op tooltip behavior for the JS platform while maintaining API consistency. The unused parameters are expected and documented.
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.jvm.kt (1)
10-26: LGTM! Correct JVM/Desktop implementation with full tooltip support.The implementation correctly leverages
TooltipAreato provide actual tooltip functionality on desktop platforms. The use ofTooltipPlacement.CursorPointwith the provided offset and all parameter forwarding is appropriate.mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.wasmJs.kt (1)
8-20: LGTM! Correct WASM platform implementation.The implementation correctly provides a no-op tooltip behavior for the WASM platform while maintaining API consistency. The unused parameters are expected and documented.
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.android.kt (1)
8-20: LGTM! Correct Android platform implementation.The implementation correctly provides a no-op tooltip behavior for Android while maintaining API consistency. The comment noting potential future use of
BasicTooltipBoxis a helpful note for future improvements.mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/compose/components/TooltipWrapper.ios.kt (1)
8-20: LGTM! Correct iOS platform implementation.The implementation correctly provides a no-op tooltip behavior for the iOS platform while maintaining API consistency. The unused parameters are expected and documented.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt (5)
1-36: LGTM! Imports follow guidelines.The imports correctly use
MaterialTheme.colorSchemeandAutoDevColorsdesign tokens, and leverage the cross-platformTooltipWrappercomponent.
38-59: LGTM! Clear function signature.The function signature is well-documented and follows Compose best practices with proper callback naming and default parameters.
64-75: LGTM! NOT_CONFIGURED state implementation.Correctly uses
MaterialTheme.colorSchemefor colors and provides clear affordance with the Settings icon.
130-141: LGTM! ERROR state implementation.Correctly uses
AutoDevColors.Signal.errordesign tokens and provides clear visual feedback with the Warning icon.
143-156: LGTM! CONFIGURED FAB logic is sound.The state-dependent icon and color logic correctly reflects whether the default configuration is running, providing clear visual feedback.
mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.js.kt (1)
1-23: LGTM!The JS platform implementation correctly provides stub implementations that indicate native process execution is not supported, while still providing a functional time provider via
Clock.System.now(). Theexpect/actualpattern is properly used as per coding guidelines.mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.kt (1)
1-32: Well-structured expect declarations with clear documentation.The
expect/actualpattern is correctly applied per coding guidelines. The KDoc comments clearly document the expected behavior for each platform, which helps maintainability.mpp-core/src/iosMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.ios.kt (1)
1-23: LGTM!The iOS platform implementation correctly provides stub implementations consistent with other non-JVM platforms, while providing functional time support via kotlinx-datetime.
mpp-core/src/jvmMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.jvm.kt (2)
90-95: Cancellation handling is correct but relies on unsynchronized flag.The cancellation logic correctly propagates the exception after interrupting reader threads. However, setting
cancelled = truewithout synchronization means reader threads may not see the update immediately.
98-111: LGTM for utility functions.
killProcessPlatform,isNativeProcess, andcurrentTimeMillisare correctly implemented with appropriate error handling.mpp-core/src/wasmJsMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.wasmJs.kt (1)
1-23: LGTM!The WASM platform implementation follows the same pattern as JS and iOS stubs, correctly indicating that native process execution is not supported while providing functional time utilities.
mpp-core/src/androidMain/kotlin/cc/unitmesh/agent/runconfig/ProcessOutputReader.android.kt (1)
103-116: Android-specific liveness check is appropriate.Using
exitValue()exception to check if the process is alive is a reasonable approach for Android API compatibility whereisAlivemay not be available on older API levels.
| DropdownMenu( | ||
| expanded = menuExpanded, | ||
| onDismissRequest = { menuExpanded = false } | ||
| ) { | ||
| // Show all configs (avoid duplicates) | ||
| configs.forEach { cfg -> | ||
| val running = isRunning && runningConfigId == cfg.id | ||
| val isDefault = cfg.id == defaultConfig?.id | ||
| val tint = when { | ||
| running -> AutoDevColors.Signal.error | ||
| isDefault -> AutoDevColors.Signal.success | ||
| else -> MaterialTheme.colorScheme.onSurface | ||
| } | ||
| DropdownMenuItem( | ||
| text = { | ||
| Text( | ||
| text = if (isDefault) "${cfg.name} (default)" else cfg.name, | ||
| style = if (isDefault) MaterialTheme.typography.bodyMedium else MaterialTheme.typography.bodySmall | ||
| ) | ||
| }, | ||
| onClick = { | ||
| menuExpanded = false | ||
| println("[FloatingRunButton] Running config: ${cfg.name}") | ||
| if (running) onStopRunning() else onRunConfig(cfg) | ||
| }, | ||
| leadingIcon = { | ||
| Icon( | ||
| imageVector = if (running) AutoDevComposeIcons.Stop else AutoDevComposeIcons.PlayArrow, | ||
| contentDescription = null, | ||
| tint = tint | ||
| ) | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| DropdownMenuItem( | ||
| text = { Text("Re-analyze Project") }, | ||
| onClick = { | ||
| menuExpanded = false | ||
| onConfigure() | ||
| }, | ||
| leadingIcon = { | ||
| Icon( | ||
| imageVector = AutoDevComposeIcons.Refresh, | ||
| contentDescription = null | ||
| ) | ||
| } | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug println statement.
Line 180 contains a debug print statement that should be removed before merging to production.
🔎 Proposed fix
onClick = {
menuExpanded = false
- println("[FloatingRunButton] Running config: ${cfg.name}")
if (running) onStopRunning() else onRunConfig(cfg)
},🤖 Prompt for AI Agents
In
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/runconfig/FloatingRunButton.kt
around lines 158 to 206, remove the debug println call on line 180
("println(\"[FloatingRunButton] Running config: ${cfg.name}\")"); delete that
statement and ensure no other side effects are tied to it (if you need to record
this action use the existing logging mechanism instead of println).
Description
This PR implements dynamic run configuration generation using AI analysis, addressing #529.
Features
LLM-based project analysis with streaming output
IDE-like Run Button (floating in top-right corner)
Real-time command execution with live streaming output
bootRun)Stop/Cancel functionality
RunOutputDock for displaying:
Key Components
Core
LLMRunConfigAnalyzer: Streams AI analysis of project structureRunConfigService: Manages run configs with live execution supportRunConfig: Data models for run configurationsUI
FloatingRunButton: IDE-style floating action buttonRunConfigViewModel: ViewModel for UI state managementRunOutputDock: Terminal-like output displayHow It Works
Screenshots / Demo
Testing
Related Issues
Closes #529
Notes
This implementation focuses on the "Run Config" aspect of GenAction, providing a concrete example of how Agent-driven actions can be dynamically generated and executed. The architecture can be extended to support other types of IDE actions in the future.
Summary by CodeRabbit
New Features
Platform
UI
✏️ Tip: You can customize this high-level summary in your review settings.