Skip to main content

bake_agent_context/agent/context/
mod.rs

1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4mod index;
5mod installer;
6
7pub use index::AgentIndex;
8pub use installer::{ContextFile, ContextPackage, Installer};
9
10use bake::{Context, Error, Result};
11
12fn installer(context: &mut Context) -> Result<Installer> {
13    if let Some(installer) = context.get::<Installer>() {
14        return Ok(installer.clone());
15    }
16
17    let installer = Installer::new(context.root())?;
18    context.insert(installer.clone());
19    Ok(installer)
20}
21
22/// List dependency crates that ship context, or list one crate's context files.
23#[bake::task]
24pub fn list(context: &mut Context, package: Option<String>) -> Result<String> {
25    let installer = installer(context)?;
26
27    if let Some(package) = package {
28        let Some(package) = installer.find_package(&package)? else {
29            return Ok(format!("No context found for crate '{package}'"));
30        };
31
32        let files = installer.list_context_files(&package)?;
33        let mut output = format!("Context files for crate '{}':", package.selector());
34        for file in files {
35            output.push_str(&format!("\n  {}", file.path.display()));
36        }
37        return Ok(output);
38    }
39
40    let packages = installer.packages();
41    if packages.is_empty() {
42        return Ok("No Cargo dependencies with context found".to_owned());
43    }
44
45    let mut output = String::from("Crates with context available:");
46    for package in packages {
47        output.push_str(&format!("\n  {} ({})", package.selector(), package.version));
48    }
49    Ok(output)
50}
51
52/// Show a context file from one resolved dependency crate.
53#[bake::task]
54pub fn show(
55    context: &mut Context,
56    #[bake(named)] package: String,
57    #[bake(named)] file: String,
58) -> Result<String> {
59    let installer = installer(context)?;
60    let Some(content) = installer.show_context_file(&package, &file)? else {
61        return Err(Error::new(format!(
62            "context file {file:?} was not found in crate {package:?}"
63        )));
64    };
65    Ok(content)
66}
67
68/// Install one crate's context or all dependency context, then update agents.md.
69#[bake::task]
70pub fn install(context: &mut Context, package: Option<String>) -> Result<String> {
71    let installer = installer(context)?;
72    let installed = if let Some(package) = package {
73        if installer.install_package(&package)? {
74            vec![package]
75        } else {
76            Vec::new()
77        }
78    } else {
79        installer.install_all()?
80    };
81
82    AgentIndex::new(context.root())
83        .with_packages(installer.packages())
84        .update_agents_md("agents.md")?;
85
86    if installed.is_empty() {
87        Ok("No dependency context was installed".to_owned())
88    } else {
89        Ok(format!("Installed context from: {}", installed.join(", ")))
90    }
91}
92
93/// Create or update the generated Context section in an agents.md file.
94#[bake::task(name = "agents-md")]
95pub fn agents_md(
96    context: &mut Context,
97    #[bake(default = "agents.md")] path: String,
98) -> Result<String> {
99    let installer = installer(context)?;
100    AgentIndex::new(context.root())
101        .with_packages(installer.packages())
102        .update_agents_md(&path)?;
103    Ok(format!("Updated {path}"))
104}