Compare commits

...
3 Commits
Author SHA1 Message Date
bruno b7f29baea5 fix: assistant setup — liste des providers construite depuis le registre (QwenCloud absents de la liste codee en dur, v1.1.9)
release / release (push) Successful in 13m41s
release / macos (push) Canceled after 0s
2026-09-01 11:40:05 -04:00
bruno cf53c98d98 feat: providers QwenCloud v1.1.8 — token-plan (Credits) et pay-as-you-go (Model Studio/DashScope), qwen-code configure par defaut
release / release (push) Successful in 13m39s
release / macos (push) Canceled after 0s
2026-09-01 10:20:07 -04:00
bruno 991c79de50 feat: catalogue v1.1.7 — Antigravity CLI remplace Gemini CLI (fin de service 2026-06-18)
release / release (push) Successful in 14m0s
release / macos (push) Canceled after 0s
- nouvelle entrée 'antigravity-cli' (agy, release GitHub google-antigravity/antigravity-cli, binaire Go)
- 'gemini-cli' marqué déprécié : installable: false (réservé clé API payante), note de migration
- alias gemini/agy -> antigravity-cli ; groupe mainstream et gabarit web mis à jour
- setup_hints: agy (auth trousseau) + agy plugin import gemini (import extensions/skills)
- ROADMAP v1.1.7, README 78 agents, man pages régénérées
2026-09-01 08:48:20 -04:00
9 changed files with 154 additions and 32 deletions
+2 -2
View File
@@ -230,7 +230,7 @@ settings:
aliases:
cc: claude-code
gemini: gemini-cli
gemini: antigravity-cli
groups:
dev: [claude-code, aider, codex]
@@ -506,7 +506,7 @@ Disponibles avant ou après la sous-commande.
```yaml
aliases:
cc: claude-code
gemini: gemini-cli
gemini: antigravity-cli
```
`am start cc` démarre `claude-code`.
Generated
+1 -1
View File
@@ -21,7 +21,7 @@ dependencies = [
[[package]]
name = "agent-manager"
version = "1.1.6"
version = "1.1.9"
dependencies = [
"anyhow",
"base64",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "agent-manager"
version = "1.1.6"
version = "1.1.9"
edition = "2021"
description = "Manage local AI coding agents: list, install, start, stop, update — with automatic dependency handling and a YAML-driven catalog."
license = "MIT"
+4 -4
View File
@@ -1,7 +1,7 @@
# 🚀 agent-manager — vos agents IA, gérés comme des apps
**am** liste, installe, démarre, met à jour et désinstalle vos agents IA de
coding (Claude Code, Codex, Aider, jcode, Prime Agent… **77 agents connus**)
coding (Claude Code, Codex, Aider, jcode, Prime Agent… **78 agents connus**)
en une ligne de commande — avec la gestion des dépendances (Node.js, Python,
Go, Rust…) prise en charge pour vous.
@@ -11,7 +11,7 @@ Go, Rust…) prise en charge pour vous.
## ✨ Pourquoi am ?
- 🗂️ **Catalogue de 77 agents prêts à l'emploi** — descriptions, méthodes
- 🗂️ **Catalogue de 78 agents prêts à l'emploi** — descriptions, méthodes
d'installation, dépendances et commandes de démarrage déjà configurées.
- 📦 **9 méthodes d'installation** — npm, pip, uv, cargo, go, bun, script
d'installation, binaire (GitHub Releases/Gitea) et dépôt git — toujours
@@ -158,7 +158,7 @@ Les variables d'environnement `AGENT_MANAGER_DATA`, `AGENT_MANAGER_STATE` et `AG
#### Cycle d'installation (`am install <agent>`)
1. **Résolution du catalogue** — `am` fusionne le catalogue embarqué (77 agents) avec `~/.config/agent-manager/config.yaml` et un éventuel `./agent-manager.yaml`. Vos définitions remplacent les entrées par défaut du même nom.
1. **Résolution du catalogue** — `am` fusionne le catalogue embarqué (78 agents) avec `~/.config/agent-manager/config.yaml` et un éventuel `./agent-manager.yaml`. Vos définitions remplacent les entrées par défaut du même nom.
2. **Vérification des dépendances** — les outils requis (`node`, `python`, `uv`, `cargo`, `go`, `bun`, `git`...) sont détectés sur le `PATH`. S'il en manque, `am` détecte l'OS et propose la commande d'installation (`scoop`, `winget`, `apt`, `dnf`, `pacman`, `brew`...). Avec `--yes` ou `settings.auto_install_deps: true`, il peut l'exécuter à votre place.
3. **Choix de la méthode** — chaque agent déclare une méthode principale et des alternatives. `--method <nom>` force le choix ; sinon `am` prend la première disponible. Pour `am update`, c'est la méthode utilisée lors de l'installation initiale qui est reconstruite.
4. **Exécution de l'installateur** — selon le type, les commandes réelles lancées sont :
@@ -383,7 +383,7 @@ supprimez aussi ces emplacements.)
1. --config <fichier>
2. ./agent-manager.yaml (répertoire courant)
3. ~/.config/agent-manager/config.yaml
4. catalogue embarqué (77 agents)
4. catalogue embarqué (78 agents)
Vos définitions **complètent ou surchargent** le catalogue par nom.
+26
View File
@@ -129,6 +129,32 @@
> avec vraies longueurs > 125 octets, boucle de traduction i18n de la page
> web corrigée (plus de boucle infinie), index du trousseau (keyring)
> sérialisé en JSON (clés contenant des virgules).
>
> 🔧 **Maintenance v1.1.7 (2026-09-01)** : Gemini CLI a cessé de servir les
> comptes Google gratuits/Pro/Ultra le 2026-06-18 — remplacé par **Antigravity
> CLI** (`agy`, binaire Go fermé, même harnais qu'Antigravity 2.0). Nouveau
> catalogue : entrée `antigravity-cli` (release GitHub
> google-antigravity/antigravity-cli), `gemini-cli` marqué déprécié
> (`installable: false`, clé API payante seulement), alias `gemini`/`agy`
> redirigés, groupe `mainstream` et gabarit web mis à jour. Import des
> extensions/skills Gemini : `agy plugin import gemini`.
>
> 🔧 **Maintenance v1.1.8 (2026-09-01)** : providers **QwenCloud** ajoutés au
> registre par défaut — `qwen-cloud` (pay-as-you-go, Model Studio/DashScope,
> dashscope-intl…/compatible-mode/v1) et `qwen-cloud-token-plan` (abonnement
> Credits, token-plan.ap-southeast-1…/compatible-mode/v1, modèles restreints
> dont qwen3.8-max). `qwen-code` pointe sur qwen-cloud par défaut (env_map
> OpenAI-compatible OPENAI_API_KEY/BASE_URL/MODEL) avec hints pour basculer
> sur le Token Plan (`--provider qwen-cloud-token-plan`, clé dédiée via
> `am providers set-token`).
>
> 🐛 **Maintenance v1.1.9 (2026-09-01)** : l'assistant `am setup` listait ses
> providers dans une constante codée en dur — les QwenCloud ajoutés au
> registre n'y apparaissaient pas. La liste est maintenant construite depuis
> le registre fusionné (embarqué + config utilisateur), ollama et custom en
> plus, sans doublon. Test de non-régression ajouté. Bonus : le wizard ne
> boucle plus à l'infini sur stdin fermé (erreur claire + suggestion
> `--yes`).
---
+50 -5
View File
@@ -108,6 +108,20 @@ settings:
base_url: https://generativelanguage.googleapis.com/v1beta
default_model: gemini-2.5-pro
models: [gemini-2.5-pro, gemini-2.5-flash]
# QwenCloud (Alibaba) — deux modes de facturation, deux providers :
# - qwen-cloud-token-plan : abonnement Credits (Personal/Team), clé
# dédiée Token Plan, modèles restreints (voir docs.qwencloud.com).
# Base URL internationale (ap-southeast-1) ; la variante Pékin
# token-plan.cn-beijing.maas.aliyuncs.com sert la Chine.
# - qwen-cloud : pay-as-you-go (clé sk- Model Studio / DashScope).
qwen-cloud-token-plan:
base_url: https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1
default_model: qwen3.8-max
models: [qwen3.8-max, qwen3.8-flash, qwen3.7-max, qwen3.7-plus, qwen3.6-flash, deepseek-v4-pro, glm-5.2]
qwen-cloud:
base_url: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
default_model: qwen3.7-plus
models: [qwen3.8-max, qwen3.7-max, qwen3.7-plus, qwen3.6-flash, qwen3-coder-plus, qwen3-coder-next, deepseek-v4-pro]
openrouter:
base_url: https://openrouter.ai/api/v1
default_model: deepseek/deepseek-chat-v3-0324
@@ -122,7 +136,10 @@ settings:
# --- Command aliases ----------------------------------------------------------
aliases:
cc: claude-code
gemini: gemini-cli
# Gemini CLI (npm @google/gemini-cli) ne sert plus les comptes Google
# gratuits/Pro/Ultra depuis le 2026-06-18 — remplacé par Antigravity CLI.
gemini: antigravity-cli
agy: antigravity-cli
gh-copilot: github-copilot
q: amazon-q
droid: factory-droid
@@ -136,7 +153,7 @@ groups:
dev: [claude-code, aider, codex]
local: [atomic-agent, nanocoder, gptme, goose]
claw: [openclaw, zeroclaw, picoclaw]
mainstream: [claude-code, opencode, gemini-cli, codex, qwen-code]
mainstream: [claude-code, opencode, antigravity-cli, codex, qwen-code]
# ============================================================================
# Agents
@@ -290,9 +307,26 @@ agents:
note: "projet en mode musée — maintenance 100 % automatisée"
tags: [rust, clean-room, fork]
- name: antigravity-cli
display_name: "Antigravity CLI"
description: "Agent terminal officiel de Google (successseur de Gemini CLI, fermé, binaire Go). Même harnais d'agents qu'Antigravity 2.0 : multi-agents asynchrones, skills, hooks, plugins, MCP. Auth Google via keyring système, quota partagé avec l'app desktop (surveiller /usage)."
category: coding-agent
website: https://antigravity.google/cli
install:
type: binary
repo: google-antigravity/antigravity-cli
binary: agy
run: agy
provider: google
setup_hints:
- "agy (premier lancement : setup interactif + auth Google via le trousseau)"
- "agy plugin import gemini (importe extensions/skills de l'ancien Gemini CLI)"
tags: [google, mainstream, antigravity]
note: "Remplace Gemini CLI : le 2026-06-18, Gemini CLI a cessé de servir les comptes Google gratuits/Pro/Ultra. Import des extensions/skills Gemini : 'agy plugin import gemini' ; GEMINI.md et ~/.gemini/ restent lus (config native : ~/.gemini/antigravity-cli/, skills workspace : .agents/skills/, MCP : mcp_config.json avec clé 'serverUrl')."
- name: gemini-cli
display_name: "Gemini CLI"
description: "Agent terminal officiel Google (Apache-2.0), outils repo + recherche."
display_name: "Gemini CLI (déprécié)"
description: "Ancien agent terminal Google (Apache-2.0). Ne sert plus les comptes Google gratuits/Pro/Ultra depuis le 2026-06-18 ; reste utilisable avec une clé API Gemini payante. Migrer vers antigravity-cli."
category: coding-agent
website: https://github.com/google-gemini/gemini-cli
install:
@@ -301,7 +335,9 @@ agents:
dependencies:
- { name: node, min_version: "18.0.0", install_hint: "https://nodejs.org" }
run: gemini
tags: [google, mainstream]
installable: false
tags: [google, deprecated]
note: "déprécié — remplacé par Antigravity CLI ('am install antigravity-cli'), voir la note de cette entrée"
- name: codex
display_name: "Codex CLI"
@@ -435,6 +471,15 @@ agents:
dependencies:
- { name: node, min_version: "18.0.0", install_hint: "https://nodejs.org" }
run: qwen-code
provider: qwen-cloud
config:
env_map:
api_key: OPENAI_API_KEY
model: OPENAI_MODEL
base_url: OPENAI_BASE_URL
setup_hints:
- "am install qwen-code --provider qwen-cloud-token-plan (abonnement Credits Token Plan au lieu du pay-as-you-go)"
- "am providers set-token qwen-cloud-token-plan (clé dédiée Token Plan ; pay-as-you-go : clé sk- Model Studio)"
tags: [alibaba, qwen]
- name: grok-build
+2 -2
View File
@@ -1,6 +1,6 @@
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH am 1 "am 1.1.6"
.TH am 1 "am 1.1.9"
.SH NAME
am \- agent\-manager (am) — manage local AI coding agents
.SH SYNOPSIS
@@ -250,4 +250,4 @@ Export the configuration and installation state (backup)
am\-import(1)
Import a previously exported configuration and state
.SH VERSION
v1.1.6
v1.1.9
+67 -16
View File
@@ -14,9 +14,22 @@ use crate::secrets::SecretStore;
use anyhow::{bail, Result};
use std::io::Write;
/// Known providers offered by the wizard (from the embedded registry plus
/// the local Ollama runtime and a custom endpoint).
const CHOICES: &[&str] = &["anthropic", "openai", "deepseek", "google", "openrouter", "ollama", "custom"];
/// Providers offered by the wizard: every provider defined in the merged
/// registry (embedded + user config, sorted), plus the local Ollama runtime
/// and a custom endpoint. Built dynamically so new registry providers
/// (e.g. the QwenCloud token-plan / pay-as-you-go pair) show up without a
/// code change — CHOICES used to be a hardcoded list that silently drifted.
fn choices(app: &App) -> Vec<String> {
let mut v: Vec<String> = crate::providers::defined(&app.config)
.map(|(k, _)| k.clone())
.collect();
v.sort();
if !v.iter().any(|p| p == "ollama") {
v.push("ollama".to_string());
}
v.push("custom".to_string());
v
}
/// True when the user has not configured a usable default provider yet:
/// no `settings.default_provider`, or the default provider has no API
@@ -36,10 +49,14 @@ pub fn needs_setup(app: &App) -> bool {
.is_none()
}
fn read_line() -> Result<String> {
/// Read one trimmed line from stdin. `None` on EOF — callers must not spin
/// on empty input when nothing is connected (pipes, --yes in CI).
fn read_line() -> Result<Option<String>> {
let mut line = String::new();
std::io::stdin().read_line(&mut line)?;
Ok(line.trim().to_string())
if std::io::stdin().read_line(&mut line)? == 0 {
return Ok(None);
}
Ok(Some(line.trim().to_string()))
}
/// Ask a y/N question on stderr, defaulting to yes when `yes` is set.
@@ -50,7 +67,7 @@ fn ask(app: &App, question: &str, default_yes: bool) -> Result<bool> {
let suffix = if default_yes { "[Y/n]" } else { "[y/N]" };
eprint!("{question} {suffix} ");
std::io::stderr().flush()?;
let answer = read_line()?.to_lowercase();
let answer = read_line()?.unwrap_or_default().to_lowercase();
Ok(match answer.as_str() {
"" => default_yes,
"y" | "yes" | "o" | "oui" | "true" => true,
@@ -61,9 +78,10 @@ fn ask(app: &App, question: &str, default_yes: bool) -> Result<bool> {
/// Pick a provider from the list (or the current default when --yes).
fn pick_provider(app: &App) -> Result<(String, Option<String>)> {
let current = crate::providers::default_name(&app.config);
let choices = choices(app);
println!("Provider disponible :");
for (i, p) in CHOICES.iter().enumerate() {
let star = if Some(*p) == current { " (actuel)" } else { "" };
for (i, p) in choices.iter().enumerate() {
let star = if Some(p.as_str()) == current { " (actuel)" } else { "" };
println!(" {}) {}{star}", i + 1, p);
}
if app.cli.yes {
@@ -71,16 +89,18 @@ fn pick_provider(app: &App) -> Result<(String, Option<String>)> {
return Ok((name, None));
}
loop {
print!("Choisissez (1-{}) : ", CHOICES.len());
print!("Choisissez (1-{}) : ", choices.len());
std::io::stdout().flush()?;
let raw = read_line()?;
let Some(raw) = read_line()? else {
bail!("aucune entrée sur stdin — relancez en terminal interactif ou utilisez 'am setup --yes'");
};
if let Ok(n) = raw.parse::<usize>() {
if (1..=CHOICES.len()).contains(&n) {
let name = CHOICES[n - 1];
if (1..=choices.len()).contains(&n) {
let name = &choices[n - 1];
if name == "custom" {
print!("Base URL de l'API (ex: https://api.openai.com/v1) : ");
std::io::stdout().flush()?;
let url = read_line()?;
let url = read_line()?.unwrap_or_default();
if url.is_empty() {
continue;
}
@@ -126,7 +146,9 @@ fn pick_model(app: &App, name: &str) -> Result<String> {
loop {
print!("Modèle par défaut : ");
std::io::stdout().flush()?;
let raw = read_line()?;
let Some(raw) = read_line()? else {
bail!("aucune entrée sur stdin — relancez en terminal interactif ou utilisez 'am setup --yes'");
};
if let Ok(n) = raw.parse::<usize>() {
if n >= 1 && n <= models.len() {
return Ok(models[n - 1].clone());
@@ -134,7 +156,7 @@ fn pick_model(app: &App, name: &str) -> Result<String> {
if n == models.len() + 1 {
print!("Nom du modèle : ");
std::io::stdout().flush()?;
let custom = read_line()?;
let custom = read_line()?.unwrap_or_default();
if !custom.is_empty() {
return Ok(custom);
}
@@ -347,6 +369,35 @@ mod tests {
assert!(needs_setup(&app));
}
#[test]
fn wizard_offers_every_registry_provider() {
// Regression (v1.1.8): the wizard used a hardcoded CHOICES list and
// the QwenCloud providers added to the registry never appeared.
// The list must now track the merged registry + ollama + custom.
let app = app_with("");
let c = choices(&app);
for p in [
"anthropic",
"openai",
"deepseek",
"google",
"openrouter",
"qwen-cloud",
"qwen-cloud-token-plan",
"ollama",
"custom",
] {
assert!(c.iter().any(|x| x == p), "wizard must offer {p}: {c:?}");
}
// User-defined providers show up too, ollama is never duplicated.
let app2 = app_with(
" default_provider: anthropic\n providers:\n ollama:\n base_url: http://localhost:11434/v1\n my-llm:\n base_url: https://llm.internal/v1\n",
);
let c2 = choices(&app2);
assert_eq!(c2.iter().filter(|x| *x == "ollama").count(), 1, "{c2:?}");
assert!(c2.iter().any(|x| x == "my-llm"), "{c2:?}");
}
#[test]
fn needs_setup_false_for_local_ollama() {
let app = app_with(
+1 -1
View File
@@ -18,7 +18,7 @@ const WEB: &str = r#"# Généré par 'am init --template web' — projet {projec
# Répertoire : {dir}
version: "1.0"
groups:
dev: [claude-code, opencode, gemini-cli]
dev: [claude-code, opencode, antigravity-cli]
profiles:
dev:
agent: claude-code