Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

FrenchExDev: A .NET & PowerShell Development Ecosystem

FrenchExDev

Personal monorepo for developer tools, libraries, and infrastructure — 20 top-level projects, 7 CLI binary wrappers, 3 DSL frameworks, and a philosophy that if the compiler can catch it, it should.

Overview

FrenchExDev is a monorepo built around one conviction: type safety scales better than conventions. Where most .NET ecosystems lean on runtime reflection, string templates, and hope, FrenchExDev pushes decisions to compile time through Roslyn source generators, sealed records, and explicit error types.

The ecosystem targets .NET 10 and PowerShell 7+, spanning functional error handling, CLI binary automation, document processing, AI-powered search, static analysis, and development infrastructure.

FrenchExDev/
├── Net/FrenchExDev/          .NET libraries, source generators, CLI tools
├── PoSh/FrenchExDev/         PowerShell modules & developer shell
├── Skills/                   Claude Code skill definitions
└── Doc/                      Cross-cutting documentation & assets

.NET Projects at a Glance

Project Description GitHub
Result Composable Result<T> / Result<T, TError> types — Map, Bind, Recover — replacing exceptions with explicit error values source
Builder Source-generator-powered async object construction with fluent API, validation, and circular reference detection source
BinaryWrapper Framework for generating type-safe C# APIs from CLI --help output via Roslyn, with multi-version support source
FiniteStateMachine Minimal generic FSM with enum-typed states, transitions, and async triggers source
Wrapper.Versioning Generic design pipeline for downloading, transforming, and saving versioned items from GitHub/GitLab APIs source
JsonSchema Design pipeline specialization for JSON schema versioned downloads and transformations source
Dsl M3 meta-metamodeling framework — 5 self-describing primitives for building any DSL source
Ddd Attribute-based DDD DSL built on Dsl — [AggregateRoot], [Entity], [ValueObject], [Command], [DomainEvent], [Invariant] source
Requirements Type-safe feature tracking DSL — requirements as abstract classes, ACs as abstract methods, compile-time traceability source
Diem Content Management Framework — 4 sub-DSLs (Content, Admin, Pages, Workflow) generating full-stack Blazor apps source
Vagrant Typed wrapper for HashiCorp Vagrant (2.4.3–2.4.9) with custom parser, event hierarchies, and result collectors source
Packer Typed wrapper for HashiCorp Packer — build, validate, init, fmt, inspect, plugins source
Podman Typed wrapper for Podman (58 versions, 180+ commands, 18 groups) — the largest BinaryWrapper consumer source
Podman Compose Typed wrapper for Podman Compose (Python CLI, argparse format, 6 versions) source
Docker Compose Typed wrapper for Docker Compose V2 (57 versions, 37 commands, channel-based scraping) source
GitLab.Cli Typed wrapper for glab GitLab CLI — BinaryWrapper consumer with custom Cobra parser source
Docker Typed wrapper for Docker CLI (in progress) source
Vos VM orchestration framework — up/halt/destroy/provision/snapshot across backends with configuration merging and group execution source
Vos.Alpine Alpine-specific VirtualBox configuration defaults for Vos — SATA SSD, nested virtualization, NIC promiscuous mode source
Vos.Alpine.DockerHost Docker-host machine type for Vos — combines Alpine base with Docker provisioning and shared folders source
Packer.Alpine Alpine Linux provisioning helpers for Packer — answer file generation, base setup, SSH keys, Alpine scripting source
Packer.Alpine.DockerHost Docker provisioning plugin for Alpine Packer builds — installs Docker, docker-compose, openrc services source
Doc2Pdf Document converter (DOCX, XLSX, PPTX, RTF, TXT to PDF) — library + CLI tool source
DockAi AI-powered document indexing & search — Lucene.Net, multi-LLM (Claude, OpenAI, Ollama), Blazor viewer source
QualityGate Static analysis & quality metrics — Roslyn semantic analysis, complexity, coupling, mutation, CI/CD gates source
HttpClient HTTP abstraction + testing fakes — IHttpClient interface, FakeHttpClient, fluent response builders source
Alpine.Version Alpine Linux version discovery — CDN querier, version comparison, architecture/flavor filtering, checksums source

.NET Libraries

Result (FrenchExDev.Net.Result)

Explicit success/failure values instead of exceptions for control flow. Three sealed record variants — Result, Result<T>, Result<T, TError> — with composable operations that chain naturally:

Result<User> result = await GetUserAsync(id)
    .Map(user => user with { LastLogin = DateTime.UtcNow })
    .Bind(user => ValidateUser(user))
    .Recover(err => Log.Error(err, "Validation failed"));

Map transforms success values. Bind chains operations that themselves return Results. Recover handles failures. Tap and Ensure round out the pipeline. Every operation preserves the error channel — no silent swallowing, no thrown exceptions, no ambiguity about what went wrong.

See the dedicated article for the full API and design rationale.

Builder (FrenchExDev.Net.Builder)

Source-generator-powered async object construction for domain models that need validation, async resolution, or circular reference detection before they can be instantiated.

[Builder]
public partial record Order
{
    public required string CustomerId { get; init; }
    public required List<OrderItem> Items { get; init; }
    public required decimal Total { get; init; }
}

[Builder]
public partial record OrderItem 
{
    public required int Quantity { get; init; }
    public required int Currency { get; init; }
    public required double Price { get; init; }
    public required string Sku { get; init; }
}

// Generated: OrderBuilder with fluent API, validation hooks, and async Build()
var order = await new OrderBuilder()
    .WithCustomerId("C-42")
    .WithItems(items => items
        .Add(i => i.WithSku("WIDGET-01").WithQuantity(3))
        .Add(i => i.WithSku("GADGET-07").WithQuantity(1)))
    .BuildAsync(ct);

The [Builder] attribute triggers a Roslyn incremental generator that emits fluent With*() methods, per-property validation overrides, BuilderList<T, TBuilder> for nested collections, DictionaryBuilder<K, V>, and thread-safe single-flight caching via SemaphoreSlim. Two modes: simple construction, or exception-aware with [Builder(Exception = typeof(...))] for builders that surface typed build errors.

See the dedicated article for the full deep-dive.

BinaryWrapper (FrenchExDev.Net.BinaryWrapper)

The flagship project. Turns any CLI binary into a fully typed C# API by scraping --help output, serializing command trees to JSON, and feeding them to a Roslyn source generator that emits command classes, fluent builders, and typed clients — with multi-version awareness and runtime version guards.

[BinaryWrapper("packer", FlagPrefix = "-", FlagValueSeparator = "=", UseBoolEqualsFormat = true)]
public partial class PackerDescriptor;  // 6 lines. The generator does the rest.

// At runtime: full IntelliSense, every flag is a typed property
var cmd = client.Build(b => b
    .WithForce(true)
    .WithParallelBuilds(4)
    .WithVar(["region=us-east-1"])
    .WithTemplate("template.pkr.hcl"));

The three-phase architecture (design-time scraping, build-time generation, runtime execution) means each concern evolves independently. Five pluggable help parsers cover GNU, cobra, argparse, and custom formats. VersionDiffer.Merge() computes [SinceVersion]/[UntilVersion] annotations across all scraped versions, and generated builders inject VersionGuard checks that throw clear exceptions instead of letting a wrong flag silently fail in stderr.

See the dedicated article for the full deep-dive.

Wrapper.Versioning (FrenchExDev.Net.Wrapper.Versioning)

A generic design pipeline framework for downloading, transforming, and saving versioned items from GitHub and GitLab API endpoints. Handles pagination (via Link header parsing), rate limiting, filtering, and parallel processing through System.Threading.Channels.

await new DesignPipelineRunner<string>
{
    ItemCollector = new GitHubReleasesVersionCollector("containers", "podman"),
    Pipeline = new DesignPipeline<string>()
        .UseHttpDownload(v => $"https://example.com/{v}/schema.json")
        .UseContentTransform((v, content) => Minify(content))
        .UseSave()
        .Build(),
    KeySelector = v => v,
    OutputDir = "output/",
}.RunAsync(["--parallel", "4", "--min-version", "4.0.0"]);

Provides the foundation for BinaryWrapper's design-time scraping and JsonSchema's schema downloading. CLI flags: --parallel, --min-version, --missing, --dashboard.

JsonSchema (FrenchExDev.Net.JsonSchema)

A specialization of Wrapper.Versioning for downloading and processing versioned JSON schemas. Used by Docker Compose Bundle to fetch all 32 Compose specification schema versions from GitHub, transform them, and generate typed C# models.

See the Docker Compose Bundle article for the full pipeline.

Finite State Machine (FrenchExDev.Net.FiniteStateMachine)

Production-grade async FSM framework with three tiers — Dynamic (string-based), Typed (enum-based), and Rich (OOP with computed transitions). Guards, entry/exit actions, hierarchical states, deferred events, parallel regions, timers, graph introspection, and Mermaid export:

var fsm = new FiniteStateMachine<OrderState, OrderEvent>(OrderState.Pending)
    .AddTransition(OrderState.Pending, OrderEvent.Confirm, OrderState.Confirmed)
    .AddTransition(OrderState.Confirmed, OrderEvent.Ship, OrderState.Shipped);

await fsm.TriggerAsync(OrderEvent.Confirm);
// State is now OrderState.Confirmed

All three tiers return Result<Transition<TState>> — no exceptions for denied transitions.

See the dedicated article for the full architecture including source generation, testing utilities, and model-based path generation.

Dsl — Meta-Metamodel (FrenchExDev.Net.Dsl)

The M3 foundation that all DSLs are built from. Five self-describing primitives — [MetaConcept], [MetaProperty], [MetaReference], [MetaConstraint], [MetaInherits] — equivalent to OMG MOF / Eclipse EMF Ecore but native C# with compile-time validation.

// M3 primitives define themselves (fixed point)
[MetaConcept("MetaConcept")]
public sealed class MetaConceptAttribute : Attribute
{
    [MetaProperty("Name", Required = true)]
    public string Name { get; }
}

Behavioral companions extend a MetaConcept base class with constraint methods (type-safe validation, not string expressions). A Roslyn incremental source generator auto-discovers all concepts and emits a MetamodelRegistry. Zero external dependencies — this is the root of the DSL dependency graph.

See the dedicated article for the full M0–M3 architecture.

Ddd — Domain-Driven Design DSL (FrenchExDev.Net.Ddd)

An M2 DSL built on Dsl for implementing DDD tactical patterns. Developers annotate partial classes with 13 attributes — [AggregateRoot], [Entity], [ValueObject], [Command], [DomainEvent], [Property], [Composition], [Association], [Invariant], and more — and a Roslyn source generator validates against metamodel constraints and produces entity implementations, builders, EF Core configurations, repositories, and CQRS handlers.

[AggregateRoot]
public partial class Order
{
    [Property("CustomerId", Required = true)]
    public partial string CustomerId { get; }

    [Composition(typeof(OrderLine))]
    public partial IReadOnlyList<OrderLine> Lines { get; }

    [Invariant]
    public Result<Order, InvalidOperationException> MustHaveAtLeastOneLine()
    {
        if (!Lines.Any())
            return Result<Order, InvalidOperationException>.Failure(
                new InvalidOperationException("Order must have at least one line"));
        return Result<Order, InvalidOperationException>.Success(this);
    }
}

See the dedicated article for DDD patterns and the CMF article for the full-stack code generation pipeline.

Requirements — Feature Tracking DSL (FrenchExDev.Net.Requirements)

An M2 DSL built on Dsl for type-safe requirement traceability. Requirements are abstract classes. Acceptance criteria are abstract methods. The compiler refuses to build until every AC has a specification, an implementation, and a test — all linked by typeof() and nameof(), not strings.

public abstract class UserRoleManagement : Feature<EnterprisePlatform>
{
    public abstract AcceptanceCriterionResult AdminCanAssignRoles(UserId admin, RoleId role);
    public abstract AcceptanceCriterionResult UserCanViewOwnRoles(UserId user);
}

// Specification: compiler enforces method signatures match the ACs
[ForRequirement(typeof(UserRoleManagement))]
public interface IRoleAssignmentSpec
{
    Result<User, DomainException> AssignRole(UserId admin, RoleId role);
}

A Roslyn source generator emits a RequirementRegistry, and analyzers report diagnostics (REQ100–REQ302) for missing specs, unlinked implementations, and untested ACs.

See the dedicated article for the full chain from feature definition to end-to-end testing.

Diem — Content Management Framework (FrenchExDev.Net.Diem)

A 62-project CMF delivering four sub-DSLs on top of Dsl and Ddd: Content (parts, blocks, StreamFields), Admin (auto-generated CRUD lists, forms, actions), Pages (layouts, zones, widgets, dynamic routing), and Workflow (state machines, gates, scheduling, per-locale progress). From attribute-decorated models, the compiler produces Blazor components, EF Core persistence, REST + GraphQL APIs, and a workflow engine.

Draws from Orchard Core (content parts), Wagtail (StreamFields), Drupal (versioning/taxonomy), and the original 2010 PHP Diem project (page composition model). Includes a cmf CLI for scaffolding, generation, validation, and migration.

See the dedicated article for the full architecture.

CLI Binary Wrappers

Built on BinaryWrapper, each wrapper is a production consumer that demonstrates the framework against real-world CLI tools with different help formats, versioning schemes, and output patterns.

Binary Versions Scraped Commands Parser Notes
Podman 58 (4.1.0–5.8.1) 180+ CobraHelpParser 386 generated files, 18 command groups
Docker Compose 57 (2.20.0–5.1.0) 37 CobraHelpParser Channel-based parallel scraping
Vagrant 7 (2.4.3–2.4.9) 30+ VagrantHelpParser (custom) Typed events, machine-readable output
Packer Multiple 8 groups PackerHelpParser (custom) HCL template workflows
Podman Compose 6 (1.1.0–1.5.0) 25 ArgparseHelpParser Python CLI (argparse format)
GitLab CLI (glab) 2+ (1.46.1–1.47.0) 50+ GlabHelpParser (custom) GitLab API version discovery, Alpine container scraping
Docker In progress CobraHelpParser Structure in place

Podman is the largest: 58 scraped versions producing 192 command classes and 192 builders across Container, Image, Network, Volume, Pod, System, Machine, Secret, and Manifest groups.

Vagrant is the most interesting architecturally: a custom help parser for Vagrant's unusual Common commands: / Available subcommands: format, typed event hierarchies (MachineOutput, ProvisionerOutput, ActionCompleted), result collectors (VagrantUpCollectorVagrantUpResult), and edge-case handling for version-specific bugs.

Infrastructure

Vos (FrenchExDev.Net.Vos)

VM orchestration framework for managing virtual machine instances across backends. Supports up, halt, destroy, provision, snapshot operations with configuration merging, group execution, and a contributor-based extensibility model.

var machine = vos.Machine("dev-docker-host")
    .WithType<AlpineDockerHost>()
    .WithContributors(alpine, docker, sharedFolders);

await machine.UpAsync(ct);
await machine.ProvisionAsync(ct);

Machines are composed from typed contributors that layer configuration: Vos.Alpine provides VirtualBox defaults (SATA SSD, nested virtualization, NIC promiscuous mode, Vagrant plugins), and Vos.Alpine.DockerHost adds Docker provisioning (installation, docker-compose, openrc services, shared folder setup).

Packer.Alpine (FrenchExDev.Net.Packer.Alpine)

Alpine Linux provisioning helpers for Packer builds. Provides answer file generation for unattended Alpine installs, base system setup scripts, Vagrant SSH key injection, and Alpine-specific provisioning steps.

Packer.Alpine.DockerHost extends this with Docker provisioning: installs Docker daemon, docker-compose, configures openrc services, and sets up the host as a ready-to-use Docker environment — producing golden VM images that Vos can instantiate.

Together with the Packer BinaryWrapper, this forms a type-safe image building pipeline: Packer (typed CLI) → Packer.Alpine (OS config) → Packer.Alpine.DockerHost (Docker layer) → VM image.

Applications

Doc2Pdf (FrenchExDev.Net.Doc2Pdf)

Document format converter — DOCX, XLSX, PPTX, RTF, TXT to PDF — available as both a library and a CLI tool. Built on OpenXml, ClosedXML, and PdfPig, with high-fidelity layout preservation and batch processing support.

DockAi (FrenchExDev.Net.DockAi)

AI-powered document indexing and search, split into two components:

  • DockAi.Api — Web API for document ingestion, full-text search (Lucene.Net with faceting, suggestions, and highlighting), entity extraction, and ontology generation. Multi-provider LLM support: Claude, OpenAI, Ollama.
  • DockAi.Viewer — Blazor-based document renderer for viewing and navigating indexed corpora.

Parses DOCX, XLSX, CSV, Markdown, HTML, and PDF. Designed for legal document corpus analysis with narrative extraction.

QualityGate (FrenchExDev.Net.QualityGate)

Static analysis and quality metrics tool that integrates with Roslyn's MSBuildWorkspace for full semantic analysis. Computes cyclomatic complexity, cognitive complexity, class coupling, inheritance depth, maintainability index, LCOM, code duplication, and test quality. Ingests XPlat Code Coverage (Cobertura) and Stryker mutation reports. Outputs JSON + HTML reports with CI/CD exit codes.

dotnet quality-gate analyze --solution MyApp.sln
dotnet quality-gate check --gate quality-gate.yml

Configuration via YAML quality gates and coverage.runsettings. Runs as a .NET local tool.

See the dedicated article for the full architecture.

HttpClient (FrenchExDev.Net.HttpClient)

Minimal HTTP abstraction providing a single IHttpClient interface for dependency injection and testability. The runtime implementation wraps System.Net.Http.HttpClient; the testing package provides FakeHttpClient for deterministic unit tests.

// Production: inject the abstraction
public class MyService(IHttpClient client)
{
    public async Task<string> FetchAsync(string url)
    {
        var response = await client.GetAsync(url);
        return await response.Content.ReadAsStringAsync();
    }
}

// Testing: sequential fake responses, no network
var fake = new FakeHttpClient(
    new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("data") },
    new HttpResponseMessage(HttpStatusCode.NotFound));

// Or fluent builder
var client = await new FakeHttpClientBuilder()
    .Response(r => r.Ok().Content("first"))
    .Response(r => r.StatusCode(HttpStatusCode.BadRequest).Content("error"))
    .BuildAsync();

The testing package (FrenchExDev.Net.HttpClient.Testing) includes FakeHttpClient, FakeHttpClientBuilder, and HttpResponseMessageBuilder — all implementing IBuilder<T> from the Builder library. Used as the HTTP seam across the ecosystem: Alpine.Version, BinaryWrapper design-time scrapers, and DockAi all depend on this interface.

Alpine.Version (FrenchExDev.Net.Alpine.Version)

Alpine Linux version discovery and comparison library. Queries the Alpine CDN to find available releases, architectures, and flavors — with filtering, checksums, and version range support.

var searcher = new AlpineVersionSearcher(httpClient);
var results = await searcher.SearchAsync(filters => filters
    .WithMinimumVersion("3.18.0")
    .WithArchitectures(new[] { AlpineArchitectures.x86_64 })
    .WithFlavors(new[] { AlpineFlavors.MiniRootFs })
    .WithRc(false));

foreach (var record in results)
    Console.WriteLine($"{record.Version} {record.Architecture} — {record.Sha256}");

Key types: AlpineVersion (immutable, IComparable<T>, handles edge releases and RCs), AlpineVersionSearcher (CDN querier with Parallel.ForEachAsync), and AlpineVersionSearchingFiltersBuilder (fluent filter configuration). Each result record includes version, architecture, flavor, download URL, SHA256, and SHA512.

Used by the BinaryWrapper container-based scraping infrastructure: when scraping CLI help from tools like glab or podman, Alpine.Version finds the right base image version to build scraping containers from.

PowerShell Modules

30+ PowerShell 7 modules organized by domain — the automation layer of the ecosystem.

Developer Shell

Module Cmdlets Description
DevPoSh 6 Developer shell profile — UTF-8, structured logging, module auto-loading, VS Code integration
DevSetup 5 Machine provisioning — installs .NET, PowerShell packages, and tooling
DevBash Bash integration helpers for cross-platform scripts
System 8 System utilities — admin checks, JSON conversion, hosts file editing
Env 4 .env file management — read, import, update environment variables
VsCode 4 VS Code workspace management — multi-root configurations, extensions

Source Control & CI

Module Cmdlets Description
Git 3 Git status across multiple repos, batch operations
GitHub 1 GitHub repo creation and management
GitLab.DockerCompose 6 GitLab CE Docker Compose deployment — runner, registry, volumes

Container & Orchestration

Module Cmdlets Description
Docker 4 Docker context management — create, switch, remove
DockerCompose 20 Full Docker Compose lifecycle — build, up, down, exec, logs, 20 commands
Kubernetes Kubernetes cluster management utilities
Traefik.DockerCompose 11 Traefik reverse proxy — Docker Compose generation, TLS, routing
Traefik 1 Traefik TLS certificate configuration

VM & Image Building

Module Cmdlets Description
Packer 14 HashiCorp Packer lifecycle — build, validate, init, inspect, plugins
Packer.Alpine 3 Alpine Linux Packer templates — answer files, config objects
Packer.Alpine.Docker 2 Alpine Docker image builds via Packer
Packer.Alpine.Kubernetes 2 Alpine Kubernetes node images via Packer
Packer.Debian Debian image builds via Packer
Vagrant 3 Vagrant box management — list, status, SSH keys
Vagrant.Catalog 5 Vagrant box catalog — version management, metadata generation
VirtualBox 5 VirtualBox VM management — cleanup, guest additions, snapshots

Virtual Operating Systems (Vos)

Module Cmdlets Description
Vos 20 Core Vos framework — VM lifecycle, configuration, provisioning
Vos.Alpine 1 Alpine Linux Vos templates
Vos.Alpine.Docker 1 Alpine Docker-based Vos instances
Vos.Alpine.Kubernetes 5 Alpine Kubernetes Vos — jumpbox, machine config, cluster setup

Infrastructure Services

Module Cmdlets Description
LocalHost.HomeLab 1 Home lab orchestration — all services, all machines
EtcHosts 5 /etc/hosts management — add, remove, list entries
SshConfig 8 SSH config management — add hosts, copy keys, edit config
MkCert 2 Local TLS certificates via mkcert
Net 2 Network utilities — MAC address generation, validation
PiHole 3 Pi-hole DNS management — query, configure
PiHole.DockerCompose 1 Pi-hole Docker Compose deployment
Keycloak Keycloak identity management
Keycloak.DockerCompose 2 Keycloak Docker Compose deployment
Sonatype.Nexus.DockerCompose 2 Nexus artifact repository Docker Compose deployment

.NET & Build

Module Cmdlets Description
Dotnet 12 .NET project management — version bumping, solution cleanup, NuGet operations
Alpine Alpine Linux package and version discovery

AI & Productivity

Module Cmdlets Description
Claude Claude Code VM lifecycle — restart, reset, workflow automation
MyInfra Infrastructure-as-code orchestration for development environments
MyRoot.VsCode Root VS Code workspace and profile management

Development Practices

Central Package Management

All 57 .csproj files share a single Directory.Packages.props for unified dependency versions. No version drift, no "works on my machine" from mismatched transitive dependencies.

Interactive NuGet Updater (Update-Packages.ps1)

Custom PowerShell tool that queries NuGet APIs in parallel (4 concurrent by default), displays available updates in a live-updating table with animated spinners, and lets you selectively apply upgrades. Because dotnet outdated wasn't interactive enough.

Documentation Standards

Every major project includes doc/ARCHITECTURE.md (system design, data flow, mermaid diagrams), doc/HOW-TO.md (step-by-step procedures, troubleshooting), and selected projects carry doc/PHILOSOPHY.md for design rationale. A Claude Code skill (Skills/Net/Documentation/) enforces consistent documentation structure across all projects.

Quality Gates

Per-project quality-gate.yml configurations define thresholds for complexity, coupling, coverage, and mutation scores. The QualityGate tool integrates into CI/CD pipelines with pass/fail exit codes, closing the loop between code health metrics and merge gates.

Design Philosophy

Type safety over conventions. If the compiler can enforce it, it should. Source generators replace runtime reflection. Sealed records replace mutable classes. Explicit Result types replace thrown exceptions.

Composability over frameworks. Small, focused libraries with clear interfaces that compose through pipelines — Map, Bind, Recover for error handling; IOutputParserIResultCollector for process output; middleware chains for scraping pipelines.

Developer experience is a feature. IntelliSense for 200 CLI flags. Tab completion in PowerShell. Interactive package updates with live tables. If the tooling isn't pleasant to use, it won't get used.

Anything-as-code. Infrastructure, quality gates, documentation templates, development environments — if it can be versioned and automated, it is.


FrenchExDev is a personal initiative. Built with .NET 10, Roslyn source generators, PowerShell 7, and the conviction that the best abstractions are the ones the compiler understands.