# How the `vp staged` Command Works with Git Pre-Commit Hooks in Vite-Plus

> Discover how the vp staged command triggers lint-staged for Git pre-commit hooks in Vite-Plus projects. Optimize your workflow with this essential tool.

- Repository: [VoidZero/vite-plus](https://github.com/voidzero-dev/vite-plus)
- Tags: how-to-guide
- Published: 2026-03-16

---

**The `vp staged` command executes the lint-staged pipeline defined in your [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) and serves as the entry point for Git pre-commit hooks in voidzero-dev/vite-plus projects.**

When working with the Vite-Plus toolchain, the `vp staged` command bridges Git's native hook mechanism with your project's linting configuration. This integration allows the same staged-file processing to run both manually via CLI and automatically during the Git commit workflow, ensuring consistent code quality checks across your development team.

## The Complete Execution Flow

The integration between Git hooks and Vite-Plus follows a deterministic sequence from installation to execution:

1. **Hook Installation**: The `vp config --hooks` command creates the `.vite-hooks/_` directory structure and registers it as Git's `core.hooksPath`.
2. **Pre-Commit Trigger**: When `git commit` executes, Git invokes `.vite-hooks/pre-commit`, which sources a shim script to locate the local `vp` binary in `node_modules/.bin`.
3. **Command Delegation**: The hook invokes `vp staged`, which the Rust CLI at [`crates/vite_global_cli/src/commands/staged.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/staged.rs) delegates to the JavaScript implementation.
4. **Pipeline Execution**: The JavaScript entry in [`packages/cli/src/staged/bin.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/staged/bin.ts) loads [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts), extracts the `staged` object, and passes it to the **lint-staged** programmatic API.

### Hook Installation and Shim Creation

In [`packages/cli/src/config/hooks.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/config/hooks.ts), the installation process determines the Git root using `git rev-parse --show-prefix`, then writes two critical components to `.vite-hooks/_/`:

- A central **shim script** (`h`) that prepends the project's `node_modules/.bin` to `PATH` and executes the actual hook script
- Individual hook files (e.g., `pre-commit`) that source the shim via `. "$(dirname "$0")/h"`

This architecture ensures the hooks remain portable across different environments while always resolving to the project's local Vite-Plus installation. The shim also sources optional initialization files from `~/.config/vite-plus/hooks-init.sh` for user-level customization.

### Pre-Commit Hook Execution

When Git triggers the pre-commit hook, the shim at `.vite-hooks/h` resolves the real script at `.vite-hooks/_/pre-commit` and executes it via `sh -e "$s" "$@"`. The generated pre-commit script is intentionally minimal:

```sh
#!/usr/bin/env sh
. "$(dirname "$0")/h"

```

This indirection allows the shim to configure the environment—including the critical `PATH` modification—before delegating to the logic that ultimately calls `vp staged`.

### Configuration Resolution

The `resolveViteConfig()` function in [`packages/cli/src/resolve-vite-config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/resolve-vite-config.ts) loads the project's [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) and extracts the `staged` configuration block. This object follows the standard lint-staged format, mapping glob patterns to shell commands that will execute against staged files only.

### Lint-Staged Pipeline Execution

The main implementation in [`packages/cli/src/staged/bin.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/staged/bin.ts) parses CLI flags using `mri`, constructs a lint-staged **Configuration** object from the `viteConfig.staged` property, and invokes the programmatic API:

```ts
const success = await lintStaged(options);
process.exit(success ? 0 : 1);

```

The `options` object supports all lint-staged flags including `--allow-empty`, `--concurrent`, and `--debug`. If any configured task fails, the process exits with code `1`, aborting the Git commit.

## Deep Dive into Hook Architecture

### The `.vite-hooks` Directory Structure

The installation creates a hidden directory structure that separates the portable shim from concrete hook implementations:

- `.vite-hooks/h`: The central shim that configures `PATH` and executes target scripts
- `.vite-hooks/_/pre-commit`: The concrete pre-commit script generated by Vite-Plus
- `.vite-hooks/_/pre-push`: Additional hooks as needed for other Git lifecycle events

This design allows users to maintain custom initialization logic while keeping repository-specific hooks functional regardless of where the repository is cloned.

### Rust-to-JavaScript Command Delegation

The Rust CLI wrapper at [`crates/vite_global_cli/src/commands/staged.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/staged.rs) provides a thin forwarding layer that ensures consistent behavior across platforms:

```rust
pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result<ExitStatus, Error> {
    super::delegate::execute(cwd, "staged", args).await
}

```

This delegates to the Node.js-based CLI implementation, maintaining a unified interface whether running through Git hooks or directly via the command line.

## Configuring the Staged Pipeline in vite.config.ts

The `staged` block in your Vite configuration defines which commands run against which staged files:

```ts
import { defineConfig } from 'vite-plus';

export default defineConfig({
  staged: {
    '*.{js,ts,tsx,vue,svelte}': 'vp check --fix',
    '*.css': 'stylelint --fix',
  },
});

```

Each glob pattern maps to a shell command that lint-staged executes only on files that Git reports as staged. The configuration supports multiple patterns, command chaining with `&&`, and all standard lint-staged features.

## Practical Usage Examples

### Installing Git Hooks

Initialize the hook infrastructure once per repository:

```bash
vp config --hooks

```

This creates `.vite-hooks/_`, sets `git config core.hooksPath .vite-hooks/_`, and generates the shim and hook scripts required for automatic execution.

### Running Staged Checks Manually

Debug the pipeline without triggering a commit:

```bash
vp staged --debug

```

This executes the same lint-staged configuration that the pre-commit hook uses, loading the `staged` block from [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) and reporting which files match your configured globs.

### Typical Commit Workflow

```bash
git add src/**/*.ts
git commit -m "Apply lint fixes"

```

The commit triggers `.vite-hooks/pre-commit` → shim → `vp staged` → lint-staged. If `vp check --fix` modifies files or exits with an error, the commit aborts and must be retried after restaging the fixes.

## Summary

- The `vp staged` command in [`packages/cli/src/staged/bin.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/staged/bin.ts) serves as the primary entry point for running lint-staged against Git staged files in Vite-Plus projects.
- Hook installation via [`packages/cli/src/config/hooks.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/config/hooks.ts) creates a portable shim system in `.vite-hooks/_` that configures `PATH` before executing commands.
- The Rust wrapper at [`crates/vite_global_cli/src/commands/staged.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/staged.rs) delegates to the JavaScript implementation for cross-platform consistency.
- Configuration is defined in the `staged` block of [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) and passed directly to the lint-staged programmatic API.
- Exit codes propagate to Git, aborting commits when linting fails and allowing them when all tasks succeed.

## Frequently Asked Questions

### What is the `.vite-hooks` directory and why is it hidden?

The `.vite-hooks` directory contains the shim scripts and concrete hook implementations that Vite-Plus installs when you run `vp config --hooks`. It is prefixed with a dot to indicate it contains generated infrastructure rather than user-editable code, while the `_` subdirectory contains the actual executable scripts that source the central shim.

### Can I use `vp staged` without installing Git hooks?

Yes. Running `vp staged` manually from the command line executes the same lint-staged pipeline defined in your [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) without requiring Git hooks to be installed. This is useful for testing configurations or running checks on demand before attempting a commit.

### How does `vp staged` differ from running lint-staged directly?

`vp staged` automatically resolves your [`vite.config.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/vite.config.ts) and extracts the `staged` configuration block, passing it as inline options to lint-staged. This eliminates the need for a separate `.lintstagedrc` file and ensures your linting configuration lives alongside your other Vite tooling in a single configuration file.

### Where does the pre-commit hook script get generated?

The pre-commit script is generated in [`packages/cli/src/config/hooks.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/config/hooks.ts) and written to `.vite-hooks/_/pre-commit`. This script simply sources the shim `h`, which then configures the environment to find the local `vp` binary before executing the actual pre-commit logic.