use crate::domain::packs::metadata::load_pack_metadata;
pub fn resolve_capability_to_packs(
surface: &str, projection: Option<&str>, runtime: Option<&str>,
) -> Result<Vec<String>, String> {
let pack_ids: Vec<&str> = match surface {
"mcp" => vec!["mcp-rust"],
"compliance-soc2" => vec!["enterprise-backend"],
"web" => match projection {
Some("fullstack" | "full-stack") => vec!["web-fullstack"],
_ => vec!["web-fullstack"],
},
"devops" => vec!["devops-automation"],
"data-science" => vec!["data-science-toolkit"],
"startup" => vec!["startup-essentials"],
"enterprise" | "enterprise-backend" => vec!["enterprise-backend"],
_ => {
return Err(format!(
"Unknown capability surface '{}'. \
Available surfaces: mcp, compliance-soc2, web, devops, \
data-science, startup, enterprise",
surface
));
}
};
let mut resolved: Vec<String> = Vec::with_capacity(pack_ids.len());
for &pack_id in &pack_ids {
match load_pack_metadata(pack_id) {
Ok(_) => {
tracing::debug!(pack_id = pack_id, "capability_registry: pack validated");
}
Err(e) => {
tracing::warn!(
pack_id = pack_id,
error = %e,
"capability_registry: pack not found locally (not yet installed)"
);
}
}
resolved.push(pack_id.to_string());
}
let _ = runtime; Ok(resolved)
}
#[derive(Debug, Clone)]
pub struct CapabilityDescriptor {
pub id: String,
pub name: String,
pub description: String,
pub category: String,
pub atomic_packs: Vec<String>,
}
pub fn list_capabilities() -> Vec<CapabilityDescriptor> {
let surfaces = [
(
"mcp",
"Model Context Protocol",
"MCP server and tooling for agent integration",
"surface",
),
(
"compliance-soc2",
"SOC2 Compliance",
"SOC2-aligned enterprise scaffolding",
"compliance",
),
(
"web",
"Web Full-Stack",
"Full-stack web application templates",
"surface",
),
(
"devops",
"DevOps Automation",
"CI/CD and infrastructure automation",
"infrastructure",
),
(
"data-science",
"Data Science Toolkit",
"ML/DS workflow templates",
"domain",
),
(
"startup",
"Startup Essentials",
"Startup project bootstrap kit",
"domain",
),
(
"enterprise-backend",
"Enterprise Backend",
"Production enterprise backend stack",
"domain",
),
];
surfaces
.iter()
.map(|(id, name, desc, cat)| {
let atomic_packs = resolve_capability_to_packs(id, None, None).unwrap_or_default();
CapabilityDescriptor {
id: (*id).to_string(),
name: (*name).to_string(),
description: (*desc).to_string(),
category: (*cat).to_string(),
atomic_packs,
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcp_resolves_to_mcp_rust() {
let result = resolve_capability_to_packs("mcp", Some("rust"), Some("axum"));
assert!(result.is_ok());
let packs = result.unwrap();
assert_eq!(packs, vec!["mcp-rust"]);
}
#[test]
fn test_mcp_projection_none_resolves_to_mcp_rust() {
let packs = resolve_capability_to_packs("mcp", None, None).unwrap();
assert_eq!(packs, vec!["mcp-rust"]);
}
#[test]
fn test_compliance_soc2_resolves_to_enterprise_backend() {
let packs = resolve_capability_to_packs("compliance-soc2", None, None).unwrap();
assert_eq!(packs, vec!["enterprise-backend"]);
}
#[test]
fn test_web_fullstack_resolves() {
let packs = resolve_capability_to_packs("web", Some("fullstack"), None).unwrap();
assert_eq!(packs, vec!["web-fullstack"]);
}
#[test]
fn test_devops_resolves() {
let packs = resolve_capability_to_packs("devops", None, None).unwrap();
assert_eq!(packs, vec!["devops-automation"]);
}
#[test]
fn test_data_science_resolves() {
let packs = resolve_capability_to_packs("data-science", None, None).unwrap();
assert_eq!(packs, vec!["data-science-toolkit"]);
}
#[test]
fn test_startup_resolves() {
let packs = resolve_capability_to_packs("startup", None, None).unwrap();
assert_eq!(packs, vec!["startup-essentials"]);
}
#[test]
fn test_unknown_surface_returns_error() {
let result = resolve_capability_to_packs("unknown-surface", None, None);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("unknown-surface"));
}
}