@@ -2155,6 +2155,44 @@ def set_all_os_env_variables(env_vars: dict[str, str | None]) -> bool:
21552155 return failed_count == 0
21562156
21572157
2158+ def install_nodejs_if_requested (config : dict [str , Any ]) -> bool :
2159+ """Install Node.js LTS if requested in configuration.
2160+
2161+ Checks the 'install-nodejs' config parameter and installs Node.js
2162+ if set to True. Uses the existing ensure_nodejs() function which:
2163+ - Checks if Node.js is already installed (prevents duplicate installation)
2164+ - Tries multiple installation methods with fallbacks
2165+ - Updates PATH after installation
2166+
2167+ Args:
2168+ config: Environment configuration dictionary
2169+
2170+ Returns:
2171+ True if Node.js is installed or not requested, False if installation fails.
2172+ """
2173+ install_nodejs_flag = config .get ('install-nodejs' , False )
2174+
2175+ if not install_nodejs_flag :
2176+ info ('Node.js installation not requested (install-nodejs: false or not set)' )
2177+ return True
2178+
2179+ info ('Node.js installation requested (install-nodejs: true)' )
2180+
2181+ # Late import to avoid circular dependency (install_claude imports from setup_environment)
2182+ from install_claude import ensure_nodejs as _ensure_nodejs
2183+
2184+ if not _ensure_nodejs ():
2185+ error ('Node.js installation failed' )
2186+ return False
2187+
2188+ # Refresh PATH from registry on Windows to pick up new installation
2189+ if platform .system () == 'Windows' :
2190+ refresh_path_from_registry ()
2191+
2192+ success ('Node.js is available' )
2193+ return True
2194+
2195+
21582196def install_dependencies (dependencies : dict [str , list [str ]] | None ) -> bool :
21592197 """Install dependencies from configuration."""
21602198 if not dependencies :
@@ -4165,35 +4203,41 @@ def main() -> None:
41654203 else :
41664204 info ('No custom files to download' )
41674205
4168- # Step 4: Install dependencies (after Claude Code which provides tools)
4206+ # Step 4: Install Node.js if requested (before dependencies)
4207+ print ()
4208+ print (f'{ Colors .CYAN } Step 4: Checking Node.js installation...{ Colors .NC } ' )
4209+ if not install_nodejs_if_requested (config ):
4210+ raise Exception ('Node.js installation failed' )
4211+
4212+ # Step 5: Install dependencies (after Claude Code which provides tools)
41694213 print ()
4170- print (f'{ Colors .CYAN } Step 4 : Installing dependencies...{ Colors .NC } ' )
4214+ print (f'{ Colors .CYAN } Step 5 : Installing dependencies...{ Colors .NC } ' )
41714215 dependencies = config .get ('dependencies' , {})
41724216 install_dependencies (dependencies )
41734217
4174- # Step 5 : Set OS environment variables
4218+ # Step 6 : Set OS environment variables
41754219 print ()
4176- print (f'{ Colors .CYAN } Step 5 : Setting OS environment variables...{ Colors .NC } ' )
4220+ print (f'{ Colors .CYAN } Step 6 : Setting OS environment variables...{ Colors .NC } ' )
41774221 if os_env_variables :
41784222 set_all_os_env_variables (os_env_variables )
41794223 else :
41804224 info ('No OS environment variables to configure' )
41814225
4182- # Step 6 : Process agents
4226+ # Step 7 : Process agents
41834227 print ()
4184- print (f'{ Colors .CYAN } Step 6 : Processing agents...{ Colors .NC } ' )
4228+ print (f'{ Colors .CYAN } Step 7 : Processing agents...{ Colors .NC } ' )
41854229 agents = config .get ('agents' , [])
41864230 process_resources (agents , agents_dir , 'agents' , config_source , base_url , args .auth )
41874231
4188- # Step 7 : Process slash commands
4232+ # Step 8 : Process slash commands
41894233 print ()
4190- print (f'{ Colors .CYAN } Step 7 : Processing slash commands...{ Colors .NC } ' )
4234+ print (f'{ Colors .CYAN } Step 8 : Processing slash commands...{ Colors .NC } ' )
41914235 commands = config .get ('slash-commands' , [])
41924236 process_resources (commands , commands_dir , 'slash commands' , config_source , base_url , args .auth )
41934237
4194- # Step 8 : Process skills
4238+ # Step 9 : Process skills
41954239 print ()
4196- print (f'{ Colors .CYAN } Step 8 : Processing skills...{ Colors .NC } ' )
4240+ print (f'{ Colors .CYAN } Step 9 : Processing skills...{ Colors .NC } ' )
41974241 skills_raw = config .get ('skills' , [])
41984242 # Convert to properly typed list using cast and list comprehension
41994243 skills : list [dict [str , Any ]] = (
@@ -4203,9 +4247,9 @@ def main() -> None:
42034247 )
42044248 process_skills (skills , skills_dir , config_source , args .auth )
42054249
4206- # Step 9 : Process system prompt (if specified)
4250+ # Step 10 : Process system prompt (if specified)
42074251 print ()
4208- print (f'{ Colors .CYAN } Step 9 : Processing system prompt...{ Colors .NC } ' )
4252+ print (f'{ Colors .CYAN } Step 10 : Processing system prompt...{ Colors .NC } ' )
42094253 prompt_path = None
42104254 if system_prompt :
42114255 # Strip query parameters from URL to get clean filename
@@ -4216,9 +4260,9 @@ def main() -> None:
42164260 else :
42174261 info ('No additional system prompt configured' )
42184262
4219- # Step 10 : Configure MCP servers
4263+ # Step 11 : Configure MCP servers
42204264 print ()
4221- print (f'{ Colors .CYAN } Step 10 : Configuring MCP servers...{ Colors .NC } ' )
4265+ print (f'{ Colors .CYAN } Step 11 : Configuring MCP servers...{ Colors .NC } ' )
42224266 mcp_servers = config .get ('mcp-servers' , [])
42234267
42244268 # Verify Node.js is available before configuring MCP servers
@@ -4232,15 +4276,15 @@ def main() -> None:
42324276
42334277 # Check if command creation is needed
42344278 if command_name :
4235- # Step 11 : Download hooks
4279+ # Step 12 : Download hooks
42364280 print ()
4237- print (f'{ Colors .CYAN } Step 11 : Downloading hooks...{ Colors .NC } ' )
4281+ print (f'{ Colors .CYAN } Step 12 : Downloading hooks...{ Colors .NC } ' )
42384282 hooks = config .get ('hooks' , {})
42394283 download_hook_files (hooks , claude_user_dir , config_source , base_url , args .auth )
42404284
4241- # Step 12 : Configure settings
4285+ # Step 13 : Configure settings
42424286 print ()
4243- print (f'{ Colors .CYAN } Step 12 : Configuring settings...{ Colors .NC } ' )
4287+ print (f'{ Colors .CYAN } Step 13 : Configuring settings...{ Colors .NC } ' )
42444288 create_additional_settings (
42454289 hooks ,
42464290 claude_user_dir ,
@@ -4252,27 +4296,27 @@ def main() -> None:
42524296 always_thinking_enabled ,
42534297 )
42544298
4255- # Step 13 : Create launcher script
4299+ # Step 14 : Create launcher script
42564300 print ()
4257- print (f'{ Colors .CYAN } Step 13 : Creating launcher script...{ Colors .NC } ' )
4301+ print (f'{ Colors .CYAN } Step 14 : Creating launcher script...{ Colors .NC } ' )
42584302 # Strip query parameters from system prompt filename (must match download logic)
42594303 prompt_filename : str | None = None
42604304 if system_prompt :
42614305 clean_prompt = system_prompt .split ('?' )[0 ] if '?' in system_prompt else system_prompt
42624306 prompt_filename = Path (clean_prompt ).name
42634307 launcher_path = create_launcher_script (claude_user_dir , command_name , prompt_filename , mode )
42644308
4265- # Step 14 : Register global command
4309+ # Step 15 : Register global command
42664310 if launcher_path :
42674311 print ()
4268- print (f'{ Colors .CYAN } Step 14 : Registering global { command_name } command...{ Colors .NC } ' )
4312+ print (f'{ Colors .CYAN } Step 15 : Registering global { command_name } command...{ Colors .NC } ' )
42694313 register_global_command (launcher_path , command_name )
42704314 else :
42714315 warning ('Launcher script was not created' )
42724316 else :
42734317 # Skip command creation
42744318 print ()
4275- print (f'{ Colors .CYAN } Steps 11-14 : Skipping command creation (no command-name specified)...{ Colors .NC } ' )
4319+ print (f'{ Colors .CYAN } Steps 12-15 : Skipping command creation (no command-name specified)...{ Colors .NC } ' )
42764320 info ('Environment configuration completed successfully' )
42774321 info ('To create a custom command, add "command-name: your-command-name" to your config' )
42784322
0 commit comments