feat: v0.7.0 — AgentDef provider/model + install --provider/--model/--no-config : résolution flag > agent > défaut, erreur si provider inconnu (liste du registre), warning si modèle absent, résolution affichée en résumé et au --dry-run (closes #90)

- Providers réels livrés dans le catalogue par défaut (anthropic★/openai/deepseek/google) + hints provider sur claude-code/deepseek-harness
- Suppression par null dans l'overlay : am providers remove écrit <nom>: null (merge traite null comme suppression, set_path remplace le null avant de descendre)
- Cascade @secret (#89) : le provider déclaré de l'agent prime sur default_provider
This commit is contained in:
2026-08-19 20:03:47 -04:00
parent 7312e6ceab
commit ebc753cc4a
14 changed files with 336 additions and 38 deletions
+32 -4
View File
@@ -148,7 +148,7 @@ pub struct Settings {
/// they live in the OS keyring under `providers/<name>/api_key`
/// (issue #89).
#[serde(default)]
pub providers: Option<BTreeMap<String, ProviderDef>>,
pub providers: Option<BTreeMap<String, Option<ProviderDef>>>,
}
/// One entry of the LLM provider registry (issue #88).
@@ -275,7 +275,10 @@ impl Settings {
/// Look up one provider of the registry (issue #88).
pub fn provider(&self, name: &str) -> Option<&ProviderDef> {
self.providers.as_ref().and_then(|m| m.get(name))
self.providers
.as_ref()
.and_then(|m| m.get(name))
.and_then(|p| p.as_ref())
}
/// Name of the default provider (issue #88).
@@ -331,6 +334,14 @@ pub struct AgentDef {
/// CLI flag carrying the model name at launch (issue #71, e.g. "--model").
#[serde(default)]
pub model_arg: Option<String>,
/// Preferred provider of this agent (issue #90, e.g. "anthropic" for
/// claude-code). Falls back to settings.default_provider when unset.
#[serde(default)]
pub provider: Option<String>,
/// Preferred model for this agent (issue #90). Falls back to the
/// provider's default_model when unset.
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
/// When false, the agent is listed but cannot be installed locally.
@@ -588,6 +599,10 @@ pub struct InstallSpec {
/// Commands executed after a successful install (PATH includes the bin dir).
#[serde(default)]
pub post_install: Vec<String>,
/// The agent accepts a post-install provider configuration (issue #91).
/// False for agents without any API (issue #90).
#[serde(default = "default_true")]
pub configurable: bool,
}
impl InstallSpec {
@@ -815,12 +830,21 @@ pub fn merge(base: &mut Config, overlay: Config) {
s.default_provider = o.default_provider;
}
// Providers merge key by key (issue #88): an overlay adds or replaces
// one provider without wiping the others.
// one provider without wiping the others. An explicit `null` in the
// overlay DELETES the provider (the standard YAML overlay pattern) so a
// provider shipped in the default catalog can be removed by the user.
if let Some(overlay_providers) = o.providers {
match s.providers.as_mut() {
Some(map) => {
for (k, v) in overlay_providers {
map.insert(k, v);
match v {
Some(def) => {
map.insert(k, Some(def));
}
None => {
map.remove(&k);
}
}
}
}
None => s.providers = Some(overlay_providers),
@@ -900,6 +924,10 @@ pub fn validate(config: &Config) -> Vec<String> {
}
if let Some(providers) = &config.settings.providers {
for (name, p) in providers {
// A null entry is a deletion marker (issue #90): skip it.
let Some(p) = p else {
continue;
};
if p.base_url.trim().is_empty() {
problems.push(format!("provider '{name}': 'base_url' is required"));
}