claude-code-workflow 6.2.9 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +17 -2
- package/.claude/commands/workflow/lite-plan.md +7 -4
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
- package/.claude/workflows/cli-tools-usage.md +14 -24
- package/.claude/workflows/context-tools-ace.md +105 -0
- package/.codex/AGENTS.md +51 -1
- package/.codex/prompts/compact.md +378 -0
- package/.gemini/GEMINI.md +57 -20
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +3 -1
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts +2 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +129 -8
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/hook.d.ts.map +1 -1
- package/ccw/dist/commands/hook.js +3 -2
- package/ccw/dist/commands/hook.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
- package/ccw/dist/config/litellm-api-config-manager.js +770 -0
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
- package/ccw/dist/config/provider-models.d.ts +73 -0
- package/ccw/dist/config/provider-models.d.ts.map +1 -0
- package/ccw/dist/config/provider-models.js +172 -0
- package/ccw/dist/config/provider-models.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts.map +1 -1
- package/ccw/dist/core/cache-manager.js +3 -5
- package/ccw/dist/core/cache-manager.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +3 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +208 -0
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +234 -18
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.js +30 -32
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.js +85 -0
- package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.js +2 -2
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +39 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +15 -1
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/mcp-server/index.js +1 -1
- package/ccw/dist/mcp-server/index.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +97 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
- package/ccw/dist/tools/claude-cli-tools.js +276 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +80 -20
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +9 -2
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +114 -9
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/context-cache-store.d.ts +136 -0
- package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache-store.js +256 -0
- package/ccw/dist/tools/context-cache-store.js.map +1 -0
- package/ccw/dist/tools/context-cache.d.ts +56 -0
- package/ccw/dist/tools/context-cache.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache.js +294 -0
- package/ccw/dist/tools/context-cache.js.map +1 -0
- package/ccw/dist/tools/core-memory.d.ts.map +1 -1
- package/ccw/dist/tools/core-memory.js +33 -19
- package/ccw/dist/tools/core-memory.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +85 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-client.js +188 -0
- package/ccw/dist/tools/litellm-client.js.map +1 -0
- package/ccw/dist/tools/litellm-executor.d.ts +34 -0
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-executor.js +192 -0
- package/ccw/dist/tools/litellm-executor.js.map +1 -0
- package/ccw/dist/tools/pattern-parser.d.ts +55 -0
- package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
- package/ccw/dist/tools/pattern-parser.js +237 -0
- package/ccw/dist/tools/pattern-parser.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +1 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +117 -41
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/dist/types/litellm-api-config.d.ts +294 -0
- package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
- package/ccw/dist/types/litellm-api-config.js +8 -0
- package/ccw/dist/types/litellm-api-config.js.map +1 -0
- package/ccw/src/cli.ts +3 -1
- package/ccw/src/commands/cli.ts +153 -9
- package/ccw/src/commands/hook.ts +3 -2
- package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
- package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
- package/ccw/src/config/provider-models.ts +222 -0
- package/ccw/src/core/cache-manager.ts +292 -294
- package/ccw/src/core/dashboard-generator.ts +3 -1
- package/ccw/src/core/routes/cli-routes.ts +234 -0
- package/ccw/src/core/routes/codexlens-routes.ts +241 -19
- package/ccw/src/core/routes/hooks-routes.ts +399 -405
- package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
- package/ccw/src/core/routes/litellm-routes.ts +107 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
- package/ccw/src/core/routes/status-routes.ts +51 -0
- package/ccw/src/core/server.ts +15 -1
- package/ccw/src/mcp-server/index.ts +1 -1
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
- package/ccw/src/templates/dashboard-css/18-cli-settings.css +34 -0
- package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
- package/ccw/src/templates/dashboard-js/components/cli-status.js +376 -9
- package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
- package/ccw/src/templates/dashboard-js/i18n.js +589 -3
- package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +212 -24
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
- package/ccw/src/templates/dashboard.html +840 -831
- package/ccw/src/tools/claude-cli-tools.ts +371 -0
- package/ccw/src/tools/cli-executor.ts +87 -20
- package/ccw/src/tools/codex-lens.ts +146 -9
- package/ccw/src/tools/context-cache-store.ts +368 -0
- package/ccw/src/tools/context-cache.ts +393 -0
- package/ccw/src/tools/core-memory.ts +33 -19
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +246 -0
- package/ccw/src/tools/litellm-executor.ts +241 -0
- package/ccw/src/tools/pattern-parser.ts +329 -0
- package/ccw/src/tools/smart-search.ts +142 -41
- package/ccw/src/types/litellm-api-config.ts +402 -0
- package/ccw-litellm/README.md +180 -0
- package/ccw-litellm/pyproject.toml +35 -0
- package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
- package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
- package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
- package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +360 -22
- package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
- package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
- package/codex-lens/src/codexlens/cli/output.py +12 -1
- package/codex-lens/src/codexlens/config.py +93 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +6 -2
- package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
- package/codex-lens/src/codexlens/search/ranking.py +1 -1
- package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/base.py +61 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
- package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
- package/codex-lens/src/codexlens/semantic/factory.py +98 -0
- package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
- package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
- package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/package.json +3 -1
- package/.codex/prompts.zip +0 -0
|
@@ -33,6 +33,14 @@ const VENV_PYTHON =
|
|
|
33
33
|
let bootstrapChecked = false;
|
|
34
34
|
let bootstrapReady = false;
|
|
35
35
|
|
|
36
|
+
// Venv status cache with TTL
|
|
37
|
+
interface VenvStatusCache {
|
|
38
|
+
status: ReadyStatus;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
}
|
|
41
|
+
let venvStatusCache: VenvStatusCache | null = null;
|
|
42
|
+
const VENV_STATUS_TTL = 5 * 60 * 1000; // 5 minutes TTL
|
|
43
|
+
|
|
36
44
|
// Track running indexing process for cancellation
|
|
37
45
|
let currentIndexingProcess: ReturnType<typeof spawn> | null = null;
|
|
38
46
|
let currentIndexingAborted = false;
|
|
@@ -77,6 +85,7 @@ interface SemanticStatus {
|
|
|
77
85
|
backend?: string;
|
|
78
86
|
accelerator?: string;
|
|
79
87
|
providers?: string[];
|
|
88
|
+
litellmAvailable?: boolean;
|
|
80
89
|
error?: string;
|
|
81
90
|
}
|
|
82
91
|
|
|
@@ -115,6 +124,13 @@ interface ProgressInfo {
|
|
|
115
124
|
totalFiles?: number;
|
|
116
125
|
}
|
|
117
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Clear venv status cache (call after install/uninstall operations)
|
|
129
|
+
*/
|
|
130
|
+
function clearVenvStatusCache(): void {
|
|
131
|
+
venvStatusCache = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
118
134
|
/**
|
|
119
135
|
* Detect available Python 3 executable
|
|
120
136
|
* @returns Python executable command
|
|
@@ -137,17 +153,27 @@ function getSystemPython(): string {
|
|
|
137
153
|
|
|
138
154
|
/**
|
|
139
155
|
* Check if CodexLens venv exists and has required packages
|
|
156
|
+
* @param force - Force refresh cache (default: false)
|
|
140
157
|
* @returns Ready status
|
|
141
158
|
*/
|
|
142
|
-
async function checkVenvStatus(): Promise<ReadyStatus> {
|
|
159
|
+
async function checkVenvStatus(force = false): Promise<ReadyStatus> {
|
|
160
|
+
// Use cached result if available and not expired
|
|
161
|
+
if (!force && venvStatusCache && (Date.now() - venvStatusCache.timestamp < VENV_STATUS_TTL)) {
|
|
162
|
+
return venvStatusCache.status;
|
|
163
|
+
}
|
|
164
|
+
|
|
143
165
|
// Check venv exists
|
|
144
166
|
if (!existsSync(CODEXLENS_VENV)) {
|
|
145
|
-
|
|
167
|
+
const result = { ready: false, error: 'Venv not found' };
|
|
168
|
+
venvStatusCache = { status: result, timestamp: Date.now() };
|
|
169
|
+
return result;
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
// Check python executable exists
|
|
149
173
|
if (!existsSync(VENV_PYTHON)) {
|
|
150
|
-
|
|
174
|
+
const result = { ready: false, error: 'Python executable not found in venv' };
|
|
175
|
+
venvStatusCache = { status: result, timestamp: Date.now() };
|
|
176
|
+
return result;
|
|
151
177
|
}
|
|
152
178
|
|
|
153
179
|
// Check codexlens is importable
|
|
@@ -168,15 +194,21 @@ async function checkVenvStatus(): Promise<ReadyStatus> {
|
|
|
168
194
|
});
|
|
169
195
|
|
|
170
196
|
child.on('close', (code) => {
|
|
197
|
+
let result: ReadyStatus;
|
|
171
198
|
if (code === 0) {
|
|
172
|
-
|
|
199
|
+
result = { ready: true, version: stdout.trim() };
|
|
173
200
|
} else {
|
|
174
|
-
|
|
201
|
+
result = { ready: false, error: `CodexLens not installed: ${stderr}` };
|
|
175
202
|
}
|
|
203
|
+
// Cache the result
|
|
204
|
+
venvStatusCache = { status: result, timestamp: Date.now() };
|
|
205
|
+
resolve(result);
|
|
176
206
|
});
|
|
177
207
|
|
|
178
208
|
child.on('error', (err) => {
|
|
179
|
-
|
|
209
|
+
const result = { ready: false, error: `Failed to check venv: ${err.message}` };
|
|
210
|
+
venvStatusCache = { status: result, timestamp: Date.now() };
|
|
211
|
+
resolve(result);
|
|
180
212
|
});
|
|
181
213
|
});
|
|
182
214
|
}
|
|
@@ -198,8 +230,15 @@ async function checkSemanticStatus(): Promise<SemanticStatus> {
|
|
|
198
230
|
import sys
|
|
199
231
|
import json
|
|
200
232
|
try:
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
import codexlens.semantic as semantic
|
|
234
|
+
SEMANTIC_AVAILABLE = bool(getattr(semantic, "SEMANTIC_AVAILABLE", False))
|
|
235
|
+
SEMANTIC_BACKEND = getattr(semantic, "SEMANTIC_BACKEND", None)
|
|
236
|
+
LITELLM_AVAILABLE = bool(getattr(semantic, "LITELLM_AVAILABLE", False))
|
|
237
|
+
result = {
|
|
238
|
+
"available": SEMANTIC_AVAILABLE,
|
|
239
|
+
"backend": SEMANTIC_BACKEND if SEMANTIC_AVAILABLE else None,
|
|
240
|
+
"litellm_available": LITELLM_AVAILABLE,
|
|
241
|
+
}
|
|
203
242
|
|
|
204
243
|
# Get ONNX providers for accelerator info
|
|
205
244
|
try:
|
|
@@ -250,6 +289,7 @@ except Exception as e:
|
|
|
250
289
|
backend: result.backend,
|
|
251
290
|
accelerator: result.accelerator || 'CPU',
|
|
252
291
|
providers: result.providers || [],
|
|
292
|
+
litellmAvailable: result.litellm_available || false,
|
|
253
293
|
error: result.error
|
|
254
294
|
});
|
|
255
295
|
} catch {
|
|
@@ -263,6 +303,77 @@ except Exception as e:
|
|
|
263
303
|
});
|
|
264
304
|
}
|
|
265
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Ensure LiteLLM embedder dependencies are available in the CodexLens venv.
|
|
308
|
+
* Installs ccw-litellm into the venv if needed.
|
|
309
|
+
*/
|
|
310
|
+
async function ensureLiteLLMEmbedderReady(): Promise<BootstrapResult> {
|
|
311
|
+
// Ensure CodexLens venv exists and CodexLens is installed.
|
|
312
|
+
const readyStatus = await ensureReady();
|
|
313
|
+
if (!readyStatus.ready) {
|
|
314
|
+
return { success: false, error: readyStatus.error || 'CodexLens not ready' };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check if ccw_litellm can be imported
|
|
318
|
+
const importStatus = await new Promise<{ ok: boolean; error?: string }>((resolve) => {
|
|
319
|
+
const child = spawn(VENV_PYTHON, ['-c', 'import ccw_litellm; print("OK")'], {
|
|
320
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
321
|
+
timeout: 15000,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
let stderr = '';
|
|
325
|
+
child.stderr.on('data', (data) => {
|
|
326
|
+
stderr += data.toString();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
child.on('close', (code) => {
|
|
330
|
+
resolve({ ok: code === 0, error: stderr.trim() || undefined });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
child.on('error', (err) => {
|
|
334
|
+
resolve({ ok: false, error: err.message });
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
if (importStatus.ok) {
|
|
339
|
+
return { success: true };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const pipPath =
|
|
343
|
+
process.platform === 'win32'
|
|
344
|
+
? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
|
|
345
|
+
: join(CODEXLENS_VENV, 'bin', 'pip');
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
console.log('[CodexLens] Installing ccw-litellm for LiteLLM embedding backend...');
|
|
349
|
+
|
|
350
|
+
const possiblePaths = [
|
|
351
|
+
join(process.cwd(), 'ccw-litellm'),
|
|
352
|
+
join(__dirname, '..', '..', '..', 'ccw-litellm'), // ccw/src/tools -> project root
|
|
353
|
+
join(homedir(), 'ccw-litellm'),
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
let installed = false;
|
|
357
|
+
for (const localPath of possiblePaths) {
|
|
358
|
+
if (existsSync(join(localPath, 'pyproject.toml'))) {
|
|
359
|
+
console.log(`[CodexLens] Installing ccw-litellm from local path: ${localPath}`);
|
|
360
|
+
execSync(`"${pipPath}" install -e "${localPath}"`, { stdio: 'inherit' });
|
|
361
|
+
installed = true;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!installed) {
|
|
367
|
+
console.log('[CodexLens] Installing ccw-litellm from PyPI...');
|
|
368
|
+
execSync(`"${pipPath}" install ccw-litellm`, { stdio: 'inherit' });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return { success: true };
|
|
372
|
+
} catch (err) {
|
|
373
|
+
return { success: false, error: `Failed to install ccw-litellm: ${(err as Error).message}` };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
266
377
|
/**
|
|
267
378
|
* GPU acceleration mode for semantic search
|
|
268
379
|
*/
|
|
@@ -421,6 +532,17 @@ async function installSemantic(gpuMode: GpuMode = 'cpu'): Promise<BootstrapResul
|
|
|
421
532
|
|
|
422
533
|
child.on('close', (code) => {
|
|
423
534
|
if (code === 0) {
|
|
535
|
+
// IMPORTANT: fastembed installs onnxruntime (CPU) as dependency, which conflicts
|
|
536
|
+
// with onnxruntime-directml/gpu. Reinstall the GPU version to ensure it takes precedence.
|
|
537
|
+
if (gpuMode !== 'cpu') {
|
|
538
|
+
try {
|
|
539
|
+
console.log(`[CodexLens] Reinstalling ${onnxPackage} to ensure GPU provider works...`);
|
|
540
|
+
execSync(`"${pipPath}" install --force-reinstall ${onnxPackage}`, { stdio: 'pipe', timeout: 300000 });
|
|
541
|
+
console.log(`[CodexLens] ${onnxPackage} reinstalled successfully`);
|
|
542
|
+
} catch (e) {
|
|
543
|
+
console.warn(`[CodexLens] Warning: Failed to reinstall ${onnxPackage}: ${(e as Error).message}`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
424
546
|
console.log(`[CodexLens] Semantic dependencies installed successfully (${gpuMode} mode)`);
|
|
425
547
|
resolve({ success: true, message: `Installed with ${modeDescription}` });
|
|
426
548
|
} else {
|
|
@@ -490,6 +612,8 @@ async function bootstrapVenv(): Promise<BootstrapResult> {
|
|
|
490
612
|
execSync(`"${pipPath}" install codexlens`, { stdio: 'inherit' });
|
|
491
613
|
}
|
|
492
614
|
|
|
615
|
+
// Clear cache after successful installation
|
|
616
|
+
clearVenvStatusCache();
|
|
493
617
|
return { success: true };
|
|
494
618
|
} catch (err) {
|
|
495
619
|
return { success: false, error: `Failed to install codexlens: ${(err as Error).message}` };
|
|
@@ -1209,6 +1333,7 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
|
|
1209
1333
|
// Reset bootstrap cache
|
|
1210
1334
|
bootstrapChecked = false;
|
|
1211
1335
|
bootstrapReady = false;
|
|
1336
|
+
clearVenvStatusCache();
|
|
1212
1337
|
|
|
1213
1338
|
console.log('[CodexLens] CodexLens uninstalled successfully');
|
|
1214
1339
|
return { success: true, message: 'CodexLens uninstalled successfully' };
|
|
@@ -1273,7 +1398,19 @@ function isIndexingInProgress(): boolean {
|
|
|
1273
1398
|
export type { ProgressInfo, ExecuteOptions };
|
|
1274
1399
|
|
|
1275
1400
|
// Export for direct usage
|
|
1276
|
-
export {
|
|
1401
|
+
export {
|
|
1402
|
+
ensureReady,
|
|
1403
|
+
executeCodexLens,
|
|
1404
|
+
checkVenvStatus,
|
|
1405
|
+
bootstrapVenv,
|
|
1406
|
+
checkSemanticStatus,
|
|
1407
|
+
ensureLiteLLMEmbedderReady,
|
|
1408
|
+
installSemantic,
|
|
1409
|
+
detectGpuSupport,
|
|
1410
|
+
uninstallCodexLens,
|
|
1411
|
+
cancelIndexing,
|
|
1412
|
+
isIndexingInProgress,
|
|
1413
|
+
};
|
|
1277
1414
|
export type { GpuMode };
|
|
1278
1415
|
|
|
1279
1416
|
// Backward-compatible export for tests
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Cache Store - In-memory cache with TTL and LRU eviction
|
|
3
|
+
* Stores packed file contents with session-based lifecycle management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Cache entry metadata */
|
|
7
|
+
export interface CacheMetadata {
|
|
8
|
+
files: string[]; // Source file paths
|
|
9
|
+
patterns: string[]; // Original @patterns
|
|
10
|
+
total_bytes: number; // Total content bytes
|
|
11
|
+
file_count: number; // Number of files packed
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Cache entry structure */
|
|
15
|
+
export interface CacheEntry {
|
|
16
|
+
session_id: string;
|
|
17
|
+
created_at: number; // Timestamp ms
|
|
18
|
+
accessed_at: number; // Last access timestamp
|
|
19
|
+
ttl: number; // TTL in ms
|
|
20
|
+
content: string; // Packed file content
|
|
21
|
+
metadata: CacheMetadata;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Paginated read result */
|
|
25
|
+
export interface PagedReadResult {
|
|
26
|
+
content: string; // Current page content
|
|
27
|
+
offset: number; // Current byte offset
|
|
28
|
+
limit: number; // Requested bytes
|
|
29
|
+
total_bytes: number; // Total content bytes
|
|
30
|
+
has_more: boolean; // Has more content
|
|
31
|
+
next_offset: number | null; // Next page offset (null if no more)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Cache status info */
|
|
35
|
+
export interface CacheStatus {
|
|
36
|
+
entries: number; // Total cache entries
|
|
37
|
+
total_bytes: number; // Total bytes cached
|
|
38
|
+
oldest_session: string | null;
|
|
39
|
+
newest_session: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Session status info */
|
|
43
|
+
export interface SessionStatus {
|
|
44
|
+
session_id: string;
|
|
45
|
+
exists: boolean;
|
|
46
|
+
files?: string[];
|
|
47
|
+
file_count?: number;
|
|
48
|
+
total_bytes?: number;
|
|
49
|
+
created_at?: string;
|
|
50
|
+
expires_at?: string;
|
|
51
|
+
accessed_at?: string;
|
|
52
|
+
ttl_remaining_ms?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Default configuration */
|
|
56
|
+
const DEFAULT_MAX_ENTRIES = 100;
|
|
57
|
+
const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
58
|
+
const DEFAULT_PAGE_SIZE = 65536; // 64KB
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Context Cache Store singleton
|
|
62
|
+
* Manages in-memory cache with TTL expiration and LRU eviction
|
|
63
|
+
*/
|
|
64
|
+
class ContextCacheStore {
|
|
65
|
+
private cache: Map<string, CacheEntry> = new Map();
|
|
66
|
+
private maxEntries: number;
|
|
67
|
+
private defaultTTL: number;
|
|
68
|
+
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
69
|
+
|
|
70
|
+
constructor(options: {
|
|
71
|
+
maxEntries?: number;
|
|
72
|
+
defaultTTL?: number;
|
|
73
|
+
cleanupIntervalMs?: number;
|
|
74
|
+
} = {}) {
|
|
75
|
+
this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
76
|
+
this.defaultTTL = options.defaultTTL ?? DEFAULT_TTL_MS;
|
|
77
|
+
|
|
78
|
+
// Start periodic cleanup
|
|
79
|
+
const cleanupMs = options.cleanupIntervalMs ?? 60000; // 1 minute
|
|
80
|
+
this.cleanupInterval = setInterval(() => {
|
|
81
|
+
this.cleanupExpired();
|
|
82
|
+
}, cleanupMs);
|
|
83
|
+
|
|
84
|
+
// Allow cleanup to not keep process alive
|
|
85
|
+
if (this.cleanupInterval.unref) {
|
|
86
|
+
this.cleanupInterval.unref();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Store packed content in cache
|
|
92
|
+
*/
|
|
93
|
+
set(
|
|
94
|
+
sessionId: string,
|
|
95
|
+
content: string,
|
|
96
|
+
metadata: CacheMetadata,
|
|
97
|
+
ttl?: number
|
|
98
|
+
): CacheEntry {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const entryTTL = ttl ?? this.defaultTTL;
|
|
101
|
+
|
|
102
|
+
// Evict if at capacity
|
|
103
|
+
if (this.cache.size >= this.maxEntries && !this.cache.has(sessionId)) {
|
|
104
|
+
this.evictOldest();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const entry: CacheEntry = {
|
|
108
|
+
session_id: sessionId,
|
|
109
|
+
created_at: now,
|
|
110
|
+
accessed_at: now,
|
|
111
|
+
ttl: entryTTL,
|
|
112
|
+
content,
|
|
113
|
+
metadata,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
this.cache.set(sessionId, entry);
|
|
117
|
+
return entry;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get cache entry by session ID
|
|
122
|
+
*/
|
|
123
|
+
get(sessionId: string): CacheEntry | null {
|
|
124
|
+
const entry = this.cache.get(sessionId);
|
|
125
|
+
|
|
126
|
+
if (!entry) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check TTL expiration
|
|
131
|
+
if (this.isExpired(entry)) {
|
|
132
|
+
this.cache.delete(sessionId);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Update access time (LRU)
|
|
137
|
+
entry.accessed_at = Date.now();
|
|
138
|
+
return entry;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Read content with pagination
|
|
143
|
+
*/
|
|
144
|
+
read(
|
|
145
|
+
sessionId: string,
|
|
146
|
+
offset: number = 0,
|
|
147
|
+
limit: number = DEFAULT_PAGE_SIZE
|
|
148
|
+
): PagedReadResult | null {
|
|
149
|
+
const entry = this.get(sessionId);
|
|
150
|
+
|
|
151
|
+
if (!entry) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const content = entry.content;
|
|
156
|
+
const totalBytes = Buffer.byteLength(content, 'utf-8');
|
|
157
|
+
|
|
158
|
+
// Handle byte-based offset for UTF-8
|
|
159
|
+
// For simplicity, we use character-based slicing
|
|
160
|
+
// This is approximate but works for most use cases
|
|
161
|
+
const charOffset = Math.min(offset, content.length);
|
|
162
|
+
const charLimit = Math.min(limit, content.length - charOffset);
|
|
163
|
+
|
|
164
|
+
const pageContent = content.slice(charOffset, charOffset + charLimit);
|
|
165
|
+
const endOffset = charOffset + pageContent.length;
|
|
166
|
+
const hasMore = endOffset < content.length;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
content: pageContent,
|
|
170
|
+
offset: charOffset,
|
|
171
|
+
limit: charLimit,
|
|
172
|
+
total_bytes: totalBytes,
|
|
173
|
+
has_more: hasMore,
|
|
174
|
+
next_offset: hasMore ? endOffset : null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Release (delete) cache entry
|
|
180
|
+
*/
|
|
181
|
+
release(sessionId: string): { released: boolean; freed_bytes: number } {
|
|
182
|
+
const entry = this.cache.get(sessionId);
|
|
183
|
+
|
|
184
|
+
if (!entry) {
|
|
185
|
+
return { released: false, freed_bytes: 0 };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const freedBytes = entry.metadata.total_bytes;
|
|
189
|
+
this.cache.delete(sessionId);
|
|
190
|
+
|
|
191
|
+
return { released: true, freed_bytes: freedBytes };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get session status
|
|
196
|
+
*/
|
|
197
|
+
getSessionStatus(sessionId: string): SessionStatus {
|
|
198
|
+
const entry = this.cache.get(sessionId);
|
|
199
|
+
|
|
200
|
+
if (!entry) {
|
|
201
|
+
return { session_id: sessionId, exists: false };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check if expired
|
|
205
|
+
if (this.isExpired(entry)) {
|
|
206
|
+
this.cache.delete(sessionId);
|
|
207
|
+
return { session_id: sessionId, exists: false };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
const expiresAt = entry.created_at + entry.ttl;
|
|
212
|
+
const ttlRemaining = Math.max(0, expiresAt - now);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
session_id: sessionId,
|
|
216
|
+
exists: true,
|
|
217
|
+
files: entry.metadata.files,
|
|
218
|
+
file_count: entry.metadata.file_count,
|
|
219
|
+
total_bytes: entry.metadata.total_bytes,
|
|
220
|
+
created_at: new Date(entry.created_at).toISOString(),
|
|
221
|
+
expires_at: new Date(expiresAt).toISOString(),
|
|
222
|
+
accessed_at: new Date(entry.accessed_at).toISOString(),
|
|
223
|
+
ttl_remaining_ms: ttlRemaining,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get overall cache status
|
|
229
|
+
*/
|
|
230
|
+
getStatus(): CacheStatus {
|
|
231
|
+
let totalBytes = 0;
|
|
232
|
+
let oldest: CacheEntry | null = null;
|
|
233
|
+
let newest: CacheEntry | null = null;
|
|
234
|
+
|
|
235
|
+
for (const entry of this.cache.values()) {
|
|
236
|
+
// Skip expired entries
|
|
237
|
+
if (this.isExpired(entry)) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
totalBytes += entry.metadata.total_bytes;
|
|
242
|
+
|
|
243
|
+
if (!oldest || entry.created_at < oldest.created_at) {
|
|
244
|
+
oldest = entry;
|
|
245
|
+
}
|
|
246
|
+
if (!newest || entry.created_at > newest.created_at) {
|
|
247
|
+
newest = entry;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
entries: this.cache.size,
|
|
253
|
+
total_bytes: totalBytes,
|
|
254
|
+
oldest_session: oldest?.session_id ?? null,
|
|
255
|
+
newest_session: newest?.session_id ?? null,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Cleanup expired entries
|
|
261
|
+
*/
|
|
262
|
+
cleanupExpired(): { removed: number } {
|
|
263
|
+
let removed = 0;
|
|
264
|
+
const now = Date.now();
|
|
265
|
+
|
|
266
|
+
for (const [sessionId, entry] of this.cache.entries()) {
|
|
267
|
+
if (this.isExpired(entry, now)) {
|
|
268
|
+
this.cache.delete(sessionId);
|
|
269
|
+
removed++;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { removed };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Clear all cache entries
|
|
278
|
+
*/
|
|
279
|
+
clear(): { removed: number } {
|
|
280
|
+
const count = this.cache.size;
|
|
281
|
+
this.cache.clear();
|
|
282
|
+
return { removed: count };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Check if entry is expired
|
|
287
|
+
*/
|
|
288
|
+
private isExpired(entry: CacheEntry, now?: number): boolean {
|
|
289
|
+
const currentTime = now ?? Date.now();
|
|
290
|
+
return currentTime > entry.created_at + entry.ttl;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Evict oldest entry (LRU)
|
|
295
|
+
*/
|
|
296
|
+
private evictOldest(): void {
|
|
297
|
+
let oldest: [string, CacheEntry] | null = null;
|
|
298
|
+
|
|
299
|
+
for (const [sessionId, entry] of this.cache.entries()) {
|
|
300
|
+
if (!oldest || entry.accessed_at < oldest[1].accessed_at) {
|
|
301
|
+
oldest = [sessionId, entry];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (oldest) {
|
|
306
|
+
this.cache.delete(oldest[0]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Stop cleanup timer (for graceful shutdown)
|
|
312
|
+
*/
|
|
313
|
+
destroy(): void {
|
|
314
|
+
if (this.cleanupInterval) {
|
|
315
|
+
clearInterval(this.cleanupInterval);
|
|
316
|
+
this.cleanupInterval = null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* List all session IDs
|
|
322
|
+
*/
|
|
323
|
+
listSessions(): string[] {
|
|
324
|
+
return Array.from(this.cache.keys());
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Check if session exists and is valid
|
|
329
|
+
*/
|
|
330
|
+
has(sessionId: string): boolean {
|
|
331
|
+
const entry = this.cache.get(sessionId);
|
|
332
|
+
if (!entry) return false;
|
|
333
|
+
if (this.isExpired(entry)) {
|
|
334
|
+
this.cache.delete(sessionId);
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Singleton instance
|
|
342
|
+
let cacheInstance: ContextCacheStore | null = null;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get the singleton cache instance
|
|
346
|
+
*/
|
|
347
|
+
export function getContextCacheStore(options?: {
|
|
348
|
+
maxEntries?: number;
|
|
349
|
+
defaultTTL?: number;
|
|
350
|
+
cleanupIntervalMs?: number;
|
|
351
|
+
}): ContextCacheStore {
|
|
352
|
+
if (!cacheInstance) {
|
|
353
|
+
cacheInstance = new ContextCacheStore(options);
|
|
354
|
+
}
|
|
355
|
+
return cacheInstance;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Reset the cache instance (for testing)
|
|
360
|
+
*/
|
|
361
|
+
export function resetContextCacheStore(): void {
|
|
362
|
+
if (cacheInstance) {
|
|
363
|
+
cacheInstance.destroy();
|
|
364
|
+
cacheInstance = null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export { ContextCacheStore };
|