104 lines
3.9 KiB
Rust
104 lines
3.9 KiB
Rust
//! alias: manage command aliases (cc -> claude-code) in the user config.
|
|
|
|
use super::*;
|
|
use crate::cli::AliasCmd;
|
|
use crate::output::print_json;
|
|
use anyhow::{anyhow, Context, Result};
|
|
use std::path::PathBuf;
|
|
|
|
/// Ensure the user config file exists and return its path.
|
|
fn user_config(app: &App) -> Result<PathBuf> {
|
|
let dir = app
|
|
.paths
|
|
.config_dir
|
|
.clone()
|
|
.ok_or_else(|| anyhow!("cannot determine the user config directory (no HOME set)"))?;
|
|
std::fs::create_dir_all(&dir)?;
|
|
let path = dir.join(crate::config::CONFIG_FILE_NAME);
|
|
if !path.exists() {
|
|
std::fs::write(&path, concat!("version: \"1.0\"\n", "agents: []\n"))
|
|
.with_context(|| format!("cannot create {}", path.display()))?;
|
|
}
|
|
Ok(path)
|
|
}
|
|
|
|
/// Read the aliases map from the user config file.
|
|
fn read_aliases(app: &App) -> Result<(PathBuf, serde_yaml::Mapping)> {
|
|
let path = user_config(app)?;
|
|
let text = std::fs::read_to_string(&path)
|
|
.with_context(|| format!("cannot read {}", path.display()))?;
|
|
let mut value: serde_yaml::Value = serde_yaml::from_str(&text)
|
|
.with_context(|| format!("{} is not valid YAML", path.display()))?;
|
|
let mapping = value
|
|
.as_mapping_mut()
|
|
.ok_or_else(|| anyhow!("{} must contain a YAML mapping", path.display()))?;
|
|
let aliases = mapping
|
|
.entry(serde_yaml::Value::String("aliases".to_string()))
|
|
.or_insert_with(|| serde_yaml::Value::Mapping(Default::default()))
|
|
.as_mapping()
|
|
.cloned()
|
|
.unwrap_or_default();
|
|
Ok((path, aliases))
|
|
}
|
|
|
|
fn write_aliases(app: &App, path: &std::path::Path, aliases: serde_yaml::Mapping) -> Result<()> {
|
|
let text = std::fs::read_to_string(path)
|
|
.with_context(|| format!("cannot read {}", path.display()))?;
|
|
let mut value: serde_yaml::Value = serde_yaml::from_str(&text)
|
|
.with_context(|| format!("{} is not valid YAML", path.display()))?;
|
|
if let Some(mapping) = value.as_mapping_mut() {
|
|
mapping.insert(
|
|
serde_yaml::Value::String("aliases".to_string()),
|
|
serde_yaml::Value::Mapping(aliases),
|
|
);
|
|
}
|
|
std::fs::write(path, serde_yaml::to_string(&value)?)
|
|
.with_context(|| format!("cannot write {}", path.display()))
|
|
}
|
|
|
|
pub fn run(app: &App, sub: &AliasCmd) -> Result<i32> {
|
|
match sub {
|
|
AliasCmd::Add { name, target } => {
|
|
// The target must be a known agent (or another alias).
|
|
require_agent(app, target)?;
|
|
let (path, mut aliases) = read_aliases(app)?;
|
|
aliases.insert(
|
|
serde_yaml::Value::String(name.clone()),
|
|
serde_yaml::Value::String(target.clone()),
|
|
);
|
|
write_aliases(app, &path, aliases)?;
|
|
app.log.success(&format!("alias '{name}' -> '{target}'"));
|
|
Ok(0)
|
|
}
|
|
AliasCmd::Remove { name } => {
|
|
let (path, mut aliases) = read_aliases(app)?;
|
|
let removed = aliases
|
|
.remove(&serde_yaml::Value::String(name.clone()))
|
|
.is_some();
|
|
if removed {
|
|
write_aliases(app, &path, aliases)?;
|
|
app.log.success(&format!("alias '{name}' removed"));
|
|
} else {
|
|
app.log.info(&format!("alias '{name}' does not exist"));
|
|
}
|
|
Ok(0)
|
|
}
|
|
AliasCmd::List => {
|
|
if app.json() {
|
|
print_json(&app.config.aliases);
|
|
return Ok(0);
|
|
}
|
|
if app.config.aliases.is_empty() {
|
|
app.log.info("no aliases defined — 'am alias add cc claude-code'");
|
|
return Ok(0);
|
|
}
|
|
let mut table = crate::output::Table::new(vec!["ALIAS", "TARGET"]);
|
|
for (k, v) in &app.config.aliases {
|
|
table.row(vec![k.clone(), v.clone()]);
|
|
}
|
|
print!("{}", table.render());
|
|
Ok(0)
|
|
}
|
|
}
|
|
}
|