@@ -10,7 +10,7 @@ const API_BASE: &str = "https://www.toptal.com/developers/gitignore/api";
1010pub enum IgnoreCommand {
1111 /// Generate .gitignore for the given templates
1212 Add {
13- /// Comma-separated list of templates (e.g. rust,vscode)
13+ /// Comma-separated list of templates (e.g. rust,vscode,agentic )
1414 templates : String ,
1515 #[ arg( short, long) ]
1616 yes : bool ,
@@ -44,12 +44,7 @@ fn add(templates: &str, yes: bool, force: bool, dry_run: bool) -> Result<()> {
4444 return Ok ( ( ) ) ;
4545 }
4646
47- let url = format ! ( "{API_BASE}/{templates}" ) ;
48- let content = ureq:: get ( & url)
49- . call ( )
50- . context ( "Failed to fetch gitignore templates" ) ?
51- . into_string ( )
52- . context ( "Failed to read response" ) ?;
47+ let content = resolve_templates ( templates) ?;
5348
5449 if dry_run {
5550 println ! ( "[dry-run] Would write .gitignore:\n {content}" ) ;
@@ -61,7 +56,48 @@ fn add(templates: &str, yes: bool, force: bool, dry_run: bool) -> Result<()> {
6156 Ok ( ( ) )
6257}
6358
59+ /// Split templates, resolve built-ins locally, fetch the rest from the API.
60+ /// Combines both into a single output.
61+ fn resolve_templates ( templates : & str ) -> Result < String > {
62+ let mut builtin_parts: Vec < & str > = Vec :: new ( ) ;
63+ let mut api_templates: Vec < & str > = Vec :: new ( ) ;
64+
65+ for t in templates. split ( ',' ) . map ( str:: trim) {
66+ if builtins:: get ( t) . is_some ( ) {
67+ builtin_parts. push ( t) ;
68+ } else {
69+ api_templates. push ( t) ;
70+ }
71+ }
72+
73+ let mut output = String :: new ( ) ;
74+
75+ for name in & builtin_parts {
76+ output. push_str ( builtins:: get ( name) . unwrap ( ) ) ;
77+ }
78+
79+ if !api_templates. is_empty ( ) {
80+ let joined = api_templates. join ( "," ) ;
81+ let url = format ! ( "{API_BASE}/{joined}" ) ;
82+ let fetched = ureq:: get ( & url)
83+ . call ( )
84+ . context ( "Failed to fetch gitignore templates" ) ?
85+ . into_string ( )
86+ . context ( "Failed to read response" ) ?;
87+ output. push_str ( & fetched) ;
88+ }
89+
90+ Ok ( output)
91+ }
92+
6493fn list ( filter : Option < & str > ) -> Result < ( ) > {
94+ // Always show built-ins first
95+ for name in builtins:: NAMES {
96+ if filter. is_none_or ( |f| name. contains ( f) ) {
97+ println ! ( "{name} (built-in)" ) ;
98+ }
99+ }
100+
65101 let url = format ! ( "{API_BASE}/list?format=lines" ) ;
66102 let content = ureq:: get ( & url)
67103 . call ( )
@@ -76,3 +112,32 @@ fn list(filter: Option<&str>) -> Result<()> {
76112 }
77113 Ok ( ( ) )
78114}
115+
116+ mod builtins {
117+ pub ( super ) const NAMES : & [ & str ] = & [ "agentic" ] ;
118+
119+ pub ( super ) fn get ( name : & str ) -> Option < & ' static str > {
120+ match name {
121+ "agentic" => Some ( AGENTIC ) ,
122+ _ => None ,
123+ }
124+ }
125+
126+ const AGENTIC : & str = "\
127+ # Kiro
128+ .kiro/
129+ skills-lock.json
130+
131+ # Agent specs / project context
132+ .agents/
133+
134+ # Cursor
135+ .cursor/
136+
137+ # GitHub Copilot
138+ .copilot/
139+
140+ # Continue
141+ .continue/
142+ " ;
143+ }
0 commit comments