agent-source-repository 0.1.0

Agent Source Repository local context registry for coding agents
Documentation
use std::path::Path;

use super::{
    checked_git_root, git, now_rfc3339, open_store, path_string, require_repo,
    source_type_for_kind, status_text, store, validate_asr_home_outside_repo, validate_repo_name,
    AsrPaths, AsrResult, InitOutput, RepoAddOutput, RepoListOutput, RepoRecord, RepoStatusOutput,
};

pub(crate) fn init() -> AsrResult<InitOutput> {
    let paths = AsrPaths::resolve()?;
    let created = paths.create_all()?;
    let store = store::Store::open(&paths.db_path)?;
    store.init_schema()?;

    Ok(InitOutput {
        ok: true,
        asr_home: path_string(&paths.home),
        db_path: path_string(&paths.db_path),
        created,
    })
}

pub(crate) fn repo_add(name: &str, path: &Path) -> AsrResult<RepoAddOutput> {
    validate_repo_name(name)?;
    let (paths, store) = open_store()?;

    if store.get_repo(name)?.is_some() {
        return Err(super::AsrError::new(
            "repo_name_exists",
            format!("Repository name already exists: {name}"),
        ));
    }

    let resolved = git::canonical_repo_root(path)?;
    let git_root = resolved.root;
    validate_asr_home_outside_repo(&paths, &git_root)?;
    let branch = git::current_branch(&git_root);
    let head_commit = git::head_commit(&git_root);
    let now = now_rfc3339();

    let record = RepoRecord {
        name: name.to_string(),
        source_type: source_type_for_kind(resolved.kind).to_string(),
        remote_url: None,
        local_path: path_string(&git_root),
        git_root: path_string(&git_root),
        default_branch: branch,
        head_commit,
        created_at: now.clone(),
        updated_at: now,
    };

    store.insert_repo(&record)?;

    Ok(RepoAddOutput { repo: record })
}

pub(crate) fn repo_list() -> AsrResult<RepoListOutput> {
    let (_paths, store) = open_store()?;
    Ok(RepoListOutput {
        repos: store.list_repos()?,
    })
}

pub(crate) fn repo_status(name: &str) -> AsrResult<RepoStatusOutput> {
    validate_repo_name(name)?;
    let (_paths, store) = open_store()?;
    let repo = require_repo(&store, name)?;
    let git_root = checked_git_root(&repo)?;
    let branch = git::current_branch(&git_root);
    let head_commit = git::head_commit(&git_root);
    let worktree = git::repository_status(&git_root, super::repo_is_bare(&repo))?;
    let status = status_text(head_commit.as_deref(), worktree.dirty);

    Ok(RepoStatusOutput {
        repo: repo.name.clone(),
        source_type: repo.source_type,
        local_path: repo.local_path,
        git_root: repo.git_root.clone(),
        branch,
        head_commit,
        dirty: worktree.dirty,
        untracked: worktree.untracked,
        modified: worktree.modified,
        worktree_fingerprint: worktree.worktree_fingerprint,
        status: status.to_string(),
        index_state: store.get_index_state(&repo.name)?,
    })
}