impl TurnHost {
async fn configure_skills(
&mut self,
config: &WireDaemonConfig,
applied: &mut WireDaemonConfig,
) {
if !config.skills.is_empty() || config.clears("skills") {
let provisioned: Vec<theway_core::Skill> = config
.skills
.iter()
.map(|skill| theway_core::Skill {
name: skill.name.clone(),
description: skill.description.clone(),
file_path: skill.file_path.clone(),
content: skill.content.clone(),
disable_model_invocation: skill.disable_model_invocation,
source: if skill.source == "project" {
theway_core::SkillSource::Project
} else {
theway_core::SkillSource::User
},
})
.collect();
let builtins: Vec<_> = self
.session
.kernel
.harness()
.skills()
.into_iter()
.filter(|skill| matches!(skill.source, theway_core::SkillSource::Builtin))
.collect();
let mut merged =
crate::builtin_skills::merge_with_user_project(builtins, &provisioned);
let overrides = crate::skill_overrides::load(&self.runtime.paths.base).await;
crate::skill_overrides::apply(&overrides, &mut merged);
self.session.kernel.harness().replace_skills(merged);
*write_lock(&self.runtime.provisioned_skills) = provisioned.clone();
if provisioned.is_empty() {
applied.clear_fields.push("skills".into());
} else {
applied.skills = config.skills.clone();
}
}
}
fn configure_templates(&mut self, config: &WireDaemonConfig, applied: &mut WireDaemonConfig) {
if !config.templates.is_empty() || config.clears("templates") {
let provisioned: Vec<theway_core::PromptTemplate> = config
.templates
.iter()
.map(|template| theway_core::PromptTemplate {
name: template.name.clone(),
description: if template.description.trim().is_empty() {
None
} else {
Some(template.description.clone())
},
content: template.content.clone(),
file_path: template.file_path.clone(),
})
.collect();
self.session.kernel.harness().replace_templates(provisioned.clone());
*write_lock(&self.runtime.provisioned_templates) = provisioned.clone();
if provisioned.is_empty() {
applied.clear_fields.push("templates".into());
} else {
applied.templates = config.templates.clone();
}
}
}
fn configure_builtin_skills(
&mut self,
config: &WireDaemonConfig,
applied: &mut WireDaemonConfig,
) {
if !config.builtin_skills.is_empty() || config.clears("builtin_skills") {
let requested = if config.clears("builtin_skills") && config.builtin_skills.is_empty() {
Vec::new()
} else {
config.builtin_skills.clone()
};
let resolved = crate::builtin_skills::resolve_builtins(&[], &requested)
.expect("an empty CLI list cannot produce a hard builtin error");
for diagnostic in resolved.diagnostics {
self.error_line(diagnostic);
}
let enabled: Vec<String> = resolved
.skills
.iter()
.map(|skill| skill.name.clone())
.collect();
let non_builtin: Vec<_> = self
.session
.kernel
.harness()
.skills()
.into_iter()
.filter(|skill| !matches!(skill.source, theway_core::SkillSource::Builtin))
.collect();
self.session.kernel
.harness()
.replace_skills(crate::builtin_skills::merge_with_user_project(
resolved.skills,
&non_builtin,
));
if enabled.is_empty() {
applied.clear_fields.push("builtin_skills".into());
} else {
applied.builtin_skills = enabled;
}
}
}
async fn configure_skills_dirs(
&mut self,
config: &WireDaemonConfig,
turn: &mut TurnState,
applied: &mut WireDaemonConfig,
) {
if !config.skills_dirs.is_empty() || config.clears("skills_dirs") {
let dirs = if config.skills_dirs.is_empty() {
Vec::new()
} else {
config.skills_dirs.clone()
};
self.handle_set_skill_dirs(dirs, turn).await;
let actual = read_lock(&self.runtime.path_context).skills_dirs.clone();
if actual.is_empty() {
applied.clear_fields.push("skills_dirs".into());
} else {
applied.skills_dirs = actual;
}
}
}
}