@@ -1738,6 +1738,297 @@ The agent handles various tasks and operations in the system.
17381738 } )
17391739 } )
17401740
1741+ describe ( "CLI flags: --help" , ( ) => {
1742+ it ( "postinstall --help shows usage and exits 0" , async ( ) => {
1743+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--help" ] , {
1744+ cwd : process . cwd ( ) ,
1745+ stdout : "pipe" ,
1746+ stderr : "pipe" ,
1747+ } )
1748+
1749+ const exitCode = await proc . exited
1750+ const stdout = await new Response ( proc . stdout ) . text ( )
1751+ const stderr = await new Response ( proc . stderr ) . text ( )
1752+
1753+ expect ( exitCode ) . toBe ( 0 )
1754+ expect ( stdout ) . toContain ( "Usage:" )
1755+ expect ( stdout ) . toContain ( "--dry-run" )
1756+ expect ( stdout ) . toContain ( "--verbose" )
1757+ expect ( stdout ) . toContain ( "--help" )
1758+ expect ( stdout ) . toContain ( "Examples:" )
1759+ expect ( stderr ) . toBe ( "" )
1760+ } )
1761+
1762+ it ( "preuninstall --help shows usage and exits 0" , async ( ) => {
1763+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--help" ] , {
1764+ cwd : process . cwd ( ) ,
1765+ stdout : "pipe" ,
1766+ stderr : "pipe" ,
1767+ } )
1768+
1769+ const exitCode = await proc . exited
1770+ const stdout = await new Response ( proc . stdout ) . text ( )
1771+ const stderr = await new Response ( proc . stderr ) . text ( )
1772+
1773+ expect ( exitCode ) . toBe ( 0 )
1774+ expect ( stdout ) . toContain ( "Usage:" )
1775+ expect ( stdout ) . toContain ( "--dry-run" )
1776+ expect ( stdout ) . toContain ( "--verbose" )
1777+ expect ( stdout ) . toContain ( "--help" )
1778+ expect ( stdout ) . toContain ( "Examples:" )
1779+ expect ( stderr ) . toBe ( "" )
1780+ } )
1781+
1782+ it ( "postinstall --help does not perform installation" , async ( ) => {
1783+ // Track state before --help
1784+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1785+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1786+
1787+ // Ensure agents are NOT installed
1788+ for ( const file of agentFiles ) {
1789+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1790+ if ( existsSync ( targetPath ) ) {
1791+ rmSync ( targetPath )
1792+ }
1793+ }
1794+
1795+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--help" ] , {
1796+ cwd : process . cwd ( ) ,
1797+ stdout : "pipe" ,
1798+ stderr : "pipe" ,
1799+ } )
1800+
1801+ await proc . exited
1802+
1803+ // Verify no files were created
1804+ for ( const file of agentFiles ) {
1805+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1806+ expect ( existsSync ( targetPath ) ) . toBe ( false )
1807+ }
1808+ } )
1809+ } )
1810+
1811+ describe ( "CLI flags: --dry-run" , ( ) => {
1812+ it ( "postinstall --dry-run does not create files" , async ( ) => {
1813+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1814+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1815+
1816+ // Ensure agents are NOT installed
1817+ for ( const file of agentFiles ) {
1818+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1819+ if ( existsSync ( targetPath ) ) {
1820+ rmSync ( targetPath )
1821+ }
1822+ }
1823+
1824+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" ] , {
1825+ cwd : process . cwd ( ) ,
1826+ stdout : "pipe" ,
1827+ stderr : "pipe" ,
1828+ } )
1829+
1830+ const exitCode = await proc . exited
1831+ const stdout = await new Response ( proc . stdout ) . text ( )
1832+
1833+ expect ( exitCode ) . toBe ( 0 )
1834+ expect ( stdout ) . toContain ( "[DRY-RUN]" )
1835+ expect ( stdout ) . toContain ( "Would install:" )
1836+
1837+ // Verify no files were actually created
1838+ for ( const file of agentFiles ) {
1839+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1840+ expect ( existsSync ( targetPath ) ) . toBe ( false )
1841+ }
1842+ } )
1843+
1844+ it ( "preuninstall --dry-run does not delete files" , async ( ) => {
1845+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1846+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1847+
1848+ // Ensure target directory exists
1849+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
1850+
1851+ // Install agents first
1852+ for ( const file of agentFiles ) {
1853+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
1854+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1855+ if ( existsSync ( sourcePath ) ) {
1856+ const content = readFileSync ( sourcePath , "utf-8" )
1857+ writeFileSync ( targetPath , content )
1858+ }
1859+ }
1860+
1861+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" ] , {
1862+ cwd : process . cwd ( ) ,
1863+ stdout : "pipe" ,
1864+ stderr : "pipe" ,
1865+ } )
1866+
1867+ const exitCode = await proc . exited
1868+ const stdout = await new Response ( proc . stdout ) . text ( )
1869+
1870+ expect ( exitCode ) . toBe ( 0 )
1871+ expect ( stdout ) . toContain ( "[DRY-RUN]" )
1872+ expect ( stdout ) . toContain ( "Would remove:" )
1873+
1874+ // Verify files were NOT deleted
1875+ for ( const file of agentFiles ) {
1876+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1877+ expect ( existsSync ( targetPath ) ) . toBe ( true )
1878+ }
1879+
1880+ // Clean up
1881+ for ( const file of agentFiles ) {
1882+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1883+ if ( existsSync ( targetPath ) ) {
1884+ rmSync ( targetPath )
1885+ }
1886+ }
1887+ } )
1888+
1889+ it ( "postinstall --dry-run does not create target directory" , async ( ) => {
1890+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1891+
1892+ // Remove the target directory if it exists
1893+ if ( existsSync ( AGENTS_TARGET_DIR ) ) {
1894+ // Only remove if empty or contains only our agents
1895+ const files = readdirSync ( AGENTS_TARGET_DIR )
1896+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1897+ const onlyOurAgents = files . every ( ( f ) => agentFiles . includes ( f ) )
1898+
1899+ if ( onlyOurAgents ) {
1900+ rmSync ( AGENTS_TARGET_DIR , { recursive : true } )
1901+ }
1902+ }
1903+
1904+ // Only run test if we successfully removed the directory
1905+ if ( ! existsSync ( AGENTS_TARGET_DIR ) ) {
1906+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" ] , {
1907+ cwd : process . cwd ( ) ,
1908+ stdout : "pipe" ,
1909+ stderr : "pipe" ,
1910+ } )
1911+
1912+ const exitCode = await proc . exited
1913+ const stdout = await new Response ( proc . stdout ) . text ( )
1914+
1915+ expect ( exitCode ) . toBe ( 0 )
1916+ expect ( stdout ) . toContain ( "Would create" )
1917+
1918+ // Verify directory was NOT created
1919+ expect ( existsSync ( AGENTS_TARGET_DIR ) ) . toBe ( false )
1920+ }
1921+ } )
1922+ } )
1923+
1924+ describe ( "CLI flags: --verbose" , ( ) => {
1925+ it ( "postinstall --verbose shows detailed output" , async ( ) => {
1926+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
1927+ cwd : process . cwd ( ) ,
1928+ stdout : "pipe" ,
1929+ stderr : "pipe" ,
1930+ } )
1931+
1932+ const exitCode = await proc . exited
1933+ const stdout = await new Response ( proc . stdout ) . text ( )
1934+
1935+ expect ( exitCode ) . toBe ( 0 )
1936+ expect ( stdout ) . toContain ( "[VERBOSE]" )
1937+ expect ( stdout ) . toContain ( "Package root:" )
1938+ expect ( stdout ) . toContain ( "Source directory:" )
1939+ expect ( stdout ) . toContain ( "Target directory:" )
1940+ expect ( stdout ) . toContain ( "Dry run:" )
1941+ } )
1942+
1943+ it ( "preuninstall --verbose shows detailed output" , async ( ) => {
1944+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1945+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1946+
1947+ // Ensure target directory exists with agents
1948+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
1949+ for ( const file of agentFiles ) {
1950+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
1951+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1952+ if ( existsSync ( sourcePath ) ) {
1953+ const content = readFileSync ( sourcePath , "utf-8" )
1954+ writeFileSync ( targetPath , content )
1955+ }
1956+ }
1957+
1958+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
1959+ cwd : process . cwd ( ) ,
1960+ stdout : "pipe" ,
1961+ stderr : "pipe" ,
1962+ } )
1963+
1964+ const exitCode = await proc . exited
1965+ const stdout = await new Response ( proc . stdout ) . text ( )
1966+
1967+ expect ( exitCode ) . toBe ( 0 )
1968+ expect ( stdout ) . toContain ( "[VERBOSE]" )
1969+ expect ( stdout ) . toContain ( "Package root:" )
1970+ expect ( stdout ) . toContain ( "Source directory:" )
1971+ expect ( stdout ) . toContain ( "Target directory:" )
1972+ expect ( stdout ) . toContain ( "Dry run:" )
1973+
1974+ // Clean up
1975+ for ( const file of agentFiles ) {
1976+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1977+ if ( existsSync ( targetPath ) ) {
1978+ rmSync ( targetPath )
1979+ }
1980+ }
1981+ } )
1982+
1983+ it ( "postinstall --verbose shows file processing details" , async ( ) => {
1984+ const proc = Bun . spawn ( [ "node" , "postinstall.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+ expect ( stdout ) . toContain ( "Processing:" )
1995+ expect ( stdout ) . toContain ( "Source path:" )
1996+ expect ( stdout ) . toContain ( "Target path:" )
1997+ expect ( stdout ) . toContain ( "Validation passed" )
1998+ } )
1999+
2000+ it ( "postinstall without --verbose does not show verbose markers" , async ( ) => {
2001+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" ] , {
2002+ cwd : process . cwd ( ) ,
2003+ stdout : "pipe" ,
2004+ stderr : "pipe" ,
2005+ } )
2006+
2007+ const exitCode = await proc . exited
2008+ const stdout = await new Response ( proc . stdout ) . text ( )
2009+
2010+ expect ( exitCode ) . toBe ( 0 )
2011+ expect ( stdout ) . not . toContain ( "[VERBOSE]" )
2012+ expect ( stdout ) . not . toContain ( "Processing:" )
2013+ expect ( stdout ) . not . toContain ( "Validation passed" )
2014+ } )
2015+
2016+ it ( "preuninstall without --verbose does not show verbose markers" , async ( ) => {
2017+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" ] , {
2018+ cwd : process . cwd ( ) ,
2019+ stdout : "pipe" ,
2020+ stderr : "pipe" ,
2021+ } )
2022+
2023+ const exitCode = await proc . exited
2024+ const stdout = await new Response ( proc . stdout ) . text ( )
2025+
2026+ expect ( exitCode ) . toBe ( 0 )
2027+ expect ( stdout ) . not . toContain ( "[VERBOSE]" )
2028+ expect ( stdout ) . not . toContain ( "Processing:" )
2029+ } )
2030+ } )
2031+
17412032 describe ( "full install/uninstall cycle" , ( ) => {
17422033 it ( "should install and then cleanly uninstall" , async ( ) => {
17432034 // Create scripts
0 commit comments