${status.shortcut} and run OpenClaude commands from anywhere.
+ diff --git a/README.md b/README.md index cdfd9142..f37611e9 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,11 @@ Best if you want local inference on Apple Silicon with Atomic Chat. See [Advance --- + +## VS Code Extension + +Want a native VS Code experience? Use the in-repo extension at `vscode-extension/openclaude-vscode` for one-command terminal launch and the `OpenClaude Terminal Black` theme. + ## What Works - **All tools**: Bash, FileRead, FileWrite, FileEdit, Glob, Grep, WebFetch, WebSearch, Agent, MCP, LSP, NotebookEdit, Tasks diff --git a/vscode-extension/openclaude-vscode/.vscode/launch.json b/vscode-extension/openclaude-vscode/.vscode/launch.json new file mode 100644 index 00000000..b521eca0 --- /dev/null +++ b/vscode-extension/openclaude-vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/out/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/vscode-extension/openclaude-vscode/README.md b/vscode-extension/openclaude-vscode/README.md new file mode 100644 index 00000000..9d67cad1 --- /dev/null +++ b/vscode-extension/openclaude-vscode/README.md @@ -0,0 +1,44 @@ +# OpenClaude VS Code Extension + +A sleek VS Code companion for OpenClaude with a visual **Control Center** plus terminal-first workflows. + +## Features + +- **Control Center sidebar UI** in the Activity Bar: + - Launch OpenClaude + - Open repository/docs + - Open VS Code theme picker +- **Terminal launch command**: `OpenClaude: Launch in Terminal` +- **Built-in dark theme**: `OpenClaude Terminal Black` (terminal-inspired, low-glare, neon accents) + +## Requirements + +- VS Code `1.95+` +- `openclaude` available in your terminal PATH (`npm install -g @gitlawb/openclaude`) + +## Commands + +- `OpenClaude: Open Control Center` +- `OpenClaude: Launch in Terminal` +- `OpenClaude: Open Repository` + +## Settings + +- `openclaude.launchCommand` (default: `openclaude`) +- `openclaude.terminalName` (default: `OpenClaude`) +- `openclaude.useOpenAIShim` (default: `true`) + +## Development + +From this folder: + +```bash +npm run lint +``` + +To package (optional): + +```bash +npm run package +``` + diff --git a/vscode-extension/openclaude-vscode/media/openclaude.svg b/vscode-extension/openclaude-vscode/media/openclaude.svg new file mode 100644 index 00000000..b492c423 --- /dev/null +++ b/vscode-extension/openclaude-vscode/media/openclaude.svg @@ -0,0 +1,6 @@ + diff --git a/vscode-extension/openclaude-vscode/package.json b/vscode-extension/openclaude-vscode/package.json new file mode 100644 index 00000000..441e28de --- /dev/null +++ b/vscode-extension/openclaude-vscode/package.json @@ -0,0 +1,102 @@ +{ + "name": "openclaude-vscode", + "displayName": "OpenClaude", + "description": "Sleek VS Code extension for OpenClaude with a visual Control Center and terminal-aligned theme.", + "version": "0.1.1", + "publisher": "devnull-bootloader", + "engines": { + "vscode": "^1.95.0" + }, + "categories": [ + "Themes", + "Other" + ], + "activationEvents": [ + "onStartupFinished", + "onCommand:openclaude.start", + "onCommand:openclaude.openDocs", + "onCommand:openclaude.openControlCenter", + "onView:openclaude.controlCenter" + ], + "main": "./src/extension.js", + "contributes": { + "commands": [ + { + "command": "openclaude.start", + "title": "OpenClaude: Launch in Terminal", + "category": "OpenClaude" + }, + { + "command": "openclaude.openDocs", + "title": "OpenClaude: Open Repository", + "category": "OpenClaude" + }, + { + "command": "openclaude.openControlCenter", + "title": "OpenClaude: Open Control Center", + "category": "OpenClaude" + } + ], + "viewsContainers": { + "activitybar": [ + { + "id": "openclaude", + "title": "OpenClaude", + "icon": "media/openclaude.svg" + } + ] + }, + "views": { + "openclaude": [ + { + "id": "openclaude.controlCenter", + "name": "Control Center", + "type": "webview" + } + ] + }, + "configuration": { + "title": "OpenClaude", + "properties": { + "openclaude.launchCommand": { + "type": "string", + "default": "openclaude", + "description": "Command run in the integrated terminal when launching OpenClaude." + }, + "openclaude.terminalName": { + "type": "string", + "default": "OpenClaude", + "description": "Integrated terminal tab name for OpenClaude sessions." + }, + "openclaude.useOpenAIShim": { + "type": "boolean", + "default": false, + "description": "Optionally set CLAUDE_CODE_USE_OPENAI=1 in launched OpenClaude terminals." + } + } + }, + "themes": [ + { + "label": "OpenClaude Terminal Black", + "uiTheme": "vs-dark", + "path": "./themes/OpenClaude-Terminal-Black.json" + } + ] + }, + "scripts": { + "lint": "node --check ./src/extension.js", + "package": "npx @vscode/vsce package --no-dependencies" + }, + "keywords": [ + "openclaude", + "terminal", + "theme", + "cli", + "llm" + ], + "repository": { + "type": "git", + "url": "https://github.com/Gitlawb/openclaude" + }, + "license": "MIT" +} diff --git a/vscode-extension/openclaude-vscode/src/extension.js b/vscode-extension/openclaude-vscode/src/extension.js new file mode 100644 index 00000000..d04fd662 --- /dev/null +++ b/vscode-extension/openclaude-vscode/src/extension.js @@ -0,0 +1,335 @@ +const vscode = require('vscode'); +const crypto = require('crypto'); +const { exec } = require('child_process'); +const { promisify } = require('util'); + +const execAsync = promisify(exec); +const OPENCLAUDE_REPO_URL = 'https://github.com/Gitlawb/openclaude'; + +async function isCommandAvailable(command) { + try { + if (!command) { + return false; + } + + if (process.platform === 'win32') { + await execAsync(`where ${command}`); + } else { + await execAsync(`command -v ${command}`); + } + + return true; + } catch { + return false; + } +} + +function getExecutableFromCommand(command) { + return command.trim().split(/\s+/)[0]; +} + +async function launchOpenClaude() { + const configured = vscode.workspace.getConfiguration('openclaude'); + const launchCommand = configured.get('launchCommand', 'openclaude'); + const terminalName = configured.get('terminalName', 'OpenClaude'); + const shimEnabled = configured.get('useOpenAIShim', false); + const executable = getExecutableFromCommand(launchCommand); + const installed = await isCommandAvailable(executable); + + if (!installed) { + const action = await vscode.window.showErrorMessage( + `OpenClaude command not found: ${executable}. Install it with: npm install -g @gitlawb/openclaude`, + 'Open Repository' + ); + + if (action === 'Open Repository') { + await vscode.env.openExternal(vscode.Uri.parse(OPENCLAUDE_REPO_URL)); + } + + return; + } + + const env = {}; + if (shimEnabled) { + env.CLAUDE_CODE_USE_OPENAI = '1'; + } + + const terminal = vscode.window.createTerminal({ + name: terminalName, + env, + }); + + terminal.show(true); + terminal.sendText(launchCommand, true); +} + +class OpenClaudeControlCenterProvider { + async resolveWebviewView(webviewView) { + webviewView.webview.options = { enableScripts: true }; + const configured = vscode.workspace.getConfiguration('openclaude'); + const launchCommand = configured.get('launchCommand', 'openclaude'); + const executable = getExecutableFromCommand(launchCommand); + const installed = await isCommandAvailable(executable); + const shimEnabled = configured.get('useOpenAIShim', false); + const shortcut = process.platform === 'darwin' ? 'Cmd+Shift+P' : 'Ctrl+Shift+P'; + + webviewView.webview.html = this.getHtml(webviewView.webview, { + installed, + shimEnabled, + shortcut, + executable, + }); + + webviewView.webview.onDidReceiveMessage(async (message) => { + if (message?.type === 'launch') { + await launchOpenClaude(); + return; + } + + if (message?.type === 'docs') { + await vscode.env.openExternal(vscode.Uri.parse(OPENCLAUDE_REPO_URL)); + return; + } + + if (message?.type === 'commands') { + await vscode.commands.executeCommand('workbench.action.showCommands'); + } + }); + } + + getHtml(webview, status) { + const nonce = crypto.randomBytes(16).toString('base64'); + const runtimeLabel = status.installed ? 'available' : 'missing'; + const shimLabel = status.shimEnabled ? 'enabled (CLAUDE_CODE_USE_OPENAI=1)' : 'disabled'; + return ` + +
+ + + + + + +${status.shortcut} and run OpenClaude commands from anywhere.
+