use anyhow::Result;
use std::path::Path;
use agent_kit::skill::SkillConfig;
const BUNDLED_SKILL: &str = include_str!("../SKILL.md");
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn config() -> SkillConfig {
SkillConfig::new("agent-doc", BUNDLED_SKILL, VERSION)
}
fn resolve_root() -> Option<std::path::PathBuf> {
let output = std::process::Command::new("git")
.args(["rev-parse", "--show-superproject-working-tree"])
.output()
.ok()?;
let root = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !root.is_empty() {
return Some(std::path::PathBuf::from(root));
}
let output = std::process::Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.ok()?;
let root = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !root.is_empty() {
return Some(std::path::PathBuf::from(root));
}
None
}
#[allow(dead_code)]
pub fn install_at(root: Option<&Path>) -> Result<()> {
let resolved = root.map(|p| p.to_path_buf()).or_else(resolve_root);
config().install(resolved.as_deref())
}
#[allow(dead_code)]
pub fn install() -> Result<()> {
install_at(None)
}
pub fn install_and_check_updated() -> Result<bool> {
let cfg = config();
let resolved = resolve_root();
let path = cfg.skill_path(resolved.as_deref());
let was_current = path.exists()
&& std::fs::read_to_string(&path)
.map(|existing| existing == cfg.content)
.unwrap_or(false);
cfg.install(resolved.as_deref())?;
Ok(!was_current)
}
pub fn check_at(root: Option<&Path>) -> Result<()> {
let resolved = root.map(|p| p.to_path_buf()).or_else(resolve_root);
let up_to_date = config().check(resolved.as_deref())?;
if !up_to_date {
std::process::exit(1);
}
Ok(())
}
pub fn check() -> Result<()> {
check_at(None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bundled_skill_is_not_empty() {
assert!(!BUNDLED_SKILL.is_empty());
}
#[test]
fn bundled_skill_contains_agent_doc() {
assert!(BUNDLED_SKILL.contains("agent-doc"));
}
#[test]
fn install_creates_file() {
let dir = tempfile::tempdir().unwrap();
install_at(Some(dir.path())).unwrap();
let path = dir.path().join(".claude/skills/agent-doc/SKILL.md");
assert!(path.exists());
let content = std::fs::read_to_string(&path).unwrap();
assert_eq!(content, BUNDLED_SKILL);
}
#[test]
fn install_idempotent() {
let dir = tempfile::tempdir().unwrap();
install_at(Some(dir.path())).unwrap();
install_at(Some(dir.path())).unwrap();
let path = dir.path().join(".claude/skills/agent-doc/SKILL.md");
let content = std::fs::read_to_string(&path).unwrap();
assert_eq!(content, BUNDLED_SKILL);
}
#[test]
fn check_not_installed() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join(".claude/skills/agent-doc/SKILL.md");
assert!(!path.exists());
}
#[test]
fn install_overwrites_outdated() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join(".claude/skills/agent-doc/SKILL.md");
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, "old content").unwrap();
install_at(Some(dir.path())).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert_eq!(content, BUNDLED_SKILL);
}
}