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
ADR-002: Bundle Wrapper Deployment Architecture
Date: 2025-11-17 Status: ACCEPTED Version: 2.0 (Updated with hybrid NuShell + Bash fallback) Author: Architecture Review Scope: Hybrid runtime wrapper deployment for bundle installations
Problem Statement
Question: Should bundle installations create the same three-layer wrapper structure that development installations create?
Current State:
-
Development users (
scripts/install-syntaxis.nu): Get full wrapper infrastructure- Binaries renamed to
.real - Bash wrappers at original locations
- NuShell wrappers deployed to
~/.config/syntaxis/scripts/ - Result: Auto-config injection ✅ → Users type:
syntaxis-cli status
- Binaries renamed to
-
Bundle users (
scripts/provisioning/install.sh): Get direct binaries only- Binaries installed directly
- No wrapper infrastructure
- No config auto-injection
- Result: Manual config flags ❌ → Users must type:
syntaxis-cli --config path status
The Issue: Inconsistent user experience between installation methods.
Wrapper Architecture: Hybrid Design (NuShell + Bash Fallback)
With NuShell Installed (Preferred): Three Layers
Layer 1: Bash Wrapper
~/.local/bin/syntaxis-cli (300 bytes)
├─ Checks: Is NuShell available?
├─ If YES: Sets NU_LIB_DIRS="$HOME/.config/syntaxis/scripts"
└─ Calls: nu "$HOME/.config/syntaxis/scripts/syntaxis-cli.nu" "$@"
Layer 2: NuShell Wrapper
~/.config/syntaxis/scripts/syntaxis-cli.nu (2.2 KB)
├─ Searches: Config file locations
├─ Discovers: ~/.config/syntaxis/syntaxis-cli.toml
├─ Injects: --config ~/.config/syntaxis/syntaxis-cli.toml
└─ Calls: ~/.local/bin/syntaxis-cli.real --config ... status
Layer 3: Real Binary
~/.local/bin/syntaxis-cli.real (7.2 MB)
└─ Actual compiled Rust binary
Without NuShell: Two Layers (Simplified)
Layer 1: Bash Wrapper (with inline config discovery)
~/.local/bin/syntaxis-cli (300 bytes)
├─ Checks: Is NuShell available?
├─ If NO: Use bash-only config discovery
├─ Searches: Config in ~/.config/syntaxis/*, .syntaxis/, .project/, .coder/
├─ If found: Injects --config flag
└─ Calls: ~/.local/bin/syntaxis-cli.real --config <found> status
Layer 2: Real Binary
~/.local/bin/syntaxis-cli.real (7.2 MB)
└─ Actual compiled Rust binary
User Experience
$ syntaxis-cli status
# Wrapper automatically injects --config
# Clean, simple, no typing burden
Decision: ADD HYBRID WRAPPERS TO BUNDLE
Recommendation: Bundle installations should create hybrid wrappers that work with or without NuShell.
Rationale
-
Self-Contained Bundle
- No hard dependency on NuShell
- Works offline (no downloads required)
- Fallback ensures functionality
-
User Flexibility
- NuShell users get full features immediately
- Non-NuShell users still get auto-config
- User prompted to install NuShell (optional upgrade)
-
Graceful Degradation
- With NuShell: Full three-layer architecture (best UX)
- Without NuShell: Simple bash discovery (good UX)
- Both modes provide auto-config injection
-
Minimal Maintenance
- Only ~15 lines of bash fallback code
- No library translation needed
- NuShell libraries only used when available
-
Daily UX Impact
- Users invoke 100s of times per day/week
- Auto-config saves significant typing burden
- Works regardless of NuShell availability
Implementation Plan
1. Update scripts/provisioning/install.sh
Key functions added:
a) Detect NuShell:
check_nushell() {
if command -v nu &> /dev/null; then
NUSHELL_VERSION=$(nu --version 2>/dev/null | head -1)
return 0
else
return 1
fi
}
b) Prompt user about NuShell:
prompt_nushell() {
# Shows installation guide if NuShell not found
# Offers user choice: continue or install NuShell
# Non-blocking (continues with fallback if needed)
}
c) Create hybrid bash wrapper:
create_bash_wrapper() {
local dest="$1"
local binary_name=$(basename "$dest")
# Create wrapper that:
# 1. Checks if NuShell is available
# 2a. If YES: use full three-layer architecture
# 2b. If NO: use bash-only config discovery
}
d) Wrapper logic:
#!/bin/bash
# Hybrid wrapper - tries NuShell first
if command -v nu &>/dev/null; then
# Full mode: NuShell + libraries
export NU_LIB_DIRS="$HOME/.config/syntaxis/scripts"
exec nu "$HOME/.config/syntaxis/scripts/${BINARY_NAME}.nu" "$@"
fi
# Fallback: Bash-only config discovery
for cfg_path in \
"$HOME/.config/syntaxis/${BINARY_NAME}.toml" \
"$HOME/.config/syntaxis/config.toml" \
".syntaxis/${BINARY_NAME}.toml"; do
if [[ -f "$cfg_path" ]]; then
exec "$REAL_BINARY" --config "$cfg_path" "$@"
fi
done
# No config found
exec "$REAL_BINARY" "$@"
2. Deploy NuShell Wrapper Scripts
Add to install.sh:
deploy_wrapper_scripts() {
local scripts_dir="./scripts"
local config_scripts_dir="$HOME/.config/syntaxis/scripts"
mkdir -p "$config_scripts_dir"
# Deploy shared library
cp "$scripts_dir/syntaxis-lib.nu" "$config_scripts_dir/"
# Deploy binary-specific wrappers
for binary in syntaxis-cli syntaxis-tui syntaxis-api; do
cp "$scripts_dir/${binary}.nu" "$config_scripts_dir/" 2>/dev/null || true
cp "$scripts_dir/${binary}-lib.nu" "$config_scripts_dir/" 2>/dev/null || true
done
chmod +x "$config_scripts_dir"/*.nu
}
3. Update provisioning.toml
Add wrapper scripts to artifacts:
[artifacts]
# ... existing config ...
# Wrapper scripts for auto-config injection
wrapper_scripts = [
"scripts/syntaxis-lib.nu",
"scripts/syntaxis-cli.nu",
"scripts/syntaxis-cli-lib.nu",
"scripts/syntaxis-tui.nu",
"scripts/syntaxis-tui-lib.nu",
"scripts/syntaxis-api.nu",
"scripts/syntaxis-api-lib.nu",
]
4. Update Bundle Structure
Result after installation:
User's system after bundle installation:
~/.cargo/bin/
├── syntaxis-cli ← Bash wrapper
├── syntaxis-cli.real ← Actual binary
├── syntaxis-tui ← Bash wrapper
├── syntaxis-tui.real ← Actual binary
└── syntaxis-api ← Bash wrapper
└── syntaxis-api.real ← Actual binary
~/.config/syntaxis/scripts/
├── syntaxis-lib.nu ← Shared library
├── syntaxis-cli.nu ← NuShell wrapper
├── syntaxis-cli-lib.nu ← CLI utilities
├── syntaxis-tui.nu ← NuShell wrapper
├── syntaxis-tui-lib.nu ← TUI utilities
├── syntaxis-api.nu ← NuShell wrapper
└── syntaxis-api-lib.nu ← API utilities
Technical Specifications
Wrapper Scripts to Include
| Script | Size | Purpose |
|---|---|---|
syntaxis-lib.nu |
1.1 KB | Shared library (config discovery) |
syntaxis-cli.nu |
2.2 KB | CLI wrapper (auto-config injection) |
syntaxis-cli-lib.nu |
1.5 KB | CLI utilities |
syntaxis-tui.nu |
2.2 KB | TUI wrapper |
syntaxis-tui-lib.nu |
743 B | TUI utilities |
syntaxis-api.nu |
3.6 KB | API wrapper |
syntaxis-api-lib.nu |
2.2 KB | API utilities |
| Total | ~13.5 KB | All wrapper infrastructure |
Bundle Size Impact
- Before: Binaries + configs + docs = ~9.2 MB
- After: + wrapper scripts (~13.5 KB) = ~9.2 MB (negligible)
Installation Steps
- Extract bundle
- Run
bash install.sh --interactive- Installs binaries to
~/.local/bin/(or custom prefix) - Deploys wrapper scripts to
~/.config/syntaxis/scripts/ - Renames binaries to
.real - Creates bash wrappers at original locations
- Installs binaries to
- Run
bash setup-config.sh --interactive- Creates configuration files
Benefits
✅ Users Don't Type --config
- Every invocation automatically injects config path
- Saves millions of characters per year
✅ Transparent
- Users see no difference from direct binary
- Wrapper is invisible to end users
✅ Flexible
- Config files can be in multiple locations
- Wrapper discovers automatically
✅ Consistent
- Same experience as development installation
- Feature parity across installation methods
✅ Maintainable
- Clear separation: wrapper scripts vs binaries
- Easy to update wrappers without rebuilding binaries
Trade-offs
With NuShell (Full Mode)
Pros:
- ✅ Excellent user experience (no --config needed)
- ✅ Advanced config discovery (multiple paths, precedence)
- ✅ Full feature set available
- ✅ Consistent with development installation
Cons:
- ⚠️ Startup overhead (~50ms from NuShell)
- ⚠️ Requires NuShell to be installed
- ⚠️ Slightly more complex
Without NuShell (Simplified Mode)
Pros:
- ✅ No external dependencies
- ✅ Works offline (self-contained)
- ✅ Still provides auto-config injection
- ✅ Simple bash-only fallback
Cons:
- ⚠️ Less advanced config discovery
- ⚠️ Simplified version of full features
- ⚠️ User prompted to install NuShell (optional)
Overall Hybrid Approach
Pros:
- ✅ Self-contained bundle (no requirements)
- ✅ Works for all users (NuShell or not)
- ✅ Graceful degradation (best effort)
- ✅ Minimal maintenance (only ~15 lines bash fallback)
- ✅ User-aware (prompted to upgrade if desired)
Cons:
- ⚠️ Two code paths to test
- ⚠️ Slightly more complexity in wrapper
- ⚠️ NuShell libraries deployment (optional when not available)
Risk Assessment
Low Risk ✅
- Exact same code as development installation
- NuShell is already required for development
- Startup overhead is acceptable for CLI tools
- Can always use
.realbinaries if needed
Implementation Checklist
- Modify
scripts/provisioning/install.shto create wrappers - Add
deploy_wrapper_scripts()function to install.sh - Update
configs/provisioning.tomlto include wrapper scripts - Update
scripts/provisioning/pack.nuto package wrapper scripts - Test bundle installation creates complete wrapper structure
- Update documentation (docs/installation.md, README.md)
- Rebuild bundle with wrapper support
- Verify wrapper functionality post-install
Comparison: With vs Without Wrappers
Without Wrappers (Current Bundle)
$ syntaxis-cli project list
# User must provide --config flag every time
$ syntaxis-cli --config ~/.config/syntaxis/syntaxis-cli.toml project list
# Burden: 50+ characters per invocation
# 100 invocations = 5,000+ characters of typing
With Wrappers (Proposed)
$ syntaxis-cli project list
# Wrapper injects --config automatically
# Clean, simple, Unix-like experience
# Burden: 0 additional typing
# Users just type normal commands
Related Documentation
docs/installation/WRAPPER_DESIGN.md- Technical wrapper designscripts/install-syntaxis.nu- Development installation with wrappersscripts/provisioning/install.sh- Bundle installation (to be updated)
Conclusion
Decision: Implement full wrapper deployment in bundle installations.
Rationale:
- High value (users don't type --config)
- Low complexity (existing code, ~14 KB)
- Better user experience
- Feature parity with development installation
This approach aligns with SYNTAXIS philosophy of providing excellent, transparent tooling for developers and teams.
Status: ACCEPTED - Ready for implementation