Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@

# .NET owners
/dotnet/ @eavanvalkenburg @SergeyMenshykh @rogerbarreto @westey-m @moonbox3 @baywet @peibekwe

# Public Python consumer skill
/skills/agent-framework-builder-python/ @eavanvalkenburg @SergeyMenshykh @rogerbarreto @westey-m @moonbox3 @jpalvarezl @sophia-ramsey @TaoChenOSU @giles17
13 changes: 13 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ Microsoft Agent Framework - a multi-language framework for building, orchestrati
- `python/` - Python implementation → see [python/AGENTS.md](../python/AGENTS.md)
- `dotnet/` - C#/.NET implementation → see [dotnet/AGENTS.md](../dotnet/AGENTS.md)
- `docs/` - Design documents and architectural decision records
- `skills/` - Public, installable Agent Skills for developers using Agent Framework

## Public Consumer Skills

The skills under `skills/` are distribution artifacts for developers building Agent Framework applications. They are
not project skills for contributors working in this repository:

- Do not load or invoke them as instructions for normal Agent Framework repository work.
- Keep them outside `.github/skills`, `.agents/skills`, and `.claude/skills` so coding agents do not auto-discover them.
- When a change affects public APIs, packages, samples, configuration, evaluation, integrations, or hosting guidance,
review and update the corresponding consumer skill as maintained product documentation.
- Read a consumer skill during repository work only when that maintenance check is relevant; do not let its standalone
application defaults override repository-specific instructions.

## Architectural Decision Records (ADRs)

Expand Down
66 changes: 66 additions & 0 deletions .github/scripts/validate_agent_skill_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft. All rights reserved.
"""Validate local Markdown resources bundled with public Agent Skills."""

from __future__ import annotations

import re
import stat
import sys
from pathlib import Path

LINK_PATTERN = re.compile(r"\[[^\]]+\]\(([^)]+)\)")


def validate_agent_skill_links(skills_directory: Path) -> list[str]:
"""Return validation errors for local Markdown links in public Agent Skills."""
errors: list[str] = []
skill_roots = sorted(path.parent for path in skills_directory.rglob("SKILL.md"))

if not skill_roots:
return [f"No Agent Skills found under {skills_directory}."]

for skill_root in skill_roots:
resolved_root = skill_root.resolve(strict=True)
for markdown_path in skill_root.rglob("*.md"):
for target in LINK_PATTERN.findall(markdown_path.read_text(encoding="utf-8")):
if "://" in target or target.startswith(("#", "mailto:")):
continue

relative_target = target.split("#", 1)[0]
if not relative_target:
continue

target_path = Path(relative_target)
if target_path.is_absolute():
errors.append(f"{markdown_path}: absolute local link is not allowed: {target}")
continue

candidate = markdown_path.parent / target_path
try:
resolved_candidate = candidate.resolve(strict=True)
except FileNotFoundError:
errors.append(f"{markdown_path}: missing local resource: {target}")
continue

try:
resolved_candidate.relative_to(resolved_root)
except ValueError:
errors.append(f"{markdown_path}: local resource escapes skill root: {target}")
continue

if not stat.S_ISREG(candidate.lstat().st_mode):
errors.append(f"{markdown_path}: local resource is not a regular file: {target}")

return errors


def main() -> None:
skills_directory = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("skills")
errors = validate_agent_skill_links(skills_directory)
if errors:
raise SystemExit("\n".join(errors))


if __name__ == "__main__":
main()
82 changes: 82 additions & 0 deletions .github/tests/test_validate_agent_skill_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright (c) Microsoft. All rights reserved.

# ruff:file-ignore[implicit-namespace-package, undocumented-public-class, undocumented-public-method]

from __future__ import annotations

import sys
import tempfile
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parents[1] / "scripts"))

from validate_agent_skill_links import validate_agent_skill_links


class ValidateAgentSkillLinksTests(unittest.TestCase):
def setUp(self) -> None:
self.temp_dir = tempfile.TemporaryDirectory()
self.root = Path(self.temp_dir.name)
self.skills = self.root / "skills"
self.skill = self.skills / "example-skill"
self.skill.mkdir(parents=True)

def tearDown(self) -> None:
self.temp_dir.cleanup()

def write_skill(self, body: str) -> None:
(self.skill / "SKILL.md").write_text(body, encoding="utf-8")

def test_accepts_regular_file_inside_skill(self) -> None:
(self.skill / "references").mkdir()
(self.skill / "references" / "details.md").write_text("# Details\n", encoding="utf-8")
self.write_skill("[Details](references/details.md)\n")

self.assertEqual([], validate_agent_skill_links(self.skills))

def test_rejects_parent_traversal(self) -> None:
(self.skills / "outside.md").write_text("# Outside\n", encoding="utf-8")
self.write_skill("[Outside](../outside.md)\n")

errors = validate_agent_skill_links(self.skills)

self.assertTrue(any("escapes skill root" in error for error in errors))

def test_rejects_absolute_path(self) -> None:
absolute_file = self.root / "outside.md"
absolute_file.write_text("# Outside\n", encoding="utf-8")
self.write_skill(f"[Outside]({absolute_file})\n")

errors = validate_agent_skill_links(self.skills)

self.assertTrue(any("absolute local link" in error for error in errors))

def test_rejects_directory_target(self) -> None:
(self.skill / "references").mkdir()
self.write_skill("[References](references)\n")

errors = validate_agent_skill_links(self.skills)

self.assertTrue(any("not a regular file" in error for error in errors))

def test_rejects_symlink_target(self) -> None:
target = self.skill / "details.md"
target.write_text("# Details\n", encoding="utf-8")
(self.skill / "linked.md").symlink_to(target)
self.write_skill("[Details](linked.md)\n")

errors = validate_agent_skill_links(self.skills)

self.assertTrue(any("not a regular file" in error for error in errors))

def test_rejects_missing_target(self) -> None:
self.write_skill("[Missing](references/missing.md)\n")

errors = validate_agent_skill_links(self.skills)

self.assertTrue(any("missing local resource" in error for error in errors))


if __name__ == "__main__":
unittest.main()
62 changes: 62 additions & 0 deletions .github/workflows/agent-skills.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Validate Agent Skills

on:
workflow_dispatch:
pull_request:
paths:
- "skills/**"
- ".github/scripts/validate_agent_skill_links.py"
- ".github/tests/test_validate_agent_skill_links.py"
- ".github/workflows/agent-skills.yml"
push:
branches: ["main"]
paths:
- "skills/**"
- ".github/scripts/validate_agent_skill_links.py"
- ".github/tests/test_validate_agent_skill_links.py"
- ".github/workflows/agent-skills.yml"

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"

- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version-file: "python/pyproject.toml"

- name: Validate Agent Skills specification
shell: bash
run: |
set -euo pipefail

found_skill=0
while IFS= read -r skill_file; do
[[ -z "$skill_file" ]] && continue
found_skill=1
uvx --from "skills-ref==0.1.1" agentskills validate "$(dirname "$skill_file")"
done < <(find skills -mindepth 2 -maxdepth 2 -name SKILL.md -print | sort)

if (( found_skill == 0 )); then
echo "No public Agent Skills found under skills/." >&2
exit 1
fi

- name: Validate relative resource links
run: |
python .github/tests/test_validate_agent_skill_links.py
python .github/scripts/validate_agent_skill_links.py skills

- name: Validate GitHub skill publishing
env:
GH_TOKEN: ${{ github.token }}
run: gh skill publish skills --dry-run
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Explore new MAF capabilities and real implementation patterns on the [official b
- [Quickstart](#quickstart)
- [Basic Agent - Python](#basic-agent---python)
- [Basic Agent - .NET](#basic-agent---net)
- [Agent Skills for Coding Agents](#agent-skills-for-coding-agents)
- [More Examples & Samples](#more-examples--samples)
- [Community & Feedback](#community--feedback)
- [Troubleshooting](#troubleshooting)
Expand Down Expand Up @@ -161,6 +162,23 @@ AIAgent agent =
Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Framework."));
```

## Agent Skills for Coding Agents

Install the Python Agent Framework builder skill to give a compatible coding agent current guidance for choosing and
building agents, harnesses, workflows, evaluations, integrations, and hosting:

```bash
gh skill install microsoft/agent-framework agent-framework-builder-python
```

The cross-agent `skills` CLI is also supported:

```bash
npx skills add microsoft/agent-framework --skill agent-framework-builder-python
```

See the [consumer skills catalog](./skills/) for details.

## More Examples & Samples

### Python
Expand Down
9 changes: 9 additions & 0 deletions python/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ Instructions for AI coding agents working in the Python codebase.
- `pull-requests` — writing PR descriptions (template) and handling/resolving PR review comments
- `agent-framework-py-release` — Python release PR workflow, CHANGELOG-driven package bumps, lifecycle version rules, and dependency-floor validation

**Public consumer skill** (`../skills/agent-framework-builder-python/`) — installable guidance for developers building
Python Agent Framework applications. It is a distribution artifact, not a repository skill: do not invoke it for
normal framework development. Review it as documentation when changes affect the public developer experience.

## Maintaining Documentation

When making changes to a package, check if the following need updates:
- The package's `AGENTS.md` file (adding/removing/renaming public APIs, architecture changes, import path changes)
- The agent skills in `.github/skills/` if conventions, commands, or workflows change
- The public Python consumer skill in `../skills/agent-framework-builder-python/` if public APIs, package names or
maturity, canonical samples, configuration, evaluation, integrations, or hosting guidance change

At the end of every run, re-read `AGENTS.md` and the relevant skill files and
update any guidance that the conversation revealed to be out of date,
Expand All @@ -31,6 +37,9 @@ the user confirmed, etc.). **Before adding a new principle or rule, ask the
user whether they want it captured as a durable principle** — do not invent
team norms from a single conversation without explicit confirmation.

Only inspect the public consumer skill when the change falls into its maintenance scope. Do not load it as an active
skill or apply its standalone-project defaults to this monorepo.

## Terminology

- **Avoid "GA" for Agent Framework code.** Reserve *GA* for hosted services
Expand Down
35 changes: 35 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Microsoft Agent Framework consumer skills

This directory contains installable Agent Skills for developers building applications with Microsoft Agent Framework.
These are public distribution artifacts, not contributor instructions for working on the Agent Framework repository.

## Available skills

| Skill | Purpose |
| --- | --- |
| `agent-framework-builder-python` | Build, modify, evaluate, integrate, and host Python Agent Framework applications. |

## Install

Preview and install with GitHub CLI:

```bash
gh skill preview microsoft/agent-framework agent-framework-builder-python
gh skill install microsoft/agent-framework agent-framework-builder-python
```

Or use the cross-agent skills CLI:

```bash
npx skills add microsoft/agent-framework --skill agent-framework-builder-python
```

## Repository maintenance contract

- Coding agents working on Agent Framework itself must not treat the skills in this directory as repository
instructions or automatically load them for normal framework development.
- Keep the skills outside `.github/skills`, `.agents/skills`, and `.claude/skills` so repository agents do not
discover them as project skills.
- Review the Python consumer skill whenever a change affects public APIs, package names or maturity, canonical samples,
configuration, evaluation, integrations, or hosting guidance.
- Update only the affected guidance. Do not copy internal contributor conventions into the consumer skill.
Loading
Loading