-
Notifications
You must be signed in to change notification settings - Fork 0
UI and added new features #1
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
base: main
Are you sure you want to change the base?
Conversation
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 a major UI overhaul and introduces several significant new features to the Lucid VS Code extension, transforming it from a simple Ollama chat interface into a full-featured AI coding assistant with agent capabilities and CLI command execution.
Key Changes
- Agent Mode: New mode that allows AI to propose code changes with user approval before applying
- CLI Command Detection & Execution: Automatically detects and can execute CLI commands from AI responses
- State Persistence: Conversation history and UI state now persist across sessions
- Enhanced Code Actions: Code blocks now have interactive buttons (copy, insert, apply, diff)
- Selection Context: UI now displays and uses active code selection in prompts
Reviewed changes
Copilot reviewed 4 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Added skipLibCheck to speed up compilation |
| package.json | Changed from Ollama-specific defaults to generic API endpoint configuration, removed explicit activation events |
| extension.ts | Added 1300+ lines implementing agent mode, CLI commands, code editing helpers, conversation history, and enhanced message handling |
| ui.html | Complete UI rewrite (900→2472 lines) with mode toggle, agent changes panel, CLI commands UI, and state persistence |
| config.ts | Updated default endpoint and API key header, made model name optional |
| lucid-icon.svg | New SVG icon with Turkish comment "sixty" typo |
| lucid-icon.png | New PNG icon (binary file) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| exec( | ||
| command, | ||
| { | ||
| cwd, | ||
| shell: | ||
| process.platform === "win32" | ||
| ? "powershell.exe" | ||
| : "/bin/zsh", | ||
| }, | ||
| (error: any, stdout: any, stderr: any) => { | ||
| // Display output in terminal | ||
| if (stdout) terminal.sendText(stdout); | ||
| if (stderr) terminal.sendText(stderr); | ||
|
|
||
| if (error) { | ||
| webviewView.webview.postMessage({ | ||
| type: "cliCommandCompleted", | ||
| success: false, | ||
| command: command, | ||
| commandIndex: commandIndex, | ||
| error: error.message, | ||
| }); | ||
| } else { | ||
| webviewView.webview.postMessage({ | ||
| type: "cliCommandCompleted", | ||
| success: true, | ||
| command: command, | ||
| commandIndex: commandIndex, | ||
| }); | ||
| } | ||
| } | ||
| ); |
Copilot
AI
Dec 15, 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 exec function executes shell commands without any input sanitization or validation. This creates a command injection vulnerability where malicious commands extracted from AI responses could be executed with the user's privileges. Consider using execFile instead of exec, implementing a whitelist of allowed commands, or at least sanitizing/validating the command string before execution.
| "onCommand:lucid.closeChatView", | ||
| "onCommand:lucid.dumpState" | ||
| ], | ||
| "activationEvents": [], |
Copilot
AI
Dec 15, 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.
Setting activationEvents to an empty array means the extension will activate on startup. This can impact VS Code's startup performance. Consider using specific activation events like onView:lucid.chatView or onCommand:* to activate the extension only when needed.
| "activationEvents": [], | |
| "activationEvents": [ | |
| "onCommand:lucid.setModel", | |
| "onCommand:lucid.sendFile", | |
| "onCommand:lucid.sendActiveFile", | |
| "onCommand:lucid.sendActiveForEdit", | |
| "onCommand:lucid.sendFiles", | |
| "onCommand:lucid.openChatView", | |
| "onCommand:lucid.closeChatView", | |
| "onCommand:lucid.dumpState" | |
| ], |
| var detectedCommands = []; | ||
| var currentCommandIndex = 0; | ||
| var commandsExecuted = []; |
Copilot
AI
Dec 15, 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 variables detectedCommands, currentCommandIndex, and commandsExecuted are declared twice (lines 843-846 and 844-846). This is redundant and could cause confusion. Remove the duplicate declarations.
| var detectedCommands = []; | |
| var currentCommandIndex = 0; | |
| var commandsExecuted = []; |
| for (let i = 0; i < 24; i++) nonce += chars.charAt(Math.floor(Math.random() * chars.length)); | ||
| const chars = | ||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
| let nonce = ""; |
Copilot
AI
Dec 15, 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 word "sixty" should be a number "60" in this SVG path data. This appears to be a typo that would cause the SVG to render incorrectly.
|
|
||
| if (waitForCompletion) { | ||
| // Use Node.js child_process to run command and wait for completion | ||
| const { exec } = require("child_process"); |
Copilot
AI
Dec 15, 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.
Using require("child_process") dynamically bypasses static analysis and creates a security risk. The child_process module should be imported at the top of the file. Additionally, executing arbitrary CLI commands from user input without proper validation or sandboxing is a critical security vulnerability that could allow command injection attacks.
| var fileHint = document.getElementById("fileHint"); | ||
|
|
||
| // Mode toggle elements | ||
| var modeToggle = document.getElementById("modeToggle"); |
Copilot
AI
Dec 15, 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.
Unused variable modeToggle.
| var modeToggle = document.getElementById("modeToggle"); |
| // Read a specific file's content | ||
| async function readFileContent(filePath: string): Promise<string | null> { | ||
| try { | ||
| const uri = vscode.Uri.file(filePath); | ||
| const bytes = await vscode.workspace.fs.readFile(uri); | ||
| return Buffer.from(bytes).toString("utf8"); | ||
| } catch (e) { | ||
| LucidLogger.error("readFileContent error", e); | ||
| return null; | ||
| } | ||
| } | ||
|
|
Copilot
AI
Dec 15, 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.
Unused function readFileContent.
| // Read a specific file's content | |
| async function readFileContent(filePath: string): Promise<string | null> { | |
| try { | |
| const uri = vscode.Uri.file(filePath); | |
| const bytes = await vscode.workspace.fs.readFile(uri); | |
| return Buffer.from(bytes).toString("utf8"); | |
| } catch (e) { | |
| LucidLogger.error("readFileContent error", e); | |
| return null; | |
| } | |
| } |
| if (fileSearch) { | ||
| fileSearch.addEventListener("input", function () { | ||
| try { | ||
| renderFileList(currentFiles, currentAttached); | ||
| } catch (e) {} | ||
| }); | ||
| // show results on focus as well if the field has content | ||
| fileSearch.addEventListener("focus", function () { | ||
| try { | ||
| renderFileList(currentFiles, currentAttached); | ||
| } catch (e) {} | ||
| }); | ||
| // hide results if clicking outside (simple handler) | ||
| document.addEventListener("click", function (ev) { | ||
| try { | ||
| var target = ev.target; | ||
| if (!target) return; | ||
| if (target === fileSearch) return; | ||
| // if click inside the fileListRoot, ignore | ||
| if ( | ||
| fileListRoot && | ||
| fileListRoot.contains && | ||
| fileListRoot.contains(target) | ||
| ) | ||
| return; | ||
| // otherwise hide dropdown | ||
| if (fileListRoot) fileListRoot.style.display = "none"; | ||
| if (fileHint) fileHint.style.display = "none"; | ||
| } catch (e) {} | ||
| }); | ||
| } |
Copilot
AI
Dec 15, 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 use of variable 'fileSearch' always evaluates to true.
| if (fileSearch) { | |
| fileSearch.addEventListener("input", function () { | |
| try { | |
| renderFileList(currentFiles, currentAttached); | |
| } catch (e) {} | |
| }); | |
| // show results on focus as well if the field has content | |
| fileSearch.addEventListener("focus", function () { | |
| try { | |
| renderFileList(currentFiles, currentAttached); | |
| } catch (e) {} | |
| }); | |
| // hide results if clicking outside (simple handler) | |
| document.addEventListener("click", function (ev) { | |
| try { | |
| var target = ev.target; | |
| if (!target) return; | |
| if (target === fileSearch) return; | |
| // if click inside the fileListRoot, ignore | |
| if ( | |
| fileListRoot && | |
| fileListRoot.contains && | |
| fileListRoot.contains(target) | |
| ) | |
| return; | |
| // otherwise hide dropdown | |
| if (fileListRoot) fileListRoot.style.display = "none"; | |
| if (fileHint) fileHint.style.display = "none"; | |
| } catch (e) {} | |
| }); | |
| } | |
| fileSearch.addEventListener("input", function () { | |
| try { | |
| renderFileList(currentFiles, currentAttached); | |
| } catch (e) {} | |
| }); | |
| // show results on focus as well if the field has content | |
| fileSearch.addEventListener("focus", function () { | |
| try { | |
| renderFileList(currentFiles, currentAttached); | |
| } catch (e) {} | |
| }); | |
| // hide results if clicking outside (simple handler) | |
| document.addEventListener("click", function (ev) { | |
| try { | |
| var target = ev.target; | |
| if (!target) return; | |
| if (target === fileSearch) return; | |
| // if click inside the fileListRoot, ignore | |
| if ( | |
| fileListRoot && | |
| fileListRoot.contains && | |
| fileListRoot.contains(target) | |
| ) | |
| return; | |
| // otherwise hide dropdown | |
| if (fileListRoot) fileListRoot.style.display = "none"; | |
| if (fileHint) fileHint.style.display = "none"; | |
| } catch (e) {} | |
| }); |
| } | ||
| } | ||
| // keyboard navigation for the dropdown | ||
| if (fileSearch) { |
Copilot
AI
Dec 15, 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 use of variable 'fileSearch' always evaluates to true.
No description provided.