nushell-plugins/nu_plugin_nickel/adr-001-nickel-cli-wrapper-architecture.md
Jesús Pérez d9ef2f0d5b
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
Nightly Build / Check for Changes (push) Has been cancelled
Nightly Build / Validate Setup (push) Has been cancelled
Nightly Build / Nightly Build (darwin-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (darwin-arm64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (windows-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-arm64) (push) Has been cancelled
Nightly Build / Create Nightly Pre-release (push) Has been cancelled
Nightly Build / Notify Build Status (push) Has been cancelled
Nightly Build / Nightly Maintenance (push) Has been cancelled
chore: update all plugins to Nushell 0.111.0
- Bump all 18 plugins from 0.110.0 to 0.111.0
  - Update rust-toolchain.toml channel to 1.93.1 (nu 0.111.0 requires ≥1.91.1)

  Fixes:
  - interprocess pin =2.2.x → ^2.3.1 in nu_plugin_mcp, nu_plugin_nats, nu_plugin_typedialog
    (required by nu-plugin-core 0.111.0)
  - nu_plugin_typedialog: BackendType::Web initializer — add open_browser: false field
  - nu_plugin_auth: implement missing user_info_to_value helper referenced in tests

  Scripts:
  - update_all_plugins.nu: fix [package].version update on minor bumps; add [dev-dependencies]
    pass; add nu-plugin-test-support to managed crates
  - download_nushell.nu: rustup override unset before rm -rf on nushell dir replace;
    fix unclosed ) in string interpolation
2026-03-11 03:22:42 +00:00

7.8 KiB

ADR-001: Nickel Plugin CLI Wrapper Architecture\n\n## Status\n\nAccepted - 2025-12-15\n\n## Context\n\nThe nu_plugin_nickel project provides Nushell integration for Nickel configuration language. The core decision was whether to implement this as:\n\n1. Pure Rust Implementation (using nickel-lang-core crate directly)\n2. CLI Wrapper (using Command::new("nickel") to invoke external binary)\n\n### Technical Constraints\n\nNickel is a configuration language with module system:\n\n- Import system: import "path/to/module"\n- Module resolution with search paths\n- Standard library (builtins, stdlib packages)\n- Complex evaluation context\n- Package management system\n\n### User Requirements\n\nConfiguration files often use Nickel's module system for:\n\n- Code organization\n- Reusable configurations\n- Standard library access\n- External module dependencies\n\n## Decision\n\nImplement nu_plugin_nickel as a CLI Wrapper that invokes the external nickel binary.\n\n### Architecture\n\nplaintext\nNushell Script\n ↓\nnickel-export (plugin command)\n ↓\nhelpers.rs: run_nickel_command()\n ↓\nstd::process::Command::new("nickel")\n ↓\nNickel CLI (official binary)\n ↓\nModule Resolution (guaranteed correct)\n ↓\nJSON/YAML Output\n ↓\nPlugin: serde_json::Value → nu_protocol::Value\n ↓\nNushell Records/Lists\n\n\n### Implementation Details\n\nCore Functions (helpers.rs):\n\nrust\npub(crate) fn run_nickel_command(\n file: &str,\n format: &str,\n output: Option<&str>,\n) -> Result<String>\n\n\nPlugin Commands (main.rs):\n\n1. nickel-export - Export/evaluate Nickel files (JSON/YAML)\n2. nickel-eval - Evaluate with automatic caching (primary config loader)\n3. nickel-format - Format Nickel files\n4. nickel-validate - Validate Nickel files/directories\n5. nickel-cache-status - Show cache information\n\nOutput Processing:\n\n- Invokes: nickel export /file.ncl --format json\n- Captures: stdout (JSON string)\n- Parses: serde_json::Value\n- Converts: json_value_to_nu_value() recursive function\n- Returns: nu_protocol::Value (records/lists, not strings)\n\nCaching (non-blocking, graceful degradation):\n\n- SHA256 content-addressed cache\n- Location: ~/.cache/provisioning/config-cache/\n- Key: SHA256(file_content + format + context)\n- Hit rate: Expected 80-90% in typical workflows\n\n### Type System\n\nCommand signatures declare Type::Any output:\n\nrust\n.input_output_type(Type::Any, Type::Any)\n\n\nThis allows:\n\n- Plugin returns: nu_protocol::Value::Record\n- Nushell receives: proper record (not string)\n- Cell path access works: nickel-export json /file.ncl | .config | .servers\n\n## Rationale\n\n### Why CLI Wrapper Over Pure Rust\n\n| Aspect | Pure Rust (nickel-lang-core) | CLI Wrapper (chosen) |\n|--------|----------------------------|----------------------|\n| Module resolution | Undocumented | Works automatically |\n| Import system | Unclear how to use | Built-in |\n| Standard library | Access unclear | Automatic |\n| Search paths | How to configure? | CLI handles |\n| Maintenance | Track CLI changes | No maintenance |\n| Error handling | Different from CLI | Same as CLI |\n| Complexity | 🔴 High (undocumented) | 🟢 Low |\n| External CLI | None needed | Requires nickel binary |\n\n### Why Not Pure Rust\n\nUsing nickel-lang-core directly would require:\n\n1. Understand module resolution:\n - How does it find imported modules?\n - What are the search paths?\n - How does it resolve import "base/package"?\n\n2. Access standard library:\n\n rust\n // Where is the stdlib?\n let stdlib_path = find_nickel_stdlib()?;\n // Is it version-dependent?\n // How to verify?\n \n\n3. Handle evaluation context:\n - Build context configuration\n - Search path management\n - Module caching\n - Dependency resolution\n\n4. Match CLI behavior exactly:\n - Error messages\n - Validation rules\n - Output formatting\n - Export modes\n\nThis requires deep understanding of Nickel internals and maintaining parity with CLI.\n\n### Single Source of Truth\n\nDelegating to the CLI ensures:\n\n- Official implementation handles all cases\n- Nickel updates automatically available\n- No maintenance burden\n- Guaranteed compatibility\n\n## Consequences\n\n### Positive\n\n- Correctness: Module resolution guaranteed correct by official CLI\n- Simplicity: No need to reverse-engineer Nickel internals\n- Maintenance: Updates to Nickel automatically available\n- Features: All CLI features automatically supported\n- Compatibility: Works with all Nickel versions\n- Reliability: Single point of truth (official implementation)\n- Error Handling: Consistent with CLI user expectations\n\n### Negative\n\n- External Dependency: Requires nickel binary in PATH\n- Performance Overhead: Process fork (~100-200ms vs direct call)\n- Process Management: Spawns subprocess for each execution\n- Error Output: Subprocess stderr handling required\n\n### Mitigations\n\nFor External Dependency:\n\n- Clear documentation: setup guide with Nickel installation\n- Error messages: helpful if nickel not found\n- Distribution: Nickel included in provisioning distributions\n\nFor Performance Overhead:\n\n- Caching: 80-90% hit rate in typical workflows\n- Cache hits: ~1-5ms (not 100-200ms)\n- Lazy evaluation: Only runs when needed\n\n## Alternatives Considered\n\n### Alternative 1: Pure Rust with nickel-lang-core\n\nRejected: Module system undocumented, high maintenance cost\n\n### Alternative 2: Pure Rust with manual module implementation\n\nRejected: Duplicates official CLI, maintenance nightmare\n\n### Alternative 3: Hybrid (pure Rust + CLI fallback)\n\nRejected: Adds complexity, two implementations to maintain\n\n### Alternative 4: Use Nickel LSP (Language Server)\n\nRejected: LSP not designed for programmatic evaluation\n\n## Implementation Status\n\n### Completed\n\n- Plugin command infrastructure (5 commands)\n- CLI invocation via Command::new("nickel")\n- Correct command syntax: nickel export /file --format json\n- JSON output parsing (serde_json → nu_protocol)\n- Recursive value conversion (records, lists, primitives)\n- Caching system (SHA256, filesystem-based)\n- Error handling (CLI errors → Nushell errors)\n- Type system (Type::Any for proper output types)\n\n### Key Fix\n\nCommand Syntax: Changed from positional to flag-based:\n\nrust\n// BEFORE (WRONG):\ncmd.arg("export").arg(format).arg(file);\n// Result: "nickel export json /file" → auto-imports nonexistent JSON module\n\n// AFTER (CORRECT):\ncmd.arg("export").arg(file).arg("--format").arg(format);\n// Result: "nickel export /file --format json" → works correctly\n\n\n### Files\n\n- src/main.rs - Plugin commands and JSON parsing (95 lines of logic)\n- src/helpers.rs - CLI invocation and caching (300+ lines)\n- tests/ - Test suite for all commands\n\n## Testing\n\nManual Testing:\n\nbash\n# Test basic execution\nnickel-export json /path/to/file.ncl\n\n# Test with configuration\nnickel-export json /workspace/config.ncl | .database\n\n# Test cache\nnickel-cache-status\n\n\nVerification:\n\n- Module imports work correctly\n- Output is proper records (not strings)\n- Cell path access works\n- Cache hits are fast\n- Error messages are helpful\n\n## References\n\n- Nickel Official Documentation\n- nickel-lang-core Crate\n- Module System Design\n- Caching Strategy\n- JSON Output Format\n\n---\n\nAuthor: Architecture Team\nDate: 2025-12-15\nDecision Made By: Technical Review