diff --git a/.claude/workflows/cli-tools-usage.md b/.claude/workflows/cli-tools-usage.md index 6113f635..8f2f7c0f 100644 --- a/.claude/workflows/cli-tools-usage.md +++ b/.claude/workflows/cli-tools-usage.md @@ -419,7 +419,7 @@ CCW automatically maps to tool-specific syntax: **Analysis Task** (Security Audit): ```bash -timeout 600 ccw cli -p " +ccw cli -p " PURPOSE: Identify OWASP Top 10 vulnerabilities in authentication module to pass security audit; success = all critical/high issues documented with remediation TASK: • Scan for injection flaws (SQL, command, LDAP) • Check authentication bypass vectors • Evaluate session management • Assess sensitive data exposure MODE: analysis @@ -431,7 +431,7 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) $ **Implementation Task** (New Feature): ```bash -timeout 1800 ccw cli -p " +ccw cli -p " PURPOSE: Implement rate limiting for API endpoints to prevent abuse; must be configurable per-endpoint; backward compatible with existing clients TASK: • Create rate limiter middleware with sliding window • Implement per-route configuration • Add Redis backend for distributed state • Include bypass for internal services MODE: write @@ -443,7 +443,7 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) $(ca **Bug Fix Task**: ```bash -timeout 900 ccw cli -p " +ccw cli -p " PURPOSE: Fix memory leak in WebSocket connection handler causing server OOM after 24h; root cause must be identified before any fix TASK: • Trace connection lifecycle from open to close • Identify event listener accumulation • Check cleanup on disconnect • Verify garbage collection eligibility MODE: analysis @@ -455,7 +455,7 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) $ **Refactoring Task**: ```bash -timeout 1200 ccw cli -p " +ccw cli -p " PURPOSE: Refactor payment processing to use strategy pattern for multi-gateway support; no functional changes; all existing tests must pass TASK: • Extract gateway interface from current implementation • Create strategy classes for Stripe, PayPal • Implement factory for gateway selection • Migrate existing code to use strategies MODE: write @@ -470,13 +470,7 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) $(ca ### Timeout Allocation (Bash) -CLI internal timeout is disabled; controlled by external bash `timeout` command: - -```bash -# Syntax: timeout ccw cli ... -timeout 600 ccw cli -p "..." --tool gemini --mode analysis # 10 minutes -timeout 1800 ccw cli -p "..." --tool codex --mode write # 30 minutes -``` +controlled by external bash `timeout` command: **Recommended Time Allocation**: diff --git a/ccw/src/core/routes/codexlens-routes.ts b/ccw/src/core/routes/codexlens-routes.ts index 3bac068e..1d49f27b 100644 --- a/ccw/src/core/routes/codexlens-routes.ts +++ b/ccw/src/core/routes/codexlens-routes.ts @@ -9,6 +9,7 @@ import { bootstrapVenv, executeCodexLens, checkSemanticStatus, + ensureLiteLLMEmbedderReady, installSemantic, detectGpuSupport, uninstallCodexLens, @@ -405,9 +406,17 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise // API: CodexLens Init (Initialize workspace index) if (pathname === '/api/codexlens/init' && req.method === 'POST') { handlePostRequest(req, res, async (body) => { - const { path: projectPath, indexType = 'vector', embeddingModel = 'code', embeddingBackend = 'fastembed' } = body; + const { path: projectPath, indexType = 'vector', embeddingModel = 'code', embeddingBackend = 'fastembed', maxWorkers = 1 } = body; const targetPath = projectPath || initialPath; + // Ensure LiteLLM backend dependencies are installed before running the CLI + if (indexType !== 'normal' && embeddingBackend === 'litellm') { + const installResult = await ensureLiteLLMEmbedderReady(); + if (!installResult.success) { + return { success: false, error: installResult.error || 'Failed to prepare LiteLLM embedder', status: 500 }; + } + } + // Build CLI arguments based on index type const args = ['init', targetPath, '--json']; if (indexType === 'normal') { @@ -419,6 +428,10 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise if (embeddingBackend && embeddingBackend !== 'fastembed') { args.push('--embedding-backend', embeddingBackend); } + // Add max workers for concurrent API calls (useful for litellm backend) + if (maxWorkers && maxWorkers > 1) { + args.push('--max-workers', String(maxWorkers)); + } } // Broadcast start event diff --git a/ccw/src/templates/dashboard-js/views/codexlens-manager.js b/ccw/src/templates/dashboard-js/views/codexlens-manager.js index ed1de360..53471bd4 100644 --- a/ccw/src/templates/dashboard-js/views/codexlens-manager.js +++ b/ccw/src/templates/dashboard-js/views/codexlens-manager.js @@ -1167,14 +1167,17 @@ async function deleteModel(profile) { * @param {string} indexType - 'vector' (with embeddings), 'normal' (FTS only), or 'full' (FTS + Vector) * @param {string} embeddingModel - Model profile: 'code', 'fast' * @param {string} embeddingBackend - Backend: 'fastembed' (local) or 'litellm' (API) + * @param {number} maxWorkers - Max concurrent API calls for embedding generation (default: 1) */ -async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend) { +async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend, maxWorkers) { indexType = indexType || 'vector'; embeddingModel = embeddingModel || 'code'; embeddingBackend = embeddingBackend || 'fastembed'; + maxWorkers = maxWorkers || 1; - // For vector or full index, check if semantic dependencies are available - if (indexType === 'vector' || indexType === 'full') { + // For vector/full index with local backend, check if semantic dependencies are available + // LiteLLM backend uses remote embeddings and does not require fastembed/ONNX deps. + if ((indexType === 'vector' || indexType === 'full') && embeddingBackend !== 'litellm') { try { var semanticResponse = await fetch('/api/codexlens/semantic/status'); var semanticStatus = await semanticResponse.json(); @@ -1275,7 +1278,7 @@ async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend) { var apiIndexType = (indexType === 'full') ? 'vector' : indexType; // Start indexing with specified type and model - startCodexLensIndexing(apiIndexType, embeddingModel, embeddingBackend); + startCodexLensIndexing(apiIndexType, embeddingModel, embeddingBackend, maxWorkers); } /** @@ -1283,11 +1286,13 @@ async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend) { * @param {string} indexType - 'vector' or 'normal' * @param {string} embeddingModel - Model profile: 'code', 'fast' * @param {string} embeddingBackend - Backend: 'fastembed' (local) or 'litellm' (API) + * @param {number} maxWorkers - Max concurrent API calls for embedding generation (default: 1) */ -async function startCodexLensIndexing(indexType, embeddingModel, embeddingBackend) { +async function startCodexLensIndexing(indexType, embeddingModel, embeddingBackend, maxWorkers) { indexType = indexType || 'vector'; embeddingModel = embeddingModel || 'code'; embeddingBackend = embeddingBackend || 'fastembed'; + maxWorkers = maxWorkers || 1; var statusText = document.getElementById('codexlensIndexStatus'); var progressBar = document.getElementById('codexlensIndexProgressBar'); var percentText = document.getElementById('codexlensIndexPercent'); @@ -1319,11 +1324,11 @@ async function startCodexLensIndexing(indexType, embeddingModel, embeddingBacken } try { - console.log('[CodexLens] Starting index for:', projectPath, 'type:', indexType, 'model:', embeddingModel, 'backend:', embeddingBackend); + console.log('[CodexLens] Starting index for:', projectPath, 'type:', indexType, 'model:', embeddingModel, 'backend:', embeddingBackend, 'maxWorkers:', maxWorkers); var response = await fetch('/api/codexlens/init', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path: projectPath, indexType: indexType, embeddingModel: embeddingModel, embeddingBackend: embeddingBackend }) + body: JSON.stringify({ path: projectPath, indexType: indexType, embeddingModel: embeddingModel, embeddingBackend: embeddingBackend, maxWorkers: maxWorkers }) }); var result = await response.json(); @@ -1992,6 +1997,17 @@ function buildCodexLensManagerPage(config) { '' + '

' + t('codexlens.modelHint') + '

' + '' + + // Concurrency selector (only for LiteLLM backend) + '' + // Index buttons - two modes: full (FTS + Vector) or FTS only '
' + '