Codex Handbook
core/src/skills.rs 108 lines
use crate::config::Config;use crate::session::session::Session;use crate::session::turn_context::TurnContext;use codex_analytics::InvocationType;use codex_analytics::SkillInvocation;use codex_analytics::build_track_events_context;use codex_protocol::protocol::SkillScope;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_plugins::PluginSkillRoot;pub use codex_core_skills::SkillError;pub use codex_core_skills::SkillLoadOutcome;pub use codex_core_skills::SkillMetadata;pub use codex_core_skills::SkillPolicy;pub use codex_core_skills::SkillRenderReport;pub use codex_core_skills::SkillsLoadInput;pub use codex_core_skills::SkillsManager;pub use codex_core_skills::build_available_skills;pub use codex_core_skills::build_skill_name_counts;pub use codex_core_skills::config_rules;pub use codex_core_skills::default_skill_metadata_budget;pub use codex_core_skills::detect_implicit_skill_invocation_for_command;pub use codex_core_skills::filter_skill_load_outcome_for_product;pub use codex_core_skills::injection;pub use codex_core_skills::injection::SkillInjections;pub use codex_core_skills::injection::build_skill_injections;pub use codex_core_skills::injection::collect_explicit_skill_mentions;pub use codex_core_skills::loader;pub use codex_core_skills::manager;pub use codex_core_skills::model;pub use codex_core_skills::remote;pub use codex_core_skills::render;pub use codex_core_skills::render::SkillRenderSideEffects;pub use codex_core_skills::system;pub(crate) fn skills_load_input_from_config(    config: &Config,    effective_skill_roots: Vec<PluginSkillRoot>,) -> SkillsLoadInput {    SkillsLoadInput::new(        config.cwd.clone(),        effective_skill_roots,        config.config_layer_stack.clone(),        config.bundled_skills_enabled(),    )}pub(crate) async fn maybe_emit_implicit_skill_invocation(    sess: &Session,    turn_context: &TurnContext,    command: &str,    workdir: &AbsolutePathBuf,) {    let Some(candidate) = detect_implicit_skill_invocation_for_command(        turn_context.turn_skills.outcome.as_ref(),        command,        workdir,    ) else {        return;    };    let invocation = SkillInvocation {        skill_name: candidate.name,        skill_scope: candidate.scope,        skill_path: candidate.path_to_skills_md.to_path_buf(),        plugin_id: candidate.plugin_id,        invocation_type: InvocationType::Implicit,    };    let skill_scope = match invocation.skill_scope {        SkillScope::User => "user",        SkillScope::Repo => "repo",        SkillScope::System => "system",        SkillScope::Admin => "admin",    };    let skill_path = invocation.skill_path.to_string_lossy();    let skill_name = invocation.skill_name.clone();    let seen_key = format!("{skill_scope}:{skill_path}:{skill_name}");    let inserted = {        let mut seen_skills = turn_context            .turn_skills            .implicit_invocation_seen_skills            .lock()            .await;        seen_skills.insert(seen_key)    };    if !inserted {        return;    }    turn_context.session_telemetry.counter(        "codex.skill.injected",        /*inc*/ 1,        &[            ("status", "ok"),            ("skill", skill_name.as_str()),            ("invoke_type", "implicit"),        ],    );    sess.services        .analytics_events_client        .track_skill_invocations(            build_track_events_context(                turn_context.model_info.slug.clone(),                sess.thread_id.to_string(),                turn_context.sub_id.clone(),            ),            vec![invocation],        );}