diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index ee061158065..744926738b2 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1460,6 +1460,7 @@ pub enum UserInput { Text { text: String }, Image { url: String }, LocalImage { path: PathBuf }, + Skill { name: String, path: PathBuf }, } impl UserInput { @@ -1468,6 +1469,7 @@ impl UserInput { UserInput::Text { text } => CoreUserInput::Text { text }, UserInput::Image { url } => CoreUserInput::Image { image_url: url }, UserInput::LocalImage { path } => CoreUserInput::LocalImage { path }, + UserInput::Skill { name, path } => CoreUserInput::Skill { name, path }, } } } @@ -1478,6 +1480,7 @@ impl From for UserInput { CoreUserInput::Text { text } => UserInput::Text { text }, CoreUserInput::Image { image_url } => UserInput::Image { url: image_url }, CoreUserInput::LocalImage { path } => UserInput::LocalImage { path }, + CoreUserInput::Skill { name, path } => UserInput::Skill { name, path }, _ => unreachable!("unsupported user input variant"), } } @@ -2063,6 +2066,10 @@ mod tests { CoreUserInput::LocalImage { path: PathBuf::from("local/image.png"), }, + CoreUserInput::Skill { + name: "skill-creator".to_string(), + path: PathBuf::from("/repo/.codex/skills/skill-creator/SKILL.md"), + }, ], }); @@ -2080,6 +2087,10 @@ mod tests { UserInput::LocalImage { path: PathBuf::from("local/image.png"), }, + UserInput::Skill { + name: "skill-creator".to_string(), + path: PathBuf::from("/repo/.codex/skills/skill-creator/SKILL.md"), + }, ], } ); diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 88d96dda56b..e09c47ce027 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -200,13 +200,14 @@ You can optionally specify config overrides on the new turn. If specified, these ### Example: Start a turn (invoke a skill) -Invoke a skill by sending a text input that begins with `$`. +Invoke a skill explicitly by including `$` in the text input and adding a `skill` input item alongside it. ```json { "method": "turn/start", "id": 33, "params": { "threadId": "thr_123", "input": [ - { "type": "text", "text": "$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage." } + { "type": "text", "text": "$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage." }, + { "type": "skill", "name": "skill-creator", "path": "/Users/me/.codex/skills/skill-creator/SKILL.md" } ] } } { "id": 33, "result": { "turn": { @@ -428,7 +429,23 @@ UI guidance for IDEs: surface an approval dialog as soon as the request arrives. ## Skills -Skills are invoked by sending a text input that starts with `$`. The rest of the text is passed to the skill as its input. +Invoke a skill by including `$` in the text input. Add a `skill` input item (recommended) so the backend injects full skill instructions instead of relying on the model to resolve the name. + +```json +{ + "method": "turn/start", + "id": 101, + "params": { + "threadId": "thread-1", + "input": [ + { "type": "text", "text": "$skill-creator Add a new skill for triaging flaky CI." }, + { "type": "skill", "name": "skill-creator", "path": "/Users/me/.codex/skills/skill-creator/SKILL.md" } + ] + } +} +``` + +If you omit the `skill` item, the model will still parse the `$` marker and try to locate the skill, which can add latency. Example: