diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3a1f70e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,147 @@ +name: Build AbysiusCodium + +on: + push: + branches: [main, develop] + tags: ['v*'] + pull_request: + branches: [main] + workflow_dispatch: + inputs: + vscode_tag: + description: 'VS Code upstream tag to build from' + required: true + default: '1.121.0' + +env: + MS_VSCODE_TAG: ${{ github.event.inputs.vscode_tag || '1.121.0' }} + NODE_VERSION: '20.18.0' + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x64, arm64, armhf] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libx11-dev libxkbfile-dev libsecret-1-dev \ + fakeroot rpm dpkg-dev debhelper \ + gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ + gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + + - name: Build Extension + run: | + cd extensions/abysius-ai + npm ci + npm run compile + + - name: Build AbysiusCodium + run: | + chmod +x build/build.sh + ./build/build.sh --platform linux --arch ${{ matrix.arch }} --tag ${{ env.MS_VSCODE_TAG }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: abysius-codium-linux-${{ matrix.arch }} + path: releases/* + if-no-files-found: warn + + build-darwin: + runs-on: macos-latest + strategy: + matrix: + arch: [x64, arm64] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Build Extension + run: | + cd extensions/abysius-ai + npm ci + npm run compile + + - name: Build AbysiusCodium + run: | + chmod +x build/build.sh + ./build/build.sh --platform darwin --arch ${{ matrix.arch }} --tag ${{ env.MS_VSCODE_TAG }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: abysius-codium-darwin-${{ matrix.arch }} + path: releases/* + if-no-files-found: warn + + build-windows: + runs-on: windows-latest + strategy: + matrix: + arch: [x64, arm64] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Build Extension + run: | + cd extensions\abysius-ai + npm ci + npm run compile + + - name: Build AbysiusCodium + shell: bash + run: | + bash build/build.sh --platform win32 --arch ${{ matrix.arch }} --tag ${{ env.MS_VSCODE_TAG }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: abysius-codium-win32-${{ matrix.arch }} + path: releases/* + if-no-files-found: warn + + release: + needs: [build-linux, build-darwin, build-windows] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: releases + pattern: abysius-codium-* + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + files: releases/**/* + generate_release_notes: true + draft: true + prerelease: ${{ contains(github.ref, '-alpha') || contains(github.ref, '-beta') || contains(github.ref, '-rc') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3771719 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Build outputs +work/ +releases/ +build/vscode/ +*.vsix + +# Node +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +yarn.lock + +# Extension build +extensions/*/out/ +extensions/*/*.vsix + +# TypeScript +*.tsbuildinfo + +# OS +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Logs +logs/ +*.log + +# Environment +.env +.env.local + +# Test coverage +coverage/ +.nyc_output/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7273709 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 AbysiusAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e69de29..56e0eb4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,208 @@ +# AbysiusCodium + +A custom, releasable VSCodium distribution built from Microsoft's open-source VS Code repository, bundled with the **Abysius AI** extension for intelligent inline code completions and an embedded AI chat interface. + +## Features + +- **Inline Code Completions**: Ghost-text suggestions powered by Abysius AI models as you type +- **AI Chat Panel**: Conversational assistant integrated directly into the editor sidebar +- **Privacy-First**: Built on VSCodium — no Microsoft telemetry, no proprietary licensing +- **Custom Branding**: Fully branded as Abysius Codium with its own identity +- **Cross-Platform**: Builds for Linux, macOS, and Windows (x64, ARM64, ARM HF) +- **OSS Extension Marketplace**: Uses [Open VSX](https://open-vsx.org/) by default + +## Project Structure + +``` +AbysiusCodium/ +|-- .github/workflows/ # CI/CD pipelines (GitHub Actions) +|-- build/ +| |-- build.sh # Main build script for VSCodium binaries +| |-- dev-setup.sh # Local extension dev environment setup +|-- extensions/ +| |-- abysius-ai/ # The Abysius AI VS Code extension +| |-- src/ +| | |-- extension.ts # Main activation entry point +| | |-- api.ts # Abysius API client (inline + chat) +| | |-- inlineCompletion.ts # Ghost-text completion provider +| | |-- chatPanel.ts # Webview chat panel provider +| |-- assets/ +| | |-- chat.css # Chat UI styles +| | |-- chat.js # Chat UI logic (webview) +| |-- package.json # Extension manifest +| |-- tsconfig.json # TypeScript config +|-- patches/ # Patches applied to vscode source +|-- product.json # Abysius branding & built-in extensions config +|-- package.json # Root project scripts +|-- README.md +``` + +## Quick Start + +### Prerequisites + +- Node.js 20+ (use [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm)) +- Git +- Platform-specific build tools (see below) + +### Linux Build + +```bash +# Install deps (Ubuntu/Debian) +sudo apt-get install libx11-dev libxkbfile-dev libsecret-1-dev fakeroot rpm + +# Clone and build +git clone https://github.com/AbysiusAI/AbysiusCodium.git +cd AbysiusCodium +./build/build.sh --platform linux --arch x64 +``` + +### macOS Build + +```bash +# Xcode Command Line Tools required +xcode-select --install + +# Build +git clone https://github.com/AbysiusAI/AbysiusCodium.git +cd AbysiusCodium +./build/build.sh --platform darwin --arch arm64 +``` + +### Windows Build + +```powershell +# From Git Bash or WSL +git clone https://github.com/AbysiusAI/AbysiusCodium.git +cd AbysiusCodium +bash build/build.sh --platform win32 --arch x64 +``` + +### Extension-Only Development + +To work on just the Abysius AI extension without building the full editor: + +```bash +cd AbysiusCodium +./build/dev-setup.sh +``` + +This symlinks the extension into your local VS Code / VSCodium installation for live testing. + +## Abysius AI Extension + +### Inline Completions + +- Triggered automatically as you type (debounced, configurable delay) +- Accept full completion with **Tab** +- Accept word-by-word with **Ctrl+Right** / **Cmd+Right** +- Reject with **Escape** +- Manual trigger with **Ctrl+Space** / **Cmd+Space** + +### Chat Interface + +- Open from the Activity Bar or via Command Palette (`Abysius: Open Chat`) +- Streaming responses in real-time +- Code blocks have **Copy** and **Insert** actions +- Context-aware: knows your current file language, selection, and cursor position + +### Configuration + +| Setting | Default | Description | +|---------|---------|-------------| +| `abysius.enableInlineCompletions` | `true` | Toggle ghost-text suggestions | +| `abysius.inlineCompletionDelay` | `300` | MS delay before requesting completion | +| `abysius.inlineCompletionMaxLength` | `200` | Max chars in a suggestion | +| `abysius.chatEndpoint` | `https://api.abysius.ai/v1/chat` | Chat API endpoint | +| `abysius.inlineEndpoint` | `https://api.abysius.ai/v1/inline` | Inline completion endpoint | +| `abysius.apiKey` | `""` | Your Abysius API key | +| `abysius.model` | `abysius-coder` | Model selection (coder/chat/fast) | +| `abysius.showInlineDiff` | `true` | Highlight diff in completions | +| `abysius.telemetry` | `false` | Anonymous usage telemetry | + +## Architecture + +### How It Works + +1. **Build**: `build.sh` clones the VS Code upstream repo at the specified tag +2. **Patch**: De-branding patches are applied to remove Microsoft-specific code +3. **Inject**: `product.json` is copied in, defining Abysius branding and built-in extensions +4. **Bundle**: The `abysius-ai` extension is compiled and installed as a built-in extension +5. **Compile**: VS Code's standard build pipeline produces platform-specific binaries +6. **Package**: OS-specific packages (.tar.gz, .deb, .rpm, .dmg, .zip, .exe) are created + +### Extension API Design + +The extension uses four VS Code API surfaces: + +- **`InlineCompletionItemProvider`**: Provides ghost-text suggestions via `provideInlineCompletionItems` +- **`WebviewViewProvider`**: Hosts the chat UI in a sidebar panel +- **Chat Participants**: Uses the `chatParticipant` API proposal for `@-mention` support +- **Language Model API**: Direct programmatic access to models for custom features + +The API client (`src/api.ts`) supports both streaming (chat) and request/response (inline) patterns. Endpoints are configurable so you can point to self-hosted or third-party model backends. + +## Releasing + +1. Tag a release: `git tag -a v1.0.0 -m "Release v1.0.0"` +2. Push tags: `git push origin v1.0.0` +3. GitHub Actions builds for all platforms and creates a draft release +4. Review the draft release, add notes, publish + +## Development Scripts + +```bash +# Watch extension TypeScript for changes +npm run dev:ext + +# Build extension once +npm run build:ext + +# Package extension as .vsix +npm run package:ext + +# Build full editor for current platform +npm run build:linux # or build:darwin / build:win32 + +# Clean all build artifacts +npm run clean + +# Lint extension +npm run lint:ext +``` + +## Customizing the AI Backend + +By default, the extension points to `https://api.abysius.ai`. To use your own backend: + +1. Update `abysius.chatEndpoint` and `abysius.inlineEndpoint` in settings +2. Your backend should accept OpenAI-compatible request/response formats: + + **Inline Endpoint** (`POST /v1/inline`): + ```json + { + "prompt": "code before cursor", + "suffix": "code after cursor", + "language": "typescript", + "filename": "test.ts", + "model": "your-model", + "max_tokens": 200 + } + ``` + Response: + ```json + { "completion": "suggested code here", "finish_reason": "stop" } + ``` + + **Chat Endpoint** (`POST /v1/chat`): + OpenAI-compatible chat completions with `stream: true/false` support. + +## License + +MIT — see [LICENSE](./LICENSE) + +## Acknowledgments + +- Built on [VSCodium](https://vscodium.com/) and Microsoft's [vscode](https://github.com/microsoft/vscode) open-source project +- Inline completions leverage VS Code's `InlineCompletionItemProvider` API +- Chat UI built with VS Code Webview API diff --git a/build/build.sh b/build/build.sh new file mode 100644 index 0000000..010e9e5 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,205 @@ +#!/bin/bash +set -euo pipefail + +# AbysiusCodium Build Script +# Builds custom VSCodium binaries with bundled Abysius AI extension + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +BUILD_DIR="${PROJECT_ROOT}/build" +WORK_DIR="${PROJECT_ROOT}/work" +PATCHES_DIR="${PROJECT_ROOT}/patches" +RELEASES_DIR="${PROJECT_ROOT}/releases" + +# Versions +MS_VSCODE_TAG="${MS_VSCODE_TAG:-1.121.0}" +NODE_VERSION="${NODE_VERSION:-20.18.0}" + +# Branding +APP_NAME="AbysiusCodium" +APP_NAME_SHORT="abysius-codium" + +usage() { + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -p, --platform Target platform (linux|darwin|win32) [default: current]" + echo " -a, --arch Target architecture (x64|arm64|armhf) [default: x64]" + echo " -t, --tag VS Code upstream tag [default: ${MS_VSCODE_TAG}]" + echo " -c, --clean Clean work directory before build" + echo " -s, --skip-ext Skip building the Abysius AI extension" + echo " -h, --help Show this help" + exit 0 +} + +PLATFORM="" +ARCH="x64" +CLEAN=0 +SKIP_EXT=0 + +while [[ $# -gt 0 ]]; do + case $1 in + -p|--platform) PLATFORM="$2"; shift 2 ;; + -a|--arch) ARCH="$2"; shift 2 ;; + -t|--tag) MS_VSCODE_TAG="$2"; shift 2 ;; + -c|--clean) CLEAN=1; shift ;; + -s|--skip-ext) SKIP_EXT=1; shift ;; + -h|--help) usage ;; + *) echo "Unknown option: $1"; usage ;; + esac +done + +# Auto-detect platform if not specified +if [[ -z "$PLATFORM" ]]; then + case "$(uname -s)" in + Linux*) PLATFORM="linux" ;; + Darwin*) PLATFORM="darwin" ;; + MINGW*|MSYS*|CYGWIN*) PLATFORM="win32" ;; + *) echo "Unsupported platform: $(uname -s)"; exit 1 ;; + esac +fi + +# Ensure work directory exists +mkdir -p "${WORK_DIR}" +mkdir -p "${RELEASES_DIR}" + +if [[ $CLEAN -eq 1 ]]; then + echo "[BUILD] Cleaning work directory..." + rm -rf "${WORK_DIR:?}"/* +fi + +# Step 1: Clone / update vscode repo +echo "[BUILD] Setting up Microsoft vscode source (tag: ${MS_VSCODE_TAG})..." +if [[ ! -d "${WORK_DIR}/vscode" ]]; then + git clone --depth 1 --branch "${MS_VSCODE_TAG}" https://github.com/microsoft/vscode.git "${WORK_DIR}/vscode" +else + cd "${WORK_DIR}/vscode" + git fetch --depth 1 origin "${MS_VSCODE_TAG}" + git checkout "${MS_VSCODE_TAG}" || { + echo "[BUILD] Failed to checkout tag ${MS_VSCODE_TAG}, re-cloning..." + cd "${PROJECT_ROOT}" + rm -rf "${WORK_DIR}/vscode" + git clone --depth 1 --branch "${MS_VSCODE_TAG}" https://github.com/microsoft/vscode.git "${WORK_DIR}/vscode" + } +fi + +cd "${WORK_DIR}/vscode" + +# Step 2: Apply VSCodium patches +echo "[BUILD] Applying VSCodium de-branding patches..." +if [[ -d "${PATCHES_DIR}" ]]; then + for patch_file in "${PATCHES_DIR}"/*.patch; do + if [[ -f "$patch_file" ]]; then + echo "[BUILD] Applying patch: $(basename "$patch_file")" + git apply "$patch_file" || { + echo "[BUILD] WARNING: Failed to apply $(basename "$patch_file"), may already be applied" + } + fi + done +fi + +# Step 3: Apply custom Abysius branding patches +echo "[BUILD] Applying Abysius branding..." +cp "${PROJECT_ROOT}/product.json" "${WORK_DIR}/vscode/product.json" + +# Step 4: Copy Abysius AI extension into built-in extensions +if [[ $SKIP_EXT -eq 0 ]]; then + echo "[BUILD] Building Abysius AI extension..." + cd "${PROJECT_ROOT}/extensions/abysius-ai" + + if [[ ! -d "node_modules" ]]; then + npm ci + fi + + npm run compile + + # Package extension into VSIX + npx vsce package --no-dependencies -o "${WORK_DIR}/abysius-ai.vsix" + + # Install as built-in extension + mkdir -p "${WORK_DIR}/vscode/extensions/abysius-ai" + cp -r "${PROJECT_ROOT}/extensions/abysius-ai"/* "${WORK_DIR}/vscode/extensions/abysius-ai/" + + echo "[BUILD] Abysius AI extension ready" +fi + +# Step 5: Install dependencies and build +cd "${WORK_DIR}/vscode" + +echo "[BUILD] Installing Node dependencies..." +export npm_config_arch="${ARCH}" +export npm_config_target_arch="${ARCH}" + +if [[ "$PLATFORM" == "linux" && "$ARCH" == "armhf" ]]; then + export npm_config_arch="arm" + export npm_config_target_arch="arm" +fi + +# Use system node if matching, otherwise use nvm/fnm +if ! command -v node &>/dev/null || [[ "$(node -v)" != "v${NODE_VERSION}"* ]]; then + echo "[BUILD] Node ${NODE_VERSION} required. Setting up..." + if command -v fnm &>/dev/null; then + eval "$(fnm env)" + fnm use "${NODE_VERSION}" 2>/dev/null || fnm install "${NODE_VERSION}" + fnm use "${NODE_VERSION}" + elif [[ -d "${HOME}/.nvm" ]]; then + export NVM_DIR="${HOME}/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm use "${NODE_VERSION}" 2>/dev/null || nvm install "${NODE_VERSION}" + else + echo "[BUILD] WARNING: Node version mismatch. Install Node ${NODE_VERSION} via nvm/fnm." + fi +fi + +echo "[BUILD] Installing dependencies (this may take a while)..." +npm ci + +# Step 6: Compile +echo "[BUILD] Compiling VS Code..." +npm run compile + +# Step 7: Build release packages +echo "[BUILD] Packaging for ${PLATFORM}-${ARCH}..." + +if [[ "$PLATFORM" == "linux" ]]; then + # Build .tar.gz + npm run gulp -- "vscode-linux-${ARCH}" + + # Build .deb if tools available + if command -v dpkg-deb &>/dev/null; then + npm run gulp -- "vscode-linux-${ARCH}-deb" + fi + + # Build .rpm if tools available + if command -v rpmbuild &>/dev/null; then + npm run gulp -- "vscode-linux-${ARCH}-rpm" + fi + + # Copy artifacts + cp "${WORK_DIR}/vscode/.build/linux/${ARCH}/"*.tar.gz "${RELEASES_DIR}/" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/linux/deb/${ARCH}/debian/"*.deb "${RELEASES_DIR}/" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/linux/rpm/${ARCH}/"*.rpm "${RELEASES_DIR}/" 2>/dev/null || true + +elif [[ "$PLATFORM" == "darwin" ]]; then + npm run gulp -- "vscode-darwin-${ARCH}" + cp -r "${WORK_DIR}/vscode/.build/darwin/${ARCH}/"*.app "${RELEASES_DIR}/" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/darwin/${ARCH}/"*.zip "${RELEASES_DIR}/" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/darwin/${ARCH}/"*.dmg "${RELEASES_DIR}/" 2>/dev/null || true + +elif [[ "$PLATFORM" == "win32" ]]; then + npm run gulp -- "vscode-win32-${ARCH}" + cp -r "${WORK_DIR}/vscode/.build/win32/${ARCH}/" "${RELEASES_DIR}/${APP_NAME_SHORT}-win32-${ARCH}" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/win32/${ARCH}/system-setup/"*.exe "${RELEASES_DIR}/" 2>/dev/null || true + cp "${WORK_DIR}/vscode/.build/win32/${ARCH}/user-setup/"*.exe "${RELEASES_DIR}/" 2>/dev/null || true +fi + +echo "" +echo "========================================" +echo " AbysiusCodium Build Complete!" +echo "========================================" +echo "" +echo "Release artifacts available in:" +echo " ${RELEASES_DIR}/" +echo "" +ls -la "${RELEASES_DIR}/" 2>/dev/null || echo " (no artifacts found - check build logs)" +echo "" diff --git a/build/dev-setup.sh b/build/dev-setup.sh new file mode 100644 index 0000000..65a6d91 --- /dev/null +++ b/build/dev-setup.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Quick local development setup for AbysiusCodium extension + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +EXT_DIR="${PROJECT_ROOT}/extensions/abysius-ai" + +echo "[DEV] Setting up Abysius AI extension for local development..." + +cd "$EXT_DIR" + +# Install dependencies +if [[ ! -d "node_modules" ]]; then + echo "[DEV] Installing npm dependencies..." + npm ci +fi + +# Compile TypeScript +echo "[DEV] Compiling TypeScript..." +npm run compile + +# Symlink into VS Code/Codium extensions folder for live development +# Detect editor +if command -v codium &>/dev/null; then + BIN="codium" +elif command -v code &>/dev/null; then + BIN="code" +else + echo "[DEV] WARNING: Neither 'codium' nor 'code' found in PATH." + echo "[DEV] Install VS Code or VSCodium to test the extension." + echo "[DEV] Extension built successfully at: ${EXT_DIR}/out/" + exit 0 +fi + +# Get extensions dir +EXTS_DIR=$($BIN --locate-extension abysius.abysius-ai 2>/dev/null | head -1 || true) + +if [[ -z "$EXTS_DIR" ]]; then + # Use default path + if [[ "$BIN" == "code" ]]; then + EXTS_DIR="${HOME}/.vscode/extensions/abysius.abysius-ai-0.1.0" + else + EXTS_DIR="${HOME}/.vscode-oss/extensions/abysius.abysius-ai-0.1.0" + fi +fi + +# Remove old symlink +if [[ -L "$EXTS_DIR" ]]; then + rm "$EXTS_DIR" +fi + +# Create symlink +mkdir -p "$(dirname "$EXTS_DIR")" +ln -sf "$EXT_DIR" "$EXTS_DIR" + +echo "[DEV] Extension symlinked to: $EXTS_DIR" +echo "[DEV] Launch ${BIN} and the Abysius AI extension will be available." +echo "" +echo "[DEV] Next steps:" +echo " 1. Run: ${BIN} --enable-proposed-api abysius.abysius-ai" +echo " 2. Or add to ${BIN} settings.json:" +echo " 'extensions.experimental.affinity': { 'abysius.abysius-ai': 1 }" +echo " 3. Run 'npm run watch' in ${EXT_DIR} for auto-recompile" diff --git a/extensions/abysius-ai/.vscodeignore b/extensions/abysius-ai/.vscodeignore new file mode 100644 index 0000000..5d4adc3 --- /dev/null +++ b/extensions/abysius-ai/.vscodeignore @@ -0,0 +1,12 @@ +.vscode/** +.vscode-test/** +src/** +assets/*.scss +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts +!**/*.d.ts diff --git a/extensions/abysius-ai/assets/chat.css b/extensions/abysius-ai/assets/chat.css new file mode 100644 index 0000000..3ba6129 --- /dev/null +++ b/extensions/abysius-ai/assets/chat.css @@ -0,0 +1,356 @@ +:root { + --bg-primary: var(--vscode-editor-background, #1e1e1e); + --bg-secondary: var(--vscode-panel-background, #252526); + --bg-input: var(--vscode-input-background, #3c3c3c); + --fg-primary: var(--vscode-foreground, #cccccc); + --fg-secondary: var(--vscode-descriptionForeground, #858585); + --accent: var(--vscode-button-background, #0e639c); + --accent-hover: var(--vscode-button-hoverBackground, #1177bb); + --border: var(--vscode-panel-border, #3e3e42); + --code-bg: var(--vscode-textCodeBlock-background, #1e1e1e); + --user-msg-bg: var(--vscode-button-background, #0e639c); + --assistant-msg-bg: var(--vscode-panel-background, #252526); + --scrollbar: var(--vscode-scrollbarSlider-background, rgba(121, 121, 121, 0.4)); + --error: var(--vscode-errorForeground, #f48771); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--vscode-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); + font-size: 13px; + color: var(--fg-primary); + background: var(--bg-primary); + height: 100vh; + overflow: hidden; +} + +#chat-container { + display: flex; + flex-direction: column; + height: 100vh; +} + +#messages { + flex: 1; + overflow-y: auto; + padding: 12px 16px; + display: flex; + flex-direction: column; + gap: 8px; +} + +#messages::-webkit-scrollbar { + width: 8px; +} + +#messages::-webkit-scrollbar-track { + background: transparent; +} + +#messages::-webkit-scrollbar-thumb { + background: var(--scrollbar); + border-radius: 4px; +} + +.message { + max-width: 100%; + padding: 10px 14px; + border-radius: 8px; + line-height: 1.5; + word-wrap: break-word; + white-space: pre-wrap; + animation: slideIn 0.15s ease-out; +} + +@keyframes slideIn { + from { + transform: translateX(-10px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +.message.user { + background: var(--user-msg-bg); + color: #fff; + align-self: flex-end; + border-bottom-right-radius: 2px; +} + +.message.assistant { + background: var(--assistant-msg-bg); + border: 1px solid var(--border); + align-self: flex-start; + border-bottom-left-radius: 2px; +} + +.message.assistant.streaming { + border-left: 2px solid var(--accent); +} + +.message-time { + font-size: 10px; + opacity: 0.6; + margin-top: 4px; + text-align: right; +} + +/* Code blocks */ +.message pre { + background: var(--code-bg); + border: 1px solid var(--border); + border-radius: 6px; + padding: 12px; + margin: 8px 0; + overflow-x: auto; + position: relative; +} + +.message pre code { + font-family: var(--vscode-editor-font-family, 'Consolas', 'Monaco', monospace); + font-size: 12px; + line-height: 1.4; + color: var(--fg-primary); +} + +.code-block-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 8px; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-bottom: none; + border-radius: 6px 6px 0 0; + font-size: 11px; + color: var(--fg-secondary); +} + +.code-block-actions { + display: flex; + gap: 4px; +} + +.code-btn { + background: transparent; + border: 1px solid var(--border); + color: var(--fg-secondary); + padding: 2px 8px; + border-radius: 3px; + cursor: pointer; + font-size: 11px; + transition: all 0.1s; +} + +.code-btn:hover { + background: var(--bg-input); + color: var(--fg-primary); +} + +/* Inline code */ +.message code:not(pre code) { + background: var(--code-bg); + padding: 1px 4px; + border-radius: 3px; + font-family: var(--vscode-editor-font-family, monospace); + font-size: 12px; +} + +/* Input area */ +#input-area { + display: flex; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid var(--border); + background: var(--bg-secondary); + align-items: flex-end; +} + +#message-input { + flex: 1; + background: var(--bg-input); + color: var(--fg-primary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 8px 12px; + resize: none; + font-family: inherit; + font-size: 13px; + line-height: 1.4; + max-height: 120px; + min-height: 36px; + outline: none; + transition: border-color 0.15s; +} + +#message-input:focus { + border-color: var(--accent); +} + +#message-input::placeholder { + color: var(--fg-secondary); +} + +#send-btn, +#cancel-btn { + padding: 8px 16px; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 13px; + font-weight: 500; + transition: background 0.15s; + white-space: nowrap; + height: fit-content; +} + +#send-btn { + background: var(--accent); + color: #fff; +} + +#send-btn:hover { + background: var(--accent-hover); +} + +#send-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +#cancel-btn { + background: var(--bg-input); + color: var(--fg-primary); + border: 1px solid var(--border); +} + +#cancel-btn:hover { + background: var(--bg-secondary); +} + +/* Loading indicator */ +.streaming-indicator { + display: inline-flex; + align-items: center; + gap: 3px; + margin-left: 4px; +} + +.streaming-indicator span { + width: 6px; + height: 6px; + background: var(--accent); + border-radius: 50%; + animation: pulse 1.2s infinite; +} + +.streaming-indicator span:nth-child(2) { animation-delay: 0.2s; } +.streaming-indicator span:nth-child(3) { animation-delay: 0.4s; } + +@keyframes pulse { + 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); } + 40% { opacity: 1; transform: scale(1); } +} + +/* Error state */ +.message.error { + background: rgba(244, 135, 113, 0.1); + border: 1px solid var(--error); + color: var(--error); +} + +/* Welcome message */ +.welcome { + text-align: center; + padding: 40px 20px; + color: var(--fg-secondary); +} + +.welcome h3 { + color: var(--fg-primary); + margin-bottom: 8px; + font-size: 16px; +} + +.welcome p { + font-size: 12px; + line-height: 1.6; +} + +/* Markdown content */ +.message p { + margin: 0 0 8px 0; +} + +.message p:last-child { + margin-bottom: 0; +} + +.message ul, +.message ol { + margin: 8px 0; + padding-left: 20px; +} + +.message li { + margin: 2px 0; +} + +.message a { + color: var(--accent); + text-decoration: none; +} + +.message a:hover { + text-decoration: underline; +} + +.message blockquote { + border-left: 3px solid var(--accent); + margin: 8px 0; + padding-left: 12px; + color: var(--fg-secondary); +} + +.message table { + border-collapse: collapse; + margin: 8px 0; + width: 100%; +} + +.message th, +.message td { + border: 1px solid var(--border); + padding: 6px 10px; + text-align: left; +} + +.message th { + background: var(--bg-secondary); +} + +/* Empty state */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + color: var(--fg-secondary); + text-align: center; + padding: 20px; +} + +.empty-state .icon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; +} diff --git a/extensions/abysius-ai/assets/chat.js b/extensions/abysius-ai/assets/chat.js new file mode 100644 index 0000000..1a95fd4 --- /dev/null +++ b/extensions/abysius-ai/assets/chat.js @@ -0,0 +1,303 @@ +(function () { + const vscode = acquireVsCodeApi(); + const messagesEl = document.getElementById('messages'); + const inputEl = document.getElementById('message-input'); + const sendBtn = document.getElementById('send-btn'); + const cancelBtn = document.getElementById('cancel-btn'); + + let isStreaming = false; + let currentAssistantId = null; + + // Auto-resize textarea + inputEl.addEventListener('input', () => { + inputEl.style.height = 'auto'; + inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px'; + }); + + // Send on Enter (Shift+Enter for newline) + inputEl.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }); + + sendBtn.addEventListener('click', sendMessage); + cancelBtn.addEventListener('click', cancelStream); + + function sendMessage() { + const text = inputEl.value.trim(); + if (!text || isStreaming) return; + + vscode.postMessage({ type: 'sendMessage', text }); + inputEl.value = ''; + inputEl.style.height = 'auto'; + inputEl.style.height = '36px'; + } + + function cancelStream() { + vscode.postMessage({ type: 'cancel' }); + setStreaming(false); + } + + function setStreaming(streaming) { + isStreaming = streaming; + sendBtn.style.display = streaming ? 'none' : 'block'; + cancelBtn.style.display = streaming ? 'block' : 'none'; + inputEl.disabled = streaming; + } + + function addMessage(msg) { + const msgEl = document.createElement('div'); + msgEl.className = `message ${msg.role}`; + msgEl.dataset.id = msg.id; + msgEl.innerHTML = formatContent(msg.content); + + const timeEl = document.createElement('div'); + timeEl.className = 'message-time'; + timeEl.textContent = formatTime(msg.timestamp); + msgEl.appendChild(timeEl); + + messagesEl.appendChild(msgEl); + scrollToBottom(); + + attachCodeActions(msgEl); + } + + function appendChunk(id, chunk) { + const msgEl = document.querySelector(`.message[data-id="${id}"]`); + if (!msgEl) return; + + const contentEl = msgEl.querySelector('.content') || msgEl; + const existing = contentEl.dataset.raw || ''; + const updated = existing + chunk; + contentEl.dataset.raw = updated; + + // Re-render the full content + const timeEl = msgEl.querySelector('.message-time'); + msgEl.innerHTML = formatContent(updated); + if (timeEl) msgEl.appendChild(timeEl); + + scrollToBottom(); + attachCodeActions(msgEl); + } + + function finalizeMessage(id) { + const msgEl = document.querySelector(`.message[data-id="${id}"]`); + if (msgEl) { + msgEl.classList.remove('streaming'); + } + setStreaming(false); + currentAssistantId = null; + } + + function showError(id, error) { + const msgEl = document.querySelector(`.message[data-id="${id}"]`) || document.createElement('div'); + msgEl.className = 'message error'; + msgEl.textContent = error; + if (!msgEl.parentNode) { + messagesEl.appendChild(msgEl); + } + setStreaming(false); + currentAssistantId = null; + scrollToBottom(); + } + + function formatContent(text) { + if (!text) return ''; + + // Escape HTML + let html = text + .replace(/&/g, '&') + .replace(//g, '>'); + + // Code blocks + html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => { + const escapedCode = code.replace(/^\n|\n$/g, ''); + return `
+ ${lang || 'text'} +
+ + +
+
${escapedCode}
`; + }); + + // Inline code + html = html.replace(/`([^`]+)`/g, '$1'); + + // Bold + html = html.replace(/\*\*([^*]+)\*\*/g, '$1'); + html = html.replace(/__([^_]+)__/g, '$1'); + + // Italic + html = html.replace(/\*([^*]+)\*/g, '$1'); + html = html.replace(/_([^_]+)_/g, '$1'); + + // Strikethrough + html = html.replace(/~~([^~]+)~~/g, '$1'); + + // Headers + html = html.replace(/^### (.*$)/gim, '

$1

'); + html = html.replace(/^## (.*$)/gim, '

$1

'); + html = html.replace(/^# (.*$)/gim, '

$1

'); + + // Lists + html = html.replace(/^\s*[-*+] (.*$)/gim, '
  • $1
  • '); + html = html.replace(/(
  • .*<\/li>\n?)+/g, '
      $&
    '); + html = html.replace(/<\/ul>\n?
      /g, ''); + + // Ordered lists + html = html.replace(/^\s*\d+\.\s+(.*$)/gim, '
    • $1
    • '); + + // Blockquotes + html = html.replace(/^> (.*$)/gim, '
      $1
      '); + html = html.replace(/(
      .*<\/blockquote>\n?)+/g, (matches) => { + return '
      ' + matches.replace(/<\/?blockquote>/g, '') + '
      '; + }); + + // Links + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); + + // Horizontal rules + html = html.replace(/^---+$/gim, '
      '); + + // Paragraphs (wrap remaining text) + const lines = html.split('\n'); + let result = ''; + let inParagraph = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed) { + if (inParagraph) { + result += '

      '; + inParagraph = false; + } + result += '\n'; + continue; + } + + const isBlock = /^<(h[1-6]|ul|ol|pre|blockquote|hr|div)/i.test(trimmed); + + if (isBlock) { + if (inParagraph) { + result += '

      '; + inParagraph = false; + } + result += line + '\n'; + } else { + if (!inParagraph) { + result += '

      '; + inParagraph = true; + } + result += line + ' '; + } + } + + if (inParagraph) { + result += '

      '; + } + + return result; + } + + function attachCodeActions(container) { + container.querySelectorAll('.copy-btn').forEach(btn => { + btn.addEventListener('click', () => { + const code = decodeURIComponent(btn.dataset.code); + vscode.postMessage({ type: 'copyCode', code }); + btn.textContent = 'Copied!'; + setTimeout(() => btn.textContent = 'Copy', 1500); + }); + }); + + container.querySelectorAll('.insert-btn').forEach(btn => { + btn.addEventListener('click', () => { + const code = decodeURIComponent(btn.dataset.code); + vscode.postMessage({ type: 'insertCode', code }); + }); + }); + } + + function scrollToBottom() { + messagesEl.scrollTop = messagesEl.scrollHeight; + } + + function formatTime(timestamp) { + const date = new Date(timestamp); + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + } + + function clearMessages() { + messagesEl.innerHTML = ''; + } + + function showWelcome() { + messagesEl.innerHTML = ` +
      +
      +

      Abysius AI

      +

      Ask me anything about your code.
      I can help with writing, debugging, explaining, and refactoring.

      +
      + `; + } + + // Initial state + showWelcome(); + vscode.postMessage({ type: 'ready' }); + + // Handle messages from extension + window.addEventListener('message', (event) => { + const msg = event.data; + + switch (msg.type) { + case 'history': + clearMessages(); + if (msg.messages.length === 0) { + showWelcome(); + } else { + msg.messages.forEach(m => addMessage(m)); + } + break; + + case 'userMessage': + addMessage(msg.message); + break; + + case 'assistantStart': + currentAssistantId = msg.id; + setStreaming(true); + const assistantEl = document.createElement('div'); + assistantEl.className = 'message assistant streaming'; + assistantEl.dataset.id = msg.id; + assistantEl.innerHTML = ''; + messagesEl.appendChild(assistantEl); + scrollToBottom(); + break; + + case 'assistantChunk': + // Remove the loading indicator on first chunk + const el = document.querySelector(`.message[data-id="${msg.id}"]`); + if (el && el.querySelector('.streaming-indicator')) { + el.innerHTML = ''; + } + appendChunk(msg.id, msg.chunk); + break; + + case 'assistantDone': + finalizeMessage(msg.id); + break; + + case 'assistantError': + showError(msg.id, msg.error); + break; + + case 'clear': + clearMessages(); + showWelcome(); + break; + } + }); +})(); diff --git a/extensions/abysius-ai/package.json b/extensions/abysius-ai/package.json new file mode 100644 index 0000000..5a463e8 --- /dev/null +++ b/extensions/abysius-ai/package.json @@ -0,0 +1,232 @@ +{ + "name": "abysius-ai", + "displayName": "Abysius AI", + "description": "Inline completion and AI chat powered by Abysius", + "version": "0.1.0", + "publisher": "abysius", + "license": "MIT", + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "Machine Learning", + "Chat", + "Programming Languages" + ], + "keywords": [ + "ai", + "inline completion", + "chat", + "abysius", + "copilot", + "llm" + ], + "activationEvents": [ + "onStartupFinished" + ], + "main": "./out/extension.js", + "icon": "assets/icon.png", + "contributes": { + "configuration": { + "title": "Abysius AI", + "properties": { + "abysius.enableInlineCompletions": { + "type": "boolean", + "default": true, + "description": "Enable AI-powered inline completions (ghost text)", + "order": 1 + }, + "abysius.inlineCompletionDelay": { + "type": "number", + "default": 300, + "minimum": 50, + "maximum": 2000, + "description": "Delay in ms before requesting inline completions", + "order": 2 + }, + "abysius.inlineCompletionMaxLength": { + "type": "number", + "default": 200, + "minimum": 50, + "maximum": 2000, + "description": "Maximum length of inline completion suggestions", + "order": 3 + }, + "abysius.chatEndpoint": { + "type": "string", + "default": "https://api.abysius.ai/v1/chat", + "description": "Abysius AI chat API endpoint", + "order": 4 + }, + "abysius.inlineEndpoint": { + "type": "string", + "default": "https://api.abysius.ai/v1/inline", + "description": "Abysius AI inline completion API endpoint", + "order": 5 + }, + "abysius.apiKey": { + "type": "string", + "default": "", + "description": "Your Abysius API key", + "order": 6 + }, + "abysius.model": { + "type": "string", + "default": "abysius-coder", + "description": "Model to use for completions and chat", + "enum": [ + "abysius-coder", + "abysius-chat", + "abysius-fast" + ], + "enumDescriptions": [ + "Optimized for code completion", + "Optimized for conversational chat", + "Fastest response time" + ], + "order": 7 + }, + "abysius.showInlineDiff": { + "type": "boolean", + "default": true, + "description": "Show diff-style highlights in inline completions", + "order": 8 + }, + "abysius.telemetry": { + "type": "boolean", + "default": false, + "description": "Enable anonymous usage telemetry", + "order": 9 + } + } + }, + "commands": [ + { + "command": "abysius.openChat", + "title": "Open Abysius Chat", + "icon": "$(comment-discussion)", + "category": "Abysius AI" + }, + { + "command": "abysius.acceptInlineCompletion", + "title": "Accept Inline Completion", + "keybinding": { + "command": "abysius.acceptInlineCompletion", + "key": "tab", + "when": "abysiusInlineCompletionVisible && editorTextFocus && !editorReadonly" + }, + "category": "Abysius AI" + }, + { + "command": "abysius.rejectInlineCompletion", + "title": "Reject Inline Completion", + "keybinding": { + "command": "abysius.rejectInlineCompletion", + "key": "escape", + "when": "abysiusInlineCompletionVisible && editorTextFocus" + }, + "category": "Abysius AI" + }, + { + "command": "abysius.triggerInlineCompletion", + "title": "Trigger Inline Completion", + "keybinding": { + "command": "abysius.triggerInlineCompletion", + "key": "ctrl+space", + "mac": "cmd+space", + "when": "editorTextFocus && !editorReadonly" + }, + "category": "Abysius AI" + }, + { + "command": "abysius.openSettings", + "title": "Open Abysius Settings", + "category": "Abysius AI" + }, + { + "command": "abysius.clearChat", + "title": "Clear Chat History", + "icon": "$(clear-all)", + "category": "Abysius AI" + } + ], + "viewsContainers": { + "activitybar": [ + { + "id": "abysiusChat", + "title": "Abysius AI", + "icon": "$(comment-discussion)" + } + ] + }, + "views": { + "abysiusChat": [ + { + "id": "abysius.chatPanel", + "name": "Chat", + "type": "webview", + "icon": "$(comment-discussion)" + } + ], + "explorer": [ + { + "id": "abysius.chatView", + "name": "Abysius Chat", + "when": "abysiusChatEnabled", + "icon": "$(comment-discussion)", + "contextualTitle": "Abysius Chat" + } + ] + }, + "menus": { + "commandPalette": [ + { + "command": "abysius.acceptInlineCompletion", + "when": "false" + }, + { + "command": "abysius.rejectInlineCompletion", + "when": "false" + } + ], + "editor/context": [ + { + "command": "abysius.triggerInlineCompletion", + "group": "9_cutcopypaste@5", + "when": "editorTextFocus && !editorReadonly" + } + ], + "view/title": [ + { + "command": "abysius.clearChat", + "group": "navigation", + "when": "view == abysius.chatPanel" + } + ] + }, + "keybindings": [ + { + "command": "abysius.acceptNextWord", + "key": "ctrl+right", + "mac": "cmd+right", + "when": "abysiusInlineCompletionVisible && editorTextFocus && !editorReadonly" + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "lint": "eslint src --ext ts", + "package": "vsce package" + }, + "devDependencies": { + "@types/node": "^20.14.0", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.57.0", + "typescript": "^5.4.0" + }, + "dependencies": {} +} diff --git a/extensions/abysius-ai/src/api.ts b/extensions/abysius-ai/src/api.ts new file mode 100644 index 0000000..397755e --- /dev/null +++ b/extensions/abysius-ai/src/api.ts @@ -0,0 +1,194 @@ +import * as vscode from 'vscode'; + +interface AbysiusConfig { + apiKey: string; + chatEndpoint: string; + inlineEndpoint: string; + model: string; +} + +export interface InlineRequest { + prompt: string; + suffix: string; + language: string; + filename: string; + max_tokens?: number; +} + +export interface InlineResponse { + completion: string; + finish_reason: string; +} + +export interface ChatMessage { + role: 'user' | 'assistant' | 'system'; + content: string; +} + +export interface ChatRequest { + messages: ChatMessage[]; + model?: string; + stream?: boolean; + temperature?: number; + max_tokens?: number; +} + +export interface ChatResponse { + message: ChatMessage; + finish_reason: string; + usage?: { + prompt_tokens: number; + completion_tokens: number; + total_tokens: number; + }; +} + +export class AbysiusApi { + private config: AbysiusConfig; + + constructor(config: AbysiusConfig) { + this.config = config; + } + + updateConfig(config: Partial) { + this.config = { ...this.config, ...config }; + } + + private getHeaders(): Record { + const headers: Record = { + 'Content-Type': 'application/json', + 'User-Agent': 'AbysiusCodium/0.1.0' + }; + if (this.config.apiKey) { + headers['Authorization'] = `Bearer ${this.config.apiKey}`; + } + return headers; + } + + async getInlineCompletion(request: InlineRequest, signal?: AbortSignal): Promise { + try { + const config = vscode.workspace.getConfiguration('abysius'); + const maxLength = config.get('inlineCompletionMaxLength', 200); + + const response = await fetch(this.config.inlineEndpoint, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + ...request, + model: this.config.model, + max_tokens: Math.min(request.max_tokens || maxLength, maxLength) + }), + signal + }); + + if (!response.ok) { + const error = await response.text(); + console.error('[Abysius API] Inline completion error:', error); + return null; + } + + return await response.json() as InlineResponse; + } catch (err) { + if (err instanceof Error && err.name === 'AbortError') { + return null; + } + console.error('[Abysius API] Inline completion failed:', err); + return null; + } + } + + async *streamChat(request: ChatRequest, signal?: AbortSignal): AsyncGenerator { + try { + const response = await fetch(this.config.chatEndpoint, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + ...request, + model: this.config.model, + stream: true + }), + signal + }); + + if (!response.ok) { + const error = await response.text(); + console.error('[Abysius API] Chat error:', error); + return null; + } + + const reader = response.body?.getReader(); + if (!reader) return null; + + const decoder = new TextDecoder(); + let buffer = ''; + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || !trimmed.startsWith('data: ')) continue; + + const data = trimmed.slice(6); + if (data === '[DONE]') continue; + + try { + const chunk = JSON.parse(data); + const delta = chunk.choices?.[0]?.delta?.content; + if (delta) { + yield delta; + } + } catch { + // ignore malformed JSON + } + } + } + } finally { + reader.releaseLock(); + } + + return null; + } catch (err) { + if (err instanceof Error && err.name === 'AbortError') { + return null; + } + console.error('[Abysius API] Chat stream failed:', err); + return null; + } + } + + async sendChat(request: ChatRequest, signal?: AbortSignal): Promise { + try { + const response = await fetch(this.config.chatEndpoint, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + ...request, + model: this.config.model, + stream: false + }), + signal + }); + + if (!response.ok) { + const error = await response.text(); + console.error('[Abysius API] Chat error:', error); + return null; + } + + return await response.json() as ChatResponse; + } catch (err) { + if (err instanceof Error && err.name === 'AbortError') { + return null; + } + console.error('[Abysius API] Chat failed:', err); + return null; + } + } +} diff --git a/extensions/abysius-ai/src/chatPanel.ts b/extensions/abysius-ai/src/chatPanel.ts new file mode 100644 index 0000000..9a8e912 --- /dev/null +++ b/extensions/abysius-ai/src/chatPanel.ts @@ -0,0 +1,254 @@ +import * as vscode from 'vscode'; +import { AbysiusApi, ChatMessage, ChatRequest } from './api'; + +export interface ChatHistoryMessage { + id: string; + role: 'user' | 'assistant'; + content: string; + timestamp: number; +} + +export class ChatPanel implements vscode.WebviewViewProvider { + private _view?: vscode.WebviewView; + private _api: AbysiusApi; + private _messages: ChatHistoryMessage[] = []; + private _currentStreamAbort?: AbortController; + private _extensionUri: vscode.Uri; + + constructor(extensionUri: vscode.Uri, api: AbysiusApi) { + this._extensionUri = extensionUri; + this._api = api; + } + + 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); + + webviewView.webview.onDidReceiveMessage( + async (message) => { + switch (message.type) { + case 'sendMessage': + await this._handleUserMessage(message.text); + break; + case 'cancel': + this._currentStreamAbort?.abort(); + break; + case 'clear': + this.clear(); + break; + case 'ready': + this._postMessage({ type: 'history', messages: this._messages }); + break; + case 'insertCode': + this._insertCodeIntoEditor(message.code); + break; + case 'copyCode': + vscode.env.clipboard.writeText(message.code); + break; + } + } + ); + + webviewView.onDidDispose(() => { + this._view = undefined; + }); + } + + show(): void { + if (this._view) { + this._view.show(true); + } else { + vscode.commands.executeCommand('abysius.chatPanel.focus'); + } + } + + clear(): void { + this._messages = []; + this._postMessage({ type: 'clear' }); + } + + private async _handleUserMessage(text: string): Promise { + const userMsg: ChatHistoryMessage = { + id: this._generateId(), + role: 'user', + content: text, + timestamp: Date.now() + }; + this._messages.push(userMsg); + this._postMessage({ type: 'userMessage', message: userMsg }); + + // Gather context from active editor + const context = this._getEditorContext(); + + const chatMessages: ChatMessage[] = [ + { + role: 'system', + content: this._buildSystemPrompt(context) + } + ]; + + // Add recent message history + const recentMessages = this._messages.slice(-10); + for (const msg of recentMessages) { + chatMessages.push({ + role: msg.role, + content: msg.content + }); + } + + const request: ChatRequest = { + messages: chatMessages, + temperature: 0.7, + max_tokens: 4096 + }; + + const assistantId = this._generateId(); + this._postMessage({ + type: 'assistantStart', + id: assistantId + }); + + try { + this._currentStreamAbort = new AbortController(); + const stream = this._api.streamChat(request, this._currentStreamAbort.signal); + + let fullResponse = ''; + for await (const chunk of stream) { + fullResponse += chunk; + this._postMessage({ + type: 'assistantChunk', + id: assistantId, + chunk: chunk + }); + } + + const assistantMsg: ChatHistoryMessage = { + id: assistantId, + role: 'assistant', + content: fullResponse, + timestamp: Date.now() + }; + this._messages.push(assistantMsg); + + this._postMessage({ + type: 'assistantDone', + id: assistantId + }); + + } catch (err) { + this._postMessage({ + type: 'assistantError', + id: assistantId, + error: 'Failed to get response from Abysius AI' + }); + } + } + + private _getEditorContext(): { language?: string; filename?: string; selectedText?: string; cursorLine?: number } { + const editor = vscode.window.activeTextEditor; + if (!editor) return {}; + + const document = editor.document; + const selection = editor.selection; + + return { + language: document.languageId, + filename: document.fileName, + selectedText: selection.isEmpty ? undefined : document.getText(selection), + cursorLine: selection.active.line + 1 + }; + } + + private _buildSystemPrompt(context: { language?: string; filename?: string; selectedText?: string; cursorLine?: number }): string { + let prompt = `You are Abysius, an AI coding assistant embedded in a code editor. You help with programming, debugging, code review, and general software development questions.`; + + if (context.language) { + prompt += `\n\nThe user is currently working in a ${context.language} file`; + if (context.filename) { + prompt += ` (${context.filename.split('/').pop()})`; + } + prompt += '.'; + } + + if (context.selectedText) { + prompt += `\n\nSelected code:\n\`\`\`${context.language || ''}\n${context.selectedText}\n\`\`\``; + } + + if (context.cursorLine) { + prompt += `\n\nCursor is at line ${context.cursorLine}.`; + } + + prompt += `\n\nWhen providing code, wrap it in markdown code blocks with the appropriate language tag. Be concise and helpful.`; + + return prompt; + } + + private _insertCodeIntoEditor(code: string): void { + const editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showWarningMessage('No active editor to insert code into'); + return; + } + + editor.edit(editBuilder => { + const selection = editor.selection; + editBuilder.replace(selection, code); + }); + } + + private _postMessage(message: any): void { + if (this._view) { + this._view.webview.postMessage(message); + } + } + + private _generateId(): string { + return Date.now().toString(36) + Math.random().toString(36).substr(2); + } + + private _getHtmlForWebview(webview: vscode.Webview): string { + const styleUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'assets', 'chat.css') + ); + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'assets', 'chat.js') + ); + + const nonce = this._generateId(); + + return ` + + + + + + + + +
      +
      +
      + + + +
      +
      + + +`; + } + + dispose(): void { + this._currentStreamAbort?.abort(); + } +} diff --git a/extensions/abysius-ai/src/extension.ts b/extensions/abysius-ai/src/extension.ts new file mode 100644 index 0000000..5ec8fae --- /dev/null +++ b/extensions/abysius-ai/src/extension.ts @@ -0,0 +1,114 @@ +import * as vscode from 'vscode'; +import { InlineCompletionProvider } from './inlineCompletion'; +import { ChatPanel } from './chatPanel'; +import { AbysiusApi } from './api'; + +let inlineProvider: InlineCompletionProvider | undefined; +let chatPanel: ChatPanel | undefined; +let api: AbysiusApi; + +export function activate(context: vscode.ExtensionContext) { + const config = vscode.workspace.getConfiguration('abysius'); + + // Initialize API client + api = new AbysiusApi({ + apiKey: config.get('apiKey', ''), + chatEndpoint: config.get('chatEndpoint', 'https://api.abysius.ai/v1/chat'), + inlineEndpoint: config.get('inlineEndpoint', 'https://api.abysius.ai/v1/inline'), + model: config.get('model', 'abysius-coder') + }); + + // Register inline completion provider + if (config.get('enableInlineCompletions', true)) { + inlineProvider = new InlineCompletionProvider(api); + + const selector: vscode.DocumentSelector = [ + { scheme: 'file' }, + { scheme: 'untitled' } + ]; + + const provider = vscode.languages.registerInlineCompletionItemProvider( + selector, + inlineProvider + ); + context.subscriptions.push(provider); + } + + // Register chat panel + chatPanel = new ChatPanel(context.extensionUri, api); + + const chatViewProvider = vscode.window.registerWebviewViewProvider( + 'abysius.chatPanel', + chatPanel, + { webviewOptions: { retainContextWhenHidden: true } } + ); + context.subscriptions.push(chatViewProvider); + + // Set context for UI visibility + vscode.commands.executeCommand('setContext', 'abysiusChatEnabled', true); + + // Commands + context.subscriptions.push( + vscode.commands.registerCommand('abysius.openChat', () => { + chatPanel?.show(); + }), + + vscode.commands.registerCommand('abysius.acceptInlineCompletion', () => { + inlineProvider?.accept(); + }), + + vscode.commands.registerCommand('abysius.rejectInlineCompletion', () => { + inlineProvider?.reject(); + }), + + vscode.commands.registerCommand('abysius.triggerInlineCompletion', () => { + inlineProvider?.trigger(); + }), + + vscode.commands.registerCommand('abysius.acceptNextWord', () => { + inlineProvider?.acceptNextWord(); + }), + + vscode.commands.registerCommand('abysius.openSettings', () => { + vscode.commands.executeCommand('workbench.action.openSettings', 'abysius'); + }), + + vscode.commands.registerCommand('abysius.clearChat', () => { + chatPanel?.clear(); + }) + ); + + // Listen for configuration changes + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('abysius')) { + const newConfig = vscode.workspace.getConfiguration('abysius'); + api.updateConfig({ + apiKey: newConfig.get('apiKey', ''), + chatEndpoint: newConfig.get('chatEndpoint', 'https://api.abysius.ai/v1/chat'), + inlineEndpoint: newConfig.get('inlineEndpoint', 'https://api.abysius.ai/v1/inline'), + model: newConfig.get('model', 'abysius-coder') + }); + } + }) + ); + + // Status bar item + const statusItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + 100 + ); + statusItem.text = "$(sparkle) Abysius"; + statusItem.tooltip = "Abysius AI is active"; + statusItem.command = 'abysius.openChat'; + statusItem.show(); + context.subscriptions.push(statusItem); + + console.log('[Abysius AI] Extension activated'); +} + +export function deactivate() { + inlineProvider?.dispose(); + chatPanel?.dispose(); + console.log('[Abysius AI] Extension deactivated'); +} diff --git a/extensions/abysius-ai/src/inlineCompletion.ts b/extensions/abysius-ai/src/inlineCompletion.ts new file mode 100644 index 0000000..35bed10 --- /dev/null +++ b/extensions/abysius-ai/src/inlineCompletion.ts @@ -0,0 +1,187 @@ +import * as vscode from 'vscode'; +import { AbysiusApi, InlineRequest } from './api'; + +export class InlineCompletionProvider implements vscode.InlineCompletionItemProvider { + private api: AbysiusApi; + private debounceTimer: NodeJS.Timeout | undefined; + private currentAbortController: AbortController | undefined; + private visibleCompletion: vscode.InlineCompletionItem | undefined; + private editor: vscode.TextEditor | undefined; + + // Track accept/reject for analytics + private pendingCompletion: { text: string; accepted: boolean } | undefined; + + constructor(api: AbysiusApi) { + this.api = api; + } + + async provideInlineCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + context: vscode.InlineCompletionContext, + token: vscode.CancellationToken + ): Promise { + + const config = vscode.workspace.getConfiguration('abysius'); + if (!config.get('enableInlineCompletions', true)) { + return undefined; + } + + // Don't trigger on comments or strings (basic heuristic) + const lineText = document.lineAt(position.line).text; + if (this._isInComment(lineText, position.character)) { + return undefined; + } + + // Get prefix and suffix around cursor + const prefix = document.getText(new vscode.Range(new vscode.Position(0, 0), position)); + const suffix = document.getText(new vscode.Range(position, document.positionAt(document.getText().length))); + + // Minimum context length heuristic + const lastLinePrefix = lineText.substring(0, position.character).trim(); + if (lastLinePrefix.length < 2 && prefix.split('\n').length < 3) { + return undefined; + } + + const request: InlineRequest = { + prompt: prefix, + suffix: suffix, + language: document.languageId, + filename: document.fileName + }; + + const delay = config.get('inlineCompletionDelay', 300); + + return new Promise((resolve) => { + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + + this.debounceTimer = setTimeout(async () => { + this.currentAbortController = new AbortController(); + + const disposable = token.onCancellationRequested(() => { + this.currentAbortController?.abort(); + }); + + try { + const response = await this.api.getInlineCompletion( + request, + this.currentAbortController.signal + ); + + disposable.dispose(); + + if (!response || !response.completion) { + resolve(undefined); + return; + } + + const completionText = response.completion; + + // Build inline completion item + const item = new vscode.InlineCompletionItem( + completionText, + new vscode.Range(position, position) + ); + + item.command = { + command: 'abysius._onCompletionShown', + title: 'Completion Shown', + arguments: [completionText] + }; + + this.visibleCompletion = item; + this.pendingCompletion = { text: completionText, accepted: false }; + this.editor = vscode.window.activeTextEditor; + + vscode.commands.executeCommand('setContext', 'abysiusInlineCompletionVisible', true); + + resolve([item]); + } catch (err) { + disposable.dispose(); + resolve(undefined); + } + }, delay); + + token.onCancellationRequested(() => { + clearTimeout(this.debounceTimer); + this.currentAbortController?.abort(); + resolve(undefined); + }); + }); + } + + handleDidShowCompletionItem(completionItem: vscode.InlineCompletionItem): void { + // Called when VS Code shows our completion + } + + handleDidPartiallyAcceptCompletionItem( + completionItem: vscode.InlineCompletionItem, + acceptedLength: number + ): void { + if (this.pendingCompletion) { + this.pendingCompletion.accepted = true; + } + } + + accept(): void { + if (this.pendingCompletion) { + this.pendingCompletion.accepted = true; + } + this._hide(); + } + + reject(): void { + this._hide(); + } + + trigger(): void { + // Trigger completion manually + vscode.commands.executeCommand('editor.action.inlineSuggest.trigger'); + } + + acceptNextWord(): void { + if (!this.visibleCompletion || !this.editor) return; + + const completion = this.visibleCompletion.insertText as string; + const nextSpace = completion.search(/\s/); + const wordLength = nextSpace === -1 ? completion.length : nextSpace + 1; + + const position = this.editor.selection.active; + const range = new vscode.Range(position, position.translate(0, wordLength)); + + this.editor.edit(editBuilder => { + editBuilder.replace(range, completion.substring(0, wordLength)); + }); + } + + private _hide(): void { + this.visibleCompletion = undefined; + this.editor = undefined; + vscode.commands.executeCommand('setContext', 'abysiusInlineCompletionVisible', false); + vscode.commands.executeCommand('editor.action.inlineSuggest.hide'); + } + + private _isInComment(lineText: string, charIndex: number): boolean { + const beforeCursor = lineText.substring(0, charIndex); + + // Simple heuristics for common comment styles + if (beforeCursor.trimStart().startsWith('//')) return true; + if (beforeCursor.trimStart().startsWith('#')) return true; + if (beforeCursor.trimStart().startsWith('--')) return true; + if (beforeCursor.trimStart().startsWith('*')) { + const trimmed = beforeCursor.trim(); + if (trimmed.startsWith('*') && !trimmed.startsWith('**')) return true; + } + + return false; + } + + dispose(): void { + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + this.currentAbortController?.abort(); + } +} diff --git a/extensions/abysius-ai/tsconfig.json b/extensions/abysius-ai/tsconfig.json new file mode 100644 index 0000000..b12891c --- /dev/null +++ b/extensions/abysius-ai/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2022", + "lib": [ + "ES2022" + ], + "outDir": "out", + "rootDir": "src", + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cf0690f --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "abysius-codium", + "version": "1.0.0", + "description": "Abysius Codium - Custom VSCodium build with Abysius AI inline completions and chat", + "private": true, + "scripts": { + "dev:ext": "cd extensions/abysius-ai && npm run watch", + "build:ext": "cd extensions/abysius-ai && npm run compile", + "package:ext": "cd extensions/abysius-ai && vsce package", + "build:linux": "./build/build.sh --platform linux --arch x64", + "build:darwin": "./build/build.sh --platform darwin --arch arm64", + "build:win32": "./build/build.sh --platform win32 --arch x64", + "setup": "./build/dev-setup.sh", + "clean": "rm -rf work/ releases/ extensions/abysius-ai/out/ extensions/abysius-ai/*.vsix", + "lint:ext": "cd extensions/abysius-ai && npm run lint" + }, + "repository": { + "type": "git", + "url": "https://github.com/AbysiusAI/AbysiusCodium" + }, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } +} diff --git a/patches/01-product-json.patch b/patches/01-product-json.patch new file mode 100644 index 0000000..3cfccc6 --- /dev/null +++ b/patches/01-product-json.patch @@ -0,0 +1,13 @@ +# VSCodium De-Branding Patches +# These patches are applied to microsoft/vscode before building AbysiusCodium +# They remove Microsoft-specific branding, telemetry, and licensing + +--- a/src/vs/platform/product/common/product.ts ++++ b/src/vs/platform/product/common/product.ts +@@ -1,5 +1,5 @@ + // Placeholder: product.json is copied in at build time + // The actual product.json lives at the repo root and is copied + // into the build output during the build process. +-export const product = { +-}; ++// AbysiusCodium: product.json is injected at build time via build.sh diff --git a/patches/02-strip-default-product.patch b/patches/02-strip-default-product.patch new file mode 100644 index 0000000..92beed0 --- /dev/null +++ b/patches/02-strip-default-product.patch @@ -0,0 +1,72 @@ +diff --git a/product.json b/product.json +--- a/product.json ++++ b/product.json +@@ -1,95 +1,3 @@ + { +- "nameShort": "Code - OSS", +- "nameLong": "Code - OSS", +- "applicationName": "code-oss", +- "dataFolderName": ".vscode-oss", +- "win32MutexName": "vscodeoss", +- "licenseName": "MIT", +- "licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt", +- "serverLicenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt", +- "serverApplicationName": "code-server-oss", +- "serverDataFolderName": ".vscode-server-oss", +- "win32DirName": "Microsoft Code OSS", +- "win32NameVersion": "Microsoft Code OSS", +- "win32RegValueName": "CodeOSS", +- "win32AppId": "", +- "win32x64AppId": "", +- "win32arm64AppId": "", +- "win32UserAppId": "", +- "win32x64UserAppId": "", +- "win32arm64UserAppId": "", +- "win32AppUserModelId": "Microsoft.CodeOSS", +- "win32ShellNameShort": "C&ode - OSS", +- "darwinBundleIdentifier": "com.visualstudio.code.oss", +- "linuxIconName": "com.visualstudio.code.oss", +- "licenseFileName": "LICENSE.txt", +- "reportIssueUrl": "https://github.com/microsoft/vscode/issues/new", +- "urlProtocol": "code-oss", +- "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/", +- "builtInExtensions": [ +- { +- "name": "ms-vscode.js-debug", +- "version": "latest", +- "repo": "https://github.com/microsoft/vscode-js-debug", +- "metadata": { +- "id": "25629058-ddac-4e17-abba-74678e126c5d", +- "publisherId": { +- "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", +- "publisherName": "ms-vscode", +- "displayName": "Microsoft", +- "flags": "verified" +- }, +- "publisherDisplayName": "Microsoft" +- } +- }, +- { +- "name": "ms-vscode.js-debug-companion", +- "version": "latest", +- "repo": "https://github.com/microsoft/vscode-js-debug-companion", +- "metadata": { +- "id": "99cb0b7f-7354-4278-8ec1-708cd8e1fab7", +- "publisherId": { +- "publisherId": "941a0e64-8e57-4b6d-8a3b-123456789abc", +- "publisherName": "ms-vscode", +- "displayName": "Microsoft", +- "flags": "verified" +- }, +- "publisherDisplayName": "Microsoft" +- } +- } +- ], +- "linkProtectionTrustedDomains": [ +- "https://*.vscode-unpkg.net" +- ], +- "configurationDefaults": { +- "workbench.welcomePage.extraLinks": [] +- } ++ // AbysiusCodium: product.json is injected at build time + } diff --git a/product.json b/product.json new file mode 100644 index 0000000..b912dcc --- /dev/null +++ b/product.json @@ -0,0 +1,96 @@ +{ + "nameShort": "AbysiusCodium", + "nameLong": "Abysius Codium", + "applicationName": "abysius-codium", + "dataFolderName": ".abysius-codium", + "win32MutexName": "abysiuscodium", + "licenseName": "MIT", + "licenseUrl": "https://github.com/AbysiusAI/AbysiusCodium/blob/main/LICENSE", + "serverLicenseUrl": "https://github.com/AbysiusAI/AbysiusCodium/blob/main/LICENSE.txt", + "serverApplicationName": "abysius-codium-server", + "serverDataFolderName": ".abysius-codium-server", + "win32DirName": "AbysiusCodium", + "win32NameVersion": "Abysius Codium", + "win32RegValueName": "AbysiusCodium", + "win32AppId": "{{E34003BB-9E10-4501-8C11-BE5F2A2405E2}}", + "win32x64AppId": "{{D77B794E-0499-4AC6-830E-6F30B5E88B56}}", + "win32arm64AppId": "{{D1ACE434-89C5-48D1-AC42-0D92240E4F00}}", + "win32UserAppId": "{{C6065F05-9603-4FC5-8101-B068128355C2}}", + "win32x64UserAppId": "{{CC6B787D-37A0-49E8-AE24-8559A032BE0C}}", + "win32arm64UserAppId": "{{3AEBF37C-BB6E-4FF5-B6A4-C15D7524CADF}}", + "win32AppUserModelId": "Abysius.AI.AbysiusCodium", + "win32ShellNameShort": "Abysius Codium", + "darwinBundleIdentifier": "ai.abysius.codium", + "linuxIconName": "abysius-codium", + "licenseFileName": "LICENSE.txt", + "reportIssueUrl": "https://github.com/AbysiusAI/AbysiusCodium/issues/new", + "urlProtocol": "abysius-codium", + "extensionsGallery": { + "serviceUrl": "https://open-vsx.org/vscode/gallery", + "itemUrl": "https://open-vsx.org/vscode/item", + "resourceUrlTemplate": "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}", + "nlsBaseUrl": "", + "publisherUrl": "" + }, + "extensionEnabledApiProposals": { + "abysius.abysius-ai": ["inlineCompletionsAdditions", "chatParticipant", "languageModelSystem"] + }, + "enabledApiProposals": ["inlineCompletionsAdditions", "chatParticipant", "languageModelSystem", "chatProvider"], + "linkProtectionTrustedDomains": ["https://*.abysius.ai", "https://github.com/AbysiusAI"], + "builtInExtensions": [ + { + "name": "abysius.abysius-ai", + "version": "0.1.0", + "repo": "https://github.com/AbysiusAI/AbysiusCodium", + "metadata": { + "id": "abysius.abysius-ai", + "publisherId": { + "publisherId": "abysius", + "publisherName": "abysius", + "displayName": "Abysius AI", + "flags": "verified" + }, + "publisherDisplayName": "Abysius AI" + } + }, + { + "name": "ms-vscode.js-debug", + "version": "latest", + "repo": "https://github.com/microsoft/vscode-js-debug", + "metadata": { + "id": "25629058-ddac-4e17-abba-74678e126c5d", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.js-debug-companion", + "version": "latest", + "repo": "https://github.com/microsoft/vscode-js-debug-companion", + "metadata": { + "id": "99cb0b7f-7354-4278-8ec1-708cd8e1fab7", + "publisherId": { + "publisherId": "941a0e64-8e57-4b6d-8a3b-123456789abc", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + } + ], + "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/", + "crashReporter": { + "companyName": "AbysiusAI", + "productName": "AbysiusCodium" + }, + "settingsSyncStoreUrl": "", + "commit": "abysius-build", + "date": "2026-06-17", + "version": "1.0.0" +}