From 6e378185e9f86949cf7ac471f74ed90588100744 Mon Sep 17 00:00:00 2001 From: Yeachan-Heo Date: Tue, 31 Mar 2026 20:28:36 +0000 Subject: [PATCH] Accept $skill invocation form in Skill tool Teach Skill path resolution to accept the common $skill invocation form in addition to bare names and /skill prefixes. Keep the behavior narrow and add regression coverage using the existing help skill fixture. Constraint: Must not touch unrelated dirty api files in this worktree Constraint: Keep the change limited to rust/crates/tools Rejected: Canonicalize the returned skill field to the resolved name | would change caller-visible output semantics unnecessarily Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep invocation-prefix normalization aligned with how prompt and skill references are written elsewhere in the CLI Tested: cargo test -p tools Not-tested: CODEX_HOME layouts with unusual symlink arrangements --- rust/crates/tools/src/lib.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/rust/crates/tools/src/lib.rs b/rust/crates/tools/src/lib.rs index b82c707..7bb6179 100644 --- a/rust/crates/tools/src/lib.rs +++ b/rust/crates/tools/src/lib.rs @@ -1090,7 +1090,7 @@ fn todo_store_path() -> Result { } fn resolve_skill_path(skill: &str) -> Result { - let requested = skill.trim().trim_start_matches('/'); + let requested = skill.trim().trim_start_matches('/').trim_start_matches('$'); if requested.is_empty() { return Err(String::from("skill must not be empty")); } @@ -1961,6 +1961,21 @@ mod tests { .as_str() .expect("prompt") .contains("Guide on using oh-my-codex plugin")); + + let dollar_result = execute_tool( + "Skill", + &json!({ + "skill": "$help" + }), + ) + .expect("Skill should accept $skill invocation form"); + let dollar_output: serde_json::Value = + serde_json::from_str(&dollar_result).expect("valid json"); + assert_eq!(dollar_output["skill"], "$help"); + assert!(dollar_output["path"] + .as_str() + .expect("path") + .ends_with("/help/SKILL.md")); } #[test]