Husky.Net puts a task runner behind every git hook
Lint your commit messages, run tests, format or analyze code, and perform any other checks automatically when you commit or push. 🚀
At a glance
- What is it?
- Husky.Net is a .NET port of the JavaScript husky and lint-staged tooling that adds commit message linting, staged file handling and a built-in task runner to git hooks through core.hooksPath. It suits teams that want format-and-test checks to run locally, and suits less well teams that would rather keep every check in CI.
- Who is it for?
- Husky.Net is a sensible choice for a .NET team that wants lint, format and test steps to run at commit time on every developer machine, especially one working in a monorepo with several projects and one formatting tool already picked out. It is not the right choice for a team that treats CI as the only gate, or for a repository whose contributors are not all on a machine where a global .NET tool can be installed.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 176 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A hook system for teams that live in Visual Studio
The problem Husky.Net addresses is narrow and familiar: the checks you want to run before a commit lands, run automatically, on every machine, without a developer remembering to run them. The project describes it as linting commit messages, running tests, formatting or analyzing code, and performing any other check automatically when you commit or push.
What makes it interesting for .NET specifically is the shape of the configuration. The project says it was inspired by the JavaScript tools husky and lint-staged, and it brings two ideas across that boundary: a dev-dependency concept, where a project declares the tool versions its checks need, and an internal task runner, so hook actions are named steps rather than ad hoc shell lines. The feature list also names all Git and gitflow hooks, CSharp scripts, user-defined variables, custom directories, monorepo support and Git GUIs.
The audience is a .NET developer who already wants the pre-commit and pre-push discipline that the JavaScript ecosystem has had for years, and who does not want to hand-roll the same scripts in every repository. The tradeoff is that the tool takes over a piece of your local workflow, and the project is upfront that the work happens on your machine, at commit time, before anyone else sees the change.
core.hooksPath is the entire installation story
The list of features credits a modern Git feature for the setup, and that feature is core.hooksPath. Git can be pointed at a directory of hook scripts instead of the default .git/hooks location, and Husky.Net uses that as the foundation for installing and running its own hook files. There is no separate daemon, no background service, and no global state to clean up: the hooks are files in your repository, and the Git configuration entry is what makes Git find them.
That design has a practical consequence worth understanding before you commit to it. Because the hooks live in the repository, they are versioned, which means they can be reviewed in a pull request like any other change. But it also means the hook runs on whatever machine the contributor has, and the check that fires depends on the tool versions that machine can resolve.
The repository layout backs this up. The top level contains a .husky/ directory alongside src/, tests/, docs/, a .config/ folder, an .editorconfig, a .gitattributes and a Dockerfile. The src/ tree holds the actual Husky project, with a solution file named Husky.sln at the root, and a tests/ tree beside it. The presence of .husky/ at the top of the project's own repository is consistent with a tool that stores its hooks as repository files.
The internal task runner and its many file states
The internal task runner is the part that goes past what a plain hook script can do, and the feature list pairs it with support for multiple file states, naming staged, last-commit and git-files among them. That distinction matters more than it sounds. A hook that formats everything in the working tree will rewrite files that were never part of the commit. A hook that formats only the staged set changes exactly what the commit is about to contain, which is the behaviour a pre-commit formatter is supposed to have.
Supporting several file states means the same task can be reused in different places. Staged files are the natural input for a pre-commit check, the last commit is the natural input for a post-commit report, and a git-files mode covers everything Git knows about when you want a broader sweep. The project description does not reproduce the configuration syntax for choosing between them, so the getting started page in the documentation site is where the actual details live.
CSharp scripts, listed as csx support, extend the same idea. A hook action can be a C# script rather than a shell command, which means the logic can use .NET types and the .NET ecosystem directly. The trade-off is that this pushes hook execution toward needing a working .NET script runtime on every contributor machine, not just a .NET SDK.
The dev-dependency idea, imported from JavaScript
The dev-dependency concept is the most interesting import and the least explained one. In the JavaScript world, lint-staged lets a project pin the version of each linter it runs, so the check you get matches the check your team agreed on, and a contributor does not have to install the right global version by hand.
Husky.Net claims to bring that to .NET, and the repository layout shows how the analogy is being implemented. There is a package.json at the root of the repository, but its contents are for the documentation site rather than for the tool:
{
"name": "husky.net",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
},The devDependencies in that file are all VuePress packages such as @vuepress/bundler-vite, @vuepress/plugin-search and highlight.js. There is a yarn.lock alongside it and a .dockerignore, so the JavaScript toolchain in this repository exists to build the documentation site rather than to configure the hooks.
What the .NET side of the dev-dependency model looks like in practice is not spelled out anywhere in the repository listing, and that is the gap to probe first. Whether a project declares its pinned formatter and linter versions in a manifest file, in the project file, or in the hook configuration itself changes how much setup a new contributor has to do, and the documentation site is the only place that can answer it.
dotnet-format, CSharpier and ReSharper as hook targets
The compatibility list is the most concrete part of the feature set. Husky.Net is stated to be compatible with dotnet-format, CSharpier, the ReSharper command line tools, and other formatting tools. The important word there is compatible rather than bundled: Husky.Net does not reimplement formatting, it invokes tools you already trust and passes them the right set of files.
That makes the file-state machinery from the previous section the load-bearing part of the whole design. A formatter that rewrites the entire tree during a pre-commit hook will produce a diff unrelated to the commit, and the developer will learn to bypass the hook or to stage unrelated changes. Passing the staged set only is what makes the hook safe to leave enabled, and it is why the tool is not just a convenience wrapper around a shell script.
The ReSharper command line tools entry also widens the audience beyond open source tooling, since a commercial static analysis tool can be driven from a hook. The cost of that flexibility is that Husky.Net inherits whatever each target tool needs: a .NET SDK for dotnet-format, a separately installed executable for the ReSharper command line tools, and a separate distribution channel for CSharpier. The feature list does not describe how these are discovered on a contributor's machine, which is exactly the kind of detail that turns into a broken hook on a new machine.
Monorepos, custom directories and hook placement
Two features on the list exist for larger repositories, and both are structural. Monorepo support matters because a repository with several .NET projects has a different shape of change: a commit might touch files in five directories, and a hook needs to know which project a changed file belongs to before it can pick a task. Custom directories matters because some teams do not want the standard layout, and because a tool that insists on one directory name stops being usable in a repository that already has conventions.
The repository's own tree is a reasonable model of the target case. src/ contains the Husky project itself, tests/ sits beside it, docs/ holds the VuePress site that the package.json scripts build, and a .config/ directory sits at the root. The .husky/ folder gives the hooks their own home rather than mixing them with application code. A repository shaped this way is precisely the case where a generic per-file formatter hook would be wrong, and where a task runner that can scope work to a project is earning its keep.
The Git GUI entry matters for a practical reason: several desktop Git clients have their own ideas about how hooks are configured, and a tool that only works from the command line can be a reason for a teammate to keep the hooks disabled. No list of supported clients is given, so that claim needs verification before you rely on it.
What the hooks cost you, and when to skip it
Local hooks are a genuine trade-off and not a free win. A pre-commit check that formats, analyzes and tests turns a commit from an instant operation into a slow one, and on a large solution that delay is felt on every commit by every developer. The standard mitigation is to keep the pre-commit step to fast work, formatting and linting, and leave the slow test run to the pre-push hook or to CI, and the support for all Git and gitflow hooks means you can spread the work that way rather than doing everything at commit time.
The deeper cost is trust. Once a hook can block a commit, contributors learn to bypass it, and a bypassed hook is worse than no hook because it still looks like a control. If your team already runs format, analysis and test steps on the server and treats CI as the authoritative gate, Husky.Net adds a second, faster feedback loop but also a second place where a check can fail for environment reasons rather than code reasons.
The clearest case against it is a repository with many occasional contributors. A hook that depends on a global tool being installed, on a .NET SDK of the right version, and on third-party executables being present will fail in ways that have nothing to do with the change being committed. The alternative approach, and the one Husky.Net is explicitly modelled on, is to do the same work in a pipeline that has a fixed, reproducible environment, and keep the local step to fast feedback.
Release cadence and the state of the repository
The release history is short and tells you where the project stands. The listed versions are v0.9.0 on 2026-03-11, v0.9.1 on 2026-03-13, and v0.8.0 on 2025-11-12. The last push to the repository was on 2026-04-30, and the repository is not archived. The version numbering is still below 1.0, so treat the configuration format as something that may still move.
The build setup gives a hint about how the project is tested. The Dockerfile builds from the official.NET SDK image tagged 10.0, installs Git in the final stage, packs a NuGet package with a test version, and installs it as a global tool from the local package source. The relevant lines are:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env
ENV HUSKY=0
RUN dotnet restore /app/src/HuskyThe build then produces a package and installs it the way a user would, which suggests the project has an integration test path that drives the tool end to end. The Dockerfile does not explain what the HUSKY variable does on a developer machine, and the documentation site is the place to confirm that.
The project is MIT licensed, and the documentation is published as a VuePress site with a getting started page, a contribution page and the full guide. The copyright and attribution terms of the tools you invoke from a hook are separate from Husky.Net's own license, which is worth checking for a commercial formatter inside a commercial product.
Editorial conclusion
Husky.Net is a sensible choice for a .NET team that wants lint, format and test steps to run at commit time on every developer machine, especially one working in a monorepo with several projects and one formatting tool already picked out. It is not the right choice for a team that treats CI as the only gate, or for a repository whose contributors are not all on a machine where a global .NET tool can be installed. Before adopting it, check that the tasks you want run on staged files rather than the whole tree, confirm which file states you need beyond staged and last-commit, and read the getting started page in the documentation site for the configuration syntax, which the project description does not reproduce.
Frequently asked questions
How do I install Husky.Net in a .NET project?
It is distributed as a NuGet package named Husky and installed as a global .NET tool, which is how the project's own Dockerfile installs it. The project points to a getting started page in the documentation site for the per-repository setup, which is where the core.hooksPath configuration is described.
Does Husky.Net work on Windows, macOS and Linux?
The feature list states that it supports macOS, Linux and Windows. The project also uses the official .NET SDK container image for its own builds, and the Docker image installs Git in the final stage, so the tool is exercised on Linux in continuous integration.
Can Husky.Net run C# scripts as hook tasks?
Yes, CSharp scripts with the csx extension are listed as a supported feature alongside the internal task runner. That lets a hook action be a C# script rather than a shell command, at the cost of requiring a .NET script runtime on the machine running the hook.
Can I skip Husky.Net for one commit?
The project's Dockerfile sets an environment variable named HUSKY to 0 in its build environment, but the documentation available here does not explain what that variable does for a developer machine, so confirm the bypass mechanism in the documentation site before relying on it.
Does Husky.Net work in a monorepo with several .NET projects?
Monorepo support and custom directories are both listed as features. The project's own repository is laid out that way, with src/, tests/ and docs/ directories side by side and a .husky/ folder at the root for the hook files.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/alirezanet-husky-net)Was this useful?