@@ -2029,6 +2029,162 @@ The agent handles various tasks and operations in the system.
20292029 } )
20302030 } )
20312031
2032+ describe ( "stale file detection" , ( ) => {
2033+ it ( "should log verbose message when overwriting existing file with different content" , async ( ) => {
2034+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2035+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2036+
2037+ // Ensure target directory exists
2038+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2039+
2040+ // Create stale (different) content in target
2041+ writeFileSync (
2042+ join ( AGENTS_TARGET_DIR , "opencoder.md" ) ,
2043+ "# Old stale content\nThis is different from the source" ,
2044+ )
2045+
2046+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
2047+ cwd : process . cwd ( ) ,
2048+ stdout : "pipe" ,
2049+ stderr : "pipe" ,
2050+ } )
2051+
2052+ const exitCode = await proc . exited
2053+ const stdout = await new Response ( proc . stdout ) . text ( )
2054+
2055+ expect ( exitCode ) . toBe ( 0 )
2056+ // Should contain the stale file detection message
2057+ expect ( stdout ) . toContain (
2058+ "[VERBOSE] Overwriting existing file: opencoder.md (content differs)" ,
2059+ )
2060+
2061+ // Clean up
2062+ for ( const file of agentFiles ) {
2063+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2064+ if ( existsSync ( targetPath ) ) {
2065+ rmSync ( targetPath )
2066+ }
2067+ }
2068+ } )
2069+
2070+ it ( "should log verbose message when target file has unchanged content" , async ( ) => {
2071+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2072+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2073+
2074+ // Ensure target directory exists
2075+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2076+
2077+ // Copy the actual source content to target (identical content)
2078+ const sourceContent = readFileSync ( join ( process . cwd ( ) , "agents" , "opencoder.md" ) , "utf-8" )
2079+ writeFileSync ( join ( AGENTS_TARGET_DIR , "opencoder.md" ) , sourceContent )
2080+
2081+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
2082+ cwd : process . cwd ( ) ,
2083+ stdout : "pipe" ,
2084+ stderr : "pipe" ,
2085+ } )
2086+
2087+ const exitCode = await proc . exited
2088+ const stdout = await new Response ( proc . stdout ) . text ( )
2089+
2090+ expect ( exitCode ) . toBe ( 0 )
2091+ // Should contain the unchanged message
2092+ expect ( stdout ) . toContain ( "[VERBOSE] Target file unchanged: opencoder.md" )
2093+ // Should NOT contain the overwriting message for this file
2094+ expect ( stdout ) . not . toContain ( "Overwriting existing file: opencoder.md" )
2095+
2096+ // Clean up
2097+ for ( const file of agentFiles ) {
2098+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2099+ if ( existsSync ( targetPath ) ) {
2100+ rmSync ( targetPath )
2101+ }
2102+ }
2103+ } )
2104+
2105+ it ( "should not log stale file message without --verbose flag" , async ( ) => {
2106+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2107+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2108+
2109+ // Ensure target directory exists
2110+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2111+
2112+ // Create stale content in target
2113+ writeFileSync (
2114+ join ( AGENTS_TARGET_DIR , "opencoder.md" ) ,
2115+ "# Old stale content\nThis is different from the source" ,
2116+ )
2117+
2118+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" ] , {
2119+ cwd : process . cwd ( ) ,
2120+ stdout : "pipe" ,
2121+ stderr : "pipe" ,
2122+ } )
2123+
2124+ const exitCode = await proc . exited
2125+ const stdout = await new Response ( proc . stdout ) . text ( )
2126+
2127+ expect ( exitCode ) . toBe ( 0 )
2128+ // Should NOT contain verbose markers or stale file message
2129+ expect ( stdout ) . not . toContain ( "[VERBOSE]" )
2130+ expect ( stdout ) . not . toContain ( "Overwriting existing file" )
2131+ expect ( stdout ) . not . toContain ( "content differs" )
2132+
2133+ // Clean up
2134+ for ( const file of agentFiles ) {
2135+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2136+ if ( existsSync ( targetPath ) ) {
2137+ rmSync ( targetPath )
2138+ }
2139+ }
2140+ } )
2141+
2142+ it ( "should detect stale files during actual install (not dry-run)" , async ( ) => {
2143+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2144+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2145+
2146+ // Ensure target directory exists
2147+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2148+
2149+ // Create stale content in target for all files
2150+ for ( const file of agentFiles ) {
2151+ writeFileSync (
2152+ join ( AGENTS_TARGET_DIR , file ) ,
2153+ `# Old stale content for ${ file } \nThis is different` ,
2154+ )
2155+ }
2156+
2157+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--verbose" ] , {
2158+ cwd : process . cwd ( ) ,
2159+ stdout : "pipe" ,
2160+ stderr : "pipe" ,
2161+ } )
2162+
2163+ const exitCode = await proc . exited
2164+ const stdout = await new Response ( proc . stdout ) . text ( )
2165+
2166+ expect ( exitCode ) . toBe ( 0 )
2167+ // Should detect stale files for all three agents
2168+ expect ( stdout ) . toContain (
2169+ "[VERBOSE] Overwriting existing file: opencoder.md (content differs)" ,
2170+ )
2171+ expect ( stdout ) . toContain (
2172+ "[VERBOSE] Overwriting existing file: opencoder-planner.md (content differs)" ,
2173+ )
2174+ expect ( stdout ) . toContain (
2175+ "[VERBOSE] Overwriting existing file: opencoder-builder.md (content differs)" ,
2176+ )
2177+
2178+ // Clean up
2179+ for ( const file of agentFiles ) {
2180+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2181+ if ( existsSync ( targetPath ) ) {
2182+ rmSync ( targetPath )
2183+ }
2184+ }
2185+ } )
2186+ } )
2187+
20322188 describe ( "shipped agent files validation" , ( ) => {
20332189 it ( "should validate all shipped agent files pass content validation" , async ( ) => {
20342190 const { AGENT_NAMES , validateAgentContent } = await import ( "../src/paths.mjs" )
0 commit comments