Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions src/aci/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@
context_settings={"color": False},
)

def get_services():
def _project_metadata_db_path(path: Path) -> Path:
"""Return the metadata DB path scoped to a project root."""
return path.resolve() / ".aci" / "index.db"


def get_services(metadata_db_path: Path | None = None):
"""
Initialize services from .env with config-driven settings.

Expand All @@ -67,7 +72,7 @@ def get_services():
Tuple of (config, embedding_client, vector_store, metadata_store,
file_scanner, chunker, reranker)
"""
container = create_services()
container = create_services(metadata_db_path=metadata_db_path)
return (
container.config,
container.embedding_client,
Expand Down Expand Up @@ -104,7 +109,7 @@ def index(
file_scanner,
chunker,
reranker,
) = get_services()
) = get_services(metadata_db_path=_project_metadata_db_path(path))

# Use config workers if not overridden by CLI
actual_workers = workers if workers is not None else cfg.indexing.max_workers
Expand Down Expand Up @@ -221,6 +226,9 @@ def search(
):
"""Search the indexed codebase."""
try:
# Determine the search base path
search_base = path if path is not None else Path.cwd()

(
cfg,
embedding_client,
Expand All @@ -229,10 +237,7 @@ def search(
file_scanner,
chunker,
config_reranker,
) = get_services()

# Determine the search base path
search_base = path if path is not None else Path.cwd()
) = get_services(metadata_db_path=_project_metadata_db_path(search_base))

# Use centralized repository resolution for path validation and collection name
resolution = resolve_repository(search_base, metadata_store)
Expand Down Expand Up @@ -380,7 +385,7 @@ def update(
file_scanner,
chunker,
reranker,
) = get_services()
) = get_services(metadata_db_path=_project_metadata_db_path(path))

with Progress(
SpinnerColumn(),
Expand Down
51 changes: 51 additions & 0 deletions tests/unit/test_cli_metadata_db_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from pathlib import Path

import pytest
import typer

import aci.cli as cli


def test_project_metadata_db_path_is_scoped_to_project_root(tmp_path: Path) -> None:
nested = tmp_path / "repo" / "subdir"
nested.mkdir(parents=True)

db_path = cli._project_metadata_db_path(nested)

assert db_path == nested.resolve() / ".aci" / "index.db"


def test_index_uses_project_scoped_metadata_db_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
indexed_path = tmp_path / "repo"
indexed_path.mkdir()
captured: dict[str, Path | None] = {"metadata_db_path": None}

def fake_get_services(metadata_db_path: Path | None = None):
captured["metadata_db_path"] = metadata_db_path
raise RuntimeError("stop")

monkeypatch.setattr(cli, "get_services", fake_get_services)

with pytest.raises(typer.Exit):
cli.index(path=indexed_path, workers=None)

assert captured["metadata_db_path"] == indexed_path.resolve() / ".aci" / "index.db"


def test_search_uses_explicit_path_for_project_scoped_metadata_db_path(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
search_path = tmp_path / "repo"
search_path.mkdir()
captured: dict[str, Path | None] = {"metadata_db_path": None}

def fake_get_services(metadata_db_path: Path | None = None):
captured["metadata_db_path"] = metadata_db_path
raise RuntimeError("stop")

monkeypatch.setattr(cli, "get_services", fake_get_services)

with pytest.raises(typer.Exit):
cli.search(query="hello", path=search_path)

assert captured["metadata_db_path"] == search_path.resolve() / ".aci" / "index.db"
Loading