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
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ Testing:
- Examples:
- `dotnet test test/dotnet.Tests/dotnet.Tests.csproj --filter "Name~ItShowsTheAppropriateMessageToTheUser"`
- `dotnet exec artifacts/bin/redist/Debug/dotnet.Tests.dll -method "*ItShowsTheAppropriateMessageToTheUser*"`
- For incremental test runs of `dotnet.Tests` (avoids slow full `build.cmd`), see the skill at `.github/copilot/skills/incremental-test.md`. In short: build only the modified projects, copy their output DLLs into the redist SDK layout, then run the tests.
- For incremental test runs of `dotnet.Tests` (avoids slow full `build.cmd`), use the `incremental-test` skill.
- To test CLI command changes:
- Build the redist SDK: `./build.sh` from repo root
- Create a dogfood environment: `source eng/dogfood.sh`
- Create a dogfood environment: `source eng/dogfood.sh`
- Test commands in the dogfood shell (e.g., `dnx --help`, `dotnet tool install --help`)
- The dogfood script sets up PATH and environment to use the newly built SDK

Expand Down
24 changes: 24 additions & 0 deletions .github/skills/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Agent Skills

When creating skills, follow:
- Agent skills specification: https://agentskills.io/specification.md
- Best practices: https://agentskills.io/skill-creation/best-practices.md

## Structure

```
.github/skills/skill-name/
├── SKILL.md # Required: metadata + instructions
├── scripts/ # Optional: executable code
├── references/ # Optional: documentation
├── assets/ # Optional: templates, resources
└── ... # Any additional files or directories
```

## Quick Checklist

- [ ] Run `dotnet .github/skills/ValidateSkill.cs <skill-dir>` to validate format.
- [ ] `description` describes what the skill does and when to use it. Skill body does not include "When to use this skill".
- [ ] Skill does not explain things the agent already knows. Focus on what's specific to the task at hand.
- [ ] Deterministic processes use scripts (for example, to fetch and format data from an API).
- [ ] Scripts use PowerShell or .NET file-based apps, not bash.
103 changes: 103 additions & 0 deletions .github/skills/ValidateSkill.cs
Comment thread
lbussell marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env dotnet
#:property ManagePackageVersionsCentrally=false
#:property PublishAot=false
#:package YamlDotNet@16.3.0

using YamlDotNet.Serialization;
using System.Text.RegularExpressions;

if (args.Length == 0)
{
Console.Error.WriteLine("Usage: dotnet ValidateSkill.cs <path-to-skill-directory>");
return 1;
}

string skillDir = Path.GetFullPath(args[0]);
string skillName = Path.GetFileName(Path.TrimEndingDirectorySeparator(skillDir));
string skillFile = Path.Combine(skillDir, "SKILL.md");

// SKILL.md must exist in the skill directory
if (!File.Exists(skillFile))
{
Console.Error.WriteLine($"SKILL.md not found in {skillDir}");
return 1;
}

string text = File.ReadAllText(skillFile);

// SKILL.md must begin with YAML frontmatter delimited by ---
if (!text.StartsWith("---"))
{
Console.Error.WriteLine("No YAML frontmatter found.");
return 1;
}

Match frontmatterMatch = Regex.Match(
text,
@"\A---\r?\n(?<yaml>.*?)(?:\r?\n)---(?:\r?\n|$)",
RegexOptions.Singleline);
if (!frontmatterMatch.Success)
{
Console.Error.WriteLine("Unterminated YAML frontmatter.");
return 1;
}

string yaml = frontmatterMatch.Groups["yaml"].Value.Trim();

IDeserializer deserializer = new DeserializerBuilder().Build();
Dictionary<string, object> frontmatter = deserializer.Deserialize<Dictionary<string, object>>(yaml);

Comment on lines +47 to +49
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deserialize<Dictionary<string, object>> can throw on invalid YAML, which will surface as an unhandled exception/stack trace rather than a clear validation failure. Catch YAML parsing exceptions and return exit code 1 with a concise error message.

Copilot uses AI. Check for mistakes.
// name is required
if (!frontmatter.TryGetValue("name", out object? nameValue) || nameValue is not string frontmatterName)
{
Console.Error.WriteLine("Frontmatter missing 'name' field.");
return 1;
}

// name must be 1-64 characters
if (frontmatterName.Length == 0 || frontmatterName.Length > 64)
{
Console.Error.WriteLine($"Name is {frontmatterName.Length} chars (must be 1-64).");
return 1;
}

// name: lowercase alphanumeric and hyphens only, no leading/trailing/consecutive hyphens
if (!Regex.IsMatch(frontmatterName, @"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$")
|| frontmatterName.Contains("--"))
{
Console.Error.WriteLine($"Invalid name '{frontmatterName}'. Must be lowercase letters, numbers, and hyphens only. Must not start/end with a hyphen or contain consecutive hyphens.");
return 1;
}

// name must match the parent directory name
if (!string.Equals(skillName, frontmatterName, StringComparison.Ordinal))
{
Console.Error.WriteLine($"Name mismatch: directory is '{skillName}' but SKILL.md name is '{frontmatterName}'.");
return 1;
}

// description is required
if (!frontmatter.TryGetValue("description", out object? descValue) || descValue is not string description)
{
Console.Error.WriteLine("Frontmatter missing 'description' field.");
return 1;
}

// description must be 1-1024 characters
if (description.Length == 0 || description.Length > 1024)
{
Console.Error.WriteLine($"Description is {description.Length} chars (must be 1-1024).");
return 1;
}

// Keep SKILL.md under 500 lines; move detailed content to references/ or scripts/
// See "Progressive Disclosure" at https://agentskills.io/specification.md
int lineCount = text.Split('\n').Length;
if (lineCount > 500)
{
Console.Error.WriteLine($"SKILL.md is {lineCount} lines (max 500). See \"Progressive Disclosure\" at https://agentskills.io/specification.md");
return 1;
Comment on lines +95 to +99
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text.Split('\n').Length can overcount by 1 when the file ends with a trailing newline (common in repo files), potentially rejecting a 500-line file as 501. Use File.ReadLines(...).Count() or a split that removes empty trailing entries / handles CRLF consistently.

Copilot uses AI. Check for mistakes.
}

Console.WriteLine($"Skill '{frontmatterName}' is valid.");
return 0;
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
# Incremental Test Runner for dotnet.Tests
---
name: incremental-test
description: >-
Run dotnet.Tests incrementally without a full build.cmd rebuild. Use after
modifying source code in SDK projects to quickly build only changed projects,
deploy their outputs into the redist SDK layout, and run tests against them.
---

This skill enables fast incremental test runs of `dotnet.Tests` without a full `build.cmd` rebuild.
Use it after making source code changes to quickly build only the modified projects and deploy their outputs into the redist SDK layout so tests can run against them.
# Incremental Test Runner for dotnet.Tests

## Prerequisites

- A full build must have been completed at least once (via `build.cmd` or `build.sh`) so that the redist SDK layout exists at `artifacts\bin\redist\Debug\dotnet\sdk\<version>\`.
- The repo-local `.dotnet` SDK must match the version expected by the test projects. If the runtime or SDK version is out of date (e.g., test build fails with a missing framework error), run `.\restore.cmd` (or `./restore.sh` on macOS/Linux) to download the correct SDK into `.dotnet`.
- This workflow uses Windows/PowerShell commands and paths. On macOS/Linux, substitute forward slashes and use `cp` instead of `Copy-Item`.

## When to use

Use this skill when you need to run `dotnet.Tests` after modifying source code in one or more SDK projects. It avoids the slow full `build.cmd` by only rebuilding the changed projects and copying their output DLLs into the redist layout.

## Workflow

### Step 1: Identify modified projects
Expand Down
Loading