@@ -27,30 +27,38 @@ async function safeReadFile(filePath: string): Promise<string> {
2727}
2828
2929/**
30- * Safely read a file and follow symlinks if necessary
30+ * Resolve a symlink to its target path
3131 */
32- async function safeReadFileFollowingSymlinks ( filePath : string ) : Promise < string > {
32+ async function resolveSymlinkPath ( symlinkPath : string , depth : number = 0 ) : Promise < string > {
33+ // Avoid cyclic symlinks
34+ if ( depth > MAX_DEPTH ) {
35+ throw new Error ( `Maximum symlink depth exceeded for ${ symlinkPath } ` )
36+ }
37+
3338 try {
3439 // Check if the path is a symlink
35- const stats = await fs . lstat ( filePath )
36- if ( stats . isSymbolicLink ( ) ) {
37- // Resolve the symlink to get the actual file path
38- const linkTarget = await fs . readlink ( filePath )
39- const resolvedPath = path . resolve ( path . dirname ( filePath ) , linkTarget )
40- // Read from the resolved path
41- const content = await fs . readFile ( resolvedPath , "utf-8" )
42- return content . trim ( )
43- } else {
44- // Not a symlink, read normally
45- const content = await fs . readFile ( filePath , "utf-8" )
46- return content . trim ( )
40+ const stats = await fs . lstat ( symlinkPath )
41+ if ( ! stats . isSymbolicLink ( ) ) {
42+ // Not a symlink, return the original path
43+ return symlinkPath
4744 }
48- } catch ( err ) {
49- const errorCode = ( err as NodeJS . ErrnoException ) . code
50- if ( ! errorCode || ! [ "ENOENT" , "EISDIR" ] . includes ( errorCode ) ) {
51- throw err
45+
46+ // Get the symlink target
47+ const linkTarget = await fs . readlink ( symlinkPath )
48+ // Resolve the target path (relative to the symlink location)
49+ const resolvedTarget = path . resolve ( path . dirname ( symlinkPath ) , linkTarget )
50+
51+ // Check if the resolved target is also a symlink (handle nested symlinks)
52+ const targetStats = await fs . lstat ( resolvedTarget )
53+ if ( targetStats . isSymbolicLink ( ) ) {
54+ // Recursively resolve nested symlinks
55+ return resolveSymlinkPath ( resolvedTarget , depth + 1 )
5256 }
53- return ""
57+
58+ return resolvedTarget
59+ } catch ( err ) {
60+ // If we can't resolve the symlink, return the original path
61+ return symlinkPath
5462 }
5563}
5664
@@ -250,7 +258,10 @@ export async function loadRuleFiles(cwd: string): Promise<string> {
250258async function loadAgentRulesFile ( cwd : string ) : Promise < string > {
251259 try {
252260 const agentsPath = path . join ( cwd , "AGENTS.md" )
253- const content = await safeReadFileFollowingSymlinks ( agentsPath )
261+ // Resolve symlinks if necessary
262+ const resolvedPath = await resolveSymlinkPath ( agentsPath )
263+ // Read the file content
264+ const content = await safeReadFile ( resolvedPath )
254265 if ( content ) {
255266 return `# Agent Rules Standard (AGENTS.md):\n${ content } `
256267 }
0 commit comments