# Wrapper Script Design Technical documentation for syntaxis wrapper scripts. ## Overview Wrapper scripts are NuShell scripts created during installation that provide intelligent configuration discovery and environment setup for workspace binaries. ### Goals 1. **Automatic config discovery** - Find configuration files without user intervention 2. **Environment setup** - Set required environment variables before execution 3. **Transparency** - User sees no difference between wrapper and binary 4. **Flexibility** - Support multiple config file locations 5. **Debugging** - Easy to inspect what's happening ## Architecture ### Binary Layout After Installation ``` ~/.cargo/bin/ ├── workspace # Wrapper script (NuShell) ├── workspace.real # Actual binary (compiled executable) ├── syntaxis-tui # Wrapper script (NuShell) ├── syntaxis-tui.real # Actual binary (compiled executable) └── syntaxis-dashboard # Actual binary (no wrapper, runs standalone) ``` ### Wrapper Script Flow ``` User executes: workspace --help ↓ NuShell wrapper script (workspace) ↓ 1. Search for config file in priority order ↓ 2. If found: Set WORKSPACE_CONFIG_PATH ↓ 3. Set other environment variables: - WORKSPACE_CONFIG_DIR - WORKSPACE_DATA_DIR ↓ 4. Create data directory if needed ↓ 5. Execute actual binary (workspace.real) ↓ Binary executes with environment setup ``` ## Configuration Discovery ### Search Order Wrapper scripts search for configuration files in this priority order: ``` 1. ~/.config/core/workspace.toml └─ User configuration 2. ~/.config/core/syntaxis-api.toml └─ Fallback to main API config 3. .project/workspace.toml └─ Project-specific configuration 4. .vapora/workspace.toml └─ VAPORA-specific configuration ``` Returns first matching file, or `null` if none found. ### Configuration Discovery Logic ```nushell let config_paths = [ $"($env.HOME)/.config/core/workspace.toml" $"($env.HOME)/.config/core/syntaxis-api.toml" ".project/workspace.toml" ".vapora/workspace.toml" ] let config_file = ( $config_paths | where { |p| $p | path exists } | first ) ``` This ensures: - User configs are tried first - Fallback to shared config if specific config not found - Project configs override user configs - VAPORA configs are respected - If no config found, `config_file` is `null` ## Environment Variables ### Set by Wrapper Wrapper scripts set these environment variables before execution: | Variable | Purpose | Example | |----------|---------|---------| | `WORKSPACE_CONFIG_PATH` | Absolute path to found config file | `~/.config/core/workspace.toml` | | `WORKSPACE_CONFIG_DIR` | Configuration directory | `~/.config/syntaxis` | | `WORKSPACE_DATA_DIR` | Runtime data directory | `~/.local/share/syntaxis` | ### Read by Application The workspace binary reads these variables: ```rust // In Rust code let config_path = std::env::var("WORKSPACE_CONFIG_PATH").ok(); let config_dir = std::env::var("WORKSPACE_CONFIG_DIR").ok(); let data_dir = std::env::var("WORKSPACE_DATA_DIR").ok(); ``` The binary will: 1. Use `WORKSPACE_CONFIG_PATH` if set (from wrapper) 2. Search in `WORKSPACE_CONFIG_DIR` if set 3. Fall back to default locations if neither set 4. Create `WORKSPACE_DATA_DIR` if it doesn't exist ## Wrapper Script Implementation ### workspace wrapper script location `~/.cargo/bin/workspace` (or wherever cargo/bin is) ### Full Wrapper Script Example ```nushell #!/usr/bin/env nu # Generated wrapper for workspace # Automatically finds configuration and sets up environment # Find configuration file in standard locations let config_paths = [ $"($env.HOME)/.config/core/workspace.toml" $"($env.HOME)/.config/core/syntaxis-api.toml" ".project/workspace.toml" ".vapora/workspace.toml" ] let config_file = ( $config_paths | where { |p| $p | path exists } | first ) # Setup environment variables if ($config_file != null) { $env.WORKSPACE_CONFIG_PATH = $config_file } $env.WORKSPACE_CONFIG_DIR = $"($env.HOME)/.config/syntaxis" $env.WORKSPACE_DATA_DIR = $"($env.HOME)/.local/share/syntaxis" # Create data directory if needed ^mkdir -p $env.WORKSPACE_DATA_DIR # Call actual binary with all arguments ^~/.cargo/bin/workspace.real ``` ### Key Design Decisions 1. **NuShell as wrapper language** - Reason: Already used in project build system - Advantage: Native path handling, string interpolation - Consistency: All scripts use same language 2. **Search before setting variables** - Reason: Only set `WORKSPACE_CONFIG_PATH` if config actually exists - Advantage: Clear separation of concerns - Benefit: Application can detect which config was used 3. **Always set `WORKSPACE_CONFIG_DIR` and `WORKSPACE_DATA_DIR`** - Reason: Application needs these for fallback behavior - Advantage: Consistent environment across all invocations - Benefit: Predictable runtime behavior 4. **Create data directory automatically** - Reason: Application might not have write permission to create - Advantage: Fails fast if permission issues - Benefit: Cleaner error messages 5. **Pass all arguments through** - Reason: Transparent to user - Advantage: No argument parsing needed in wrapper - Method: Shell directly executes real binary (arguments auto-passed) ## Binary Renaming Strategy ### Why Rename? Renaming actual binaries to `.real` suffix: 1. **Clarity** - Easy to see which files are wrappers vs. actual binaries 2. **Safety** - Prevents accidental execution of wrong file 3. **Maintenance** - Can easily identify wrapped binaries 4. **Reversibility** - Can restore by removing wrappers ### Renaming Process ```nushell def create_wrappers [] { let cargo_bin = ($env.HOME + "/.cargo/bin") let binaries = ["workspace" "syntaxis-tui"] for binary in $binaries { let binary_path = ($cargo_bin + "/" + $binary) let real_binary_path = ($cargo_bin + "/" + $binary + ".real") if ($binary_path | path exists) { ^mv $binary_path $real_binary_path # Now create wrapper script at original location } } } ``` Results: ``` Before: ~/.cargo/bin/workspace (executable binary) After: ~/.cargo/bin/workspace.real (actual binary) ~/.cargo/bin/workspace (wrapper script) ``` ## Installation Integration ### Creation During Installation The `install_binary()` function: 1. Compiles binary with `cargo install` 2. Creates wrapper script via `create_wrapper_script()` 3. Renames binary to `.real` via `create_wrappers()` 4. Registers in manifest via `register_installations()` ### Manifest Tracking Wrapper creation is tracked in `.syntaxis/manifest.toml`: ```toml [binaries.workspace] installed = true wrapper = true # ← Indicates wrapper created path = "~/.cargo/bin/workspace" # ← Wrapper location real_binary = "~/.cargo/bin/workspace.real" # ← Real binary location ``` ## Debugging Wrappers ### Inspect Wrapper Script ```bash cat ~/.cargo/bin/workspace ``` Shows exact config discovery logic and environment setup. ### Trace Execution Add debug output to see what's happening: ```nushell # Modify wrapper to add debugging let config_file = ( $config_paths | where { |p| $p | path exists } | first ) print -e $"Found config: ($config_file)" # ← Add this line print -e $"Config dir: ($env.WORKSPACE_CONFIG_DIR)" print -e $"Data dir: ($env.WORKSPACE_DATA_DIR)" ^~/.cargo/bin/workspace.real ``` ### Check Environment ```bash # See what environment wrapper sets strace -e execve ~/.cargo/bin/workspace --help 2>&1 | grep execve # Or use nu debugging nu -c 'source ~/.cargo/bin/workspace; print $env' ``` ### Test Config Discovery ```bash # Create test wrapper cat > /tmp/test_wrapper.nu << 'EOF' #!/usr/bin/env nu let config_paths = [ $"($env.HOME)/.config/core/workspace.toml" $"($env.HOME)/.config/core/syntaxis-api.toml" ".project/workspace.toml" ".vapora/workspace.toml" ] let config_file = ( $config_paths | where { |p| $p | path exists } | first ) print $"Found config: ($config_file)" EOF # Run test nu /tmp/test_wrapper.nu ``` ## Performance Considerations ### Startup Overhead Wrapper adds minimal overhead: 1. NuShell startup: ~10-50ms 2. Config search: ~0-5ms (depends on number of existing config files) 3. Directory creation: ~1-2ms 4. Total: ~15-60ms (negligible for CLI tools) ### Optimization Opportunities If performance becomes critical: 1. **Cache config file location** ```nushell # Store found path in environment for child processes $env.WORKSPACE_CONFIG_FOUND = $config_file ``` 2. **Short-circuit search** ```nushell # Stop searching after first find if ($"($env.HOME)/.config/core/workspace.toml" | path exists) { # Use this, don't check others } ``` 3. **Skip directory creation** ```nushell # Only create if needed (check first) if not ($env.WORKSPACE_DATA_DIR | path exists) { ^mkdir -p $env.WORKSPACE_DATA_DIR } ``` ## Troubleshooting Guide ### Wrapper Not Found **Problem**: `command not found: workspace` **Solutions**: 1. Check PATH: `echo $PATH | grep .cargo/bin` 2. Verify installation: `ls -la ~/.cargo/bin/workspace*` 3. Check permissions: `ls -l ~/.cargo/bin/workspace` 4. Re-install: `just scripts-install workspace` ### Config Not Loading **Problem**: Binary doesn't see configuration **Solutions**: 1. Verify config exists: `ls ~/.config/core/workspace.toml` 2. Check wrapper sees it: `cat ~/.cargo/bin/workspace | grep config_paths` 3. Add debugging to wrapper (see Debugging section) 4. Check environment: `env | grep WORKSPACE` ### Permission Denied **Problem**: `permission denied: ~/.cargo/bin/workspace` **Solutions**: 1. Check permissions: `ls -l ~/.cargo/bin/workspace` 2. Make executable: `chmod +x ~/.cargo/bin/workspace` 3. Re-install: `just scripts-install workspace` ### Wrong Binary Executed **Problem**: `.real` binary being called instead of wrapper **Solutions**: 1. Check file order: `ls -la ~/.cargo/bin/workspace*` 2. Verify PATH order: `which workspace` 3. Clear PATH cache: `hash -r` (bash) or `rehash` (zsh) 4. Use full path: `~/.cargo/bin/workspace --help` ## Future Improvements ### Version 1.1 1. **Config file caching** - Cache found path to reduce startup time 2. **Wrapper updates** - Auto-update wrappers when configs change 3. **Enhanced debugging** - Built-in debug mode via `--debug` flag 4. **Config merging** - Merge multiple config files for inheritance ### Version 2.0 1. **Binary shims** - Replace NuShell wrappers with compiled shims (faster) 2. **Config templating** - Jinja2-style variable substitution in configs 3. **Hot reload** - Reload configs without restarting application 4. **Remote configs** - Load configs from remote servers ## Related Documentation - **docs/installation-guide.md** - How to install wrappers - **docs/configuration.md** - Configuration file options - **scripts/install-cli.nu** - Wrapper creation implementation