# Plugin Exclusion System (v1.0.0)\n\n## Overview\n\nThe Plugin Exclusion System is a configuration-driven mechanism that allows selective exclusion of plugins from distributions, collections, and installations while maintaining full availability for development, testing, and reference purposes.\n\n**Status**: Implemented (2025-12-03)\n**Purpose**: Exclude reference/documentation plugins (like `nu_plugin_example`) from end-user distributions\n\n---\n\n## Architecture\n\n### Design Principle\n\n**Single Source of Truth**: All plugin exclusions are centrally defined in `etc/plugin_registry.toml`, ensuring consistency across all distribution-related operations.\n\n```plaintext\nplugin_registry.toml (source of truth)\n ↓\n ┌───┴────────────────────────────────────┐\n ↓ ↓\ncollect_full_binaries.nu create_distribution_packages.nu\n(collection operations) (packaging operations)\n ↓ ↓\ndistribution/ directories bin_archives/ packages\n```\n\n### Configuration\n\n#### Plugin Registry Entry\n\n**File**: `etc/plugin_registry.toml`\n\n```toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example"\n]\nreason = "Reference/documentation plugin - excluded from distributions, installations, and collections. Still included in build and test for validation."\n```\n\n**Structure**:\n\n- `excluded_plugins` (required): List of plugin names to exclude from distributions\n- `reason` (optional): Documentation of why plugins are excluded\n\n**Adding New Exclusions**:\n\n```toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example",\n "nu_plugin_new_reference" # Add here\n]\n```\n\n---\n\n## Implementation\n\n### 1. Collection System (`scripts/collect_full_binaries.nu`)\n\n#### Helper Function\n\n```nu\ndef get_excluded_plugins []: nothing -> list {\n try {\n let registry_path = "./etc/plugin_registry.toml"\n if not ($registry_path | path exists) {\n return []\n }\n\n let registry_content = open $registry_path\n let excluded = try {\n $registry_content.distribution.excluded_plugins\n } catch {\n []\n }\n\n return $excluded\n } catch {\n return []\n }\n}\n```\n\n**Key Features**:\n\n- Reads exclusion list from registry\n- Graceful error handling (returns empty list if registry missing/malformed)\n- Non-blocking (collection continues even if registry unavailable)\n\n#### Workspace Plugins Filtering\n\n```nu\ndef get_workspace_plugins_info [platform: string, use_release: bool, profile: string]: nothing -> list {\n let excluded_plugins = (get_excluded_plugins)\n\n let workspace_plugins = [\n "nu_plugin_custom_values"\n "nu_plugin_example"\n "nu_plugin_formats"\n # ... other plugins\n ]\n\n # Filter out excluded plugins\n let available_plugins = $workspace_plugins | where { |p| $p not-in $excluded_plugins }\n\n # Process only available plugins\n for plugin in $available_plugins {\n # ... collection logic\n }\n}\n```\n\n#### Custom Plugins Filtering\n\n```nu\ndef get_custom_plugins_info [platform: string, use_release: bool, profile: string]: nothing -> list {\n let excluded_plugins = (get_excluded_plugins)\n\n let plugin_dirs = (glob $"nu_plugin_*")\n | where ($it | path type) == "dir"\n | where ($it | path basename) != "nushell"\n | where { |p| ($p | path basename) not-in $excluded_plugins } # Filter excluded\n | each { |p| $p | path basename }\n\n # Process remaining plugins\n}\n```\n\n### 2. Packaging System (`scripts/create_distribution_packages.nu`)\n\n#### Helper Function\n\n```nu\ndef get_excluded_plugins_dist []: nothing -> list {\n try {\n let registry_path = "./etc/plugin_registry.toml"\n if not ($registry_path | path exists) {\n return []\n }\n\n let registry_content = open $registry_path\n let excluded = try {\n $registry_content.distribution.excluded_plugins\n } catch {\n []\n }\n\n return $excluded\n } catch {\n return []\n }\n}\n```\n\n#### Plugin Components Filtering\n\n```nu\ndef get_plugin_components [platform: string, version: string] {\n let extension = get_binary_extension $platform\n let excluded_plugins = (get_excluded_plugins_dist)\n\n # Get custom plugins - skip excluded ones\n let custom_plugin_binaries = (\n glob "nu_plugin_*"\n | where ($it | path type) == "dir"\n | each {|plugin_dir|\n let plugin_name = ($plugin_dir | path basename)\n if $plugin_name in $excluded_plugins {\n null\n } else {\n # ... process plugin\n }\n }\n | compact\n )\n\n # Get workspace plugins - filter excluded\n let workspace_plugins = [\n "nu_plugin_custom_values"\n "nu_plugin_example"\n # ... other plugins\n ]\n\n let workspace_plugin_binaries = (\n $workspace_plugins\n | where { |p| $p not-in $excluded_plugins } # Filter excluded\n | each {|plugin_name|\n # ... process plugin\n }\n | compact\n )\n\n {\n binaries: ($custom_plugin_binaries | append $workspace_plugin_binaries)\n }\n}\n```\n\n### 3. Installation Configuration (`scripts/templates/default_config.nu`)\n\n#### Auto-load Plugin List\n\n**Before**:\n\n```nu\nlet plugin_binaries = [\n "nu_plugin_clipboard"\n "nu_plugin_desktop_notifications"\n "nu_plugin_hashes"\n "nu_plugin_highlight"\n "nu_plugin_image"\n "nu_plugin_kcl"\n "nu_plugin_tera"\n "nu_plugin_custom_values"\n "nu_plugin_example" # ❌ Would be auto-loaded\n "nu_plugin_formats"\n # ...\n]\n```\n\n**After**:\n\n```nu\n# Auto-load common plugins if they're available\n# NOTE: nu_plugin_example is excluded from distributions - it's for reference and development only\nlet plugin_binaries = [\n "nu_plugin_clipboard"\n "nu_plugin_desktop_notifications"\n "nu_plugin_hashes"\n "nu_plugin_highlight"\n "nu_plugin_image"\n "nu_plugin_kcl"\n "nu_plugin_tera"\n "nu_plugin_custom_values"\n "nu_plugin_formats" # ✅ Auto-loaded (example removed)\n # ...\n]\n```\n\n---\n\n## Behavior Matrix\n\n| Operation | Excluded Plugin | Included Plugin |\n|-----------|-----------------|-----------------|\n| `just build` | ✅ Built | ✅ Built |\n| `just build-nushell` | ✅ Built | ✅ Built |\n| `just test` | ✅ Tested | ✅ Tested |\n| `just collect` | ❌ Excluded | ✅ Collected |\n| `just collect-full` | ❌ Excluded | ✅ Collected |\n| `just pack` | ❌ Excluded | ✅ Packaged |\n| `just pack-full` | ❌ Excluded | ✅ Packaged |\n| Distribution Installation | ❌ Not auto-loaded | ✅ Auto-loaded |\n| Manual Reference Use | ✅ Available | ✅ Available |\n\n---\n\n## Use Cases\n\n### Use Case 1: Reference Plugin\n\n**Scenario**: Plugin serves as a template/documentation reference but shouldn't ship with distributions\n\n**Configuration**:\n\n```toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example"\n]\nreason = "Template for plugin developers. Not intended for end users."\n```\n\n**Result**:\n\n- Developers can still use it: `./nushell/target/release/nu_plugin_example`\n- End-user distributions don't include it\n- Documentation can reference it as a learning resource\n\n### Use Case 2: Experimental Plugin\n\n**Scenario**: Plugin is under development and not yet stable\n\n**Configuration**:\n\n```toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example",\n "nu_plugin_experimental"\n]\nreason = "Experimental features. Stable once API is finalized."\n```\n\n**Result**:\n\n- Can be tested internally\n- Not distributed to users until ready\n- Easily re-enabled by removing from list\n\n### Use Case 3: Conditional Exclusion\n\n**Scenario**: Plugin should only be excluded for specific use cases\n\n**Implementation Note**: The current system excludes globally. For conditional exclusion, extend the registry:\n\n```toml\n[distribution]\nexcluded_plugins = ["nu_plugin_example"]\n\n[distribution.profiles]\nenterprise = ["nu_plugin_example", "nu_plugin_dev_tools"]\nminimal = ["nu_plugin_example", "nu_plugin_kcl", "nu_plugin_tera"]\n```\n\nThen update scripts to support profile-based filtering.\n\n---\n\n## Error Handling\n\n### Scenario: Registry File Missing\n\n**Behavior**: Scripts return empty exclusion list, all plugins included\n\n```nu\nif not ($registry_path | path exists) {\n return [] # No exclusions\n}\n```\n\n**Result**: Safe degradation - system works without registry\n\n### Scenario: Registry Parse Error\n\n**Behavior**: Catches exception, returns empty list\n\n```nu\nlet excluded = try {\n $registry_content.distribution.excluded_plugins\n} catch {\n [] # If key missing or malformed\n}\n```\n\n**Result**: Malformed registry doesn't break distribution process\n\n### Scenario: Invalid Plugin Name\n\n**Behavior**: Non-existent plugins in exclusion list are silently skipped\n\n```nu\n| where { |p| $p not-in $excluded_plugins } # No match = included\n```\n\n**Result**: Future-proofs against plugin renames or removals\n\n---\n\n## Integration Points\n\n### 1. Collection Workflow\n\n```plaintext\njust collect\n ↓\ncollect_full_binaries.nu main\n ↓\nget_excluded_plugins() → registry.toml\n ↓\nget_workspace_plugins_info() → [filtered list]\nget_custom_plugins_info() → [filtered list]\n ↓\ndistribution/ (without excluded plugins)\n```\n\n### 2. Packaging Workflow\n\n```plaintext\njust pack-full\n ↓\ncreate_distribution_packages.nu main\n ↓\nget_excluded_plugins_dist() → registry.toml\n ↓\nget_plugin_components() → [filtered list]\n ↓\nbin_archives/ (without excluded plugins)\n```\n\n### 3. Build Workflow\n\n```plaintext\njust build (unchanged)\n ↓\nbuild_all.nu\n ↓\ncargo build (all plugins including excluded)\n ↓\ntarget/release/ (includes ALL plugins)\n```\n\n### 4. Installation Workflow\n\n```plaintext\ndistribution/platform/\n ↓\ndefault_config.nu (filters excluded at config level)\n ↓\nUser's Nushell config (excluded plugins not auto-loaded)\n```\n\n---\n\n## Maintenance\n\n### Adding a Plugin to Exclusion List\n\n1. **Update Registry**:\n\n```bash\n# Edit: etc/plugin_registry.toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example",\n "nu_plugin_new_ref" # ← Add here\n]\n```\n\n1. **Optional: Update Default Config**:\n\n```bash\n# Edit: scripts/templates/default_config.nu\n# Add comment explaining why it's excluded\n```\n\n1. **Test**:\n\n```bash\njust collect # Should exclude both plugins\njust pack-full # Should package without both\njust build # Should still build both\n```\n\n### Removing a Plugin from Exclusion List\n\n1. **Update Registry**:\n\n```bash\n# Edit: etc/plugin_registry.toml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example" # ← Removed\n]\n```\n\n1. **Test**:\n\n```bash\njust collect # Should now include it\njust pack-full # Should now package it\n```\n\n---\n\n## Files Modified\n\n| File | Changes | Type |\n|------|---------|------|\n| `etc/plugin_registry.toml` | Added `[distribution]` section | Config |\n| `scripts/collect_full_binaries.nu` | Added `get_excluded_plugins()`, updated workspace/custom filtering | Feature |\n| `scripts/create_distribution_packages.nu` | Added `get_excluded_plugins_dist()`, updated component filtering | Feature |\n| `scripts/templates/default_config.nu` | Removed excluded plugin from auto-load list | Config |\n\n---\n\n## Performance Impact\n\n- **Collection**: Negligible (single registry read, O(n) filtering where n = excluded count)\n- **Packaging**: Negligible (same as collection)\n- **Build**: None (excluded plugins still built)\n- **Installation**: None (config parsing is same cost)\n\n---\n\n## Future Enhancements\n\n1. **Profile-Based Exclusion**: Support different exclusion lists per distribution profile\n\n ```toml\n [distribution.profiles]\n enterprise = [...]\n minimal = [...]\n ```\n\n2. **Conditional Compilation**: Exclude from build based on feature flags\n\n ```rust\n #[cfg(feature = "include_example")]\n pub mod example;\n ```\n\n3. **Deprecation Timeline**: Mark plugins as deprecated with removal date\n\n ```toml\n [distribution.deprecated]\n "nu_plugin_old" = "2025-12-31" # Will be removed after date\n ```\n\n4. **Exclusion Reasoning**: Rich metadata about why plugins are excluded\n\n ```toml\n [distribution.exclusions."nu_plugin_example"]\n reason = "reference_plugin"\n since_version = "0.109.0"\n target_inclusion = "never" # or "1.0.0"\n ```\n\n---\n\n## References\n\n- **Registry**: `etc/plugin_registry.toml`\n- **Collection Scripts**: `scripts/collect_full_binaries.nu`\n- **Packaging Scripts**: `scripts/create_distribution_packages.nu`\n- **Configuration**: `scripts/templates/default_config.nu`\n- **Build System**: `justfile`, `justfiles/build.just`, `scripts/build_all.nu`\n\n---\n\n## Testing\n\n### Verification Checklist\n\n- [ ] Registry reads correctly: `nu -c "open ./etc/plugin_registry.toml | get distribution.excluded_plugins"`\n- [ ] Collection excludes: `just collect && ls distribution/ | grep example` (should be empty)\n- [ ] Packaging excludes: `just pack-full && tar -tzf bin_archives/*.tar.gz | grep example` (should be empty)\n- [ ] Build includes: `just build-nushell && ls nushell/target/release/ | grep example` (should exist)\n- [ ] Config doesn't auto-load: `grep nu_plugin_example scripts/templates/default_config.nu` (should not appear in plugin_binaries list)\n\n---\n\n**Version**: 1.0.0\n**Last Updated**: 2025-12-03\n**Status**: Stable