Official agent skill

Extension Points

by microsoft in microsoft/testfx

Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout…

OfficialMITAuto-check passedDevOps & Cloud

Install Extension Points

skills CLI
$ npx skills add microsoft/testfx --skill extension-points -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install microsoft/testfx extension-points --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/microsoft/testfx.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/extension-points .claude/skills/extension-points && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
extension-points
GitHub stars
1k
Token cost
~2.6k tokens
SKILL.md length
588 words
Files
1
Skills in repo
44
Repo updated
First seen
Licence
MIT

At a glance

Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout…

  • Works in 3 steps: .nuspec mappings — copy a single source… → .csproj metadata on or items — same… → SDK conventions — IncludeBuildOutput,…
  • : diagnosing and fixing MSBuild import and hook patterns
  • SKILL.md covers CustomBefore / CustomAfter Hooks, Wildcard Import Directories, Import Gating — Control… and NuGet Package Build Extension…, plus 5 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Extension Points is an agent skill from microsoft/testfx, published by the product's own GitHub organization. Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout (build/buildTransitive), and the MicrosoftCommonPropsHasBeenImported guard. USE FOR: diagnosing and fixing MSBuild import and hook patterns, reviewing and fixing extension point anti-patterns in Directory.Build files, fixing missing Exists() guards on imports that break fresh clones, fixing NuGet package hooks being silently dropped…

Its SKILL.md is about 2.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in DevOps & Cloud, covering CI/CD. The repository describes itself as: This repository holds the source code of Microsoft.Testing.Platform (MTP), a lightweight alternative to VSTest, as well as MSTest adapter and framework. The licence is MIT.

When your agent uses it

  • : diagnosing and fixing MSBuild import and hook patterns
  • Reviewing and fixing extension point anti-patterns in Directory.Build files
  • Fixing missing Exists() guards on imports that break fresh clones
  • Fixing NuGet package hooks being silently dropped instead of appended

Example prompts

  • “/extension-points”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. .nuspec mappings — copy a single source file into multiple per-TFM targets
  2. .csproj metadata on or items — same effect via SDK pack. Use one item per destination to keep the mapping unambiguous
  3. SDK conventions — IncludeBuildOutput, BuildOutputTargetFolder, IncludeContentInPack automatically place built outputs under lib// or…

What it can do on your machine

Read from SKILL.md and the folder at commit 44b9dcc. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are xml).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Extension Points loads about 2.6k tokens when it runs. Until then it costs about 236 tokens; SKILL.md has 588 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~236
When it runs · the whole SKILL.md, loaded when a task matches
~2.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from microsoft/testfx at commit 44b9dcc, republished under its MIT licence (© microsoft). 588 words, ~2,645 tokens.

Download SKILL.mdSave it as .claude/skills/extension-points/SKILL.md (or your agent's skills folder).
name
extension-points
description
Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout (build/buildTransitive), and the MicrosoftCommonPropsHasBeenImported guard. USE FOR: diagnosing and fixing MSBuild import and hook patterns, reviewing and fixing extension point anti-patterns in Directory.Build files, fixing missing Exists() guards on imports that break fresh clones, fixing NuGet package hooks being silently dropped instead of appended, making build targets extensible for other projects, injecting custom logic into the build pipeline, creating NuGet packages that extend the build, conditionally disabling imports. DO NOT USE FOR: target authoring patterns (use target-authoring), props vs targets placement (use directory-build-organization), general anti-patterns (use msbuild-antipatterns), non-MSBuild build systems.
license
MIT

MSBuild Extension Points

How the MSBuild pipeline provides hooks for SDKs, NuGet packages, repos, and users to inject custom logic.

CustomBefore / CustomAfter Hooks

Every major .targets file defines import hooks:

xml
<PropertyGroup>
  <CustomBeforeMicrosoftCommonTargets Condition="'$(CustomBeforeMicrosoftCommonTargets)' == ''">
    $(MSBuildExtensionsPath)\v$(MSBuildToolsVersion)\Custom.Before.Microsoft.Common.targets
  </CustomBeforeMicrosoftCommonTargets>
</PropertyGroup>

<Import Project="$(CustomBeforeMicrosoftCommonTargets)"
    Condition="'$(CustomBeforeMicrosoftCommonTargets)' != '' and Exists('$(CustomBeforeMicrosoftCommonTargets)')"/>
<!-- ... core targets ... -->
<Import Project="$(CustomAfterMicrosoftCommonTargets)"
    Condition="'$(CustomAfterMicrosoftCommonTargets)' != '' and Exists('$(CustomAfterMicrosoftCommonTargets)')"/>
Rules
  • Default path includes version (v$(MSBuildToolsVersion)) for side-by-side installations.
  • Always check Exists(). The file may not be present on every machine.
  • Append to the property (don't overwrite) to chain multiple hooks:
xml
<PropertyGroup>
  <CustomBeforeMicrosoftCommonTargets>
    $(CustomBeforeMicrosoftCommonTargets);$(MSBuildThisFileDirectory)MyExtension.targets
  </CustomBeforeMicrosoftCommonTargets>
</PropertyGroup>

Wildcard Import Directories

MSBuild imports all files in extension directories, sorted alphabetically:

xml
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore\*"
    Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == 'true'
               and Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.props\ImportBefore')" />
Key paths
PropertyResolves toScope
$(MSBuildUserExtensionsPath)%APPDATA%\Microsoft\MSBuildPer-user
$(MSBuildExtensionsPath)MSBuild install directoryMachine-wide
$(MSBuildProjectExtensionsPath)obj/ directoryPer-project (NuGet)

Name files with numeric prefixes for ordering: 01-first.props, 02-second.props.

Import Gating — Control Properties

Every wildcard import is gated by a boolean property:

xml
<PropertyGroup>
  <ImportByWildcardBeforeMicrosoftCommonProps
      Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
  <ImportDirectoryBuildProps
      Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
</PropertyGroup>
Available control properties
PropertyWhat it disables
ImportDirectoryBuildPropsDirectory.Build.props auto-discovery
ImportDirectoryBuildTargetsDirectory.Build.targets auto-discovery
ImportProjectExtensionPropsNuGet-generated *.props in obj/
ImportProjectExtensionTargetsNuGet-generated *.targets in obj/
ImportByWildcardBefore*Machine-level ImportBefore extensions
ImportByWildcardAfter*Machine-level ImportAfter extensions

NuGet Package Build Extension Layout

NuGet packages inject build logic via build/ or buildTransitive/ folders:

text
MyPackage/
  build/
    MyPackage.props      ← imported via *.props wildcard
    MyPackage.targets    ← imported via *.targets wildcard
  buildTransitive/
    MyPackage.props      ← imported by transitive consumers
    MyPackage.targets
Rules
  • File names must match the package ID exactly.
  • build/ affects direct consumers only. buildTransitive/ affects the entire dependency chain.
  • Props are imported early (before the project), targets are imported late (after the project).

Source Tree vs Packed Layout

When reviewing a NuGet build-extension package, the source layout in the repository can legitimately differ from the packed layout inside the produced .nupkg. This is a common source of false-positive "import points at a missing file" findings.

Three packaging mechanisms reshape the layout at pack time:

  1. .nuspec <file src=… target=…> mappings — copy a single source file into multiple per-TFM targets:

    xml
    <!-- Source tree has ONE shared file:
           buildTransitive\common\MyAdapter.props
         Pack rewrites it to per-TFM targets inside the .nupkg:
           buildTransitive\net462\MyAdapter.props
           buildTransitive\net8.0\MyAdapter.props
           buildTransitive\net9.0\MyAdapter.props -->
    <files>
      <file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net462\MyAdapter.props" />
      <file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net8.0\MyAdapter.props" />
      <file src="buildTransitive\common\MyAdapter.props" target="buildTransitive\net9.0\MyAdapter.props" />
    </files>

    In the <file> element, a target ending in \ is treated as a folder (filename preserved from src); a target ending in a filename renames the file.

  2. .csproj <PackagePath> metadata on <None Update=…> or <Content Include=…> items — same effect via SDK pack. Use one item per destination to keep the mapping unambiguous:

    xml
    <ItemGroup>
      <None Include="buildTransitive\common\MyAdapter.props" Pack="true" PackagePath="buildTransitive\net8.0\MyAdapter.props" />
      <None Include="buildTransitive\common\MyAdapter.props" Pack="true" PackagePath="buildTransitive\net9.0\MyAdapter.props" />
    </ItemGroup>

    NuGet/SDK pack also accepts a semicolon-separated list (PackagePath="buildTransitive\net8.0\;buildTransitive\net9.0\") to fan one source out to multiple destinations, but the multi-item form above is harder to misread.

  3. SDK conventions — IncludeBuildOutput, BuildOutputTargetFolder, IncludeContentInPack automatically place built outputs under lib/<tfm>/ or build/<tfm>/.

Show full SKILL.md (237 more words)Show less
Implication for reviewers

A forwarder like the following inside a packed build/net462/ folder is not a "missing-file" bug, even if the source tree has no buildTransitive/net462/ directory:

xml
<!-- In packed build/net462/MyAdapter.props -->
<Project>
  <Import Project="$(MSBuildThisFileDirectory)..\..\buildTransitive\net462\MyAdapter.props" />
</Project>

Before flagging an unguarded <Import> inside a build/<tfm>/ or buildTransitive/<tfm>/ folder:

  1. Look for *.nuspec in the project directory and its immediate parent directory (do not walk further up). Read every <file target=…> whose target matches the imported path.
  2. Read the .csproj for <PackagePath> metadata on <None>/<Content> items.
  3. Only flag the import if the target path is missing from both the source tree and the projected package layout.

See also msbuild-antipatterns AP-13 ("NuGet package forwarders" exception).

Import Guard Pattern

The .targets file ensures .props was imported using a guard property:

xml
<!-- End of Microsoft.Common.props -->
<PropertyGroup>
  <MicrosoftCommonPropsHasBeenImported>true</MicrosoftCommonPropsHasBeenImported>
</PropertyGroup>

<!-- Top of Microsoft.Common.CurrentVersion.targets -->
<Import Project="Microsoft.Common.props"
    Condition="'$(MicrosoftCommonPropsHasBeenImported)' != 'true'" />

This handles projects that only import .targets.

Directory.Build Discovery

MSBuild walks up the directory tree to find the nearest Directory.Build.props:

xml
<_DirectoryBuildPropsBasePath>
  $([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildProjectDirectory)', 'Directory.Build.props'))
</_DirectoryBuildPropsBasePath>

Only the nearest file is discovered. Nested hierarchies must explicitly import parents:

xml
<!-- src/Directory.Build.props -->
<PropertyGroup>
  <_ParentPropsPath>$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))</_ParentPropsPath>
</PropertyGroup>
<Import Project="$(_ParentPropsPath)" Condition="'$(_ParentPropsPath)' != ''" />

Creating Your Own Extension Point

xml
<!-- MySDK.targets -->
<Project>
  <Import Project="MySDK.props" Condition="'$(MySDKPropsImported)' != 'true'" />

  <PropertyGroup>
    <CustomBeforeMySDK Condition="'$(CustomBeforeMySDK)' == ''">$(MSBuildProjectDirectory)\MySDK.Before.targets</CustomBeforeMySDK>
    <CustomAfterMySDK Condition="'$(CustomAfterMySDK)' == ''">$(MSBuildProjectDirectory)\MySDK.After.targets</CustomAfterMySDK>
  </PropertyGroup>

  <Import Project="$(CustomBeforeMySDK)" Condition="Exists('$(CustomBeforeMySDK)')" />

  <PropertyGroup>
    <MySDKBuildDependsOn>BeforeMySDKBuild;CoreMySDKBuild;AfterMySDKBuild</MySDKBuildDependsOn>
  </PropertyGroup>
  <Target Name="MySDKBuild" DependsOnTargets="$(MySDKBuildDependsOn)" />
  <Target Name="BeforeMySDKBuild" />
  <Target Name="AfterMySDKBuild" />
  <Target Name="CoreMySDKBuild">
    <!-- implementation -->
  </Target>

  <Import Project="$(CustomAfterMySDK)" Condition="Exists('$(CustomAfterMySDK)')" />
</Project>

Common Pitfalls

  • Missing Exists() on optional imports causes build failures when files are absent. Exception: imports inside published build/<tfm>/ and buildTransitive/<tfm>/ folders of a NuGet package are a package contract — the target is guaranteed by the packed layout (see "Source Tree vs Packed Layout" above). Don't guard them and don't flag them.
  • Overwriting Custom properties* drops prior hooks. Append with ; separator.
  • NuGet package file names not matching package ID silently skips the import.
  • Nested Directory.Build.props without parent import loses repo-root settings.

© microsoft, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/extension-points of microsoft/testfx.

Open the folder on GitHubat commit 44b9dcc

Compare with similar skills

Extension Points next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Extension Points compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Extension Points this skillmicrosoft/testfx1k—~2.6kAutomated safety check: PassMIT
Monitor CInrwl/nx29k5 repos~4.7kAutomated safety check: PassMIT
Terraform and OpenTofu Guideagentscope-ai/QwenPaw35k6 repos~4.2kAutomated safety check: PassApache-2.0
Analyze GitHub Action Logswithastro/astro63k1 repos~1.3kAutomated safety check: PassCustom licence
Azure Pipelines Log Downloaderansible/ansible71k—~825Automated safety check: PassGPL-3.0
GitHub Actions Templatesbartstc/vite-ts-react-template12213 repos~1.9kAutomated safety check: PassMIT

Similar skills

  • Monitor CI

    nrwl/nx

    Monitor Nx Cloud CI pipeline and handle self-healing fixes. An agent skill from nrwl/nx.

    29k GitHub starsUsed in 5 repos~4.7k tokens
    DevOps & CloudAuto-check passed
  • Terraform and OpenTofu Guide

    agentscope-ai/QwenPaw

    Guidance for writing and testing Terraform and OpenTofu code: module structure, naming, test approaches, CI/CD workflows, state handling and security scanning.

    35k GitHub starsUsed in 6 repos~4.2k tokens
    DevOps & CloudAuto-check passed
  • Official

    Analyze recent GitHub Actions workflow runs to identify patterns, mistakes, and improvements.

    63k GitHub starsUsed in 1 repo~1.3k tokens
    DevOps & CloudAuto-check passed
  • Downloads Azure Pipelines CI logs for an Ansible pull request or build so the agent can analyze test failures, after asking you first.

    71k GitHub stars~825 tokensUpdated today
    DevOps & CloudAuto-check passed
  • GitHub Actions Templates

    bartstc/vite-ts-react-template

    Create production-ready GitHub Actions workflows for automated testing, building, and deploying applications.

    122 GitHub starsUsed in 13 repos~1.9k tokens
    DevOps & CloudAuto-check passed
  • Nushell

    ccusage/ccusage

    Guides ccusage Nushell scripts. An agent skill from ccusage/ccusage.

    19k GitHub stars~938 tokensUpdated today
    DevOps & CloudAuto-check passed

More from microsoft/testfx

All 44 skills in this repo
  • Official

    Guide for organizing MSBuild infrastructure with Directory.Build.props, Directory.Build.targets, Directory.Packages.props, and Directory.Build.rsp.

    1k GitHub starsUsed in 1 repo~2.5k tokens
    Auto-check passed
  • Binlog Failure Analysis

    microsoft/testfx

    Official

    Analyze MSBuild binary logs to diagnose build failures. An agent skill from microsoft/testfx.

    1k GitHub starsUsed in 3 repos~730 tokens
    Auto-check passed
  • Coverage Analysis

    microsoft/testfx

    Official

    Project-wide code coverage and CRAP (Change Risk Anti-Patterns) score analysis for .NET projects.

    1k GitHub stars~7.3k tokensUpdated today
    Auto-check passed
  • Incremental Build

    microsoft/testfx

    Official

    Guide for optimizing MSBuild incremental builds. An agent skill from microsoft/testfx.

    1k GitHub starsUsed in 3 repos~3.7k tokens
    Auto-check passed
  • Msbuild Modernization

    microsoft/testfx

    Official

    Guide for modernizing and migrating MSBuild project files to SDK-style format.

    1k GitHub starsUsed in 3 repos~4.3k tokens
    Auto-check passed
  • Msbuild Antipatterns

    microsoft/testfx

    Official

    Catalog of MSBuild anti-patterns with detection rules and fix recipes.

    1k GitHub stars~4.5k tokensUpdated today
    Auto-check passed

Categories

Questions about Extension Points

What does Extension Points do?

Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout…. Extension Points is an agent skill from microsoft/testfx, published by the product's own GitHub organization. Guide for MSBuild extensibility: CustomBefore/CustomAfter hooks, wildcard imports with alphabetic ordering, import gating with control properties, NuGet package build extension layout (build/buildTransitive), and the MicrosoftCommonPropsHasBeenImported guard.

When should I use Extension Points?

Extension Points fits situations like: : diagnosing and fixing MSBuild import and hook patterns; reviewing and fixing extension point anti-patterns in Directory.Build files; fixing missing Exists() guards on imports that break fresh clones; fixing NuGet package hooks being silently dropped instead of appended.

How do I install Extension Points in Claude Code?

Run `npx skills add microsoft/testfx --skill extension-points -a claude-code`. Or copy the skill folder (.agents/skills/extension-points in microsoft/testfx) into .claude/skills/extension-points in your project. Claude Code loads it when a task matches its description.

How do I install Extension Points in Codex?

Run `npx skills add microsoft/testfx --skill extension-points -a codex`. Or copy the skill folder (.agents/skills/extension-points in microsoft/testfx) into .agents/skills/extension-points in your project. Codex loads it when a task matches its description.

Can I use Extension Points in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add microsoft/testfx --skill extension-points -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/extension-points, .gemini/skills/extension-points, .github/skills/extension-points and .opencode/skills/extension-points in your project.

What does Extension Points need to run?

SKILL.md names no scripts, command-line tools or credentials: Extension Points is instructions for the agent only.

Does Extension Points access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Extension Points safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Extension Points use?

Extension Points is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Extension Points use?

About 2.6k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Extension Points?

Skills that share tags, products or a category with Extension Points: Monitor CI (nrwl/nx, 29k stars), Terraform and OpenTofu Guide (agentscope-ai/QwenPaw, 35k stars), Analyze GitHub Action Logs (withastro/astro, 63k stars) and Azure Pipelines Log Downloader (ansible/ansible, 71k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Extension Points?

microsoft (a GitHub organization, an official publisher) maintains it in microsoft/testfx, which has 1,047 GitHub stars. The repository holds 44 skills in this directory. The repository was last updated on October 7, 2026.

Source: microsoft/testfx on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.