claude-code-workflow 6.2.6 → 6.2.9

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.
Files changed (25) hide show
  1. package/ccw/dist/cli.d.ts.map +1 -1
  2. package/ccw/dist/cli.js +18 -7
  3. package/ccw/dist/cli.js.map +1 -1
  4. package/ccw/dist/core/routes/system-routes.js +1 -1
  5. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  6. package/ccw/src/cli.ts +256 -244
  7. package/ccw/src/core/routes/system-routes.ts +1 -1
  8. package/ccw/src/templates/dashboard-js/components/cli-status.js +35 -2
  9. package/ccw/src/templates/dashboard-js/views/cli-manager.js +45 -6
  10. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  11. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  12. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  13. package/codex-lens/src/codexlens/cli/commands.py +41 -15
  14. package/codex-lens/src/codexlens/cli/embedding_manager.py +15 -0
  15. package/codex-lens/src/codexlens/cli/model_manager.py +337 -311
  16. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  17. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  18. package/codex-lens/src/codexlens/search/chain_search.py +14 -1
  19. package/codex-lens/src/codexlens/search/hybrid_search.py +25 -12
  20. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  21. package/codex-lens/src/codexlens/semantic/vector_store.py +97 -0
  22. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  23. package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
  24. package/package.json +13 -5
  25. package/ccw/package.json +0 -65
@@ -1,311 +1,337 @@
1
- """Model Manager - Manage fastembed models for semantic search."""
2
-
3
- import json
4
- import os
5
- import shutil
6
- from pathlib import Path
7
- from typing import Dict, List, Optional
8
-
9
- try:
10
- from fastembed import TextEmbedding
11
- FASTEMBED_AVAILABLE = True
12
- except ImportError:
13
- FASTEMBED_AVAILABLE = False
14
-
15
-
16
- # Model profiles with metadata
17
- # Note: 768d is max recommended dimension for optimal performance/quality balance
18
- # 1024d models are available but not recommended due to higher resource usage
19
- MODEL_PROFILES = {
20
- "fast": {
21
- "model_name": "BAAI/bge-small-en-v1.5",
22
- "dimensions": 384,
23
- "size_mb": 80,
24
- "description": "Fast, lightweight, English-optimized",
25
- "use_case": "Quick prototyping, resource-constrained environments",
26
- "recommended": True,
27
- },
28
- "base": {
29
- "model_name": "BAAI/bge-base-en-v1.5",
30
- "dimensions": 768,
31
- "size_mb": 220,
32
- "description": "General purpose, good balance of speed and quality",
33
- "use_case": "General text search, documentation",
34
- "recommended": True,
35
- },
36
- "code": {
37
- "model_name": "jinaai/jina-embeddings-v2-base-code",
38
- "dimensions": 768,
39
- "size_mb": 150,
40
- "description": "Code-optimized, best for programming languages",
41
- "use_case": "Open source projects, code semantic search",
42
- "recommended": True,
43
- },
44
- "minilm": {
45
- "model_name": "sentence-transformers/all-MiniLM-L6-v2",
46
- "dimensions": 384,
47
- "size_mb": 90,
48
- "description": "Popular lightweight model, good quality",
49
- "use_case": "General purpose, low resource environments",
50
- "recommended": True,
51
- },
52
- "multilingual": {
53
- "model_name": "intfloat/multilingual-e5-large",
54
- "dimensions": 1024,
55
- "size_mb": 1000,
56
- "description": "Multilingual + code support (high resource usage)",
57
- "use_case": "Enterprise multilingual projects",
58
- "recommended": False, # 1024d not recommended
59
- },
60
- "balanced": {
61
- "model_name": "mixedbread-ai/mxbai-embed-large-v1",
62
- "dimensions": 1024,
63
- "size_mb": 600,
64
- "description": "High accuracy, general purpose (high resource usage)",
65
- "use_case": "High-quality semantic search, balanced performance",
66
- "recommended": False, # 1024d not recommended
67
- },
68
- }
69
-
70
-
71
- def get_cache_dir() -> Path:
72
- """Get fastembed cache directory.
73
-
74
- Returns:
75
- Path to cache directory (usually ~/.cache/fastembed or %LOCALAPPDATA%\\Temp\\fastembed_cache)
76
- """
77
- # Check HF_HOME environment variable first
78
- if "HF_HOME" in os.environ:
79
- return Path(os.environ["HF_HOME"])
80
-
81
- # Default cache locations
82
- if os.name == "nt": # Windows
83
- cache_dir = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local")) / "Temp" / "fastembed_cache"
84
- else: # Unix-like
85
- cache_dir = Path.home() / ".cache" / "fastembed"
86
-
87
- return cache_dir
88
-
89
-
90
- def list_models() -> Dict[str, any]:
91
- """List available model profiles and their installation status.
92
-
93
- Returns:
94
- Dictionary with model profiles, installed status, and cache info
95
- """
96
- if not FASTEMBED_AVAILABLE:
97
- return {
98
- "success": False,
99
- "error": "fastembed not installed. Install with: pip install codexlens[semantic]",
100
- }
101
-
102
- cache_dir = get_cache_dir()
103
- cache_exists = cache_dir.exists()
104
-
105
- models = []
106
- for profile, info in MODEL_PROFILES.items():
107
- model_name = info["model_name"]
108
-
109
- # Check if model is cached
110
- installed = False
111
- cache_size_mb = 0
112
-
113
- if cache_exists:
114
- # Check for model directory in cache
115
- model_cache_path = cache_dir / f"models--{model_name.replace('/', '--')}"
116
- if model_cache_path.exists():
117
- installed = True
118
- # Calculate cache size
119
- total_size = sum(
120
- f.stat().st_size
121
- for f in model_cache_path.rglob("*")
122
- if f.is_file()
123
- )
124
- cache_size_mb = round(total_size / (1024 * 1024), 1)
125
-
126
- models.append({
127
- "profile": profile,
128
- "model_name": model_name,
129
- "dimensions": info["dimensions"],
130
- "estimated_size_mb": info["size_mb"],
131
- "actual_size_mb": cache_size_mb if installed else None,
132
- "description": info["description"],
133
- "use_case": info["use_case"],
134
- "installed": installed,
135
- })
136
-
137
- return {
138
- "success": True,
139
- "result": {
140
- "models": models,
141
- "cache_dir": str(cache_dir),
142
- "cache_exists": cache_exists,
143
- },
144
- }
145
-
146
-
147
- def download_model(profile: str, progress_callback: Optional[callable] = None) -> Dict[str, any]:
148
- """Download a model by profile name.
149
-
150
- Args:
151
- profile: Model profile name (fast, code, multilingual, balanced)
152
- progress_callback: Optional callback function to report progress
153
-
154
- Returns:
155
- Result dictionary with success status
156
- """
157
- if not FASTEMBED_AVAILABLE:
158
- return {
159
- "success": False,
160
- "error": "fastembed not installed. Install with: pip install codexlens[semantic]",
161
- }
162
-
163
- if profile not in MODEL_PROFILES:
164
- return {
165
- "success": False,
166
- "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
167
- }
168
-
169
- model_name = MODEL_PROFILES[profile]["model_name"]
170
-
171
- try:
172
- # Download model by instantiating TextEmbedding
173
- # This will automatically download to cache if not present
174
- if progress_callback:
175
- progress_callback(f"Downloading {model_name}...")
176
-
177
- embedder = TextEmbedding(model_name=model_name)
178
-
179
- if progress_callback:
180
- progress_callback(f"Model {model_name} downloaded successfully")
181
-
182
- # Get cache info
183
- cache_dir = get_cache_dir()
184
- model_cache_path = cache_dir / f"models--{model_name.replace('/', '--')}"
185
-
186
- cache_size = 0
187
- if model_cache_path.exists():
188
- total_size = sum(
189
- f.stat().st_size
190
- for f in model_cache_path.rglob("*")
191
- if f.is_file()
192
- )
193
- cache_size = round(total_size / (1024 * 1024), 1)
194
-
195
- return {
196
- "success": True,
197
- "result": {
198
- "profile": profile,
199
- "model_name": model_name,
200
- "cache_size_mb": cache_size,
201
- "cache_path": str(model_cache_path),
202
- },
203
- }
204
-
205
- except Exception as e:
206
- return {
207
- "success": False,
208
- "error": f"Failed to download model: {str(e)}",
209
- }
210
-
211
-
212
- def delete_model(profile: str) -> Dict[str, any]:
213
- """Delete a downloaded model from cache.
214
-
215
- Args:
216
- profile: Model profile name to delete
217
-
218
- Returns:
219
- Result dictionary with success status
220
- """
221
- if profile not in MODEL_PROFILES:
222
- return {
223
- "success": False,
224
- "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
225
- }
226
-
227
- model_name = MODEL_PROFILES[profile]["model_name"]
228
- cache_dir = get_cache_dir()
229
- model_cache_path = cache_dir / f"models--{model_name.replace('/', '--')}"
230
-
231
- if not model_cache_path.exists():
232
- return {
233
- "success": False,
234
- "error": f"Model {profile} ({model_name}) is not installed",
235
- }
236
-
237
- try:
238
- # Calculate size before deletion
239
- total_size = sum(
240
- f.stat().st_size
241
- for f in model_cache_path.rglob("*")
242
- if f.is_file()
243
- )
244
- size_mb = round(total_size / (1024 * 1024), 1)
245
-
246
- # Delete model directory
247
- shutil.rmtree(model_cache_path)
248
-
249
- return {
250
- "success": True,
251
- "result": {
252
- "profile": profile,
253
- "model_name": model_name,
254
- "deleted_size_mb": size_mb,
255
- "cache_path": str(model_cache_path),
256
- },
257
- }
258
-
259
- except Exception as e:
260
- return {
261
- "success": False,
262
- "error": f"Failed to delete model: {str(e)}",
263
- }
264
-
265
-
266
- def get_model_info(profile: str) -> Dict[str, any]:
267
- """Get detailed information about a model profile.
268
-
269
- Args:
270
- profile: Model profile name
271
-
272
- Returns:
273
- Result dictionary with model information
274
- """
275
- if profile not in MODEL_PROFILES:
276
- return {
277
- "success": False,
278
- "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
279
- }
280
-
281
- info = MODEL_PROFILES[profile]
282
- model_name = info["model_name"]
283
-
284
- # Check installation status
285
- cache_dir = get_cache_dir()
286
- model_cache_path = cache_dir / f"models--{model_name.replace('/', '--')}"
287
- installed = model_cache_path.exists()
288
-
289
- cache_size_mb = None
290
- if installed:
291
- total_size = sum(
292
- f.stat().st_size
293
- for f in model_cache_path.rglob("*")
294
- if f.is_file()
295
- )
296
- cache_size_mb = round(total_size / (1024 * 1024), 1)
297
-
298
- return {
299
- "success": True,
300
- "result": {
301
- "profile": profile,
302
- "model_name": model_name,
303
- "dimensions": info["dimensions"],
304
- "estimated_size_mb": info["size_mb"],
305
- "actual_size_mb": cache_size_mb,
306
- "description": info["description"],
307
- "use_case": info["use_case"],
308
- "installed": installed,
309
- "cache_path": str(model_cache_path) if installed else None,
310
- },
311
- }
1
+ """Model Manager - Manage fastembed models for semantic search."""
2
+
3
+ import json
4
+ import os
5
+ import shutil
6
+ from pathlib import Path
7
+ from typing import Dict, List, Optional
8
+
9
+ try:
10
+ from fastembed import TextEmbedding
11
+ FASTEMBED_AVAILABLE = True
12
+ except ImportError:
13
+ FASTEMBED_AVAILABLE = False
14
+
15
+
16
+ # Model profiles with metadata
17
+ # Note: 768d is max recommended dimension for optimal performance/quality balance
18
+ # 1024d models are available but not recommended due to higher resource usage
19
+ # cache_name: The actual Hugging Face repo name used by fastembed for ONNX caching
20
+ MODEL_PROFILES = {
21
+ "fast": {
22
+ "model_name": "BAAI/bge-small-en-v1.5",
23
+ "cache_name": "qdrant/bge-small-en-v1.5-onnx-q", # fastembed uses ONNX version
24
+ "dimensions": 384,
25
+ "size_mb": 80,
26
+ "description": "Fast, lightweight, English-optimized",
27
+ "use_case": "Quick prototyping, resource-constrained environments",
28
+ "recommended": True,
29
+ },
30
+ "base": {
31
+ "model_name": "BAAI/bge-base-en-v1.5",
32
+ "cache_name": "qdrant/bge-base-en-v1.5-onnx-q", # fastembed uses ONNX version
33
+ "dimensions": 768,
34
+ "size_mb": 220,
35
+ "description": "General purpose, good balance of speed and quality",
36
+ "use_case": "General text search, documentation",
37
+ "recommended": True,
38
+ },
39
+ "code": {
40
+ "model_name": "jinaai/jina-embeddings-v2-base-code",
41
+ "cache_name": "jinaai/jina-embeddings-v2-base-code", # Uses original name
42
+ "dimensions": 768,
43
+ "size_mb": 150,
44
+ "description": "Code-optimized, best for programming languages",
45
+ "use_case": "Open source projects, code semantic search",
46
+ "recommended": True,
47
+ },
48
+ "minilm": {
49
+ "model_name": "sentence-transformers/all-MiniLM-L6-v2",
50
+ "cache_name": "qdrant/all-MiniLM-L6-v2-onnx", # fastembed uses ONNX version
51
+ "dimensions": 384,
52
+ "size_mb": 90,
53
+ "description": "Popular lightweight model, good quality",
54
+ "use_case": "General purpose, low resource environments",
55
+ "recommended": True,
56
+ },
57
+ "multilingual": {
58
+ "model_name": "intfloat/multilingual-e5-large",
59
+ "cache_name": "qdrant/multilingual-e5-large-onnx", # fastembed uses ONNX version
60
+ "dimensions": 1024,
61
+ "size_mb": 1000,
62
+ "description": "Multilingual + code support (high resource usage)",
63
+ "use_case": "Enterprise multilingual projects",
64
+ "recommended": False, # 1024d not recommended
65
+ },
66
+ "balanced": {
67
+ "model_name": "mixedbread-ai/mxbai-embed-large-v1",
68
+ "cache_name": "mixedbread-ai/mxbai-embed-large-v1", # Uses original name
69
+ "dimensions": 1024,
70
+ "size_mb": 600,
71
+ "description": "High accuracy, general purpose (high resource usage)",
72
+ "use_case": "High-quality semantic search, balanced performance",
73
+ "recommended": False, # 1024d not recommended
74
+ },
75
+ }
76
+
77
+
78
+ def get_cache_dir() -> Path:
79
+ """Get fastembed cache directory.
80
+
81
+ Returns:
82
+ Path to cache directory (usually ~/.cache/fastembed or %LOCALAPPDATA%\\Temp\\fastembed_cache)
83
+ """
84
+ # Check HF_HOME environment variable first
85
+ if "HF_HOME" in os.environ:
86
+ return Path(os.environ["HF_HOME"])
87
+
88
+ # Default cache locations
89
+ if os.name == "nt": # Windows
90
+ cache_dir = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local")) / "Temp" / "fastembed_cache"
91
+ else: # Unix-like
92
+ cache_dir = Path.home() / ".cache" / "fastembed"
93
+
94
+ return cache_dir
95
+
96
+
97
+ def _get_model_cache_path(cache_dir: Path, info: Dict) -> Path:
98
+ """Get the actual cache path for a model.
99
+
100
+ fastembed uses ONNX versions of models with different names than the original.
101
+ This function returns the correct path based on the cache_name field.
102
+
103
+ Args:
104
+ cache_dir: The fastembed cache directory
105
+ info: Model profile info dictionary
106
+
107
+ Returns:
108
+ Path to the model cache directory
109
+ """
110
+ cache_name = info.get("cache_name", info["model_name"])
111
+ return cache_dir / f"models--{cache_name.replace('/', '--')}"
112
+
113
+
114
+ def list_models() -> Dict[str, any]:
115
+ """List available model profiles and their installation status.
116
+
117
+ Returns:
118
+ Dictionary with model profiles, installed status, and cache info
119
+ """
120
+ if not FASTEMBED_AVAILABLE:
121
+ return {
122
+ "success": False,
123
+ "error": "fastembed not installed. Install with: pip install codexlens[semantic]",
124
+ }
125
+
126
+ cache_dir = get_cache_dir()
127
+ cache_exists = cache_dir.exists()
128
+
129
+ models = []
130
+ for profile, info in MODEL_PROFILES.items():
131
+ model_name = info["model_name"]
132
+
133
+ # Check if model is cached using the actual cache name
134
+ installed = False
135
+ cache_size_mb = 0
136
+
137
+ if cache_exists:
138
+ # Check for model directory in cache using correct cache_name
139
+ model_cache_path = _get_model_cache_path(cache_dir, info)
140
+ if model_cache_path.exists():
141
+ installed = True
142
+ # Calculate cache size
143
+ total_size = sum(
144
+ f.stat().st_size
145
+ for f in model_cache_path.rglob("*")
146
+ if f.is_file()
147
+ )
148
+ cache_size_mb = round(total_size / (1024 * 1024), 1)
149
+
150
+ models.append({
151
+ "profile": profile,
152
+ "model_name": model_name,
153
+ "dimensions": info["dimensions"],
154
+ "estimated_size_mb": info["size_mb"],
155
+ "actual_size_mb": cache_size_mb if installed else None,
156
+ "description": info["description"],
157
+ "use_case": info["use_case"],
158
+ "installed": installed,
159
+ })
160
+
161
+ return {
162
+ "success": True,
163
+ "result": {
164
+ "models": models,
165
+ "cache_dir": str(cache_dir),
166
+ "cache_exists": cache_exists,
167
+ },
168
+ }
169
+
170
+
171
+ def download_model(profile: str, progress_callback: Optional[callable] = None) -> Dict[str, any]:
172
+ """Download a model by profile name.
173
+
174
+ Args:
175
+ profile: Model profile name (fast, code, multilingual, balanced)
176
+ progress_callback: Optional callback function to report progress
177
+
178
+ Returns:
179
+ Result dictionary with success status
180
+ """
181
+ if not FASTEMBED_AVAILABLE:
182
+ return {
183
+ "success": False,
184
+ "error": "fastembed not installed. Install with: pip install codexlens[semantic]",
185
+ }
186
+
187
+ if profile not in MODEL_PROFILES:
188
+ return {
189
+ "success": False,
190
+ "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
191
+ }
192
+
193
+ info = MODEL_PROFILES[profile]
194
+ model_name = info["model_name"]
195
+
196
+ try:
197
+ # Download model by instantiating TextEmbedding
198
+ # This will automatically download to cache if not present
199
+ if progress_callback:
200
+ progress_callback(f"Downloading {model_name}...")
201
+
202
+ embedder = TextEmbedding(model_name=model_name)
203
+
204
+ if progress_callback:
205
+ progress_callback(f"Model {model_name} downloaded successfully")
206
+
207
+ # Get cache info using correct cache_name
208
+ cache_dir = get_cache_dir()
209
+ model_cache_path = _get_model_cache_path(cache_dir, info)
210
+
211
+ cache_size = 0
212
+ if model_cache_path.exists():
213
+ total_size = sum(
214
+ f.stat().st_size
215
+ for f in model_cache_path.rglob("*")
216
+ if f.is_file()
217
+ )
218
+ cache_size = round(total_size / (1024 * 1024), 1)
219
+
220
+ return {
221
+ "success": True,
222
+ "result": {
223
+ "profile": profile,
224
+ "model_name": model_name,
225
+ "cache_size_mb": cache_size,
226
+ "cache_path": str(model_cache_path),
227
+ },
228
+ }
229
+
230
+ except Exception as e:
231
+ return {
232
+ "success": False,
233
+ "error": f"Failed to download model: {str(e)}",
234
+ }
235
+
236
+
237
+ def delete_model(profile: str) -> Dict[str, any]:
238
+ """Delete a downloaded model from cache.
239
+
240
+ Args:
241
+ profile: Model profile name to delete
242
+
243
+ Returns:
244
+ Result dictionary with success status
245
+ """
246
+ if profile not in MODEL_PROFILES:
247
+ return {
248
+ "success": False,
249
+ "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
250
+ }
251
+
252
+ info = MODEL_PROFILES[profile]
253
+ model_name = info["model_name"]
254
+ cache_dir = get_cache_dir()
255
+ model_cache_path = _get_model_cache_path(cache_dir, info)
256
+
257
+ if not model_cache_path.exists():
258
+ return {
259
+ "success": False,
260
+ "error": f"Model {profile} ({model_name}) is not installed",
261
+ }
262
+
263
+ try:
264
+ # Calculate size before deletion
265
+ total_size = sum(
266
+ f.stat().st_size
267
+ for f in model_cache_path.rglob("*")
268
+ if f.is_file()
269
+ )
270
+ size_mb = round(total_size / (1024 * 1024), 1)
271
+
272
+ # Delete model directory
273
+ shutil.rmtree(model_cache_path)
274
+
275
+ return {
276
+ "success": True,
277
+ "result": {
278
+ "profile": profile,
279
+ "model_name": model_name,
280
+ "deleted_size_mb": size_mb,
281
+ "cache_path": str(model_cache_path),
282
+ },
283
+ }
284
+
285
+ except Exception as e:
286
+ return {
287
+ "success": False,
288
+ "error": f"Failed to delete model: {str(e)}",
289
+ }
290
+
291
+
292
+ def get_model_info(profile: str) -> Dict[str, any]:
293
+ """Get detailed information about a model profile.
294
+
295
+ Args:
296
+ profile: Model profile name
297
+
298
+ Returns:
299
+ Result dictionary with model information
300
+ """
301
+ if profile not in MODEL_PROFILES:
302
+ return {
303
+ "success": False,
304
+ "error": f"Unknown profile: {profile}. Available: {', '.join(MODEL_PROFILES.keys())}",
305
+ }
306
+
307
+ info = MODEL_PROFILES[profile]
308
+ model_name = info["model_name"]
309
+
310
+ # Check installation status using correct cache_name
311
+ cache_dir = get_cache_dir()
312
+ model_cache_path = _get_model_cache_path(cache_dir, info)
313
+ installed = model_cache_path.exists()
314
+
315
+ cache_size_mb = None
316
+ if installed:
317
+ total_size = sum(
318
+ f.stat().st_size
319
+ for f in model_cache_path.rglob("*")
320
+ if f.is_file()
321
+ )
322
+ cache_size_mb = round(total_size / (1024 * 1024), 1)
323
+
324
+ return {
325
+ "success": True,
326
+ "result": {
327
+ "profile": profile,
328
+ "model_name": model_name,
329
+ "dimensions": info["dimensions"],
330
+ "estimated_size_mb": info["size_mb"],
331
+ "actual_size_mb": cache_size_mb,
332
+ "description": info["description"],
333
+ "use_case": info["use_case"],
334
+ "installed": installed,
335
+ "cache_path": str(model_cache_path) if installed else None,
336
+ },
337
+ }