diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py
index 5ff4555..4843fc7 100644
--- a/pre_commit/commands/hook_impl.py
+++ b/pre_commit/commands/hook_impl.py
@@ -147,15 +147,44 @@ def _pre_push_ns(
     return None
 
 
+_EXPECTED_ARG_LENGTH_BY_HOOK = {
+    'commit-msg': 1,
+    'post-checkout': 3,
+    'pre-commit': 0,
+    'pre-merge-commit': 0,
+    'pre-push': 2,
+}
+
+
+def _check_args_length(hook_type: str, args: Sequence[str]) -> None:
+    if hook_type == 'prepare-commit-msg':
+        if len(args) < 1 or len(args) > 3:
+            raise SystemExit(
+                f'hook-impl for {hook_type} expected 1, 2, or 3 arguments '
+                f'but got {len(args)}: {args}',
+            )
+    elif hook_type in _EXPECTED_ARG_LENGTH_BY_HOOK:
+        expected = _EXPECTED_ARG_LENGTH_BY_HOOK[hook_type]
+        if len(args) != expected:
+            arguments_s = 'argument' if expected == 1 else 'arguments'
+            raise SystemExit(
+                f'hook-impl for {hook_type} expected {expected} {arguments_s} '
+                f'but got {len(args)}: {args}',
+            )
+    else:
+        raise AssertionError(f'unexpected hook type: {hook_type}')
+
+
 def _run_ns(
         hook_type: str,
         color: bool,
         args: Sequence[str],
         stdin: bytes,
 ) -> Optional[argparse.Namespace]:
+    _check_args_length(hook_type, args)
     if hook_type == 'pre-push':
         return _pre_push_ns(color, args, stdin)
-    elif hook_type in {'prepare-commit-msg', 'commit-msg'}:
+    elif hook_type in {'commit-msg', 'prepare-commit-msg'}:
         return _ns(hook_type, color, commit_msg_filename=args[0])
     elif hook_type in {'pre-merge-commit', 'pre-commit'}:
         return _ns(hook_type, color)

pre-commt run --all-files only runs on what is staged still
When I run .git_hooks/pre-commit run --all-files, pre-commit still only runs on what is staged.  Infact, passing any options to pre-commit doesn't seem to have any affect on what is executed.

```
git_hooks/pre-commit run --all-files
Trim Trailing Whitespace.............................(no files to check)Skipped
Fix End of Files.....................................(no files to check)Skipped
Check Yaml...........................................(no files to check)Skipped
Check for added large files..........................(no files to check)Skipped
Check JSON...........................................(no files to check)Skipped
Check Xml............................................(no files to check)Skipped
Check for merge conflicts............................(no files to check)Skipped
Pretty format JSON...................................(no files to check)Skipped
```


```
.git_hooks/pre-commit flake8
Trim Trailing Whitespace.............................(no files to check)Skipped
Fix End of Files.....................................(no files to check)Skipped
Check Yaml...........................................(no files to check)Skipped
Check for added large files..........................(no files to check)Skipped
Check JSON...........................................(no files to check)Skipped
Check Xml............................................(no files to check)Skipped
Check for merge conflicts............................(no files to check)Skipped
Pretty format JSON...................................(no files to check)Skipped
```

Wondering if my install is somehow incorrect?

Thx,
Phil
diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py
index 032fa8f..ddf65b7 100644
--- a/tests/commands/hook_impl_test.py
+++ b/tests/commands/hook_impl_test.py
@@ -89,6 +89,51 @@ def test_run_legacy_recursive(tmpdir):
             call()
 
 
+@pytest.mark.parametrize(
+    ('hook_type', 'args'),
+    (
+        ('pre-commit', []),
+        ('pre-merge-commit', []),
+        ('pre-push', ['branch_name', 'remote_name']),
+        ('commit-msg', ['.git/COMMIT_EDITMSG']),
+        ('post-checkout', ['old_head', 'new_head', '1']),
+        # multiple choices for commit-editmsg
+        ('prepare-commit-msg', ['.git/COMMIT_EDITMSG']),
+        ('prepare-commit-msg', ['.git/COMMIT_EDITMSG', 'message']),
+        ('prepare-commit-msg', ['.git/COMMIT_EDITMSG', 'commit', 'deadbeef']),
+    ),
+)
+def test_check_args_length_ok(hook_type, args):
+    hook_impl._check_args_length(hook_type, args)
+
+
+def test_check_args_length_error_too_many_plural():
+    with pytest.raises(SystemExit) as excinfo:
+        hook_impl._check_args_length('pre-commit', ['run', '--all-files'])
+    msg, = excinfo.value.args
+    assert msg == (
+        'hook-impl for pre-commit expected 0 arguments but got 2: '
+        "['run', '--all-files']"
+    )
+
+
+def test_check_args_length_error_too_many_singluar():
+    with pytest.raises(SystemExit) as excinfo:
+        hook_impl._check_args_length('commit-msg', [])
+    msg, = excinfo.value.args
+    assert msg == 'hook-impl for commit-msg expected 1 argument but got 0: []'
+
+
+def test_check_args_length_prepare_commit_msg_error():
+    with pytest.raises(SystemExit) as excinfo:
+        hook_impl._check_args_length('prepare-commit-msg', [])
+    msg, = excinfo.value.args
+    assert msg == (
+        'hook-impl for prepare-commit-msg expected 1, 2, or 3 arguments '
+        'but got 0: []'
+    )
+
+
 def test_run_ns_pre_commit():
     ns = hook_impl._run_ns('pre-commit', True, (), b'')
     assert ns is not None

pip install -e .[dev]
parse_log_pytest
requirements.txt
3.7
requirements-dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
cfgv==3.3.1
covdefaults==2.3.0
coverage==7.2.7
distlib==0.3.9
exceptiongroup==1.2.2
filelock==3.12.2
identify==2.5.24
importlib-metadata==6.7.0
iniconfig==2.0.0
nodeenv==1.9.1
packaging==24.0
platformdirs==4.0.0
pluggy==1.2.0
-e git+https://github.com/pre-commit/pre-commit.git@282527ef16588b943fe7c2ab8aadd3269946922f#egg=pre_commit
pytest==7.4.4
pytest-env==1.0.1
PyYAML==6.0.1
toml==0.10.2
tomli==2.0.1
typing_extensions==4.7.1
virtualenv==20.26.6
zipp==3.15.0

name: pre-commit
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cfgv==3.3.1
      - covdefaults==2.3.0
      - coverage==7.2.7
      - distlib==0.3.9
      - exceptiongroup==1.2.2
      - filelock==3.12.2
      - identify==2.5.24
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - nodeenv==1.9.1
      - packaging==24.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - pytest-env==1.0.1
      - pyyaml==6.0.1
      - toml==0.10.2
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - virtualenv==20.26.6
      - zipp==3.15.0
prefix: /opt/conda/envs/pre-commit

tests/commands/hook_impl_test.py::test_check_args_length_ok[pre-commit-args0] tests/commands/hook_impl_test.py::test_check_args_length_ok[pre-merge-commit-args1] tests/commands/hook_impl_test.py::test_check_args_length_ok[pre-push-args2] tests/commands/hook_impl_test.py::test_check_args_length_ok[commit-msg-args3] tests/commands/hook_impl_test.py::test_check_args_length_ok[post-checkout-args4] tests/commands/hook_impl_test.py::test_check_args_length_ok[prepare-commit-msg-args5] tests/commands/hook_impl_test.py::test_check_args_length_ok[prepare-commit-msg-args6] tests/commands/hook_impl_test.py::test_check_args_length_ok[prepare-commit-msg-args7] tests/commands/hook_impl_test.py::test_check_args_length_error_too_many_plural tests/commands/hook_impl_test.py::test_check_args_length_error_too_many_singluar tests/commands/hook_impl_test.py::test_check_args_length_prepare_commit_msg_error
tests/commands/hook_impl_test.py::test_validate_config_file_exists tests/commands/hook_impl_test.py::test_validate_config_missing tests/commands/hook_impl_test.py::test_validate_config_skip_missing_config tests/commands/hook_impl_test.py::test_validate_config_skip_via_env_variable tests/commands/hook_impl_test.py::test_run_legacy_does_not_exist tests/commands/hook_impl_test.py::test_run_legacy_executes_legacy_script tests/commands/hook_impl_test.py::test_run_legacy_pre_push_returns_stdin tests/commands/hook_impl_test.py::test_run_legacy_recursive tests/commands/hook_impl_test.py::test_run_ns_pre_commit tests/commands/hook_impl_test.py::test_run_ns_commit_msg tests/commands/hook_impl_test.py::test_run_ns_post_checkout tests/commands/hook_impl_test.py::test_run_ns_pre_push_updating_branch tests/commands/hook_impl_test.py::test_run_ns_pre_push_new_branch tests/commands/hook_impl_test.py::test_run_ns_pre_push_new_branch_existing_rev tests/commands/hook_impl_test.py::test_pushing_orphan_branch tests/commands/hook_impl_test.py::test_run_ns_pre_push_deleting_branch tests/commands/hook_impl_test.py::test_hook_impl_main_noop_pre_push tests/commands/hook_impl_test.py::test_hook_impl_main_runs_hooks
pre_commit/commands/hook_impl.py
pre_commit/commands/hook_impl.py