#![deny(warnings)]
#![allow(unexpected_cfgs)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
#![allow(non_upper_case_globals)] #![allow(clippy::unused_unit)] #![allow(
clippy::needless_borrows_for_generic_args,
clippy::needless_question_mark,
clippy::new_without_default,
clippy::question_mark,
clippy::too_many_arguments,
clippy::unnecessary_lazy_evaluations,
clippy::unnecessary_map_or,
clippy::useless_conversion,
clippy::used_underscore_binding, // #[verb] macro generates code referencing underscore-prefixed CLI flag params
clippy::unused_async_trait_impl,
clippy::needless_pass_by_ref_mut
)]
pub mod config_clap;
pub mod error;
pub mod pack_install;
pub mod prelude;
pub mod progress;
pub mod validation_lib;
pub mod version_checker;
pub mod cmds; pub mod conventions; pub mod receipt_manager; pub mod runtime; pub mod runtime_helper;
pub use clap_noun_verb::{run, Result as ClapNounVerbResult};
pub use ggen_core::utils::error::Result;
pub async fn cli_match() -> ggen_core::utils::error::Result<()> {
version_checker::check_outdated_binary();
let mut manifest_path = "ggen.toml".to_string();
let args: Vec<String> = std::env::args().collect();
for i in 0..args.len() {
if (args[i] == "--manifest" || args[i] == "-m") && i + 1 < args.len() {
manifest_path = args[i + 1].clone();
break;
}
}
let mut telemetry_config = None;
if std::path::Path::new(&manifest_path).exists() {
if let Ok(content) = std::fs::read_to_string(&manifest_path) {
if let Ok(config) = toml::from_str::<ggen_core::config_lib::GgenConfig>(&content) {
if let Some(ref tel) = config.telemetry {
telemetry_config = Some(ggen_core::telemetry::TelemetryConfig {
endpoint: tel.endpoint.clone(),
service_name: tel.service_name.clone(),
console_output: tel.console_output,
});
}
}
}
}
let _telemetry_guard = if let Some(cfg) = telemetry_config {
ggen_core::telemetry::init_telemetry(cfg).ok()
} else {
None
};
let args: Vec<String> = std::env::args().skip(1).collect();
let span = tracing::info_span!("ggen.cli", command = %args.join(" "), version = env!("CARGO_PKG_VERSION"));
let _enter = span.enter();
let args: Vec<String> = std::env::args().collect();
if args.iter().any(|arg| arg == "--version" || arg == "-V") {
println!("ggen {}", env!("CARGO_PKG_VERSION"));
return Ok(());
}
clap_noun_verb::run().map_err(|e| {
ggen_core::utils::error::Error::new(&format!("CLI execution failed: {}", e))
})?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct RunResult {
pub code: i32,
pub stdout: String,
pub stderr: String,
}
pub async fn run_for_node(args: Vec<String>) -> ggen_core::utils::error::Result<RunResult> {
use std::sync::Arc;
use std::sync::Mutex;
const KNOWN_NOUNS: &[&str] = &[
"sync",
"init",
"doctor",
"pack",
"agent",
"packs",
"capability",
"graph",
"receipt",
"utils",
"policy",
"market",
"lifecycle",
"a2a",
"ci",
"framework",
"git-hooks",
"lsp",
"mcp",
"sigma",
"template",
"wizard",
"--help",
"-h",
"--version",
"-V",
"--format",
"--select",
"--introspect",
"--structured-errors",
"--autonomic",
"help",
];
let early_exit_code: Option<i32> = if let Some(first) = args.first() {
if KNOWN_NOUNS.contains(&first.as_str()) {
None } else {
log::error!("error: unrecognized subcommand '{}'", first);
Some(1)
}
} else {
None };
if let Some(code) = early_exit_code {
return Ok(RunResult {
code,
stdout: String::new(),
stderr: format!(
"error: unrecognized subcommand '{}'",
args.first().unwrap_or(&String::new())
),
});
}
let _argv: Vec<String> = std::iter::once("ggen".to_string())
.chain(args.into_iter())
.collect();
let stdout_buffer = Arc::new(Mutex::new(Vec::new()));
let stderr_buffer = Arc::new(Mutex::new(Vec::new()));
let stdout_clone = Arc::clone(&stdout_buffer);
let stderr_clone = Arc::clone(&stderr_buffer);
let result = tokio::task::spawn_blocking(move || {
let code = match cmds::run_cli() {
Ok(()) => 0,
Err(err) => {
log::error!("{}", err);
1
}
};
let _ = (stdout_clone, stderr_clone);
code
})
.await
.map_err(|e| ggen_core::utils::error::Error::new(&format!("Failed to execute CLI: {}", e)))?;
let stdout = match stdout_buffer.lock() {
Ok(guard) => String::from_utf8_lossy(&guard).to_string(),
Err(_poisoned) => {
log::warn!("Stdout buffer mutex was poisoned when reading, using empty string");
String::new()
}
};
let stderr = match stderr_buffer.lock() {
Ok(guard) => String::from_utf8_lossy(&guard).to_string(),
Err(_poisoned) => {
log::warn!("Stderr buffer mutex was poisoned when reading, using empty string");
String::new()
}
};
Ok(RunResult {
code: result,
stdout,
stderr,
})
}