diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 0b2e2c0a3..2d8a9113f 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1476,6 +1476,7 @@ pub enum UserInput { Text { text: String }, Image { url: String }, LocalImage { path: PathBuf }, + Skill { name: String, path: PathBuf }, } impl UserInput { @@ -1484,6 +1485,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 }, } } } @@ -1494,6 +1496,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"), } } @@ -2079,6 +2082,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"), + }, ], }); @@ -2096,6 +2103,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 4457f4d27..0d8fafad2 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -201,13 +201,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": { @@ -429,7 +430,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: