Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .kiro/skills/library-development/references/project-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
72 changes: 9 additions & 63 deletions docs/configuration/Chapter_19.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
15 changes: 4 additions & 11 deletions src/strands_compose/config/resolvers/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 4 additions & 11 deletions src/strands_compose/config/resolvers/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.