Skip to content

Commit f218507

Browse files
authored
Merge pull request #200 from alex-feel/alex-feel-dev
Improve console output formatting and separate hooks from settings
2 parents e70ab70 + f99a2a9 commit f218507

File tree

3 files changed

+78
-42
lines changed

3 files changed

+78
-42
lines changed

scripts/setup_environment.py

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,16 +3031,57 @@ def configure_all_mcp_servers(servers: list[dict[str, Any]]) -> bool:
30313031
return True
30323032

30333033

3034+
def download_hook_files(
3035+
hooks: dict[str, Any],
3036+
claude_user_dir: Path,
3037+
config_source: str | None = None,
3038+
base_url: str | None = None,
3039+
auth_param: str | None = None,
3040+
) -> bool:
3041+
"""Download hook files from configuration.
3042+
3043+
Args:
3044+
hooks: Hooks configuration dictionary with 'files' key
3045+
claude_user_dir: Path to Claude user directory
3046+
config_source: Optional config source for resolving resource paths
3047+
base_url: Optional base URL for resolving resources
3048+
auth_param: Optional authentication parameter
3049+
3050+
Returns:
3051+
bool: True if successful, False otherwise.
3052+
"""
3053+
hook_files = hooks.get('files', [])
3054+
3055+
if not hook_files:
3056+
info('No hook files to download')
3057+
return True
3058+
3059+
hooks_dir = claude_user_dir / 'hooks'
3060+
hooks_dir.mkdir(parents=True, exist_ok=True)
3061+
3062+
for file in hook_files:
3063+
# Strip query parameters from URL to get clean filename
3064+
clean_file = file.split('?')[0] if '?' in file else file
3065+
filename = Path(clean_file).name
3066+
destination = hooks_dir / filename
3067+
# Handle hook files (download or copy)
3068+
if config_source:
3069+
handle_resource(file, destination, config_source, base_url, auth_param)
3070+
else:
3071+
# This shouldn't happen, but handle gracefully
3072+
error(f'No config source provided for hook file: {file}')
3073+
return False
3074+
3075+
return True
3076+
3077+
30343078
def create_additional_settings(
30353079
hooks: dict[str, Any],
30363080
claude_user_dir: Path,
30373081
command_name: str,
30383082
model: str | None = None,
30393083
permissions: dict[str, Any] | None = None,
30403084
env: dict[str, str] | None = None,
3041-
config_source: str | None = None,
3042-
base_url: str | None = None,
3043-
auth_param: str | None = None,
30443085
include_co_authored_by: bool | None = None,
30453086
) -> bool:
30463087
"""Create {command_name}-additional-settings.json with environment-specific settings.
@@ -3055,9 +3096,6 @@ def create_additional_settings(
30553096
model: Optional model alias or custom model name
30563097
permissions: Optional permissions configuration dict
30573098
env: Optional environment variables dict
3058-
config_source: Optional config source for resolving resource paths
3059-
base_url: Optional base URL for resolving resources
3060-
auth_param: Optional authentication parameter
30613099
include_co_authored_by: Optional flag to include co-authored-by in commits
30623100
30633101
Returns:
@@ -3098,31 +3136,13 @@ def create_additional_settings(
30983136
info(f'Setting includeCoAuthoredBy: {include_co_authored_by}')
30993137

31003138
# Handle hooks if present
3101-
hook_files: list[str] = []
31023139
hook_events: list[dict[str, Any]] = []
31033140

31043141
if hooks:
31053142
settings['hooks'] = {}
3106-
# Extract files and events from the hooks configuration
3107-
hook_files = hooks.get('files', [])
3143+
# Extract events from the hooks configuration (files are downloaded separately)
31083144
hook_events = hooks.get('events', [])
31093145

3110-
# Process all hook files first
3111-
if hook_files:
3112-
hooks_dir = claude_user_dir / 'hooks'
3113-
hooks_dir.mkdir(parents=True, exist_ok=True)
3114-
for file in hook_files:
3115-
# Strip query parameters from URL to get clean filename
3116-
clean_file = file.split('?')[0] if '?' in file else file
3117-
filename = Path(clean_file).name
3118-
destination = hooks_dir / filename
3119-
# Handle hook files (download or copy)
3120-
if config_source:
3121-
handle_resource(file, destination, config_source, base_url, auth_param)
3122-
else:
3123-
# This shouldn't happen, but handle gracefully
3124-
error(f'No config source provided for hook file: {file}')
3125-
31263146
# Process each hook event
31273147
for hook in hook_events:
31283148
event = hook.get('event')
@@ -4076,44 +4096,46 @@ def main() -> None:
40764096

40774097
# Check if command creation is needed
40784098
if command_name:
4079-
# Step 11: Configure hooks and settings
4099+
# Step 11: Download hooks
40804100
print()
4081-
print(f'{Colors.CYAN}Step 11: Configuring hooks and settings...{Colors.NC}')
4101+
print(f'{Colors.CYAN}Step 11: Downloading hooks...{Colors.NC}')
40824102
hooks = config.get('hooks', {})
4103+
download_hook_files(hooks, claude_user_dir, config_source, base_url, args.auth)
4104+
4105+
# Step 12: Configure settings
4106+
print()
4107+
print(f'{Colors.CYAN}Step 12: Configuring settings...{Colors.NC}')
40834108
create_additional_settings(
40844109
hooks,
40854110
claude_user_dir,
40864111
command_name,
40874112
model,
40884113
permissions,
40894114
env_variables,
4090-
config_source,
4091-
base_url,
4092-
args.auth,
40934115
include_co_authored_by,
40944116
)
40954117

4096-
# Step 12: Create launcher script
4118+
# Step 13: Create launcher script
40974119
print()
4098-
print(f'{Colors.CYAN}Step 12: Creating launcher script...{Colors.NC}')
4120+
print(f'{Colors.CYAN}Step 13: Creating launcher script...{Colors.NC}')
40994121
# Strip query parameters from system prompt filename (must match download logic)
41004122
prompt_filename: str | None = None
41014123
if system_prompt:
41024124
clean_prompt = system_prompt.split('?')[0] if '?' in system_prompt else system_prompt
41034125
prompt_filename = Path(clean_prompt).name
41044126
launcher_path = create_launcher_script(claude_user_dir, command_name, prompt_filename, mode)
41054127

4106-
# Step 13: Register global command
4128+
# Step 14: Register global command
41074129
if launcher_path:
41084130
print()
4109-
print(f'{Colors.CYAN}Step 13: Registering global {command_name} command...{Colors.NC}')
4131+
print(f'{Colors.CYAN}Step 14: Registering global {command_name} command...{Colors.NC}')
41104132
register_global_command(launcher_path, command_name)
41114133
else:
41124134
warning('Launcher script was not created')
41134135
else:
41144136
# Skip command creation
41154137
print()
4116-
print(f'{Colors.CYAN}Steps 11-13: Skipping command creation (no command-name specified)...{Colors.NC}')
4138+
print(f'{Colors.CYAN}Steps 11-14: Skipping command creation (no command-name specified)...{Colors.NC}')
41174139
info('Environment configuration completed successfully')
41184140
info('To create a custom command, add "command-name: your-command-name" to your config')
41194141

@@ -4132,9 +4154,9 @@ def main() -> None:
41324154
print(f' * Skills: {len(skills)} installed')
41334155
if system_prompt:
41344156
if mode == 'append':
4135-
print(f' * System prompt: Appending to default ({system_prompt})')
4157+
print(' * System prompt: appending to default')
41364158
else: # mode == 'replace'
4137-
print(f' * System prompt: Replacing default ({system_prompt})')
4159+
print(' * System prompt: replacing default')
41384160
if model:
41394161
print(f' * Model: {model}')
41404162
print(f' * MCP servers: {len(mcp_servers)} configured')
@@ -4156,9 +4178,9 @@ def main() -> None:
41564178
set_vars = sum(1 for v in os_env_variables.values() if v is not None)
41574179
del_vars = sum(1 for v in os_env_variables.values() if v is None)
41584180
if set_vars > 0:
4159-
print(f' * OS env variables set: {set_vars}')
4181+
print(f' * OS environment variables: {set_vars} configured')
41604182
if del_vars > 0:
4161-
print(f' * OS env variables deleted: {del_vars}')
4183+
print(f' * OS environment variables: {del_vars} deleted')
41624184
# Only show hooks count if command_name was specified (hooks was defined)
41634185
if command_name:
41644186
hooks = config.get('hooks', {})
@@ -4190,7 +4212,7 @@ def main() -> None:
41904212
print()
41914213
print(f'{Colors.YELLOW}Documentation:{Colors.NC}')
41924214
print(' * Setup Guide: https://github.com/alex-feel/claude-code-toolbox')
4193-
print(' * Claude Code Docs: https://docs.anthropic.com/claude-code')
4215+
print(' * Claude Code Docs: https://code.claude.com/docs')
41944216
print()
41954217

41964218
# If running elevated via UAC, add a pause so user can see the results

tests/test_setup_environment.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,11 +864,18 @@ def test_create_additional_settings_with_hooks(self, mock_download):
864864
],
865865
}
866866

867+
# First download hook files
868+
setup_environment.download_hook_files(
869+
hooks,
870+
claude_dir,
871+
config_source='https://example.com/config.yaml',
872+
)
873+
874+
# Then create additional settings
867875
result = setup_environment.create_additional_settings(
868876
hooks,
869877
claude_dir,
870878
'test-env',
871-
config_source='https://example.com/config.yaml',
872879
)
873880

874881
assert result is True

tests/test_setup_environment_additional.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,11 +1033,18 @@ def test_create_additional_settings_hooks_linux(self, _mock_system, mock_downloa
10331033
],
10341034
}
10351035

1036+
# First download hook files
1037+
setup_environment.download_hook_files(
1038+
hooks,
1039+
claude_dir,
1040+
config_source='https://example.com/config.yaml',
1041+
)
1042+
1043+
# Then create additional settings
10361044
result = setup_environment.create_additional_settings(
10371045
hooks,
10381046
claude_dir,
10391047
'test',
1040-
config_source='https://example.com/config.yaml',
10411048
)
10421049

10431050
assert result is True

0 commit comments

Comments
 (0)