FsHotWatch 0.8.0-alpha.31

This is a prerelease version of FsHotWatch.
dotnet add package FsHotWatch --version 0.8.0-alpha.31
                    
NuGet\Install-Package FsHotWatch -Version 0.8.0-alpha.31
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FsHotWatch" Version="0.8.0-alpha.31" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FsHotWatch" Version="0.8.0-alpha.31" />
                    
Directory.Packages.props
<PackageReference Include="FsHotWatch" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FsHotWatch --version 0.8.0-alpha.31
                    
#r "nuget: FsHotWatch, 0.8.0-alpha.31"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FsHotWatch@0.8.0-alpha.31
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FsHotWatch&version=0.8.0-alpha.31&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FsHotWatch&version=0.8.0-alpha.31&prerelease
                    
Install as a Cake Tool

FsHotWatch

Trying to speed up the F# development feedback loop.

FsHotWatch is a background daemon that watches your source files and aims to keep the F# compiler warm, so saving a file re-checks just what changed and hands the results to your tools (linters, analyzers, test runners) — instead of each tool restarting the compiler from scratch every time.

Status: early alpha, and a lot of it is AI-written. It runs the author's own daily F# work, but behavior and APIs shift between versions, rough edges are expected, and your mileage may vary. The goal is a faster F# loop — it's still finding its shape, so issues and PRs are very welcome.

The problem

F# tools are slow because each one starts its own compiler from zero. A 15-project solution can take ~2 minutes to analyze. Every save restarts your linter, your analyzer, and your test runner — all re-parsing and type-checking the same hundreds of files again.

How it works

FsHotWatch runs one compiler in the background and shares it with all your tools:

  1. You save a file — FsHotWatch notices.
  2. It re-checks just that file using the already-warm compiler — ideally milliseconds rather than minutes.
  3. Plugins get the results instantly — your linter, analyzer, and test runner see the new check results without re-parsing.
  4. You query the resultsfshw check runs everything and reports what each tool found.

Saves are debounced: if 10 files change at once (a formatter sweeping the repo, say), FsHotWatch waits for things to settle and processes them in one batch.

Quick start

# Install the CLI
dotnet tool install -g FsHotWatch.Cli

# Run all checks. This auto-starts the daemon the first time —
# no separate "start" step needed. Verbose by default.
fshw check

# Prefer one line per plugin?
fshw check --compact   # or -q

fshw init writes a starter .fshw.json; see Configuration.

Commands

Command What it does
fshw check Run all configured checks and report findings. Auto-starts the daemon. --run-once runs without a daemon; -q/--compact for one line per plugin.
fshw status [plugin] Show the daemon's current status (optionally for one plugin).
fshw start Run the daemon in the foreground (Ctrl+C to stop). Optional — check/status start it for you.
fshw stop Stop the running daemon.
fshw format Format the code (Fantomas).
fshw test-rerun Rerun tests for an xUnit v3 --filter-class / --filter-trait slice.
fshw rerun <plugin> Force one plugin to re-run, clearing its cached state.
fshw init Generate a starter .fshw.json.
fshw config check Validate .fshw.json without starting the daemon.

Add -v for debug logging or -a for agent-friendly, parseable output. Run fshw --help for the full list.

Packages

FsHotWatch is split into small packages so you install only what you need:

Package What it does
FsHotWatch Core library — the daemon, file watcher, plugin system, IPC
FsHotWatch.Cli CLI tool — fshw check, start, stop, status, …
FsHotWatch.TestPrune Plugin: figures out which tests to run when code changes
FsHotWatch.Analyzers Plugin: runs F# analyzers (like G-Research or your own)
FsHotWatch.Lint Plugin: runs FSharpLint using the warm compiler's results
FsHotWatch.Fantomas Plugin: checks if your files are formatted with Fantomas
FsHotWatch.Build Plugin: runs dotnet build and emits BuildCompleted events
FsHotWatch.FileCommand Plugin: runs custom commands when specific files change
FsHotWatch.Coverage Plugin: checks per-file line/branch coverage thresholds after each test run

Configuration

Run fshw init to scaffold a .fshw.json in your repo root, or write one by hand. Every field is optional — sensible defaults apply when omitted.

{
  "build": {
    "command": "dotnet",
    "args": "build"
  },
  "format": true,
  "lint": true,
  "cache": "file",
  "tests": {
    "beforeRun": "dotnet build",
    "dependsOn": ["src/MyApp/Database/Migrations/**"],
    "projects": [
      {
        "project": "MyProject.Tests",
        "command": "dotnet",
        "args": "run --project tests/MyProject.Tests --no-build --",
        "filterTemplate": "--filter-class {classes}",
        "classJoin": " ",
        "group": "unit"
      }
    ]
  },
  "analyzers": {
    "paths": ["analyzers/"]
  },
  "fileCommands": [
    {
      "pattern": "*.fsx",
      "command": "dotnet",
      "args": "fsi --typecheck-only"
    }
  ],
  "coverage": {
    "configPath": "coverage-ratchet.json",
    "searchDir": "coverage"
  }
}

Reference

Field Type Default Description
build object \| bool {"command": "dotnet", "args": "build"} Build command. false disables.
format bool true Enable Fantomas format-on-save preprocessor.
lint bool true Enable FSharpLint plugin. Uses fsharplint.json if found.
cache string \| bool "file" Cache strategy: "none", "memory", or "file". ("jj" is a legacy alias for "file".)
tests object Test runner config. See below.
coverage object Coverage threshold checking.
analyzers object F# Analyzers SDK integration.
fileCommands array [] Custom commands triggered by file patterns.

For memory/idle-exit, FSEvents latency, and per-task timeout keys, see Memory & tuning.

build fields:

Field Type Default Description
command string "dotnet" Build command.
args string "build" Arguments to the build command.
buildTemplate string Template for incremental builds. {projects} is replaced with changed project paths.

tests fields:

Field Type Default Description
beforeRun string Command to run before each test run (e.g. "dotnet build").
dependsOn string[] [] Repo-root-relative globs (*, ?, **) naming external test inputs the symbol-diff can't see — DB migrations, generated files, schemas. Their content hash salts the test cache key, so editing a matched file forces a real test re-run even when no test source changed.
coverageDir string "coverage" Directory (repo-root-relative) for per-project Cobertura artifacts.
projects array [] List of test project configurations.

tests.projects[] fields:

Field Type Default Description
project string "unknown" Project name (used for filtering and display).
command string "dotnet" Test runner command.
args string "test --project <project>" Arguments to the test runner.
group string "default" Group name (for running subsets).
environment object {} Extra environment variables as "KEY": "VALUE" pairs.
filterTemplate string Template for class-based filtering. {classes} is replaced with affected test class names.
classJoin string " " Separator for joining class names in the filter.

analyzers fields:

Field Type Default Description
paths string[] Directories containing analyzer DLLs. Relative paths resolved from repo root.

fileCommands[] fields:

Field Type Default Description
pattern string "*.fsx" File extension pattern to match (e.g. "*.fsx", "*.sql").
command string "echo" Command to run when a matching file changes.
args string "" Arguments to the command.

coverage fields:

Field Type Default Description
configPath string "coverage-ratchet.json" Path to the coverage-ratchet thresholds file (relative to repo root or absolute).
searchDir string "." Directory tree to search for coverage.cobertura.xml files after each test run.

Cache directory

FsHotWatch keeps its check-result cache and the TestPrune database in .fshw/ at the repository root. Add it to your .gitignore:

.fshw/

What it does and doesn't cache. The on-disk cache stores FCS check results, keyed by file content — so a fresh daemon can replay unchanged files instead of re-checking them. It does not persist the compiler's in-memory warmth: FSharpChecker and its FCS caches are rebuilt from cold on every daemon start, so the first scan after a (re)start still pays that warm-up before the cached results start landing.

Writing plugins

Plugins are declarative update functions over a shared warm compiler: you define how your state reacts to events (file checked, build completed, tests finished), and the framework manages the agent, status, caching, and IPC. See Writing a plugin.

Memory & tuning

The daemon keeps the F# compiler warm, which costs memory. FsHotWatch ships with conservative defaults (aggressive GC, optional idle-exit) so this stays in check — see Memory & tuning if you want to adjust them.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on FsHotWatch:

Package Downloads
FsHotWatch.TestPrune

FsHotWatch plugin for TestPrune test impact analysis

FsHotWatch.Analyzers

FsHotWatch plugin for F# Analyzers SDK integration

FsHotWatch.Build

FsHotWatch plugin that runs dotnet build and emits BuildCompleted events

FsHotWatch.Coverage

FsHotWatch plugin that checks per-file coverage thresholds after test runs

FsHotWatch.Fantomas

FsHotWatch plugin for Fantomas format checking

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0-alpha.31 0 6/17/2026
0.8.0-alpha.30 33 6/16/2026
0.8.0-alpha.29 52 6/15/2026
0.8.0-alpha.28 60 6/12/2026
0.8.0-alpha.27 60 6/11/2026
0.8.0-alpha.26 56 6/10/2026
0.8.0-alpha.25 53 6/10/2026
0.8.0-alpha.24 86 6/8/2026
0.8.0-alpha.23 56 6/7/2026
0.8.0-alpha.22 60 6/7/2026
0.8.0-alpha.21 53 6/6/2026
0.8.0-alpha.20 54 6/5/2026
0.8.0-alpha.19 55 6/4/2026
0.8.0-alpha.18 51 6/4/2026
0.8.0-alpha.17 62 6/3/2026
0.8.0-alpha.16 60 6/2/2026
0.8.0-alpha.15 88 5/28/2026
0.8.0-alpha.14 60 5/26/2026
0.8.0-alpha.13 76 5/4/2026
0.8.0-alpha.12 74 4/29/2026
Loading failed