Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
11 KiB
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
- Automatic config discovery - Find configuration files without user intervention
- Environment setup - Set required environment variables before execution
- Transparency - User sees no difference between wrapper and binary
- Flexibility - Support multiple config file locations
- 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
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_fileisnull
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:
// 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:
- Use
WORKSPACE_CONFIG_PATHif set (from wrapper) - Search in
WORKSPACE_CONFIG_DIRif set - Fall back to default locations if neither set
- Create
WORKSPACE_DATA_DIRif it doesn't exist
Wrapper Script Implementation
workspace wrapper script location
~/.cargo/bin/workspace (or wherever cargo/bin is)
Full Wrapper Script Example
#!/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
-
NuShell as wrapper language
- Reason: Already used in project build system
- Advantage: Native path handling, string interpolation
- Consistency: All scripts use same language
-
Search before setting variables
- Reason: Only set
WORKSPACE_CONFIG_PATHif config actually exists - Advantage: Clear separation of concerns
- Benefit: Application can detect which config was used
- Reason: Only set
-
Always set
WORKSPACE_CONFIG_DIRandWORKSPACE_DATA_DIR- Reason: Application needs these for fallback behavior
- Advantage: Consistent environment across all invocations
- Benefit: Predictable runtime behavior
-
Create data directory automatically
- Reason: Application might not have write permission to create
- Advantage: Fails fast if permission issues
- Benefit: Cleaner error messages
-
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:
- Clarity - Easy to see which files are wrappers vs. actual binaries
- Safety - Prevents accidental execution of wrong file
- Maintenance - Can easily identify wrapped binaries
- Reversibility - Can restore by removing wrappers
Renaming Process
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:
- Compiles binary with
cargo install - Creates wrapper script via
create_wrapper_script() - Renames binary to
.realviacreate_wrappers() - Registers in manifest via
register_installations()
Manifest Tracking
Wrapper creation is tracked in .syntaxis/manifest.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
cat ~/.cargo/bin/workspace
Shows exact config discovery logic and environment setup.
Trace Execution
Add debug output to see what's happening:
# 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
# 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
# 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:
- NuShell startup: ~10-50ms
- Config search: ~0-5ms (depends on number of existing config files)
- Directory creation: ~1-2ms
- Total: ~15-60ms (negligible for CLI tools)
Optimization Opportunities
If performance becomes critical:
-
Cache config file location
# Store found path in environment for child processes $env.WORKSPACE_CONFIG_FOUND = $config_file -
Short-circuit search
# Stop searching after first find if ($"($env.HOME)/.config/core/workspace.toml" | path exists) { # Use this, don't check others } -
Skip directory creation
# 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:
- Check PATH:
echo $PATH | grep .cargo/bin - Verify installation:
ls -la ~/.cargo/bin/workspace* - Check permissions:
ls -l ~/.cargo/bin/workspace - Re-install:
just scripts-install workspace
Config Not Loading
Problem: Binary doesn't see configuration
Solutions:
- Verify config exists:
ls ~/.config/core/workspace.toml - Check wrapper sees it:
cat ~/.cargo/bin/workspace | grep config_paths - Add debugging to wrapper (see Debugging section)
- Check environment:
env | grep WORKSPACE
Permission Denied
Problem: permission denied: ~/.cargo/bin/workspace
Solutions:
- Check permissions:
ls -l ~/.cargo/bin/workspace - Make executable:
chmod +x ~/.cargo/bin/workspace - Re-install:
just scripts-install workspace
Wrong Binary Executed
Problem: .real binary being called instead of wrapper
Solutions:
- Check file order:
ls -la ~/.cargo/bin/workspace* - Verify PATH order:
which workspace - Clear PATH cache:
hash -r(bash) orrehash(zsh) - Use full path:
~/.cargo/bin/workspace --help
Future Improvements
Version 1.1
- Config file caching - Cache found path to reduce startup time
- Wrapper updates - Auto-update wrappers when configs change
- Enhanced debugging - Built-in debug mode via
--debugflag - Config merging - Merge multiple config files for inheritance
Version 2.0
- Binary shims - Replace NuShell wrappers with compiled shims (faster)
- Config templating - Jinja2-style variable substitution in configs
- Hot reload - Reload configs without restarting application
- 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