@@ -464,28 +464,33 @@ def configure_all_mcp_servers(servers: list[dict[str, Any]]) -> bool:
464464 return True
465465
466466
467- def update_hooks_settings (hooks : list [dict [str , Any ]], claude_user_dir : Path ) -> bool :
468- """Update hooks in settings.json."""
469- if not hooks :
470- return True
467+ def create_additional_settings (hooks : list [dict [str , Any ]], claude_user_dir : Path ) -> bool :
468+ """Create additional-settings.json with environment-specific hooks.
471469
472- info ('Configuring hooks...' )
470+ This file is always overwritten to avoid duplicate hooks when re-running the installer.
471+ It's loaded via --settings flag when launching Claude.
473472
474- settings_path = claude_user_dir / 'settings.json'
475-
476- # Load existing settings or create new
477- if settings_path .exists ():
473+ Returns:
474+ bool: True if successful, False otherwise.
475+ """
476+ if not hooks :
477+ # Still create an empty additional-settings.json for consistency
478+ additional_settings_path = claude_user_dir / 'additional-settings.json'
478479 try :
479- with open (settings_path ) as f :
480- settings = json .load (f )
481- except (OSError , json .JSONDecodeError ):
482- settings = {}
483- else :
484- settings = {}
480+ with open (additional_settings_path , 'w' ) as f :
481+ json .dump ({}, f , indent = 2 )
482+ info ('Created empty additional-settings.json (no hooks configured)' )
483+ return True
484+ except Exception as e :
485+ error (f'Failed to create additional-settings.json: { e } ' )
486+ return False
485487
486- # Ensure hooks key exists
487- if 'hooks' not in settings :
488- settings ['hooks' ] = {}
488+ info ('Configuring hooks in additional-settings.json...' )
489+
490+ # Create fresh settings structure for this environment
491+ settings = {
492+ 'hooks' : {},
493+ }
489494
490495 # Process each hook
491496 for hook in hooks :
@@ -542,7 +547,9 @@ def update_hooks_settings(hooks: list[dict[str, Any]], claude_user_dir: Path) ->
542547 # Windows needs explicit Python interpreter
543548 # Use 'py' which is more reliable on Windows, fallback to 'python'
544549 python_cmd = 'py' if shutil .which ('py' ) else 'python'
545- full_command = f'{ python_cmd } "{ str (hook_path )} "'
550+ # Use forward slashes for the path (works on Windows and avoids JSON escaping issues)
551+ hook_path_str = hook_path .as_posix ()
552+ full_command = f'{ python_cmd } { hook_path_str } '
546553 else :
547554 # Unix-like systems can use shebang directly
548555 # Make script executable
@@ -553,23 +560,22 @@ def update_hooks_settings(hooks: list[dict[str, Any]], claude_user_dir: Path) ->
553560 # Not a Python script, use command as-is
554561 full_command = command
555562
556- # Add hook if not already present
563+ # Add hook configuration
557564 hook_config = {
558565 'type' : hook_type ,
559566 'command' : full_command ,
560567 }
568+ matcher_group ['hooks' ].append (hook_config )
561569
562- if hook_config not in matcher_group ['hooks' ]:
563- matcher_group ['hooks' ].append (hook_config )
564-
565- # Save settings
570+ # Save additional settings (always overwrite)
571+ additional_settings_path = claude_user_dir / 'additional-settings.json'
566572 try :
567- with open (settings_path , 'w' ) as f :
573+ with open (additional_settings_path , 'w' ) as f :
568574 json .dump (settings , f , indent = 2 )
569- success ('Hooks configured successfully ' )
575+ success ('Created additional-settings.json with environment hooks ' )
570576 return True
571577 except Exception as e :
572- error (f'Failed to save settings.json: { e } ' )
578+ error (f'Failed to save additional- settings.json: { e } ' )
573579 return False
574580
575581
@@ -624,13 +630,16 @@ def create_launcher_script(claude_user_dir: Path, command_name: str, system_prom
624630 $argsString = $escapedArgs -join ' '
625631 # Use a here-string to avoid complex quote escaping
626632 $bashCommand = @"
627- p=`$(tr -d '\\ \\ r' < ~/.claude/prompts/{ system_prompt_file } ); exec claude --append-system-prompt=\\ "`$p\\ " $argsString
633+ p=`$(tr -d '\\ \\ r' < ~/.claude/prompts/{ system_prompt_file } ); s=~/.claude/additional-settings.json; \\
634+ exec claude --append-system-prompt=\\ "`$p\\ " --settings=\\ "`$s\\ " $argsString
628635"@
629636 & $bashPath -lc $bashCommand
630637}} else {{
631638 # Use --% to stop PowerShell parsing, with literal command string
632- & $bashPath `
633- --% -lc "p=$(tr -d '\\ r' < ~/.claude/prompts/{ system_prompt_file } ); exec claude --append-system-prompt=\\ "$p\\ ""
639+ $bashCommand = "p=`$(tr -d '\\ r' < ~/.claude/prompts/{ system_prompt_file } ); " + `
640+ "s=~/.claude/additional-settings.json; " + `
641+ "exec claude --append-system-prompt=\\ `"`$p\\ `" --settings=\\ `"`$s\\ `""
642+ & $bashPath --% -lc $bashCommand
634643}}
635644'''
636645 launcher_path .write_text (launcher_content )
@@ -654,11 +663,12 @@ def create_launcher_script(claude_user_dir: Path, command_name: str, system_prom
654663REM Build the command with all arguments
655664set BASH_EXE="C:\\ Program Files\\ Git\\ bin\\ bash.exe"
656665set CMD_PREFIX=p=$(tr -d '\\ r' ^< ~/.claude/prompts/{ system_prompt_file } )
666+ set SETTINGS_PATH=~/.claude/additional-settings.json
657667if "%~1"=="" (
658- %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ ""
668+ %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " --settings= \\ "%SETTINGS_PATH% \\ " "
659669) else (
660670 echo Passing additional arguments: %*
661- %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " %*"
671+ %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " --settings= \\ "%SETTINGS_PATH% \\ " %*"
662672)
663673'''
664674 batch_path .write_text (batch_content )
@@ -683,13 +693,14 @@ def create_launcher_script(claude_user_dir: Path, command_name: str, system_prom
683693
684694# Read the prompt content
685695PROMPT_CONTENT=$(cat "$PROMPT_PATH")
696+ SETTINGS_PATH="$CLAUDE_USER_DIR/additional-settings.json"
686697
687698# Pass any additional arguments to Claude
688699if [ $# -gt 0 ]; then
689700 echo -e "\\ 033[0;36mPassing additional arguments: $@\\ 033[0m"
690- claude --append-system-prompt "$PROMPT_CONTENT" "$@"
701+ claude --append-system-prompt "$PROMPT_CONTENT" --settings "$SETTINGS_PATH" "$@"
691702else
692- claude --append-system-prompt "$PROMPT_CONTENT"
703+ claude --append-system-prompt "$PROMPT_CONTENT" --settings "$SETTINGS_PATH"
693704fi
694705'''
695706 launcher_path .write_text (launcher_content )
@@ -722,10 +733,11 @@ def register_global_command(launcher_path: Path, command_name: str, system_promp
722733REM Global { command_name } command for CMD
723734set BASH_EXE="C:\\ Program Files\\ Git\\ bin\\ bash.exe"
724735set CMD_PREFIX=p=$(tr -d '\\ r' ^< ~/.claude/prompts/{ system_prompt_file } )
736+ set SETTINGS_PATH=~/.claude/additional-settings.json
725737if "%~1"=="" (
726- %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ ""
738+ %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " --settings= \\ "%SETTINGS_PATH% \\ " "
727739) else (
728- %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " %*"
740+ %BASH_EXE% -lc "%CMD_PREFIX%; exec claude --append-system-prompt=\\ "$p\\ " --settings= \\ "%SETTINGS_PATH% \\ " %*"
729741)
730742'''
731743 batch_path .write_text (batch_content )
@@ -755,13 +767,14 @@ def register_global_command(launcher_path: Path, command_name: str, system_promp
755767
756768# Read the prompt content and pass it directly to claude
757769PROMPT_CONTENT=$(cat "$PROMPT_PATH")
770+ SETTINGS_PATH="$HOME/.claude/additional-settings.json"
758771
759772# Pass all arguments to claude with the system prompt
760773if [ $# -gt 0 ]; then
761774 echo "Passing additional arguments: $@"
762- claude --append-system-prompt "$PROMPT_CONTENT" "$@"
775+ claude --append-system-prompt "$PROMPT_CONTENT" --settings "$SETTINGS_PATH" "$@"
763776else
764- claude --append-system-prompt "$PROMPT_CONTENT"
777+ claude --append-system-prompt "$PROMPT_CONTENT" --settings "$SETTINGS_PATH"
765778fi
766779'''
767780 bash_wrapper_path .write_text (bash_content , newline = '\n ' ) # Use Unix line endings
@@ -920,7 +933,7 @@ def main() -> None:
920933 print (f'{ Colors .CYAN } Step 9: Configuring hooks...{ Colors .NC } ' )
921934 hooks = config .get ('hooks' , [])
922935 if hooks :
923- update_hooks_settings (hooks , claude_user_dir )
936+ create_additional_settings (hooks , claude_user_dir )
924937 else :
925938 info ('No hooks configured' )
926939
@@ -965,7 +978,8 @@ def main() -> None:
965978 else :
966979 print (f' * Full path: { launcher_path } ' )
967980 if prompt_path :
968- print (f" * Manual: claude --append-system-prompt '@{ prompt_path } '" )
981+ settings_path = claude_user_dir / 'additional-settings.json'
982+ print (f" * Manual: claude --append-system-prompt '@{ prompt_path } ' --settings '{ settings_path } '" )
969983
970984 print ()
971985 print (f'{ Colors .YELLOW } Available Commands (after starting Claude):{ Colors .NC } ' )
0 commit comments