- Commande am ai (alias CLI: am shell) : conversationnel par defaut (aichat -f <ctx> "<prompt>"), mode --exec avec generation aichat --dry-run - Securite (issue #97) : classification safe/risky + certainite affichee, politique dry-run par defaut (settings.shell_ai.default_safety: dry-run | confirm | auto), confirmation y/N pour les commandes risky, --yes pour executer, --dry-run force la simulation - Flags : --exec/-e, --files/-f (defaut: dossier courant), --provider, --model (registre providers -> env aichat + modele, token via keyring) - Execution via le shell utilisateur (default_shell > SHELL > COMSPEC) - Journalisation : EventKind::ShellAi (mode, risk, certainty, executed) - Module src/shell_ai.rs (classification, politique, generation, execution) - Câblage complet : cli, dispatch, help, REPL (parseur+completer+banner), tip, man pages (am-ai.1), i18n EN, config.yaml (bloc shell_ai) - Tests : 7 unitaires (classification, certainty, extraction, decision) + 4 integration (shim aichat.cmd : dry-run par defaut, --yes, mode conversationnel, mode confirm) — 339 tests verts - v1.0.5 ; ROADMAP axe 13.3 coche (6 lignes), epic #93 clôturee closes #93 closes #96 closes #97
This commit is contained in:
+56
-2
@@ -114,6 +114,7 @@ const COMMAND_DESCRIPTIONS: &[(&str, &str)] = &[
|
||||
("providers", "manage the LLM provider registry (base URLs, models, default)"),
|
||||
("suggest", "recommend an agent for a request"),
|
||||
("ask", "natural language → am commands (local rules first, optional LLM refinement)"),
|
||||
("ai", "natural language → shell action via AIChat (--exec to run, dry-run by default)"),
|
||||
("audit", "who changed what, when (config checksums)"),
|
||||
("service", "register an agent as a system service (autostart)"),
|
||||
("schedule", "plan am commands (daily) and check the fleet health"),
|
||||
@@ -239,7 +240,7 @@ impl AmCompleter {
|
||||
"start", "stop", "restart", "run", "doctor", "config", "completion",
|
||||
"self-update", "self-uninstall", "export", "import", "shell", "theme", "lang",
|
||||
"tip", "dashboard", "favorite", "unfavorite", "note", "tag", "untag", "tags",
|
||||
"profile", "man", "models", "catalog", "providers", "suggest", "ask", "registry", "audit",
|
||||
"profile", "man", "models", "catalog", "providers", "suggest", "ask", "ai", "registry", "audit",
|
||||
"service", "schedule", "monitor", "web", "serve", "sync", "migrate", "playbook", "lab", "plugins",
|
||||
"ls", "dir", "cd", "ps", "where", "get", "help", "version", "exit",
|
||||
],
|
||||
@@ -799,7 +800,7 @@ pub fn banner_box(
|
||||
.to_string(),
|
||||
));
|
||||
rows.push(inner(
|
||||
" models models · models --prune · catalog · suggest · ask · audit".to_string(),
|
||||
" models models · models --prune · catalog · suggest · ask · ai · audit".to_string(),
|
||||
));
|
||||
rows.push(inner(
|
||||
" automate service install · schedule add · doctor --watch · monitor · sync · migrate · playbook".to_string(),
|
||||
@@ -1603,6 +1604,58 @@ fn handle_line(
|
||||
yes: flag("--yes"),
|
||||
}
|
||||
},
|
||||
"ai" => {
|
||||
let mut exec = false;
|
||||
let mut yes = false;
|
||||
let mut dry_run = false;
|
||||
let mut files: Vec<std::path::PathBuf> = Vec::new();
|
||||
let mut provider: Option<String> = None;
|
||||
let mut model: Option<String> = None;
|
||||
let mut prompt: Vec<String> = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < rest.len() {
|
||||
match rest[i].as_str() {
|
||||
"--exec" | "-e" => exec = true,
|
||||
"--yes" | "-y" => yes = true,
|
||||
"--dry-run" => dry_run = true,
|
||||
"--files" | "-f" => {
|
||||
i += 1;
|
||||
if i < rest.len() {
|
||||
files.push(std::path::PathBuf::from(&rest[i]));
|
||||
}
|
||||
}
|
||||
"--provider" => {
|
||||
i += 1;
|
||||
if i < rest.len() {
|
||||
provider = Some(rest[i].clone());
|
||||
}
|
||||
}
|
||||
"--model" => {
|
||||
i += 1;
|
||||
if i < rest.len() {
|
||||
model = Some(rest[i].clone());
|
||||
}
|
||||
}
|
||||
other => prompt.push(other.to_string()),
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if prompt.is_empty() {
|
||||
app.log.error(crate::i18n::tr(
|
||||
"usage: ai <demande> — ex: ai --exec \"compresse les fichiers JSON\"",
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
Command::Ai(crate::cli::AiArgs {
|
||||
prompt,
|
||||
exec,
|
||||
files,
|
||||
provider,
|
||||
model,
|
||||
yes,
|
||||
dry_run,
|
||||
})
|
||||
},
|
||||
"alias" => match rest.first().map(|s| s.as_str()) {
|
||||
Some("add") if rest.len() >= 3 => Command::Alias(crate::cli::AliasCmd::Add {
|
||||
name: rest[1].clone(),
|
||||
@@ -2019,6 +2072,7 @@ pub fn is_am_command(word: &str) -> bool {
|
||||
| "providers"
|
||||
| "suggest"
|
||||
| "ask"
|
||||
| "ai"
|
||||
| "audit"
|
||||
| "service"
|
||||
| "schedule"
|
||||
|
||||
Reference in New Issue
Block a user