use std::path::{Path, PathBuf};
pub struct SccEngine {
root: PathBuf,
config: scc_indexer::Config,
store: scc_store::Store,
}
impl SccEngine {
pub fn open(root: impl AsRef<Path>) -> crate::Result<Self> {
let root = root.as_ref().to_path_buf();
let config = crate::workspace::load_config(&root)?;
let store = crate::workspace::open_store(&root)?;
Ok(SccEngine { root, config, store })
}
pub fn root(&self) -> &Path {
&self.root
}
pub fn invoke(
&self,
operation: &str,
input: serde_json::Value,
) -> crate::Result<serde_json::Value> {
crate::invoke::invoke(&self.root, operation, input)
}
pub fn session(&self) -> crate::Result<crate::workspace::Session> {
crate::workspace::open_session(&self.store, &self.config)
}
pub fn invoke_session(
&self,
session: &crate::workspace::Session,
operation: &str,
input: serde_json::Value,
) -> crate::Result<serde_json::Value> {
self.with_engine(|engine| engine.invoke_session(session, operation, input))
}
pub fn refresh(&mut self) -> crate::Result<()> {
self.store = crate::workspace::open_store(&self.root)?;
self.config = crate::workspace::load_config(&self.root)?;
Ok(())
}
pub fn with_engine<T>(
&self,
f: impl FnOnce(crate::workspace::Engine<'_>) -> crate::Result<T>,
) -> crate::Result<T> {
let engine = crate::workspace::open_engine(
&self.store,
&self.config,
crate::workspace::stale_paths(&self.store)?,
)?;
f(engine)
}
pub fn workspace_status(&self) -> crate::Result<crate::status::Status> {
crate::status::status(&self.store)
}
pub fn index_full(&self) -> crate::Result<scc_indexer::IndexReport> {
let report = crate::index::full(&self.root, &self.config)?;
self.refresh_store_only()?;
Ok(report)
}
pub fn index_paths(&self, paths: &[String]) -> crate::Result<scc_indexer::IndexReport> {
let report = crate::index::refresh_paths(&self.root, &self.config, paths)?;
self.refresh_store_only()?;
Ok(report)
}
fn refresh_store_only(&self) -> crate::Result<()> {
Ok(())
}
pub fn graph_entity(&self, id: &str) -> crate::Result<Option<scc_core::Entity>> {
Ok(self.store.get_entity(id)?)
}
pub fn graph_relationships(
&self,
subject: Option<&str>,
predicate: Option<&str>,
limit: usize,
) -> crate::Result<Vec<scc_core::Relationship>> {
crate::graph::relationships(&self.store, subject, predicate, limit)
}
pub fn evidence_list(
&self,
path: Option<&str>,
limit: usize,
) -> crate::Result<Vec<scc_core::Evidence>> {
let all = self.store.all_evidence()?;
Ok(all
.into_iter()
.filter(|e| path.is_none_or(|p| e.path.as_deref().is_some_and(|ep| ep.contains(p))))
.take(limit.max(1))
.collect())
}
pub fn model(&self) -> crate::Result<serde_json::Value> {
crate::exports::model_get(&self.store)
}
pub fn history(&self) -> crate::Result<Vec<scc_store::history::GraphRevision>> {
crate::history::revisions(&self.store)
}
pub fn history_diff(
&self,
from: i64,
to: i64,
) -> crate::Result<scc_store::history::SemanticDelta> {
crate::history::diff(&self.store, from, to)
}
pub fn runtime_status(&self) -> crate::Result<Vec<scc_indexer::runtime::RuntimeEdge>> {
crate::state::runtime_edges(&self.root)
}
pub fn runtime_reconcile(&self) -> crate::Result<scc_indexer::runtime::Reconciliation> {
crate::state::reconcile(&self.root)
}
pub fn surface_build(
&self,
task: &str,
budget: usize,
explain: bool,
) -> crate::Result<(scc_core::SurfaceRenderResult, String)> {
self.with_engine(|engine| {
let req = scc_api::SurfaceRequest {
task: Some(task.to_string()),
budget: Some(budget),
explain,
stages: None,
};
let (scorer, _) = crate::inference::rankers(engine.store, &self.config, task);
let semantic: Option<&dyn scc_context::rank::SemanticScorer> =
scorer.as_ref().map(|s| s as &dyn scc_context::rank::SemanticScorer);
engine.context().surface(&req, semantic)
})
}
pub fn ranking_symbols(
&self,
goal: &str,
limit: usize,
explain: bool,
) -> crate::Result<scc_api::RankResult> {
self.with_engine(|engine| {
engine.ranking().symbols(&scc_api::RankRequest {
goal: Some(goal.to_string()),
profile: None,
limit,
explain,
include_features: false,
include_intermediate: false,
})
})
}
pub fn context_task(
&self,
goal: &str,
budget: usize,
) -> crate::Result<crate::task::TaskContextArtifact> {
self.with_engine(|engine| {
let req = scc_api::TaskContextRequest {
goal: goal.to_string(),
files: vec![],
symbols: vec![],
budget: Some(budget),
hook: false,
record_visibility: true,
};
let (scorer, reranker) = crate::inference::rankers(engine.store, &self.config, goal);
let scorer_trait: Option<&dyn scc_context::rank::SemanticScorer> =
scorer.as_ref().map(|s| s as &dyn scc_context::rank::SemanticScorer);
let reranker_trait: Option<&dyn scc_context::rank::Reranker> =
reranker.as_ref().map(|r| r as &dyn scc_context::rank::Reranker);
crate::task::build_task_context(&engine, &self.config, &self.root, &req, scorer_trait, reranker_trait)
})
}
pub fn context_atlas(&self, budget: usize) -> crate::Result<scc_context::ContextPack> {
self.with_engine(|engine| engine.context().atlas(Some(budget), false, false))
}
pub fn context_ns(&self) -> ContextNs<'_> {
ContextNs { engine: self }
}
pub fn surface_ns(&self) -> SurfaceNs<'_> {
SurfaceNs { engine: self }
}
pub fn ranking_ns(&self) -> RankingNs<'_> {
RankingNs { engine: self }
}
pub fn graph_ns(&self) -> GraphNs<'_> {
GraphNs { engine: self }
}
}
pub struct ContextNs<'a> {
engine: &'a SccEngine,
}
impl ContextNs<'_> {
pub fn overview(&self) -> crate::Result<scc_context::ContextPack> {
self.engine.with_engine(|e| e.context().overview())
}
pub fn atlas(&self, budget: usize) -> crate::Result<scc_context::ContextPack> {
self.engine.context_atlas(budget)
}
pub fn atlas_model(
&self,
scope: scc_context::atlas::AtlasScope,
) -> crate::Result<scc_core::SystemAtlas> {
self.engine.with_engine(|e| e.context().atlas_model(scope))
}
pub fn task(
&self,
goal: &str,
budget: usize,
) -> crate::Result<crate::task::TaskContextArtifact> {
self.engine.context_task(goal, budget)
}
}
pub struct SurfaceNs<'a> {
engine: &'a SccEngine,
}
impl SurfaceNs<'_> {
pub fn build(
&self,
task: &str,
budget: usize,
explain: bool,
) -> crate::Result<(scc_core::SurfaceRenderResult, String)> {
self.engine.surface_build(task, budget, explain)
}
}
pub struct RankingNs<'a> {
engine: &'a SccEngine,
}
impl RankingNs<'_> {
pub fn symbols(
&self,
goal: &str,
limit: usize,
explain: bool,
) -> crate::Result<scc_api::RankResult> {
self.engine.ranking_symbols(goal, limit, explain)
}
}
pub struct GraphNs<'a> {
engine: &'a SccEngine,
}
impl GraphNs<'_> {
pub fn entity(&self, id: &str) -> crate::Result<Option<scc_core::Entity>> {
self.engine.graph_entity(id)
}
pub fn relationships(
&self,
subject: Option<&str>,
predicate: Option<&str>,
limit: usize,
) -> crate::Result<Vec<scc_core::Relationship>> {
self.engine.graph_relationships(subject, predicate, limit)
}
pub fn query(
&self,
query: &str,
limit: usize,
) -> crate::Result<crate::graph::QueryHit> {
self.engine.with_engine(|_| {
crate::graph::query(
&self.engine.store,
&scc_api::QueryRequest { query: query.to_string(), limit },
)
})
}
}