mod exclude;
mod index;
mod installer;
mod skill;
pub use index::ContextIndex;
pub use installer::{ContextFile, ContextPackage, Installer};
pub use skill::{Skill, install_skills, list_skills};
use bake::{Context, Error, Result};
fn installer(context: &mut Context) -> Result<Installer> {
if let Some(installer) = context.get::<Installer>() {
return Ok(installer.clone());
}
let installer = Installer::new(context.root())?;
context.insert(installer.clone());
Ok(installer)
}
#[bake::task]
pub fn list(context: &mut Context, package: Option<String>) -> Result<String> {
let installer = installer(context)?;
if let Some(package) = package {
let Some(package) = installer.find_package(&package)? else {
return Ok(format!("No context found for crate '{package}'"));
};
let files = installer.list_context_files(&package)?;
if files.is_empty() {
return Ok(format!(
"No context files found for crate '{}'.",
package.selector()
));
}
let mut output = format!("Context files for crate '{}':", package.selector());
for file in files {
output.push_str(&format!("\n {}", file.path.display()));
}
return Ok(output);
}
let mut packages = Vec::new();
for package in installer.packages() {
if !installer.list_context_files(package)?.is_empty() {
packages.push(package);
}
}
if packages.is_empty() {
return Ok("No dependency context files found".to_owned());
}
let mut output = String::from("Crates with context available:");
for package in packages {
output.push_str(&format!("\n {} ({})", package.selector(), package.version));
}
Ok(output)
}
#[bake::task]
pub fn show(
context: &mut Context,
#[bake(named)] package: String,
#[bake(named)] file: String,
) -> Result<String> {
let installer = installer(context)?;
let Some(content) = installer.show_context_file(&package, &file)? else {
return Err(Error::new(format!(
"context file {file:?} was not found in crate {package:?}"
)));
};
Ok(content)
}
#[bake::task]
pub fn install(context: &mut Context, package: Option<String>) -> Result<String> {
let installer = installer(context)?;
let installed_context = if let Some(package) = package.as_deref() {
if installer.install_package(package)? {
vec![package.to_owned()]
} else {
Vec::new()
}
} else {
installer.install_all()?
};
let installed_skills = install_skills(&installer, package.as_deref(), None)?;
ContextIndex::new(context.root())
.with_packages(installer.packages())
.update_index()?;
let mut output = Vec::new();
if !installed_context.is_empty() {
output.push(format!(
"Installed context from: {}",
installed_context.join(", ")
));
}
if !installed_skills.is_empty() {
output.push(format!("Installed skills: {}", installed_skills.join(", ")));
}
if output.is_empty() {
Ok("No dependency context or skills were installed".to_owned())
} else {
Ok(output.join("\n"))
}
}
#[bake::task(name = "agent:context:skill:list")]
pub fn skill_list(context: &mut Context, package: Option<String>) -> Result<String> {
let installer = installer(context)?;
let skills = list_skills(&installer, package.as_deref())?;
if skills.is_empty() {
return Ok("No dependency skills found".to_owned());
}
let mut output = String::from("Available dependency skills:");
for skill in skills {
output.push_str(&format!(
"\n {} ({}) — {}",
skill.name,
skill.package_selector(),
skill.description
));
}
Ok(output)
}
#[bake::task(name = "agent:context:skill:install")]
pub fn skill_install(
context: &mut Context,
package: Option<String>,
skill: Option<String>,
) -> Result<String> {
let installer = installer(context)?;
let installed = install_skills(&installer, package.as_deref(), skill.as_deref())?;
if installed.is_empty() {
Ok("No dependency skills were installed".to_owned())
} else {
Ok(format!("Installed skills: {}", installed.join(", ")))
}
}
#[bake::task(name = "agent:context:index")]
pub fn index(context: &mut Context) -> Result<String> {
let installer = installer(context)?;
ContextIndex::new(context.root())
.with_packages(installer.packages())
.update_index()?;
Ok("Updated .agents/context/index.md".to_owned())
}