Skip to content

feat: add staged property to custom variables for re-staging support#163

Merged
alirezanet merged 3 commits intomasterfrom
copilot/add-csharpier-pre-commit-hook
Mar 11, 2026
Merged

feat: add staged property to custom variables for re-staging support#163
alirezanet merged 3 commits intomasterfrom
copilot/add-csharpier-pre-commit-hook

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

Custom variables in task-runner.json don't re-stage formatted files after a task runs in a pre-commit hook — unlike the built-in ${staged} variable. This is because custom variable files are tagged ArgumentTypes.CustomVariable, causing a plain ExecutableTask to be created instead of a StagedTask.

Description

A new staged boolean property has been added to custom variable definitions. When set to true, files returned by the custom variable are treated as staged files, enabling the same re-staging behavior as the built-in ${staged} variable (including partial staging support).

The staged property is scoped exclusively to a new dedicated HuskyVariable model — it is not part of the general HuskyTask model used for task definitions — keeping the API clean and purpose-specific.

Changes

  • HuskyVariable (new model) — Dedicated class for custom variable definitions (Name, Command, Args, Cwd, Staged). Replaces the use of HuskyTask for variable loading in ArgumentParser.
  • ArgumentParser.AddCustomVariable() — When the variable has staged: true, tags output files as ArgumentTypes.StagedFile instead of ArgumentTypes.CustomVariable, triggering the existing StagedTask machinery (re-staging, partial staging, etc.).
  • Utils.GetTaskCwdAsync() — Added overload accepting HuskyVariable.
  • schema.json — Added staged (boolean, default: false) and cwd properties to the variableTask definition.
  • docs/guide/task-configuration.md — Documents the new staged property with a CSharpier-style example.
  • Unit tests (ArgumentParserTests) — 4 tests covering staged: trueStagedFile argument type, staged: falseCustomVariable type, no staged property (default) → CustomVariable type, and unknown variable → empty args.
  • Integration tests (StagedCustomVariableTests) — 4 Docker-based tests verifying real behavior: staged variable with matching files executes and re-stages, non-staged variable works normally, and both skip correctly when no matching files are staged.

Usage

{
  "variables": [
    {
      "name": "staged-diff-files",
      "command": "git",
      "args": ["diff", "--cached", "--name-only", "--no-ext-diff", "--diff-filter=ACMRTUXB"],
      "staged": true
    }
  ],
  "tasks": [
    {
      "name": "Run csharpier",
      "group": "pre-commit",
      "command": "dotnet",
      "args": ["csharpier", "${staged-diff-files}"],
      "include": ["**/*.cs"]
    }
  ]
}

Non-breaking — existing custom variables without "staged": true are unaffected.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation
  • I have commented my code, particularly in hard-to-understand areas
  • New and existing unit tests pass locally with my changes
  • I did test corresponding changes on Windows
  • I did test corresponding changes on Linux
  • I did test corresponding changes on Mac
Original prompt

This section details on the original issue you should resolve

<issue_title>Csharpier pre commit hook with custom variable not included in commit</issue_title>
<issue_description>### Version

Seen on v0.6.4 to v0.7.1

Details

So I am trying to run csharpier as a pre commit hook and support formatting files which have been renamed in git, I opted for using a custom variable that was inspired by the ${staged} variable in the husky code.

{
  "variables": [
    {
      "name": "staged-diff-files",
      "command": "git",
      "args": ["diff", "--cached", "--name-only", "--no-ext-diff", "--diff-filter=ACMRTUXB"]
    }
  ],
  "tasks": [
    {
      "name": "Run csharpier",
      "group": "pre-commit",
      "command": "dotnet",
      "args": [ "csharpier", "${staged-diff-files}" ],
      "include": [ "**/*.cs" ]
    },
  ]
}

I noticed that the formatting changes only appear post commit, unstaged.

When I use this instead :

{
  "tasks": [
    {
      "name": "Run csharpier",
      "group": "pre-commit",
      "command": "dotnet",
      "args": [ "csharpier", "${staged}" ],
      "include": [ "**/*.cs" ]
    },
  ]
}

It adds the formatting to the commit as is expected with a pre commit hook.

Even when I set the variable to the exact same as in the code for the ${staged} variable using --diff-filter=AM it does not have the same behavior, it does not add the formatting changes to the commit.

Wondering if this is expected behavior with custom variables or if this is a bug.

Steps to reproduce

Set task runner json with a variable in the csharpier task, with husky running it a pre-commit hook :

{
  "variables": [
    {
      "name": "staged-diff-files",
      "command": "git",
      "args": ["diff", "--cached", "--name-only", "--no-ext-diff", "--diff-filter=AM"]
    }
  ],
  "tasks": [
    {
      "name": "Run csharpier",
      "group": "pre-commit",
      "command": "dotnet",
      "args": [ "csharpier", "${staged-diff-files}" ],
      "include": [ "**/*.cs" ]
    },
  ]
}

Take a .cs file, change the formatting so it triggers csharpier, add it, commit it. The formatting will not be included in the commit, but will be an unstaged change after the commit, not the case when using ${staged}

</issue_description>

<agent_instructions>what do you think about this issue? is it a valid request? should we support it or user using husky in a wrong way? is it possible to support it without any breaking changes?</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@alirezanet Hello @VinceBroAmilia, I will investigate this as soon as I have some free time. Currently, my schedule is quite full. I believe this issue might be addressed by Husky, so I am treating it as a bug for the time being. Thank you for your feedback. @alirezanet Hi @mithileshz,

Thanks for pointing this out! I think you're right—custom variables aren’t directly tied to tasks, so they don’t inherently trigger re-staging, and in most cases, it’s not needed.

One option I’m considering is adding a setting to custom variables that could toggle behavior like re-staging, but I’m not sure if that’s the best approach yet. To be honest, the exact use case for this custom variable isn’t fully clear to me. Understanding the need behind it would help explore whether a new built-in variable might be a better solution 🤔 or if we should adjust the current built-in ${staged} variable if it’s not working as expected.

For now, this might require some experimentation to figure out the cleanest way forward. Please feel free to jump in if you can help with this issue!</comment_new>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Add Csharpier pre commit hook with custom variable feat: add staged property to custom variables for re-staging support Mar 11, 2026
Copy link
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need more Unit tests and Integration tests to make sure this would work as expected.
also we should test the existing behavior if it is not provided.

Copy link
Contributor Author

Copilot AI commented Mar 11, 2026

we need more Unit tests and Integration tests to make sure this would work as expected.
also we should test the existing behavior if it is not provided.

Added in commit 120a950:

  • Unit tests (tests/HuskyTest/TaskRunner/ArgumentParserTests.cs): 4 tests covering staged: trueStagedFile argument type, staged: falseCustomVariable type, default (no staged property) → CustomVariable type, and unknown variable → empty args.
  • Integration tests (tests/HuskyIntegrationTests/StagedCustomVariableTests.cs): 4 Docker-based tests verifying real behavior — staged variable with matching files executes successfully, non-staged variable works normally, and both skip correctly when no matching files are staged.

@alirezanet alirezanet marked this pull request as ready for review March 11, 2026 19:02
Copy link
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

This was referenced Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Csharpier pre commit hook with custom variable not included in commit

2 participants