Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7f29baea5 | ||
|
|
cf53c98d98 | ||
|
|
991c79de50 |
+2
-2
@@ -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
@@ -21,7 +21,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-manager"
|
||||
version = "1.1.6"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user