# dotnet/skills Project Structure: How the Mono-Repo Is Organized

> Explore the dotnet/skills mono-repo structure, detailing plugins, tests, the execution engine, and CI automation. Understand how this project is organized for efficient development.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: architecture
- Published: 2026-07-08

---

**The dotnet/skills repository is a mono-repo organized into four primary zones: `plugins/` for skill definitions, `tests/` for validation fixtures, `eng/skill-validator/` for the execution engine, and `.github/workflows/` for CI automation.**

The dotnet/skills repository houses reusable, self-contained automation units called "skills" that the .NET team runs in CI, bots, or local environments. Understanding the dotnet/skills project structure is essential for contributing new capabilities or debugging existing automation. Everything is grouped by purpose, with strict conventions that allow the CI pipeline to discover and validate new skills automatically without manual registration.

## Top-Level Directory Layout

The root of the repository contains standard open-source files plus four critical directories that define the project architecture:

| Entry | Purpose |
|-------|---------|
| `plugins/` | **All skill definitions**. Each sub-folder is a distinct skill plugin (e.g., `dotnet-upgrade`, `dotnet-test`). |
| `tests/` | Integration test suites that mirror the plugin hierarchy and contain validation fixtures. |
| `eng/skill-validator/` | The **skill-validator** tool that compiles plugins, checks schema compliance, and produces JSON reports. |
| `.github/` | GitHub Actions workflows including [`skill-validator.yml`](https://github.com/dotnet/skills/blob/main/skill-validator.yml), [`skill-coverage.yml`](https://github.com/dotnet/skills/blob/main/skill-coverage.yml), and [`pr-triage.yml`](https://github.com/dotnet/skills/blob/main/pr-triage.yml). |
| `docs/` | Human-readable design notes, workflow diagrams, and validator explanations. |
| [`AGENTS.md`](https://github.com/dotnet/skills/blob/main/AGENTS.md) | Documentation for "agents"—small helper scripts invoked by the validator or bots. |
| [`global.json`](https://github.com/dotnet/skills/blob/main/global.json) | Pins the .NET SDK version (currently 8.0.x) used for building the repository. |

## The plugins/ Directory: Skill Definitions

The `plugins/` directory is the heart of the dotnet/skills project structure. Each skill lives under `plugins/<category>/<skill-name>/` and follows a strict contract.

A typical plugin layout looks like this:

```text
plugins/
├─ dotnet-upgrade/
│  ├─ skills/
│  │  └─ migrate-nullable-references/
│  │     ├─ SKILL.md          ← declarative description of the skill
│  │     └─ references/
│  │        └─ breaking‑changes.md
│  └─ plugin.json            ← metadata for the validator
└─ dotnet-test/
   └─ skills/
      └─ test‑tagging/
         └─ SKILL.md

```

Each skill requires three key components:

- **[`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md)**: A markdown file following a well-defined schema (documented in [`eng/skill-validator/src/docs/InvestigatingResults.md`](https://github.com/dotnet/skills/blob/main/eng/skill-validator/src/docs/InvestigatingResults.md)) that describes inputs, outputs, and execution logic. The validator consumes this to generate the JSON model used by CI pipelines.
- **[`plugin.json`](https://github.com/dotnet/skills/blob/main/plugin.json)**: Metadata that tells the validator which skills are present, their version, and runtime requirements.
- **`references/`** (optional): Supplemental markdown files (e.g., breaking-change lists) linked from the main skill description.

## The tests/ Directory: Validation and Fixtures

Test projects reside under `tests/` and mirror the plugin hierarchy exactly. This structure allows the skill-validator to correlate each skill with its validation suite automatically.

```

tests/
├─ dotnet-upgrade/
│  ├─ thread‑abort‑migration/
│  │  └─ eval.yaml               ← Vally evaluation configuration
│  └─ migrate‑nullable‑references/
│     └─ fixtures/
│        ├─ nrt‑enabled/
│        │   └─ NrtEnabled.csproj
│        └─ nrt‑disabled/
│            └─ NrtDisabled.csproj
└─ dotnet‑msbuild/
   └─ resolve‑project‑references/
      ├─ App.csproj
      ├─ LibA/
      │   └─ LibA.csproj
      └─ LibB/
          └─ LibB.csproj

```

Key files in the tests directory:

- **[`eval.yaml`](https://github.com/dotnet/skills/blob/main/eval.yaml)**: Describes how a Vally evaluation should run, specifying input files and expected outputs.
- **`.csproj` fixtures**: Minimal sample projects that serve as targets for the skill during automated testing.
- **Test results**: Collected by the skill-validator and reported via the [`skill-validator.yml`](https://github.com/dotnet/skills/blob/main/skill-validator.yml) GitHub workflow.

## The eng/skill-validator/ Directory: The Execution Engine

The **skill-validator** is the runtime engine that makes the dotnet/skills project structure executable. Located at `eng/skill-validator/`, this tool loads all [`plugin.json`](https://github.com/dotnet/skills/blob/main/plugin.json) files, parses [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) specifications, and invokes the actual skill code (typically PowerShell scripts or .NET executables).

Critical paths within this directory:

- `src/SkillValidator.csproj`: The core project that orchestrates discovery and execution.
- `src/docs/`: Validator documentation including troubleshooting guides like [`InvestigatingResults.md`](https://github.com/dotnet/skills/blob/main/InvestigatingResults.md).
- `tests/SkillValidator.Tests.csproj`: Unit tests for the validator itself.

The validator produces a JSON report consumed by the **skill-coverage** workflow to ensure every skill has adequate test coverage.

## CI/CD and Automation

The `.github/workflows/` directory enforces quality gates across the dotnet/skills project structure:

- **[`skill-validator.yml`](https://github.com/dotnet/skills/blob/main/skill-validator.yml)**: Builds the validator and runs all `tests/**/eval.yaml` files, failing the PR if any skill is broken.
- **[`skill-coverage.yml`](https://github.com/dotnet/skills/blob/main/skill-coverage.yml)**: Verifies that each skill has a matching test suite and meets coverage thresholds.
- **[`pr-triage.yml`](https://github.com/dotnet/skills/blob/main/pr-triage.yml)**: Labels new PRs and ensures they match contribution guidelines defined in [`CONTRIBUTING.md`](https://github.com/dotnet/skills/blob/main/CONTRIBUTING.md).
- **[`evaluation.yml`](https://github.com/dotnet/skills/blob/main/evaluation.yml)**: Executes Vally-based evaluations for skills requiring runtime verification (e.g., code-generation).
- **[`markdownlint.yml`](https://github.com/dotnet/skills/blob/main/markdownlint.yml)**: Enforces consistent markdown style across [`README.md`](https://github.com/dotnet/skills/blob/main/README.md), [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md), and other documentation.

## Adding a New Skill

To extend the repository, create the folder structure and metadata files following the established dotnet/skills project structure conventions:

```bash

# 1️⃣ Create the folder structure

mkdir -p plugins/dotnet-example/skills/hello-world

# 2️⃣ Write a minimal SKILL.md

cat > plugins/dotnet-example/skills/hello-world/SKILL.md <<'EOF'

# Hello‑World Skill

**Description**: Prints “Hello, world!” to the console.

**Inputs**
- `name` (string, optional): Name to greet. Default: “world”.

**Outputs**
- `greeting` (string): The greeting message.

**Execution**

```powershell
param($name = 'world')
Write-Output "Hello, $name!"

```

EOF

# 3️⃣ Add plugin metadata

cat > plugins/dotnet-example/plugin.json <<'EOF'
{
  "name": "dotnet-example",
  "version": "1.0.0",
  "skills": [
    {
      "id": "hello-world",
      "path": "skills/hello-world"
    }
  ]
}
EOF

# 4️⃣ Add a test fixture

mkdir -p tests/dotnet-example/hello-world/fixtures/simple
cat > tests/dotnet-example/hello-world/fixtures/simple/eval.yaml <<'EOF'
skill: dotnet-example/hello-world
inputs:
  name: "OpenAI"
expected:
  greeting: "Hello, OpenAI!"
EOF

```

The validator automatically picks up the new [`plugin.json`](https://github.com/dotnet/skills/blob/main/plugin.json), parses [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md), and runs the [`eval.yaml`](https://github.com/dotnet/skills/blob/main/eval.yaml) test during the next CI run without requiring changes to any other part of the repository.

## Running Skills Locally

You can invoke skills directly from the command line using the validator project:

```powershell

# Clone the repo

git clone https://github.com/dotnet/skills.git
cd skills

# Build the validator

dotnet build eng/skill-validator/src/SkillValidator.csproj

# Execute a skill directly

dotnet run --project eng/skill-validator/src/SkillValidator.csproj `
  --skill dotnet-upgrade/migrate-nullable-references `
  --input ProjectPath=tests/dotnet-upgrade/migrate-nullable-references/fixtures/nrt-enabled/NrtEnabled.csproj

```

This command loads the skill definition from [`plugins/dotnet-upgrade/skills/migrate-nullable-references/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-upgrade/skills/migrate-nullable-references/SKILL.md), runs the embedded PowerShell script, and prints the migration report to the console.

## Summary

- **`plugins/`** contains all skill definitions, with each skill using [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) for logic and [`plugin.json`](https://github.com/dotnet/skills/blob/main/plugin.json) for metadata registration.
- **`tests/`** mirrors the plugin structure and houses [`eval.yaml`](https://github.com/dotnet/skills/blob/main/eval.yaml) Vally configurations plus `.csproj` fixtures for integration testing.
- **`eng/skill-validator/`** hosts the execution engine (`SkillValidator.csproj`) that parses markdown specifications and runs skill code.
- **`.github/workflows/`** automates validation, coverage checks, and PR triage via GitHub Actions.
- **[`global.json`](https://github.com/dotnet/skills/blob/main/global.json)** pins the .NET SDK version, while **[`AGENTS.md`](https://github.com/dotnet/skills/blob/main/AGENTS.md)** documents auxiliary helper scripts.

## Frequently Asked Questions

### What is the role of the plugin.json file?

The [`plugin.json`](https://github.com/dotnet/skills/blob/main/plugin.json) file acts as the registration manifest for the skill-validator. According to the dotnet/skills source code, it declares the plugin name, version, and an array of skill IDs with their relative paths, enabling the validator to discover and load [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) files without hard-coded references.

### How does the skill-validator parse SKILL.md files?

The validator, implemented in `eng/skill-validator/src/SkillValidator.csproj`, consumes [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) files following a schema defined in [`eng/skill-validator/src/docs/InvestigatingResults.md`](https://github.com/dotnet/skills/blob/main/eng/skill-validator/src/docs/InvestigatingResults.md). It extracts the declarative description, inputs, outputs, and execution blocks to generate a JSON model used by CI pipelines to invoke the correct automation scripts.

### What is the difference between the plugins/ and tests/ directories?

The `plugins/` directory contains the actual automation logic and metadata consumed by the validator, while `tests/` contains validation fixtures and [`eval.yaml`](https://github.com/dotnet/skills/blob/main/eval.yaml) configuration files that define how to verify those skills. The directory structures mirror each other so that the [`skill-coverage.yml`](https://github.com/dotnet/skills/blob/main/skill-coverage.yml) workflow can ensure every plugin has a corresponding test suite.

### Can I run a skill without using the GitHub Actions pipeline?

Yes. You can execute skills locally by building `eng/skill-validator/src/SkillValidator.csproj` and using `dotnet run` with the `--skill` and `--input` arguments. This loads the skill definition directly from the `plugins/` directory and executes the embedded scripts (typically PowerShell) against your specified inputs, producing the same JSON reports used in CI.