mirror of
https://github.com/openai/codex.git
synced 2026-09-16 12:13:30 +00:00
Support symlinked SKILL.md files
This commit is contained in:
@@ -282,6 +282,10 @@ fn discover_skills_under_root(root: &Path, scope: SkillScope, outcome: &mut Skil
|
||||
continue;
|
||||
}
|
||||
|
||||
if metadata.is_file() && file_name == SKILLS_FILENAME {
|
||||
record_skill_from_path(&path, scope, outcome);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -300,19 +304,7 @@ fn discover_skills_under_root(root: &Path, scope: SkillScope, outcome: &mut Skil
|
||||
}
|
||||
|
||||
if file_type.is_file() && file_name == SKILLS_FILENAME {
|
||||
match parse_skill_file(&path, scope) {
|
||||
Ok(skill) => {
|
||||
outcome.skills.push(skill);
|
||||
}
|
||||
Err(err) => {
|
||||
if scope != SkillScope::System {
|
||||
outcome.errors.push(SkillError {
|
||||
path,
|
||||
message: err.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
record_skill_from_path(&path, scope, outcome);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,6 +318,22 @@ fn discover_skills_under_root(root: &Path, scope: SkillScope, outcome: &mut Skil
|
||||
}
|
||||
}
|
||||
|
||||
fn record_skill_from_path(path: &Path, scope: SkillScope, outcome: &mut SkillLoadOutcome) {
|
||||
match parse_skill_file(path, scope) {
|
||||
Ok(skill) => {
|
||||
outcome.skills.push(skill);
|
||||
}
|
||||
Err(err) => {
|
||||
if scope != SkillScope::System {
|
||||
outcome.errors.push(SkillError {
|
||||
path: path.to_path_buf(),
|
||||
message: err.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, SkillParseError> {
|
||||
let contents = fs::read_to_string(path).map_err(SkillParseError::Read)?;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::Path;
|
||||
|
||||
fn write_skill(home: &Path, name: &str, description: &str, body: &str) -> std::path::PathBuf {
|
||||
@@ -154,6 +155,57 @@ async fn skill_load_errors_surface_in_session_configured() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn list_skills_includes_symlinked_skill_md() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let mut builder = test_codex().with_pre_build_hook(|home| {
|
||||
let external_dir = home.join("external-skills").join("demo-symlink");
|
||||
fs::create_dir_all(&external_dir).unwrap();
|
||||
let contents = "---\nname: demo-symlink\ndescription: demo skill\n---\n\nbody\n";
|
||||
let external_skill_path = external_dir.join("SKILL.md");
|
||||
fs::write(&external_skill_path, contents).unwrap();
|
||||
|
||||
let skill_dir = home.join("skills").join("demo-symlink");
|
||||
fs::create_dir_all(&skill_dir).unwrap();
|
||||
symlink(&external_skill_path, skill_dir.join("SKILL.md")).unwrap();
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
test.codex
|
||||
.submit(Op::ListSkills {
|
||||
cwds: Vec::new(),
|
||||
force_reload: true,
|
||||
})
|
||||
.await?;
|
||||
let response =
|
||||
core_test_support::wait_for_event_match(test.codex.as_ref(), |event| match event {
|
||||
codex_core::protocol::EventMsg::ListSkillsResponse(response) => Some(response.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
|
||||
let cwd = test.cwd_path();
|
||||
let (skills, errors) = response
|
||||
.skills
|
||||
.iter()
|
||||
.find(|entry| entry.cwd.as_path() == cwd)
|
||||
.map(|entry| (entry.skills.clone(), entry.errors.clone()))
|
||||
.unwrap_or_default();
|
||||
|
||||
assert!(
|
||||
errors.is_empty(),
|
||||
"expected no load errors for symlinked skill, got {errors:?}"
|
||||
);
|
||||
assert!(
|
||||
skills.iter().any(|skill| skill.name == "demo-symlink"),
|
||||
"expected symlinked skill to be listed, got {skills:?}"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn list_skills_includes_system_cache_entries() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
Reference in New Issue
Block a user