## NimPak CLI - Next-generation package manager for NexusOS ## ## Main entry point for the `nip` command-line tool. ## Provides atomic, reproducible package management with ACUL compliance. import std/[os, strformat, strutils, posix, asyncdispatch, tables, sequtils] import nimpak/cli/[core, commands, enhanced_search, diagnostics_commands, setup_commands] import nimpak/cli/help import nimpak/cli/graft_commands as graft # Variant system integration import nimpak/cli/build_commands as build # Source building with variants import nimpak/cli/variant_switch # Variant switching commands import nimpak/cli/bootstrap_commands # Build tool bootstrap # import nip/doctor # import nip/cli/resolve_command # Dependency resolution commands import nimpak/config # import nip/[manifest_parser, archives, nip_installer, namespace, cas, types] const NimPakVersion = "0.1.0" NimPakBanner = """ 🌱 NimPak v$1 - Universal Package Manager for NexusOS Atomic • Reproducible • ACUL Compliant • 205,000+ Packages """ % [NimPakVersion] proc showConfigCommand(): CommandResult = ## Show current configuration let config = loadConfig() let output = fmt""" Current Configuration: Programs Dir: {config.programsDir} Links Dir: {config.linksDir} Cache Dir: {config.cacheDir} Active Profile: {config.activeProfile} Adapters: {config.adapters.len} enabled Variant Targets: {config.defaultTarget} """ return successResult(output) proc initConfigCommand(): CommandResult = ## Initialize default configuration try: initDefaultConfig() return successResult(fmt"Initialized default configuration at {getConfigPath()}") except Exception as e: return errorResult(fmt"Failed to initialize configuration: {e.msg}") proc dispatchCommand(args: seq[string]): int = ## Main command dispatcher with advanced CLI features if args.len == 0: showMainHelp() return 0 # Parse global options let (globalOptions, remainingArgs) = parseGlobalOptions(args) discard initCliContext(globalOptions) if remainingArgs.len == 0: showMainHelp() return 0 let command = remainingArgs[0].toLower() let commandArgs = remainingArgs[1..^1] # Check PATH configuration (except for setup command itself) if command != "setup" and not checkPathConfigured(): echo "⚠️ NIP binary path is NOT in your PATH." echo " Run `nip setup user` to automatically configure your shell." echo "" var commandRes: CommandResult try: case command: # Core package management commands of "install": if commandArgs.len == 0: commandRes = errorResult("Usage: nip install [options]") else: let target = commandArgs[0] if target.endsWith(".nip"): commandRes = errorResult("Local NIP installation not yet migrated") else: let verbose = globalOptions.verbose # Map 'install' to 'graft' for now as per MVP let exitCode = graft.graftCommand(target, verbose) commandRes = if exitCode == 0: successResult("Package installed") else: errorResult("Install failed") of "remove": if commandArgs.len == 0: commandRes = errorResult("Usage: nip remove ") else: let verbose = globalOptions.verbose let exitCode = graft.removeCommand(commandArgs[0], verbose) commandRes = if exitCode == 0: successResult("Package removed") else: errorResult("Failed to remove package") of "update": commandRes = errorResult("Update command not yet implemented") of "setup": commandRes = setup_commands.setupCommand(commandArgs) of "search": if commandArgs.len == 0: commandRes = errorResult("Usage: nip search ") else: # Check for --remote flag manually if needed, or pass all args let queryArgs = commandArgs.filterIt(it != "--remote") let query = queryArgs.join(" ") commandRes = enhanced_search.enhancedSearchCommand(query) # Variant & Graft commands of "graft": if commandArgs.len == 0: commandRes = errorResult("Usage: nip graft ") else: let verbose = globalOptions.verbose let exitCode = graft.graftCommand(commandArgs[0], verbose) commandRes = if exitCode == 0: successResult("Graft complete") else: errorResult("Graft failed") of "switch": if commandArgs.len < 2: commandRes = errorResult("Usage: nip switch ") else: let exitCode = variant_switch.switchCommand(commandArgs[0], commandArgs[1]) commandRes = if exitCode == 0: successResult("Switch complete") else: errorResult("Switch failed") of "build": if commandArgs.len == 0: commandRes = errorResult("Usage: nip build [flags]") else: let pkg = commandArgs[0] let flags = if commandArgs.len > 1: commandArgs[1..^1] else: @[] let verbose = globalOptions.verbose let exitCode = build.buildCommand(pkg, flags, "auto", verbose) commandRes = if exitCode == 0: successResult("Build complete") else: errorResult("Build failed") of "bootstrap": if commandArgs.len == 0: commandRes = errorResult("Usage: nip bootstrap ...") else: let subCmd = commandArgs[0].toLower() let exitCode = case subCmd of "list": bootstrap_commands.bootstrapListRecipesCommand() of "install": if commandArgs.len > 1: bootstrap_commands.bootstrapInstallCommand(commandArgs[1]) else: 1 of "remove": if commandArgs.len > 1: bootstrap_commands.bootstrapRemoveCommand(commandArgs[1]) else: 1 of "info": if commandArgs.len > 1: bootstrap_commands.bootstrapInfoCommand(commandArgs[1]) else: 1 of "recipes": bootstrap_commands.bootstrapListRecipesCommand() of "update-recipes": bootstrap_commands.bootstrapUpdateRecipesCommand() of "help": bootstrap_commands.bootstrapHelpCommand() 0 else: 1 commandRes = if exitCode == 0: successResult("Bootstrap command successful") else: errorResult("Bootstrap command failed") # Cell Management of "cell": commandRes = errorResult("Cell commands not fully implemented in this dispatch") # App / Nexter / GC / CAS stubs of "app", "nexter", "gc", "cas": commandRes = successResult(fmt"Command '{command}' is a placeholder in this MVP") # Reproducibility commands of "lock": commandRes = lockCommand() of "restore": if commandArgs.len == 0: commandRes = errorResult("Usage: nip restore ") else: commandRes = restoreCommand(commandArgs[0]) of "diff": commandRes = diffCommand() # Verification commands of "verify": commandRes = enhanced_search.verifyCommand(commandArgs) of "diagnose": if commandArgs.len == 0: commandRes = errorResult("Usage: nip diagnose ") else: commandRes = enhanced_search.diagnoseCommand(commandArgs) # Doctor/health check commands of "doctor": let autoRepair = "--fix" in commandArgs or "--auto" in commandArgs let outputFormat = if "--json" in commandArgs: "json" else: "plain" let report = waitFor diagnostics_commands.nipDoctor(outputFormat, autoRepair) echo report commandRes = successResult("System health check completed") # Configuration commands of "config": if commandArgs.len == 0: commandRes = errorResult("Usage: nip config ") else: case commandArgs[0].toLower(): of "show": commandRes = showConfigCommand() of "init": commandRes = initConfigCommand() of "path": commandRes = successResult(getConfigPath()) else: commandRes = errorResult("Unknown config command") of "resolve": commandRes = errorResult("Resolve command not yet migrated") of "explain": commandRes = errorResult("Explain command not yet migrated") # Help commands of "help", "--help", "-h": if commandArgs.len > 0: showCommandHelp(commandArgs[0]) else: showMainHelp() return 0 of "version", "--version", "-v": echo NimPakBanner return 0 else: commandRes = errorResult(fmt"Unknown command: {command}") except Exception as e: commandRes = errorResult(fmt"Unexpected error: {e.msg}") # Output result if not commandRes.success: echo fmt"❌ Error: {commandRes.message}" return 1 else: if commandRes.message != "": echo commandRes.message return 0 # ============================================================================= # Main Entry Point # ============================================================================= when isMainModule: let args = commandLineParams() let exitCode = dispatchCommand(args) quit(exitCode)