FrenchExDev: A .NET & PowerShell Development Ecosystem

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 & assetsFrenchExDev/
├── 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"));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);[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"));[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"]);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.Confirmedvar 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.ConfirmedAll 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; }
}// 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);
}
}[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);
}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 (VagrantUpCollector → VagrantUpResult), 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);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.ymldotnet quality-gate analyze --solution MyApp.sln
dotnet quality-gate check --gate quality-gate.ymlConfiguration 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();// 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}");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; IOutputParser → IResultCollector 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.