#!/usr/bin/env nu # Plugin Status Dashboard # Shows the current status of all plugins and their upstream sync state # Version check - mandatory for all plugin operations def version_check [] { try { nu scripts/check_version.nu --quiet | ignore } catch { print "āŒ Nushell version mismatch detected!" print "šŸ”§ Run: nu scripts/check_version.nu --fix" exit 1 } } # Load plugin registry def load_registry [] { let registry_path = "etc/plugin_registry.toml" if not ($registry_path | path exists) { error make {msg: "Plugin registry not found. Please run from repository root directory."} } open $registry_path } # Load exclusions configuration def load_exclusions [] { let exclude_path = "etc/upstream_exclude.toml" if ($exclude_path | path exists) { let config = open $exclude_path { all: ($config.exclude.plugins? | default []), check: ($config.exclude.check.plugins? | default []), merge: ($config.exclude.merge.plugins? | default []), patterns: ($config.exclude.patterns.plugins? | default []) } } else { {all: [], check: [], merge: [], patterns: []} } } # Check if plugin should be excluded def is_plugin_excluded [plugin_name: string, exclusions: record] { # Check direct exclusions if $plugin_name in $exclusions.all { return true } # Check pattern exclusions for pattern in $exclusions.patterns { if ($plugin_name | str contains ($pattern | str replace "*" "")) { return true } } false } # Filter plugins to only include existing directories and non-excluded plugins def filter_plugins [plugins: record, exclusions: record] { $plugins | transpose name config | where { let plugin_name = $in.name let plugin_path = $in.config.local_path # Check if directory exists let dir_exists = ($plugin_path | path exists) # Check if plugin is excluded let is_excluded = (is_plugin_excluded $plugin_name $exclusions) $dir_exists and not $is_excluded } | reduce -f {} {|it, acc| $acc | insert $it.name $it.config } } # Save plugin registry def save_registry [registry: record] { $registry | to toml | save -f "etc/plugin_registry.toml" } # Get status emoji and description def get_status_emoji [status: string] { match $status { "ok" => "āœ…", "pending" => "āš ļø", "error" => "āŒ", "local_only" => "šŸ ", "unknown" => "ā“", "conflict" => "šŸ”„", _ => "šŸ“¦" } } # Get status description def get_status_description [status: string] { match $status { "ok" => "Synchronized with upstream", "pending" => "Changes detected - needs review", "error" => "Error during upstream check", "local_only" => "No upstream - local development", "unknown" => "Not yet checked", "conflict" => "Merge conflicts detected", _ => "Unknown status" } } # Format date for display def format_date [date_str: string] { if ($date_str | is-empty) { "Never" } else { try { $date_str | str substring 0..9 # Show just the date part } catch { $date_str } } } # Get plugin directory status def get_directory_status [local_path: string] { let full_path = $local_path if ($full_path | path exists) { if ($"($full_path)/Cargo.toml" | path exists) { "šŸ“‚ Present" } else { "šŸ“ No Cargo.toml" } } else { "āŒ Missing" } } # Display summary statistics def show_summary [plugins: record] { print "šŸ“Š Plugin Status Summary" print "==================================================" let status_counts = $plugins | values | group-by status | transpose status plugins | each {|entry| { status: $entry.status, count: ($entry.plugins | length), emoji: (get_status_emoji $entry.status) }} | sort-by count --reverse let total_plugins = ($plugins | values | length) print $"šŸ“¦ Total Plugins: ($total_plugins)" print "" for status in $status_counts { let description = get_status_description $status.status print $"($status.emoji) ($description): ($status.count) plugins" } print "" # Show breakdown by type let upstream_plugins = $plugins | values | where ($it.upstream_url | is-not-empty) | length let local_plugins = $plugins | values | where ($it.upstream_url | is-empty) | length print "šŸ“‹ Plugin Types:" print $" šŸ”— With Upstream: ($upstream_plugins) plugins" print $" šŸ  Local Only: ($local_plugins) plugins" print "" } # Show detailed plugin table def show_plugin_table [plugins: record, show_all: bool] { let plugin_data = $plugins | transpose name config | each {|plugin| { Plugin: $plugin.name, Status: $"(get_status_emoji $plugin.config.status) ($plugin.config.status)", "Last Check": (format_date $plugin.config.last_checked_date), "Directory": (get_directory_status $plugin.config.local_path), "Upstream": (if ($plugin.config.upstream_url | is-empty) { "None" } else { "šŸ“” Yes" }), "Auto-OK": (if $plugin.config.auto_ok_on_nu_deps_only { "āœ“" } else { "āœ—" }), "Description": $plugin.config.description } } let filtered_data = if $show_all { $plugin_data } else { $plugin_data | where Status !~ "local_only" } $filtered_data | table --expand } # Show plugins that need attention def show_attention_needed [plugins: record] { let needs_attention = $plugins | transpose name config | where $it.config.status in ["pending", "error", "conflict", "unknown"] if ($needs_attention | length) > 0 { print "\n🚨 Plugins Requiring Attention:" print "========================================" for plugin in $needs_attention { let emoji = get_status_emoji $plugin.config.status print $"($emoji) ($plugin.name): ($plugin.config.status)" if not ($plugin.config.upstream_url | is-empty) { print $" Upstream: ($plugin.config.upstream_url)" } if not ($plugin.config.last_checked_date | is-empty) { print $" Last checked: ($plugin.config.last_checked_date)" } print "" } } } # Show recent activity def show_recent_activity [plugins: record] { let recent_plugins = $plugins | transpose name config | where not ($it.config.last_checked_date | is-empty) | sort-by config.last_checked_date --reverse | first 5 if ($recent_plugins | length) > 0 { print "\nšŸ“… Recently Checked Plugins:" print "=" * 35 for plugin in $recent_plugins { let emoji = get_status_emoji $plugin.config.status print $"($emoji) ($plugin.name) - ($plugin.config.last_checked_date)" } print "" } } # Update plugin status manually def update_plugin_status [plugin_name: string, new_status: string] { let registry = load_registry if not ($plugin_name in ($registry.plugins | columns)) { error make {msg: $"Plugin '($plugin_name)' not found in registry"} } let valid_statuses = ["ok", "pending", "error", "conflict", "unknown", "local_only"] if not ($new_status in $valid_statuses) { error make {msg: $"Invalid status. Valid options: ($valid_statuses | str join ', ')"} } let updated_registry = $registry | upsert $"plugins.($plugin_name).status" $new_status save_registry $updated_registry print $"āœ… Updated ($plugin_name) status to '($new_status)'" } # Show help information def show_help [] { print "Plugin Status Dashboard - Nushell Plugin Upstream Tracker" print "" print "USAGE:" print " nu plugin_status.nu [OPTIONS] [COMMAND]" print "" print "OPTIONS:" print " --all, -a Show all plugins including local-only" print " --json, -j Output in JSON format" print " --help, -h Show this help message" print "" print "COMMANDS:" print " status Show plugin status dashboard (default)" print " update Update plugin status manually" print " summary Show summary statistics only" print " attention Show only plugins needing attention" print "" print "STATUS VALUES:" print " āœ… ok - Synchronized with upstream" print " āš ļø pending - Changes detected - needs review" print " āŒ error - Error during upstream check" print " šŸ”„ conflict - Merge conflicts detected" print " ā“ unknown - Not yet checked" print " šŸ  local_only - No upstream - local development" print "" print "EXAMPLES:" print " nu plugin_status.nu # Show status dashboard" print " nu plugin_status.nu --all # Show all plugins" print " nu plugin_status.nu summary # Show summary only" print " nu plugin_status.nu update highlight ok # Mark plugin as OK" } # Main function def main [ command?: string # Command to execute plugin?: string # Plugin name (for update command) status?: string # New status (for update command) --all (-a) # Show all plugins including local-only --json (-j) # Output in JSON format --help (-h) # Show help ] { if $help { show_help return } # Mandatory version check before any plugin operations version_check # Ensure we're in the repository root directory if not ("nu_plugin_clipboard" | path exists) { error make {msg: "Please run this script from the nushell-plugins repository root directory"} } # Load registry and exclusions let registry = load_registry let exclusions = load_exclusions let filtered_plugins = filter_plugins $registry.plugins $exclusions match ($command | default "status") { "status" => { if $json { $filtered_plugins | to json } else { show_summary $filtered_plugins show_plugin_table $filtered_plugins $all show_attention_needed $filtered_plugins show_recent_activity $filtered_plugins print $"\nšŸ’” Tips:" print " • Run 'nu check_upstream_changes.nu' to check for updates" print " • Use 'nu plugin_status.nu --all' to see all plugins" print " • Use 'nu plugin_status.nu update ' to manually update status" } }, "summary" => { show_summary $filtered_plugins }, "attention" => { show_attention_needed $filtered_plugins }, "update" => { if ($plugin | is-empty) or ($status | is-empty) { error make {msg: "Update command requires plugin name and status. Use: update "} } update_plugin_status $plugin $status }, _ => { error make {msg: $"Unknown command: ($command). Use --help for usage information."} } } } if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") { main }