@@ -14,6 +14,9 @@ const BRIN_DOCS_DIR: &str = ".brin-docs";
1414const BRIN_MARKER_START : & str = "[brin Docs Index]" ;
1515const BRIN_MARKER_END : & str = "[/brin Docs Index]" ;
1616
17+ /// Marker to detect package installation instructions in AGENTS.md
18+ const INSTALL_INSTRUCTIONS_MARKER : & str = "## Package Installation" ;
19+
1720/// Convert a package name to a valid filename
1821/// - Lowercase only
1922/// - Alphanumeric and hyphens only
@@ -270,6 +273,41 @@ fn remove_brin_section(content: &str) -> String {
270273 }
271274}
272275
276+ /// Add package installation instructions to AGENTS.md (idempotent)
277+ pub fn add_install_instructions ( ) -> Result < ( ) > {
278+ add_install_instructions_at_path ( Path :: new ( AGENTS_MD_PATH ) )
279+ }
280+
281+ fn add_install_instructions_at_path ( agents_path : & Path ) -> Result < ( ) > {
282+ let instructions = r#"
283+ ## Package Installation
284+
285+ Use `brin` for all package operations:
286+ - `brin add <pkg>` -- install with security scan
287+ - `brin remove <pkg>` -- uninstall
288+ - `brin scan` -- audit all dependencies
289+ - `brin check <pkg>` -- look up without installing
290+
291+ Do not use npm install, yarn add, pnpm add, or pip install directly.
292+ "# ;
293+
294+ if agents_path. exists ( ) {
295+ let content = fs:: read_to_string ( agents_path) ?;
296+ if content. contains ( INSTALL_INSTRUCTIONS_MARKER ) {
297+ // Already present, nothing to do
298+ return Ok ( ( ) ) ;
299+ }
300+ let new_content = format ! ( "{}{}" , content, instructions) ;
301+ fs:: write ( agents_path, new_content) ?;
302+ } else {
303+ // Create new AGENTS.md with just the instructions
304+ let content = format ! ( "# AGENTS.md\n {}" , instructions) ;
305+ fs:: write ( agents_path, content) ?;
306+ }
307+
308+ Ok ( ( ) )
309+ }
310+
273311/// Ensure .brin-docs directory exists
274312pub fn ensure_docs_dir ( ) -> Result < ( ) > {
275313 fs:: create_dir_all ( BRIN_DOCS_DIR ) ?;
@@ -413,6 +451,49 @@ mod tests {
413451 assert ! ( !content. contains( "old.md" ) ) ;
414452 }
415453
454+ #[ test]
455+ fn test_add_install_instructions_to_existing ( ) {
456+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
457+ let agents_path = temp_dir. path ( ) . join ( "AGENTS.md" ) ;
458+
459+ fs:: write ( & agents_path, "# AGENTS.md\n \n Some content\n " ) . unwrap ( ) ;
460+
461+ add_install_instructions_at_path ( & agents_path) . unwrap ( ) ;
462+
463+ let content = fs:: read_to_string ( & agents_path) . unwrap ( ) ;
464+ assert ! ( content. contains( "## Package Installation" ) ) ;
465+ assert ! ( content. contains( "brin add <pkg>" ) ) ;
466+ assert ! ( content. contains( "Do not use npm install" ) ) ;
467+ }
468+
469+ #[ test]
470+ fn test_add_install_instructions_idempotent ( ) {
471+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
472+ let agents_path = temp_dir. path ( ) . join ( "AGENTS.md" ) ;
473+
474+ fs:: write ( & agents_path, "# AGENTS.md\n \n Some content\n " ) . unwrap ( ) ;
475+
476+ add_install_instructions_at_path ( & agents_path) . unwrap ( ) ;
477+ let content_after_first = fs:: read_to_string ( & agents_path) . unwrap ( ) ;
478+
479+ add_install_instructions_at_path ( & agents_path) . unwrap ( ) ;
480+ let content_after_second = fs:: read_to_string ( & agents_path) . unwrap ( ) ;
481+
482+ assert_eq ! ( content_after_first, content_after_second) ;
483+ }
484+
485+ #[ test]
486+ fn test_add_install_instructions_creates_new ( ) {
487+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
488+ let agents_path = temp_dir. path ( ) . join ( "AGENTS.md" ) ;
489+
490+ add_install_instructions_at_path ( & agents_path) . unwrap ( ) ;
491+
492+ let content = fs:: read_to_string ( & agents_path) . unwrap ( ) ;
493+ assert ! ( content. contains( "# AGENTS.md" ) ) ;
494+ assert ! ( content. contains( "## Package Installation" ) ) ;
495+ }
496+
416497 #[ test]
417498 fn test_remove_agents_md_index ( ) {
418499 let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
0 commit comments