diff --git a/.kiro/skills/library-development/references/project-map.md b/.kiro/skills/library-development/references/project-map.md index de65999..2d59341 100644 --- a/.kiro/skills/library-development/references/project-map.md +++ b/.kiro/skills/library-development/references/project-map.md @@ -113,7 +113,7 @@ for the chapter-by-chapter reference. - **Python ≥ 3.11** (ruff/ty target 3.13). Runtime deps: `strands-agents` (>=1.48,<2), `pydantic` v2, `pyyaml`, `mcp`. Optional extras: - `agentcore-memory`, `ollama`, `openai`, `gemini`. `anthropic`. + `agentcore-memory`, `ollama`, `openai`, `gemini`, `anthropic`. - **MCP servers** run on a background daemon thread with a self-managed `uvicorn.Server` (HTTP transports only — `streamable-http`, `sse`); `stdio` is client-side (the client spawns a subprocess). diff --git a/docs/configuration/Chapter_19.md b/docs/configuration/Chapter_19.md index 1ef8479..69ab709 100644 --- a/docs/configuration/Chapter_19.md +++ b/docs/configuration/Chapter_19.md @@ -103,68 +103,15 @@ agents: skills: ./skills/ # one skill directory, or a parent of several ``` -> **Paths in `params` are not rewritten.** Unlike a plugin's `type:` (which is resolved -> relative to the config file), everything under `params` is forwarded to the plugin -> verbatim. `skills: ./skills/` therefore resolves against the process working directory — -> run the config from the directory that makes that path valid. +> **Note — path params are relative to the working directory.** A plugin's `type:` is +> resolved relative to the config file, but any path *inside* `params` (such as a skills +> directory) is forwarded verbatim and resolves against the current working directory. Run +> the config from a directory where that path is valid. (This may change in a future +> release.) -### Authoring a skill (SKILL.md) - -Agent Skills is an open, cross-vendor format — originated by Anthropic and adopted across -agent tooling. A skill is a directory (named after the skill) containing a `SKILL.md`: YAML -frontmatter followed by a markdown body. It may also ship resource directories the agent -reads on demand. - -``` -conventional-commits/ -├── SKILL.md # required — frontmatter + instructions -├── scripts/ # optional — runnable scripts -├── references/ # optional — docs loaded only when needed -└── assets/ # optional — templates, data, images -``` - -Frontmatter fields, per the [Agent Skills specification](https://agentskills.io/specification): - -| Field | Required | Notes | -|-------|----------|-------| -| `name` | yes | 1–64 chars; lowercase `a–z`, `0–9`, `-`; no leading/trailing or consecutive hyphens. **Must match the directory name.** | -| `description` | yes | 1–1024 chars. State *what* it does **and** *when* to use it, with keywords the model can match — this is all the agent sees until it activates the skill. | -| `license` | no | A license name or a bundled license file. | -| `compatibility` | no | ≤500 chars. Environment requirements (target product, system packages, network access). | -| `metadata` | no | Arbitrary string-to-string map for client-specific data. | -| `allowed-tools` | no | Space-separated pre-approved tool names (experimental; support varies). | - -```markdown ---- -name: conventional-commits -description: Write git commit messages that follow the Conventional Commits standard. Use when the user asks for a commit message or wants a change summary written up for git. -license: Apache-2.0 ---- - -# Conventional Commits - -## When to use -Activate when the user asks for a commit message... - -## Steps -1. Choose the single type that best fits the change... - -## Examples -Input: "added pagination to the users endpoint" -> `feat(api): add pagination to the users endpoint` - -## Edge cases -- Several unrelated changes -> recommend separate commits. -``` - -**Write for progressive disclosure.** The `description` is the model's only cue to activate -the skill, so make it specific. Keep the body focused — the spec recommends under ~500 lines -(≈5000 tokens) — and move long reference material into `references/` files that load only -when the task needs them. - -> **Resource files need a tool.** `AgentSkills` handles discovery and activation only. A -> skill that ships scripts or references also needs a file-reading tool such as `file_read` -> from [`strands-agents-tools`](https://strandsagents.com/docs/user-guide/concepts/plugins/skills/#providing-tools-for-resource-access); -> add it under the agent's `tools:`. Instruction-only skills need none. +A skill is just a `SKILL.md` directory that `AgentSkills` discovers. Authoring one — the +frontmatter fields, progressive disclosure, resource files — is owned by strands and the +open Agent Skills format, not by strands-compose; `examples/15_plugins/` ships a working one. ### Further reading @@ -178,8 +125,7 @@ when the task needs them. | Condition | Exception | |-----------|-----------| -| `type` has no `:` separator | `ValueError` | -| Malformed spec / missing file / module / attribute | `ImportResolutionError` (a `ValueError` subclass, from `load_object`) | +| Malformed spec (no `:` separator) / missing file / module / attribute | `ImportResolutionError` (a `ValueError` subclass, from `load_object`) | | Resolved object is not callable | `TypeError` | | Resolved object is callable but does not return a `Plugin` | `TypeError` | | Constructor or factory raises | the original exception, unwrapped | diff --git a/pyproject.toml b/pyproject.toml index ac519c3..80a84f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ gemini = [ "strands-agents[gemini]>=1.48.0,<2.0.0", ] anthropic = [ - "strands-agents[anthropic]>=1.35.0,<2.0.0", + "strands-agents[anthropic]>=1.48.0,<2.0.0", ] [project.urls] diff --git a/src/strands_compose/config/resolvers/hooks.py b/src/strands_compose/config/resolvers/hooks.py index 57b12e2..2d879bf 100644 --- a/src/strands_compose/config/resolvers/hooks.py +++ b/src/strands_compose/config/resolvers/hooks.py @@ -27,24 +27,17 @@ def resolve_hook(hook_def: HookDef) -> HookProvider: Instantiated HookProvider. Raises: - ValueError: If ``type`` is not in ``module:Class`` format. + ImportResolutionError: If ``type`` is not a valid ``module:Class`` / + ``./file.py:Class`` spec (a ``ValueError`` subclass, from ``load_object``). TypeError: If the resolved object is not a HookProvider subclass. """ - type_str = hook_def.type - if ":" not in type_str: - raise ValueError( - f"Hook type {type_str!r} is not a valid import spec.\n" - f"Use 'module.path:ClassName' (e.g. 'strands_compose.hooks:StopGuard') " - f"or './path/to/file.py:ClassName'." - ) - - cls = load_object(type_str, target="hook") + cls = load_object(hook_def.type, target="hook") hook = cls(**hook_def.params) if not isinstance(hook, HookProvider): raise TypeError( - f"Hook {type_str!r} returned {type(hook).__name__}, expected HookProvider subclass." + f"Hook {hook_def.type!r} returned {type(hook).__name__}, expected HookProvider subclass." ) return hook diff --git a/src/strands_compose/config/resolvers/plugins.py b/src/strands_compose/config/resolvers/plugins.py index fc2b645..aed5cc6 100644 --- a/src/strands_compose/config/resolvers/plugins.py +++ b/src/strands_compose/config/resolvers/plugins.py @@ -27,24 +27,17 @@ def resolve_plugin(plugin_def: PluginDef) -> Plugin: Instantiated Plugin. Raises: - ValueError: If ``type`` is not in ``module:Class`` format. + ImportResolutionError: If ``type`` is not a valid ``module:Class`` / + ``./file.py:Class`` spec (a ``ValueError`` subclass, from ``load_object``). TypeError: If calling the resolved object does not produce a Plugin. """ - type_str = plugin_def.type - if ":" not in type_str: - raise ValueError( - f"Plugin type {type_str!r} is not a valid import spec.\n" - f"Use 'module.path:ClassName' (e.g. 'strands:AgentSkills') " - f"or './path/to/file.py:ClassName'." - ) - - obj = load_object(type_str, target="plugin") + obj = load_object(plugin_def.type, target="plugin") plugin = obj(**plugin_def.params) if not isinstance(plugin, Plugin): raise TypeError( - f"Plugin {type_str!r} returned {type(plugin).__name__}, " + f"Plugin {plugin_def.type!r} returned {type(plugin).__name__}, " f"expected strands.plugins.Plugin subclass." ) return plugin diff --git a/uv.lock b/uv.lock index d7d6710..7e22195 100644 --- a/uv.lock +++ b/uv.lock @@ -1939,7 +1939,7 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.12.5" }, { name = "pyyaml", specifier = ">=6.0.0" }, { name = "strands-agents", specifier = ">=1.48.0,<2.0.0" }, - { name = "strands-agents", extras = ["anthropic"], marker = "extra == 'anthropic'", specifier = ">=1.35.0,<2.0.0" }, + { name = "strands-agents", extras = ["anthropic"], marker = "extra == 'anthropic'", specifier = ">=1.48.0,<2.0.0" }, { name = "strands-agents", extras = ["gemini"], marker = "extra == 'gemini'", specifier = ">=1.48.0,<2.0.0" }, { name = "strands-agents", extras = ["ollama"], marker = "extra == 'ollama'", specifier = ">=1.48.0,<2.0.0" }, { name = "strands-agents", extras = ["openai"], marker = "extra == 'openai'", specifier = ">=1.48.0,<2.0.0" },