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 `${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"
+}