diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index bd1ed62e7..37d3b71b3 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1057,6 +1057,9 @@ pub enum SkillScope { pub struct SkillMetadata { pub name: String, pub description: String, + #[ts(optional)] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub short_description: Option, pub path: PathBuf, pub scope: SkillScope, } @@ -1083,6 +1086,7 @@ impl From for SkillMetadata { Self { name: value.name, description: value.description, + short_description: value.short_description, path: value.path, scope: value.scope.into(), } diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 2d581e238..88c0e7dd6 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -3319,6 +3319,7 @@ fn skills_to_info( .map(|skill| codex_app_server_protocol::SkillMetadata { name: skill.name.clone(), description: skill.description.clone(), + short_description: skill.short_description.clone(), path: skill.path.clone(), scope: skill.scope.into(), }) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index c15fa03cf..5deca299f 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -2187,6 +2187,7 @@ fn skills_to_info(skills: &[SkillMetadata]) -> Vec { .map(|skill| ProtocolSkillMetadata { name: skill.name.clone(), description: skill.description.clone(), + short_description: skill.short_description.clone(), path: skill.path.clone(), scope: skill.scope, }) diff --git a/codex-rs/core/src/skills/assets/samples/plan/SKILL.md b/codex-rs/core/src/skills/assets/samples/plan/SKILL.md index f202ee9e4..a515fa659 100644 --- a/codex-rs/core/src/skills/assets/samples/plan/SKILL.md +++ b/codex-rs/core/src/skills/assets/samples/plan/SKILL.md @@ -1,6 +1,8 @@ --- name: plan description: Generate a plan for how an agent should accomplish a complex coding task. Use when a user asks for a plan, and optionally when they want to save, find, read, update, or delete plan files in $CODEX_HOME/plans (default ~/.codex/plans). +metadata: + short-description: Create and manage plan markdown files under $CODEX_HOME/plans. --- # Plan diff --git a/codex-rs/core/src/skills/loader.rs b/codex-rs/core/src/skills/loader.rs index 32c5db843..3fbcfc93d 100644 --- a/codex-rs/core/src/skills/loader.rs +++ b/codex-rs/core/src/skills/loader.rs @@ -20,6 +20,14 @@ use tracing::error; struct SkillFrontmatter { name: String, description: String, + #[serde(default)] + metadata: SkillFrontmatterMetadata, +} + +#[derive(Debug, Default, Deserialize)] +struct SkillFrontmatterMetadata { + #[serde(default, rename = "short-description")] + short_description: Option, } const SKILLS_FILENAME: &str = "SKILL.md"; @@ -27,6 +35,7 @@ const SKILLS_DIR_NAME: &str = "skills"; const REPO_ROOT_CONFIG_DIR_NAME: &str = ".codex"; const MAX_NAME_LEN: usize = 64; const MAX_DESCRIPTION_LEN: usize = 1024; +const MAX_SHORT_DESCRIPTION_LEN: usize = MAX_DESCRIPTION_LEN; #[derive(Debug)] enum SkillParseError { @@ -218,15 +227,29 @@ fn parse_skill_file(path: &Path, scope: SkillScope) -> Result, pub path: PathBuf, pub scope: SkillScope, } diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index b3165acbe..d26d8318a 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -1697,6 +1697,9 @@ pub enum SkillScope { pub struct SkillMetadata { pub name: String, pub description: String, + #[ts(optional)] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub short_description: Option, pub path: PathBuf, pub scope: SkillScope, } diff --git a/codex-rs/tui/src/bottom_pane/skill_popup.rs b/codex-rs/tui/src/bottom_pane/skill_popup.rs index 2e1e5878c..bac1264ea 100644 --- a/codex-rs/tui/src/bottom_pane/skill_popup.rs +++ b/codex-rs/tui/src/bottom_pane/skill_popup.rs @@ -86,7 +86,11 @@ impl SkillPopup { .and_then(|n| n.to_str()) .unwrap_or(&skill.name); let name = format!("{} ({slug})", skill.name); - let description = skill.description.clone(); + let description = skill + .short_description + .as_ref() + .unwrap_or(&skill.description) + .clone(); GenericDisplayRow { name, match_indices: indices, diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 4d2ed8983..d04b3d0b5 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -3634,6 +3634,7 @@ fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec Vec