feat: am setup — provider openrouter + modeles proposes ; am ai — role am-copilot par defaut ; fix migrate import (config active)
- setup: openrouter ajoute aux CHOICES (base URL, OPENROUTER_API_KEY, 6 modeles declares dans le registre embarqué, proposes au choix) - setup: pick_model liste les modeles disponibles (registre) + autre modele - am ai sans --role: am-copilot par defaut (regeneration auto des roles si fichier manquant, degradation douce si echec; role explicite inconnu = erreur) - fix migrate --import: import_bundle ecrivait la config dans user_config_dir() GLOBAL -> ecrasait la config reelle de la machine (et cargo test detruisait la config utilisateur a chaque suite via export_import_round_trip). L'import ecrit desormais dans app.paths.config_dir (config active). v1.1.2
This commit is contained in:
Generated
+1
-1
@@ -21,7 +21,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-manager"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "agent-manager"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
edition = "2021"
|
||||
description = "Manage local AI coding agents: list, install, start, stop, update — with automatic dependency handling and a YAML-driven catalog."
|
||||
license = "MIT"
|
||||
|
||||
+12
@@ -70,6 +70,18 @@
|
||||
> `settings.providers.<nom>.base_url` + `.default_model` (le schéma exige
|
||||
> base_url quand un bloc provider est écrit). Test de régression
|
||||
> `setup_persists_under_settings_providers`.
|
||||
>
|
||||
> 🔧 **Maintenance v1.1.2 (2026-08-20)** :
|
||||
> - `am setup` : **openrouter** ajouté au choix des providers (base URL,
|
||||
> token via OPENROUTER_API_KEY, 6 modèles déclarés proposés) ;
|
||||
> le choix du modèle liste les modèles disponibles du provider.
|
||||
> - `am ai` sans `--role` utilise **am-copilot par défaut** (régénération
|
||||
> automatique des rôles si le fichier manque, dégradation douce).
|
||||
> - Bug `am migrate --import` : la config importée était écrite dans le
|
||||
> répertoire config GLOBAL (user_config_dir) au lieu du config actif de
|
||||
> l'app — l'import écrasait la config réelle de la machine (et la suite
|
||||
> de tests détruisait la config utilisateur à chaque `cargo test`).
|
||||
> L'import respecte désormais `app.paths.config_dir` (--config/isolated).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+10
@@ -108,6 +108,16 @@ settings:
|
||||
base_url: https://generativelanguage.googleapis.com/v1beta
|
||||
default_model: gemini-2.5-pro
|
||||
models: [gemini-2.5-pro, gemini-2.5-flash]
|
||||
openrouter:
|
||||
base_url: https://openrouter.ai/api/v1
|
||||
default_model: deepseek/deepseek-chat-v3-0324
|
||||
models:
|
||||
- deepseek/deepseek-chat-v3-0324
|
||||
- anthropic/claude-sonnet-4.5
|
||||
- openai/gpt-4o
|
||||
- google/gemini-2.5-pro
|
||||
- meta-llama/llama-3.3-70b-instruct
|
||||
- mistralai/mistral-large-2411
|
||||
|
||||
# --- Command aliases ----------------------------------------------------------
|
||||
aliases:
|
||||
|
||||
+24
-9
@@ -75,21 +75,36 @@ fn aichat_installed(app: &App) -> bool {
|
||||
|| which::which("aichat").is_ok()
|
||||
}
|
||||
|
||||
/// Validate the --role flag: the file must exist among the generated
|
||||
/// am-* roles (v1.1.0 F3). Returns the aichat --agent arguments.
|
||||
/// Build the aichat `--agent` arguments for the requested role. Without a
|
||||
/// `--role` flag the am-copilot role is used by default (v1.1.2): when its
|
||||
/// file is missing the roles are regenerated on the fly, and a generation
|
||||
/// failure degrades gracefully (no role). An explicit unknown role is an
|
||||
/// error.
|
||||
fn role_args(app: &App, role: Option<&str>) -> Result<Vec<String>> {
|
||||
let Some(role) = role else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let role = role.unwrap_or("am-copilot");
|
||||
let dir = crate::roles::aichat_agents_dir();
|
||||
let path = dir.join(format!("{role}.md"));
|
||||
if !path.exists() {
|
||||
if path.exists() {
|
||||
return Ok(vec!["--agent".to_string(), role.to_string()]);
|
||||
}
|
||||
if role == "am-copilot" {
|
||||
if let Err(e) = crate::roles::generate_all(app) {
|
||||
app.log.verbose(&format!("roles generation skipped: {e:#}"));
|
||||
}
|
||||
if path.exists() {
|
||||
return Ok(vec!["--agent".to_string(), role.to_string()]);
|
||||
}
|
||||
app.log.warn(crate::i18n::tr_in(
|
||||
app.lang(),
|
||||
"rôle 'am-copilot' introuvable — lancez am setup --roles",
|
||||
));
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
bail!(crate::i18n::tr_in(
|
||||
app.lang(),
|
||||
"rôle '{}' inconnu — générez les rôles avec: am setup --roles (am-copilot, am-operator, am-dev, am-do, am-analyst, am-orchestrator)"
|
||||
).replace("{}", role));
|
||||
}
|
||||
Ok(vec!["--agent".to_string(), role.to_string()])
|
||||
)
|
||||
.replace("{}", role));
|
||||
}
|
||||
|
||||
/// Conversational mode: aichat -f <ctx> "<prompt>" in the foreground, with
|
||||
|
||||
@@ -266,9 +266,14 @@ pub fn import_bundle(app: &App, bundle: &Path, confirm: bool) -> Result<Vec<Stri
|
||||
for (bf, data) in manifest.files.iter().zip(blobs.iter()) {
|
||||
let rel = Path::new(&bf.path);
|
||||
let target = if bf.path == "config.yaml" {
|
||||
// Config goes to the user config directory of machine B.
|
||||
let dir = crate::config::user_config_dir()
|
||||
.unwrap_or_else(|| crate::config::home_dir().unwrap_or_else(|| PathBuf::from(".")));
|
||||
// Config goes to the ACTIVE user config directory of this app
|
||||
// (respects --config / isolated tests) — NOT the machine-global
|
||||
// user config dir: importing must not clobber the real config.
|
||||
let dir = app
|
||||
.paths
|
||||
.config_dir
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("cannot determine the user config directory (no HOME set)"))?;
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
dir.join(crate::config::CONFIG_FILE_NAME)
|
||||
} else {
|
||||
@@ -292,8 +297,11 @@ pub fn import_bundle(app: &App, bundle: &Path, confirm: bool) -> Result<Vec<Stri
|
||||
// confirmed; otherwise it is written next to it as config.migrated.yaml.
|
||||
let cfg_rel = "config.yaml";
|
||||
if restored.iter().any(|r| r == cfg_rel) && !confirm {
|
||||
let dir = crate::config::user_config_dir()
|
||||
.unwrap_or_else(|| crate::config::home_dir().unwrap_or_else(|| PathBuf::from(".")));
|
||||
let dir = app
|
||||
.paths
|
||||
.config_dir
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("cannot determine the user config directory (no HOME set)"))?;
|
||||
let imported = dir.join(crate::config::CONFIG_FILE_NAME);
|
||||
if imported.exists() {
|
||||
let target = dir.join("config.migrated.yaml");
|
||||
|
||||
@@ -180,6 +180,7 @@ pub const CATALOG: &[(&str, &str)] = &[
|
||||
("Config aichat créée (provider + modèle)", "aichat config created (provider + model)"),
|
||||
("am est configuré — essayez: am ai \"...\" ou am ai --exec \"...\"", "am is configured — try: am ai \"...\" or am ai --exec \"...\""),
|
||||
("rôle '{}' inconnu — générez les rôles avec: am setup --roles (am-copilot, am-operator, am-dev, am-do, am-analyst, am-orchestrator)", "unknown role '{}' — generate the roles with: am setup --roles (am-copilot, am-operator, am-dev, am-do, am-analyst, am-orchestrator)"),
|
||||
("rôle 'am-copilot' introuvable — lancez am setup --roles", "role 'am-copilot' not found — run am setup --roles"),
|
||||
("usage: ask <demande> — ex: ask installe claude et lance-le", "usage: ask <request> — e.g. ask installe claude et lance-le"),
|
||||
("aucune source enregistrée — settings.registry.sources ou: am registry install <url>", "no sources registered — settings.registry.sources or: am registry install <url>"),
|
||||
("installation refusée — source non fiable", "installation refused — untrusted source"),
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ 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", "ollama", "custom"];
|
||||
const CHOICES: &[&str] = &["anthropic", "openai", "deepseek", "google", "openrouter", "ollama", "custom"];
|
||||
|
||||
/// True when the user has not configured a usable default provider yet:
|
||||
/// no `settings.default_provider`, or the default provider has no API
|
||||
|
||||
Reference in New Issue
Block a user