feat: skills delete — 交互式删除用户 Skill

新增 ias skills delete:
  扫描 skills/ 目录,列出所有用户 Skill
  MultiSelect TUI 选择要删除的 Skill
  确认后 remove_dir_all 物理删除
This commit is contained in:
2026-06-01 22:00:48 +08:00
parent cef8bee8ee
commit 438c52e79b
12 changed files with 1582 additions and 0 deletions
+57
View File
@@ -814,5 +814,62 @@ async fn cmd_skills(action: SkillsAction) {
println!("\n完成:导入 {imported} 个,跳过 {skipped}");
println!("重启 iAs 后生效");
}
SkillsAction::Delete => {
let skills_dir = std::path::PathBuf::from("skills");
if !skills_dir.exists() {
println!("skills 目录不存在,没有可删除的 Skill");
return;
}
let mut entries: Vec<(String, String)> = Vec::new();
if let Ok(dir) = std::fs::read_dir(&skills_dir) {
for entry in dir.flatten() {
let path = entry.path();
if path.is_dir() && path.join("SKILL.md").exists() {
let name = path.file_name().unwrap_or_default().to_string_lossy().to_string();
let desc = std::fs::read_to_string(path.join("SKILL.md"))
.ok()
.and_then(|s| s.lines().nth(0).map(|l| l.trim_start_matches("# ").to_string()))
.unwrap_or_default();
entries.push((name, desc));
}
}
}
entries.sort_by(|a, b| a.0.cmp(&b.0));
if entries.is_empty() {
println!("skills 目录下没有可删除的 Skill");
return;
}
let items: Vec<String> = entries.iter()
.map(|(name, desc)| format!("{}{}", name, desc))
.collect();
println!("选择要删除的 Skill(空格选择,回车确认,q 退出):");
let selection = MultiSelect::with_theme(&ColorfulTheme::default())
.items(&items)
.interact_on(&Term::stderr())
.unwrap_or_default();
if selection.is_empty() {
println!("未选择任何 Skill");
return;
}
let mut deleted = 0;
for idx in selection {
let (name, _) = &entries[idx];
let path = skills_dir.join(name);
match std::fs::remove_dir_all(&path) {
Ok(_) => {
println!(" ✅ 已删除 {name}");
deleted += 1;
}
Err(e) => println!(" ❌ 删除 {name} 失败: {e}"),
}
}
println!("\n完成:删除 {deleted}");
}
}
}