goose-config — AgentTeamsModule¶
Locate and safely mutate Goose's config.yaml for source/model switching.
Source:
agentteams/goose_config.py
Purpose¶
Powers the agentteams --goose-source / --goose-model / --goose-show CLI action. The module
has three concerns:
- Location protocol — resolve which
config.yamlGoose actually reads, and report how it was found so the choice is never silent. - Source registry — a per-source default model + provider-key env-var name (never a value),
seeded for
ollama+openrouterand extensible via~/.config/agentteams/goose-sources.json. - Config mutation — set only the top-level
GOOSE_PROVIDER/GOOSE_MODELscalars, with a timestamped backup written before the rewrite, and never touch the nestedextensions:block.
config.yaml is the persistent default Goose reads when no env override is set. An active
GOOSE_PROVIDER / GOOSE_MODEL env (e.g. a goose-or shell) wins over it — callers must surface
that via current_status / env_override.
No secrets. This module never reads or writes provider API keys. The source registry stores only the name of the env var that holds a key (
key_env), never the key itself.
Public Types¶
SourceSpec¶
A Goose source (provider) with its default model and provider-key env-var name.
Frozen dataclass fields:
default_model(str): the model slug applied when switching to this source.key_env(str | None): the name of the env var that holds the provider key — never a value (defaultNone).host_env(str | None): the name of the env var for the provider host/endpoint (defaultNone).
BUILTIN_SOURCES¶
dict[str, SourceSpec] — the seeded source registry. Ships with ollama and openrouter; merged
with (and overridable by) a user file in load_sources.
NewSchemaTargetError¶
ValueError subclass raised by
set_provider_model when the target
config.yaml already uses the newer providers:/active_provider: schema. See that
function's Raises section.
Public Functions¶
load_sources(user_file=None)¶
Built-in sources merged with an optional user JSON file (user wins per key).
Args:
user_file(Path | None): override path; defaults to~/.config/agentteams/goose-sources.json.
File shape: {"sources": {"<name>": {"default_model": "...", "key_env": "...", "host_env": "..."}}}.
Unreadable or invalid files are ignored and the built-ins are kept.
Returns:
dict[str, SourceSpec]
resolve_goose_config_path(explicit=None, env=None, platform=None, runner=subprocess.run)¶
Resolve Goose's config.yaml path and report which method found it.
Resolution order:
- explicit flag /
AGENTTEAMS_GOOSE_CONFIGenv → method"explicit". goose info(authoritative — Goose's own resolver) → method"goose-info".$XDG_CONFIG_HOME/ platform default → method"xdg"or"platform-default".
Args:
explicit(str | None): explicit path override.env(dict[str, str] | None): environment mapping (defaults toos.environ).platform(str | None): platform string (defaults tosys.platform).runner:subprocess.run-compatible callable, injectable for tests.
Returns:
tuple[Path, str]— the resolved path and the resolution method.
parse_goose_info_config_path(text)¶
Extract the config.yaml path from goose info stdout.
Tolerant of fixed-column trailing padding and a trailing ... missing (can create) status token;
does not split on internal whitespace (a Windows path may contain spaces).
Args:
text(str): capturedgoose infostdout.
Returns:
str | None— the parsed path, orNonewhen noconfig yaml:line is present.
read_config(path)¶
Parse top-level GOOSE_*: value scalars; ignore the nested extensions: block. Also parses
the newer providers:/active_provider: schema (2026-07-24) that recent goose configure
(1.37+) writes instead of flat keys.
Args:
path(Path): path to theconfig.yamlfile.
Returns:
tuple[dict[str, str], dict[str, object] | None]— the top-levelGOOSE_*scalars (empty when the file is absent, unreadable, or uses only the newer schema), and the parsedproviders:block ({"active_provider", "model", "models_by_provider"}) orNonewhen the file uses the older flat-key schema.
set_provider_model(path, provider=None, model=None)¶
Set top-level GOOSE_PROVIDER / GOOSE_MODEL, preserving everything else.
Writes a timestamped backup before the rewrite (no partial-write window). Creates a minimal
config if the file is absent. Anchors on column 0, so the nested extensions: keys are never
touched. Never reads or writes provider keys.
Write support is flat-schema only. Against a target already using the newer
providers:/active_provider: schema, this refuses instead of writing a flat
GOOSE_PROVIDER:/GOOSE_MODEL: pair goose's new-schema reader would never consult — no write,
no backup, file untouched. The exception message names the exact indented model: line to
edit by hand.
Args:
path(Path): path to theconfig.yamlfile.provider(str | None): newGOOSE_PROVIDERvalue (optional).model(str | None): newGOOSE_MODELvalue (optional).
Returns:
str | None— the backup path, orNonewhen the file was newly created.
Raises:
ValueErrorwhen neitherprovidernormodelis supplied.NewSchemaTargetError(aValueErrorsubclass) whenpathalready uses the newerproviders:/active_provider:schema — callers that need to distinguish this from the missing-args case (the CLI prints a different message for each) should catch it first.
model_provider_mismatch(provider, model)¶
Return a human-readable reason when a model slug is namespace-incompatible with the provider.
ollama uses name:tag (no /); OpenRouter uses vendor/slug (hyphens, : only for real
variants). Catches the common trap of pasting an Ollama :tag into an OpenRouter slug.
Args:
provider(str): the source/provider name.model(str): the candidate model slug.
Returns:
str | None— a reason string, orNonewhen the slug is compatible.
env_override(env=None)¶
Return any active GOOSE_PROVIDER / GOOSE_MODEL env override (masks config.yaml).
Args:
env(dict[str, str] | None): environment mapping (defaults toos.environ).
Returns:
dict[str, str]— the subset ofGOOSE_PROVIDER/GOOSE_MODELcurrently set in the environment.
current_status(path, env=None)¶
Snapshot the config.yaml provider/model and any masking env override.
Prefers the newer providers:/active_provider: schema when read_config finds one; falls
back to the flat GOOSE_PROVIDER/GOOSE_MODEL keys otherwise.
Args:
path(Path): path to theconfig.yamlfile.env(dict[str, str] | None): environment mapping (defaults toos.environ).
Returns:
dict[str, object]with keysconfig_provider,config_model,config_mode,env_override, andschema_source("v2"|"v1"|"none"— which shapeconfig_provider/config_modelwere resolved from).
Notes¶
- Location — prefers
goose infoover a guessed platform path; seeresolve_goose_config_path. - Mutation — backup-before-write, column-0 anchored (the
extensions:block is preserved); seeset_provider_model. - Env override — always wins over
config.yaml; see the Purpose note above andcurrent_status. - Schemas — reads both flat-key and
providers:/active_provider:; writes only flat-key; seeread_config/set_provider_model.
See also¶
mcp_emit— wiresmcp_servers[]into the Goose recipeextensions:block this module deliberately leaves untouched.bridge_subagents_goose— emits Goose subagent-stub recipes for a bridged source team.