This commit is contained in:
Ahmed Ibrahim
2026-03-25 01:22:38 -07:00
parent 6ac900908b
commit 21a359eee8
13 changed files with 26 additions and 92 deletions

14
codex-rs/Cargo.lock generated
View File

@@ -1926,6 +1926,7 @@ dependencies = [
"codex-utils-image",
"codex-utils-output-truncation",
"codex-utils-path",
"codex-utils-plugins",
"codex-utils-pty",
"codex-utils-readiness",
"codex-utils-stream-parser",
@@ -2004,10 +2005,10 @@ dependencies = [
"codex-instructions",
"codex-login",
"codex-otel",
"codex-plugin",
"codex-protocol",
"codex-skills",
"codex-utils-absolute-path",
"codex-utils-plugins",
"dirs",
"dunce",
"pretty_assertions",
@@ -2449,9 +2450,9 @@ name = "codex-plugin"
version = "0.0.0"
dependencies = [
"codex-utils-absolute-path",
"codex-utils-plugins",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
]
@@ -3009,6 +3010,15 @@ dependencies = [
"tempfile",
]
[[package]]
name = "codex-utils-plugins"
version = "0.0.0"
dependencies = [
"serde",
"serde_json",
"tempfile",
]
[[package]]
name = "codex-utils-pty"
version = "0.0.0"

View File

@@ -71,6 +71,7 @@ members = [
"utils/oss",
"utils/output-truncation",
"utils/path-utils",
"utils/plugins",
"utils/fuzzy-match",
"utils/stream-parser",
"codex-client",
@@ -168,6 +169,7 @@ codex-utils-json-to-toml = { path = "utils/json-to-toml" }
codex-utils-oss = { path = "utils/oss" }
codex-utils-output-truncation = { path = "utils/output-truncation" }
codex-utils-path = { path = "utils/path-utils" }
codex-utils-plugins = { path = "utils/plugins" }
codex-utils-pty = { path = "utils/pty" }
codex-utils-readiness = { path = "utils/readiness" }
codex-utils-rustls-provider = { path = "utils/rustls-provider" }

View File

@@ -20,10 +20,10 @@ codex-config = { workspace = true }
codex-instructions = { workspace = true }
codex-login = { workspace = true }
codex-otel = { workspace = true }
codex-plugin = { workspace = true }
codex-protocol = { workspace = true }
codex-skills = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-plugins = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
reqwest = { workspace = true, features = ["json", "stream"] }

View File

@@ -10,7 +10,7 @@ use codex_analytics::SkillInvocation;
use codex_analytics::TrackEventsContext;
use codex_instructions::SkillInstructions;
use codex_otel::SessionTelemetry;
use codex_plugin::mention_syntax::TOOL_MENTION_SIGIL;
use codex_utils_plugins::mention_syntax::TOOL_MENTION_SIGIL;
use codex_protocol::models::ResponseItem;
use codex_protocol::user_input::UserInput;
use tokio::fs;

View File

@@ -13,7 +13,7 @@ use codex_config::ConfigLayerStackOrdering;
use codex_config::default_project_root_markers;
use codex_config::merge_toml_values;
use codex_config::project_root_markers_from_config;
use codex_plugin::plugin_namespace_for_skill_path;
use codex_utils_plugins::plugin_namespace_for_skill_path;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
use codex_protocol::models::NetworkPermissions;

View File

@@ -61,6 +61,7 @@ codex-utils-image = { workspace = true }
codex-utils-home-dir = { workspace = true }
codex-utils-output-truncation = { workspace = true }
codex-utils-path = { workspace = true }
codex-utils-plugins = { workspace = true }
codex-utils-pty = { workspace = true }
codex-utils-readiness = { workspace = true }
codex-secrets = { workspace = true }

View File

@@ -1,2 +1,2 @@
pub use codex_plugin::mention_syntax::PLUGIN_TEXT_MENTION_SIGIL;
pub use codex_plugin::mention_syntax::TOOL_MENTION_SIGIL;
pub use codex_utils_plugins::mention_syntax::PLUGIN_TEXT_MENTION_SIGIL;
pub use codex_utils_plugins::mention_syntax::TOOL_MENTION_SIGIL;

View File

@@ -1,4 +1,4 @@
use codex_plugin::PLUGIN_MANIFEST_PATH;
use codex_utils_plugins::PLUGIN_MANIFEST_PATH;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde::Deserialize;
use serde_json::Value as JsonValue;

View File

@@ -1,5 +1,5 @@
use super::load_plugin_manifest;
use codex_plugin::PLUGIN_MANIFEST_PATH;
use codex_utils_plugins::PLUGIN_MANIFEST_PATH;
use codex_plugin::PluginId;
use codex_plugin::validate_plugin_segment;
use codex_utils_absolute_path::AbsolutePathBuf;

View File

@@ -14,9 +14,8 @@ workspace = true
[dependencies]
codex-utils-absolute-path = { workspace = true }
codex-utils-plugins = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }

View File

@@ -1,10 +1,11 @@
//! Shared plugin identifiers and telemetry-facing summaries.
pub mod mention_syntax;
pub use codex_utils_plugins::mention_syntax;
pub use codex_utils_plugins::PLUGIN_MANIFEST_PATH;
pub use codex_utils_plugins::plugin_namespace_for_skill_path;
mod load_outcome;
mod plugin_id;
mod plugin_namespace;
pub use load_outcome::EffectiveSkillRoots;
pub use load_outcome::LoadedPlugin;
@@ -13,8 +14,6 @@ pub use load_outcome::prompt_safe_plugin_description;
pub use plugin_id::PluginId;
pub use plugin_id::PluginIdError;
pub use plugin_id::validate_plugin_segment;
pub use plugin_namespace::PLUGIN_MANIFEST_PATH;
pub use plugin_namespace::plugin_namespace_for_skill_path;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AppConnectorId(pub String);

View File

@@ -1,7 +0,0 @@
//! Sigils for tool/plugin mentions in plaintext (shared across Codex crates).
/// Default plaintext sigil for tools.
pub const TOOL_MENTION_SIGIL: char = '$';
/// Plugins use `@` in linked plaintext outside TUI.
pub const PLUGIN_TEXT_MENTION_SIGIL: char = '@';

View File

@@ -1,70 +0,0 @@
//! Resolve plugin namespace from skill file paths by walking ancestors for `plugin.json`.
use std::fs;
use std::path::Path;
/// Relative path from a plugin root to its manifest file.
pub const PLUGIN_MANIFEST_PATH: &str = ".codex-plugin/plugin.json";
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawPluginManifestName {
#[serde(default)]
name: String,
}
fn plugin_manifest_name(plugin_root: &Path) -> Option<String> {
let manifest_path = plugin_root.join(PLUGIN_MANIFEST_PATH);
if !manifest_path.is_file() {
return None;
}
let contents = fs::read_to_string(&manifest_path).ok()?;
let RawPluginManifestName { name: raw_name } = serde_json::from_str(&contents).ok()?;
Some(
plugin_root
.file_name()
.and_then(|entry| entry.to_str())
.filter(|_| raw_name.trim().is_empty())
.unwrap_or(raw_name.as_str())
.to_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> {
for ancestor in path.ancestors() {
if let Some(name) = plugin_manifest_name(ancestor) {
return Some(name);
}
}
None
}
#[cfg(test)]
mod tests {
use super::plugin_namespace_for_skill_path;
use std::fs;
use tempfile::tempdir;
#[test]
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");
fs::create_dir_all(skill_path.parent().expect("parent")).expect("mkdir");
fs::create_dir_all(plugin_root.join(".codex-plugin")).expect("mkdir manifest");
fs::write(
plugin_root.join(".codex-plugin/plugin.json"),
r#"{"name":"sample"}"#,
)
.expect("write manifest");
fs::write(&skill_path, "---\ndescription: search\n---\n").expect("write skill");
assert_eq!(
plugin_namespace_for_skill_path(&skill_path),
Some("sample".to_string())
);
}
}