Load plugin skill roots through the host skills service (#37440)

## What changed

- Implement `SkillRootLoader<PluginSkillRoot>` for `HostSkillsService`.
- Load roots concurrently while preserving request order, product restrictions, cached snapshots, skill errors, and per-skill filesystem metadata.
- Deduplicate overlapping roots and prefer native plugin skills over migrated commands with the same plugin ID and skill name.

## Testing

- Cover native and migrated skill precedence, nested and overlapping roots, product filtering, snapshot reuse, error propagation, and symlinked migration roots.

GitOrigin-RevId: 2270d10616b3a0463852c563ee071c524cd11e6b
This commit is contained in:
felixxia-oai
2026-08-07 13:23:23 +00:00
committed by copyberry
parent a4b129eb3e
commit e75a1888d7
10 changed files with 634 additions and 24 deletions
@@ -8,13 +8,12 @@ use super::supported_command_sources;
use super::unique_command_sources;
use crate::manifest::load_plugin_command_paths;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_plugins::migrated_command_skills_root;
use std::fs;
use std::io;
use std::path::Path;
const PLUGIN_COMMANDS_DIR: &str = "commands";
const PLUGIN_METADATA_DIR: &str = ".codex-plugin";
const MIGRATED_COMMAND_SKILLS_DIR: &str = "migrated-command-skills";
const MAX_MIGRATED_COMMAND_SKILL_BYTES: usize = 4_000;
const PLUGIN_REWRITE_PROFILE: RewriteProfile = RewriteProfile::new("AGENTS.md", &[]);
@@ -24,9 +23,8 @@ const PLUGIN_MIGRATION_PROFILE: CommandMigrationProfile = CommandMigrationProfil
);
pub(crate) fn migrate_plugin_commands(plugin_root: &Path) -> io::Result<()> {
let target_skills = plugin_root
.join(PLUGIN_METADATA_DIR)
.join(MIGRATED_COMMAND_SKILLS_DIR);
let absolute_plugin_root = AbsolutePathBuf::from_absolute_path(plugin_root)?;
let target_skills = migrated_command_skills_root(&absolute_plugin_root);
if target_skills.is_dir() {
fs::remove_dir_all(&target_skills)?;
} else if target_skills.exists() {
@@ -41,12 +39,6 @@ pub(crate) fn migrate_plugin_commands(plugin_root: &Path) -> io::Result<()> {
Ok(())
}
pub(crate) fn migrated_command_skills_root(plugin_root: &AbsolutePathBuf) -> AbsolutePathBuf {
plugin_root
.join(PLUGIN_METADATA_DIR)
.join(MIGRATED_COMMAND_SKILLS_DIR)
}
fn plugin_command_sources(plugin_root: &Path) -> io::Result<Vec<CommandSource>> {
let command_paths = load_plugin_command_paths(plugin_root)?
.unwrap_or_else(|| vec![plugin_root.join(PLUGIN_COMMANDS_DIR)]);