Files
cleveragents-core/robot/helper_repo_indexing_cli.py
T
brent.edwards 4f950a1600
CI / lint (push) Successful in 24s
CI / build (push) Successful in 16s
CI / typecheck (push) Successful in 38s
CI / quality (push) Successful in 43s
CI / security (push) Successful in 53s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 2m39s
CI / unit_tests (push) Successful in 3m58s
CI / docker (push) Successful in 1m8s
CI / e2e_tests (push) Successful in 5m21s
CI / coverage (push) Successful in 6m31s
CI / benchmark-publish (push) Successful in 19m39s
feat(cli): repo indexing CLI functional (#982)
## Summary

Add `agents repo index` and `agents repo status` CLI commands, wiring the existing `RepoIndexingService` backend to the CLI layer.

### Commands

- **`agents repo index <resource_name>`** — Triggers full or incremental indexing of a repository resource. Options: `--full` (force full re-index), `--format text|json`
- **`agents repo status <resource_name>`** — Displays indexing metadata: status, file count, token estimate, primary language, last indexed timestamp. Options: `--format text|json`

### Implementation

- New CLI module `src/cleveragents/cli/commands/repo.py` (238 lines)
- Registered as `repo` subcommand group in `cli/main.py`
- Resolves resource by name via `ResourceRegistryService`
- Calls `RepoIndexingService.index_resource()` / `refresh_index()` / `get_index_status()`
- Both text (Rich panel) and JSON output formats

### Quality Gates

| Session | Result |
|---|---|
| `nox -s lint` | PASS |
| `nox -s typecheck` | PASS (0 errors) |
| `nox -s unit_tests` | PASS (10,819 scenarios) |
| `nox -s coverage_report` | 98% (>= 97%) |
| Integration tests | 5/5 PASS |

Closes #856

Reviewed-on: #982
Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
2026-03-20 22:28:32 +00:00

185 lines
5.5 KiB
Python

"""Robot Framework helper for repo indexing CLI integration tests.
Provides end-to-end test scenarios that exercise the RepoIndexingService
and CLI wiring together, using an in-memory SQLite database.
"""
from __future__ import annotations
import tempfile
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from cleveragents.application.services.repo_indexing_service import (
RepoIndexingService,
)
from cleveragents.application.services.resource_registry_service import (
ResourceRegistryService,
)
from cleveragents.infrastructure.database.models import Base
def _make_services() -> tuple[ResourceRegistryService, RepoIndexingService]:
"""Create fresh services backed by an in-memory database."""
engine = create_engine("sqlite:///:memory:", echo=False)
Base.metadata.create_all(engine)
factory = sessionmaker(bind=engine, expire_on_commit=False)
resource_svc = ResourceRegistryService(session_factory=factory)
resource_svc.bootstrap_builtin_types()
indexing_svc = RepoIndexingService(session_factory=factory)
return resource_svc, indexing_svc
def test_e2e_add_index_status() -> str:
"""End-to-end: add resource -> index -> check status."""
resource_svc, indexing_svc = _make_services()
# Create a temp directory with sample files
tmp_dir = tempfile.mkdtemp()
readme = Path(tmp_dir) / "README.md"
readme.write_text("# Test\nSample content.\n")
main_py = Path(tmp_dir) / "main.py"
main_py.write_text("print('hello')\n")
# Register resource
resource = resource_svc.register_resource(
type_name="git-checkout",
name="local/e2e-repo",
location=tmp_dir,
properties={"path": tmp_dir},
)
# Index
repo_index = indexing_svc.index_resource(resource.resource_id, tmp_dir)
assert repo_index.metadata.status.value == "ready"
assert repo_index.metadata.file_count >= 2
# Check status
status = indexing_svc.get_index_status(resource.resource_id)
assert status is not None
assert status.status.value == "ready"
assert status.file_count == repo_index.metadata.file_count
return "E2E add-index-status PASSED"
def test_incremental_reindex_after_change() -> str:
"""Incremental re-index after file change."""
resource_svc, indexing_svc = _make_services()
tmp_dir = tempfile.mkdtemp()
readme = Path(tmp_dir) / "README.md"
readme.write_text("# Original\n")
resource = resource_svc.register_resource(
type_name="git-checkout",
name="local/incr-repo",
location=tmp_dir,
properties={"path": tmp_dir},
)
# Initial index
idx1 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
count1 = idx1.metadata.file_count
# Add a new file
new_file = Path(tmp_dir) / "app.py"
new_file.write_text("x = 1\n")
# Incremental refresh
idx2 = indexing_svc.refresh_index(resource.resource_id, tmp_dir)
assert idx2.metadata.file_count == count1 + 1
assert idx2.metadata.status.value == "ready"
return "Incremental reindex PASSED"
def test_full_reindex() -> str:
"""Full re-index replaces the entire index."""
resource_svc, indexing_svc = _make_services()
tmp_dir = tempfile.mkdtemp()
readme = Path(tmp_dir) / "README.md"
readme.write_text("# Test\n")
resource = resource_svc.register_resource(
type_name="git-checkout",
name="local/full-repo",
location=tmp_dir,
properties={"path": tmp_dir},
)
# Initial index
idx1 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
# Full re-index
idx2 = indexing_svc.index_resource(resource.resource_id, tmp_dir)
assert idx2.metadata.status.value == "ready"
assert idx2.metadata.file_count == idx1.metadata.file_count
return "Full reindex PASSED"
def test_status_no_index() -> str:
"""Status returns None when no index exists."""
resource_svc, indexing_svc = _make_services()
tmp_dir = tempfile.mkdtemp()
resource = resource_svc.register_resource(
type_name="git-checkout",
name="local/no-index-repo",
location=tmp_dir,
properties={"path": tmp_dir},
)
status = indexing_svc.get_index_status(resource.resource_id)
assert status is None
return "Status no-index PASSED"
def test_remove_index() -> str:
"""Remove index cleans up all data."""
resource_svc, indexing_svc = _make_services()
tmp_dir = tempfile.mkdtemp()
readme = Path(tmp_dir) / "README.md"
readme.write_text("# Test\n")
resource = resource_svc.register_resource(
type_name="git-checkout",
name="local/remove-repo",
location=tmp_dir,
properties={"path": tmp_dir},
)
indexing_svc.index_resource(resource.resource_id, tmp_dir)
assert indexing_svc.get_index_status(resource.resource_id) is not None
removed = indexing_svc.remove_index(resource.resource_id)
assert removed is True
assert indexing_svc.get_index_status(resource.resource_id) is None
return "Remove index PASSED"
if __name__ == "__main__":
import sys
_dispatch = {
"e2e": test_e2e_add_index_status,
"incremental": test_incremental_reindex_after_change,
"full": test_full_reindex,
"no-index": test_status_no_index,
"remove": test_remove_index,
}
if len(sys.argv) < 2 or sys.argv[1] not in _dispatch:
# Run all tests
for fn in _dispatch.values():
print(fn())
else:
print(_dispatch[sys.argv[1]]())