diff --git a/codegen-vscode-extension/README.md b/codegen-vscode-extension/README.md
new file mode 100644
index 000000000..f213f58c4
--- /dev/null
+++ b/codegen-vscode-extension/README.md
@@ -0,0 +1,78 @@
+# Codegen VSCode Extension
+
+A VSCode extension for Codegen - AI-powered code generation and assistance.
+
+## Features
+
+- **Ask Questions**: Get answers to your programming questions directly in VSCode
+- **Generate Code**: Generate code snippets based on your descriptions
+- **Explain Code**: Get explanations for selected code
+- **Improve Code**: Get suggestions to improve your code
+- **Fix Code**: Get help fixing bugs in your code
+
+## Requirements
+
+- VSCode 1.78.0 or higher
+- A Codegen API key (get one at [codegen.sh](https://codegen.sh))
+
+## Installation
+
+1. Install the extension from the VSCode Marketplace
+1. Set your Codegen API key in the extension settings
+1. Start using Codegen in your development workflow!
+
+## Extension Settings
+
+This extension contributes the following settings:
+
+- `codegen.apiKey`: Your Codegen API key
+- `codegen.endpoint`: The endpoint for the Codegen API (defaults to https://api.codegen.sh)
+
+## Commands
+
+The extension provides the following commands:
+
+- `codegen.askQuestion`: Ask Codegen a question
+- `codegen.generateCode`: Generate code with Codegen
+- `codegen.explainCode`: Explain selected code
+- `codegen.improveCode`: Improve selected code
+- `codegen.fixCode`: Fix selected code
+
+## Usage
+
+### Ask a Question
+
+1. Open the command palette (`Ctrl+Shift+P` or `Cmd+Shift+P` on macOS)
+1. Type "Ask Codegen a Question" and press Enter
+1. Enter your question and press Enter
+1. View the response in the Codegen Chat view
+
+### Generate Code
+
+1. Open the command palette
+1. Type "Generate Code with Codegen" and press Enter
+1. Describe the code you want to generate
+1. The generated code will be inserted at your cursor position
+
+### Explain Code
+
+1. Select the code you want to explain
+1. Right-click and select "Explain Selected Code" from the context menu
+1. View the explanation in the Codegen Chat view
+
+### Improve Code
+
+1. Select the code you want to improve
+1. Right-click and select "Improve Selected Code" from the context menu
+1. Review the suggested improvements and apply them if desired
+
+### Fix Code
+
+1. Select the code you want to fix
+1. Right-click and select "Fix Selected Code" from the context menu
+1. Optionally provide the error message you're getting
+1. Review the suggested fixes and apply them if desired
+
+## License
+
+MIT
diff --git a/codegen-vscode-extension/media/codegen-icon.svg b/codegen-vscode-extension/media/codegen-icon.svg
new file mode 100644
index 000000000..53aa599b8
--- /dev/null
+++ b/codegen-vscode-extension/media/codegen-icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/codegen-vscode-extension/media/main.css b/codegen-vscode-extension/media/main.css
new file mode 100644
index 000000000..86a842023
--- /dev/null
+++ b/codegen-vscode-extension/media/main.css
@@ -0,0 +1,289 @@
+:root {
+ --container-padding: 10px;
+ --input-padding-vertical: 6px;
+ --input-padding-horizontal: 8px;
+ --input-margin-vertical: 4px;
+ --input-margin-horizontal: 0;
+ --message-max-height: 150px; /* Added for collapsed messages */
+}
+
+body {
+ padding: 0;
+ margin: 0;
+ color: var(--vscode-foreground);
+ font-size: var(--vscode-font-size);
+ font-weight: var(--vscode-font-weight);
+ font-family: var(--vscode-font-family);
+ background-color: var(--vscode-editor-background);
+}
+
+#chat-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ padding: var(--container-padding);
+ box-sizing: border-box;
+}
+
+#messages {
+ flex: 1;
+ overflow-y: auto;
+ margin-bottom: 10px;
+ padding-right: 8px;
+}
+
+.message {
+ margin-bottom: 10px;
+ padding: 8px 12px;
+ border-radius: 6px;
+ max-width: 90%;
+ word-wrap: break-word;
+ cursor: pointer; /* Added to indicate clickable */
+ transition: max-height 0.3s ease; /* Added for smooth transition */
+}
+
+.user-message {
+ background-color: var(--vscode-button-background);
+ color: var(--vscode-button-foreground);
+ align-self: flex-end;
+ margin-left: auto;
+}
+
+.assistant-message {
+ background-color: var(--vscode-editor-inactiveSelectionBackground);
+ color: var(--vscode-editor-foreground);
+ align-self: flex-start;
+}
+
+.message-content {
+ margin-bottom: 5px;
+ overflow: hidden; /* Added for collapsed state */
+}
+
+.code-block {
+ background-color: var(--vscode-editor-background);
+ border: 1px solid var(--vscode-editor-lineHighlightBorder);
+ border-radius: 3px;
+ padding: 8px;
+ margin-top: 5px;
+ font-family: var(--vscode-editor-font-family);
+ font-size: var(--vscode-editor-font-size);
+ white-space: pre-wrap;
+ overflow-x: auto;
+}
+
+.code-actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 5px;
+}
+
+.code-action-button {
+ background-color: var(--vscode-button-secondaryBackground);
+ color: var(--vscode-button-secondaryForeground);
+ border: none;
+ padding: 4px 8px;
+ border-radius: 2px;
+ cursor: pointer;
+ font-size: 12px;
+ margin-left: 5px;
+}
+
+.code-action-button:hover {
+ background-color: var(--vscode-button-secondaryHoverBackground);
+}
+
+.timestamp {
+ font-size: 10px;
+ color: var(--vscode-descriptionForeground);
+ text-align: right;
+ margin-top: 2px;
+}
+
+#input-container {
+ display: flex;
+ padding: 8px 0;
+}
+
+#message-input {
+ flex: 1;
+ height: 60px;
+ padding: var(--input-padding-vertical) var(--input-padding-horizontal);
+ border: 1px solid var(--vscode-input-border);
+ background-color: var(--vscode-input-background);
+ color: var(--vscode-input-foreground);
+ resize: none;
+ font-family: var(--vscode-font-family);
+ font-size: var(--vscode-font-size);
+ border-radius: 2px;
+}
+
+#message-input:focus {
+ outline: 1px solid var(--vscode-focusBorder);
+}
+
+#send-button {
+ margin-left: 8px;
+ padding: 0 14px;
+ background-color: var(--vscode-button-background);
+ color: var(--vscode-button-foreground);
+ border: none;
+ cursor: pointer;
+ border-radius: 2px;
+}
+
+#send-button:hover {
+ background-color: var(--vscode-button-hoverBackground);
+}
+
+#loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.3);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.hidden {
+ display: none !important;
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 4px solid rgba(255, 255, 255, 0.3);
+ border-radius: 50%;
+ border-top-color: var(--vscode-button-background);
+ animation: spin 1s ease-in-out infinite;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* Markdown styling */
+.markdown h1,
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+ margin-top: 16px;
+ margin-bottom: 8px;
+ font-weight: 600;
+}
+
+.markdown p {
+ margin-top: 0;
+ margin-bottom: 8px;
+}
+
+.markdown ul,
+.markdown ol {
+ padding-left: 20px;
+ margin-top: 0;
+ margin-bottom: 8px;
+}
+
+.markdown code {
+ font-family: var(--vscode-editor-font-family);
+ background-color: var(--vscode-textCodeBlock-background);
+ padding: 2px 4px;
+ border-radius: 3px;
+}
+
+.markdown pre {
+ background-color: var(--vscode-textCodeBlock-background);
+ padding: 8px;
+ border-radius: 3px;
+ overflow-x: auto;
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
+.markdown pre code {
+ background-color: transparent;
+ padding: 0;
+}
+
+.markdown a {
+ color: var(--vscode-textLink-foreground);
+ text-decoration: none;
+}
+
+.markdown a:hover {
+ text-decoration: underline;
+}
+
+.markdown blockquote {
+ border-left: 3px solid var(--vscode-textBlockQuote-border);
+ padding-left: 8px;
+ margin-left: 0;
+ margin-right: 0;
+ color: var(--vscode-textBlockQuote-foreground);
+}
+
+.markdown table {
+ border-collapse: collapse;
+ width: 100%;
+ margin-bottom: 16px;
+}
+
+.markdown th,
+.markdown td {
+ border: 1px solid var(--vscode-editor-lineHighlightBorder);
+ padding: 6px 13px;
+}
+
+.markdown th {
+ background-color: var(--vscode-editor-inactiveSelectionBackground);
+ font-weight: 600;
+}
+
+/* Added for collapsed messages */
+.message-content.collapsed {
+ max-height: var(--message-max-height);
+ overflow: hidden;
+ position: relative;
+}
+
+/* Added gradient fade for collapsed messages */
+.message-content.collapsed::after {
+ content: "";
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 40px;
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ var(--vscode-editor-inactiveSelectionBackground)
+ );
+ pointer-events: none;
+}
+
+/* Adjust gradient color for user messages */
+.user-message .message-content.collapsed::after {
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ var(--vscode-button-background)
+ );
+}
+
+/* Added expand indicator */
+.expand-indicator {
+ text-align: center;
+ font-size: 12px;
+ color: var(--vscode-descriptionForeground);
+ margin-top: 4px;
+ user-select: none;
+}
diff --git a/codegen-vscode-extension/media/main.js b/codegen-vscode-extension/media/main.js
new file mode 100644
index 000000000..fa098d2c7
--- /dev/null
+++ b/codegen-vscode-extension/media/main.js
@@ -0,0 +1,227 @@
+(() => {
+ // Get VS Code API
+ const vscode = acquireVsCodeApi();
+
+ // DOM elements
+ const messagesContainer = document.getElementById("messages");
+ const messageInput = document.getElementById("message-input");
+ const sendButton = document.getElementById("send-button");
+ const loadingElement = document.getElementById("loading");
+
+ // Store messages
+ let messages = [];
+
+ // Initialize
+ window.addEventListener("message", (event) => {
+ const message = event.data;
+
+ switch (message.command) {
+ case "updateMessages":
+ messages = message.messages;
+ renderMessages();
+ break;
+ case "showLoading":
+ toggleLoading(message.value);
+ break;
+ }
+ });
+
+ // Send message when button is clicked
+ sendButton.addEventListener("click", () => {
+ sendMessage();
+ });
+
+ // Send message when Enter is pressed (without Shift)
+ messageInput.addEventListener("keydown", (e) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ sendMessage();
+ }
+ });
+
+ // Send message to extension
+ function sendMessage() {
+ const text = messageInput.value.trim();
+ if (text) {
+ vscode.postMessage({
+ command: "sendMessage",
+ text: text,
+ });
+ messageInput.value = "";
+ }
+ }
+
+ // Check if content needs to be collapsed
+ function shouldCollapseContent(contentElement) {
+ // Get the computed max-height value from CSS variable
+ const maxHeight = Number.parseInt(
+ getComputedStyle(document.documentElement).getPropertyValue(
+ "--message-max-height",
+ ),
+ );
+
+ // If the content is taller than the max height, it should be collapsed
+ return contentElement.scrollHeight > maxHeight;
+ }
+
+ // Toggle message expansion
+ function toggleMessageExpansion(contentElement, indicatorElement) {
+ if (contentElement.classList.contains("collapsed")) {
+ // Expand
+ contentElement.classList.remove("collapsed");
+ indicatorElement.textContent = "Click to collapse";
+ } else {
+ // Collapse
+ contentElement.classList.add("collapsed");
+ indicatorElement.textContent = "Click to expand";
+ }
+ }
+
+ // Render all messages
+ function renderMessages() {
+ messagesContainer.innerHTML = "";
+
+ messages.forEach((message) => {
+ const messageElement = document.createElement("div");
+ messageElement.className = `message ${message.role}-message`;
+
+ // Message content
+ const contentElement = document.createElement("div");
+ contentElement.className = "message-content markdown";
+ contentElement.innerHTML = formatMarkdown(message.content);
+ messageElement.appendChild(contentElement);
+
+ // Add expand indicator element
+ const expandIndicator = document.createElement("div");
+ expandIndicator.className = "expand-indicator";
+ messageElement.appendChild(expandIndicator);
+
+ // Check if content should be collapsed (after adding to DOM to get accurate height)
+ setTimeout(() => {
+ if (shouldCollapseContent(contentElement)) {
+ contentElement.classList.add("collapsed");
+ expandIndicator.textContent = "Click to expand";
+ } else {
+ expandIndicator.style.display = "none"; // Hide indicator if not needed
+ }
+ }, 0);
+
+ // Add click event to toggle expansion
+ messageElement.addEventListener("click", (e) => {
+ // Don't toggle if clicking on code action buttons
+ if (e.target.closest(".code-action-button")) {
+ return;
+ }
+
+ // Toggle expansion
+ if (shouldCollapseContent(contentElement)) {
+ toggleMessageExpansion(contentElement, expandIndicator);
+ }
+ });
+
+ // Code block (if any)
+ if (message.code) {
+ const codeElement = document.createElement("div");
+ codeElement.className = "code-block";
+ codeElement.textContent = message.code;
+ messageElement.appendChild(codeElement);
+
+ // Code actions
+ const actionsElement = document.createElement("div");
+ actionsElement.className = "code-actions";
+
+ // Insert code button
+ const insertButton = document.createElement("button");
+ insertButton.className = "code-action-button";
+ insertButton.textContent = "Insert Code";
+ insertButton.addEventListener("click", () => {
+ vscode.postMessage({
+ command: "insertCode",
+ code: message.code,
+ });
+ });
+ actionsElement.appendChild(insertButton);
+
+ // Copy code button
+ const copyButton = document.createElement("button");
+ copyButton.className = "code-action-button";
+ copyButton.textContent = "Copy";
+ copyButton.addEventListener("click", () => {
+ navigator.clipboard.writeText(message.code);
+ copyButton.textContent = "Copied!";
+ setTimeout(() => {
+ copyButton.textContent = "Copy";
+ }, 2000);
+ });
+ actionsElement.appendChild(copyButton);
+
+ messageElement.appendChild(actionsElement);
+ }
+
+ // Timestamp
+ const timestampElement = document.createElement("div");
+ timestampElement.className = "timestamp";
+ timestampElement.textContent = formatTimestamp(message.timestamp);
+ messageElement.appendChild(timestampElement);
+
+ messagesContainer.appendChild(messageElement);
+ });
+
+ // Scroll to bottom
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
+ }
+
+ // Format timestamp
+ function formatTimestamp(timestamp) {
+ const date = new Date(timestamp);
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
+ }
+
+ // Toggle loading indicator
+ function toggleLoading(show) {
+ if (show) {
+ loadingElement.classList.remove("hidden");
+ } else {
+ loadingElement.classList.add("hidden");
+ }
+ }
+
+ // Format markdown to HTML
+ function formatMarkdown(text) {
+ // This is a very simple markdown parser
+ // In a real extension, you might want to use a library like marked.js
+
+ // Code blocks
+ text = text.replace(
+ /```(\w*)\n([\s\S]*?)\n```/g,
+ "
$2
",
+ );
+
+ // Inline code
+ text = text.replace(/`([^`]+)`/g, "$1");
+
+ // Headers
+ text = text.replace(/^### (.*$)/gm, "$1
");
+ text = text.replace(/^## (.*$)/gm, "$1
");
+ text = text.replace(/^# (.*$)/gm, "$1
");
+
+ // Bold
+ text = text.replace(/\*\*(.*?)\*\*/g, "$1");
+
+ // Italic
+ text = text.replace(/\*(.*?)\*/g, "$1");
+
+ // Lists
+ text = text.replace(/^\s*\*\s(.*$)/gm, "$1");
+ text = text.replace(/(.*<\/li>)/g, "");
+
+ // Links
+ text = text.replace(/\[(.*?)\]\((.*?)\)/g, '$1');
+
+ // Paragraphs
+ text = text.replace(/\n\n/g, "");
+ text = "
" + text + "
";
+
+ return text;
+ }
+})();
diff --git a/codegen-vscode-extension/package.json b/codegen-vscode-extension/package.json
new file mode 100644
index 000000000..18475fd69
--- /dev/null
+++ b/codegen-vscode-extension/package.json
@@ -0,0 +1,126 @@
+{
+ "name": "codegen-vscode-extension",
+ "displayName": "Codegen",
+ "description": "VSCode extension for Codegen - AI-powered code generation and assistance",
+ "version": "0.1.0",
+ "engines": {
+ "vscode": "^1.78.0"
+ },
+ "categories": ["Programming Languages", "Machine Learning", "Other"],
+ "activationEvents": ["onStartupFinished"],
+ "main": "./dist/extension.js",
+ "contributes": {
+ "commands": [
+ {
+ "command": "codegen.askQuestion",
+ "title": "Ask Codegen a Question",
+ "category": "Codegen"
+ },
+ {
+ "command": "codegen.generateCode",
+ "title": "Generate Code with Codegen",
+ "category": "Codegen"
+ },
+ {
+ "command": "codegen.explainCode",
+ "title": "Explain Selected Code",
+ "category": "Codegen"
+ },
+ {
+ "command": "codegen.improveCode",
+ "title": "Improve Selected Code",
+ "category": "Codegen"
+ },
+ {
+ "command": "codegen.fixCode",
+ "title": "Fix Selected Code",
+ "category": "Codegen"
+ }
+ ],
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "codegen-sidebar",
+ "title": "Codegen",
+ "icon": "media/codegen-icon.svg"
+ }
+ ]
+ },
+ "views": {
+ "codegen-sidebar": [
+ {
+ "type": "webview",
+ "id": "codegen.chatView",
+ "name": "Codegen Chat"
+ }
+ ]
+ },
+ "configuration": {
+ "title": "Codegen",
+ "properties": {
+ "codegen.apiKey": {
+ "type": "string",
+ "default": "",
+ "description": "API key for Codegen services"
+ },
+ "codegen.endpoint": {
+ "type": "string",
+ "default": "https://api.codegen.sh",
+ "description": "Endpoint for Codegen API"
+ }
+ }
+ },
+ "menus": {
+ "editor/context": [
+ {
+ "command": "codegen.explainCode",
+ "when": "editorHasSelection",
+ "group": "codegen"
+ },
+ {
+ "command": "codegen.improveCode",
+ "when": "editorHasSelection",
+ "group": "codegen"
+ },
+ {
+ "command": "codegen.fixCode",
+ "when": "editorHasSelection",
+ "group": "codegen"
+ },
+ {
+ "command": "codegen.generateCode",
+ "group": "codegen"
+ }
+ ]
+ }
+ },
+ "scripts": {
+ "vscode:prepublish": "npm run package",
+ "compile": "webpack",
+ "watch": "webpack --watch",
+ "package": "webpack --mode production --devtool source-map --config ./webpack.config.js",
+ "compile-tests": "tsc -p . --outDir out",
+ "watch-tests": "tsc -p . -w --outDir out",
+ "lint": "eslint src --ext ts",
+ "test": "node ./out/test/runTest.js"
+ },
+ "devDependencies": {
+ "@types/glob": "^8.1.0",
+ "@types/mocha": "^10.0.1",
+ "@types/node": "16.x",
+ "@types/vscode": "^1.78.0",
+ "@typescript-eslint/eslint-plugin": "^5.59.1",
+ "@typescript-eslint/parser": "^5.59.1",
+ "@vscode/test-electron": "^2.3.0",
+ "eslint": "^8.39.0",
+ "glob": "^8.1.0",
+ "mocha": "^10.2.0",
+ "ts-loader": "^9.4.2",
+ "typescript": "^5.0.4",
+ "webpack": "^5.81.0",
+ "webpack-cli": "^5.0.2"
+ },
+ "dependencies": {
+ "axios": "^1.4.0"
+ }
+}
diff --git a/codegen-vscode-extension/src/api/codegenAPI.ts b/codegen-vscode-extension/src/api/codegenAPI.ts
new file mode 100644
index 000000000..f201c20e0
--- /dev/null
+++ b/codegen-vscode-extension/src/api/codegenAPI.ts
@@ -0,0 +1,155 @@
+import axios, { type AxiosInstance } from "axios";
+import * as vscode from "vscode";
+
+export interface CodegenResponse {
+ text: string;
+ code?: string;
+ language?: string;
+}
+
+export class CodegenAPI {
+ private client: AxiosInstance;
+ private apiKey = "";
+ private endpoint = "";
+
+ constructor() {
+ this.loadConfiguration();
+
+ this.client = axios.create({
+ baseURL: this.endpoint,
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${this.apiKey}`,
+ },
+ });
+
+ // Listen for configuration changes
+ vscode.workspace.onDidChangeConfiguration((e) => {
+ if (
+ e.affectsConfiguration("codegen.apiKey") ||
+ e.affectsConfiguration("codegen.endpoint")
+ ) {
+ this.loadConfiguration();
+ this.updateClient();
+ }
+ });
+ }
+
+ private loadConfiguration() {
+ const config = vscode.workspace.getConfiguration("codegen");
+ this.apiKey = config.get("apiKey") || "";
+ this.endpoint = config.get("endpoint") || "https://api.codegen.sh";
+ }
+
+ private updateClient() {
+ this.client = axios.create({
+ baseURL: this.endpoint,
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${this.apiKey}`,
+ },
+ });
+ }
+
+ public async validateApiKey(): Promise {
+ if (!this.apiKey) {
+ return false;
+ }
+
+ try {
+ // Make a simple request to validate the API key
+ await this.client.get("/api/validate");
+ return true;
+ } catch (error) {
+ console.error("API key validation failed:", error);
+ return false;
+ }
+ }
+
+ public async askQuestion(
+ question: string,
+ context?: string,
+ ): Promise {
+ try {
+ const response = await this.client.post("/api/ask", {
+ question,
+ context,
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error("Error asking question:", error);
+ throw new Error("Failed to get response from Codegen API");
+ }
+ }
+
+ public async generateCode(
+ prompt: string,
+ language?: string,
+ ): Promise {
+ try {
+ const response = await this.client.post("/api/generate", {
+ prompt,
+ language,
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error("Error generating code:", error);
+ throw new Error("Failed to generate code from Codegen API");
+ }
+ }
+
+ public async explainCode(
+ code: string,
+ language?: string,
+ ): Promise {
+ try {
+ const response = await this.client.post("/api/explain", {
+ code,
+ language,
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error("Error explaining code:", error);
+ throw new Error("Failed to explain code from Codegen API");
+ }
+ }
+
+ public async improveCode(
+ code: string,
+ language?: string,
+ ): Promise {
+ try {
+ const response = await this.client.post("/api/improve", {
+ code,
+ language,
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error("Error improving code:", error);
+ throw new Error("Failed to improve code from Codegen API");
+ }
+ }
+
+ public async fixCode(
+ code: string,
+ error?: string,
+ language?: string,
+ ): Promise {
+ try {
+ const response = await this.client.post("/api/fix", {
+ code,
+ error,
+ language,
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error("Error fixing code:", error);
+ throw new Error("Failed to fix code from Codegen API");
+ }
+ }
+}
diff --git a/codegen-vscode-extension/src/commands.ts b/codegen-vscode-extension/src/commands.ts
new file mode 100644
index 000000000..e605d4e0e
--- /dev/null
+++ b/codegen-vscode-extension/src/commands.ts
@@ -0,0 +1,328 @@
+import * as vscode from "vscode";
+import type { CodegenAPI } from "./api/codegenAPI";
+import type { ChatViewProvider } from "./providers/chatViewProvider";
+import { getDocumentLanguage, getSelectedText, insertText } from "./utils";
+
+export function registerCommands(
+ context: vscode.ExtensionContext,
+ api: CodegenAPI,
+ chatViewProvider: ChatViewProvider,
+) {
+ // Ask a question
+ context.subscriptions.push(
+ vscode.commands.registerCommand("codegen.askQuestion", async () => {
+ const question = await vscode.window.showInputBox({
+ prompt: "What would you like to ask Codegen?",
+ placeHolder: "Ask a programming question...",
+ });
+
+ if (!question) {
+ return;
+ }
+
+ try {
+ vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: "Asking Codegen...",
+ cancellable: false,
+ },
+ async (progress) => {
+ progress.report({ increment: 0 });
+
+ const response = await api.askQuestion(question);
+
+ progress.report({ increment: 100 });
+
+ // Send to chat view
+ chatViewProvider.addMessage("user", question);
+ chatViewProvider.addMessage("assistant", response.text);
+
+ // Show the chat view
+ vscode.commands.executeCommand("codegen.chatView.focus");
+
+ return response;
+ },
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Error: ${error.message}`);
+ }
+ }),
+ );
+
+ // Generate code
+ context.subscriptions.push(
+ vscode.commands.registerCommand("codegen.generateCode", async () => {
+ const prompt = await vscode.window.showInputBox({
+ prompt: "Describe the code you want to generate",
+ placeHolder: "Generate a function that...",
+ });
+
+ if (!prompt) {
+ return;
+ }
+
+ const editor = vscode.window.activeTextEditor;
+ const language = editor
+ ? getDocumentLanguage(editor.document)
+ : undefined;
+
+ try {
+ vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: "Generating code...",
+ cancellable: false,
+ },
+ async (progress) => {
+ progress.report({ increment: 0 });
+
+ const response = await api.generateCode(prompt, language);
+
+ progress.report({ increment: 100 });
+
+ if (editor && response.code) {
+ // Insert the generated code at the cursor position
+ insertText(editor, response.code);
+ }
+
+ // Send to chat view
+ chatViewProvider.addMessage("user", `Generate code: ${prompt}`);
+ chatViewProvider.addMessage(
+ "assistant",
+ response.text,
+ response.code,
+ );
+
+ return response;
+ },
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Error: ${error.message}`);
+ }
+ }),
+ );
+
+ // Explain code
+ context.subscriptions.push(
+ vscode.commands.registerCommand("codegen.explainCode", async () => {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ vscode.window.showErrorMessage("No active editor");
+ return;
+ }
+
+ const selectedText = getSelectedText(editor);
+ if (!selectedText) {
+ vscode.window.showErrorMessage("No code selected");
+ return;
+ }
+
+ const language = getDocumentLanguage(editor.document);
+
+ try {
+ vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: "Explaining code...",
+ cancellable: false,
+ },
+ async (progress) => {
+ progress.report({ increment: 0 });
+
+ const response = await api.explainCode(selectedText, language);
+
+ progress.report({ increment: 100 });
+
+ // Send to chat view
+ chatViewProvider.addMessage(
+ "user",
+ `Explain this code:\n\`\`\`${language}\n${selectedText}\n\`\`\``,
+ );
+ chatViewProvider.addMessage("assistant", response.text);
+
+ // Show the chat view
+ vscode.commands.executeCommand("codegen.chatView.focus");
+
+ return response;
+ },
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Error: ${error.message}`);
+ }
+ }),
+ );
+
+ // Improve code
+ context.subscriptions.push(
+ vscode.commands.registerCommand("codegen.improveCode", async () => {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ vscode.window.showErrorMessage("No active editor");
+ return;
+ }
+
+ const selectedText = getSelectedText(editor);
+ if (!selectedText) {
+ vscode.window.showErrorMessage("No code selected");
+ return;
+ }
+
+ const language = getDocumentLanguage(editor.document);
+
+ try {
+ vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: "Improving code...",
+ cancellable: false,
+ },
+ async (progress) => {
+ progress.report({ increment: 0 });
+
+ const response = await api.improveCode(selectedText, language);
+
+ progress.report({ increment: 100 });
+
+ if (response.code) {
+ // Show diff and ask if user wants to apply changes
+ const document = editor.document;
+ const selection = editor.selection;
+
+ const diffEditor =
+ await vscode.diff.createTextDocumentAndEditorEdit(
+ document,
+ selection,
+ response.code,
+ );
+
+ // Add buttons to apply or discard changes
+ const applyChanges = "Apply Changes";
+ const discardChanges = "Discard";
+
+ const choice = await vscode.window.showInformationMessage(
+ "Review the suggested improvements",
+ applyChanges,
+ discardChanges,
+ );
+
+ if (choice === applyChanges) {
+ // Replace the selected text with the improved code
+ editor.edit((editBuilder) => {
+ editBuilder.replace(selection, response.code || "");
+ });
+ }
+ }
+
+ // Send to chat view
+ chatViewProvider.addMessage(
+ "user",
+ `Improve this code:\n\`\`\`${language}\n${selectedText}\n\`\`\``,
+ );
+ chatViewProvider.addMessage(
+ "assistant",
+ response.text,
+ response.code,
+ );
+
+ return response;
+ },
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Error: ${error.message}`);
+ }
+ }),
+ );
+
+ // Fix code
+ context.subscriptions.push(
+ vscode.commands.registerCommand("codegen.fixCode", async () => {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ vscode.window.showErrorMessage("No active editor");
+ return;
+ }
+
+ const selectedText = getSelectedText(editor);
+ if (!selectedText) {
+ vscode.window.showErrorMessage("No code selected");
+ return;
+ }
+
+ const language = getDocumentLanguage(editor.document);
+
+ // Ask for error message
+ const errorMessage = await vscode.window.showInputBox({
+ prompt: "What error are you getting? (optional)",
+ placeHolder: "Error message or description of the issue",
+ });
+
+ try {
+ vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: "Fixing code...",
+ cancellable: false,
+ },
+ async (progress) => {
+ progress.report({ increment: 0 });
+
+ const response = await api.fixCode(
+ selectedText,
+ errorMessage,
+ language,
+ );
+
+ progress.report({ increment: 100 });
+
+ if (response.code) {
+ // Show diff and ask if user wants to apply changes
+ const document = editor.document;
+ const selection = editor.selection;
+
+ const diffEditor =
+ await vscode.diff.createTextDocumentAndEditorEdit(
+ document,
+ selection,
+ response.code,
+ );
+
+ // Add buttons to apply or discard changes
+ const applyChanges = "Apply Changes";
+ const discardChanges = "Discard";
+
+ const choice = await vscode.window.showInformationMessage(
+ "Review the suggested fixes",
+ applyChanges,
+ discardChanges,
+ );
+
+ if (choice === applyChanges) {
+ // Replace the selected text with the fixed code
+ editor.edit((editBuilder) => {
+ editBuilder.replace(selection, response.code || "");
+ });
+ }
+ }
+
+ // Send to chat view
+ const userMessage = errorMessage
+ ? `Fix this code (error: ${errorMessage}):\n\`\`\`${language}\n${selectedText}\n\`\`\``
+ : `Fix this code:\n\`\`\`${language}\n${selectedText}\n\`\`\``;
+
+ chatViewProvider.addMessage("user", userMessage);
+ chatViewProvider.addMessage(
+ "assistant",
+ response.text,
+ response.code,
+ );
+
+ return response;
+ },
+ );
+ } catch (error) {
+ vscode.window.showErrorMessage(`Error: ${error.message}`);
+ }
+ }),
+ );
+}
diff --git a/codegen-vscode-extension/src/extension.ts b/codegen-vscode-extension/src/extension.ts
new file mode 100644
index 000000000..fa01b5d29
--- /dev/null
+++ b/codegen-vscode-extension/src/extension.ts
@@ -0,0 +1,39 @@
+import * as vscode from "vscode";
+import { CodegenAPI } from "./api/codegenAPI";
+import { registerCommands } from "./commands";
+import { ChatViewProvider } from "./providers/chatViewProvider";
+
+export function activate(context: vscode.ExtensionContext) {
+ console.log("Codegen extension is now active!");
+
+ // Initialize the API client
+ const api = new CodegenAPI();
+
+ // Register the chat view provider
+ const chatViewProvider = new ChatViewProvider(context.extensionUri, api);
+ context.subscriptions.push(
+ vscode.window.registerWebviewViewProvider(
+ "codegen.chatView",
+ chatViewProvider,
+ { webviewOptions: { retainContextWhenHidden: true } },
+ ),
+ );
+
+ // Register commands
+ registerCommands(context, api, chatViewProvider);
+
+ // Status bar item
+ const statusBarItem = vscode.window.createStatusBarItem(
+ vscode.StatusBarAlignment.Right,
+ 100,
+ );
+ statusBarItem.text = "$(sparkle) Codegen";
+ statusBarItem.tooltip = "Codegen AI Assistant";
+ statusBarItem.command = "codegen.askQuestion";
+ statusBarItem.show();
+ context.subscriptions.push(statusBarItem);
+}
+
+export function deactivate() {
+ // Clean up resources
+}
diff --git a/codegen-vscode-extension/src/providers/chatViewProvider.ts b/codegen-vscode-extension/src/providers/chatViewProvider.ts
new file mode 100644
index 000000000..d10774f39
--- /dev/null
+++ b/codegen-vscode-extension/src/providers/chatViewProvider.ts
@@ -0,0 +1,159 @@
+import * as vscode from "vscode";
+import type { CodegenAPI } from "../api/codegenAPI";
+
+interface ChatMessage {
+ role: "user" | "assistant";
+ content: string;
+ code?: string;
+ timestamp: number;
+}
+
+export class ChatViewProvider implements vscode.WebviewViewProvider {
+ public static readonly viewType = "codegen.chatView";
+ private _view?: vscode.WebviewView;
+ private _messages: ChatMessage[] = [];
+
+ constructor(
+ private readonly _extensionUri: vscode.Uri,
+ private readonly _api: CodegenAPI,
+ ) {}
+
+ public resolveWebviewView(
+ webviewView: vscode.WebviewView,
+ context: vscode.WebviewViewResolveContext,
+ _token: vscode.CancellationToken,
+ ) {
+ this._view = webviewView;
+
+ webviewView.webview.options = {
+ enableScripts: true,
+ localResourceRoots: [this._extensionUri],
+ };
+
+ webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
+
+ // Handle messages from the webview
+ webviewView.webview.onDidReceiveMessage(async (message) => {
+ switch (message.command) {
+ case "sendMessage":
+ await this._handleUserMessage(message.text);
+ break;
+ case "clearChat":
+ this._messages = [];
+ this._updateWebview();
+ break;
+ case "insertCode":
+ this._insertCodeToEditor(message.code);
+ break;
+ }
+ });
+
+ // Update the webview with existing messages
+ this._updateWebview();
+ }
+
+ public addMessage(
+ role: "user" | "assistant",
+ content: string,
+ code?: string,
+ ) {
+ this._messages.push({
+ role,
+ content,
+ code,
+ timestamp: Date.now(),
+ });
+
+ this._updateWebview();
+ }
+
+ private async _handleUserMessage(text: string) {
+ // Add user message to chat
+ this.addMessage("user", text);
+
+ try {
+ // Show loading indicator
+ this._view?.webview.postMessage({ command: "showLoading", value: true });
+
+ // Get response from API
+ const response = await this._api.askQuestion(text);
+
+ // Add assistant message to chat
+ this.addMessage("assistant", response.text, response.code);
+
+ // Hide loading indicator
+ this._view?.webview.postMessage({ command: "showLoading", value: false });
+ } catch (error) {
+ // Hide loading indicator
+ this._view?.webview.postMessage({ command: "showLoading", value: false });
+
+ // Show error message
+ this.addMessage("assistant", `Error: ${error.message}`);
+ }
+ }
+
+ private _updateWebview() {
+ if (this._view) {
+ this._view.webview.postMessage({
+ command: "updateMessages",
+ messages: this._messages,
+ });
+ }
+ }
+
+ private _insertCodeToEditor(code: string) {
+ const editor = vscode.window.activeTextEditor;
+ if (editor) {
+ editor.edit((editBuilder) => {
+ editBuilder.insert(editor.selection.active, code);
+ });
+ }
+ }
+
+ private _getHtmlForWebview(webview: vscode.Webview) {
+ // Create URIs for scripts and styles
+ const scriptUri = webview.asWebviewUri(
+ vscode.Uri.joinPath(this._extensionUri, "media", "main.js"),
+ );
+ const styleUri = webview.asWebviewUri(
+ vscode.Uri.joinPath(this._extensionUri, "media", "main.css"),
+ );
+
+ // Use a nonce to allow only specific scripts to be run
+ const nonce = this._getNonce();
+
+ return `
+
+
+
+
+
+
+ Codegen Chat
+
+
+
+
+
+ `;
+ }
+
+ private _getNonce() {
+ let text = "";
+ const possible =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ for (let i = 0; i < 32; i++) {
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
+ }
+ return text;
+ }
+}
diff --git a/codegen-vscode-extension/src/utils.ts b/codegen-vscode-extension/src/utils.ts
new file mode 100644
index 000000000..f0478eba0
--- /dev/null
+++ b/codegen-vscode-extension/src/utils.ts
@@ -0,0 +1,110 @@
+import * as vscode from "vscode";
+
+/**
+ * Get the selected text from the editor
+ */
+export function getSelectedText(editor: vscode.TextEditor): string {
+ const selection = editor.selection;
+ if (selection.isEmpty) {
+ return "";
+ }
+ return editor.document.getText(selection);
+}
+
+/**
+ * Get the language of the document
+ */
+export function getDocumentLanguage(document: vscode.TextDocument): string {
+ return document.languageId;
+}
+
+/**
+ * Insert text at the current cursor position
+ */
+export function insertText(editor: vscode.TextEditor, text: string): void {
+ const selection = editor.selection;
+ editor.edit((editBuilder) => {
+ editBuilder.insert(selection.active, text);
+ });
+}
+
+/**
+ * Get the current file path
+ */
+export function getCurrentFilePath(): string | undefined {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ return undefined;
+ }
+ return editor.document.uri.fsPath;
+}
+
+/**
+ * Get the current workspace folder
+ */
+export function getCurrentWorkspaceFolder():
+ | vscode.WorkspaceFolder
+ | undefined {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ return undefined;
+ }
+ return vscode.workspace.getWorkspaceFolder(editor.document.uri);
+}
+
+/**
+ * Get the current project name
+ */
+export function getCurrentProjectName(): string | undefined {
+ const workspaceFolder = getCurrentWorkspaceFolder();
+ if (!workspaceFolder) {
+ return undefined;
+ }
+ return workspaceFolder.name;
+}
+
+/**
+ * Get the current file name
+ */
+export function getCurrentFileName(): string | undefined {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ return undefined;
+ }
+ return editor.document.fileName.split(/[\\/]/).pop();
+}
+
+/**
+ * Get the current line of code
+ */
+export function getCurrentLine(editor: vscode.TextEditor): string {
+ const position = editor.selection.active;
+ const line = editor.document.lineAt(position.line);
+ return line.text;
+}
+
+/**
+ * Get the current function or class
+ * This is a simple implementation and might not work for all languages
+ */
+export function getCurrentFunction(
+ editor: vscode.TextEditor,
+): string | undefined {
+ const document = editor.document;
+ const position = editor.selection.active;
+
+ // Simple implementation - search backwards for function or class definition
+ for (let i = position.line; i >= 0; i--) {
+ const line = document.lineAt(i).text.trim();
+ if (
+ line.startsWith("function ") ||
+ line.startsWith("class ") ||
+ line.startsWith("def ") ||
+ line.match(/^[a-zA-Z0-9_]+\s*\([^)]*\)\s*{/)
+ ) {
+ return line;
+ }
+ }
+
+ return undefined;
+}
diff --git a/codegen-vscode-extension/tsconfig.json b/codegen-vscode-extension/tsconfig.json
new file mode 100644
index 000000000..0749ae80f
--- /dev/null
+++ b/codegen-vscode-extension/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "ES2020",
+ "outDir": "out",
+ "lib": ["ES2020", "DOM"],
+ "sourceMap": true,
+ "rootDir": "src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "exclude": ["node_modules", ".vscode-test"]
+}
diff --git a/codegen-vscode-extension/webpack.config.js b/codegen-vscode-extension/webpack.config.js
new file mode 100644
index 000000000..8fbf295ab
--- /dev/null
+++ b/codegen-vscode-extension/webpack.config.js
@@ -0,0 +1,34 @@
+const path = require("path");
+
+const config = {
+ target: "node",
+ entry: "./src/extension.ts",
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "extension.js",
+ libraryTarget: "commonjs2",
+ devtoolModuleFilenameTemplate: "../[resource-path]",
+ },
+ devtool: "source-map",
+ externals: {
+ vscode: "commonjs vscode",
+ },
+ resolve: {
+ extensions: [".ts", ".js"],
+ },
+ module: {
+ rules: [
+ {
+ test: /\.ts$/,
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: "ts-loader",
+ },
+ ],
+ },
+ ],
+ },
+};
+
+module.exports = config;