cocoindex-code 0.2.7__tar.gz → 0.2.8__tar.gz

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 (20) hide show
  1. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/PKG-INFO +34 -1
  2. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/README.md +33 -0
  3. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/_version.py +2 -2
  4. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/cli.py +14 -2
  5. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/config.py +4 -2
  6. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/daemon.py +9 -1
  7. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/project.py +9 -5
  8. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/protocol.py +6 -0
  9. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/settings.py +78 -2
  10. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/.gitignore +0 -0
  11. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/LICENSE +0 -0
  12. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/pyproject.toml +0 -0
  13. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/__init__.py +0 -0
  14. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/__main__.py +0 -0
  15. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/client.py +0 -0
  16. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/indexer.py +0 -0
  17. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/query.py +0 -0
  18. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/schema.py +0 -0
  19. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/server.py +0 -0
  20. {cocoindex_code-0.2.7 → cocoindex_code-0.2.8}/src/cocoindex_code/shared.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocoindex-code
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: MCP server for indexing and querying codebases using CocoIndex
5
5
  Project-URL: Homepage, https://github.com/cocoindex-io/cocoindex-code
6
6
  Project-URL: Repository, https://github.com/cocoindex-io/cocoindex-code
@@ -457,6 +457,26 @@ embedding:
457
457
  | xml | | `.xml` |
458
458
  | yaml | | `.yaml`, `.yml` |
459
459
 
460
+ ### Custom Database Location
461
+
462
+ By default, index databases (`cocoindex.db` and `target_sqlite.db`) live alongside settings in `<project>/.cocoindex_code/`. When running in Docker, you may want the databases on the container's native filesystem for performance (LMDB doesn't work well on mounted volumes) while keeping the source code and settings on a mounted volume.
463
+
464
+ Set `COCOINDEX_CODE_DB_PATH_MAPPING` to remap database locations by path prefix:
465
+
466
+ ```bash
467
+ COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files
468
+ ```
469
+
470
+ With this mapping, a project at `/workspace/myrepo` stores its databases in `/db-files/myrepo/` instead of `/workspace/myrepo/.cocoindex_code/`. Settings files remain in the original location.
471
+
472
+ Multiple mappings are comma-separated and resolved in order (first match wins):
473
+
474
+ ```bash
475
+ COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files,/workspace2=/db-files2
476
+ ```
477
+
478
+ Both source and target must be absolute paths. If no mapping matches, the default location is used.
479
+
460
480
  ## Troubleshooting
461
481
 
462
482
  Run `ccc doctor` to diagnose common issues. It checks your settings, daemon health, embedding model, file matching, and index status — all in one command.
@@ -501,6 +521,19 @@ If you previously configured `cocoindex-code` via environment variables, the `co
501
521
 
502
522
  If you need help with remote setup, please email our maintainer linghua@cocoindex.io, happy to help!
503
523
 
524
+ ## Contributing
525
+
526
+ We welcome contributions! Before you start, please install the [pre-commit](https://pre-commit.com/) hooks so that linting, formatting, type checking, and tests run automatically before each commit:
527
+
528
+ ```bash
529
+ pip install pre-commit
530
+ pre-commit install
531
+ ```
532
+
533
+ This catches common issues — trailing whitespace, lint errors (Ruff), type errors (mypy), and test failures — before they reach CI.
534
+
535
+ For more details, see our [contributing guide](https://cocoindex.io/docs/contributing/guide).
536
+
504
537
  ## License
505
538
 
506
539
  Apache-2.0
@@ -418,6 +418,26 @@ embedding:
418
418
  | xml | | `.xml` |
419
419
  | yaml | | `.yaml`, `.yml` |
420
420
 
421
+ ### Custom Database Location
422
+
423
+ By default, index databases (`cocoindex.db` and `target_sqlite.db`) live alongside settings in `<project>/.cocoindex_code/`. When running in Docker, you may want the databases on the container's native filesystem for performance (LMDB doesn't work well on mounted volumes) while keeping the source code and settings on a mounted volume.
424
+
425
+ Set `COCOINDEX_CODE_DB_PATH_MAPPING` to remap database locations by path prefix:
426
+
427
+ ```bash
428
+ COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files
429
+ ```
430
+
431
+ With this mapping, a project at `/workspace/myrepo` stores its databases in `/db-files/myrepo/` instead of `/workspace/myrepo/.cocoindex_code/`. Settings files remain in the original location.
432
+
433
+ Multiple mappings are comma-separated and resolved in order (first match wins):
434
+
435
+ ```bash
436
+ COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files,/workspace2=/db-files2
437
+ ```
438
+
439
+ Both source and target must be absolute paths. If no mapping matches, the default location is used.
440
+
421
441
  ## Troubleshooting
422
442
 
423
443
  Run `ccc doctor` to diagnose common issues. It checks your settings, daemon health, embedding model, file matching, and index status — all in one command.
@@ -462,6 +482,19 @@ If you previously configured `cocoindex-code` via environment variables, the `co
462
482
 
463
483
  If you need help with remote setup, please email our maintainer linghua@cocoindex.io, happy to help!
464
484
 
485
+ ## Contributing
486
+
487
+ We welcome contributions! Before you start, please install the [pre-commit](https://pre-commit.com/) hooks so that linting, formatting, type checking, and tests run automatically before each commit:
488
+
489
+ ```bash
490
+ pip install pre-commit
491
+ pre-commit install
492
+ ```
493
+
494
+ This catches common issues — trailing whitespace, lint errors (Ruff), type errors (mypy), and test failures — before they reach CI.
495
+
496
+ For more details, see our [contributing guide](https://cocoindex.io/docs/contributing/guide).
497
+
465
498
  ## License
466
499
 
467
500
  Apache-2.0
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.7'
32
- __version_tuple__ = version_tuple = (0, 2, 7)
31
+ __version__ = version = '0.2.8'
32
+ __version_tuple__ = version_tuple = (0, 2, 8)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -16,6 +16,7 @@ from .settings import (
16
16
  default_user_settings,
17
17
  find_parent_with_marker,
18
18
  find_project_root,
19
+ resolve_db_dir,
19
20
  save_project_settings,
20
21
  save_user_settings,
21
22
  user_settings_path,
@@ -389,10 +390,11 @@ def reset(
389
390
  """Reset project databases and optionally remove settings."""
390
391
  project_root = require_project_root()
391
392
  cocoindex_dir = project_root / ".cocoindex_code"
393
+ db_dir = resolve_db_dir(project_root)
392
394
 
393
395
  db_files = [
394
- cocoindex_dir / "cocoindex.db",
395
- cocoindex_dir / "target_sqlite.db",
396
+ db_dir / "cocoindex.db",
397
+ db_dir / "target_sqlite.db",
396
398
  ]
397
399
  settings_file = cocoindex_dir / "settings.yml"
398
400
 
@@ -436,6 +438,12 @@ def reset(
436
438
  f.unlink(missing_ok=True)
437
439
 
438
440
  if all_:
441
+ # Remove db_dir if empty and different from cocoindex_dir
442
+ if db_dir != cocoindex_dir:
443
+ try:
444
+ db_dir.rmdir()
445
+ except OSError:
446
+ pass # Not empty or doesn't exist
439
447
  # Remove .cocoindex_code/ if empty
440
448
  try:
441
449
  cocoindex_dir.rmdir()
@@ -539,6 +547,10 @@ def doctor() -> None:
539
547
  other_keys = [k for k in env_resp.env_names if k not in settings_keys]
540
548
  if other_keys:
541
549
  _typer.echo(f" Other env vars in daemon: {', '.join(sorted(other_keys))}")
550
+ if env_resp.db_path_mappings:
551
+ _typer.echo(" DB path mappings:")
552
+ for m in env_resp.db_path_mappings:
553
+ _typer.echo(f" {m.source} \u2192 {m.target}")
542
554
  except Exception as e:
543
555
  _print_error(f"Failed to get daemon env: {e}")
544
556
 
@@ -7,6 +7,8 @@ import os
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
9
 
10
+ from .settings import resolve_db_dir
11
+
10
12
  _DEFAULT_MODEL = "sbert/sentence-transformers/all-MiniLM-L6-v2"
11
13
 
12
14
 
@@ -96,8 +98,8 @@ class Config:
96
98
  _DEFAULT_MODEL,
97
99
  )
98
100
 
99
- # Index directory is always under the root
100
- index_dir = root / ".cocoindex_code"
101
+ # Index directory: apply DB path mapping if configured
102
+ index_dir = resolve_db_dir(root)
101
103
 
102
104
  # Device: auto-detect CUDA or use env override
103
105
  device = os.environ.get("COCOINDEX_CODE_DEVICE")
@@ -48,6 +48,7 @@ from .protocol import (
48
48
  from .settings import (
49
49
  global_settings_mtime_us,
50
50
  load_user_settings,
51
+ resolve_db_dir,
51
52
  user_settings_dir,
52
53
  )
53
54
  from .shared import Embedder, create_embedder
@@ -345,7 +346,7 @@ async def _check_index_status(project_root_str: str) -> DoctorCheckResult:
345
346
  from cocoindex.connectors import sqlite as coco_sqlite
346
347
 
347
348
  project_root = Path(project_root_str)
348
- db_path = project_root / ".cocoindex_code" / "target_sqlite.db"
349
+ db_path = resolve_db_dir(project_root) / "target_sqlite.db"
349
350
  details = [f"Index: {db_path}"]
350
351
 
351
352
  if not db_path.exists():
@@ -441,9 +442,16 @@ async def _dispatch(
441
442
  return StopResponse(ok=True)
442
443
 
443
444
  if isinstance(req, DaemonEnvRequest):
445
+ from .protocol import DbPathMappingEntry
446
+ from .settings import get_db_path_mappings
447
+
444
448
  return DaemonEnvResponse(
445
449
  env_names=sorted(os.environ.keys()),
446
450
  settings_env_names=settings_env_names,
451
+ db_path_mappings=[
452
+ DbPathMappingEntry(source=str(m.source), target=str(m.target))
453
+ for m in get_db_path_mappings()
454
+ ],
447
455
  )
448
456
 
449
457
  if isinstance(req, DoctorRequest):
@@ -21,6 +21,7 @@ from .protocol import (
21
21
  SearchResult,
22
22
  )
23
23
  from .query import query_codebase
24
+ from .settings import resolve_db_dir
24
25
  from .shared import (
25
26
  CODEBASE_DIR,
26
27
  EMBEDDER,
@@ -170,7 +171,7 @@ class Project:
170
171
  offset: int = 0,
171
172
  ) -> list[SearchResult]:
172
173
  """Search within this project."""
173
- target_db = self._project_root / ".cocoindex_code" / "target_sqlite.db"
174
+ target_db = resolve_db_dir(self._project_root) / "target_sqlite.db"
174
175
  results = await query_codebase(
175
176
  query=query,
176
177
  target_sqlite_db_path=target_db,
@@ -254,11 +255,14 @@ class Project:
254
255
  indexer loads them fresh from disk on every run so that user edits
255
256
  take effect without restarting the daemon.
256
257
  """
257
- index_dir = project_root / ".cocoindex_code"
258
- index_dir.mkdir(parents=True, exist_ok=True)
258
+ settings_dir = project_root / ".cocoindex_code"
259
+ settings_dir.mkdir(parents=True, exist_ok=True)
259
260
 
260
- cocoindex_db_path = index_dir / "cocoindex.db"
261
- target_sqlite_db_path = index_dir / "target_sqlite.db"
261
+ db_dir = resolve_db_dir(project_root)
262
+ db_dir.mkdir(parents=True, exist_ok=True)
263
+
264
+ cocoindex_db_path = db_dir / "cocoindex.db"
265
+ target_sqlite_db_path = db_dir / "target_sqlite.db"
262
266
 
263
267
  settings = coco.Settings.from_env(cocoindex_db_path)
264
268
 
@@ -158,9 +158,15 @@ class DoctorResponse(_msgspec.Struct, tag="doctor"):
158
158
  final: bool = False
159
159
 
160
160
 
161
+ class DbPathMappingEntry(_msgspec.Struct):
162
+ source: str
163
+ target: str
164
+
165
+
161
166
  class DaemonEnvResponse(_msgspec.Struct, tag="daemon_env"):
162
167
  env_names: list[str]
163
168
  settings_env_names: list[str]
169
+ db_path_mappings: list[DbPathMappingEntry] = []
164
170
 
165
171
 
166
172
  class ErrorResponse(_msgspec.Struct, tag="error"):
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  from dataclasses import dataclass, field
6
7
  from pathlib import Path
7
8
  from typing import Any
@@ -115,14 +116,89 @@ _SETTINGS_DIR_NAME = ".cocoindex_code"
115
116
  _SETTINGS_FILE_NAME = "settings.yml" # project-level
116
117
  _USER_SETTINGS_FILE_NAME = "global_settings.yml" # user-level
117
118
 
119
+ _ENV_DB_PATH_MAPPING = "COCOINDEX_CODE_DB_PATH_MAPPING"
120
+
121
+
122
+ @dataclass
123
+ class DbPathMapping:
124
+ source: Path
125
+ target: Path
126
+
127
+
128
+ _db_path_mapping: list[DbPathMapping] | None = None
129
+
130
+
131
+ def _parse_db_path_mapping() -> list[DbPathMapping]:
132
+ """Parse ``COCOINDEX_CODE_DB_PATH_MAPPING`` env var.
133
+
134
+ Format: ``/src1=/dst1,/src2=/dst2``
135
+ Both source and target must be absolute paths.
136
+ """
137
+ raw = os.environ.get(_ENV_DB_PATH_MAPPING, "")
138
+ if not raw.strip():
139
+ return []
140
+
141
+ mappings: list[DbPathMapping] = []
142
+ for entry in raw.split(","):
143
+ entry = entry.strip()
144
+ if not entry:
145
+ continue
146
+ parts = entry.split("=", 1)
147
+ if len(parts) != 2 or not parts[0] or not parts[1]:
148
+ raise ValueError(
149
+ f"{_ENV_DB_PATH_MAPPING}: invalid entry {entry!r}, expected format 'source=target'"
150
+ )
151
+ source = Path(parts[0])
152
+ target = Path(parts[1])
153
+ if not source.is_absolute():
154
+ raise ValueError(
155
+ f"{_ENV_DB_PATH_MAPPING}: source path must be absolute, got {source!r}"
156
+ )
157
+ if not target.is_absolute():
158
+ raise ValueError(
159
+ f"{_ENV_DB_PATH_MAPPING}: target path must be absolute, got {target!r}"
160
+ )
161
+ mappings.append(DbPathMapping(source=source.resolve(), target=target.resolve()))
162
+ return mappings
163
+
164
+
165
+ def resolve_db_dir(project_root: Path) -> Path:
166
+ """Return the directory for database files given a project root.
167
+
168
+ Applies ``COCOINDEX_CODE_DB_PATH_MAPPING`` if set, otherwise falls back
169
+ to ``project_root / ".cocoindex_code"``.
170
+ """
171
+ global _db_path_mapping # noqa: PLW0603
172
+ if _db_path_mapping is None:
173
+ _db_path_mapping = _parse_db_path_mapping()
174
+
175
+ resolved = project_root.resolve()
176
+ for mapping in _db_path_mapping:
177
+ if resolved == mapping.source or resolved.is_relative_to(mapping.source):
178
+ rel = resolved.relative_to(mapping.source)
179
+ return mapping.target / rel
180
+ return project_root / _SETTINGS_DIR_NAME
181
+
182
+
183
+ def get_db_path_mappings() -> list[DbPathMapping]:
184
+ """Return the parsed DB path mappings from ``COCOINDEX_CODE_DB_PATH_MAPPING``."""
185
+ global _db_path_mapping # noqa: PLW0603
186
+ if _db_path_mapping is None:
187
+ _db_path_mapping = _parse_db_path_mapping()
188
+ return list(_db_path_mapping)
189
+
190
+
191
+ def _reset_db_path_mapping_cache() -> None:
192
+ """Reset the cached mapping (for tests)."""
193
+ global _db_path_mapping # noqa: PLW0603
194
+ _db_path_mapping = None
195
+
118
196
 
119
197
  def user_settings_dir() -> Path:
120
198
  """Return ``~/.cocoindex_code/``.
121
199
 
122
200
  Respects ``COCOINDEX_CODE_DIR`` env var for overriding the base directory.
123
201
  """
124
- import os
125
-
126
202
  override = os.environ.get("COCOINDEX_CODE_DIR")
127
203
  if override:
128
204
  return Path(override)
File without changes