syntaxis/docs/installation/wrapper-design.md
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
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)
2025-12-26 18:36:23 +00:00

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

  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

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:

// 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

#!/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

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:

[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:

  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

    # Store found path in environment for child processes
    $env.WORKSPACE_CONFIG_FOUND = $config_file
    
  2. Short-circuit search

    # Stop searching after first find
    if ($"($env.HOME)/.config/core/workspace.toml" | path exists) {
        # Use this, don't check others
    }
    
  3. 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:

  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
  • docs/installation-guide.md - How to install wrappers
  • docs/configuration.md - Configuration file options
  • scripts/install-cli.nu - Wrapper creation implementation