@@ -201,6 +201,62 @@ describe("SessionInstructions", () => {
201201 } ) ,
202202 )
203203
204+ it . effect ( "discovers AGENTS.md on a directory listing, including the listed directory's own, and dedups with a later file read" , ( ) =>
205+ Effect . gen ( function * ( ) {
206+ const location = yield * Location . Service
207+ const dir = location . directory
208+ const rootPath = path . resolve ( dir , "AGENTS.md" )
209+ const pkgPath = path . resolve ( dir , "packages" , "foo" , "AGENTS.md" )
210+ yield * mkdir ( path . resolve ( dir , "packages" , "foo" ) )
211+ yield * writeAgents ( rootPath , "root-instructions" )
212+ yield * writeAgents ( pkgPath , "pkg-instructions" )
213+ yield * Effect . promise ( ( ) => fs . writeFile ( path . resolve ( dir , "packages" , "foo" , "file.txt" ) , "content" ) )
214+
215+ const session = yield * SessionV2 . Service
216+ const registry = yield * ToolRegistry . Service
217+ const sessionID = ( yield * session . create ( { location : Location . Ref . make ( { directory : dir } ) } ) ) . id
218+
219+ // Listing packages/foo/ discovers its own AGENTS.md, walking up to but excluding
220+ // the Location root (already supplied by the core/instructions baseline).
221+ yield * settleTool ( registry , readCall ( sessionID , "call-list" , "packages/foo" ) )
222+
223+ const firstInjected = yield * synthetics ( sessionID )
224+ expect ( firstInjected ) . toHaveLength ( 1 )
225+ expect ( firstInjected [ 0 ] ! . text ) . toBe ( `Instructions from: ${ pkgPath } \npkg-instructions` )
226+ expect ( firstInjected [ 0 ] ! . description ) . toBe ( `Loaded ${ path . relative ( dir , pkgPath ) } ` )
227+ expect ( firstInjected [ 0 ] ! . metadata ) . toEqual ( { instruction : { paths : [ pkgPath ] } } )
228+ expect ( firstInjected [ 0 ] ! . text ) . not . toContain ( "root-instructions" )
229+
230+ // A subsequent file read under the listed directory is a dedup: pkg's AGENTS.md is
231+ // already injected for this session, so nothing new is emitted.
232+ yield * settleTool ( registry , readCall ( sessionID , "call-file" , "packages/foo/file.txt" ) )
233+
234+ expect ( ( yield * synthetics ( sessionID ) ) ) . toHaveLength ( 1 )
235+ } ) ,
236+ )
237+
238+ it . effect ( "listing the Location root directory injects no instructions" , ( ) =>
239+ Effect . gen ( function * ( ) {
240+ const location = yield * Location . Service
241+ const dir = location . directory
242+ const rootPath = path . resolve ( dir , "AGENTS.md" )
243+ const subPath = path . resolve ( dir , "sub" , "AGENTS.md" )
244+ yield * mkdir ( path . resolve ( dir , "sub" ) )
245+ yield * writeAgents ( rootPath , "root-instructions" )
246+ yield * writeAgents ( subPath , "sub-instructions" )
247+
248+ const session = yield * SessionV2 . Service
249+ const registry = yield * ToolRegistry . Service
250+ const sessionID = ( yield * session . create ( { location : Location . Ref . make ( { directory : dir } ) } ) ) . id
251+
252+ // The walk starts and stops at the Location root: the root AGENTS.md is searched but
253+ // dropped by the dirname filter, and up() only walks upward so nested dirs are unseen.
254+ yield * settleTool ( registry , readCall ( sessionID , "call-root-list" , "." ) )
255+
256+ expect ( ( yield * synthetics ( sessionID ) ) ) . toHaveLength ( 0 )
257+ } ) ,
258+ )
259+
204260 it . effect ( "loads instructions directly without a read" , ( ) =>
205261 Effect . gen ( function * ( ) {
206262 const location = yield * Location . Service
0 commit comments