Skip to main content

skilllite_core/
env_spec.rs

1//! Minimal environment spec for sandbox env building.
2//!
3//! Sandbox does not depend on `skill::metadata`; callers (e.g. commands) build
4//! `EnvSpec` from `SkillMetadata` and pass it to `ensure_environment`.
5
6use std::path::Path;
7
8use crate::skill::metadata;
9
10/// Minimal input for building an isolated runtime environment (venv / node_modules).
11///
12/// Built from `SkillMetadata` by the caller so that the sandbox crate does not
13/// depend on skill parsing.
14#[derive(Debug, Clone)]
15pub struct EnvSpec {
16    /// Resolved language: "python", "node", or "bash"
17    pub language: String,
18    /// Skill name (for cache key / logging)
19    pub name: Option<String>,
20    /// Raw compatibility string (for bash+agent-browser detection, etc.)
21    pub compatibility: Option<String>,
22    /// Resolved package list (from .skilllite.lock or parsed compatibility)
23    pub resolved_packages: Option<Vec<String>>,
24}
25
26impl EnvSpec {
27    /// Build an env spec from skill metadata. Uses `metadata::detect_language`.
28    pub fn from_metadata(skill_dir: &Path, meta: &metadata::SkillMetadata) -> Self {
29        let language = metadata::detect_language(skill_dir, meta);
30        let language = if language == "bash" {
31            let has_pkg = skill_dir.join("package.json").exists();
32            let compat_has_agent_browser = meta
33                .compatibility
34                .as_ref()
35                .is_some_and(|c| c.to_lowercase().contains("agent-browser"));
36            let resolved_has_agent_browser = meta
37                .resolved_packages
38                .as_ref()
39                .is_some_and(|p| p.iter().any(|s| s.contains("agent-browser")));
40            if has_pkg || compat_has_agent_browser || resolved_has_agent_browser {
41                "node".to_string()
42            } else {
43                language
44            }
45        } else {
46            language
47        };
48
49        Self {
50            language,
51            name: Some(meta.name.clone()),
52            compatibility: meta.compatibility.clone(),
53            resolved_packages: meta.resolved_packages.clone(),
54        }
55    }
56}