feat: implement package manager

Adds dependency management for Lux projects:
- `lux pkg install` - Install all dependencies from lux.toml
- `lux pkg add <pkg>` - Add a dependency (supports --git, --path)
- `lux pkg remove <pkg>` - Remove a dependency
- `lux pkg list` - List dependencies with install status
- `lux pkg update` - Update all dependencies
- `lux pkg clean` - Remove installed packages

Supports three dependency sources:
- Registry (placeholder for future package registry)
- Git repositories (with optional branch)
- Local file paths

Packages are installed to .lux_packages/ in the project root.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 10:44:49 -05:00
parent 1c59fdd735
commit a037f5bd2f
2 changed files with 777 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ mod interpreter;
mod lexer;
mod lsp;
mod modules;
mod package;
mod parser;
mod schema;
mod typechecker;
@@ -133,6 +134,10 @@ fn main() {
std::process::exit(1);
}
}
"pkg" => {
// Package manager
handle_pkg_command(&args[2..]);
}
path => {
// Run a file
run_file(path);
@@ -156,6 +161,7 @@ fn print_help() {
println!(" lux watch <file.lux> Watch and re-run on changes");
println!(" lux debug <file.lux> Start interactive debugger");
println!(" lux init [name] Initialize a new project");
println!(" lux pkg <command> Package manager (install, add, remove, list, update)");
println!(" lux --lsp Start LSP server (for IDE integration)");
println!(" lux --help Show this help");
println!(" lux --version Show version");
@@ -382,6 +388,156 @@ fn watch_file(path: &str) {
}
}
fn handle_pkg_command(args: &[String]) {
use package::{PackageManager, DependencySource};
use std::path::PathBuf;
if args.is_empty() {
print_pkg_help();
return;
}
// Help doesn't require being in a project
if matches!(args[0].as_str(), "help" | "--help" | "-h") {
print_pkg_help();
return;
}
let project_root = match PackageManager::find_project_root() {
Some(root) => root,
None => {
eprintln!("Error: Not in a Lux project (no lux.toml found)");
eprintln!("Run 'lux init' to create a new project");
std::process::exit(1);
}
};
let pkg = PackageManager::new(&project_root);
match args[0].as_str() {
"install" | "i" => {
if let Err(e) = pkg.install() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"add" => {
if args.len() < 2 {
eprintln!("Usage: lux pkg add <package> [version] [--git <url>] [--path <path>]");
std::process::exit(1);
}
let name = &args[1];
let mut version = "0.0.0".to_string();
let mut source = DependencySource::Registry;
let mut i = 2;
while i < args.len() {
match args[i].as_str() {
"--git" => {
if i + 1 < args.len() {
let url = args[i + 1].clone();
let branch = if i + 3 < args.len() && args[i + 2] == "--branch" {
i += 2;
Some(args[i + 1].clone())
} else {
None
};
source = DependencySource::Git { url, branch };
i += 2;
} else {
eprintln!("--git requires a URL");
std::process::exit(1);
}
}
"--path" => {
if i + 1 < args.len() {
source = DependencySource::Path {
path: PathBuf::from(&args[i + 1]),
};
i += 2;
} else {
eprintln!("--path requires a path");
std::process::exit(1);
}
}
v if !v.starts_with('-') => {
version = v.to_string();
i += 1;
}
_ => i += 1,
}
}
if let Err(e) = pkg.add(name, &version, source) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"remove" | "rm" => {
if args.len() < 2 {
eprintln!("Usage: lux pkg remove <package>");
std::process::exit(1);
}
if let Err(e) = pkg.remove(&args[1]) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"list" | "ls" => {
if let Err(e) = pkg.list() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"update" => {
if let Err(e) = pkg.update() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"clean" => {
if let Err(e) = pkg.clean() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
"help" | "--help" | "-h" => {
print_pkg_help();
}
unknown => {
eprintln!("Unknown package command: {}", unknown);
print_pkg_help();
std::process::exit(1);
}
}
}
fn print_pkg_help() {
println!("Lux Package Manager");
println!();
println!("Usage: lux pkg <command> [options]");
println!();
println!("Commands:");
println!(" install, i Install all dependencies from lux.toml");
println!(" add <pkg> Add a dependency");
println!(" Options:");
println!(" [version] Specify version (default: 0.0.0)");
println!(" --git <url> Install from git repository");
println!(" --branch <name> Git branch (with --git)");
println!(" --path <path> Install from local path");
println!(" remove, rm Remove a dependency");
println!(" list, ls List dependencies and their status");
println!(" update Update all dependencies");
println!(" clean Remove installed packages");
println!();
println!("Examples:");
println!(" lux pkg install");
println!(" lux pkg add http 1.0.0");
println!(" lux pkg add mylib --git https://github.com/user/mylib");
println!(" lux pkg add local-lib --path ../lib");
println!(" lux pkg remove http");
}
fn init_project(name: Option<&str>) {
use std::fs;
use std::path::Path;