use std::{ffi::OsString, path::PathBuf};
use crate::secrets_cli::SecretsCommand;
use clap::{Arg, ArgAction, Args, CommandFactory, Parser, Subcommand};
#[derive(Parser, Debug)]
#[command(name = "greentic-dev")]
#[command(version)]
#[command(about = "cli.root.about")]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
pub fn localized_help_command(locale: &str) -> clap::Command {
let mut command = Cli::command()
.about(crate::i18n::t(locale, "cli.root.about"))
.disable_help_subcommand(true)
.disable_help_flag(true)
.disable_version_flag(true)
.arg(
Arg::new("help")
.short('h')
.long("help")
.action(ArgAction::Help)
.help(crate::i18n::t(locale, "cli.help.flag")),
)
.arg(
Arg::new("version")
.short('V')
.long("version")
.action(ArgAction::Version)
.help(crate::i18n::t(locale, "cli.version.flag")),
)
.arg(
Arg::new("locale")
.long("locale")
.global(true)
.value_name("LOCALE")
.help(crate::i18n::t(locale, "cli.option.locale")),
);
for (name, key) in [
("flow", "cli.command.flow.about"),
("pack", "cli.command.pack.about"),
("component", "cli.command.component.about"),
("bundle", "cli.command.bundle.about"),
("runner", "cli.command.runner.about"),
("config", "cli.command.config.about"),
("coverage", "cli.command.coverage.about"),
("mcp", "cli.command.mcp.about"),
("gui", "cli.command.gui.about"),
("secrets", "cli.command.secrets.about"),
("tools", "cli.command.tools.about"),
("install", "cli.command.install.about"),
("release", "cli.command.release.about"),
("cbor", "cli.command.cbor.about"),
("wizard", "cli.command.wizard.about"),
] {
command = command.mut_subcommand(name, |sub| sub.about(crate::i18n::t(locale, key)));
}
command = command.mut_subcommand("secrets", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.secrets.about"))
.mut_subcommand("init", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.secrets.init.about"))
.mut_arg("pack", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.secrets.init.pack"))
})
.mut_arg("passthrough", |arg| {
arg.help(crate::i18n::t(
locale,
"cli.command.secrets.init.passthrough",
))
})
})
});
command = command
.mut_subcommand("config", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.config.about"))
.mut_subcommand("set", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.config.set.about"))
.mut_arg("key", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.config.set.key"))
})
.mut_arg("value", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.config.set.value"))
})
.mut_arg("file", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.config.set.file"))
})
})
})
.mut_subcommand("mcp", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.mcp.about"))
.mut_subcommand("doctor", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.mcp.doctor.about"))
.mut_arg("provider", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.mcp.doctor.provider"))
})
.mut_arg("json", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.mcp.doctor.json"))
})
})
})
.mut_subcommand("tools", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.tools.about"))
.mut_subcommand("install", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.tools.install.about"))
.mut_arg("latest", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.tools.install.latest"))
})
})
})
.mut_subcommand("install", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.install.about"))
.mut_subcommand("tools", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.install.tools.about"))
.mut_arg("latest", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.tools.install.latest"))
})
})
.mut_arg("tenant", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.install.tenant"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.install.token"))
})
.mut_arg("bin_dir", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.install.bin_dir"))
})
.mut_arg("docs_dir", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.install.docs_dir"))
})
.mut_arg("locale", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.install.locale"))
})
})
.mut_subcommand("release", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.about"))
.mut_subcommand("generate", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.generate.about"))
.mut_arg("release", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.release"))
})
.mut_arg("from", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.from"))
})
.mut_arg("repo", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.repo"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.token"))
})
.mut_arg("out", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.out"))
})
.mut_arg("dry_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.dry_run"))
})
})
.mut_subcommand("publish", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.publish.about"))
.mut_arg("release", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.release"))
})
.mut_arg("from", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.from"))
})
.mut_arg("tag", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.tag"))
})
.mut_arg("manifest", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.manifest"))
})
.mut_arg("repo", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.repo"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.token"))
})
.mut_arg("out", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.out"))
})
.mut_arg("dry_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.dry_run"))
})
.mut_arg("force", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.force"))
})
})
.mut_subcommand("view", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.view.about"))
.mut_arg("release", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.release"))
})
.mut_arg("tag", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.tag"))
})
.mut_arg("repo", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.repo"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.token"))
})
})
.mut_subcommand("latest", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.latest.about"))
.mut_arg("repo", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.repo"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.token"))
})
.mut_arg("dry_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.dry_run"))
})
.mut_arg("force", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.force"))
})
})
.mut_subcommand("promote", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.release.promote.about"))
.mut_arg("release", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.release"))
})
.mut_arg("tag", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.tag"))
})
.mut_arg("repo", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.repo"))
})
.mut_arg("token", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.token"))
})
.mut_arg("dry_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.release.dry_run"))
})
})
})
.mut_subcommand("cbor", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.cbor.about"))
.mut_arg("path", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.cbor.path"))
})
})
.mut_subcommand("coverage", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.coverage.about"))
.mut_arg("skip_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.coverage.skip_run"))
})
})
.mut_subcommand("wizard", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.wizard.about"))
.mut_arg("answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.answers"))
})
.mut_arg("frontend", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.frontend"))
})
.mut_arg("locale", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.locale"))
})
.mut_arg("emit_answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.emit_answers"))
})
.mut_arg("schema", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.schema"))
.long_help(crate::i18n::t(locale, "cli.command.wizard.schema_long"))
})
.mut_arg("schema_version", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.schema_version"))
})
.mut_arg("migrate", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.migrate"))
})
.mut_arg("out", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.out"))
})
.mut_arg("dry_run", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.dry_run"))
})
.mut_arg("yes", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.yes"))
})
.mut_arg("non_interactive", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.non_interactive"))
})
.mut_arg("unsafe_commands", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.unsafe_commands"))
})
.mut_arg("allow_destructive", |arg| {
arg.help(crate::i18n::t(
locale,
"cli.command.wizard.allow_destructive",
))
})
.mut_subcommand("validate", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.wizard.validate.about"))
.mut_arg("answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.answers"))
})
.mut_arg("frontend", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.frontend"))
})
.mut_arg("locale", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.locale"))
})
.mut_arg("emit_answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.emit_answers"))
})
.mut_arg("schema_version", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.schema_version"))
})
.mut_arg("migrate", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.migrate"))
})
.mut_arg("out", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.out"))
})
})
.mut_subcommand("apply", |sub| {
sub.about(crate::i18n::t(locale, "cli.command.wizard.apply.about"))
.mut_arg("answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.answers"))
})
.mut_arg("frontend", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.frontend"))
})
.mut_arg("locale", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.locale"))
})
.mut_arg("emit_answers", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.emit_answers"))
})
.mut_arg("schema_version", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.schema_version"))
})
.mut_arg("migrate", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.migrate"))
})
.mut_arg("out", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.out"))
})
.mut_arg("yes", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.yes"))
})
.mut_arg("non_interactive", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.non_interactive"))
})
.mut_arg("unsafe_commands", |arg| {
arg.help(crate::i18n::t(locale, "cli.command.wizard.unsafe_commands"))
})
.mut_arg("allow_destructive", |arg| {
arg.help(crate::i18n::t(
locale,
"cli.command.wizard.allow_destructive",
))
})
})
});
localize_help_tree(command, locale, true)
}
fn localize_help_tree(mut command: clap::Command, locale: &str, is_root: bool) -> clap::Command {
command = command
.disable_help_subcommand(true)
.disable_help_flag(true);
let arg_ids = command
.get_arguments()
.map(|arg| arg.get_id().as_str().to_string())
.collect::<Vec<_>>();
if arg_ids.iter().any(|id| id == "help") {
command = command.mut_arg("help", |arg| {
arg.help(crate::i18n::t(locale, "cli.help.flag"))
});
} else {
command = command.arg(
Arg::new("help")
.short('h')
.long("help")
.action(ArgAction::Help)
.help(crate::i18n::t(locale, "cli.help.flag")),
);
}
if is_root && arg_ids.iter().any(|id| id == "version") {
command = command.mut_arg("version", |arg| {
arg.help(crate::i18n::t(locale, "cli.version.flag"))
});
}
let sub_names = command
.get_subcommands()
.map(|sub| sub.get_name().to_string())
.collect::<Vec<_>>();
for name in sub_names {
command = command.mut_subcommand(name, |sub| localize_help_tree(sub, locale, false));
}
command
}
#[derive(Subcommand, Debug)]
pub enum Command {
Flow(PassthroughArgs),
Pack(PassthroughArgs),
Component(PassthroughArgs),
Bundle(PassthroughArgs),
Runner(PassthroughArgs),
#[command(subcommand)]
Config(ConfigCommand),
Coverage(CoverageArgs),
#[command(subcommand)]
Mcp(McpCommand),
Gui(PassthroughArgs),
#[command(subcommand)]
Secrets(SecretsCommand),
#[command(subcommand)]
Tools(ToolsCommand),
Install(InstallArgs),
#[command(subcommand)]
Release(ReleaseCommand),
Cbor(CborArgs),
Wizard(Box<WizardCommand>),
}
#[derive(Args, Debug, Clone)]
#[command(disable_help_flag = true)]
pub struct PassthroughArgs {
#[arg(
value_name = "ARGS",
trailing_var_arg = true,
allow_hyphen_values = true
)]
pub args: Vec<OsString>,
}
#[derive(Subcommand, Debug)]
pub enum McpCommand {
Doctor(McpDoctorArgs),
}
#[derive(Args, Debug)]
pub struct McpDoctorArgs {
pub provider: String,
#[arg(long = "json")]
pub json: bool,
}
#[derive(Subcommand, Debug)]
pub enum ConfigCommand {
Set(ConfigSetArgs),
}
#[derive(Subcommand, Debug)]
pub enum ToolsCommand {
Install(ToolsInstallArgs),
}
#[derive(Subcommand, Debug)]
pub enum InstallSubcommand {
Tools(ToolsInstallArgs),
}
#[derive(Subcommand, Debug)]
pub enum ReleaseCommand {
Generate(ReleaseGenerateArgs),
Publish(ReleasePublishArgs),
View(ReleaseViewArgs),
Latest(ReleaseLatestArgs),
Promote(ReleasePromoteArgs),
}
#[derive(Args, Debug)]
pub struct InstallArgs {
#[command(subcommand)]
pub command: Option<InstallSubcommand>,
#[arg(long = "tenant")]
pub tenant: Option<String>,
#[arg(long = "token")]
pub token: Option<String>,
#[arg(long = "bin-dir")]
pub bin_dir: Option<PathBuf>,
#[arg(long = "docs-dir")]
pub docs_dir: Option<PathBuf>,
#[arg(long = "locale")]
pub locale: Option<String>,
}
#[derive(Args, Debug)]
pub struct ToolsInstallArgs {
#[arg(long = "latest")]
pub latest: bool,
}
#[derive(Args, Debug)]
pub struct ReleaseGenerateArgs {
#[arg(long = "release")]
pub release: String,
#[arg(long = "from", default_value = "latest")]
pub from: String,
#[arg(
long = "repo",
default_value = "ghcr.io/greenticai/greentic-versions/gtc"
)]
pub repo: String,
#[arg(long = "token")]
pub token: Option<String>,
#[arg(long = "out", default_value = "dist/toolchains")]
pub out: PathBuf,
#[arg(long = "dry-run")]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct ReleasePublishArgs {
#[arg(long = "release", required_unless_present = "manifest")]
pub release: Option<String>,
#[arg(long = "from", default_value = "latest", requires = "release")]
pub from: Option<String>,
#[arg(long = "tag")]
pub tag: Option<String>,
#[arg(long = "manifest", required_unless_present = "release")]
pub manifest: Option<PathBuf>,
#[arg(
long = "repo",
default_value = "ghcr.io/greenticai/greentic-versions/gtc"
)]
pub repo: String,
#[arg(long = "token")]
pub token: Option<String>,
#[arg(long = "out", default_value = "dist/toolchains")]
pub out: PathBuf,
#[arg(long = "dry-run")]
pub dry_run: bool,
#[arg(long = "force")]
pub force: bool,
}
#[derive(Args, Debug)]
pub struct ReleaseViewArgs {
#[arg(
long = "release",
conflicts_with = "tag",
required_unless_present = "tag"
)]
pub release: Option<String>,
#[arg(
long = "tag",
conflicts_with = "release",
required_unless_present = "release"
)]
pub tag: Option<String>,
#[arg(
long = "repo",
default_value = "ghcr.io/greenticai/greentic-versions/gtc"
)]
pub repo: String,
#[arg(long = "token")]
pub token: Option<String>,
}
#[derive(Args, Debug)]
pub struct ReleaseLatestArgs {
#[arg(
long = "repo",
default_value = "ghcr.io/greenticai/greentic-versions/gtc"
)]
pub repo: String,
#[arg(long = "token")]
pub token: Option<String>,
#[arg(long = "dry-run")]
pub dry_run: bool,
#[arg(long = "force")]
pub force: bool,
}
#[derive(Args, Debug)]
pub struct ReleasePromoteArgs {
#[arg(long = "release")]
pub release: String,
#[arg(long = "tag")]
pub tag: String,
#[arg(
long = "repo",
default_value = "ghcr.io/greenticai/greentic-versions/gtc"
)]
pub repo: String,
#[arg(long = "token")]
pub token: Option<String>,
#[arg(long = "dry-run")]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct ConfigSetArgs {
pub key: String,
pub value: String,
#[arg(long = "file")]
pub file: Option<PathBuf>,
}
#[derive(Args, Debug)]
pub struct CborArgs {
#[arg(value_name = "PATH")]
pub path: PathBuf,
}
#[derive(Args, Debug, Clone)]
pub struct CoverageArgs {
#[arg(long = "skip-run")]
pub skip_run: bool,
}
#[derive(Args, Debug, Clone)]
pub struct WizardCommand {
#[command(subcommand)]
pub command: Option<WizardSubcommand>,
#[command(flatten)]
pub launch: WizardLaunchArgs,
}
#[derive(Subcommand, Debug, Clone)]
pub enum WizardSubcommand {
Validate(WizardValidateArgs),
Apply(WizardApplyArgs),
}
#[derive(Args, Debug, Clone)]
pub struct WizardLaunchArgs {
#[arg(long = "answers")]
pub answers: Option<String>,
#[arg(long = "frontend", default_value = "json")]
pub frontend: String,
#[arg(long = "locale")]
pub locale: Option<String>,
#[arg(long = "emit-answers")]
pub emit_answers: Option<PathBuf>,
#[arg(long = "schema")]
pub schema: bool,
#[arg(long = "schema-version")]
pub schema_version: Option<String>,
#[arg(long = "migrate")]
pub migrate: bool,
#[arg(long = "out")]
pub out: Option<PathBuf>,
#[arg(long = "dry-run")]
pub dry_run: bool,
#[arg(long = "yes")]
pub yes: bool,
#[arg(long = "non-interactive")]
pub non_interactive: bool,
#[arg(long = "unsafe-commands")]
pub unsafe_commands: bool,
#[arg(long = "allow-destructive")]
pub allow_destructive: bool,
}
#[derive(Args, Debug, Clone)]
pub struct WizardValidateArgs {
#[arg(long = "answers")]
pub answers: String,
#[arg(long = "frontend", default_value = "json")]
pub frontend: String,
#[arg(long = "locale")]
pub locale: Option<String>,
#[arg(long = "emit-answers")]
pub emit_answers: Option<PathBuf>,
#[arg(long = "schema-version")]
pub schema_version: Option<String>,
#[arg(long = "migrate")]
pub migrate: bool,
#[arg(long = "out")]
pub out: Option<PathBuf>,
}
#[derive(Args, Debug, Clone)]
pub struct WizardApplyArgs {
#[arg(long = "answers")]
pub answers: String,
#[arg(long = "frontend", default_value = "json")]
pub frontend: String,
#[arg(long = "locale")]
pub locale: Option<String>,
#[arg(long = "emit-answers")]
pub emit_answers: Option<PathBuf>,
#[arg(long = "schema-version")]
pub schema_version: Option<String>,
#[arg(long = "migrate")]
pub migrate: bool,
#[arg(long = "out")]
pub out: Option<PathBuf>,
#[arg(long = "yes")]
pub yes: bool,
#[arg(long = "non-interactive")]
pub non_interactive: bool,
#[arg(long = "unsafe-commands")]
pub unsafe_commands: bool,
#[arg(long = "allow-destructive")]
pub allow_destructive: bool,
}