feat(cli): command dispatch, human/--json rendering, status table, completions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1,85 @@
|
|||||||
pub async fn run(_c: crate::cli::Cli) -> i32 { 0 }
|
use clap::CommandFactory;
|
||||||
|
use serde_json::Value;
|
||||||
|
use crate::cli::{Cli, Sub};
|
||||||
|
use crate::{client, mapping};
|
||||||
|
|
||||||
|
/// Entry point: returns the process exit code.
|
||||||
|
pub async fn run(cli: Cli) -> i32 {
|
||||||
|
// Completions are local — no daemon.
|
||||||
|
if let Sub::Completions { shell } = cli.cmd {
|
||||||
|
let mut cmd = Cli::command();
|
||||||
|
clap_complete::generate(shell, &mut cmd, "spacesh", &mut std::io::stdout());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify is best-effort: never fails the caller.
|
||||||
|
if let Sub::Notify { .. } = &cli.cmd {
|
||||||
|
let _ = client::notify(mapping::to_cmd(cli.cmd)).await;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_status = matches!(cli.cmd, Sub::Status);
|
||||||
|
let cmd = mapping::to_cmd(cli.cmd);
|
||||||
|
match client::request(cmd).await {
|
||||||
|
Ok(data) => {
|
||||||
|
if cli.json {
|
||||||
|
println!("{}", serde_json::to_string_pretty(&data).unwrap_or_else(|_| "null".into()));
|
||||||
|
} else if is_status {
|
||||||
|
print_status(&data);
|
||||||
|
} else {
|
||||||
|
print_human(&data);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if cli.json {
|
||||||
|
println!("{}", serde_json::json!({ "ok": false, "error": e.to_string() }));
|
||||||
|
} else {
|
||||||
|
eprintln!("{e}");
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human render for non-status commands: surface the salient id, else "ok".
|
||||||
|
fn print_human(data: &Value) {
|
||||||
|
if let Some(id) = data.get("workspace_id").and_then(|v| v.as_str()) {
|
||||||
|
println!("{id}");
|
||||||
|
} else if let Some(id) = data.get("surface_id").and_then(|v| v.as_str()) {
|
||||||
|
println!("{id}");
|
||||||
|
} else if let Some(id) = data.get("group_id").and_then(|v| v.as_str()) {
|
||||||
|
println!("{id}");
|
||||||
|
} else if let Some(ids) = data.get("surface_ids").and_then(|v| v.as_array()) {
|
||||||
|
for id in ids {
|
||||||
|
if let Some(s) = id.as_str() { println!("{s}"); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("ok");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compact table for `status`.
|
||||||
|
fn print_status(data: &Value) {
|
||||||
|
let empty = vec![];
|
||||||
|
let workspaces = data.get("workspaces").and_then(|v| v.as_array()).unwrap_or(&empty);
|
||||||
|
if workspaces.is_empty() {
|
||||||
|
println!("(no workspaces)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for w in workspaces {
|
||||||
|
let name = w.get("name").and_then(|v| v.as_str()).unwrap_or("?");
|
||||||
|
let id = w.get("id").and_then(|v| v.as_str()).unwrap_or("?");
|
||||||
|
let unread = w.get("unread").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||||
|
println!("{} ({}){}", name, id, if unread { " *" } else { "" });
|
||||||
|
if let Some(surfaces) = w.get("surfaces").and_then(|v| v.as_object()) {
|
||||||
|
for (sid, sv) in surfaces {
|
||||||
|
let running = sv.get("running").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||||
|
let state = sv.get("state").and_then(|v| v.as_str()).unwrap_or("idle");
|
||||||
|
let agent = sv.get("spec").and_then(|s| s.get("agent_label")).and_then(|v| v.as_str()).unwrap_or("shell");
|
||||||
|
let life = if running { "running" } else { "stopped" };
|
||||||
|
println!(" {sid} {agent:<8} {life:<8} {state}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user