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
@@ -349,6 +349,50 @@ function getSelectedModel() {
349
349
  return select ? select.value : 'code';
350
350
  }
351
351
 
352
+ /**
353
+ * Build model select options HTML, showing only installed models
354
+ * @returns {string} HTML string for select options
355
+ */
356
+ function buildModelSelectOptions() {
357
+ var installedModels = window.cliToolsStatus?.codexlens?.installedModels || [];
358
+ var allModels = window.cliToolsStatus?.codexlens?.allModels || [];
359
+
360
+ // Model display configuration
361
+ var modelConfig = {
362
+ 'code': { label: t('index.modelCode') || 'Code (768d)', star: true },
363
+ 'base': { label: t('index.modelBase') || 'Base (768d)', star: false },
364
+ 'fast': { label: t('index.modelFast') || 'Fast (384d)', star: false },
365
+ 'minilm': { label: t('index.modelMinilm') || 'MiniLM (384d)', star: false },
366
+ 'multilingual': { label: t('index.modelMultilingual') || 'Multilingual (1024d)', warn: true },
367
+ 'balanced': { label: t('index.modelBalanced') || 'Balanced (1024d)', warn: true }
368
+ };
369
+
370
+ // If no models installed, show placeholder
371
+ if (installedModels.length === 0) {
372
+ return '<option value="" disabled selected>' + (t('index.noModelsInstalled') || 'No models installed') + '</option>';
373
+ }
374
+
375
+ // Build options for installed models only
376
+ var options = '';
377
+ var firstInstalled = null;
378
+
379
+ // Preferred order: code, fast, minilm, base, multilingual, balanced
380
+ var preferredOrder = ['code', 'fast', 'minilm', 'base', 'multilingual', 'balanced'];
381
+
382
+ preferredOrder.forEach(function(profile) {
383
+ if (installedModels.includes(profile) && modelConfig[profile]) {
384
+ var config = modelConfig[profile];
385
+ var style = config.warn ? ' style="color: var(--muted-foreground)"' : '';
386
+ var suffix = config.star ? ' ⭐' : (config.warn ? ' ⚠️' : '');
387
+ var selected = !firstInstalled ? ' selected' : '';
388
+ if (!firstInstalled) firstInstalled = profile;
389
+ options += '<option value="' + profile + '"' + style + selected + '>' + config.label + suffix + '</option>';
390
+ }
391
+ });
392
+
393
+ return options;
394
+ }
395
+
352
396
  // ========== Tools Section (Left Column) ==========
353
397
  function renderToolsSection() {
354
398
  var container = document.getElementById('tools-section');
@@ -404,12 +448,7 @@ function renderToolsSection() {
404
448
  (codexLensStatus.ready
405
449
  ? '<span class="tool-status-text success"><i data-lucide="check-circle" class="w-3.5 h-3.5"></i> v' + (codexLensStatus.version || 'installed') + '</span>' +
406
450
  '<select id="codexlensModelSelect" class="btn-sm bg-muted border border-border rounded text-xs" onclick="event.stopPropagation()" title="' + (t('index.selectModel') || 'Select embedding model') + '">' +
407
- '<option value="code">' + (t('index.modelCode') || 'Code (768d)') + ' ⭐</option>' +
408
- '<option value="base">' + (t('index.modelBase') || 'Base (768d)') + '</option>' +
409
- '<option value="fast">' + (t('index.modelFast') || 'Fast (384d)') + '</option>' +
410
- '<option value="minilm">' + (t('index.modelMinilm') || 'MiniLM (384d)') + '</option>' +
411
- '<option value="multilingual" style="color: var(--muted-foreground)">' + (t('index.modelMultilingual') || 'Multilingual (1024d)') + ' ⚠️</option>' +
412
- '<option value="balanced" style="color: var(--muted-foreground)">' + (t('index.modelBalanced') || 'Balanced (1024d)') + ' ⚠️</option>' +
451
+ buildModelSelectOptions() +
413
452
  '</select>' +
414
453
  '<button class="btn-sm btn-primary" onclick="event.stopPropagation(); initCodexLensIndex(\'full\', getSelectedModel())" title="' + (t('index.fullDesc') || 'FTS + Semantic search (recommended)') + '"><i data-lucide="layers" class="w-3 h-3"></i> ' + (t('index.fullIndex') || '全部索引') + '</button>' +
415
454
  '<button class="btn-sm btn-outline" onclick="event.stopPropagation(); initCodexLensIndex(\'vector\', getSelectedModel())" title="' + (t('index.vectorDesc') || 'Semantic search with embeddings') + '"><i data-lucide="sparkles" class="w-3 h-3"></i> ' + (t('index.vectorIndex') || '向量索引') + '</button>' +
@@ -35,8 +35,17 @@ from .output import (
35
35
  app = typer.Typer(help="CodexLens CLI — local code indexing and search.")
36
36
 
37
37
 
38
- def _configure_logging(verbose: bool) -> None:
39
- level = logging.DEBUG if verbose else logging.INFO
38
+ def _configure_logging(verbose: bool, json_mode: bool = False) -> None:
39
+ """Configure logging level.
40
+
41
+ In JSON mode, suppress INFO logs to keep stderr clean for error parsing.
42
+ Only WARNING and above are shown to avoid mixing logs with JSON output.
43
+ """
44
+ if json_mode and not verbose:
45
+ # In JSON mode, suppress INFO logs to keep stderr clean
46
+ level = logging.WARNING
47
+ else:
48
+ level = logging.DEBUG if verbose else logging.INFO
40
49
  logging.basicConfig(level=level, format="%(levelname)s %(message)s")
41
50
 
42
51
 
@@ -53,10 +62,27 @@ def _parse_languages(raw: Optional[List[str]]) -> Optional[List[str]]:
53
62
 
54
63
 
55
64
  def _get_index_root() -> Path:
56
- """Get the index root directory from config or default."""
65
+ """Get the index root directory from config or default.
66
+
67
+ Priority order:
68
+ 1. CODEXLENS_INDEX_DIR environment variable
69
+ 2. index_dir from ~/.codexlens/config.json
70
+ 3. Default: ~/.codexlens/indexes
71
+ """
57
72
  env_override = os.getenv("CODEXLENS_INDEX_DIR")
58
73
  if env_override:
59
74
  return Path(env_override).expanduser().resolve()
75
+
76
+ # Read from config.json
77
+ config_file = Path.home() / ".codexlens" / "config.json"
78
+ if config_file.exists():
79
+ try:
80
+ cfg = json.loads(config_file.read_text(encoding="utf-8"))
81
+ if "index_dir" in cfg:
82
+ return Path(cfg["index_dir"]).expanduser().resolve()
83
+ except (json.JSONDecodeError, OSError):
84
+ pass # Fall through to default
85
+
60
86
  return Path.home() / ".codexlens" / "indexes"
61
87
 
62
88
 
@@ -95,7 +121,7 @@ def init(
95
121
  If semantic search dependencies are installed, automatically generates embeddings
96
122
  after indexing completes. Use --no-embeddings to skip this step.
97
123
  """
98
- _configure_logging(verbose)
124
+ _configure_logging(verbose, json_mode)
99
125
  config = Config()
100
126
  languages = _parse_languages(language)
101
127
  base_path = path.expanduser().resolve()
@@ -314,7 +340,7 @@ def search(
314
340
  # Force hybrid mode
315
341
  codexlens search "authentication" --mode hybrid
316
342
  """
317
- _configure_logging(verbose)
343
+ _configure_logging(verbose, json_mode)
318
344
  search_path = path.expanduser().resolve()
319
345
 
320
346
  # Validate mode
@@ -487,7 +513,7 @@ def symbol(
487
513
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable debug logging."),
488
514
  ) -> None:
489
515
  """Look up symbols by name and optional kind."""
490
- _configure_logging(verbose)
516
+ _configure_logging(verbose, json_mode)
491
517
  search_path = path.expanduser().resolve()
492
518
 
493
519
  registry: RegistryStore | None = None
@@ -538,7 +564,7 @@ def inspect(
538
564
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable debug logging."),
539
565
  ) -> None:
540
566
  """Analyze a single file and display symbols."""
541
- _configure_logging(verbose)
567
+ _configure_logging(verbose, json_mode)
542
568
  config = Config()
543
569
  factory = ParserFactory(config)
544
570
 
@@ -588,7 +614,7 @@ def status(
588
614
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable debug logging."),
589
615
  ) -> None:
590
616
  """Show index status and configuration."""
591
- _configure_logging(verbose)
617
+ _configure_logging(verbose, json_mode)
592
618
 
593
619
  registry: RegistryStore | None = None
594
620
  try:
@@ -648,7 +674,7 @@ def status(
648
674
  # Embedding manager not available
649
675
  pass
650
676
  except Exception as e:
651
- logger.debug(f"Failed to get embeddings status: {e}")
677
+ logging.debug(f"Failed to get embeddings status: {e}")
652
678
 
653
679
  stats = {
654
680
  "index_root": str(index_root),
@@ -737,7 +763,7 @@ def projects(
737
763
  - show <path>: Show details for a specific project
738
764
  - remove <path>: Remove a project from the registry
739
765
  """
740
- _configure_logging(verbose)
766
+ _configure_logging(verbose, json_mode)
741
767
 
742
768
  registry: RegistryStore | None = None
743
769
  try:
@@ -892,7 +918,7 @@ def config(
892
918
  Config keys:
893
919
  - index_dir: Directory to store indexes (default: ~/.codexlens/indexes)
894
920
  """
895
- _configure_logging(verbose)
921
+ _configure_logging(verbose, json_mode)
896
922
 
897
923
  config_file = Path.home() / ".codexlens" / "config.json"
898
924
 
@@ -1057,7 +1083,7 @@ def migrate(
1057
1083
  This is a safe operation that preserves all existing data.
1058
1084
  Progress is shown during migration.
1059
1085
  """
1060
- _configure_logging(verbose)
1086
+ _configure_logging(verbose, json_mode)
1061
1087
  base_path = path.expanduser().resolve()
1062
1088
 
1063
1089
  registry: RegistryStore | None = None
@@ -1183,7 +1209,7 @@ def clean(
1183
1209
  With path, removes that project's indexes.
1184
1210
  With --all, removes all indexes (use with caution).
1185
1211
  """
1186
- _configure_logging(verbose)
1212
+ _configure_logging(verbose, json_mode)
1187
1213
 
1188
1214
  try:
1189
1215
  mapper = PathMapper()
@@ -1329,7 +1355,7 @@ def semantic_list(
1329
1355
  Shows files that have LLM-generated summaries and keywords.
1330
1356
  Results are aggregated from all index databases in the project.
1331
1357
  """
1332
- _configure_logging(verbose)
1358
+ _configure_logging(verbose, json_mode)
1333
1359
  base_path = path.expanduser().resolve()
1334
1360
 
1335
1361
  registry: Optional[RegistryStore] = None
@@ -1798,7 +1824,7 @@ def embeddings_generate(
1798
1824
  codexlens embeddings-generate ~/.codexlens/indexes/project/_index.db # Specific index
1799
1825
  codexlens embeddings-generate ~/projects/my-app --model fast --force # Regenerate with fast model
1800
1826
  """
1801
- _configure_logging(verbose)
1827
+ _configure_logging(verbose, json_mode)
1802
1828
 
1803
1829
  from codexlens.cli.embedding_manager import generate_embeddings, generate_embeddings_recursive
1804
1830
 
@@ -279,6 +279,21 @@ def generate_embeddings(
279
279
 
280
280
  try:
281
281
  with VectorStore(index_path) as vector_store:
282
+ # Check model compatibility with existing embeddings
283
+ if not force:
284
+ is_compatible, warning = vector_store.check_model_compatibility(
285
+ model_profile, embedder.model_name, embedder.embedding_dim
286
+ )
287
+ if not is_compatible:
288
+ return {
289
+ "success": False,
290
+ "error": warning,
291
+ }
292
+
293
+ # Set/update model configuration for this index
294
+ vector_store.set_model_config(
295
+ model_profile, embedder.model_name, embedder.embedding_dim
296
+ )
282
297
  # Use bulk insert mode for efficient batch ANN index building
283
298
  # This defers ANN updates until end_bulk_insert() is called
284
299
  with vector_store.bulk_insert():