v0.4.1 : Phase 1 complete (17/17) - dashboard TUI (#33), favoris/notes/tags (#39), profils d'environnement (#40), completions dynamiques + man pages (#41), packaging officiel + CI (#42), self-update Gitea, state v3, 212 tests
This commit is contained in:
+55
-13
@@ -13,31 +13,73 @@ struct SearchRow {
|
||||
description: Option<String>,
|
||||
website: Option<String>,
|
||||
tags: Vec<String>,
|
||||
/// Personal tags (issue #39).
|
||||
personal_tags: Vec<String>,
|
||||
favorite: bool,
|
||||
}
|
||||
|
||||
pub fn run(app: &App, keyword: &str, category: Option<&str>) -> Result<i32> {
|
||||
let hits = app.catalog.search(keyword, category);
|
||||
pub fn run(app: &App, keyword: Option<&str>, category: Option<&str>, tag: Option<&str>) -> Result<i32> {
|
||||
let keyword = keyword.unwrap_or("");
|
||||
// An empty keyword (search --tag only) starts from the whole catalog.
|
||||
let mut hits: Vec<&AgentDef> = if keyword.is_empty() {
|
||||
app.catalog
|
||||
.agents()
|
||||
.iter()
|
||||
.filter(|a| !a.hidden)
|
||||
.filter(|a| category.map_or(true, |c| a.category.as_deref() == Some(c)))
|
||||
.collect()
|
||||
} else {
|
||||
app.catalog.search(keyword, category)
|
||||
};
|
||||
// Personal tag filter (issue #39): keep agents carrying this tag.
|
||||
if let Some(t) = tag {
|
||||
let tagged = crate::commands::annotations_cmd::agents_with_tag(app, t);
|
||||
if tagged.is_empty() {
|
||||
app.log.info(&format!(
|
||||
"no agent is tagged '{t}' — 'am tags' lists every personal tag"
|
||||
));
|
||||
return Ok(0);
|
||||
}
|
||||
hits.retain(|a| tagged.contains(&a.name));
|
||||
}
|
||||
if hits.is_empty() && tag.is_some() {
|
||||
app.log.info(&format!(
|
||||
"no agent matches the keyword and the tag '{}'",
|
||||
tag.unwrap_or("")
|
||||
));
|
||||
return Ok(0);
|
||||
}
|
||||
if app.json() {
|
||||
let rows: Vec<SearchRow> = hits
|
||||
.iter()
|
||||
.map(|a| SearchRow {
|
||||
name: a.name.clone(),
|
||||
display_name: a.title().to_string(),
|
||||
category: a.category.clone(),
|
||||
installable: a.installable,
|
||||
description: a.description.clone(),
|
||||
website: a.website.clone(),
|
||||
tags: a.tags.clone(),
|
||||
.map(|a| {
|
||||
let personal = crate::commands::annotations_cmd::tags_of(app, &a.name);
|
||||
SearchRow {
|
||||
name: a.name.clone(),
|
||||
display_name: a.title().to_string(),
|
||||
category: a.category.clone(),
|
||||
installable: a.installable,
|
||||
description: a.description.clone(),
|
||||
website: a.website.clone(),
|
||||
tags: a.tags.clone(),
|
||||
personal_tags: personal,
|
||||
favorite: crate::commands::annotations_cmd::is_favorite(app, &a.name),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
print_json(&rows);
|
||||
return Ok(0);
|
||||
}
|
||||
let mut table_rows = Vec::new();
|
||||
for a in hits {
|
||||
for a in &hits {
|
||||
let desc = truncate(a.description.as_deref().unwrap_or(""), 56);
|
||||
let star = if crate::commands::annotations_cmd::is_favorite(app, &a.name) {
|
||||
"★ "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
table_rows.push(vec![
|
||||
a.title().to_string(),
|
||||
format!("{star}{}", a.title()),
|
||||
a.category.clone().unwrap_or_else(|| "—".to_string()),
|
||||
if a.installable { "yes" } else { "no" }.to_string(),
|
||||
desc,
|
||||
@@ -52,7 +94,7 @@ pub fn run(app: &App, keyword: &str, category: Option<&str>) -> Result<i32> {
|
||||
app.color(),
|
||||
)
|
||||
);
|
||||
if app.catalog.search(keyword, category).is_empty() {
|
||||
if hits.is_empty() && !keyword.is_empty() {
|
||||
let suggestions = app.catalog.suggest(keyword);
|
||||
if suggestions.is_empty() {
|
||||
app.log
|
||||
|
||||
Reference in New Issue
Block a user