nip/src/nip.nim

252 lines
8.8 KiB
Nim

## 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 <package> [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 <package>")
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 <query>")
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 <package>")
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 <package> <variant>")
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 <package> [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 <list|install|remove|info> ...")
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 <lockfile>")
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 <cid>")
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 <show|init|path>")
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)