11//! Hierarchical path management for the application
22
3- #![ allow( dead_code) ] // Allow unused code during migration phase
4-
5- use std:: env:: VarError ;
6- use std:: path:: {
7- PathBuf ,
8- StripPrefixError ,
9- } ;
10-
11- use globset:: {
12- Glob ,
13- GlobSetBuilder ,
14- } ;
15- use thiserror:: Error ;
3+ use std:: path:: PathBuf ;
164
175use crate :: os:: Os ;
186
19- #[ derive( Debug , Error ) ]
7+ #[ derive( Debug , thiserror :: Error ) ]
208pub enum DirectoryError {
219 #[ error( "home directory not found" ) ]
2210 NoHomeDirectory ,
23- #[ cfg( unix) ]
24- #[ error( "runtime directory not found: neither XDG_RUNTIME_DIR nor TMPDIR were found" ) ]
25- NoRuntimeDirectory ,
2611 #[ error( "IO Error: {0}" ) ]
2712 Io ( #[ from] std:: io:: Error ) ,
28- #[ error( transparent) ]
29- TimeFormat ( #[ from] time:: error:: Format ) ,
30- #[ error( transparent) ]
31- Utf8FromPath ( #[ from] camino:: FromPathError ) ,
32- #[ error( transparent) ]
33- Utf8FromPathBuf ( #[ from] camino:: FromPathBufError ) ,
34- #[ error( transparent) ]
35- FromVecWithNul ( #[ from] std:: ffi:: FromVecWithNulError ) ,
36- #[ error( transparent) ]
37- IntoString ( #[ from] std:: ffi:: IntoStringError ) ,
38- #[ error( transparent) ]
39- StripPrefix ( #[ from] StripPrefixError ) ,
40- #[ error( transparent) ]
41- PathExpand ( #[ from] shellexpand:: LookupError < VarError > ) ,
42- #[ error( transparent) ]
43- GlobCreation ( #[ from] globset:: Error ) ,
4413}
4514
4615pub mod workspace {
4716 //! Project-level paths (relative to current working directory)
48- pub const AGENTS_DIR : & str = ".amazonq/cli-agents" ;
49- pub const PROMPTS_DIR : & str = ".amazonq/prompts" ;
5017 pub const MCP_CONFIG : & str = ".amazonq/mcp.json" ;
51- pub const TODO_LISTS_DIR : & str = ".amazonq/cli-todo-lists" ;
52- pub const SUBAGENTS_DIR : & str = ".amazonq/.subagents" ;
5318 pub const RULES_PATTERN : & str = ".amazonq/rules/**/*.md" ;
54-
55- // Default documentation files for agent resources
56- pub const DEFAULT_AGENT_RESOURCES : & [ & str ] = & [ "file://AmazonQ.md" , "file://AGENTS.md" , "file://README.md" ] ;
5719}
5820
5921pub mod global {
6022 //! User-level paths (relative to home directory)
61- pub const AGENTS_DIR : & str = ".aws/amazonq/cli-agents" ;
62- pub const PROMPTS_DIR : & str = ".aws/amazonq/prompts" ;
6323 pub const MCP_CONFIG : & str = ".aws/amazonq/mcp.json" ;
64- pub const SHADOW_REPO_DIR : & str = ".aws/amazonq/cli-checkouts" ;
65- pub const CLI_BASH_HISTORY : & str = ".aws/amazonq/.cli_bash_history" ;
6624 pub const GLOBAL_CONTEXT : & str = ".aws/amazonq/global_context.json" ;
6725 pub const PROFILES_DIR : & str = ".aws/amazonq/profiles" ;
68- pub const KNOWLEDGE_BASES_DIR : & str = ".aws/amazonq/knowledge_bases" ;
6926}
7027
7128pub mod application {
7229 //! Application data paths (system-specific)
30+ #[ cfg( unix) ]
7331 pub const DATA_DIR_NAME : & str = "amazon-q" ;
32+ #[ cfg( windows) ]
33+ pub const DATA_DIR_NAME : & str = "AmazonQ" ;
7434 pub const SETTINGS_FILE : & str = "settings.json" ;
7535 pub const DATABASE_FILE : & str = "data.sqlite3" ;
76- pub const UPDATE_LOCK_FILE : & str = "update.lock" ;
77- pub const BACKUP_DIR_NAME : & str = ".amazon-q.dotfiles.bak" ;
7836}
7937
8038type Result < T , E = DirectoryError > = std:: result:: Result < T , E > ;
@@ -121,116 +79,14 @@ pub fn home_dir(#[cfg_attr(windows, allow(unused_variables))] os: &Os) -> Result
12179 }
12280}
12381
124- /// Get the macos tempdir from the `confstr` function
125- #[ cfg( target_os = "macos" ) ]
126- fn macos_tempdir ( ) -> Result < PathBuf > {
127- let len = unsafe { libc:: confstr ( libc:: _CS_DARWIN_USER_TEMP_DIR, std:: ptr:: null :: < i8 > ( ) . cast_mut ( ) , 0 ) } ;
128- let mut buf: Vec < u8 > = vec ! [ 0 ; len] ;
129- unsafe { libc:: confstr ( libc:: _CS_DARWIN_USER_TEMP_DIR, buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) ) } ;
130- let c_string = std:: ffi:: CString :: from_vec_with_nul ( buf) ?;
131- let str = c_string. into_string ( ) ?;
132- Ok ( PathBuf :: from ( str) )
133- }
134-
135- /// Runtime dir for logs and sockets
136- #[ cfg( unix) ]
137- pub fn runtime_dir ( ) -> Result < PathBuf > {
138- let mut dir = dirs:: runtime_dir ( ) ;
139- dir = dir. or_else ( || std:: env:: var_os ( "TMPDIR" ) . map ( PathBuf :: from) ) ;
140-
141- #[ cfg( target_os = "macos" ) ]
142- {
143- let macos_tempdir = macos_tempdir ( ) ?;
144- dir = dir. or ( Some ( macos_tempdir) ) ;
145- }
146- #[ cfg( not( target_os = "macos" ) ) ]
147- {
148- dir = dir. or_else ( || Some ( std:: env:: temp_dir ( ) ) ) ;
149- }
150-
151- dir. ok_or ( DirectoryError :: NoRuntimeDirectory )
152- }
153-
15482/// The application data directory
155- /// - Linux: `$XDG_DATA_HOME/amazon-q ` or `$HOME/.local/share/amazon-q `
156- /// - MacOS: `$HOME/Library/Application Support/amazon-q `
157- /// - Windows: `%LOCALAPPDATA%\AmazonQ `
83+ /// - Linux: `$XDG_DATA_HOME/{data_dir} ` or `$HOME/.local/share/{data_dir} `
84+ /// - MacOS: `$HOME/Library/Application Support/{data_dir} `
85+ /// - Windows: `%LOCALAPPDATA%\{data_dir} `
15886pub fn app_data_dir ( ) -> Result < PathBuf > {
159- #[ cfg( unix) ]
160- {
161- Ok ( dirs:: data_local_dir ( )
162- . ok_or ( DirectoryError :: NoHomeDirectory ) ?
163- . join ( application:: DATA_DIR_NAME ) )
164- }
165- #[ cfg( windows) ]
166- {
167- Ok ( dirs:: data_local_dir ( )
168- . ok_or ( DirectoryError :: NoHomeDirectory ) ?
169- . join ( "AmazonQ" ) )
170- }
171- }
172-
173- /// The directory to all the logs
174- pub fn logs_dir ( ) -> Result < PathBuf > {
175- #[ cfg( unix) ]
176- {
177- Ok ( runtime_dir ( ) ?. join ( "qlog" ) )
178- }
179- #[ cfg( windows) ]
180- {
181- Ok ( std:: env:: temp_dir ( ) . join ( application:: DATA_DIR_NAME ) . join ( "logs" ) )
182- }
183- }
184-
185- /// Canonicalizes path given by expanding the path given
186- pub fn canonicalizes_path ( os : & Os , path_as_str : & str ) -> Result < String > {
187- let context = |input : & str | Ok ( os. env . get ( input) . ok ( ) ) ;
188- let home_dir_fn = || os. env . home ( ) . map ( |p| p. to_string_lossy ( ) . to_string ( ) ) ;
189-
190- let expanded = shellexpand:: full_with_context ( path_as_str, home_dir_fn, context) ?;
191- let path_buf = if !expanded. starts_with ( "/" ) {
192- let current_dir = os. env . current_dir ( ) ?;
193- current_dir. join ( expanded. as_ref ( ) as & str )
194- } else {
195- PathBuf :: from ( expanded. as_ref ( ) as & str )
196- } ;
197-
198- match path_buf. canonicalize ( ) {
199- Ok ( normalized) => Ok ( normalized. as_path ( ) . to_string_lossy ( ) . to_string ( ) ) ,
200- Err ( _) => {
201- let normalized = normalize_path ( & path_buf) ;
202- Ok ( normalized. to_string_lossy ( ) . to_string ( ) )
203- } ,
204- }
205- }
206-
207- /// Manually normalize a path by resolving . and .. components
208- fn normalize_path ( path : & std:: path:: Path ) -> std:: path:: PathBuf {
209- let mut components = Vec :: new ( ) ;
210- for component in path. components ( ) {
211- match component {
212- std:: path:: Component :: CurDir => { } ,
213- std:: path:: Component :: ParentDir => {
214- components. pop ( ) ;
215- } ,
216- _ => {
217- components. push ( component) ;
218- } ,
219- }
220- }
221- components. iter ( ) . collect ( )
222- }
223-
224- /// Given a globset builder and a path, build globs for both the file and directory patterns
225- /// This is needed because by default glob does not match children of a dir so we need both
226- /// patterns to exist in a globset.
227- pub fn add_gitignore_globs ( builder : & mut GlobSetBuilder , path : & str ) -> Result < ( ) > {
228- let glob_for_file = Glob :: new ( path) ?;
229- let dir_pattern: String = format ! ( "{}/**" , path. trim_end_matches( '/' ) ) ;
230- let glob_for_dir = Glob :: new ( & dir_pattern) ?;
231- builder. add ( glob_for_file) ;
232- builder. add ( glob_for_dir) ;
233- Ok ( ( ) )
87+ Ok ( dirs:: data_local_dir ( )
88+ . ok_or ( DirectoryError :: NoHomeDirectory ) ?
89+ . join ( application:: DATA_DIR_NAME ) )
23490}
23591
23692/// Path resolver with hierarchy-aware methods
@@ -252,11 +108,6 @@ impl<'a> PathResolver<'a> {
252108 pub fn global ( & self ) -> GlobalPaths < ' _ > {
253109 GlobalPaths { os : self . os }
254110 }
255-
256- /// Get application-scoped path resolver
257- pub fn application ( & self ) -> ApplicationPaths < ' _ > {
258- ApplicationPaths { os : self . os }
259- }
260111}
261112
262113/// Workspace-scoped path methods
@@ -265,33 +116,9 @@ pub struct WorkspacePaths<'a> {
265116}
266117
267118impl < ' a > WorkspacePaths < ' a > {
268- pub fn agents_dir ( & self ) -> Result < PathBuf > {
269- Ok ( self . os . env . current_dir ( ) ?. join ( workspace:: AGENTS_DIR ) )
270- }
271-
272- pub fn prompts_dir ( & self ) -> Result < PathBuf > {
273- Ok ( self . os . env . current_dir ( ) ?. join ( workspace:: PROMPTS_DIR ) )
274- }
275-
276119 pub fn mcp_config ( & self ) -> Result < PathBuf > {
277120 Ok ( self . os . env . current_dir ( ) ?. join ( workspace:: MCP_CONFIG ) )
278121 }
279-
280- pub fn todo_lists_dir ( & self ) -> Result < PathBuf > {
281- Ok ( self . os . env . current_dir ( ) ?. join ( workspace:: TODO_LISTS_DIR ) )
282- }
283-
284- pub fn subagents_dir ( & self ) -> Result < PathBuf > {
285- Ok ( self . os . env . current_dir ( ) ?. join ( workspace:: SUBAGENTS_DIR ) )
286- }
287-
288- pub async fn ensure_subagents_dir ( & self ) -> Result < PathBuf > {
289- let dir = self . subagents_dir ( ) ?;
290- if !dir. exists ( ) {
291- self . os . fs . create_dir_all ( & dir) . await ?;
292- }
293- Ok ( dir)
294- }
295122}
296123
297124/// Global-scoped path methods
@@ -300,88 +127,23 @@ pub struct GlobalPaths<'a> {
300127}
301128
302129impl < ' a > GlobalPaths < ' a > {
303- pub fn agents_dir ( & self ) -> Result < PathBuf > {
304- Ok ( home_dir ( self . os ) ?. join ( global:: AGENTS_DIR ) )
305- }
306-
307- pub fn prompts_dir ( & self ) -> Result < PathBuf > {
308- Ok ( home_dir ( self . os ) ?. join ( global:: PROMPTS_DIR ) )
309- }
310-
311130 pub fn mcp_config ( & self ) -> Result < PathBuf > {
312131 Ok ( home_dir ( self . os ) ?. join ( global:: MCP_CONFIG ) )
313132 }
314133
315- pub fn shadow_repo_dir ( & self ) -> Result < PathBuf > {
316- Ok ( home_dir ( self . os ) ?. join ( global:: SHADOW_REPO_DIR ) )
317- }
318-
319- pub fn cli_bash_history ( & self ) -> Result < PathBuf > {
320- Ok ( home_dir ( self . os ) ?. join ( global:: CLI_BASH_HISTORY ) )
321- }
322-
323134 pub fn global_context ( & self ) -> Result < PathBuf > {
324135 Ok ( home_dir ( self . os ) ?. join ( global:: GLOBAL_CONTEXT ) )
325136 }
326137
327138 pub fn profiles_dir ( & self ) -> Result < PathBuf > {
328139 Ok ( home_dir ( self . os ) ?. join ( global:: PROFILES_DIR ) )
329140 }
330-
331- pub fn knowledge_bases_dir ( & self ) -> Result < PathBuf > {
332- Ok ( home_dir ( self . os ) ?. join ( global:: KNOWLEDGE_BASES_DIR ) )
333- }
334-
335- pub fn mcp_auth_dir ( & self ) -> Result < PathBuf > {
336- Ok ( home_dir ( self . os ) ?. join ( ".aws" ) . join ( "sso" ) . join ( "cache" ) )
337- }
338-
339- pub async fn ensure_agents_dir ( & self ) -> Result < PathBuf > {
340- let dir = self . agents_dir ( ) ?;
341- if !dir. exists ( ) {
342- self . os . fs . create_dir_all ( & dir) . await ?;
343- }
344- Ok ( dir)
345- }
346- }
347-
348- /// Application-scoped path methods
349- pub struct ApplicationPaths < ' a > {
350- os : & ' a Os ,
351141}
352142
353- #[ allow( clippy:: unused_self) ] // Allow unused self during migration
354- impl < ' a > ApplicationPaths < ' a > {
355- /// The application data directory
356- pub fn data_dir ( & self ) -> Result < PathBuf > {
357- app_data_dir ( )
358- }
359-
360- /// The application settings file
361- pub fn settings_path ( & self ) -> Result < PathBuf > {
362- Ok ( app_data_dir ( ) ?. join ( application:: SETTINGS_FILE ) )
363- }
364-
365- /// The application database file
366- pub fn database_path ( & self ) -> Result < PathBuf > {
367- Ok ( app_data_dir ( ) ?. join ( application:: DATABASE_FILE ) )
368- }
369-
370- /// The application update lock file
371- pub fn update_lock_path ( & self ) -> Result < PathBuf > {
372- Ok ( app_data_dir ( ) ?. join ( application:: UPDATE_LOCK_FILE ) )
373- }
374-
375- /// The backup directory
376- pub fn backups_dir ( & self ) -> Result < PathBuf > {
377- Ok ( home_dir ( self . os ) ?. join ( application:: BACKUP_DIR_NAME ) )
378- }
379-
380- /// The logs directory
381- pub fn logs_dir ( & self ) -> Result < PathBuf > {
382- logs_dir ( )
383- }
143+ /// Application path static methods
144+ pub struct ApplicationPaths ;
384145
146+ impl ApplicationPaths {
385147 /// Static method for settings path (to avoid circular dependency)
386148 pub fn settings_path_static ( ) -> Result < PathBuf > {
387149 Ok ( app_data_dir ( ) ?. join ( application:: SETTINGS_FILE ) )
0 commit comments