Skip to content

Providers

Kiro

chico.providers.kiro

Kiro provider for chico.

Maps files fetched from a source (GitHub, S3) to their local counterparts inside a Kiro configuration directory and applies changes by writing files to disk.

Kiro uses the same directory structure at both project and global level:

  • Project-level: .kiro/ at the workspace root
  • Global-level: ~/.kiro/ in the home directory

Steering files live under {kiro_dir}/steering/*.md, and agent definitions are declared in {kiro_dir}/steering/AGENTS.md.

Example usage::

from pathlib import Path
from chico.providers.kiro import KiroProvider
from chico.core.source import FetchResult

fetch_result = github_source.fetch()

# Project-level sync
provider = KiroProvider(
    fetch_result=fetch_result,
    kiro_dir=Path(".kiro"),
    source_prefix="configs/",   # strip the repo prefix before mapping
)

for resource in provider.list_resources():
    diff = resource.diff()
    if diff.has_changes:
        resource.apply()

KiroFileResource

A single Kiro configuration file managed by chico.

Represents one file from a source (e.g. steering/product.md) mapped to its local path inside a Kiro directory. Knows how to diff and apply changes without affecting any other files.

Parameters:

Name Type Description Default
source_path str

The file's path as it appears in the source (e.g. steering/product.md).

required
source_content str

The full text content fetched from the source.

required
local_path Path

The absolute local path where the file should be written.

required
Source code in chico/providers/kiro.py
class KiroFileResource:
    """A single Kiro configuration file managed by chico.

    Represents one file from a source (e.g. ``steering/product.md``) mapped
    to its local path inside a Kiro directory. Knows how to diff and apply
    changes without affecting any other files.

    Parameters
    ----------
    source_path:
        The file's path as it appears in the source (e.g. ``steering/product.md``).
    source_content:
        The full text content fetched from the source.
    local_path:
        The absolute local path where the file should be written.
    """

    def __init__(self, source_path: str, source_content: str, local_path: Path) -> None:
        self._source_path = source_path
        self._source_content = source_content
        self._local_path = local_path

    @property
    def resource_id(self) -> str:
        """The absolute local path of this file, used as a stable identifier."""
        return str(self._local_path)

    def desired_state(self) -> dict:
        """Return the desired state as fetched from the source."""
        return {"content": self._source_content}

    def current_state(self) -> dict:
        """Return the current on-disk state, or ``{}`` if the file does not exist."""
        if not self._local_path.exists():
            return {}
        try:
            content = self._local_path.read_text(encoding="utf-8")
        except UnicodeDecodeError:
            content = self._local_path.read_text(encoding="latin-1")
        return {"content": content}

    def diff(self) -> Diff:
        """Compute the diff between desired and current state.

        Returns
        -------
        Diff
            * ``ChangeType.ADD`` — file does not exist locally yet.
            * ``ChangeType.MODIFY`` — file exists but content differs.
            * ``ChangeType.NONE`` — file exists and content matches.
        """
        current = self.current_state()

        if not current:
            return Diff(change_type=ChangeType.ADD, resource_id=self.resource_id)

        if self._source_content == current["content"]:
            return Diff(change_type=ChangeType.NONE, resource_id=self.resource_id)

        return Diff(
            change_type=ChangeType.MODIFY,
            resource_id=self.resource_id,
            changes={
                "content": FieldChange(
                    from_value=current["content"],
                    to_value=self._source_content,
                )
            },
        )

    def apply(self) -> Result:
        """Write the desired content to the local path.

        Creates any missing parent directories. Safe to call when the file
        is already in sync — the content is simply rewritten (idempotent).

        Returns
        -------
        Result
            ``ResultStatus.OK`` on success, ``ResultStatus.ERROR`` with a
            message if the write fails (e.g. permission denied).
        """
        try:
            self._local_path.parent.mkdir(parents=True, exist_ok=True)
            self._local_path.write_text(self._source_content, encoding="utf-8")
            return Result(status=ResultStatus.OK, resource_id=self.resource_id)
        except Exception as exc:
            return Result(
                status=ResultStatus.ERROR,
                resource_id=self.resource_id,
                message=str(exc),
            )

resource_id property

The absolute local path of this file, used as a stable identifier.

apply()

Write the desired content to the local path.

Creates any missing parent directories. Safe to call when the file is already in sync — the content is simply rewritten (idempotent).

Returns:

Type Description
Result

ResultStatus.OK on success, ResultStatus.ERROR with a message if the write fails (e.g. permission denied).

Source code in chico/providers/kiro.py
def apply(self) -> Result:
    """Write the desired content to the local path.

    Creates any missing parent directories. Safe to call when the file
    is already in sync — the content is simply rewritten (idempotent).

    Returns
    -------
    Result
        ``ResultStatus.OK`` on success, ``ResultStatus.ERROR`` with a
        message if the write fails (e.g. permission denied).
    """
    try:
        self._local_path.parent.mkdir(parents=True, exist_ok=True)
        self._local_path.write_text(self._source_content, encoding="utf-8")
        return Result(status=ResultStatus.OK, resource_id=self.resource_id)
    except Exception as exc:
        return Result(
            status=ResultStatus.ERROR,
            resource_id=self.resource_id,
            message=str(exc),
        )

current_state()

Return the current on-disk state, or {} if the file does not exist.

Source code in chico/providers/kiro.py
def current_state(self) -> dict:
    """Return the current on-disk state, or ``{}`` if the file does not exist."""
    if not self._local_path.exists():
        return {}
    try:
        content = self._local_path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        content = self._local_path.read_text(encoding="latin-1")
    return {"content": content}

desired_state()

Return the desired state as fetched from the source.

Source code in chico/providers/kiro.py
def desired_state(self) -> dict:
    """Return the desired state as fetched from the source."""
    return {"content": self._source_content}

diff()

Compute the diff between desired and current state.

Returns:

Type Description
Diff
  • ChangeType.ADD — file does not exist locally yet.
  • ChangeType.MODIFY — file exists but content differs.
  • ChangeType.NONE — file exists and content matches.
Source code in chico/providers/kiro.py
def diff(self) -> Diff:
    """Compute the diff between desired and current state.

    Returns
    -------
    Diff
        * ``ChangeType.ADD`` — file does not exist locally yet.
        * ``ChangeType.MODIFY`` — file exists but content differs.
        * ``ChangeType.NONE`` — file exists and content matches.
    """
    current = self.current_state()

    if not current:
        return Diff(change_type=ChangeType.ADD, resource_id=self.resource_id)

    if self._source_content == current["content"]:
        return Diff(change_type=ChangeType.NONE, resource_id=self.resource_id)

    return Diff(
        change_type=ChangeType.MODIFY,
        resource_id=self.resource_id,
        changes={
            "content": FieldChange(
                from_value=current["content"],
                to_value=self._source_content,
            )
        },
    )

KiroProvider

Provider that maps source files to a local Kiro directory.

Takes a :class:~chico.core.source.FetchResult and produces one :class:KiroFileResource per file, mapping source paths into the local kiro_dir.

Parameters:

Name Type Description Default
fetch_result FetchResult

The result of calling source.fetch(), containing the files and commit version to sync.

required
kiro_dir Path

Root Kiro directory to sync into. Use .kiro/ for project-level sync or Path.home() / ".kiro" for global sync.

required
source_prefix str

Optional prefix to strip from source paths before mapping to local paths. For example, if the GitHub repo stores files under configs/steering/, set source_prefix="configs/" so the file lands at {kiro_dir}/steering/ locally.

''
Example

::

provider = KiroProvider(
    fetch_result=github_source.fetch(),
    kiro_dir=Path(".kiro"),
    source_prefix="configs/",
)
for resource in provider.list_resources():
    print(resource.resource_id, resource.diff().change_type)
Source code in chico/providers/kiro.py
class KiroProvider:
    """Provider that maps source files to a local Kiro directory.

    Takes a :class:`~chico.core.source.FetchResult` and produces one
    :class:`KiroFileResource` per file, mapping source paths into the
    local ``kiro_dir``.

    Parameters
    ----------
    fetch_result:
        The result of calling ``source.fetch()``, containing the files
        and commit version to sync.
    kiro_dir:
        Root Kiro directory to sync into. Use ``.kiro/`` for project-level
        sync or ``Path.home() / ".kiro"`` for global sync.
    source_prefix:
        Optional prefix to strip from source paths before mapping to local
        paths. For example, if the GitHub repo stores files under
        ``configs/steering/``, set ``source_prefix="configs/"`` so the file
        lands at ``{kiro_dir}/steering/`` locally.

    Example
    -------
    ::

        provider = KiroProvider(
            fetch_result=github_source.fetch(),
            kiro_dir=Path(".kiro"),
            source_prefix="configs/",
        )
        for resource in provider.list_resources():
            print(resource.resource_id, resource.diff().change_type)
    """

    def __init__(
        self,
        fetch_result: FetchResult,
        kiro_dir: Path,
        source_prefix: str = "",
    ) -> None:
        self._fetch_result = fetch_result
        self._kiro_dir = kiro_dir
        self._source_prefix = source_prefix

    @property
    def name(self) -> str:
        """Provider name — always ``"kiro"``."""
        return "kiro"

    def list_resources(self) -> list[Resource]:
        """Return one :class:`KiroFileResource` per file in the fetch result.

        Source paths are mapped to local paths by:
        1. Stripping ``source_prefix`` from the beginning of the path.
        2. Joining the remainder onto ``kiro_dir``.

        For example, with ``source_prefix="configs/"`` and
        ``kiro_dir=Path(".kiro")``, the source path
        ``configs/steering/product.md`` maps to ``.kiro/steering/product.md``.
        """
        resources: list[Resource] = []
        for source_path, content in self._fetch_result.files.items():
            relative = source_path.removeprefix(self._source_prefix)
            local_path = self._kiro_dir / relative
            logger.info(
                "kiro.mapping",
                extra={
                    "source_path": source_path,
                    "local_path": str(local_path),
                    "prefix_stripped": self._source_prefix,
                },
            )
            resources.append(
                KiroFileResource(
                    source_path=source_path,
                    source_content=content,
                    local_path=local_path,
                )
            )
        return resources

name property

Provider name — always "kiro".

list_resources()

Return one :class:KiroFileResource per file in the fetch result.

Source paths are mapped to local paths by: 1. Stripping source_prefix from the beginning of the path. 2. Joining the remainder onto kiro_dir.

For example, with source_prefix="configs/" and kiro_dir=Path(".kiro"), the source path configs/steering/product.md maps to .kiro/steering/product.md.

Source code in chico/providers/kiro.py
def list_resources(self) -> list[Resource]:
    """Return one :class:`KiroFileResource` per file in the fetch result.

    Source paths are mapped to local paths by:
    1. Stripping ``source_prefix`` from the beginning of the path.
    2. Joining the remainder onto ``kiro_dir``.

    For example, with ``source_prefix="configs/"`` and
    ``kiro_dir=Path(".kiro")``, the source path
    ``configs/steering/product.md`` maps to ``.kiro/steering/product.md``.
    """
    resources: list[Resource] = []
    for source_path, content in self._fetch_result.files.items():
        relative = source_path.removeprefix(self._source_prefix)
        local_path = self._kiro_dir / relative
        logger.info(
            "kiro.mapping",
            extra={
                "source_path": source_path,
                "local_path": str(local_path),
                "prefix_stripped": self._source_prefix,
            },
        )
        resources.append(
            KiroFileResource(
                source_path=source_path,
                source_content=content,
                local_path=local_path,
            )
        )
    return resources