Skip to content

codenib.integrations

Compatibility providers for external coding-agent runtimes.

Modules:

Name Description
locagent

LocAgent/OpenHands-compatible tools over CodeNib repository views.

orcaloca

OrcaLoca search-manager compatibility over CodeNib repository views.

Classes:

Name Description
IntegrationCapabilityError

A required manifest view is missing, stale, or failed to load.

RepositoryAdapter

Read-only repository facts backed by one ServerContext.

RepositoryEntity

A stable adapter-local view of one graph vertex.

RepositoryPathError

A requested path is invalid or escapes the manifest repository.

IntegrationCapabilityError

Bases: RuntimeError

A required manifest view is missing, stale, or failed to load.

RepositoryAdapter

RepositoryAdapter(context: Any, *, require_graph: bool = True)

Read-only repository facts backed by one ServerContext.

Methods:

Name Description
from_manifest

Load one ServerContext and bind an adapter to it.

require_view

Return a loaded view or raise with manifest-aware diagnostics.

normalize_path

Return a safe repository-relative POSIX path.

read_range

Read an inclusive 0-based range and return its actual bounds.

containing_entity

Return the narrowest symbol containing a 0-based source line.

distance

Return the shorter directed distance, or -1 if unresolved.

file_tree

Render a deterministic tree from files represented in the graph.

skeleton

Render a language-neutral signature skeleton from graph ranges.

Source code in codenib/integrations/_repository.py
def __init__(self, context: Any, *, require_graph: bool = True) -> None:
    self.context = context
    manifest = getattr(context, "manifest", None)
    if manifest is None:
        raise IntegrationCapabilityError("ServerContext has no manifest")

    raw_root = str(getattr(manifest, "repo_path", "") or "").strip()
    if not raw_root:
        raise IntegrationCapabilityError("manifest has no repository path")
    self.repo_root = Path(raw_root).expanduser().resolve()
    if not self.repo_root.is_dir():
        raise IntegrationCapabilityError(
            f"manifest repository is not available: {self.repo_root}"
        )

    self.graph = getattr(context, "symbol_graph", None)
    if require_graph and self.graph is None:
        self.require_view("symbol_graph", "symbol_graph")

    self._entities: tuple[RepositoryEntity, ...] = ()
    self._entity_by_vertex: dict[int, RepositoryEntity] = {}
    self._entity_by_canonical: dict[str, RepositoryEntity] = {}
    self._aliases: dict[str, list[RepositoryEntity]] = {}
    self._aliases_lower: dict[str, list[RepositoryEntity]] = {}
    self._files: tuple[str, ...] = ()
    if self.graph is not None:
        self._build_entity_index()

from_manifest classmethod

from_manifest(
    manifest_path: str | Path, *, require_graph: bool = True
) -> "RepositoryAdapter"

Load one ServerContext and bind an adapter to it.

Source code in codenib/integrations/_repository.py
@classmethod
def from_manifest(
    cls, manifest_path: str | Path, *, require_graph: bool = True
) -> "RepositoryAdapter":
    """Load one ``ServerContext`` and bind an adapter to it."""

    from ..mcp.context import ServerContext

    return cls(ServerContext.load(manifest_path), require_graph=require_graph)

require_view

require_view(index_name: str, attribute: str) -> Any

Return a loaded view or raise with manifest-aware diagnostics.

Source code in codenib/integrations/_repository.py
def require_view(self, index_name: str, attribute: str) -> Any:
    """Return a loaded view or raise with manifest-aware diagnostics."""

    view = getattr(self.context, attribute, None)
    if view is not None:
        return view

    manifest = self.context.manifest
    entry = getattr(manifest, "indexes", {}).get(index_name)
    errors = getattr(self.context, "errors", {})
    if index_name in errors:
        detail = f"failed to load: {errors[index_name]}"
    elif entry is None:
        detail = "no manifest entry"
    elif not manifest.index_is_current(index_name):
        detail = f"manifest status is {entry.status!r} or source identity is stale"
    else:
        detail = "manifest entry did not produce a runtime view"
    raise IntegrationCapabilityError(
        f"required {index_name!r} view is unavailable ({detail})"
    )

normalize_path

normalize_path(value: str) -> str

Return a safe repository-relative POSIX path.

Source code in codenib/integrations/_repository.py
def normalize_path(self, value: str) -> str:
    """Return a safe repository-relative POSIX path."""

    raw = str(value or "").strip().replace("\\", "/")
    while raw.startswith("./"):
        raw = raw[2:]
    if not raw or raw in {".", "/"}:
        return "."
    if raw.startswith("/") or _WINDOWS_DRIVE.match(raw):
        raise RepositoryPathError(
            f"absolute repository path is not allowed: {value}"
        )

    pure = PurePosixPath(raw)
    if ".." in pure.parts:
        raise RepositoryPathError(
            f"repository path traversal is not allowed: {value}"
        )

    normalized = pure.as_posix()
    candidate = (self.repo_root / normalized).resolve()
    if not candidate.is_relative_to(self.repo_root):
        raise RepositoryPathError(f"repository path escapes the root: {value}")
    return normalized

read_range

read_range(
    file_path: str, start_line: int | None = None, end_line: int | None = None
) -> tuple[str, int, int]

Read an inclusive 0-based range and return its actual bounds.

Source code in codenib/integrations/_repository.py
def read_range(
    self,
    file_path: str,
    start_line: int | None = None,
    end_line: int | None = None,
) -> tuple[str, int, int]:
    """Read an inclusive 0-based range and return its actual bounds."""

    lines = self.source_lines(file_path)
    if not lines:
        return "", 0, 0
    start = 0 if start_line is None else max(0, int(start_line))
    end = len(lines) - 1 if end_line is None else min(len(lines) - 1, int(end_line))
    if end < start:
        return "", start, start
    return "\n".join(lines[start : end + 1]), start, end

containing_entity

containing_entity(file_path: str, line: int) -> RepositoryEntity | None

Return the narrowest symbol containing a 0-based source line.

Source code in codenib/integrations/_repository.py
def containing_entity(self, file_path: str, line: int) -> RepositoryEntity | None:
    """Return the narrowest symbol containing a 0-based source line."""

    files = self.find_files(file_path)
    if len(files) != 1:
        return None
    matches = [
        entity
        for entity in self.symbols
        if entity.file_path == files[0]
        and entity.start_line is not None
        and entity.end_line is not None
        and entity.start_line <= line <= entity.end_line
    ]
    if not matches:
        return None
    return min(
        matches,
        key=lambda entity: (
            (entity.end_line or 0) - (entity.start_line or 0),
            -(entity.start_line or 0),
            entity.locagent_id,
        ),
    )

distance

distance(first: str, second: str) -> int

Return the shorter directed distance, or -1 if unresolved.

Source code in codenib/integrations/_repository.py
def distance(self, first: str, second: str) -> int:
    """Return the shorter directed distance, or ``-1`` if unresolved."""

    if self.graph is None:
        self.require_view("symbol_graph", "symbol_graph")
    left = self.find_entities(first)
    right = self.find_entities(second)
    if len(left) != 1 or len(right) != 1:
        return -1
    forward = self.graph.graph.distances(
        [left[0].vertex_id], [right[0].vertex_id], mode="out"
    )[0][0]
    backward = self.graph.graph.distances(
        [right[0].vertex_id], [left[0].vertex_id], mode="out"
    )[0][0]
    finite = [
        distance
        for distance in (forward, backward)
        if not (isinstance(distance, float) and math.isinf(distance))
    ]
    if not finite:
        return -1
    return int(min(finite))

file_tree

file_tree(name: str | None = None, max_depth: int | None = None) -> str

Render a deterministic tree from files represented in the graph.

Source code in codenib/integrations/_repository.py
def file_tree(self, name: str | None = None, max_depth: int | None = None) -> str:
    """Render a deterministic tree from files represented in the graph."""

    prefix = str(name or "").strip().replace("\\", "/").strip("/")
    files = list(self._files)
    if prefix:
        exact_files = self.find_files(prefix)
        if exact_files:
            files = exact_files
            prefix = str(PurePosixPath(exact_files[0]).parent)
            if prefix == ".":
                prefix = ""
        else:
            files = [
                file_path
                for file_path in files
                if file_path == prefix or file_path.startswith(prefix + "/")
            ]
    if not files:
        return f"Cannot find the file tree rooted at {name}"

    root: dict[str, Any] = {}
    for file_path in files:
        parts = PurePosixPath(file_path).parts
        if prefix:
            prefix_parts = PurePosixPath(prefix).parts
            if tuple(parts[: len(prefix_parts)]) == prefix_parts:
                parts = parts[len(prefix_parts) :]
        node = root
        for part in parts:
            node = node.setdefault(part, {})

    limit = (
        None
        if max_depth is None or int(max_depth) == -1
        else max(0, int(max_depth))
    )
    lines = [prefix or "/"]

    def render(tree: Mapping[str, Any], indent: str, level: int) -> None:
        if limit is not None and level >= limit:
            return
        items = sorted(tree.items(), key=lambda item: (bool(item[1]), item[0]))
        for index, (label, children) in enumerate(items):
            last = index == len(items) - 1
            lines.append(f"{indent}{'`-- ' if last else '|-- '}{label}")
            render(
                children,
                indent + ("    " if last else "|   "),
                level + 1,
            )

    render(root, "", 0)
    return "\n".join(lines)

skeleton

skeleton(entity: RepositoryEntity) -> str

Render a language-neutral signature skeleton from graph ranges.

Source code in codenib/integrations/_repository.py
def skeleton(self, entity: RepositoryEntity) -> str:
    """Render a language-neutral signature skeleton from graph ranges."""

    children = self.children(entity)
    records = [entity] if entity.kind != NODE_TYPE_FILE else []
    records.extend(children)
    signatures: list[str] = []
    for record in records:
        if not record.file_path or record.start_line is None:
            continue
        content, _, _ = self.read_range(
            record.file_path, record.start_line, record.start_line
        )
        signature = content.strip()
        if not signature:
            signature = record.qualified_name or record.simple_name
        if record is not entity:
            signature = "    " + signature
        signatures.append(signature)
    return "\n".join(signatures)

RepositoryEntity dataclass

RepositoryEntity(
    vertex_id: int,
    canonical_name: str,
    display_name: str,
    kind: str,
    file_path: str,
    qualified_name: str,
    simple_name: str,
    start_line: int | None,
    end_line: int | None,
    parent_name: str | None = None,
    class_name: str | None = None,
)

A stable adapter-local view of one graph vertex.

Lines remain 0-based internally. Integrations convert them exactly once while formatting results for their external agent contract.

RepositoryPathError

Bases: ValueError

A requested path is invalid or escapes the manifest repository.