use clap::{Args, Parser, Subcommand};
pub use crate::cloud::cli::{
ActivityCommands, AuthCommands, BackupCommands, BackupConfigCommands, ClickPipeCommands,
ClickPipeCreateCommands, ClickPipeSettingsCommands, CloudArgs, CloudCommands,
InvitationCommands, KeyCommands, MemberCommands, OrgCommands, PrivateEndpointCommands,
QueryEndpointCommands, ServiceCommands,
};
pub use crate::cloud::postgres::{
CertsCommands as PostgresCertsCommands, ConfigCommands as PostgresConfigCommands,
PostgresCommands, ReadReplicaCommands as PostgresReadReplicaCommands,
};
pub use crate::local::cli::LocalArgs;
#[derive(Parser)]
#[command(name = "clickhousectl")]
#[command(about = "The official CLI for ClickHouse: local and cloud", long_about = None)]
#[command(version)]
#[command(after_help = "\
CONTEXT FOR AGENTS:
clickhousectl is a CLI to work with local ClickHouse and ClickHouse Cloud.
Two main workflows:
1. Local: Install and interact with versions of ClickHouse to develop locally.
2. Cloud: Manage ClickHouse Cloud infrastructure and push local work to cloud.
Authentication: OAuth (`cloud auth login`) is read-only. For write operations (create, update,
delete), use API key auth: `cloud auth login --api-key X --api-secret Y`.
You can install the ClickHouse Agent Skills with:
`clickhousectl skills`
Typical local workflow: `clickhousectl local install stable && clickhousectl local use stable && clickhousectl local server start`.")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
#[command(after_help = "\
CONTEXT FOR AGENTS:
Manage local ClickHouse installations: install versions, run queries, manage servers.
Typical workflow: `clickhousectl local install stable && clickhousectl local use stable && clickhousectl local server start`.
Use `clickhousectl local <command> --help` for details on each subcommand.
For a local Postgres instance via Docker, see `clickhousectl local postgres --help`.")]
Local(LocalArgs),
#[command(after_help = "\
CONTEXT FOR AGENTS:
Used for managing ClickHouse Cloud infrastructure. You need to have a ClickHouse Cloud account and be authenticated.
OAuth login (`cloud auth login`) is read-only — it can list and inspect resources but cannot create, modify, or delete.
For write operations, authenticate with API keys:
clickhousectl cloud auth login --api-key YOUR_KEY --api-secret YOUR_SECRET
If the user doesn't have an account, suggest `clickhousectl cloud auth signup` first.
Add --json to any cloud command for machine-readable output.
Typical workflow: `cloud auth login` → `cloud auth status` → `cloud org list` → `cloud service list`")]
Cloud(Box<CloudArgs>),
#[command(after_help = "\
CONTEXT FOR AGENTS:
Installs the official ClickHouse agent skills. These skills contain knowledge on how to use
ClickHouse and this CLI.
Using any flags skips interactive mode. Project scope is the default. Universal `.agents/skills`
is always included.")]
Skills(SkillsArgs),
#[command(after_help = "\
CONTEXT FOR AGENTS:
Self-update command. Downloads the latest clickhousectl release from GitHub and replaces the
current binary. Use --check to see if an update is available without installing.")]
Update(UpdateArgs),
}
#[derive(Args, Debug)]
pub struct SkillsArgs {
#[arg(
long = "agent",
value_name = "AGENT",
value_delimiter = ',',
long_help = "Install into specific agents (repeatable or comma-separated).\n\nValid names:\n claude, cursor, opencode, codex\n agent, roo, trae, windsurf\n zencoder, neovate, pochi, adal\n openclaw, cline, command-code\n kiro-cli, agents"
)]
pub agents: Vec<String>,
#[arg(long, conflicts_with_all = ["agents", "detected_only"])]
pub all: bool,
#[arg(long = "detected-only", conflicts_with_all = ["agents", "all"])]
pub detected_only: bool,
#[arg(long)]
pub global: bool,
}
#[derive(Args, Debug)]
pub struct UpdateArgs {
#[arg(long)]
pub check: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_skills_all_and_agent_flags() {
let cli = Cli::try_parse_from(["clickhousectl", "skills", "--all"]).unwrap();
let Commands::Skills(args) = cli.command else {
panic!("expected skills command");
};
assert!(args.all);
assert!(args.agents.is_empty());
assert!(!args.detected_only);
assert!(!args.global);
let cli = Cli::try_parse_from(["clickhousectl", "skills", "--global"]).unwrap();
let Commands::Skills(args) = cli.command else {
panic!("expected skills command");
};
assert!(args.global);
assert!(!args.all);
assert!(!args.detected_only);
assert!(args.agents.is_empty());
let cli = Cli::try_parse_from(["clickhousectl", "skills", "--detected-only"]).unwrap();
let Commands::Skills(args) = cli.command else {
panic!("expected skills command");
};
assert!(args.detected_only);
assert!(!args.all);
assert!(!args.global);
assert!(args.agents.is_empty());
let cli = Cli::try_parse_from([
"clickhousectl",
"skills",
"--global",
"--agent",
"claude,codex",
"--agent",
"agents",
])
.unwrap();
let Commands::Skills(args) = cli.command else {
panic!("expected skills command");
};
assert!(!args.all);
assert!(!args.detected_only);
assert!(args.global);
assert_eq!(args.agents, vec!["claude", "codex", "agents"]);
}
}