YamlFrontMatter.TypeProvider
0.9.6
See the version list below for details.
dotnet add package YamlFrontMatter.TypeProvider --version 0.9.6
NuGet\Install-Package YamlFrontMatter.TypeProvider -Version 0.9.6
<PackageReference Include="YamlFrontMatter.TypeProvider" Version="0.9.6" />
<PackageVersion Include="YamlFrontMatter.TypeProvider" Version="0.9.6" />
<PackageReference Include="YamlFrontMatter.TypeProvider" />
paket add YamlFrontMatter.TypeProvider --version 0.9.6
#r "nuget: YamlFrontMatter.TypeProvider, 0.9.6"
#:package YamlFrontMatter.TypeProvider@0.9.6
#addin nuget:?package=YamlFrontMatter.TypeProvider&version=0.9.6
#tool nuget:?package=YamlFrontMatter.TypeProvider&version=0.9.6
YamlFrontMatter
Strongly-typed access to YAML front matter in Markdown files — via an F# Type Provider and a standalone parsing library.
The library is F#-first — all core types are idiomatic F# (discriminated unions, Map, option). C# consumers can use it directly via standard .NET interop; see the C# section below.
Point the Type Provider at a directory of Markdown files and get compile-time IntelliSense with property names, types, and nullability inferred automatically from the actual data.
Quick start
Install
dotnet add package YamlFrontMatter
dotnet add package YamlFrontMatter.TypeProvider
Use the Type Provider
Given a directory of Markdown files with YAML front matter:
---
name: my-skill
description: Does something useful.
version: "2.0"
active: true
priority: 42
tags: [fsharp, dotnet]
metadata:
author: Vladimir
revision: 3
---
Reference the provider with a static directory path:
open YamlFrontMatter
type Skills = FrontMatterProvider<"/path/to/skills">
for skill in Skills.GetAll() do
printfn "%s (v%s, active=%A)" skill.Name.Value skill.Version skill.Active
The provider scans the directory at compile time, infers a cross-file schema, and generates:
FrontMatterDefinition— an erased type with typed properties for every discovered YAML keyGetAll()— returnsseq<FrontMatterDefinition>by scanning the directory at runtimeDescribe()— returns the inferred schema as an F# record declaration (useful for code generation and documentation)
Use the Core library directly
open YamlFrontMatter.SchemaInference
open YamlFrontMatter.Scanner
// Infer schema from a directory
let report = discoverSchemaWithStats "/path/to/skills" "SKILL.md"
printfn "%s" (formatSchema report)
// Stream-scan files in parallel via System.Threading.Channels
let reader = scan scanOptions cancellationToken
// ... consume ChannelReader<Result<RawSkillData option, ScanError>>
See [examples/FSharpExample/](examples/FSharpExample/) for a complete working demo.
Use from C#
The core YamlFrontMatter library works from C# without any wrappers. F# modules compile as static classes, and types are accessible as nested types within those classes.
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;
using YamlFrontMatter;
using static YamlFrontMatter.Types;
using static YamlFrontMatter.Scanner;
using static YamlFrontMatter.SchemaInference;
Read a single file:
var path = AbsoluteFilePathModule.createUnsafe("/path/to/SKILL.md");
var result = Scanner.tryReadOne(path);
if (result.IsOk && FSharpOption<RawSkillData>.get_IsSome(result.ResultValue))
{
var skill = result.ResultValue.Value;
Console.WriteLine(skill.Path.Value);
// Extract typed values via pattern matching on YamlValue DU
var nameKey = YamlKey.NewYamlKey("name");
var name = MapModule.TryFind(nameKey, skill.Fields);
if (FSharpOption<YamlValue>.get_IsSome(name) && name.Value is YamlValue.YString s)
Console.WriteLine(s.Item);
}
Schema inference:
var report = SchemaInference.discoverSchemaWithStats("/path/to/skills", "SKILL.md");
Console.WriteLine($"Scanned {report.FilesScanned} files");
Console.WriteLine(SchemaInference.formatSchema(report));
Streaming scanner via Channels:
var options = new ScanOptions(
rootDirectory: AbsoluteFilePathModule.createUnsafe("/path/to/skills"),
pattern: "SKILL.md",
parallelism: 8,
pathQueueCapacity: 256,
resultQueueCapacity: 256);
var reader = Scanner.scan(options, CancellationToken.None);
while (reader.WaitToReadAsync().AsTask().GetAwaiter().GetResult())
{
while (reader.TryRead(out var item))
{
if (item.IsOk && FSharpOption<RawSkillData>.get_IsSome(item.ResultValue))
{
var skill = item.ResultValue.Value;
// process skill...
}
}
}
C# interop notes:
| F# type | C# access |
|---|---|
Module functions (Scanner.scan) |
Static methods on the module class |
Types in modules (RawSkillData) |
Nested types: Scanner.RawSkillData |
DU cases (YamlValue.YString) |
Subtypes for is/switch: value is YamlValue.YString s |
Single-case DU (YamlKey) |
Factory: YamlKey.NewYamlKey("x"), access: .Value |
F# Map<K,V> |
FSharpMap<K,V> — use MapModule.TryFind(key, map) |
F# option<T> |
FSharpOption<T> — check with FSharpOption<T>.get_IsSome(x) |
F# Result<T,E> |
FSharpResult<T,E> — check .IsOk / .IsError |
Companion modules (AbsoluteFilePath.createUnsafe) |
AbsoluteFilePathModule.createUnsafe(s) |
See [examples/CSharpExample/](examples/CSharpExample/) for a complete working demo.
How it works
Schema inference
The library reads YAML front matter from every matching file and builds a unified schema using a type-widening lattice:
| Narrowest | → | Widest |
|---|---|---|
bool |
int → float |
string |
- Fields present in all files are marked
PresentInAll = true - Nested YAML mappings become nested record types
- Lists are element-typed (
string list,int list, etc.) - Conflicting types across files are widened to the safest common type
Type Provider architecture
The provider follows the canonical two-project layout recommended by FSharp.TypeProviders.SDK:
| Component | NuGet path | Purpose |
|---|---|---|
YamlFrontMatter.TypeProvider.dll (Runtime) |
lib/netstandard2.0/ |
Runtime helpers + TypeProviderAssembly attribute |
YamlFrontMatter.TypeProvider.DesignTime.dll |
typeproviders/fsharp41/netstandard2.0/ |
Loaded by the F# compiler at design time |
All design-time dependencies (VYaml, etc.) are bundled alongside the design-time DLL and do not pollute the consumer's runtime closure beyond YamlFrontMatter.
Static parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
RootDirectory |
string |
(required) | Absolute path to the directory to scan |
Pattern |
string |
"SKILL.md" |
File name glob pattern |
Project structure
src/
YamlFrontMatter/ Core library: types, YAML parser, schema inference, parallel scanner
YamlFrontMatter.TypeProvider/ Runtime assembly (NuGet package entry point)
YamlFrontMatter.TypeProvider.DesignTime/ Design-time assembly (provider logic, loaded by F# compiler)
dotnet-yamlfm/ CLI tool (global dotnet tool) for scanning and schema inspection
tests/
YamlFrontMatter.Tests/ xUnit tests for schema inference and the type provider
examples/
Skills/ Shared sample SKILL.md fixtures for both examples
FSharpExample/ Idiomatic F# console app using the core library
CSharpExample/ C# console app demonstrating interop with the core library
Supported YAML types
| YAML value | Inferred F# type | Property type |
|---|---|---|
true / false |
bool |
bool option |
42, -7 |
int |
int option |
3.14 |
float |
float option |
"hello" |
string |
string option |
[a, b, c] |
string list |
string list option |
| nested mapping | generated record type | XxxData option |
The Name and Description fields are treated as required and exposed as SkillName / SkillDescription (single-case DU wrappers), not options.
CLI tool
Install as a global tool:
dotnet tool install -g dotnet-yamlfm
Then use:
# Dump every SKILL.md's parsed metadata (parallel streaming)
yamlfm /path/to/skills
# Print the inferred F# record type
yamlfm /path/to/skills --schema
Or run from source:
dotnet run --project src/dotnet-yamlfm -- /path/to/skills
dotnet run --project src/dotnet-yamlfm -- /path/to/skills --schema
Building
dotnet build
dotnet test
Versioning
Major and minor version are fixed in Directory.Build.props (VersionPrefix). The patch number is auto-incremented by CI using the GitHub Actions run number.
Publishing to NuGet
Publishing is done via GitHub Actions (workflow_dispatch):
- Go to Actions → Publish to NuGet
- Click Run workflow
- Optionally provide a version override
The workflow runs tests, packs YamlFrontMatter, YamlFrontMatter.TypeProvider, and dotnet-yamlfm (global tool), pushes to NuGet via Trusted Publishing (OIDC, no API keys needed), and creates a GitHub Release.
One-time setup:
- On nuget.org → Account → Trusted Publishing → create a policy:
- Repository owner:
V0v1kkk, Repository:YamlFrontMatter, Workflow:publish.yml
- In GitHub repository secrets, add
NUGET_USERwith your nuget.org profile name
Contributing
See AGENTS.md for repository structure, conventions, and contribution guidance — written for both AI coding assistants and human contributors.
This repo also bundles two custom AI-agent skills under .skills/:
.skills/fsharp-style/— F# coding-style guide capturing the conventions this codebase follows (single-case DU shape, active patterns, computation expressions, anti-patterns)..skills/fsharp-type-provider/— comprehensive F# Type Provider authoring guide: project layout, erased vs generative, packaging, debugging, common pitfalls. Includes deeper reference material underreferences/.
These skills are versioned with the source so any AI assistant that clones the repo (Claude Code, OpenAI Codex, etc.) gets the same opinionated guidance the maintainer's agent uses.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- FSharp.Core (>= 10.1.203)
- YamlFrontMatter (>= 0.9.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.