@@ -1921,6 +1921,250 @@ The agent handles various tasks and operations in the system.
19211921 } )
19221922 } )
19231923
1924+ describe ( "preuninstall error handling" , ( ) => {
1925+ it ( "should show verbose skip message for files that don't exist in target" , async ( ) => {
1926+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1927+
1928+ // Ensure target directory exists but is empty (no agents installed)
1929+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
1930+
1931+ // Remove all agent files if they exist
1932+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1933+ for ( const file of agentFiles ) {
1934+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1935+ if ( existsSync ( targetPath ) ) {
1936+ rmSync ( targetPath )
1937+ }
1938+ }
1939+
1940+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
1941+ cwd : process . cwd ( ) ,
1942+ stdout : "pipe" ,
1943+ stderr : "pipe" ,
1944+ } )
1945+
1946+ const exitCode = await proc . exited
1947+ const stdout = await new Response ( proc . stdout ) . text ( )
1948+
1949+ expect ( exitCode ) . toBe ( 0 )
1950+
1951+ // Should show verbose messages for skipped files
1952+ expect ( stdout ) . toContain ( "[VERBOSE]" )
1953+ expect ( stdout ) . toContain ( "File does not exist, skipping" )
1954+
1955+ // Should show processing for each agent file
1956+ expect ( stdout ) . toContain ( "Processing: opencoder.md" )
1957+ expect ( stdout ) . toContain ( "Processing: opencoder-planner.md" )
1958+ expect ( stdout ) . toContain ( "Processing: opencoder-builder.md" )
1959+ } )
1960+
1961+ it ( "should show verbose skip message for partial installation (some files missing)" , async ( ) => {
1962+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1963+
1964+ // Ensure target directory exists
1965+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
1966+
1967+ // Install only one agent file
1968+ const sourcePath = join ( process . cwd ( ) , "agents" , "opencoder.md" )
1969+ const targetPath = join ( AGENTS_TARGET_DIR , "opencoder.md" )
1970+ if ( existsSync ( sourcePath ) ) {
1971+ const content = readFileSync ( sourcePath , "utf-8" )
1972+ writeFileSync ( targetPath , content )
1973+ }
1974+
1975+ // Make sure other files don't exist
1976+ const otherFiles = [ "opencoder-planner.md" , "opencoder-builder.md" ]
1977+ for ( const file of otherFiles ) {
1978+ const filePath = join ( AGENTS_TARGET_DIR , file )
1979+ if ( existsSync ( filePath ) ) {
1980+ rmSync ( filePath )
1981+ }
1982+ }
1983+
1984+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
1985+ cwd : process . cwd ( ) ,
1986+ stdout : "pipe" ,
1987+ stderr : "pipe" ,
1988+ } )
1989+
1990+ const exitCode = await proc . exited
1991+ const stdout = await new Response ( proc . stdout ) . text ( )
1992+
1993+ expect ( exitCode ) . toBe ( 0 )
1994+
1995+ // Should show that existing file would be removed
1996+ expect ( stdout ) . toContain ( "Would remove:" )
1997+ expect ( stdout ) . toContain ( "opencoder.md" )
1998+
1999+ // Should show skip messages for missing files
2000+ expect ( stdout ) . toContain ( "File does not exist, skipping" )
2001+
2002+ // Clean up
2003+ if ( existsSync ( targetPath ) ) {
2004+ rmSync ( targetPath )
2005+ }
2006+ } )
2007+
2008+ it ( "should display proper error message format in verbose mode" , async ( ) => {
2009+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2010+
2011+ // Ensure target directory exists with some agents
2012+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2013+
2014+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2015+ for ( const file of agentFiles ) {
2016+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
2017+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2018+ if ( existsSync ( sourcePath ) ) {
2019+ const content = readFileSync ( sourcePath , "utf-8" )
2020+ writeFileSync ( targetPath , content )
2021+ }
2022+ }
2023+
2024+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
2025+ cwd : process . cwd ( ) ,
2026+ stdout : "pipe" ,
2027+ stderr : "pipe" ,
2028+ } )
2029+
2030+ const exitCode = await proc . exited
2031+ const stdout = await new Response ( proc . stdout ) . text ( )
2032+ const stderr = await new Response ( proc . stderr ) . text ( )
2033+
2034+ expect ( exitCode ) . toBe ( 0 )
2035+
2036+ // Should show verbose details for each file
2037+ expect ( stdout ) . toContain ( "[VERBOSE] Processing:" )
2038+ expect ( stdout ) . toContain ( "[VERBOSE] Target path:" )
2039+ expect ( stdout ) . toContain ( "[VERBOSE] File exists, removing..." )
2040+ expect ( stdout ) . toContain ( "[VERBOSE] Successfully removed" )
2041+
2042+ // Should not have any error messages for successful removal
2043+ expect ( stderr ) . toBe ( "" )
2044+
2045+ // Clean up
2046+ for ( const file of agentFiles ) {
2047+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2048+ if ( existsSync ( targetPath ) ) {
2049+ rmSync ( targetPath )
2050+ }
2051+ }
2052+ } )
2053+
2054+ it ( "should show removal summary in verbose mode" , async ( ) => {
2055+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2056+
2057+ // Ensure target directory exists
2058+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2059+
2060+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2061+ for ( const file of agentFiles ) {
2062+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
2063+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2064+ if ( existsSync ( sourcePath ) ) {
2065+ const content = readFileSync ( sourcePath , "utf-8" )
2066+ writeFileSync ( targetPath , content )
2067+ }
2068+ }
2069+
2070+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
2071+ cwd : process . cwd ( ) ,
2072+ stdout : "pipe" ,
2073+ stderr : "pipe" ,
2074+ } )
2075+
2076+ const exitCode = await proc . exited
2077+ const stdout = await new Response ( proc . stdout ) . text ( )
2078+
2079+ expect ( exitCode ) . toBe ( 0 )
2080+
2081+ // Should show the removal summary
2082+ expect ( stdout ) . toContain ( "[VERBOSE] Removal summary: 3 files removed" )
2083+ expect ( stdout ) . toContain ( "Removed 3 agent(s)" )
2084+
2085+ // Clean up
2086+ for ( const file of agentFiles ) {
2087+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2088+ if ( existsSync ( targetPath ) ) {
2089+ rmSync ( targetPath )
2090+ }
2091+ }
2092+ } )
2093+
2094+ it ( "should handle missing target directory gracefully in verbose mode" , async ( ) => {
2095+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2096+
2097+ // Remove the target directory if it exists
2098+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2099+
2100+ // First, clean up any existing agent files
2101+ for ( const file of agentFiles ) {
2102+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2103+ if ( existsSync ( targetPath ) ) {
2104+ rmSync ( targetPath )
2105+ }
2106+ }
2107+
2108+ // Try to remove the directory if it's empty
2109+ if ( existsSync ( AGENTS_TARGET_DIR ) ) {
2110+ const files = readdirSync ( AGENTS_TARGET_DIR )
2111+ if ( files . length === 0 ) {
2112+ rmSync ( AGENTS_TARGET_DIR , { recursive : true } )
2113+ }
2114+ }
2115+
2116+ // Only run this test if directory was successfully removed
2117+ if ( ! existsSync ( AGENTS_TARGET_DIR ) ) {
2118+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
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+
2129+ // Should show verbose message about target directory check
2130+ expect ( stdout ) . toContain ( "[VERBOSE] Checking if target directory exists..." )
2131+ expect ( stdout ) . toContain ( "[VERBOSE] Target directory does not exist" )
2132+ expect ( stdout ) . toContain ( "No agents directory found, nothing to remove" )
2133+ }
2134+ } )
2135+
2136+ it ( "should show no agents installed message when target exists but has no agent files" , async ( ) => {
2137+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
2138+
2139+ // Ensure target directory exists
2140+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
2141+
2142+ // Remove all agent files
2143+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
2144+ for ( const file of agentFiles ) {
2145+ const targetPath = join ( AGENTS_TARGET_DIR , file )
2146+ if ( existsSync ( targetPath ) ) {
2147+ rmSync ( targetPath )
2148+ }
2149+ }
2150+
2151+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
2152+ cwd : process . cwd ( ) ,
2153+ stdout : "pipe" ,
2154+ stderr : "pipe" ,
2155+ } )
2156+
2157+ const exitCode = await proc . exited
2158+ const stdout = await new Response ( proc . stdout ) . text ( )
2159+
2160+ expect ( exitCode ) . toBe ( 0 )
2161+
2162+ // Should show verbose removal summary with 0 files
2163+ expect ( stdout ) . toContain ( "[VERBOSE] Removal summary: 0 files removed" )
2164+ expect ( stdout ) . toContain ( "No agents were installed, nothing removed" )
2165+ } )
2166+ } )
2167+
19242168 describe ( "CLI flags: --verbose" , ( ) => {
19252169 it ( "postinstall --verbose shows detailed output" , async ( ) => {
19262170 const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
0 commit comments