@@ -9,8 +9,8 @@ use crate::cli::{InitTarget, Scope};
99use crate :: config:: home_dir;
1010use crate :: i18n:: I18n ;
1111
12- /// Embedded shnote rules content
13- const SHNOTE_RULES : & str = r#"# shnote - Shell Command Wrapper
12+ /// Embedded shnote rules content (shared by all targets)
13+ const SHNOTE_RULES_BASE : & str = r#"# shnote - Shell Command Wrapper
1414
1515## 概述
1616
@@ -338,10 +338,25 @@ file2.txt
338338这使得 AI Agent 可以轻松追踪每个命令的意图和执行结果。
339339"# ;
340340
341+ /// Codex-specific additions to the shnote rules
342+ const SHNOTE_RULES_CODEX_EXTRA : & str = r#"## Codex 额外规则
343+
344+ 除非用户明确要求,否则默认使用 **Read** 工具和 **apply_patch** 工具来完成阅读与修改文件的操作,不要用 shnote 来做读取/编辑。
345+ "# ;
346+
341347/// Marker to identify shnote rules section in append mode
342348const SHNOTE_MARKER_START : & str = "\n <!-- shnote rules start -->\n " ;
343349const SHNOTE_MARKER_END : & str = "\n <!-- shnote rules end -->\n " ;
344350
351+ fn rules_for_target ( target : InitTarget ) -> String {
352+ let mut rules = SHNOTE_RULES_BASE . to_string ( ) ;
353+ if matches ! ( target, InitTarget :: Codex ) {
354+ rules. push_str ( "\n \n " ) ;
355+ rules. push_str ( SHNOTE_RULES_CODEX_EXTRA ) ;
356+ }
357+ rules
358+ }
359+
345360pub fn run_init ( i18n : & I18n , target : InitTarget , scope : Scope ) -> Result < ( ) > {
346361 match target {
347362 InitTarget :: Claude => init_claude ( i18n, scope) ,
@@ -361,6 +376,7 @@ fn get_base_dir(i18n: &I18n, scope: Scope) -> Result<PathBuf> {
361376fn init_claude ( i18n : & I18n , scope : Scope ) -> Result < ( ) > {
362377 let probe = probe_cli_tool ( i18n, "claude" ) ;
363378 let base = get_base_dir ( i18n, scope) ?;
379+ let rules = rules_for_target ( InitTarget :: Claude ) ;
364380
365381 // Claude Code >= 2.0.64 supports ~/.claude/rules/*.md.
366382 // For older versions (or when version cannot be determined), append rules to ~/.claude/CLAUDE.md.
@@ -387,7 +403,7 @@ fn init_claude(i18n: &I18n, scope: Scope) -> Result<()> {
387403
388404 if !migrated {
389405 // No migration needed, just write the rules file
390- fs:: write ( & target_file, SHNOTE_RULES )
406+ fs:: write ( & target_file, & rules )
391407 . context ( i18n. err_write_file ( & target_file. display ( ) . to_string ( ) ) ) ?;
392408 }
393409
@@ -410,7 +426,7 @@ fn init_claude(i18n: &I18n, scope: Scope) -> Result<()> {
410426 fs:: create_dir_all ( & claude_dir)
411427 . context ( i18n. err_create_dir ( & claude_dir. display ( ) . to_string ( ) ) ) ?;
412428 let target_file = claude_dir. join ( "CLAUDE.md" ) ;
413- append_rules ( i18n, & target_file) ?;
429+ append_rules ( i18n, & target_file, & rules ) ?;
414430 println ! (
415431 "{}" ,
416432 i18n. init_claude_success( & target_file. display( ) . to_string( ) )
@@ -442,7 +458,7 @@ fn migrate_shnote_rules(i18n: &I18n, old_file: &Path, new_file: &Path) -> Result
442458
443459 // Write extracted rules to new file (use latest rules, not old content)
444460 // This ensures we always have the latest version
445- fs:: write ( new_file, SHNOTE_RULES )
461+ fs:: write ( new_file, SHNOTE_RULES_BASE )
446462 . context ( i18n. err_write_file ( & new_file. display ( ) . to_string ( ) ) ) ?;
447463
448464 // Remove shnote rules from old file
@@ -475,14 +491,15 @@ fn migrate_shnote_rules(i18n: &I18n, old_file: &Path, new_file: &Path) -> Result
475491fn init_codex ( i18n : & I18n , scope : Scope ) -> Result < ( ) > {
476492 let _ = probe_cli_tool ( i18n, "codex" ) ;
477493 let base = get_base_dir ( i18n, scope) ?;
494+ let rules = rules_for_target ( InitTarget :: Codex ) ;
478495 let codex_dir = base. join ( ".codex" ) ;
479496 let target_file = codex_dir. join ( "AGENTS.md" ) ;
480497
481498 // Create directory if needed
482499 fs:: create_dir_all ( & codex_dir)
483500 . context ( i18n. err_create_dir ( & codex_dir. display ( ) . to_string ( ) ) ) ?;
484501
485- append_rules ( i18n, & target_file) ?;
502+ append_rules ( i18n, & target_file, & rules ) ?;
486503
487504 println ! (
488505 "{}" ,
@@ -494,14 +511,15 @@ fn init_codex(i18n: &I18n, scope: Scope) -> Result<()> {
494511fn init_gemini ( i18n : & I18n , scope : Scope ) -> Result < ( ) > {
495512 let _ = probe_cli_tool ( i18n, "gemini" ) ;
496513 let base = get_base_dir ( i18n, scope) ?;
514+ let rules = rules_for_target ( InitTarget :: Gemini ) ;
497515 let gemini_dir = base. join ( ".gemini" ) ;
498516 let target_file = gemini_dir. join ( "GEMINI.md" ) ;
499517
500518 // Create directory if needed
501519 fs:: create_dir_all ( & gemini_dir)
502520 . context ( i18n. err_create_dir ( & gemini_dir. display ( ) . to_string ( ) ) ) ?;
503521
504- append_rules ( i18n, & target_file) ?;
522+ append_rules ( i18n, & target_file, & rules ) ?;
505523
506524 println ! (
507525 "{}" ,
@@ -510,7 +528,7 @@ fn init_gemini(i18n: &I18n, scope: Scope) -> Result<()> {
510528 Ok ( ( ) )
511529}
512530
513- fn append_rules ( i18n : & I18n , target_file : & PathBuf ) -> Result < ( ) > {
531+ fn append_rules ( i18n : & I18n , target_file : & PathBuf , rules : & str ) -> Result < ( ) > {
514532 let mut content = if target_file. exists ( ) {
515533 fs:: read_to_string ( target_file)
516534 . context ( i18n. err_read_file ( & target_file. display ( ) . to_string ( ) ) ) ?
@@ -530,7 +548,7 @@ fn append_rules(i18n: &I18n, target_file: &PathBuf) -> Result<()> {
530548 let mut new_content = String :: new ( ) ;
531549 new_content. push_str ( & content[ ..start_idx] ) ;
532550 new_content. push_str ( SHNOTE_MARKER_START ) ;
533- new_content. push_str ( SHNOTE_RULES ) ;
551+ new_content. push_str ( rules ) ;
534552 new_content. push_str ( SHNOTE_MARKER_END ) ;
535553 new_content. push_str ( & content[ end_idx..] ) ;
536554
@@ -541,7 +559,7 @@ fn append_rules(i18n: &I18n, target_file: &PathBuf) -> Result<()> {
541559 } else {
542560 // Append new rules (rewrite the file to keep behavior deterministic and testable)
543561 content. push_str ( SHNOTE_MARKER_START ) ;
544- content. push_str ( SHNOTE_RULES ) ;
562+ content. push_str ( rules ) ;
545563 content. push_str ( SHNOTE_MARKER_END ) ;
546564
547565 fs:: write ( target_file, content)
@@ -667,10 +685,17 @@ mod tests {
667685 #[ test]
668686 fn shnote_rules_has_content ( ) {
669687 // Verify rules contain expected content
670- assert ! ( SHNOTE_RULES . contains( "shnote" ) ) ;
671- assert ! ( SHNOTE_RULES . contains( "--what" ) ) ;
672- assert ! ( SHNOTE_RULES . contains( "--why" ) ) ;
673- assert ! ( SHNOTE_RULES . len( ) > 1000 ) ; // Rules should be substantial
688+ assert ! ( SHNOTE_RULES_BASE . contains( "shnote" ) ) ;
689+ assert ! ( SHNOTE_RULES_BASE . contains( "--what" ) ) ;
690+ assert ! ( SHNOTE_RULES_BASE . contains( "--why" ) ) ;
691+ assert ! ( SHNOTE_RULES_BASE . len( ) > 1000 ) ; // Rules should be substantial
692+ }
693+
694+ #[ test]
695+ fn codex_rules_include_extra_instruction ( ) {
696+ let rules = rules_for_target ( InitTarget :: Codex ) ;
697+ assert ! ( rules. contains( "Read" ) ) ;
698+ assert ! ( rules. contains( "apply_patch" ) ) ;
674699 }
675700
676701 #[ test]
@@ -734,7 +759,7 @@ mod tests {
734759 let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
735760 let target_file = temp_dir. path ( ) . join ( "test.md" ) ;
736761
737- append_rules ( & i18n, & target_file) . unwrap ( ) ;
762+ append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap ( ) ;
738763
739764 assert ! ( target_file. exists( ) ) ;
740765 let content = fs:: read_to_string ( & target_file) . unwrap ( ) ;
@@ -759,13 +784,13 @@ mod tests {
759784 )
760785 . unwrap ( ) ;
761786
762- append_rules ( & i18n, & target_file) . unwrap ( ) ;
787+ append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap ( ) ;
763788
764789 let content = fs:: read_to_string ( & target_file) . unwrap ( ) ;
765790 assert ! ( content. contains( "Some content" ) ) ;
766791 assert ! ( content. contains( "More content" ) ) ;
767792 assert ! ( !content. contains( "OLD RULES" ) ) ;
768- assert ! ( content. contains( SHNOTE_RULES ) ) ;
793+ assert ! ( content. contains( SHNOTE_RULES_BASE ) ) ;
769794 }
770795
771796 #[ cfg( unix) ]
@@ -785,7 +810,7 @@ mod tests {
785810 let rules_file = temp_dir. path ( ) . join ( ".claude/rules/shnote.md" ) ;
786811 assert ! ( rules_file. exists( ) ) ;
787812 let content = fs:: read_to_string ( rules_file) . unwrap ( ) ;
788- assert_eq ! ( content, SHNOTE_RULES ) ;
813+ assert_eq ! ( content, rules_for_target ( InitTarget :: Claude ) ) ;
789814 }
790815
791816 #[ test]
@@ -943,7 +968,7 @@ mod tests {
943968 let rules_file = temp_dir. path ( ) . join ( ".claude/rules/shnote.md" ) ;
944969 assert ! ( rules_file. exists( ) ) ;
945970 let content = fs:: read_to_string ( & rules_file) . unwrap ( ) ;
946- assert_eq ! ( content, SHNOTE_RULES ) ;
971+ assert_eq ! ( content, rules_for_target ( InitTarget :: Claude ) ) ;
947972
948973 // Check old CLAUDE.md no longer has shnote rules
949974 let old_content = fs:: read_to_string ( & old_claude_md) . unwrap ( ) ;
@@ -1017,7 +1042,7 @@ mod tests {
10171042 let rules_file = temp_dir. path ( ) . join ( ".claude/rules/shnote.md" ) ;
10181043 assert ! ( rules_file. exists( ) ) ;
10191044 let content = fs:: read_to_string ( & rules_file) . unwrap ( ) ;
1020- assert_eq ! ( content, SHNOTE_RULES ) ;
1045+ assert_eq ! ( content, rules_for_target ( InitTarget :: Claude ) ) ;
10211046
10221047 // Check old CLAUDE.md is unchanged
10231048 let old_content = fs:: read_to_string ( & old_claude_md) . unwrap ( ) ;
@@ -1191,11 +1216,11 @@ mod tests {
11911216 )
11921217 . unwrap ( ) ;
11931218
1194- append_rules ( & i18n, & target_file) . unwrap ( ) ;
1219+ append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap ( ) ;
11951220
11961221 let content = fs:: read_to_string ( & target_file) . unwrap ( ) ;
11971222 assert ! ( content. contains( "before" ) ) ;
1198- assert ! ( content. contains( SHNOTE_RULES ) ) ;
1223+ assert ! ( content. contains( SHNOTE_RULES_BASE ) ) ;
11991224 assert ! ( !content. contains( "OLD RULES WITHOUT END" ) ) ;
12001225 assert ! ( !content. contains( "after" ) ) ;
12011226 }
@@ -1207,7 +1232,7 @@ mod tests {
12071232 let target_file = temp_dir. path ( ) . join ( "dir-as-file" ) ;
12081233 fs:: create_dir_all ( & target_file) . unwrap ( ) ;
12091234
1210- let err = append_rules ( & i18n, & target_file) . unwrap_err ( ) ;
1235+ let err = append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap_err ( ) ;
12111236 // Check error chain contains the file path (use Debug format to see full chain)
12121237 let err_debug = format ! ( "{:?}" , err) ;
12131238 assert ! ( err_debug. contains( "dir-as-file" ) ) ;
@@ -1232,7 +1257,7 @@ mod tests {
12321257 . unwrap ( ) ;
12331258 fs:: set_permissions ( & target_file, fs:: Permissions :: from_mode ( 0o444 ) ) . unwrap ( ) ;
12341259
1235- let err = append_rules ( & i18n, & target_file) . unwrap_err ( ) ;
1260+ let err = append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap_err ( ) ;
12361261 assert ! ( err
12371262 . to_string( )
12381263 . contains( & i18n. err_write_file( & target_file. display( ) . to_string( ) ) ) ) ;
@@ -1250,7 +1275,7 @@ mod tests {
12501275 fs:: write ( & target_file, "existing content\n " ) . unwrap ( ) ;
12511276 fs:: set_permissions ( & target_file, fs:: Permissions :: from_mode ( 0o444 ) ) . unwrap ( ) ;
12521277
1253- let err = append_rules ( & i18n, & target_file) . unwrap_err ( ) ;
1278+ let err = append_rules ( & i18n, & target_file, SHNOTE_RULES_BASE ) . unwrap_err ( ) ;
12541279 assert ! ( err
12551280 . to_string( )
12561281 . contains( & i18n. err_write_file( & target_file. display( ) . to_string( ) ) ) ) ;
@@ -1301,7 +1326,7 @@ mod tests {
13011326 let rules_file = temp_dir. path ( ) . join ( ".claude/rules/shnote.md" ) ;
13021327 assert ! ( rules_file. exists( ) ) ;
13031328 let content = fs:: read_to_string ( rules_file) . unwrap ( ) ;
1304- assert_eq ! ( content, SHNOTE_RULES ) ;
1329+ assert_eq ! ( content, rules_for_target ( InitTarget :: Claude ) ) ;
13051330
13061331 // CLAUDE.md should not exist
13071332 let claude_md = temp_dir. path ( ) . join ( ".claude/CLAUDE.md" ) ;
@@ -1327,6 +1352,7 @@ mod tests {
13271352 let content = fs:: read_to_string ( target_file) . unwrap ( ) ;
13281353 assert ! ( content. contains( SHNOTE_MARKER_START ) ) ;
13291354 assert ! ( content. contains( "shnote" ) ) ;
1355+ assert ! ( content. contains( "apply_patch" ) ) ;
13301356 }
13311357
13321358 #[ test]
0 commit comments