From 79eae8020b18d57a23606d66bc0f7ca8eebd3724 Mon Sep 17 00:00:00 2001
From: Andrew Charneski
Date: Mon, 9 Dec 2024 19:07:33 -0500
Subject: [PATCH] wip
---
gradle.properties | 2 +-
.../actions/plan/AutoPlanChatAction.kt | 8 +-
.../aicoder/actions/plan/PlanAheadAction.kt | 2 +-
.../actions/plan/PlanAheadConfigDialog.kt | 286 -------
.../aicoder/actions/plan/PlanChatAction.kt | 2 +-
.../aicoder/actions/plan/PlanConfigDialog.kt | 714 ++++++++++++++++++
.../aicoder/actions/plan/PrePlanAction.kt | 2 +-
.../aicoder/config/AppSettingsComponent.kt | 3 +-
8 files changed, 723 insertions(+), 296 deletions(-)
delete mode 100644 src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadConfigDialog.kt
create mode 100644 src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanConfigDialog.kt
diff --git a/gradle.properties b/gradle.properties
index 2b8076ee..4f2aa4fd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
pluginName=intellij-aicoder
pluginRepositoryUrl=https://github.com/SimiaCryptus/intellij-aicoder
-pluginVersion=1.9.2
+pluginVersion=1.9.3
jvmArgs=-Xmx8g
org.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=1g
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/AutoPlanChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/AutoPlanChatAction.kt
index b7b61b12..47c51e59 100644
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/AutoPlanChatAction.kt
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/AutoPlanChatAction.kt
@@ -36,7 +36,7 @@ class AutoPlanChatAction : BaseAction() {
override fun getActionUpdateThread() = ActionUpdateThread.BGT
override fun handle(e: AnActionEvent) {
- val dialog = PlanAheadConfigDialog(
+ val dialog = PlanConfigDialog(
e.project, PlanSettings(
defaultModel = AppSettingsState.instance.smartModel.chatModel(),
parsingModel = AppSettingsState.instance.fastModel.chatModel(),
@@ -63,7 +63,7 @@ class AutoPlanChatAction : BaseAction() {
}
}
- private fun initializeChat(e: AnActionEvent, dialog: PlanAheadConfigDialog, progress: ProgressIndicator) {
+ private fun initializeChat(e: AnActionEvent, dialog: PlanConfigDialog, progress: ProgressIndicator) {
progress.text = "Setting up session..."
val session = Session.newGlobalID()
val root = getProjectRoot(e) ?: throw RuntimeException("Could not determine project root")
@@ -81,7 +81,7 @@ class AutoPlanChatAction : BaseAction() {
}
}
- private fun setupChatSession(session: Session, root: File, e: AnActionEvent, dialog: PlanAheadConfigDialog) {
+ private fun setupChatSession(session: Session, root: File, e: AnActionEvent, dialog: PlanConfigDialog) {
DataStorage.sessionPaths[session] = root
SessionProxyServer.chats[session] = createChatApp(root, e, dialog)
ApplicationServer.appInfoMap[session] = AppInfoData(
@@ -94,7 +94,7 @@ class AutoPlanChatAction : BaseAction() {
SessionProxyServer.metadataStorage.setSessionName(null, session, "${javaClass.simpleName} @ ${SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis())}")
}
- private fun createChatApp(root: File, e: AnActionEvent, dialog: PlanAheadConfigDialog) =
+ private fun createChatApp(root: File, e: AnActionEvent, dialog: PlanConfigDialog) =
object : AutoPlanChatApp(
planSettings = dialog.settings.copy(
env = mapOf(),
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadAction.kt
index 81d04aa2..67678f29 100644
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadAction.kt
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadAction.kt
@@ -28,7 +28,7 @@ class PlanAheadAction : BaseAction() {
override fun getActionUpdateThread() = ActionUpdateThread.BGT
override fun handle(e: AnActionEvent) {
- val dialog = PlanAheadConfigDialog(
+ val dialog = PlanConfigDialog(
e.project, PlanSettings(
defaultModel = AppSettingsState.instance.smartModel.chatModel(),
parsingModel = AppSettingsState.instance.fastModel.chatModel(),
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadConfigDialog.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadConfigDialog.kt
deleted file mode 100644
index 57ac200b..00000000
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanAheadConfigDialog.kt
+++ /dev/null
@@ -1,286 +0,0 @@
-package com.github.simiacryptus.aicoder.actions.plan
-
-import com.github.simiacryptus.aicoder.config.AppSettingsState
-import com.intellij.openapi.fileChooser.FileChooser
-import com.intellij.openapi.fileChooser.FileChooserDescriptor
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import com.intellij.ui.components.JBScrollPane
-import com.intellij.ui.dsl.builder.Align
-import com.intellij.ui.dsl.builder.RowLayout
-import com.intellij.ui.dsl.builder.panel
-import com.intellij.ui.table.JBTable
-import com.simiacryptus.jopenai.models.ChatModel
-import com.simiacryptus.skyenet.apps.plan.PlanSettings
-import com.simiacryptus.skyenet.apps.plan.TaskSettings
-import com.simiacryptus.skyenet.apps.plan.TaskType
-import java.awt.Component
-import java.awt.Dimension
-import javax.swing.*
-import javax.swing.table.DefaultTableCellRenderer
-import javax.swing.table.DefaultTableModel
-
-class PlanAheadConfigDialog(
- project: Project?,
- val settings: PlanSettings,
-) : DialogWrapper(project) {
- private val temperatureSlider = JSlider(0, 100, (settings.temperature * 100).toInt())
- private val autoFixCheckbox = JCheckBox("Auto-apply fixes", settings.autoFix)
- private val allowBlockingCheckbox = JCheckBox("Allow blocking", settings.allowBlocking)
- private val taskTableModel = object : DefaultTableModel(arrayOf("Enabled", "Task Type", "Model"), 0) {
- override fun getColumnClass(columnIndex: Int) = when (columnIndex) {
- 0 -> java.lang.Boolean::class.java
- else -> super.getColumnClass(columnIndex)
- }
-
- override fun isCellEditable(row: Int, column: Int) = column == 0 || column == 2
- }
- private val taskTable = JBTable(taskTableModel).apply { putClientProperty("terminateEditOnFocusLost", true) }
-
- // Add a function to retrieve visible models
- private fun getVisibleModels() =
- ChatModel.values().map { it.value }.filter { isVisible(it) }.toList()
- .sortedBy { "${it.provider.name} - ${it.modelName}" }
-
- // Custom renderer to display provider name and model name
- private fun getModelRenderer() = object : DefaultTableCellRenderer() {
- override fun getTableCellRendererComponent(
- table: JTable,
- value: Any,
- isSelected: Boolean,
- hasFocus: Boolean,
- row: Int,
- column: Int
- ): Component {
- val label = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column) as JLabel
- if (value is String) {
- val model = getVisibleModels().find { it.modelName == value }
- label.text = "${model?.provider?.name} - $value"
- }
- return label
- }
- }
-
- companion object {
- fun isVisible(it: ChatModel): Boolean {
- val hasApiKey =
- AppSettingsState.instance.apiKey?.filter { it.value.isNotBlank() }?.keys?.contains(it.provider.name)
- return false != hasApiKey
- }
- }
-
- private val checkboxStates = AppSettingsState.instance.executables.map { true }.toMutableList()
- private val tableModel = object : DefaultTableModel(arrayOf("Enabled", "Command"), 0) {
-
- init {
- AppSettingsState.instance.executables.forEach { command ->
- addRow(arrayOf(true, command))
- }
- }
-
- override fun getColumnClass(columnIndex: Int) = when (columnIndex) {
- 0 -> java.lang.Boolean::class.java
- else -> super.getColumnClass(columnIndex)
- }
-
- override fun isCellEditable(row: Int, column: Int) = column == 0
-
- override fun setValueAt(aValue: Any?, row: Int, column: Int) {
- super.setValueAt(aValue, row, column)
- if (column == 0 && aValue is Boolean) {
- checkboxStates[row] = aValue
- } else {
- throw IllegalArgumentException("Invalid column index: $column")
- }
- }
-
- override fun getValueAt(row: Int, column: Int): Any =
- try {
- if (column == 0) {
- checkboxStates[row]
- } else super.getValueAt(row, column)
- } catch (e: IndexOutOfBoundsException) {
- false
- }
- }
- private val commandTable = JBTable(tableModel).apply { putClientProperty("terminateEditOnFocusLost", true) }
- private val addCommandButton = JButton("Add Command")
- private val editCommandButton = JButton("Edit Command")
-
-
- init {
- taskTable.columnModel.getColumn(2).apply {
- preferredWidth = 200
- val modelComboBox = JComboBox(getVisibleModels().map { it.modelName }.toTypedArray())
- cellEditor = DefaultCellEditor(modelComboBox)
- cellRenderer = getModelRenderer()
- }
-
- init()
- title = "Configure Plan Ahead Action"
- // Add model combobox and change listener to update the settings based on slider value
-
- temperatureSlider.addChangeListener {
- settings.temperature = temperatureSlider.value / 100.0
- }
- // Update parsingModel based on modelComboBox selection
- val fileChooserDescriptor = FileChooserDescriptor(true, false, false, false, false, false)
- .withTitle("Select Command")
- .withDescription("Choose an executable file for the auto-fix command")
- addCommandButton.addActionListener {
- val chosenFile = FileChooser.chooseFile(fileChooserDescriptor, project, null)
- if (chosenFile != null) {
- val newCommand = chosenFile.path
- val confirmResult = JOptionPane.showConfirmDialog(
- null,
- "Add command: $newCommand?",
- "Confirm Command",
- JOptionPane.YES_NO_OPTION
- )
- if (confirmResult == JOptionPane.YES_OPTION) {
- tableModel.addRow(arrayOf(true, newCommand))
- checkboxStates.add(true)
- AppSettingsState.instance.executables.add(newCommand)
- }
- }
- }
- editCommandButton.addActionListener {
- val selectedRow = commandTable.selectedRow
- if (selectedRow != -1) {
- val currentCommand = tableModel.getValueAt(selectedRow, 1) as String
- val newCommand = JOptionPane.showInputDialog(
- null,
- "Edit command:",
- currentCommand
- )
- if (newCommand != null && newCommand.isNotEmpty()) {
- val confirmResult = JOptionPane.showConfirmDialog(
- null,
- "Update command to: $newCommand?",
- "Confirm Edit",
- JOptionPane.YES_NO_OPTION
- )
- if (confirmResult == JOptionPane.YES_OPTION) {
- tableModel.setValueAt(newCommand, selectedRow, 1)
- AppSettingsState.instance.executables.remove(currentCommand)
- AppSettingsState.instance.executables.add(newCommand)
- }
- }
- } else {
- JOptionPane.showMessageDialog(null, "Please select a command to edit.")
- }
- }
- commandTable.columnModel.getColumn(0).apply {
- preferredWidth = 50
- maxWidth = 100
- }
- commandTable.selectionModel.addListSelectionListener {
- editCommandButton.isEnabled = commandTable.selectedRow != -1
- }
- editCommandButton.isEnabled = false
- // Initialize task table
- val values = TaskType.values()
- values.forEach { taskType ->
- val taskSettings = settings.getTaskSettings(taskType)
- taskTableModel.addRow(
- arrayOf(
- taskSettings.enabled,
- taskType.name,
- taskSettings.model?.modelName ?: AppSettingsState.instance.smartModel,
- )
- )
- }
- taskTable.columnModel.getColumn(0).preferredWidth = 50
- taskTable.columnModel.getColumn(0).maxWidth = 100
- taskTable.columnModel.getColumn(1).preferredWidth = 200
- taskTable.columnModel.getColumn(2).preferredWidth = 200
- // Call setupCommandTable to initialize commandTable
- setupCommandTable()
- }
-
- override fun createCenterPanel(): JComponent = panel {
- group("Tasks") {
- row("Task Types:") {
- cell(JBScrollPane(taskTable).apply {
- minimumSize = Dimension(350, 100)
- preferredSize = Dimension(350, 200)
- })
- .align(Align.FILL)
- .resizableColumn()
- }
- .resizableRow()
- .layout(RowLayout.PARENT_GRID)
- }
- .resizableRow()
- group("Settings") {
- row {
- cell(autoFixCheckbox)
- .align(Align.FILL)
- }
- row {
- cell(allowBlockingCheckbox)
- .align(Align.FILL)
- }
- row("Temperature:") {
- cell(temperatureSlider)
- .align(Align.FILL)
- }
- }
- group("Commands") {
- row("Auto-Fix Commands:") {
- cell(JBScrollPane(commandTable).apply {
- minimumSize = Dimension(350, 100)
- preferredSize = Dimension(350, 200)
- })
- .align(Align.FILL)
- .resizableColumn()
- }
- .resizableRow()
- .layout(RowLayout.PARENT_GRID)
- row {
- cell(addCommandButton)
- cell(editCommandButton)
- }
- }
- .resizableRow()
- }
-
- override fun doOKAction() {
- // Update task settings
- for (i in 0 until taskTableModel.rowCount) {
- val taskType = TaskType.valueOf(taskTableModel.getValueAt(i, 1) as String)
- val modelName = taskTableModel.getValueAt(i, 2) as String
- val selectedModel = ChatModel.values().toList().find { it.first == modelName }?.second
- settings.setTaskSettings(taskType, TaskSettings(taskTableModel.getValueAt(i, 0) as Boolean).apply {
- this.model = selectedModel
- })
- }
- settings.autoFix = autoFixCheckbox.isSelected
- settings.allowBlocking = allowBlockingCheckbox.isSelected
- settings.commandAutoFixCommands = (0 until tableModel.rowCount)
- .filter { tableModel.getValueAt(it, 0) as Boolean }
- .map { tableModel.getValueAt(it, 1) as String }
- // Update the global tool collection without removing deselected commands
- settings.commandAutoFixCommands!!.forEach { command ->
- if (!AppSettingsState.instance.executables.contains(command)) {
- AppSettingsState.instance.executables.add(command)
- }
- }
- super.doOKAction()
- }
-
- private fun setupCommandTable() {
- commandTable.columnModel.getColumn(0).apply {
- cellEditor = DefaultCellEditor(JCheckBox())
- preferredWidth = 50
- maxWidth = 100
- }
- tableModel.addTableModelListener { e ->
- if (e.column == 0) {
- val row = e.firstRow
- val value = tableModel.getValueAt(row, 0) as Boolean
- checkboxStates[row] = value
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanChatAction.kt
index 27ece3a6..f71b396a 100644
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanChatAction.kt
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanChatAction.kt
@@ -50,7 +50,7 @@ class PlanChatAction : BaseAction() {
}
private fun initializeAndOpenChat(e: AnActionEvent) {
- val dialog = PlanAheadConfigDialog(
+ val dialog = PlanConfigDialog(
e.project, PlanSettings(
defaultModel = AppSettingsState.instance.smartModel.chatModel(),
parsingModel = AppSettingsState.instance.fastModel.chatModel(),
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanConfigDialog.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanConfigDialog.kt
new file mode 100644
index 00000000..6793f385
--- /dev/null
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PlanConfigDialog.kt
@@ -0,0 +1,714 @@
+package com.github.simiacryptus.aicoder.actions.plan
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState
+import com.intellij.openapi.fileChooser.FileChooser
+import com.intellij.openapi.fileChooser.FileChooserDescriptor
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.ComboBox
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.ui.JBSplitter
+import com.intellij.ui.components.JBList
+import com.intellij.ui.components.JBScrollPane
+import com.intellij.ui.dsl.builder.Align
+import com.intellij.ui.dsl.builder.RowLayout
+import com.intellij.ui.dsl.builder.panel
+import com.intellij.ui.table.JBTable
+import com.simiacryptus.jopenai.models.ChatModel
+import com.simiacryptus.skyenet.apps.plan.PlanSettings
+import com.simiacryptus.skyenet.apps.plan.TaskSettingsBase
+import com.simiacryptus.skyenet.apps.plan.TaskType
+import java.awt.CardLayout
+import java.awt.Component
+import java.awt.Dimension
+import java.awt.Font
+import javax.swing.*
+import javax.swing.table.DefaultTableModel
+
+class PlanConfigDialog(
+ project: Project?,
+ val settings: PlanSettings,
+) : DialogWrapper(project) {
+ companion object {
+ private const val MIN_TEMP = 0
+ private const val MAX_TEMP = 100
+ private const val DEFAULT_LIST_WIDTH = 150
+ private const val DEFAULT_LIST_HEIGHT = 200
+ private const val DEFAULT_PANEL_WIDTH = 350
+ private const val DEFAULT_PANEL_HEIGHT = 200
+ private const val TEMPERATURE_SCALE = 100.0
+ private const val TEMPERATURE_LABEL = "%.2f"
+
+ fun isVisible(it: ChatModel): Boolean {
+ return AppSettingsState.instance.apiKey
+ ?.filter { it.value.isNotBlank() }
+ ?.keys
+ ?.contains(it.provider.name)
+ ?: false
+ }
+ }
+
+ private data class CommandTableEntry(
+ var enabled: Boolean,
+ val command: String
+ )
+
+ private val temperatureSlider = JSlider(MIN_TEMP, MAX_TEMP, (settings.temperature * TEMPERATURE_SCALE).toInt()).apply {
+ addChangeListener {
+ settings.temperature = value / TEMPERATURE_SCALE
+ temperatureLabel.text = TEMPERATURE_LABEL.format(settings.temperature)
+ }
+ }
+ private val temperatureLabel = JLabel(TEMPERATURE_LABEL.format(settings.temperature))
+ private val autoFixCheckbox = JCheckBox("Auto-apply fixes", settings.autoFix)
+ private val allowBlockingCheckbox = JCheckBox("Allow blocking", settings.allowBlocking)
+ private val taskTypeList = JBList(TaskType.values())
+ private val configPanelContainer = JPanel(CardLayout())
+ private val taskConfigs = mutableMapOf()
+ private val singleTaskModeCheckbox = JCheckBox("Single Task Mode", false)
+ private val removeCommandButton = JButton("Remove Command").apply {
+ isEnabled = false
+ }
+
+ private inner class TaskTypeListCellRenderer : DefaultListCellRenderer() {
+ override fun getListCellRendererComponent(
+ list: JList<*>?,
+ value: Any?,
+ index: Int,
+ isSelected: Boolean,
+ cellHasFocus: Boolean
+ ): Component {
+ val component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
+ if (component is JLabel && value is TaskType<*, *>) {
+ // Set tooltip with detailed HTML description
+ toolTipText = getTaskTooltip(value)
+ // Access settings directly through the outer class
+ val isEnabled = settings.getTaskSettings(value).enabled
+ // Set font style and color based on enabled status
+ font = when (isEnabled) {
+ true -> {
+ font.deriveFont(Font.BOLD).deriveFont(14f)
+ }
+
+ false -> {
+ font.deriveFont(Font.ITALIC).deriveFont(12f)
+ }
+ }
+ foreground = if (isEnabled) {
+ list?.foreground
+ } else {
+ list?.foreground?.darker()?.darker()
+ }
+ // Set task name and description
+ text = buildString {
+ val taskDescription = getTaskDescription(value)
+ append(value.name)
+ if (taskDescription.isNotEmpty()) {
+ append(" - ")
+ append(taskDescription)
+ }
+ }
+ }
+ return component
+ }
+
+ private fun getTaskTooltip(taskType: TaskType<*, *>): String = """
+
+
+ ${taskType.name}
+ ${
+ when (taskType) {
+ TaskType.PerformanceAnalysis -> """
+ Analyzes code performance and provides optimization recommendations.
+
+ - Identifies performance bottlenecks and hotspots
+ - Measures execution time and resource usage
+ - Suggests algorithmic and structural improvements
+ - Provides quantitative performance metrics
+ - Recommends caching and optimization strategies
+
+ """
+
+ TaskType.WebFetchAndTransform -> """
+ Fetches content from web URLs and transforms it into desired formats.
+
+ - Downloads and cleans HTML content
+ - Converts content to specified formats
+ - Handles content size limitations
+ - Supports custom transformation goals
+ - Integrates with markdown rendering
+
+ """
+
+ TaskType.GitHubSearch -> """
+ Performs comprehensive searches across GitHub's content.
+
+ - Searches repositories, code, and issues
+ - Supports advanced search queries
+ - Filters results by various criteria
+ - Formats results with relevant details
+ - Handles API rate limiting
+
+ """
+
+ TaskType.GoogleSearch -> """
+ Executes Google web searches with customizable parameters.
+
+ - Uses Google Custom Search API
+ - Supports result count configuration
+ - Includes metadata and snippets
+ - Formats results in markdown
+ - Handles URL encoding and safety
+
+ """
+
+ TaskType.Search -> """
+ Performs pattern-based searches across project files with context.
+
+ - Supports both substring and regex search patterns
+ - Shows configurable context lines around matches
+ - Groups results by file with line numbers
+ - Filters for text-based files automatically
+ - Provides organized, readable output format
+
+ """
+
+ TaskType.EmbeddingSearch -> """
+ Performs semantic search using AI embeddings across indexed content.
+
+ - Uses OpenAI embeddings for semantic matching
+ - Supports positive and negative search queries
+ - Configurable similarity metrics and thresholds
+ - Regular expression filtering capabilities
+ - Returns ranked results with context
+
+ """
+
+ TaskType.KnowledgeIndexing -> """
+ Indexes documents and code for semantic search capabilities.
+
+ - Processes both documentation and source code
+ - Creates searchable content chunks
+ - Supports parallel processing
+ - Configurable chunking strategies
+ - Progress tracking and reporting
+
+ """
+
+ TaskType.WebSearchAndIndex -> """
+ Performs web searches and indexes results for future reference.
+
+ - Integrates with Google Custom Search
+ - Downloads and processes search results
+ - Creates searchable indexes
+ - Handles content download and storage
+ - Supports batch processing
+
+ """
+
+ TaskType.ForeachTask -> """
+ Executes a set of subtasks for each item in a given list.
+
+ - Handles sequential item processing
+ - Maintains subtask dependencies
+ - Supports parallel execution within items
+ - Provides progress tracking
+ - Configurable subtask definitions
+
+ """
+
+ TaskType.CommandSession -> """
+ Manages interactive command-line sessions with state persistence.
+
+ - Creates and maintains command sessions
+ - Supports multiple concurrent sessions
+ - Configurable timeouts and cleanup
+ - Session state preservation
+ - Comprehensive output capture
+
+ """
+
+ TaskType.SeleniumSession -> """
+ Automates browser interactions using Selenium WebDriver.
+
+ - Headless Chrome browser automation
+ - JavaScript command execution
+ - Session management capabilities
+ - Configurable timeouts
+ - Detailed execution results
+
+ """
+
+ TaskType.RunShellCommand -> """
+ Executes shell commands in a controlled environment.
+
+ - Safe command execution handling
+ - Working directory configuration
+ - Output capture and formatting
+ - Error handling and reporting
+ - Interactive result review
+
+ """
+
+ TaskType.TaskPlanning -> """
+ Orchestrates complex development tasks by breaking them down into manageable subtasks.
+
+ - Analyzes project requirements and constraints to create optimal task sequences
+ - Establishes clear task dependencies and relationships between components
+ - Optimizes task ordering for maximum parallel execution efficiency
+ - Provides interactive visual dependency graphs for progress tracking
+ - Supports both fully automated and interactive planning modes
+ - Estimates task complexity and resource requirements
+ - Identifies critical paths and potential bottlenecks
+
+ """
+
+ TaskType.Inquiry -> """
+ Provides detailed answers and insights about code implementation by analyzing specified files.
+
+ - Answers detailed questions about code functionality and implementation
+ - Analyzes code patterns, relationships and architectural decisions
+ - Supports interactive discussions and follow-up questions in blocking mode
+ - Generates comprehensive markdown reports with code examples
+ - Handles multiple files and complex cross-reference queries
+ - Provides context-aware technical recommendations
+ - Explains trade-offs and rationale behind implementation choices
+
+ """
+
+ TaskType.FileModification -> """
+ Creates or modifies source files with AI assistance while maintaining code quality.
+
+ - Shows proposed changes in diff format for easy review
+ - Supports both automated application and manual approval modes
+ - Maintains project coding standards and style consistency
+ - Handles complex multi-file operations and refactoring
+ - Provides clear documentation of all changes with rationale
+ - Implements proper error handling and edge cases
+ - Updates imports and dependencies automatically
+ - Preserves existing code formatting and structure
+
+ """
+
+ TaskType.Documentation -> """
+ Generates comprehensive documentation for code files and APIs.
+
+ - Handles both inline comments and markdown files
+ - Generates detailed API documentation
+ - Documents design decisions and rationale
+ - Supports interactive approval workflow
+ - Maintains documentation consistency
+
+ """
+
+ TaskType.CodeReview -> """
+ Performs automated code reviews focusing on quality and best practices.
+
+ - Analyzes code quality and potential issues
+ - Identifies bugs and performance problems
+ - Reviews security vulnerabilities
+ - Suggests specific improvements
+ - Provides actionable recommendations
+
+ """
+
+ TaskType.TestGeneration -> """
+ Creates comprehensive test suites for code reliability and correctness.
+
+ - Generates unit and integration tests
+ - Creates positive and negative test cases
+ - Tests edge cases and boundary conditions
+ - Follows language-specific testing practices
+ - Organizes tests in appropriate directories
+
+ """
+
+ TaskType.Optimization -> """
+ Analyzes and optimizes code performance while maintaining readability.
+
+ - Identifies performance bottlenecks
+ - Suggests algorithmic improvements
+ - Analyzes memory usage patterns
+ - Recommends caching strategies
+ - Provides impact estimates
+
+ """
+
+ TaskType.SecurityAudit -> """
+ Performs security analysis to identify and fix vulnerabilities.
+
+ - Analyzes security vulnerabilities
+ - Reviews authentication/authorization
+ - Checks data handling practices
+ - Provides security recommendations
+ - Generates detailed audit reports
+
+ """
+
+ TaskType.RefactorTask -> """
+ Analyzes and improves code structure while maintaining functionality.
+
+ - Suggests structural improvements
+ - Reduces code complexity
+ - Improves naming conventions
+ - Enhances code organization
+ - Shows changes in diff format
+
+ """
+
+ else -> "No detailed description available"
+ }
+ }
+
+
+ """
+
+
+ private fun getTaskDescription(taskType: TaskType<*, *>): String = when (taskType) {
+ TaskType.Search -> "Search project files using patterns with contextual results"
+ TaskType.EmbeddingSearch -> "Perform semantic search using AI embeddings"
+ TaskType.KnowledgeIndexing -> "Index content for semantic search capabilities"
+ TaskType.WebSearchAndIndex -> "Search web content and create searchable indexes"
+ TaskType.ForeachTask -> "Execute subtasks for each item in a list"
+ TaskType.CommandSession -> "Manage interactive command-line sessions"
+ TaskType.SeleniumSession -> "Automate browser interactions with Selenium"
+ TaskType.RunShellCommand -> "Execute shell commands safely"
+ TaskType.TaskPlanning -> "Break down and coordinate complex development tasks with dependency management"
+ TaskType.Inquiry -> "Analyze code and provide detailed explanations of implementation patterns"
+ TaskType.FileModification -> "Create new files or modify existing code with AI-powered assistance"
+ TaskType.Documentation -> "Generate comprehensive documentation for code, APIs, and architecture"
+ TaskType.CodeReview -> "Perform thorough code review with quality and best practice analysis"
+ TaskType.TestGeneration -> "Generate comprehensive test suites with full coverage analysis"
+ TaskType.Optimization -> "Analyze performance bottlenecks and implement optimizations"
+ TaskType.SecurityAudit -> "Identify security vulnerabilities and provide mitigation strategies"
+ TaskType.RefactorTask -> "Improve code structure, readability and maintainability"
+ TaskType.PerformanceAnalysis -> "Analyze and optimize code performance with detailed metrics"
+ TaskType.WebFetchAndTransform -> "Fetch and transform web content into desired formats"
+ TaskType.GitHubSearch -> "Search GitHub repositories, code, issues and users"
+ TaskType.GoogleSearch -> "Perform Google web searches with custom filtering"
+ else -> "No description available"
+ }
+ }
+
+ private fun getVisibleModels() =
+ ChatModel.values().map { it.value }.filter { isVisible(it) }.toList()
+ .sortedBy { "${it.provider.name} - ${it.modelName}" }
+
+ private inner class TaskTypeConfigPanel(val taskType: TaskType<*, *>) : JPanel() {
+ val enabledCheckbox = JCheckBox("Enabled", settings.getTaskSettings(taskType).enabled)
+ private val modelComboBox = ComboBox(getVisibleModels().map { it.modelName }.toTypedArray()).apply {
+ maximumSize = Dimension(DEFAULT_PANEL_WIDTH - 50, 30)
+ preferredSize = Dimension(DEFAULT_PANEL_WIDTH - 50, 30)
+ }
+
+ init {
+ removeCommandButton.addActionListener {
+ val selectedRow = commandTable.selectedRow
+ if (selectedRow != -1) {
+ val command = tableModel.getValueAt(selectedRow, 1) as String
+ val confirmResult = JOptionPane.showConfirmDialog(
+ null,
+ "Remove command: $command?",
+ "Confirm Remove",
+ JOptionPane.YES_NO_OPTION
+ )
+ if (confirmResult == JOptionPane.YES_OPTION) {
+ tableModel.removeRow(selectedRow)
+ checkboxStates.removeAt(selectedRow)
+ AppSettingsState.instance.executables.remove(command)
+ }
+ }
+ }
+ layout = BoxLayout(this, BoxLayout.Y_AXIS)
+ alignmentX = Component.LEFT_ALIGNMENT
+ add(enabledCheckbox.apply { alignmentX = Component.LEFT_ALIGNMENT })
+ add(Box.createVerticalStrut(5))
+ add(JLabel("Model:").apply { alignmentX = Component.LEFT_ALIGNMENT })
+ add(Box.createVerticalStrut(2))
+ add(modelComboBox.apply { alignmentX = Component.LEFT_ALIGNMENT })
+ add(Box.createVerticalGlue())
+ val currentModel = settings.getTaskSettings(taskType).model
+ modelComboBox.selectedItem = currentModel?.modelName
+ enabledCheckbox.addItemListener {
+ // Update the settings immediately when checkbox state changes
+ settings.setTaskSettings(taskType, TaskSettingsBase(taskType.name, enabledCheckbox.isSelected).apply {
+ this.model = getVisibleModels().find { it.modelName == modelComboBox.selectedItem }
+ })
+ taskTypeList.repaint()
+ }
+ modelComboBox.addActionListener {
+ settings.setTaskSettings(taskType, TaskSettingsBase(taskType.name, enabledCheckbox.isSelected).apply {
+ this.model = getVisibleModels().find { it.modelName == modelComboBox.selectedItem }
+ })
+ }
+ }
+
+ fun saveSettings() {
+ // Only need to save model selection since enabled state is saved immediately
+ settings.setTaskSettings(taskType, TaskSettingsBase(taskType.name, enabledCheckbox.isSelected).apply {
+ this.model = getVisibleModels().find { it.modelName == modelComboBox.selectedItem }
+ })
+ }
+ }
+
+ private val checkboxStates = AppSettingsState.instance.executables.map { true }.toMutableList()
+ private val tableModel = object : DefaultTableModel(arrayOf("Enabled", "Command"), 0) {
+ private val entries = mutableListOf()
+
+ init {
+ AppSettingsState.instance.executables.forEach { command ->
+ entries.add(CommandTableEntry(true, command))
+ addRow(arrayOf(true, command))
+ }
+ }
+
+ override fun getColumnClass(columnIndex: Int) = when (columnIndex) {
+ 0 -> java.lang.Boolean::class.java
+ else -> super.getColumnClass(columnIndex)
+ }
+
+ override fun isCellEditable(row: Int, column: Int) = column == 0
+
+ override fun setValueAt(aValue: Any?, row: Int, column: Int) {
+ if (column == 0 && aValue is Boolean) {
+ entries[row].enabled = aValue
+ super.setValueAt(aValue, row, column)
+ fireTableCellUpdated(row, column)
+ } else {
+ throw IllegalArgumentException("Invalid column index: $column")
+ }
+ }
+
+ override fun getValueAt(row: Int, column: Int): Any =
+ try {
+ if (column == 0) {
+ entries[row].enabled
+ } else super.getValueAt(row, column)
+ } catch (e: IndexOutOfBoundsException) {
+ false
+ }
+
+ }
+ private val commandTable = JBTable(tableModel).apply {
+ putClientProperty("terminateEditOnFocusLost", true)
+ this.columnModel.getColumn(0).apply {
+ preferredWidth = 50
+ maxWidth = 100
+ }
+ }
+ private val addCommandButton = JButton("Add Command")
+ private val editCommandButton = JButton("Edit Command")
+
+ init {
+ // Set the custom cell renderer for the task type list
+ taskTypeList.cellRenderer = TaskTypeListCellRenderer()
+
+ taskTypeList.addListSelectionListener { e ->
+ if (!e.valueIsAdjusting) {
+ val selectedType = (taskTypeList.selectedValue as TaskType<*, *>).name
+ (configPanelContainer.layout as CardLayout).show(configPanelContainer, selectedType)
+ if (singleTaskModeCheckbox.isSelected) {
+ // Disable all tasks except the selected one
+ TaskType.values().forEach { taskType ->
+ val isSelected = taskType.name == selectedType
+ taskConfigs[taskType.name]?.enabledCheckbox?.isSelected = isSelected
+ }
+ }
+ }
+ }
+ // Initialize config panels for each task type
+ TaskType.values().forEach { taskType ->
+ val configPanel = TaskTypeConfigPanel(taskType)
+ taskConfigs[taskType.name] = configPanel
+ configPanelContainer.add(configPanel, taskType.name)
+ }
+
+ init()
+ title = "Configure Plan Ahead Action"
+ // Add listener for single task mode checkbox
+ singleTaskModeCheckbox.addItemListener { e ->
+ if (e.stateChange == java.awt.event.ItemEvent.SELECTED) {
+ // When enabled, only keep the currently selected task enabled
+ val selectedType = (taskTypeList.selectedValue as TaskType<*, *>).name
+ TaskType.values().forEach { taskType ->
+ val isSelected = taskType.name == selectedType
+ taskConfigs[taskType.name]?.enabledCheckbox?.isSelected = isSelected
+ }
+ }
+ }
+ // Add model combobox and change listener to update the settings based on slider value
+
+ temperatureSlider.addChangeListener {
+ settings.temperature = temperatureSlider.value / 100.0
+ }
+ // Update parsingModel based on modelComboBox selection
+ val fileChooserDescriptor = FileChooserDescriptor(true, false, false, false, false, false)
+ .withTitle("Select Command")
+ .withDescription("Choose an executable file for the auto-fix command")
+ addCommandButton.addActionListener {
+ val newCommand = if ((it as? java.awt.event.ActionEvent)?.modifiers?.and(java.awt.event.InputEvent.CTRL_DOWN_MASK) != 0) {
+ // Control was held - prompt for direct path input
+ JOptionPane.showInputDialog(
+ null,
+ "Enter command path:",
+ "Add Command",
+ JOptionPane.PLAIN_MESSAGE
+ )
+ } else {
+ // Normal click - show file chooser
+ FileChooser.chooseFile(fileChooserDescriptor, project, null)?.path
+ }
+ if (newCommand != null) {
+ val confirmResult = JOptionPane.showConfirmDialog(
+ null,
+ "Add command: $newCommand?",
+ "Confirm Command",
+ JOptionPane.YES_NO_OPTION
+ )
+ if (confirmResult == JOptionPane.YES_OPTION) {
+ tableModel.addRow(arrayOf(true, newCommand))
+ checkboxStates.add(true)
+ AppSettingsState.instance.executables.add(newCommand)
+ }
+ }
+ }
+ editCommandButton.addActionListener {
+ val selectedRow = commandTable.selectedRow
+ if (selectedRow != -1) {
+ val currentCommand = tableModel.getValueAt(selectedRow, 1) as String
+ val newCommand = JOptionPane.showInputDialog(
+ null,
+ "Edit command:",
+ currentCommand
+ )
+ if (newCommand != null && newCommand.isNotEmpty()) {
+ val confirmResult = JOptionPane.showConfirmDialog(
+ null,
+ "Update command to: $newCommand?",
+ "Confirm Edit",
+ JOptionPane.YES_NO_OPTION
+ )
+ if (confirmResult == JOptionPane.YES_OPTION) {
+ tableModel.setValueAt(newCommand, selectedRow, 1)
+ AppSettingsState.instance.executables.remove(currentCommand)
+ AppSettingsState.instance.executables.add(newCommand)
+ }
+ }
+ } else {
+ JOptionPane.showMessageDialog(null, "Please select a command to edit.")
+ }
+ }
+ commandTable.columnModel.getColumn(0).apply {
+ preferredWidth = 50
+ maxWidth = 100
+ }
+ commandTable.selectionModel.addListSelectionListener {
+ editCommandButton.isEnabled = commandTable.selectedRow != -1
+ removeCommandButton.isEnabled = commandTable.selectedRow != -1
+ }
+ editCommandButton.isEnabled = false
+ // Call setupCommandTable to initialize commandTable
+ setupCommandTable()
+ }
+
+ override fun createCenterPanel(): JComponent = panel {
+ group("Tasks") {
+ row {
+ cell(singleTaskModeCheckbox)
+ .align(Align.FILL)
+ }
+ row {
+ cell(
+ JBSplitter(false, 0.3f).apply {
+ firstComponent = JBScrollPane(taskTypeList).apply {
+ minimumSize = Dimension(DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)
+ preferredSize = Dimension(DEFAULT_LIST_WIDTH + 100, DEFAULT_LIST_HEIGHT)
+ }
+ secondComponent = JBScrollPane(configPanelContainer).apply {
+ minimumSize = Dimension(DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT / 2)
+ preferredSize = Dimension(DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT)
+ }
+ dividerWidth = 3
+ isShowDividerControls = true
+ isShowDividerIcon = true
+ }
+ )
+ .align(Align.FILL)
+ .resizableColumn()
+ }
+ .resizableRow()
+ }
+ .layout(RowLayout.PARENT_GRID)
+ .resizableRow()
+ group("Settings") {
+ row {
+ cell(autoFixCheckbox)
+ .align(Align.FILL)
+ }
+ row {
+ cell(allowBlockingCheckbox)
+ .align(Align.FILL)
+ }
+ row("Temperature:") {
+ cell(temperatureSlider).align(Align.FILL)
+ cell(temperatureLabel)
+ }
+ row("Command Line Tools:") {
+ cell(
+ JBSplitter(true, 0.9f).apply {
+ firstComponent = JBScrollPane(commandTable).apply {
+ minimumSize = Dimension(350, 100)
+ preferredSize = Dimension(350, 200)
+ }
+ secondComponent = JPanel().apply {
+ layout = BoxLayout(this, BoxLayout.X_AXIS)
+ add(addCommandButton)
+ add(Box.createHorizontalStrut(5))
+ add(editCommandButton)
+ add(Box.createHorizontalStrut(5))
+ add(removeCommandButton)
+ }
+ dividerWidth = 3
+ isShowDividerControls = true
+ isShowDividerIcon = true
+ splitterProportionKey = "planAhead.commands.splitter"
+ })
+ .align(Align.FILL)
+ .resizableColumn()
+ }
+ }
+ }
+
+ override fun doOKAction() {
+ // Save settings from all task type config panels
+ taskConfigs.values.forEach { configPanel ->
+ configPanel.saveSettings()
+ }
+ settings.autoFix = autoFixCheckbox.isSelected
+ settings.allowBlocking = allowBlockingCheckbox.isSelected
+ settings.commandAutoFixCommands = (0 until tableModel.rowCount)
+ .filter { tableModel.getValueAt(it, 0) as Boolean }
+ .map { tableModel.getValueAt(it, 1) as String }
+ // Update the global tool collection
+ settings.commandAutoFixCommands?.forEach { command ->
+ if (!AppSettingsState.instance.executables.contains(command)) {
+ AppSettingsState.instance.executables.add(command)
+ }
+ }
+ super.doOKAction()
+ }
+
+ private fun setupCommandTable() {
+ commandTable.columnModel.getColumn(0).apply {
+ cellEditor = DefaultCellEditor(JCheckBox())
+ preferredWidth = 50
+ maxWidth = 100
+ }
+ tableModel.addTableModelListener { e ->
+ if (e.column == 0) {
+ val row = e.firstRow
+ val value = tableModel.getValueAt(row, 0) as Boolean
+ checkboxStates[row] = value
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PrePlanAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PrePlanAction.kt
index 65c98bf1..4f49a212 100644
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PrePlanAction.kt
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/plan/PrePlanAction.kt
@@ -70,7 +70,7 @@ class PrePlanAction : BaseAction() {
googleApiKey = AppSettingsState.instance.googleApiKey,
googleSearchEngineId = AppSettingsState.instance.googleSearchEngineId,
)
- planSettings = PlanAheadConfigDialog(e.project, planSettings).let {
+ planSettings = PlanConfigDialog(e.project, planSettings).let {
if (!it.showAndGet()) throw RuntimeException("User cancelled")
it.settings
}
diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt
index 1ce4c27e..857c0c49 100644
--- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt
+++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt
@@ -1,7 +1,7 @@
package com.github.simiacryptus.aicoder.config
-import com.github.simiacryptus.aicoder.actions.plan.PlanAheadConfigDialog.Companion.isVisible
+import com.github.simiacryptus.aicoder.actions.plan.PlanConfigDialog.Companion.isVisible
import com.github.simiacryptus.aicoder.util.IdeaChatClient
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileChooser.FileChooser
@@ -21,7 +21,6 @@ import com.simiacryptus.jopenai.models.APIProvider
import com.simiacryptus.jopenai.models.ChatModel
import com.simiacryptus.jopenai.models.ImageModels
import com.simiacryptus.skyenet.core.platform.ApplicationServices
-import com.simiacryptus.skyenet.core.platform.AwsPlatform
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.ActionEvent