@@ -243,7 +243,7 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[
243243 } ,
244244 SlashCommandSpec {
245245 name : "skills" ,
246- aliases : & [ ] ,
246+ aliases : & [ "skill" ] ,
247247 summary : "List, install, or invoke available skills" ,
248248 argument_hint : Some ( "[list|install <path>|help|<skill> [args]]" ) ,
249249 resume_supported : true ,
@@ -1312,7 +1312,7 @@ pub fn validate_slash_command_input(
13121312 "agents" => SlashCommand :: Agents {
13131313 args : parse_list_or_help_args ( command, remainder) ?,
13141314 } ,
1315- "skills" => SlashCommand :: Skills {
1315+ "skills" | "skill" => SlashCommand :: Skills {
13161316 args : parse_skills_args ( remainder. as_deref ( ) ) ?,
13171317 } ,
13181318 "doctor" => {
@@ -1975,9 +1975,9 @@ enum DefinitionScope {
19751975impl DefinitionScope {
19761976 fn label ( self ) -> & ' static str {
19771977 match self {
1978- Self :: Project => "Project (.claw) " ,
1979- Self :: UserConfigHome => "User ($CLAW_CONFIG_HOME) " ,
1980- Self :: UserHome => "User (~/.claw) " ,
1978+ Self :: Project => "Project roots " ,
1979+ Self :: UserConfigHome => "User config roots " ,
1980+ Self :: UserHome => "User home roots " ,
19811981 }
19821982 }
19831983}
@@ -2548,6 +2548,14 @@ fn discover_definition_roots(cwd: &Path, leaf: &str) -> Vec<(DefinitionSource, P
25482548 ) ;
25492549 }
25502550
2551+ if let Ok ( claude_config_dir) = env:: var ( "CLAUDE_CONFIG_DIR" ) {
2552+ push_unique_root (
2553+ & mut roots,
2554+ DefinitionSource :: UserClaude ,
2555+ PathBuf :: from ( claude_config_dir) . join ( leaf) ,
2556+ ) ;
2557+ }
2558+
25512559 if let Some ( home) = env:: var_os ( "HOME" ) {
25522560 let home = PathBuf :: from ( home) ;
25532561 push_unique_root (
@@ -2581,6 +2589,18 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
25812589 ancestor. join ( ".claw" ) . join ( "skills" ) ,
25822590 SkillOrigin :: SkillsDir ,
25832591 ) ;
2592+ push_unique_skill_root (
2593+ & mut roots,
2594+ DefinitionSource :: ProjectClaw ,
2595+ ancestor. join ( ".omc" ) . join ( "skills" ) ,
2596+ SkillOrigin :: SkillsDir ,
2597+ ) ;
2598+ push_unique_skill_root (
2599+ & mut roots,
2600+ DefinitionSource :: ProjectClaw ,
2601+ ancestor. join ( ".agents" ) . join ( "skills" ) ,
2602+ SkillOrigin :: SkillsDir ,
2603+ ) ;
25842604 push_unique_skill_root (
25852605 & mut roots,
25862606 DefinitionSource :: ProjectCodex ,
@@ -2653,6 +2673,12 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
26532673 home. join ( ".claw" ) . join ( "skills" ) ,
26542674 SkillOrigin :: SkillsDir ,
26552675 ) ;
2676+ push_unique_skill_root (
2677+ & mut roots,
2678+ DefinitionSource :: UserClaw ,
2679+ home. join ( ".omc" ) . join ( "skills" ) ,
2680+ SkillOrigin :: SkillsDir ,
2681+ ) ;
26562682 push_unique_skill_root (
26572683 & mut roots,
26582684 DefinitionSource :: UserClaw ,
@@ -2677,6 +2703,12 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
26772703 home. join ( ".claude" ) . join ( "skills" ) ,
26782704 SkillOrigin :: SkillsDir ,
26792705 ) ;
2706+ push_unique_skill_root (
2707+ & mut roots,
2708+ DefinitionSource :: UserClaude ,
2709+ home. join ( ".claude" ) . join ( "skills" ) . join ( "omc-learned" ) ,
2710+ SkillOrigin :: SkillsDir ,
2711+ ) ;
26802712 push_unique_skill_root (
26812713 & mut roots,
26822714 DefinitionSource :: UserClaude ,
@@ -2685,6 +2717,29 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
26852717 ) ;
26862718 }
26872719
2720+ if let Ok ( claude_config_dir) = env:: var ( "CLAUDE_CONFIG_DIR" ) {
2721+ let claude_config_dir = PathBuf :: from ( claude_config_dir) ;
2722+ let skills_dir = claude_config_dir. join ( "skills" ) ;
2723+ push_unique_skill_root (
2724+ & mut roots,
2725+ DefinitionSource :: UserClaude ,
2726+ skills_dir. clone ( ) ,
2727+ SkillOrigin :: SkillsDir ,
2728+ ) ;
2729+ push_unique_skill_root (
2730+ & mut roots,
2731+ DefinitionSource :: UserClaude ,
2732+ skills_dir. join ( "omc-learned" ) ,
2733+ SkillOrigin :: SkillsDir ,
2734+ ) ;
2735+ push_unique_skill_root (
2736+ & mut roots,
2737+ DefinitionSource :: UserClaude ,
2738+ claude_config_dir. join ( "commands" ) ,
2739+ SkillOrigin :: LegacyCommandsDir ,
2740+ ) ;
2741+ }
2742+
26882743 roots
26892744}
26902745
@@ -3467,10 +3522,11 @@ fn render_skills_usage(unexpected: Option<&str>) -> String {
34673522 let mut lines = vec ! [
34683523 "Skills" . to_string( ) ,
34693524 " Usage /skills [list|install <path>|help|<skill> [args]]" . to_string( ) ,
3525+ " Alias /skill" . to_string( ) ,
34703526 " Direct CLI claw skills [list|install <path>|help|<skill> [args]]" . to_string( ) ,
34713527 " Invoke /skills help overview -> $help overview" . to_string( ) ,
34723528 " Install root $CLAW_CONFIG_HOME/skills or ~/.claw/skills" . to_string( ) ,
3473- " Sources .claw/skills, ~/.claw/skills, legacy /commands" . to_string( ) ,
3529+ " Sources .claw/skills, .omc/skills, .agents/skills, .codex/skills, .claude/skills, ~/.claw/skills, ~/.omc/skills, ~/.claude/skills/omc-learned, ~/.codex/skills, ~/.claude /skills, legacy /commands" . to_string( ) ,
34743530 ] ;
34753531 if let Some ( args) = unexpected {
34763532 lines. push ( format ! ( " Unexpected {args}" ) ) ;
@@ -3484,10 +3540,24 @@ fn render_skills_usage_json(unexpected: Option<&str>) -> Value {
34843540 "action" : "help" ,
34853541 "usage" : {
34863542 "slash_command" : "/skills [list|install <path>|help|<skill> [args]]" ,
3543+ "aliases" : [ "/skill" ] ,
34873544 "direct_cli" : "claw skills [list|install <path>|help|<skill> [args]]" ,
34883545 "invoke" : "/skills help overview -> $help overview" ,
34893546 "install_root" : "$CLAW_CONFIG_HOME/skills or ~/.claw/skills" ,
3490- "sources" : [ ".claw/skills" , "legacy /commands" , "legacy fallback dirs still load automatically" ] ,
3547+ "sources" : [
3548+ ".claw/skills" ,
3549+ ".omc/skills" ,
3550+ ".agents/skills" ,
3551+ ".codex/skills" ,
3552+ ".claude/skills" ,
3553+ "~/.claw/skills" ,
3554+ "~/.omc/skills" ,
3555+ "~/.claude/skills/omc-learned" ,
3556+ "~/.codex/skills" ,
3557+ "~/.claude/skills" ,
3558+ "legacy /commands" ,
3559+ "legacy fallback dirs still load automatically"
3560+ ] ,
34913561 } ,
34923562 "unexpected" : unexpected,
34933563 } )
@@ -3849,8 +3919,10 @@ mod tests {
38493919 use runtime:: {
38503920 CompactionConfig , ConfigLoader , ContentBlock , ConversationMessage , MessageRole , Session ,
38513921 } ;
3922+ use std:: ffi:: OsString ;
38523923 use std:: fs;
38533924 use std:: path:: { Path , PathBuf } ;
3925+ use std:: sync:: { Mutex , OnceLock } ;
38543926 use std:: time:: { SystemTime , UNIX_EPOCH } ;
38553927
38563928 fn temp_dir ( label : & str ) -> PathBuf {
@@ -3861,6 +3933,18 @@ mod tests {
38613933 std:: env:: temp_dir ( ) . join ( format ! ( "commands-plugin-{label}-{nanos}" ) )
38623934 }
38633935
3936+ fn env_lock ( ) -> & ' static Mutex < ( ) > {
3937+ static LOCK : OnceLock < Mutex < ( ) > > = OnceLock :: new ( ) ;
3938+ LOCK . get_or_init ( || Mutex :: new ( ( ) ) )
3939+ }
3940+
3941+ fn restore_env_var ( key : & str , original : Option < OsString > ) {
3942+ match original {
3943+ Some ( value) => std:: env:: set_var ( key, value) ,
3944+ None => std:: env:: remove_var ( key) ,
3945+ }
3946+ }
3947+
38643948 fn write_external_plugin ( root : & Path , name : & str , version : & str ) {
38653949 fs:: create_dir_all ( root. join ( ".claude-plugin" ) ) . expect ( "manifest dir" ) ;
38663950 fs:: write (
@@ -4275,6 +4359,7 @@ mod tests {
42754359 assert ! ( help. contains( "aliases: /plugins, /marketplace" ) ) ;
42764360 assert ! ( help. contains( "/agents [list|help]" ) ) ;
42774361 assert ! ( help. contains( "/skills [list|install <path>|help|<skill> [args]]" ) ) ;
4362+ assert ! ( help. contains( "aliases: /skill" ) ) ;
42784363 assert_eq ! ( slash_command_specs( ) . len( ) , 141 ) ;
42794364 assert ! ( resume_supported_slash_commands( ) . len( ) >= 39 ) ;
42804365 }
@@ -4517,10 +4602,10 @@ mod tests {
45174602
45184603 assert ! ( report. contains( "Agents" ) ) ;
45194604 assert ! ( report. contains( "2 active agents" ) ) ;
4520- assert ! ( report. contains( "Project (.claw) :" ) ) ;
4605+ assert ! ( report. contains( "Project roots :" ) ) ;
45214606 assert ! ( report. contains( "planner · Project planner · gpt-5.4 · medium" ) ) ;
4522- assert ! ( report. contains( "User (~/.claw) :" ) ) ;
4523- assert ! ( report. contains( "(shadowed by Project (.claw) ) planner · User planner" ) ) ;
4607+ assert ! ( report. contains( "User home roots :" ) ) ;
4608+ assert ! ( report. contains( "(shadowed by Project roots ) planner · User planner" ) ) ;
45244609 assert ! ( report. contains( "verifier · Verification agent · gpt-5.4-mini · high" ) ) ;
45254610
45264611 let _ = fs:: remove_dir_all ( workspace) ;
@@ -4628,11 +4713,11 @@ mod tests {
46284713
46294714 assert ! ( report. contains( "Skills" ) ) ;
46304715 assert ! ( report. contains( "3 available skills" ) ) ;
4631- assert ! ( report. contains( "Project (.claw) :" ) ) ;
4716+ assert ! ( report. contains( "Project roots :" ) ) ;
46324717 assert ! ( report. contains( "plan · Project planning guidance" ) ) ;
46334718 assert ! ( report. contains( "deploy · Legacy deployment guidance · legacy /commands" ) ) ;
4634- assert ! ( report. contains( "User (~/.claw) :" ) ) ;
4635- assert ! ( report. contains( "(shadowed by Project (.claw) ) plan · User planning guidance" ) ) ;
4719+ assert ! ( report. contains( "User home roots :" ) ) ;
4720+ assert ! ( report. contains( "(shadowed by Project roots ) plan · User planning guidance" ) ) ;
46364721 assert ! ( report. contains( "help · Help guidance" ) ) ;
46374722
46384723 let _ = fs:: remove_dir_all ( workspace) ;
@@ -4704,6 +4789,7 @@ mod tests {
47044789 let help = handle_skills_slash_command_json ( Some ( "help" ) , & workspace) . expect ( "skills help" ) ;
47054790 assert_eq ! ( help[ "kind" ] , "skills" ) ;
47064791 assert_eq ! ( help[ "action" ] , "help" ) ;
4792+ assert_eq ! ( help[ "usage" ] [ "aliases" ] [ 0 ] , "/skill" ) ;
47074793 assert_eq ! (
47084794 help[ "usage" ] [ "direct_cli" ] ,
47094795 "claw skills [list|install <path>|help|<skill> [args]]"
@@ -4732,8 +4818,12 @@ mod tests {
47324818 super :: handle_skills_slash_command ( Some ( "--help" ) , & cwd) . expect ( "skills help" ) ;
47334819 assert ! ( skills_help
47344820 . contains( "Usage /skills [list|install <path>|help|<skill> [args]]" ) ) ;
4821+ assert ! ( skills_help. contains( "Alias /skill" ) ) ;
47354822 assert ! ( skills_help. contains( "Invoke /skills help overview -> $help overview" ) ) ;
47364823 assert ! ( skills_help. contains( "Install root $CLAW_CONFIG_HOME/skills or ~/.claw/skills" ) ) ;
4824+ assert ! ( skills_help. contains( ".omc/skills" ) ) ;
4825+ assert ! ( skills_help. contains( ".agents/skills" ) ) ;
4826+ assert ! ( skills_help. contains( "~/.claude/skills/omc-learned" ) ) ;
47374827 assert ! ( skills_help. contains( "legacy /commands" ) ) ;
47384828
47394829 let skills_unexpected =
@@ -4744,6 +4834,7 @@ mod tests {
47444834 . expect ( "nested skills help" ) ;
47454835 assert ! ( skills_install_help
47464836 . contains( "Usage /skills [list|install <path>|help|<skill> [args]]" ) ) ;
4837+ assert ! ( skills_install_help. contains( "Alias /skill" ) ) ;
47474838 assert ! ( skills_install_help. contains( "Unexpected install" ) ) ;
47484839
47494840 let skills_unknown_help =
@@ -4752,9 +4843,87 @@ mod tests {
47524843 . contains( "Usage /skills [list|install <path>|help|<skill> [args]]" ) ) ;
47534844 assert ! ( skills_unknown_help. contains( "Unexpected show" ) ) ;
47544845
4846+ let skills_help_json =
4847+ super :: handle_skills_slash_command_json ( Some ( "help" ) , & cwd) . expect ( "skills help json" ) ;
4848+ let sources = skills_help_json[ "usage" ] [ "sources" ]
4849+ . as_array ( )
4850+ . expect ( "skills help sources" ) ;
4851+ assert_eq ! ( skills_help_json[ "usage" ] [ "aliases" ] [ 0 ] , "/skill" ) ;
4852+ assert ! ( sources. iter( ) . any( |value| value == ".omc/skills" ) ) ;
4853+ assert ! ( sources. iter( ) . any( |value| value == ".agents/skills" ) ) ;
4854+ assert ! ( sources. iter( ) . any( |value| value == "~/.omc/skills" ) ) ;
4855+ assert ! ( sources
4856+ . iter( )
4857+ . any( |value| value == "~/.claude/skills/omc-learned" ) ) ;
4858+
47554859 let _ = fs:: remove_dir_all ( cwd) ;
47564860 }
47574861
4862+ #[ test]
4863+ fn discovers_omc_skills_from_project_and_user_compatibility_roots ( ) {
4864+ let _guard = env_lock ( ) . lock ( ) . expect ( "env lock" ) ;
4865+ let workspace = temp_dir ( "skills-omc-workspace" ) ;
4866+ let user_home = temp_dir ( "skills-omc-home" ) ;
4867+ let claude_config_dir = temp_dir ( "skills-omc-claude-config" ) ;
4868+ let project_omc_skills = workspace. join ( ".omc" ) . join ( "skills" ) ;
4869+ let project_agents_skills = workspace. join ( ".agents" ) . join ( "skills" ) ;
4870+ let user_omc_skills = user_home. join ( ".omc" ) . join ( "skills" ) ;
4871+ let claude_config_skills = claude_config_dir. join ( "skills" ) ;
4872+ let claude_config_commands = claude_config_dir. join ( "commands" ) ;
4873+ let learned_skills = claude_config_dir. join ( "skills" ) . join ( "omc-learned" ) ;
4874+ let original_home = std:: env:: var_os ( "HOME" ) ;
4875+ let original_claude_config_dir = std:: env:: var_os ( "CLAUDE_CONFIG_DIR" ) ;
4876+
4877+ write_skill ( & project_omc_skills, "hud" , "OMC HUD guidance" ) ;
4878+ write_skill (
4879+ & project_agents_skills,
4880+ "trace" ,
4881+ "Compatibility skill guidance" ,
4882+ ) ;
4883+ write_skill ( & user_omc_skills, "cancel" , "OMC cancel guidance" ) ;
4884+ write_skill (
4885+ & claude_config_skills,
4886+ "statusline" ,
4887+ "Claude config skill guidance" ,
4888+ ) ;
4889+ write_legacy_command (
4890+ & claude_config_commands,
4891+ "doctor-check" ,
4892+ "Claude config command guidance" ,
4893+ ) ;
4894+ write_skill ( & learned_skills, "learned" , "Learned skill guidance" ) ;
4895+ std:: env:: set_var ( "HOME" , & user_home) ;
4896+ std:: env:: set_var ( "CLAUDE_CONFIG_DIR" , & claude_config_dir) ;
4897+
4898+ let report = super :: handle_skills_slash_command ( None , & workspace) . expect ( "skills list" ) ;
4899+ assert ! ( report. contains( "available skills" ) ) ;
4900+ assert ! ( report. contains( "hud · OMC HUD guidance" ) ) ;
4901+ assert ! ( report. contains( "trace · Compatibility skill guidance" ) ) ;
4902+ assert ! ( report. contains( "cancel · OMC cancel guidance" ) ) ;
4903+ assert ! ( report. contains( "statusline · Claude config skill guidance" ) ) ;
4904+ assert ! ( report. contains( "doctor-check · Claude config command guidance · legacy /commands" ) ) ;
4905+ assert ! ( report. contains( "learned · Learned skill guidance" ) ) ;
4906+
4907+ let help =
4908+ super :: handle_skills_slash_command_json ( Some ( "help" ) , & workspace) . expect ( "skills help" ) ;
4909+ let sources = help[ "usage" ] [ "sources" ]
4910+ . as_array ( )
4911+ . expect ( "skills help sources" ) ;
4912+ assert_eq ! ( help[ "usage" ] [ "aliases" ] [ 0 ] , "/skill" ) ;
4913+ assert ! ( sources. iter( ) . any( |value| value == ".omc/skills" ) ) ;
4914+ assert ! ( sources. iter( ) . any( |value| value == ".agents/skills" ) ) ;
4915+ assert ! ( sources. iter( ) . any( |value| value == "~/.omc/skills" ) ) ;
4916+ assert ! ( sources
4917+ . iter( )
4918+ . any( |value| value == "~/.claude/skills/omc-learned" ) ) ;
4919+
4920+ restore_env_var ( "HOME" , original_home) ;
4921+ restore_env_var ( "CLAUDE_CONFIG_DIR" , original_claude_config_dir) ;
4922+ let _ = fs:: remove_dir_all ( workspace) ;
4923+ let _ = fs:: remove_dir_all ( user_home) ;
4924+ let _ = fs:: remove_dir_all ( claude_config_dir) ;
4925+ }
4926+
47584927 #[ test]
47594928 fn mcp_usage_supports_help_and_unexpected_args ( ) {
47604929 let cwd = temp_dir ( "mcp-usage" ) ;
@@ -4991,7 +5160,7 @@ mod tests {
49915160 let listed = render_skills_report (
49925161 & load_skills_from_roots ( & roots) . expect ( "installed skills should load" ) ,
49935162 ) ;
4994- assert ! ( listed. contains( "User ($CLAW_CONFIG_HOME) :" ) ) ;
5163+ assert ! ( listed. contains( "User config roots :" ) ) ;
49955164 assert ! ( listed. contains( "help · Helpful skill" ) ) ;
49965165
49975166 let _ = fs:: remove_dir_all ( workspace) ;
0 commit comments