forge-pilot 0.1.0

Closed-loop orchestrator over runtime advisories, kernel oracles, and canonical Forge export/import lanes
Documentation
#[path = "repo_chat/answer.rs"]
mod answer;
#[path = "repo_chat/provider.rs"]
mod provider;
#[path = "repo_chat/retrieval.rs"]
mod retrieval;
#[path = "repo_chat/routing.rs"]
mod routing;
#[path = "repo_chat/types.rs"]
mod types;

use crate::config::LoopConfig;
use crate::observe::{observe_scope, Observation};
use crate::PilotError;
use knowledge_runtime::KnowledgeRuntime;
use semantic_memory::{MemoryStore, ProjectionQuery};

pub use provider::{
    provider_fallback_allowed, render_terminal_answer, repo_chat_provider_grounding_message,
};
pub use routing::route_question;
pub use types::{
    RepoChatAnswer, RepoChatCitation, RepoChatEvidence, RepoChatEvidenceType,
    RepoChatGroundingMode, RepoQuestionRoute,
};

pub async fn answer_repo_question(
    runtime: &KnowledgeRuntime,
    store: &MemoryStore,
    config: &LoopConfig,
    question: &str,
) -> Result<RepoChatAnswer, PilotError> {
    let observation = observe_scope(runtime, store, config).await?;
    answer_repo_question_from_observation(store, &observation, config, question).await
}

pub async fn answer_repo_question_from_observation(
    store: &MemoryStore,
    observation: &Observation,
    config: &LoopConfig,
    question: &str,
) -> Result<RepoChatAnswer, PilotError> {
    let route = routing::route_question(question);
    if route == RepoQuestionRoute::GeneralNonRepo {
        return Ok(answer::general_non_repo_answer(question));
    }

    if observation
        .source_inventory
        .imported_current_state
        .file_count
        == 0
    {
        return Ok(answer::missing_import_answer(route));
    }

    let Some(manifest) = retrieval::current_manifest_from_observation(observation) else {
        return Ok(answer::missing_manifest_answer(
            route,
            &observation.source_inventory.imported_current_state,
        ));
    };

    let mut query = ProjectionQuery::new(config.scope.key());
    query.limit = config.observation_projection_limit;
    let claim_versions = store.query_claim_versions(query).await?;
    let retrieval =
        retrieval::retrieve_repo_evidence(observation, &manifest, &claim_versions, question);
    Ok(answer::assemble_answer(
        observation,
        &manifest,
        question,
        retrieval,
    ))
}