pmpx 0.0.0

One command surface for a project's package managers. Detects the project type, routes the verb, and spawns the real tool (cargo, pnpm, ...).
//! pmpx —— 一个命令面,识别项目类型,转发给真正的工具。
//!
//! # 这个二进制里没有一行插件代码
//!
//! 支持 cargo 的能力、支持 pnpm 的能力,都在各自的插件仓库里,运行时 `dlopen` 进来。
//! 见 `README.md` 与设计文档第 8 节。
//!
//! # 分层
//!
//! ```text
//! main.rs       进程边界:解析 argv、把退出码翻译成进程退出
//! cli.rs        只做解析,把 argv 变成结构体
//! commands.rs   子命令的处理(读上下文 → 做事 → 打印)
//! app.rs        一次运行的上下文 + 从动词到 spawn 的完整流程
//! config.rs     全局配置 + 多层 .pmpx.toml 合并
//! discovery.rs  项目根发现(walk_up)
//! plugins.rs    扫描插件目录、读 manifest(**不 dlopen**)
//! detect.rs     打分 + 两级裁决
//! runtime.rs    加载选中的那个插件、跨 ABI 调它
//! spawn.rs      把 CommandSpec 变成真正的进程
//! hints.rs      零插件时的提示表(**不参与裁决**)
//! ```
//!
//! # 一条硬规矩
//!
//! 检测阶段**绝不加载任何插件代码**(`plugins.rs` 只读 manifest)。
//! 在一个从没装过插件的仓库里跑 `pmpx`,它不会执行任何第三方代码。

#![deny(missing_docs)]
#![warn(clippy::all)]

mod app;
mod cli;
mod commands;
mod config;
mod detect;
mod discovery;
mod error;
mod hints;
mod plugins;
mod runtime;
mod spawn;

use clap::Parser;

fn main() -> std::process::ExitCode {
    let args = cli::Cli::parse();
    let code = dispatch(&args);
    std::process::ExitCode::from(code)
}

/// 跑一次,返回进程退出码 —— **可能来自后端**(6.5 的最后一个码)。
fn dispatch(args: &cli::Cli) -> u8 {
    match commands::dispatch(args) {
        Ok(code) => code,
        Err(e) => {
            // 统一前缀:`pmpx:` 让用户在混着后端输出的日志里一眼分辨出哪些话是 pmpx 说的。
            eprintln!("pmpx: {e}");
            e.exit_code()
        }
    }
}