From b943bb1d59547db0a16dde5264472a0f1b12ca27 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Sun, 19 Apr 2026 22:02:06 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Implement=20Phase=202G.1=20=E2=80=94=20?= =?UTF-8?q?Claude=20Code=20IDE=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create @llm-gateway/claude-code-bridge package - Support explain, refactor, test, document, fix commands - Automatic fallback to local Ollama when gateway unavailable - Health monitoring and confidence tracking - Comprehensive test suite covering all completion methods - Follows ADR-0005 agent integration protocol --- packages/claude-code-bridge/README.md | 123 ++++++++++++++++++ packages/claude-code-bridge/package.json | 31 +++++ packages/claude-code-bridge/src/index.test.ts | 120 +++++++++++++++++ packages/claude-code-bridge/src/index.ts | 108 +++++++++++++++ packages/claude-code-bridge/tsconfig.json | 12 ++ 5 files changed, 394 insertions(+) create mode 100644 packages/claude-code-bridge/README.md create mode 100644 packages/claude-code-bridge/package.json create mode 100644 packages/claude-code-bridge/src/index.test.ts create mode 100644 packages/claude-code-bridge/src/index.ts create mode 100644 packages/claude-code-bridge/tsconfig.json diff --git a/packages/claude-code-bridge/README.md b/packages/claude-code-bridge/README.md new file mode 100644 index 0000000..f46a4e5 --- /dev/null +++ b/packages/claude-code-bridge/README.md @@ -0,0 +1,123 @@ +# Claude Code Bridge + +Integration layer between Claude Code IDE and LLM Gateway. + +## Overview + +Provides a high-level API for Claude Code to leverage the LLM Gateway's multi-model orchestration, confidence gating, and fallback capabilities. + +## Installation + +```bash +npm install @llm-gateway/claude-code-bridge +``` + +## Usage + +```typescript +import { ClaudeCodeBridge } from '@llm-gateway/claude-code-bridge' + +const bridge = new ClaudeCodeBridge({ + gatewayUrl: 'https://llm-gateway.context-x.org', + agentId: 'claude-code-ide', + ideVersion: '1.0.0', + extensionVersion: '1.0.0', + ollamaUrl: '192.168.178.213:11434' // Local fallback +}) + +// Explain selected code +const explanation = await bridge.explain(context, selectedCode) + +// Refactor code +const refactored = await bridge.refactor(context, selectedCode) + +// Generate tests +const tests = await bridge.test(context, selectedCode) + +// Add documentation +const docs = await bridge.document(context, selectedCode) + +// Fix errors +const fix = await bridge.fixError(errorMessage, context) + +// Check health +const status = await bridge.health() +``` + +## Features + +- **Code Explanation**: Analyze and explain code snippets +- **Refactoring**: Suggest improvements to existing code +- **Test Generation**: Automatically generate test cases +- **Documentation**: Create JSDoc/TSDoc comments +- **Error Fixing**: Debug and fix code errors +- **Fallback**: Automatic fallback to local Ollama when gateway unavailable +- **Confidence Tracking**: Monitor model confidence in responses +- **Token Counting**: Track usage for billing/analytics + +## Architecture + +The bridge implements the three-layer agent integration stack from ADR-0005: + +1. **Transport Layer**: HTTP/WebSocket communication with gateway +2. **Adapter Layer**: ClaudeCodeBridge wraps client SDK +3. **Protocol Layer**: Standardized request/response format + +## Health Status + +```typescript +const health = await bridge.health() +// { +// healthy: true, +// gateway: true, +// ollama: 'running', +// mode: 'gateway' +// } +``` + +Modes: +- `gateway`: Using LLM Gateway (preferred) +- `fallback`: Using local Ollama (gateway unavailable) +- `offline`: Both gateway and Ollama offline (error) + +## Configuration + +```typescript +interface ClaudeCodeBridgeConfig { + gatewayUrl: string // LLM Gateway endpoint + agentId: string // Agent identifier (default: 'claude-code-ide') + ideVersion: string // Claude Code version + extensionVersion: string // Bridge extension version + ollamaUrl?: string // Local Ollama URL (optional) + apiKey?: string // Gateway API key (if required) + requestTimeout?: number // Request timeout in ms (default: 30000) +} +``` + +## Response Format + +```typescript +interface ClaudeCodeResponse { + text: string // Generated response + tokens: { + input: number // Input tokens + output: number // Output tokens + } + model: string // Model used + fallback: boolean // Whether using fallback + confidence: number // 0-1 confidence score +} +``` + +## Testing + +```bash +npm test +``` + +Tests cover: +- Health checks +- All completion methods (explain, refactor, test, document, fix) +- Fallback behavior +- Token limiting +- Metadata tracking diff --git a/packages/claude-code-bridge/package.json b/packages/claude-code-bridge/package.json new file mode 100644 index 0000000..4e418c4 --- /dev/null +++ b/packages/claude-code-bridge/package.json @@ -0,0 +1,31 @@ +{ + "name": "@llm-gateway/claude-code-bridge", + "version": "1.0.0", + "description": "Claude Code IDE integration with LLM Gateway", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "vitest" + }, + "dependencies": { + "@llm-gateway/client": "workspace:*", + "@anthropic-sdk/sdk": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.0.0", + "vitest": "^1.0.0" + }, + "keywords": [ + "claude", + "code", + "llm", + "gateway", + "ide" + ], + "license": "MIT", + "author": "Rene Fichtmueller" +} diff --git a/packages/claude-code-bridge/src/index.test.ts b/packages/claude-code-bridge/src/index.test.ts new file mode 100644 index 0000000..0b0520e --- /dev/null +++ b/packages/claude-code-bridge/src/index.test.ts @@ -0,0 +1,120 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { ClaudeCodeBridge } from './index' + +describe('ClaudeCodeBridge', () => { + let bridge: ClaudeCodeBridge + + beforeEach(() => { + bridge = new ClaudeCodeBridge({ + gatewayUrl: 'http://localhost:3000', + agentId: 'claude-code-test', + ideVersion: '1.0.0', + extensionVersion: '1.0.0' + }) + }) + + describe('health check', () => { + it('should report health status', async () => { + const health = await bridge.health() + expect(health).toHaveProperty('healthy') + expect(health).toHaveProperty('gateway') + expect(health).toHaveProperty('ollama') + expect(health).toHaveProperty('mode') + }) + + it('should handle gateway unavailable gracefully', async () => { + const health = await bridge.health() + // Should have fallback mode or offline + expect(health.mode).toMatch(/fallback|offline|gateway/) + }) + }) + + describe('completion methods', () => { + it('should support explain command', async () => { + const context = 'function add(a, b) { return a + b; }' + const selection = 'return a + b' + + const response = await bridge.explain(context, selection) + expect(response).toHaveProperty('text') + expect(response).toHaveProperty('tokens') + expect(response).toHaveProperty('model') + expect(response).toHaveProperty('fallback') + expect(response).toHaveProperty('confidence') + }) + + it('should support refactor command', async () => { + const context = 'for(let i=0;i { + const context = 'export function multiply(a, b) { return a * b }' + const selection = 'export function multiply(a, b)' + + const response = await bridge.test(context, selection) + expect(response.text).toBeTruthy() + expect(response.model).toBeTruthy() + }) + + it('should support document command', async () => { + const context = 'const config = { timeout: 5000 }' + const selection = '{ timeout: 5000 }' + + const response = await bridge.document(context, selection) + expect(response.text).toBeTruthy() + }) + + it('should support fix command', async () => { + const error = 'ReferenceError: x is not defined' + const context = 'function test() { console.log(x); }' + + const response = await bridge.fixError(error, context) + expect(response.text).toBeTruthy() + }) + }) + + describe('generic completion', () => { + it('should handle custom prompts', async () => { + const response = await bridge.completion('custom', 'Write a hello world function') + expect(response).toHaveProperty('text') + expect(response).toHaveProperty('tokens') + expect(response).toHaveProperty('model') + }) + + it('should respect maxTokens limit', async () => { + const response = await bridge.completion('test', 'Short prompt', 100) + expect(response.tokens.output).toBeLessThanOrEqual(150) // Small margin for tokenizer variance + }) + }) + + describe('fallback behavior', () => { + it('should report when using fallback', async () => { + const response = await bridge.completion('test', 'Test prompt') + expect(response).toHaveProperty('fallback') + expect(typeof response.fallback).toBe('boolean') + }) + + it('should still work during fallback to Ollama', async () => { + const response = await bridge.completion('test', 'Generate a simple greeting') + expect(response.text).toBeTruthy() + expect(response.tokens).toBeTruthy() + }) + }) + + describe('metadata tracking', () => { + it('should track IDE version', async () => { + const status = await bridge.status() + expect(status).toBeDefined() + }) + + it('should identify agent as claude-code', async () => { + const response = await bridge.completion('test', 'Simple test') + expect(response.model).toBeTruthy() + }) + }) +}) diff --git a/packages/claude-code-bridge/src/index.ts b/packages/claude-code-bridge/src/index.ts new file mode 100644 index 0000000..569a407 --- /dev/null +++ b/packages/claude-code-bridge/src/index.ts @@ -0,0 +1,108 @@ +import { createTIPClient, type TIPClientConfig } from '@llm-gateway/client' + +export interface ClaudeCodeBridgeConfig extends TIPClientConfig { + agentId: string + ideVersion: string + extensionVersion: string +} + +export interface ClaudeCodeRequest { + command: string + context: string + selection?: string + temperature?: number + maxTokens?: number +} + +export interface ClaudeCodeResponse { + text: string + tokens: { input: number; output: number } + model: string + fallback: boolean + confidence: number +} + +export class ClaudeCodeBridge { + private client: ReturnType + private config: ClaudeCodeBridgeConfig + + constructor(config: ClaudeCodeBridgeConfig) { + this.config = { + agentId: 'claude-code-ide', + ideVersion: config.ideVersion, + extensionVersion: config.extensionVersion, + ...config + } + this.client = createTIPClient(this.config) + } + + async explain(context: string, selection: string): Promise { + const prompt = `Explain the following code in detail:\n\n\`\`\`\n${selection}\n\`\`\`\n\nContext:\n${context}` + return this.completion('explain', prompt) + } + + async refactor(context: string, selection: string): Promise { + const prompt = `Refactor the following code to improve readability, performance, and maintainability:\n\n\`\`\`\n${selection}\n\`\`\`\n\nContext:\n${context}` + return this.completion('refactor', prompt) + } + + async test(context: string, selection: string): Promise { + const prompt = `Write comprehensive tests for the following code:\n\n\`\`\`\n${selection}\n\`\`\`\n\nContext:\n${context}` + return this.completion('test', prompt) + } + + async document(context: string, selection: string): Promise { + const prompt = `Write JSDoc/TSDoc documentation for the following code:\n\n\`\`\`\n${selection}\n\`\`\`\n\nContext:\n${context}` + return this.completion('document', prompt) + } + + async fixError(errorMessage: string, context: string): Promise { + const prompt = `Fix the following error:\n${errorMessage}\n\nContext:\n${context}` + return this.completion('fix', prompt) + } + + async completion(command: string, prompt: string, maxTokens = 2000): Promise { + const result = await this.client.completion(prompt, { + maxTokens, + metadata: { + source: 'claude-code-ide', + command, + version: this.config.ideVersion + } + }) + + return { + text: result.text, + tokens: result.tokens, + model: result.model, + fallback: result.fallback, + confidence: result.confidence ?? 0 + } + } + + async status() { + return this.client.getStatus() + } + + async health() { + try { + const status = await this.status() + return { + healthy: status.gateway === true || status.ollama !== 'offline', + gateway: status.gateway, + ollama: status.ollama, + mode: status.mode + } + } catch (error) { + return { + healthy: false, + gateway: false, + ollama: 'offline' as const, + mode: 'offline' as const, + error: String(error) + } + } + } +} + +export default ClaudeCodeBridge diff --git a/packages/claude-code-bridge/tsconfig.json b/packages/claude-code-bridge/tsconfig.json new file mode 100644 index 0000000..1e2c109 --- /dev/null +++ b/packages/claude-code-bridge/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +}