cellstate_pipeline/pack/
agents_md.rs1use crate::compiler::CompiledConfig;
9
10pub const AGENTS_MD_SCHEMA_VERSION: &str = "cellstate.agents_md.v1";
12
13pub fn generate_agents_md(config: &CompiledConfig) -> String {
18 let mut out = String::new();
19
20 let project_name = config
22 .pack_meta_project
23 .as_deref()
24 .unwrap_or("CELLSTATE Agent Pack");
25 out.push_str(&format!("# {}\n\n", project_name));
26 out.push_str(&format!(
27 "<!-- schema_version: {} -->\n\n",
28 AGENTS_MD_SCHEMA_VERSION
29 ));
30
31 if let Some(ref desc) = config.pack_meta_description {
33 out.push_str(&format!("{}\n\n", desc));
34 }
35
36 if !config.pack_agents.is_empty() {
38 out.push_str("## Agents\n\n");
39 out.push_str("This project defines the following AI agents:\n\n");
40 for agent in &config.pack_agents {
41 if !agent.enabled {
42 continue;
43 }
44 let desc = agent
45 .description
46 .as_deref()
47 .unwrap_or("No description provided");
48 out.push_str(&format!("### {}\n\n", agent.name));
49 out.push_str(&format!("{}\n\n", desc));
50 out.push_str(&format!("- **Profile:** {}\n", agent.profile));
51 if let Some(budget) = agent.token_budget {
52 out.push_str(&format!("- **Token budget:** {}\n", budget));
53 }
54 if !agent.toolsets.is_empty() {
55 out.push_str(&format!("- **Toolsets:** {}\n", agent.toolsets.join(", ")));
56 }
57 if !agent.tags.is_empty() {
58 out.push_str(&format!("- **Tags:** {}\n", agent.tags.join(", ")));
59 }
60 out.push('\n');
61 }
62 }
63
64 if !config.tools.is_empty() {
66 out.push_str("## Tools\n\n");
67 out.push_str("Available tools in this pack:\n\n");
68 for tool in &config.tools {
69 out.push_str(&format!("- `{}` — {}\n", tool.id, tool_description(tool)));
70 }
71 out.push('\n');
72 }
73
74 if !config.toolsets.is_empty() {
76 out.push_str("## Toolsets\n\n");
77 for toolset in &config.toolsets {
78 out.push_str(&format!(
79 "- **{}:** {}\n",
80 toolset.name,
81 toolset.tools.join(", ")
82 ));
83 }
84 out.push('\n');
85 }
86
87 if let Some(ref instructions) = config.pack_meta_instructions {
89 out.push_str("## Instructions\n\n");
90 out.push_str(instructions);
91 if !instructions.ends_with('\n') {
92 out.push('\n');
93 }
94 out.push('\n');
95 }
96
97 if !config.pack_injections.is_empty() {
99 out.push_str("## Memory Configuration\n\n");
100 out.push_str("Context injection rules:\n\n");
101 for injection in &config.pack_injections {
102 out.push_str(&format!(
103 "- **{}** → {} (priority: {}, mode: {:?})\n",
104 injection.source, injection.target, injection.priority, injection.mode,
105 ));
106 }
107 out.push('\n');
108 }
109
110 out.push_str("---\n\n");
112 out.push_str(
113 "*Generated by CELLSTATE Pack. See [cellstate.batterypack.dev](https://cellstate.batterypack.dev) for details.*\n",
114 );
115
116 out
117}
118
119fn tool_description(tool: &crate::compiler::CompiledToolConfig) -> String {
121 match tool.kind {
122 crate::compiler::CompiledToolKind::Exec => {
123 format!(
124 "Executable: {}",
125 tool.cmd.as_deref().unwrap_or("unknown command")
126 )
127 }
128 crate::compiler::CompiledToolKind::WasmExec => {
129 format!(
130 "WASM executable: {}",
131 tool.cmd.as_deref().unwrap_or("unknown module")
132 )
133 }
134 crate::compiler::CompiledToolKind::Prompt => {
135 format!(
136 "Prompt tool ({})",
137 tool.prompt_md.as_deref().unwrap_or("unknown")
138 )
139 }
140 crate::compiler::CompiledToolKind::Bash => "Sandboxed bash execution".to_string(),
141 crate::compiler::CompiledToolKind::Browser => {
142 "Accessibility-first browser automation".to_string()
143 }
144 crate::compiler::CompiledToolKind::Composio => {
145 format!(
146 "Composio integration ({})",
147 tool.composio_toolkit.as_deref().unwrap_or("unknown")
148 )
149 }
150 crate::compiler::CompiledToolKind::ComposioGateway => {
151 "Composio tool discovery gateway".to_string()
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_generate_agents_md_empty_config() {
162 let config = CompiledConfig::default();
163 let md = generate_agents_md(&config);
164 assert!(md.contains("CELLSTATE Agent Pack"));
165 assert!(md.contains("schema_version: cellstate.agents_md.v1"));
166 assert!(md.contains("Generated by CELLSTATE Pack"));
167 }
168
169 #[test]
170 fn test_generate_agents_md_with_project_name() {
171 let config = CompiledConfig {
172 pack_meta_project: Some("My Research Bot".to_string()),
173 pack_meta_description: Some("A bot that researches things".to_string()),
174 ..CompiledConfig::default()
175 };
176 let md = generate_agents_md(&config);
177 assert!(md.starts_with("# My Research Bot"));
178 assert!(md.contains("A bot that researches things"));
179 }
180}