diff --git a/codex-rs/core-skills/src/render.rs b/codex-rs/core-skills/src/render.rs index 9089a05436e8..59cac8348867 100644 --- a/codex-rs/core-skills/src/render.rs +++ b/codex-rs/core-skills/src/render.rs @@ -25,7 +25,7 @@ pub fn render_skills_section(skills: &[SkillMetadata]) -> Option { - Trigger rules: If the user names a skill (with `$SkillName` or plain text) OR the task clearly matches a skill's description shown above, you must use that skill for that turn. Multiple mentions mean use them all. Do not carry skills across turns unless re-mentioned. - Missing/blocked: If a named skill isn't in the list or the path can't be read, say so briefly and continue with the best fallback. - How to use a skill (progressive disclosure): - 1) After deciding to use a skill, open its `SKILL.md`. Read only enough to follow the workflow. + 1) After deciding to use a skill, open and read its `SKILL.md` in full. This is the primary workflow definition. Then load additional resources (e.g. `references/`, `scripts/`, `assets/`) progressively as needed. 2) When `SKILL.md` references relative paths (e.g., `scripts/foo.py`), resolve them relative to the skill directory listed above first, and only consider other paths if needed. 3) If `SKILL.md` points to extra folders such as `references/`, load only the specific files needed for the request; don't bulk-load everything. 4) If `scripts/` exist, prefer running or patching them instead of retyping large code blocks. @@ -46,3 +46,32 @@ pub fn render_skills_section(skills: &[SkillMetadata]) -> Option { "{SKILLS_INSTRUCTIONS_OPEN_TAG}\n{body}\n{SKILLS_INSTRUCTIONS_CLOSE_TAG}" )) } + +#[cfg(test)] +mod tests { + use super::render_skills_section; + use crate::model::SkillMetadata; + use codex_protocol::protocol::SkillScope; + use std::path::PathBuf; + + #[test] + fn renders_full_skill_read_instruction_before_secondary_resources() { + let skills = vec![SkillMetadata { + name: "demo".to_string(), + description: "Demo skill".to_string(), + short_description: None, + interface: None, + dependencies: None, + policy: None, + path_to_skills_md: PathBuf::from("/tmp/demo/SKILL.md"), + scope: SkillScope::User, + }]; + + let rendered = render_skills_section(&skills).expect("skills section should render"); + + assert!(rendered.contains( + "1) After deciding to use a skill, open and read its `SKILL.md` in full. This is the primary workflow definition. Then load additional resources (e.g. `references/`, `scripts/`, `assets/`) progressively as needed." + )); + assert!(!rendered.contains("Read only enough to follow the workflow.")); + } +}