mirror of
https://github.com/openai/codex.git
synced 2026-05-02 02:17:22 +00:00
Make skill loading filesystem-aware (#17720)
Migrates skill loading to support reading repo skills from the remote environment.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Resolve plugin namespace from skill file paths by walking ancestors for `plugin.json`.
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use codex_exec_server::ExecutorFileSystem;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
/// Relative path from a plugin root to its manifest file.
|
||||
pub const PLUGIN_MANIFEST_PATH: &str = ".codex-plugin/plugin.json";
|
||||
@@ -13,12 +13,19 @@ struct RawPluginManifestName {
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn plugin_manifest_name(plugin_root: &Path) -> Option<String> {
|
||||
async fn plugin_manifest_name(
|
||||
fs: &dyn ExecutorFileSystem,
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
) -> Option<String> {
|
||||
let manifest_path = plugin_root.join(PLUGIN_MANIFEST_PATH);
|
||||
if !manifest_path.is_file() {
|
||||
return None;
|
||||
match fs.get_metadata(&manifest_path, /*sandbox*/ None).await {
|
||||
Ok(metadata) if metadata.is_file => {}
|
||||
Ok(_) | Err(_) => return None,
|
||||
}
|
||||
let contents = fs::read_to_string(&manifest_path).ok()?;
|
||||
let contents = fs
|
||||
.read_file_text(&manifest_path, /*sandbox*/ None)
|
||||
.await
|
||||
.ok()?;
|
||||
let RawPluginManifestName { name: raw_name } = serde_json::from_str(&contents).ok()?;
|
||||
Some(
|
||||
plugin_root
|
||||
@@ -32,9 +39,12 @@ fn plugin_manifest_name(plugin_root: &Path) -> Option<String> {
|
||||
|
||||
/// Returns the plugin manifest `name` for the nearest ancestor of `path` that contains a valid
|
||||
/// plugin manifest (same `name` rules as full manifest loading in codex-core).
|
||||
pub fn plugin_namespace_for_skill_path(path: &Path) -> Option<String> {
|
||||
pub async fn plugin_namespace_for_skill_path(
|
||||
fs: &dyn ExecutorFileSystem,
|
||||
path: &AbsolutePathBuf,
|
||||
) -> Option<String> {
|
||||
for ancestor in path.ancestors() {
|
||||
if let Some(name) = plugin_manifest_name(ancestor) {
|
||||
if let Some(name) = plugin_manifest_name(fs, &ancestor).await {
|
||||
return Some(name);
|
||||
}
|
||||
}
|
||||
@@ -44,11 +54,13 @@ pub fn plugin_namespace_for_skill_path(path: &Path) -> Option<String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::plugin_namespace_for_skill_path;
|
||||
use codex_exec_server::LOCAL_FS;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use std::fs;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn uses_manifest_name() {
|
||||
#[tokio::test]
|
||||
async fn uses_manifest_name() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let plugin_root = tmp.path().join("plugins/sample");
|
||||
let skill_path = plugin_root.join("skills/search/SKILL.md");
|
||||
@@ -63,7 +75,7 @@ mod tests {
|
||||
fs::write(&skill_path, "---\ndescription: search\n---\n").expect("write skill");
|
||||
|
||||
assert_eq!(
|
||||
plugin_namespace_for_skill_path(&skill_path),
|
||||
plugin_namespace_for_skill_path(LOCAL_FS.as_ref(), &skill_path.abs()).await,
|
||||
Some("sample".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user