Skip to content

Commit 0ff24d3

Browse files
dcramercodex
andcommitted
config: move gog preset wiring under skills.gog
Remove bundles.gog and derive default gog capability provider wiring from [skills.gog] enabled=true. Add [skills.gog.capability_provider] for provider command/namespace/timeout overrides and exclude it from skill env injection. Update docs/specs and tests to reflect the new contract. Co-Authored-By: GPT-5 Codex <codex@openai.com>
1 parent ef96ee6 commit 0ff24d3

12 files changed

Lines changed: 105 additions & 116 deletions

File tree

docs/src/content/docs/configuration/reference.mdx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -265,23 +265,21 @@ enabled = true
265265
model = "mini"
266266
allow_chat_ids = ["12345"] # Optional per-skill override
267267
PERPLEXITY_API_KEY = "..."
268-
```
269-
270-
## Bundles (`[bundles.<name>]`)
271268

272-
Use bundle toggles for packaged skill + provider presets.
269+
[skills.gog]
270+
enabled = true
273271

274-
```toml
275-
[bundles.gog]
272+
[skills.gog.capability_provider]
276273
enabled = true
274+
namespace = "gog"
275+
command = ["gogcli", "bridge"]
276+
timeout_seconds = 30
277277
```
278278

279-
When enabled, Ash applies defaults for the bundled `gog` package:
280-
281-
- `skills.gog.enabled = true`
282-
- `capabilities.providers.gog = { enabled = true, namespace = "gog", command = ["gogcli", "bridge"], timeout_seconds = 30 }`
283-
284-
Explicit `[skills.gog]` and `[capabilities.providers.gog]` values override those defaults.
279+
When `skills.gog.enabled = true`, Ash applies default provider wiring for
280+
`capabilities.providers.gog` (namespace `gog`, command `["gogcli", "bridge"]`,
281+
timeout `30`) unless overridden explicitly. Use `skills.gog.capability_provider`
282+
to keep gog package settings under `[skills.gog]`.
285283

286284
## Capabilities (`[capabilities.providers.<name>]`)
287285

docs/src/content/docs/systems/skills.mdx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ allowed_tools:
227227
Provider runtime wiring is host-owned configuration, not skill metadata:
228228

229229
```toml
230-
[capabilities.providers.gog]
230+
[skills.gog]
231+
enabled = true
232+
233+
[skills.gog.capability_provider]
231234
enabled = true
232235
namespace = "gog"
233236
command = ["gogcli", "bridge"]
@@ -240,25 +243,19 @@ Ash includes an opt-in bundled `gog` skill for dogfooding this contract.
240243

241244
Enable it in `config.toml`:
242245

243-
```toml
244-
[bundles.gog]
245-
enabled = true
246-
```
247-
248-
This preset enables both the bundled `gog` skill and default
249-
`gogcli bridge` provider wiring. You can still override either section directly:
250-
251-
```toml
252246
[skills.gog]
253247
enabled = true
254248

255-
[capabilities.providers.gog]
249+
[skills.gog.capability_provider]
256250
enabled = true
257251
namespace = "gog"
258252
command = ["gogcli", "bridge"]
259253
timeout_seconds = 30
260254
```
261255
256+
When `skills.gog.enabled = true`, Ash auto-wires default
257+
`capabilities.providers.gog` settings unless overridden explicitly.
258+
262259
## Troubleshooting
263260
264261
### Skill does not appear

specs/capabilities.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,19 @@ Capability providers are configured as external bridge commands so integrations
115115
like `gog` can live in a skill/external repo instead of Ash core code:
116116

117117
```toml
118-
[bundles.gog]
118+
[skills.gog]
119119
enabled = true
120120

121-
[capabilities.providers.gog]
121+
[skills.gog.capability_provider]
122122
enabled = true
123123
namespace = "gog"
124124
command = ["gogcli", "bridge"]
125125
timeout_seconds = 30
126126
```
127127

128-
Bundle preset semantics:
129-
- `bundles.gog.enabled = true` applies default `skills.gog` + `capabilities.providers.gog` wiring.
130-
- Explicit provider config values override preset defaults.
128+
`skills.gog.enabled = true` applies default `capabilities.providers.gog` wiring.
129+
Optional `skills.gog.capability_provider` values override command/namespace/timeout.
130+
Explicit `[capabilities.providers.gog]` remains available for host-level overrides.
131131

132132
The host invokes the bridge with JSON over stdin/stdout for `definitions`,
133133
`auth_begin`, `auth_complete`, and `invoke`.

specs/config.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Files: src/ash/config/loader.py, src/ash/config/models.py, src/ash/config/paths.
1616
- Support named model configurations (`[models.<alias>]`)
1717
- Require a `default` model alias
1818
- Support per-skill model overrides (`[skills.<name>] model = "<alias>"`)
19-
- Support bundled integration presets (`[bundles.<name>]`)
19+
- Support packaged skill/provider wiring under skill config (`[skills.gog]`)
2020
- Generate config template programmatically (no static file)
2121

2222
### SHOULD
@@ -38,7 +38,6 @@ Files: src/ash/config/loader.py, src/ash/config/models.py, src/ash/config/paths.
3838
class AshConfig(BaseModel):
3939
models: dict[str, ModelConfig] # Named model configurations
4040
skills: dict[str, dict[str, str]] # Per-skill config (model overrides)
41-
bundles: dict[str, dict[str, bool]] # Bundled integration presets
4241
sandbox: SandboxConfig
4342
memory: MemoryConfig
4443
server: ServerConfig
@@ -87,9 +86,15 @@ allow_chat_ids = ["12345"]
8786
[skills.defaults]
8887
allow_chat_ids = ["12345"]
8988

90-
[bundles.gog]
89+
[skills.gog]
9190
enabled = true
9291

92+
[skills.gog.capability_provider]
93+
enabled = true
94+
namespace = "gog"
95+
command = ["gogcli", "bridge"]
96+
timeout_seconds = 30
97+
9398
[skills.code-review]
9499
model = "sonnet"
95100

@@ -106,12 +111,6 @@ allowed_users = ["123456789"]
106111
[brave_search]
107112
api_key = "..." # or BRAVE_SEARCH_API_KEY env
108113

109-
[capabilities.providers.gog]
110-
enabled = true
111-
namespace = "gog"
112-
command = ["gogcli", "bridge"]
113-
timeout_seconds = 30
114-
115114
[embeddings]
116115
provider = "openai"
117116
model = "text-embedding-3-small"
@@ -132,11 +131,10 @@ Skill chat allowlist resolution:
132131
1. `[skills.<name>].allow_chat_ids` (per-skill override, when set)
133132
2. `[skills.defaults].allow_chat_ids` (global default)
134133

135-
Bundled `gog` preset behavior:
136-
1. `[bundles.gog].enabled = true` applies defaults:
137-
- `skills.gog.enabled = true`
138-
- `capabilities.providers.gog = { enabled=true, namespace="gog", command=["gogcli","bridge"], timeout_seconds=30 }`
139-
2. Explicit `[skills.gog]` and `[capabilities.providers.gog]` values take precedence over preset defaults.
134+
Bundled `gog` behavior:
135+
1. `[skills.gog].enabled = true` applies default `capabilities.providers.gog` wiring.
136+
2. `[skills.gog.capability_provider]` can override provider settings from the skill section.
137+
3. Explicit `[capabilities.providers.gog]` remains available for host-level overrides.
140138

141139
For API keys:
142140
1. Provider config (`[anthropic].api_key`)

specs/skills.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ Capability IDs must be namespaced (for example `gog.email`, not `email`).
133133
Provider execution details are host-owned config, not skill metadata:
134134

135135
```toml
136-
[capabilities.providers.gog]
136+
[skills.gog]
137+
enabled = true
138+
139+
[skills.gog.capability_provider]
137140
enabled = true
138141
namespace = "gog"
139142
command = ["gogcli", "bridge"]
@@ -158,8 +161,14 @@ allow_chat_ids = ["12345"] # Optional per-skill chat allowlist override
158161
[skills.defaults]
159162
allow_chat_ids = ["12345"] # Optional global default allowlist for all skills
160163

161-
[bundles.gog]
162-
enabled = true # Optional preset: enables bundled gog + default provider wiring
164+
[skills.gog]
165+
enabled = true # Enables bundled gog skill and provider auto-wiring
166+
167+
[skills.gog.capability_provider]
168+
enabled = true
169+
namespace = "gog"
170+
command = ["gogcli", "bridge"]
171+
timeout_seconds = 30
163172

164173
[skills.code-review]
165174
enabled = false # Disabled
@@ -168,9 +177,9 @@ enabled = false # Disabled
168177
Config keys match env var names exactly (UPPER_CASE). No case conversion.
169178
`allow_chat_ids` can be set globally in `[skills.defaults]` and overridden per skill.
170179

171-
`[bundles.gog].enabled = true` applies default `gog` wiring:
172-
- `skills.gog.enabled = true`
173-
- `capabilities.providers.gog = { enabled=true, namespace="gog", command=["gogcli","bridge"], timeout_seconds=30 }`
180+
`[skills.gog].enabled = true` applies default `gog` provider wiring.
181+
`[skills.gog.capability_provider]` can override provider command/namespace/timeout
182+
from the same skill section.
174183

175184
Explicit `[skills.gog]` / `[capabilities.providers.gog]` values override preset defaults.
176185

src/ash/cli/context.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,19 @@ def generate_config_template() -> str:
117117
# =============================================================================
118118
# Optional: External Capability Provider (skill-owned)
119119
# =============================================================================
120-
# One-switch preset for bundled gog package:
120+
# Configure bundled gog skill + provider wiring in one place:
121121
#
122-
# [bundles.gog]
122+
# [skills.gog]
123123
# enabled = true
124124
#
125-
# This auto-enables:
126-
# - [skills.gog] enabled = true
127-
# - [capabilities.providers.gog] with command ["gogcli", "bridge"]
128-
#
129-
# Registers an external capability bridge command (for example from a skill
130-
# package/repo) that implements the capability provider contract.
131-
132-
# [capabilities.providers.gog]
125+
# [skills.gog.capability_provider]
133126
# enabled = true
134127
# namespace = "gog"
135128
# command = ["gogcli", "bridge"]
136129
# timeout_seconds = 30
130+
#
131+
# Registers an external capability bridge command (for example from a skill
132+
# package/repo) that implements the capability provider contract.
137133
138134
# =============================================================================
139135
# Optional: Semantic Search

src/ash/config/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from ash.config.models import (
55
AshConfig,
66
BraveSearchConfig,
7-
BundleConfig,
8-
BundlesConfig,
97
CapabilitiesConfig,
108
CapabilityProviderConfig,
119
ConfigError,
@@ -29,8 +27,6 @@
2927

3028
__all__ = [
3129
"AshConfig",
32-
"BundleConfig",
33-
"BundlesConfig",
3430
"BraveSearchConfig",
3531
"CapabilitiesConfig",
3632
"CapabilityProviderConfig",

src/ash/config/models.py

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ class SkillConfig(BaseModel):
387387
model: str | None = None
388388
enabled: bool = True
389389
allow_chat_ids: list[str] | None = None
390+
capability_provider: CapabilityProviderConfig | None = None
390391

391392
@field_validator("allow_chat_ids", mode="before")
392393
@classmethod
@@ -413,7 +414,8 @@ def get_env_vars(self) -> dict[str, str]:
413414
return {
414415
k.upper(): str(v)
415416
for k, v in self.model_dump().items()
416-
if k.lower() not in {"model", "enabled", "allow_chat_ids"}
417+
if k.lower()
418+
not in {"model", "enabled", "allow_chat_ids", "capability_provider"}
417419
}
418420

419421

@@ -470,18 +472,6 @@ def _validate_source(self) -> "SkillSource":
470472
return self
471473

472474

473-
class BundleConfig(BaseModel):
474-
"""Configuration for a bundled optional integration package."""
475-
476-
enabled: bool = False
477-
478-
479-
class BundlesConfig(BaseModel):
480-
"""Top-level bundle toggles from [bundles.*]."""
481-
482-
gog: BundleConfig = Field(default_factory=BundleConfig)
483-
484-
485475
class ConfigError(Exception):
486476
"""Configuration error."""
487477

@@ -542,8 +532,6 @@ def _validate_timezone(cls, v: str) -> str:
542532
skills: dict[str, SkillConfig] = Field(default_factory=dict)
543533
# Skill defaults from [skills.defaults]
544534
skill_defaults: SkillDefaultsConfig = Field(default_factory=SkillDefaultsConfig)
545-
# Bundle presets from [bundles.*]
546-
bundles: BundlesConfig = Field(default_factory=BundlesConfig)
547535

548536
# External skill sources: [[skills.sources]] array
549537
skill_sources: list[SkillSource] = Field(default_factory=list)
@@ -607,7 +595,7 @@ def _extract_skills_config(cls, data: dict) -> dict:
607595
# Remaining entries are per-skill configs
608596
data["skills"] = skills_data
609597

610-
_apply_bundle_presets(data)
598+
_apply_gog_skill_provider_preset(data)
611599
return data
612600

613601
@model_validator(mode="after")
@@ -752,29 +740,17 @@ def get_resolved_env(self) -> dict[str, str]:
752740
}
753741

754742

755-
def _apply_bundle_presets(data: dict[str, Any]) -> None:
756-
"""Apply opinionated bundle presets without overriding explicit config."""
757-
bundles = data.get("bundles")
758-
if not isinstance(bundles, dict):
743+
def _apply_gog_skill_provider_preset(data: dict[str, Any]) -> None:
744+
"""Apply default provider wiring when bundled gog skill is enabled."""
745+
skills = data.get("skills")
746+
if not isinstance(skills, dict):
759747
return
760-
gog = bundles.get("gog")
761-
if not isinstance(gog, dict):
748+
gog_skill = skills.get("gog")
749+
if not isinstance(gog_skill, dict):
762750
return
763-
enabled = gog.get("enabled")
764-
if enabled is not True:
751+
if gog_skill.get("enabled") is not True:
765752
return
766753

767-
# Enable bundled gog skill by default.
768-
skills = data.get("skills")
769-
if not isinstance(skills, dict):
770-
skills = {}
771-
skill_gog = skills.get("gog")
772-
if not isinstance(skill_gog, dict):
773-
skill_gog = {}
774-
skill_gog.setdefault("enabled", True)
775-
skills["gog"] = skill_gog
776-
data["skills"] = skills
777-
778754
# Wire default external provider bridge command by default.
779755
capabilities = data.get("capabilities")
780756
if not isinstance(capabilities, dict):
@@ -785,6 +761,13 @@ def _apply_bundle_presets(data: dict[str, Any]) -> None:
785761
provider_gog = providers.get("gog")
786762
if not isinstance(provider_gog, dict):
787763
provider_gog = {}
764+
765+
provider_from_skill = gog_skill.get("capability_provider")
766+
if isinstance(provider_from_skill, dict):
767+
for field in ("enabled", "namespace", "command", "timeout_seconds"):
768+
if field in provider_from_skill:
769+
provider_gog[field] = provider_from_skill[field]
770+
788771
provider_gog.setdefault("enabled", True)
789772
provider_gog.setdefault("namespace", "gog")
790773
provider_gog.setdefault("command", ["gogcli", "bridge"])

src/ash/skills/bundled/README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ For sensitive integrations (email/calendar/etc.):
2626
- Host config provides provider runtime wiring in `config.toml`:
2727

2828
```toml
29-
[capabilities.providers.gog]
29+
[skills.gog]
30+
enabled = true
31+
32+
[skills.gog.capability_provider]
3033
enabled = true
3134
namespace = "gog"
3235
command = ["gogcli", "bridge"]
@@ -44,10 +47,10 @@ Skill metadata must not define provider/container command wiring.
4447

4548
This can live in a single third-party repo/package as long as Ash wiring stays split:
4649

47-
- preset toggle: `[bundles.gog] enabled = true`
48-
- optional explicit overrides:
49-
- skill enablement: `[skills.gog] enabled = true`
50-
- provider wiring: `[capabilities.providers.gog] ...`
50+
- skill enablement + provider defaults: `[skills.gog] enabled = true`
51+
- optional provider overrides:
52+
- `[skills.gog.capability_provider] ...`
53+
- or host-level `[capabilities.providers.gog] ...`
5154

5255
## Layout
5356

src/ash/skills/bundled/gog/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ ash-sb capability list
4040
If `gog.email`/`gog.calendar` are unavailable, report that host config must define:
4141

4242
```toml
43-
[bundles.gog]
43+
[skills.gog]
4444
enabled = true
4545

46-
[capabilities.providers.gog]
46+
[skills.gog.capability_provider]
4747
enabled = true
4848
namespace = "gog"
4949
command = ["gogcli", "bridge"]

0 commit comments

Comments
 (0)