From 50f515e406a64d9782d2cb66ec514605280b92c2 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 20 Aug 2026 12:45:10 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20installateur=20Windows=20=E2=80=94=20acc?= =?UTF-8?q?epter=20l'entree=20am-windows-x86=5F64.exe=20dans=20l'archive?= =?UTF-8?q?=20de=20release=20+=20i18n=20lang-aware=20pour=20am=20ai?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - install.ps1 ne trouvait que 'am.exe' dans le zip : les archives (CI et manuelles) utilisent le nom plateforme 'am-windows-x86_64.exe' -> repli systematique sur cargo install au lieu du binaire precompile - Le script accepte desormais les deux noms (copie vers am.exe) ; chemin du message cargo install corrige (.cargo\bin\am.exe au lieu de binam) - i18n : am ai utilisait tr()/tr_fmt() globaux (flaky selon le process) — passage a tr_in/tr_fmt_in(app.lang()) + helper tr_fmt_in ajoute a i18n.rs - Archive v1.0.6 : les deux entrees (am.exe + am-windows-x86_64.exe) pour la compatibilite maximale (installateurs et self-update) - Tests ai_test insensibles a la locale (FR/EN) - v1.0.6 ; ROADMAP maintenance note ajoutee --- Cargo.lock | 2 +- Cargo.toml | 2 +- ROADMAP.md | 15 ++++++++++++--- install.ps1 | 5 ++++- src/commands/ai_cmd.rs | 14 +++++++++----- src/i18n.rs | 9 +++++++++ src/shell_ai.rs | 28 +++++++++++++++++++--------- tests/ai_test.rs | 5 +++-- 8 files changed, 58 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9c23d6..770bd4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "agent-manager" -version = "1.0.5" +version = "1.0.6" dependencies = [ "anyhow", "base64", diff --git a/Cargo.toml b/Cargo.toml index e54e59a..f1bef57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-manager" -version = "1.0.5" +version = "1.0.6" edition = "2021" description = "Manage local AI coding agents: list, install, start, stop, update — with automatic dependency handling and a YAML-driven catalog." license = "MIT" diff --git a/ROADMAP.md b/ROADMAP.md index aeef807..2996c7e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,7 +1,7 @@ # 🗺️ ROADMAP agent-manager — vers le « super outil » -> **Version du document : 2.5** · Statut : phases 0 à 3 livrées -> (v0.3.0 → v1.0.1), maintenance v1.0.2 → v1.0.5 +> **Version du document : 2.6** · Statut : phases 0 à 3 livrées +> (v0.3.0 → v1.0.1), maintenance v1.0.2 → v1.0.6 > Base : analyse du code **v0.2.7** (Rust, 45+ tests, zéro dépendance runtime) > > ✅ **Phase 0 livrée en v0.3.0 (2026-08-17)** : issues #2 à #16 clôturées, @@ -47,6 +47,14 @@ > y/N pour les commandes risky, exécution via le shell de l'utilisateur), > flags --files/--provider/--model/--yes, alias CLI `am shell`, événements > `shell_ai` journalisés. Épique Axe 13 (#93) clôturée. +> +> 🔧 **Maintenance v1.0.6 (2026-08-20)** : installateur Windows corrigé — +> `install.ps1` n'acceptait que l'entrée `am.exe` dans l'archive de release +> (la CI et les archives manuelles utilisent `am-windows-x86_64.exe`) → +> repli systématique sur `cargo install` au lieu du binaire précompilé. +> Le script accepte désormais les deux noms ; archives v1.0.6 publiées avec +> les deux entrées pour la compatibilité maximale. Chemin du message +> cargo install corrigé (`.cargo\bin\am.exe`). --- @@ -628,4 +636,5 @@ configuration manuelle (tokens au keyring, provider/modèle par défaut). Phases livrées : 0 (v0.3.0), 1 (v0.4.1), 2 (v0.6.0), v0.7.0 (providers, 2026-08-19), 3 (v1.0.0, #76–#80, 2026-08-20) ; maintenance v1.0.1/v1.0.2 (self-update décompression, REPL), v1.0.3 (détection externe), v1.0.4 -(catalogue shell AI, #94 #95), v1.0.5 (commande am ai, #93 #96 #97).* +(catalogue shell AI, #94 #95), v1.0.5 (commande am ai, #93 #96 #97), +v1.0.6 (installateur Windows accepte l'entrée am-windows-x86_64.exe).* diff --git a/install.ps1 b/install.ps1 index 47d0498..8323f46 100644 --- a/install.ps1 +++ b/install.ps1 @@ -24,7 +24,7 @@ function Install-FromSource { Write-Host "Installation depuis les sources (cargo install --git $BaseUrl.git) ..." cargo install --git "$BaseUrl.git" --locked if ($LASTEXITCODE -ne 0) { Fail "cargo install a echoue" } - $bin = Join-Path $env:USERPROFILE ".cargo\binam.exe" + $bin = Join-Path $env:USERPROFILE ".cargo\bin\am.exe" Write-Host "" Write-Host "OK - am est installe dans $bin" Write-Host "Essayez : am list | am doctor (mise a jour : relancez ce script)" @@ -47,7 +47,10 @@ if ($arch -eq "x86_64") { $tmpDir = Join-Path $env:TEMP "am-$tag" Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue Expand-Archive -Path $tmp -DestinationPath $tmpDir -Force + # L'entrée de l'archive varie selon le pipeline : « am.exe » ou + # « am-windows-x86_64.exe » (nom de l'asset). Accepter les deux. $exe = Join-Path $tmpDir "am.exe" + if (-not (Test-Path $exe)) { $exe = Join-Path $tmpDir "am-windows-x86_64.exe" } if (Test-Path $exe) { New-Item -ItemType Directory -Force -Path $binDir | Out-Null Copy-Item $exe (Join-Path $binDir "am.exe") -Force diff --git a/src/commands/ai_cmd.rs b/src/commands/ai_cmd.rs index c570886..9ab18c3 100644 --- a/src/commands/ai_cmd.rs +++ b/src/commands/ai_cmd.rs @@ -129,22 +129,26 @@ fn exec_mode( ); let decision = crate::shell_ai::decide(mode, risk, dry_run, yes); + let lang = app.lang(); let mut executed = false; match decision { Decision::Abort => { let msg = if dry_run { - "dry-run : commande non exécutée" + crate::i18n::tr_in(lang, "dry-run : commande non exécutée") } else { - "politique de sécurité (dry-run par défaut) : commande non exécutée — utilisez --yes" + crate::i18n::tr_in( + lang, + "politique de sécurité (dry-run par défaut) : commande non exécutée — utilisez --yes", + ) }; - app.log.info(crate::i18n::tr(msg)); + app.log.info(msg); } Decision::Ask => { - let ok = app.confirm(crate::i18n::tr("Exécuter ?"))?; + let ok = app.confirm(crate::i18n::tr_in(lang, "Exécuter ?"))?; if ok { executed = true; } else { - app.log.info(crate::i18n::tr("annulé")); + app.log.info(crate::i18n::tr_in(lang, "annulé")); } } Decision::Execute => { diff --git a/src/i18n.rs b/src/i18n.rs index 29a9113..58f5b8e 100644 --- a/src/i18n.rs +++ b/src/i18n.rs @@ -436,6 +436,15 @@ pub fn tr_fmt<'a>(key: &'a str, args: &[&dyn std::fmt::Display]) -> String { } } +/// Format a message for a GIVEN locale (deterministic — follows the app's +/// resolved language instead of the process-global locale). +pub fn tr_fmt_in<'a>(lang: Lang, key: &'a str, args: &[&dyn std::fmt::Display]) -> String { + match lang { + Lang::Fr => fmt_args(key, args), + Lang::En => fmt_args(lookup_en(key).unwrap_or(key), args), + } +} + /// Replace `{}` placeholders (positional) with the Display of each argument. fn fmt_args(template: &str, args: &[&dyn std::fmt::Display]) -> String { let mut out = String::with_capacity(template.len() + 16); diff --git a/src/shell_ai.rs b/src/shell_ai.rs index 2058312..2bdf998 100644 --- a/src/shell_ai.rs +++ b/src/shell_ai.rs @@ -226,10 +226,10 @@ pub fn provider_env( } else { known.join(", ") }; - bail!(crate::tr_fmt!( + bail!(crate::i18n::tr_fmt_in( + app.lang(), "provider '{}' inconnu — providers enregistrés: {}", - provider.unwrap_or(""), - hint + &[&provider.unwrap_or(""), &hint] )); } return Ok((Vec::new(), BTreeMap::new())); @@ -307,16 +307,26 @@ pub fn generate( if !out.status.success() { let err = String::from_utf8_lossy(&out.stderr); let err = err.trim(); - bail!(crate::tr_fmt!( + bail!(crate::i18n::tr_fmt_in( + app.lang(), "aichat n'a pas généré de commande (exit {}){}", - out.status.code().unwrap_or(-1), - if err.is_empty() { String::new() } else { format!(": {err}") } + &[ + &out.status.code().unwrap_or(-1).to_string(), + &if err.is_empty() { + String::new() + } else { + format!(": {err}") + }, + ] )); } let stdout = String::from_utf8_lossy(&out.stdout); - extract_command(&stdout).ok_or_else(|| anyhow!(crate::i18n::tr( - "aichat n'a retourné aucune commande (dry-run)" - ))) + extract_command(&stdout).ok_or_else(|| { + anyhow!(crate::i18n::tr_in( + app.lang(), + "aichat n'a retourné aucune commande (dry-run)" + )) + }) } /// Execute a generated command through the user's shell (default_shell > diff --git a/tests/ai_test.rs b/tests/ai_test.rs index 4ba02a8..ca67767 100644 --- a/tests/ai_test.rs +++ b/tests/ai_test.rs @@ -92,7 +92,7 @@ fn ai_exec_dry_run_is_the_default_and_never_executes() { ); assert_eq!(code, 0); assert!( - log.contains("non exécutée"), + log.contains("non exécutée") || log.contains("not executed"), "dry-run policy must abort, log: {log}" ); assert!(!log.contains("exécution:"), "nothing must run, log: {log}"); @@ -148,7 +148,8 @@ fn ai_exec_respects_configured_confirm_mode() { ); assert_eq!(code, 0); assert!( - log.contains("annulé") || log.contains("cancelled") || log.contains("non exécutée"), + log.contains("annulé") || log.contains("cancelled") || log.contains("non exécutée") + || log.contains("not executed"), "declined confirmation must abort, log: {log}" ); assert!(!log.contains("exécution:"), "log: {log}");