mod agents;
mod verbs;
use anyhow::Result;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "pushkin", version, about = "Pushkin")]
struct Cli {
#[command(subcommand)]
verb: Verb,
}
#[derive(Subcommand)]
enum Verb {
Init {
#[arg(long)]
agent: Option<String>,
#[arg(long)]
remove_agent: Option<String>,
},
Check {
#[arg(long)]
staged: bool,
#[arg(long)]
json: bool,
},
Hook {
agent: String,
},
Compile,
Stats,
Daemon {
#[command(subcommand)]
action: DaemonAction,
},
Doctor {
#[arg(long)]
repair: bool,
},
Report,
Statusline,
Floor {
#[arg(long = "skip", value_name = "NAME")]
skip: Vec<String>,
},
Db {
#[command(subcommand)]
action: DbAction,
},
Policy {
#[command(subcommand)]
action: PolicyAction,
},
Board {
#[command(subcommand)]
verb: BoardCommand,
},
Affected {
#[arg(long)]
base: Option<String>,
},
Waive {
rule: String,
#[arg(long)]
path: String,
#[arg(long)]
ttl: String,
#[arg(long)]
reason: String,
#[arg(long)]
supersedes: Option<String>,
#[arg(long)]
contradicts: Option<String>,
},
Enable {
feature: String,
},
Disable {
feature: String,
},
Instructions {
#[arg(long)]
for_subagent: bool,
#[arg(long)]
digest: bool,
},
}
#[derive(Subcommand)]
enum DbAction {
Drift,
Rls,
}
#[derive(Subcommand)]
enum PolicyAction {
Test,
}
#[derive(Subcommand)]
enum BoardCommand {
Register(BoardScope),
Status {
#[arg(long)]
message: String,
#[command(flatten)]
scope: BoardScope,
},
Peers(BoardScope),
Broadcast {
#[arg(long)]
message: String,
#[command(flatten)]
scope: BoardScope,
},
Send {
#[arg(long)]
to: String,
#[arg(long)]
message: String,
#[command(flatten)]
scope: BoardScope,
},
Read(BoardScope),
Claim {
#[arg(long)]
path: String,
#[command(flatten)]
scope: BoardScope,
},
Release {
#[arg(long)]
path: String,
#[command(flatten)]
scope: BoardScope,
},
Claims(BoardScope),
}
#[derive(clap::Args)]
struct BoardScope {
#[arg(long)]
run: String,
#[arg(long)]
agent: String,
}
impl From<BoardCommand> for verbs::board::BoardArgs {
fn from(command: BoardCommand) -> Self {
use verbs::board::BoardVerb;
let (verb, scope) = match command {
BoardCommand::Register(scope) => (BoardVerb::Register, scope),
BoardCommand::Status { message, scope } => (BoardVerb::Status { message }, scope),
BoardCommand::Peers(scope) => (BoardVerb::Peers, scope),
BoardCommand::Broadcast { message, scope } => (BoardVerb::Broadcast { message }, scope),
BoardCommand::Send { to, message, scope } => (BoardVerb::Send { to, message }, scope),
BoardCommand::Read(scope) => (BoardVerb::Read, scope),
BoardCommand::Claim { path, scope } => (BoardVerb::Claim { path }, scope),
BoardCommand::Release { path, scope } => (BoardVerb::Release { path }, scope),
BoardCommand::Claims(scope) => (BoardVerb::Claims, scope),
};
Self {
verb,
run: scope.run,
agent: scope.agent,
}
}
}
#[derive(Subcommand)]
enum DaemonAction {
Serve {
#[arg(long)]
read_only: bool,
#[arg(long)]
socket: Option<std::path::PathBuf>,
},
Start {
#[arg(long)]
read_only: bool,
},
Stop,
Restart,
Status,
}
fn main() -> Result<()> {
let cli = Cli::parse();
let code = match cli.verb {
Verb::Init {
agent,
remove_agent,
} => verbs::init::run_with_args(agent.as_deref(), remove_agent.as_deref())?,
Verb::Check { staged, json } => {
verbs::check::run(verbs::check::CheckArgs { staged, json })?
}
Verb::Affected { base } => verbs::affected::run(base.as_deref())?,
Verb::Floor { skip } => verbs::floor::run(&skip)?,
Verb::Db { action } => match action {
DbAction::Drift => verbs::db::run_drift()?,
DbAction::Rls => verbs::db::run_rls()?,
},
Verb::Policy { action } => match action {
PolicyAction::Test => verbs::policy::run_test(),
},
Verb::Board { verb } => verbs::board::run(verbs::board::BoardArgs::from(verb))?,
Verb::Hook { agent } => verbs::hook::run(&agent)?,
Verb::Compile => verbs::compile::run()?,
Verb::Stats => verbs::stats::run()?,
Verb::Daemon { action } => match action {
DaemonAction::Serve { read_only, socket } => match socket {
Some(path) => verbs::daemon::run_serve_at(&path, read_only)?,
None => verbs::daemon::run_serve()?,
},
DaemonAction::Start { read_only } => verbs::daemon::run_start(read_only)?,
DaemonAction::Stop => verbs::daemon::run_stop(),
DaemonAction::Restart => verbs::daemon::run_restart(),
DaemonAction::Status => verbs::daemon::run_status(),
},
Verb::Doctor { repair } => verbs::doctor::run_with_repair(repair)?,
Verb::Report => verbs::report::run_report()?,
Verb::Statusline => verbs::report::run_statusline(),
Verb::Waive {
rule,
path,
ttl,
reason,
supersedes,
contradicts,
} => verbs::waive::run(verbs::waive::WaiveArgs {
rule,
path,
ttl,
reason,
supersedes,
contradicts,
})?,
Verb::Enable { feature } => verbs::features::run_enable(&feature)?,
Verb::Disable { feature } => verbs::features::run_disable(&feature)?,
Verb::Instructions {
for_subagent,
digest,
} => verbs::instructions::run(verbs::instructions::InstructionsArgs {
for_subagent,
digest,
})?,
};
std::process::exit(code);
}