Compare commits

...

12 Commits

Author SHA1 Message Date
Pablo F.G
d182b88b1a chore: optimize root hooks with prek builtin, priority and new checks
Migrate 6 hooks from remote pre-commit-hooks repo to prek builtin (Rust
native, offline, instant). Add check-added-large-files and
check-case-conflict. Assign priorities for parallel execution: fast
checks at P0, Python formatting chain at P1-P3, linters at P4, heavy
analysis at P5-P6. Use glob patterns for file matching.

Open questions for review:
- pretty-format-json: builtin vs remote (possible behavior diff)
- detect-private-key: excluded due to false positives in tests
- Fixer changes on existing files: include or defer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:16:27 +02:00
Pablo F.G
15eaaeb681 chore(ui): migrate UI hooks to prek workspace mode
Replace the Husky-based UI pre-commit script with native prek workspace
discovery. UI now has its own .pre-commit-config.yaml with orphan: true,
so prek auto-discovers and runs UI hooks (typecheck, lint, tests, build)
only on ui/ files. Typecheck and lint run in parallel via priority: 0.

- Create ui/.pre-commit-config.yaml with 4 local hooks
- Remove ui-checks hook from root .pre-commit-config.yaml
- Delete ui/.husky/ directory (no longer needed without Husky)
- Remove Claude Code review hook and its docs (unused, to discuss in PR)
- Update ui/.dockerignore and ui/README.md references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:36:18 +02:00
Rubén De la Torre Vico
e522065292 chore(ui): update pnpm lockfile after removing husky and lint-staged 2026-04-07 18:41:40 +02:00
Rubén De la Torre Vico
7bf7ba6d41 fix: prefer Poetry in setup-git-hooks.sh for system hook deps
Poetry must be preferred over standalone prek because local hooks
(pylint, bandit, safety, vulture, trufflehog) are Python dev
dependencies installed via Poetry. When only standalone prek is
available, warn that these hooks will fail without the deps.
2026-04-07 18:36:45 +02:00
Rubén De la Torre Vico
8b3de3a189 docs(ui): update hook references from pre-commit to prek
Update troubleshooting instructions to use prek commands and
remove references to the pre-commit Python tool in code review
documentation.
2026-04-07 18:21:58 +02:00
Rubén De la Torre Vico
b542ef6b4a chore(ui): update setup-git-hooks.js to detect prek
Rename detection function to isPrekInstalled and update log
messages. Also detects legacy pre-commit hooks for backwards
compatibility.
2026-04-07 18:21:53 +02:00
Rubén De la Torre Vico
7fb1660c30 chore: update setup-git-hooks.sh to use prek
Detect prek from PATH first (brew, npm global, pipx), then fall
back to Poetry. This allows UI-only contributors to use prek
without needing Poetry installed.
2026-04-07 18:21:49 +02:00
Rubén De la Torre Vico
8b2d156f7c chore(ui): remove husky and lint-staged dependencies
Remove husky and lint-staged npm packages since prek now manages
git hooks at the repository level. The ui/.husky/pre-commit script
is a standalone bash script that prek invokes directly via the
ui-checks hook in .pre-commit-config.yaml.
2026-04-07 18:21:43 +02:00
Rubén De la Torre Vico
de1ab57282 docs: update developer guide to use prek
Replace pre-commit references with prek in the hooks setup section
and add a warning note about using --overwrite flag when migrating
from pre-commit.
2026-04-07 18:05:33 +02:00
Rubén De la Torre Vico
f00f77eb02 docs: update README hooks setup to reference prek
Update the hooks setup section heading and description to
reference prek instead of pre-commit.
2026-04-07 18:05:21 +02:00
Rubén De la Torre Vico
2d392192cf docs: update agent instructions to use prek
Replace pre-commit commands with prek in the Python development
setup section.
2026-04-07 18:05:11 +02:00
Rubén De la Torre Vico
117fde279f chore(deps): replace pre-commit with prek
Swap the Python-based pre-commit framework for prek, a Rust-based
      drop-in replacement that is significantly faster. Prek reads the
      same .pre-commit-config.yaml format natively.
2026-04-07 18:01:56 +02:00
18 changed files with 252 additions and 1310 deletions

View File

@@ -1,18 +1,34 @@
repos:
## GENERAL
## FAST CHECKS & FIXERS (priority 0, all parallel, Rust-native offline)
- repo: builtin
hooks:
- id: check-merge-conflict
priority: 0
- id: check-json
priority: 0
- id: check-added-large-files
args: [--maxkb=1024]
priority: 0
- id: check-case-conflict
priority: 0
- id: end-of-file-fixer
priority: 0
- id: trailing-whitespace
priority: 0
- id: no-commit-to-branch
priority: 0
- id: pretty-format-json
args: ["--autofix", --no-sort-keys, --no-ensure-ascii]
priority: 0
## CHECK-YAML (needs --unsafe for CloudFormation templates in contrib/, stays remote)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-merge-conflict
- id: check-yaml
args: ["--unsafe"]
exclude: prowler/config/llm_config.yaml
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
- id: no-commit-to-branch
- id: pretty-format-json
args: ["--autofix", --no-sort-keys, --no-ensure-ascii]
priority: 0
## TOML
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
@@ -21,13 +37,15 @@ repos:
- id: pretty-format-toml
args: [--autofix]
files: pyproject.toml
priority: 0
## GITHUB ACTIONS
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.6.0
hooks:
- id: zizmor
files: ^\.github/
files: { glob: ".github/**" }
priority: 0
## BASH
- repo: https://github.com/koalaman/shellcheck-precommit
@@ -35,8 +53,9 @@ repos:
hooks:
- id: shellcheck
exclude: contrib
priority: 0
## PYTHON
## PYTHON FORMATTING CHAIN (strict order: autoflake → isort → black)
- repo: https://github.com/myint/autoflake
rev: v2.3.1
hooks:
@@ -48,6 +67,7 @@ repos:
"--remove-all-unused-imports",
"--remove-unused-variable",
]
priority: 1
- repo: https://github.com/pycqa/isort
rev: 5.13.2
@@ -55,20 +75,33 @@ repos:
- id: isort
exclude: ^skills/
args: ["--profile", "black"]
priority: 2
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
exclude: ^skills/
priority: 3
## PYTHON LINTERS (post-formatting, parallel)
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
exclude: (contrib|^skills/)
args: ["--ignore=E266,W503,E203,E501,W605"]
priority: 4
## DOCKER
- repo: https://github.com/hadolint/hadolint
rev: v2.13.0-beta
hooks:
- id: hadolint
args: ["--ignore=DL3013"]
priority: 0
## POETRY (check before lock)
- repo: https://github.com/python-poetry/poetry
rev: 2.1.1
hooks:
@@ -76,73 +109,63 @@ repos:
name: API - poetry-check
args: ["--directory=./api"]
pass_filenames: false
priority: 5
- id: poetry-lock
name: API - poetry-lock
args: ["--directory=./api"]
pass_filenames: false
priority: 6
- id: poetry-check
name: SDK - poetry-check
args: ["--directory=./"]
pass_filenames: false
priority: 5
- id: poetry-lock
name: SDK - poetry-lock
args: ["--directory=./"]
pass_filenames: false
priority: 6
- repo: https://github.com/hadolint/hadolint
rev: v2.13.0-beta
hooks:
- id: hadolint
args: ["--ignore=DL3013"]
## HEAVY ANALYSIS (parallel at their priority level)
- repo: local
hooks:
- id: pylint
name: pylint
entry: bash -c 'pylint --disable=W,C,R,E -j 0 -rn -sn prowler/'
language: system
files: '.*\.py'
files: { glob: "**/*.py" }
priority: 5
- id: trufflehog
name: TruffleHog
description: Detect secrets in your data.
entry: bash -c 'trufflehog --no-update git file://. --only-verified --fail'
# For running trufflehog in docker, use the following entry instead:
# entry: bash -c 'docker run -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --only-verified --fail'
language: system
stages: ["pre-commit", "pre-push"]
priority: 5
- id: bandit
name: bandit
description: "Bandit is a tool for finding common security issues in Python code"
entry: bash -c 'bandit -q -lll -x '*_test.py,./contrib/,./.venv/,./skills/' -r .'
language: system
files: '.*\.py'
files: { glob: "**/*.py" }
priority: 4
- id: safety
name: safety
description: "Safety is a tool that checks your installed dependencies for known security vulnerabilities"
# TODO: Botocore needs urllib3 1.X so we need to ignore these vulnerabilities 77744,77745. Remove this once we upgrade to urllib3 2.X
# TODO: 79023 & 79027 knack ReDoS until `azure-cli-core` (via `cartography`) allows `knack` >=0.13.0
# TODO: 86217 because `alibabacloud-tea-openapi == 0.4.3` don't let us upgrade `cryptography >= 46.0.0`
entry: bash -c 'safety check --ignore 70612,66963,74429,76352,76353,77744,77745,79023,79027,86217'
language: system
priority: 5
- id: vulture
name: vulture
description: "Vulture finds unused code in Python programs."
entry: bash -c 'vulture --exclude "contrib,.venv,api/src/backend/api/tests/,api/src/backend/conftest.py,api/src/backend/tasks/tests/,skills/" --min-confidence 100 .'
language: system
files: '.*\.py'
- id: ui-checks
name: UI - Husky Pre-commit
description: "Run UI pre-commit checks (Claude Code validation + healthcheck)"
entry: bash -c 'cd ui && .husky/pre-commit'
language: system
files: '^ui/.*\.(ts|tsx|js|jsx|json|css)$'
pass_filenames: false
verbose: true
files: { glob: "**/*.py" }
priority: 4

View File

@@ -153,12 +153,12 @@ Prowler is an open-source cloud security assessment tool supporting AWS, Azure,
```bash
# Setup
poetry install --with dev
poetry run pre-commit install
poetry run prek install
# Code quality
poetry run make lint
poetry run make format
poetry run pre-commit run --all-files
poetry run prek run --all-files
```
---

View File

@@ -246,14 +246,7 @@ Some pre-commit hooks require tools installed on your system:
1. **Install [TruffleHog](https://github.com/trufflesecurity/trufflehog#install)** (secret scanning) — see the [official installation options](https://github.com/trufflesecurity/trufflehog#install).
2. **Install [Safety](https://github.com/pyupio/safety)** (dependency vulnerability checking):
```console
# Requires a Python environment (e.g. via pyenv)
pip install safety
```
3. **Install [Hadolint](https://github.com/hadolint/hadolint#install)** (Dockerfile linting) — see the [official installation options](https://github.com/hadolint/hadolint#install).
2. **Install [Hadolint](https://github.com/hadolint/hadolint#install)** (Dockerfile linting) — see the [official installation options](https://github.com/hadolint/hadolint#install).
## Prowler CLI
### Pip package

View File

@@ -118,18 +118,22 @@ In case you have any doubts, consult the [Poetry environment activation guide](h
### Pre-Commit Hooks
This repository uses Git pre-commit hooks managed by the [pre-commit](https://pre-commit.com/) tool, it is installed with `poetry install --with dev`. Next, run the following command in the root of this repository:
This repository uses Git pre-commit hooks managed by the [prek](https://prek.j178.dev/) tool, it is installed with `poetry install --with dev`. Next, run the following command in the root of this repository:
```shell
pre-commit install
prek install
```
Successful installation should produce the following output:
```shell
pre-commit installed at .git/hooks/pre-commit
prek installed at `.git/hooks/pre-commit`
```
<Warning>
If pre-commit hooks were previously installed, run `prek install --overwrite` to replace the existing hook. Otherwise, both tools will run on each commit.
</Warning>
### Code Quality and Security Checks
Before merging pull requests, several automated checks and utilities ensure code security and updated dependencies:

118
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "about-time"
@@ -808,7 +808,7 @@ description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.10\""
markers = "python_version < \"3.11\""
files = [
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
@@ -1671,18 +1671,6 @@ files = [
[package.dependencies]
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
[[package]]
name = "cfgv"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]]
name = "cfn-lint"
version = "1.38.0"
@@ -2180,18 +2168,6 @@ files = [
graph = ["objgraph (>=1.7.2)"]
profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "distlib"
version = "0.4.0"
description = "Distribution utilities"
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
{file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
]
[[package]]
name = "distro"
version = "1.9.0"
@@ -2379,7 +2355,7 @@ description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
markers = "python_version == \"3.10\""
markers = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
@@ -2848,21 +2824,6 @@ files = [
{file = "iamdata-0.1.202507281.tar.gz", hash = "sha256:4050870068ca2fb044d03c46229bc8dbafb4f99db2f50c77297aafd437154ddd"},
]
[[package]]
name = "identify"
version = "2.6.12"
description = "File identification library for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"},
{file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"},
]
[package.extras]
license = ["ukkonen"]
[[package]]
name = "idna"
version = "3.10"
@@ -3938,7 +3899,7 @@ description = "Python package for creating and manipulating graphs and networks"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
markers = "python_version == \"3.10\""
markers = "python_version < \"3.11\""
files = [
{file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"},
{file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"},
@@ -4000,18 +3961,6 @@ plot = ["matplotlib"]
tgrep = ["pyparsing"]
twitter = ["twython"]
[[package]]
name = "nodeenv"
version = "1.9.1"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
files = [
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
[[package]]
name = "numpy"
version = "2.0.2"
@@ -4443,24 +4392,32 @@ files = [
]
[[package]]
name = "pre-commit"
version = "4.2.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
name = "prek"
version = "0.3.8"
description = "Better `pre-commit`, re-engineered in Rust"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"},
{file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"},
{file = "prek-0.3.8-py3-none-linux_armv6l.whl", hash = "sha256:6fb646ada60658fa6dd7771b2e0fb097f005151be222f869dada3eb26d79ed33"},
{file = "prek-0.3.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f3d7fdadb15efc19c09953c7a33cf2061a70f367d1e1957358d3ad5cc49d0616"},
{file = "prek-0.3.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:72728c3295e79ca443f8c1ec037d2a5b914ec73a358f69cf1bc1964511876bf8"},
{file = "prek-0.3.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:48efc28f2f53b5b8087efca9daaed91572d62df97d5f24a1c7a087fecb5017de"},
{file = "prek-0.3.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6ca9d63bacbc448a5c18e955c78d3ac5176c3a17c3baacdd949b1a623e08a36"},
{file = "prek-0.3.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1000f7029696b4fe712fb1fefd4c55b9c4de72b65509c8e50296370a06f9dc3f"},
{file = "prek-0.3.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ff0bed0e2c1286522987d982168a86cbbd0d069d840506a46c9fda983515517"},
{file = "prek-0.3.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fb087ac0ffda3ac65bbbae9a38326a7fd27ee007bb4a94323ce1eb539d8bbec"},
{file = "prek-0.3.8-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:2e1e5e206ff7b31bd079cce525daddc96cd6bc544d20dc128921ad92f7a4c85d"},
{file = "prek-0.3.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dcea3fe23832a4481bccb7c45f55650cb233be7c805602e788bb7dba60f2d861"},
{file = "prek-0.3.8-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:4d25e647e9682f6818ab5c31e7a4b842993c14782a6ffcd128d22b784e0d677f"},
{file = "prek-0.3.8-py3-none-musllinux_1_1_i686.whl", hash = "sha256:de528b82935e33074815acff3c7c86026754d1212136295bc88fe9c43b4231d5"},
{file = "prek-0.3.8-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6d660f1c25a126e6d9f682fe61449441226514f412a4469f5d71f8f8cad56db2"},
{file = "prek-0.3.8-py3-none-win32.whl", hash = "sha256:b0c291c577615d9f8450421dff0b32bfd77a6b0d223ee4115a1f820cb636fdf1"},
{file = "prek-0.3.8-py3-none-win_amd64.whl", hash = "sha256:bc147fdbdd4ec33fc7a987b893ecb69b1413ac100d95c9889a70f3fd58c73d06"},
{file = "prek-0.3.8-py3-none-win_arm64.whl", hash = "sha256:a2614647aeafa817a5802ccb9561e92eedc20dcf840639a1b00826e2c2442515"},
{file = "prek-0.3.8.tar.gz", hash = "sha256:434a214256516f187a3ab15f869d950243be66b94ad47987ee4281b69643a2d9"},
]
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
virtualenv = ">=20.10.0"
[[package]]
name = "propcache"
version = "0.3.2"
@@ -6094,7 +6051,7 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
markers = "python_version == \"3.10\""
markers = "python_version < \"3.11\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -6281,27 +6238,6 @@ files = [
{file = "uuid6-2024.7.10.tar.gz", hash = "sha256:2d29d7f63f593caaeea0e0d0dd0ad8129c9c663b29e19bdf882e864bedf18fb0"},
]
[[package]]
name = "virtualenv"
version = "20.32.0"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56"},
{file = "virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0"},
]
[package.dependencies]
distlib = ">=0.3.7,<1"
filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<5"
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
[[package]]
name = "vulture"
version = "2.14"
@@ -6743,4 +6679,4 @@ files = [
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<3.13"
content-hash = "91739ee5e383337160f9f08b76944ab4e8629c94084c8a9d115246862557f7c5"
content-hash = "eed563f8719cd06a8f11cc95123b6a3b2bfa4b4354a432da1e0728a334be0c05"

View File

@@ -127,7 +127,7 @@ mock = "5.2.0"
moto = {extras = ["all"], version = "5.1.11"}
openapi-schema-validator = "0.6.3"
openapi-spec-validator = "0.7.1"
pre-commit = "4.2.0"
prek = "0.3.8"
pylint = "3.3.4"
pytest = "8.3.5"
pytest-cov = "6.0.0"

View File

@@ -1,7 +1,8 @@
#!/bin/bash
# Setup Git Hooks for Prowler
# This script installs pre-commit hooks using the project's Poetry environment
# This script installs prek hooks using the project's Poetry environment
# or a system-wide prek installation
set -e
@@ -23,43 +24,50 @@ if ! git rev-parse --git-dir >/dev/null 2>&1; then
exit 1
fi
# Check if Poetry is installed
if ! command -v poetry &>/dev/null; then
echo -e "${RED}❌ Poetry is not installed${NC}"
echo -e "${YELLOW} Install Poetry: https://python-poetry.org/docs/#installation${NC}"
exit 1
fi
# Check if pyproject.toml exists
if [ ! -f "pyproject.toml" ]; then
echo -e "${RED}❌ pyproject.toml not found${NC}"
echo -e "${YELLOW} Please run this script from the repository root${NC}"
exit 1
fi
# Check if dependencies are already installed
if ! poetry run python -c "import pre_commit" 2>/dev/null; then
echo -e "${YELLOW}📦 Installing project dependencies (including pre-commit)...${NC}"
poetry install --with dev
else
echo -e "${GREEN}${NC} Dependencies already installed"
fi
echo ""
# Clear any existing core.hooksPath to avoid pre-commit conflicts
# Clear any existing core.hooksPath to avoid conflicts
if git config --get core.hooksPath >/dev/null 2>&1; then
echo -e "${YELLOW}🧹 Clearing existing core.hooksPath configuration...${NC}"
git config --unset-all core.hooksPath
fi
echo -e "${YELLOW}🔗 Installing pre-commit hooks...${NC}"
poetry run pre-commit install
echo ""
# Full setup requires Poetry for system hooks (pylint, bandit, safety, vulture, trufflehog)
# These are installed as Python dev dependencies and used by local hooks in .pre-commit-config.yaml
if command -v poetry &>/dev/null && [ -f "pyproject.toml" ]; then
if poetry run prek --version &>/dev/null 2>&1; then
echo -e "${GREEN}${NC} prek and dependencies found via Poetry"
else
echo -e "${YELLOW}📦 Installing project dependencies (including prek)...${NC}"
poetry install --with dev
fi
echo -e "${YELLOW}🔗 Installing prek hooks...${NC}"
poetry run prek install --overwrite
elif command -v prek &>/dev/null; then
# prek is available system-wide but without Poetry dev deps
echo -e "${GREEN}${NC} prek found in PATH"
echo -e "${YELLOW}🔗 Installing prek hooks...${NC}"
prek install --overwrite
echo ""
echo -e "${YELLOW}⚠️ Warning: Some hooks require Python tools installed via Poetry:${NC}"
echo -e " pylint, bandit, safety, vulture, trufflehog"
echo -e " These hooks will be skipped unless you install them or run:"
echo -e " ${GREEN}poetry install --with dev${NC}"
else
echo -e "${RED}❌ prek is not installed${NC}"
echo -e "${YELLOW} Install prek using one of these methods:${NC}"
echo -e " • brew install prek"
echo -e " • pnpm add -g @j178/prek"
echo -e " • pip install prek"
echo -e " • See https://prek.j178.dev/installation/ for more options"
exit 1
fi
echo ""
echo -e "${GREEN}✅ Git hooks successfully configured!${NC}"
echo ""
echo -e "${YELLOW}📋 Pre-commit system:${NC}"
echo -e " • Python pre-commit manages all git hooks"
echo -e "${YELLOW}📋 Prek hook system:${NC}"
echo -e " • Prek manages all git hooks"
echo -e " • API files: Python checks (black, flake8, bandit, etc.)"
echo -e " • UI files: UI checks (TypeScript, ESLint, Claude Code validation)"
echo ""

View File

@@ -13,5 +13,5 @@ README.md
!.next/static
!.next/standalone
.git
.husky
.pre-commit-config.yaml
scripts/setup-git-hooks.js

View File

@@ -1,231 +0,0 @@
#!/bin/bash
# Prowler UI - Pre-Commit Hook
# Optionally validates ONLY staged files against AGENTS.md standards using Claude Code
# Controlled by CODE_REVIEW_ENABLED in .env
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 Prowler UI - Pre-Commit Hook"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Load .env file (look in git root directory)
GIT_ROOT=$(git rev-parse --show-toplevel)
if [ -f "$GIT_ROOT/ui/.env" ]; then
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" "$GIT_ROOT/ui/.env" | cut -d'=' -f2 | tr -d ' ')
elif [ -f "$GIT_ROOT/.env" ]; then
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" "$GIT_ROOT/.env" | cut -d'=' -f2 | tr -d ' ')
elif [ -f ".env" ]; then
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" .env | cut -d'=' -f2 | tr -d ' ')
else
CODE_REVIEW_ENABLED="false"
fi
# Normalize the value to lowercase
CODE_REVIEW_ENABLED=$(echo "$CODE_REVIEW_ENABLED" | tr '[:upper:]' '[:lower:]')
echo -e "${BLUE} Code Review Status: ${CODE_REVIEW_ENABLED}${NC}"
echo ""
# Get staged files in the UI folder only (what will be committed)
# Always use GIT_ROOT-relative pathspecs so detection works regardless of cwd
STAGED_FILES=$(git -C "$GIT_ROOT" diff --cached --name-only --diff-filter=ACM -- 'ui/' | grep -E '\.(tsx?|jsx?)$' || true)
if [ "$CODE_REVIEW_ENABLED" = "true" ]; then
if [ -z "$STAGED_FILES" ]; then
echo -e "${YELLOW}⚠️ No TypeScript/JavaScript files staged to validate${NC}"
echo ""
else
echo -e "${YELLOW}🔍 Running Claude Code standards validation...${NC}"
echo ""
echo -e "${BLUE}📋 Files to validate:${NC}"
echo "$STAGED_FILES" | while IFS= read -r file; do echo " - $file"; done
echo ""
echo -e "${BLUE}📤 Sending to Claude Code for validation...${NC}"
echo ""
# Build prompt with full file contents
VALIDATION_PROMPT=$(
cat <<'PROMPT_EOF'
You are a code reviewer for the Prowler UI project. Analyze the full file contents of changed files below and validate they comply with AGENTS.md standards.
**RULES TO CHECK:**
1. React Imports: NO `import * as React` or `import React, {` → Use `import { useState }`
2. TypeScript: NO union types like `type X = "a" | "b"` → Use const-based: `const X = {...} as const`
3. Tailwind: NO `var()` or hex colors in className → Use Tailwind utilities and semantic color classes. Exception: `var()` is allowed when passing colors to chart/graph components that require CSS color strings (not Tailwind classes) for their APIs.
4. cn(): Use for merging multiple classes or for conditionals (handles Tailwind conflicts with twMerge) → `cn(BUTTON_STYLES.base, BUTTON_STYLES.active, isLoading && "opacity-50")`
5. React 19: NO `useMemo`/`useCallback` without reason
6. Zod v4: Use `.min(1)` not `.nonempty()`, `z.email()` not `z.string().email()`. All inputs must be validated with Zod.
7. File Org: 1 feature = local, 2+ features = shared
8. Directives: Server Actions need "use server", clients need "use client"
9. Implement DRY, KISS principles. (example: reusable components, avoid repetition)
10. Layout must work for all the responsive breakpoints (mobile, tablet, desktop)
11. ANY types cannot be used - CRITICAL: Check for `: any` in all visible lines
12. Use the components inside components/shadcn if possible
13. Check Accessibility best practices (like alt tags in images, semantic HTML, Aria labels, etc.)
=== FILES TO REVIEW ===
PROMPT_EOF
)
# Add full file contents for each staged file
for file in $STAGED_FILES; do
VALIDATION_PROMPT="$VALIDATION_PROMPT
=== FILE: $file ===
$(cat "$file" 2>/dev/null || echo "Error reading file")"
done
VALIDATION_PROMPT="$VALIDATION_PROMPT
=== END FILES ===
**IMPORTANT: Your response MUST start with exactly one of these lines:**
STATUS: PASSED
STATUS: FAILED
**If FAILED:** List each violation with File, Line Number, Rule Number, and Issue.
**If PASSED:** Confirm all files comply with AGENTS.md standards.
**Start your response now with STATUS:**"
# Send to Claude Code
if VALIDATION_OUTPUT=$(echo "$VALIDATION_PROMPT" | claude 2>&1); then
echo "$VALIDATION_OUTPUT"
echo ""
# Check result - STRICT MODE: fail if status unclear
if echo "$VALIDATION_OUTPUT" | grep -q "^STATUS: PASSED"; then
echo ""
echo -e "${GREEN}✅ VALIDATION PASSED${NC}"
echo ""
elif echo "$VALIDATION_OUTPUT" | grep -q "^STATUS: FAILED"; then
echo ""
echo -e "${RED}❌ VALIDATION FAILED${NC}"
echo -e "${RED}Fix violations before committing${NC}"
echo ""
exit 1
else
echo ""
echo -e "${RED}❌ VALIDATION ERROR${NC}"
echo -e "${RED}Could not determine validation status from Claude Code response${NC}"
echo -e "${YELLOW}Response must start with 'STATUS: PASSED' or 'STATUS: FAILED'${NC}"
echo ""
echo -e "${YELLOW}To bypass validation temporarily, set CODE_REVIEW_ENABLED=false in .env${NC}"
echo ""
exit 1
fi
else
echo -e "${YELLOW}⚠️ Claude Code not available${NC}"
fi
echo ""
fi
else
echo -e "${YELLOW}⏭️ Code review disabled (CODE_REVIEW_ENABLED=false)${NC}"
echo ""
fi
# Run healthcheck (typecheck and lint check) only if there are UI changes
if [ -z "$STAGED_FILES" ]; then
echo -e "${YELLOW}⏭️ No UI files staged, skipping healthcheck/tests/build${NC}"
echo ""
exit 0
fi
echo -e "${BLUE}🏥 Running healthcheck...${NC}"
echo ""
cd "$GIT_ROOT/ui"
if pnpm run healthcheck; then
echo ""
echo -e "${GREEN}✅ Healthcheck passed${NC}"
echo ""
else
echo ""
echo -e "${RED}❌ Healthcheck failed${NC}"
echo -e "${RED}Fix type errors and linting issues before committing${NC}"
echo ""
exit 1
fi
# Run unit tests (targeted based on staged files)
echo -e "${BLUE}🧪 Running unit tests...${NC}"
echo ""
# Get staged source files (exclude test files)
# Use GIT_ROOT so pathspecs are always correct regardless of cwd
STAGED_SOURCE_FILES=$(git -C "$GIT_ROOT" diff --cached --name-only --diff-filter=ACM -- 'ui/*.ts' 'ui/*.tsx' | sed 's|^ui/||' | grep -v '\.test\.\|\.spec\.\|vitest\.config\|vitest\.setup' || true)
# Check if critical paths changed (lib/, types/, config/)
CRITICAL_PATHS_CHANGED=$(git -C "$GIT_ROOT" diff --cached --name-only -- 'ui/lib/' 'ui/types/' 'ui/config/' 'ui/middleware.ts' 'ui/vitest.config.ts' 'ui/vitest.setup.ts' || true)
if [ -n "$CRITICAL_PATHS_CHANGED" ]; then
echo -e "${YELLOW}Critical paths changed - running ALL unit tests${NC}"
if pnpm run test:run; then
echo ""
echo -e "${GREEN}✅ Unit tests passed${NC}"
echo ""
else
echo ""
echo -e "${RED}❌ Unit tests failed${NC}"
echo -e "${RED}Fix failing tests before committing${NC}"
echo ""
exit 1
fi
elif [ -n "$STAGED_SOURCE_FILES" ]; then
echo -e "${YELLOW}Running tests related to changed files:${NC}"
echo "$STAGED_SOURCE_FILES" | while IFS= read -r file; do [ -n "$file" ] && echo " - $file"; done
echo ""
# shellcheck disable=SC2086 # Word splitting is intentional - vitest needs each file as separate arg
if pnpm exec vitest related $STAGED_SOURCE_FILES --run; then
echo ""
echo -e "${GREEN}✅ Unit tests passed${NC}"
echo ""
else
echo ""
echo -e "${RED}❌ Unit tests failed${NC}"
echo -e "${RED}Fix failing tests before committing${NC}"
echo ""
exit 1
fi
else
echo -e "${YELLOW}No source files changed - running ALL unit tests${NC}"
if pnpm run test:run; then
echo ""
echo -e "${GREEN}✅ Unit tests passed${NC}"
echo ""
else
echo ""
echo -e "${RED}❌ Unit tests failed${NC}"
echo -e "${RED}Fix failing tests before committing${NC}"
echo ""
exit 1
fi
fi
# Run build
echo -e "${BLUE}🔨 Running build...${NC}"
echo ""
if pnpm run build; then
echo ""
echo -e "${GREEN}✅ Build passed${NC}"
echo ""
else
echo ""
echo -e "${RED}❌ Build failed${NC}"
echo -e "${RED}Fix build errors before committing${NC}"
echo ""
exit 1
fi

View File

@@ -0,0 +1,37 @@
orphan: true
repos:
- repo: local
hooks:
- id: ui-typecheck
name: UI - TypeScript Check
entry: pnpm run typecheck
language: system
files: '\.(ts|tsx|js|jsx)$'
pass_filenames: false
priority: 0
- id: ui-lint
name: UI - ESLint
entry: pnpm run lint:check
language: system
files: '\.(ts|tsx|js|jsx)$'
pass_filenames: false
priority: 0
- id: ui-tests
name: UI - Unit Tests
entry: pnpm exec vitest related --run
language: system
files: '\.(ts|tsx|js|jsx)$'
exclude: '\.test\.|\.spec\.|vitest\.config|vitest\.setup'
pass_filenames: true
priority: 1
- id: ui-build
name: UI - Build
entry: pnpm run build
language: system
files: '\.(ts|tsx|js|jsx|json|css)$'
pass_filenames: false
priority: 2

View File

@@ -85,10 +85,10 @@ git clone git@github.com:prowler-cloud/ui.git
pnpm install
```
**Note:** The `pnpm install` command will automatically configure Git hooks for code quality checks. If you experience issues, you can manually configure them:
**Note:** The `pnpm install` command will automatically configure prek Git hooks for code quality checks. If hooks are not installed, run from the repo root:
```bash
git config core.hooksPath "ui/.husky"
prek install
```
#### Run the development server
@@ -142,12 +142,12 @@ Or remove the variable from your `.env` file.
### Troubleshooting
If hooks aren't running after commits:
If hooks aren't running after commits, verify prek is installed and hooks are set up:
```bash
# Verify hooks are configured
git config --get core.hooksPath # Should output: ui/.husky
# Check prek is available
prek --version
# Reconfigure if needed
git config core.hooksPath "ui/.husky"
# Re-install hooks if needed
prek install --overwrite
```

View File

@@ -1015,14 +1015,6 @@
"strategy": "installed",
"generatedAt": "2026-01-19T13:54:24.770Z"
},
{
"section": "devDependencies",
"name": "husky",
"from": "9.1.7",
"to": "9.1.7",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
},
{
"section": "devDependencies",
"name": "jsdom",
@@ -1031,14 +1023,6 @@
"strategy": "installed",
"generatedAt": "2026-01-29T16:42:27.795Z"
},
{
"section": "devDependencies",
"name": "lint-staged",
"from": "15.5.2",
"to": "15.5.2",
"strategy": "installed",
"generatedAt": "2025-10-22T12:36:37.962Z"
},
{
"section": "devDependencies",
"name": "postcss",

View File

@@ -1,115 +0,0 @@
# Code Review - Quick Start
## 3 Steps to Enable
### 1. Open `.env`
```bash
nano ui/.env
# or your favorite editor
```
### 2. Find this line
```bash
CODE_REVIEW_ENABLED=false
```
### 3. Change it to
```bash
CODE_REVIEW_ENABLED=true
```
**Done! ✅**
---
## What Happens Now
Every time you `git commit`:
```
✅ If your code complies with AGENTS.md standards:
→ Commit executes normally
❌ If there are standard violations:
→ Commit is BLOCKED
→ You see the errors in the terminal
→ Fix the code
→ Commit again
```
---
## Example
```bash
$ git commit -m "feat: add new component"
🏁 Prowler UI - Pre-Commit Hook
Code Review Status: true
🔍 Running Claude Code standards validation...
📋 Files to validate:
- components/my-feature.tsx
📤 Sending to Claude Code for validation...
STATUS: FAILED
- File: components/my-feature.tsx:45
Rule: React Imports
Issue: Using 'import * as React'
Expected: import { useState } from "react"
❌ VALIDATION FAILED
Fix violations before committing
# Fix the file and commit again
$ git commit -m "feat: add new component"
🏁 Prowler UI - Pre-Commit Hook
Code Review Status: true
🔍 Running Claude Code standards validation...
✅ VALIDATION PASSED
# Commit successful ✅
```
---
## Disable Temporarily
If you need to commit without validation:
```bash
# Option 1: Change in .env
CODE_REVIEW_ENABLED=false
# Option 2: Bypass (use with caution!)
git commit --no-verify
```
---
## What Gets Validated
- ✅ Correct React imports
- ✅ TypeScript patterns (const-based types)
- ✅ Tailwind CSS (no var() or hex in className)
- ✅ cn() utility (only for conditionals)
- ✅ No useMemo/useCallback without reason
- ✅ Zod v4 syntax
- ✅ File organization
- ✅ Directives "use client"/"use server"
---
## More Info
Read `CODE_REVIEW_SETUP.md` for:
- Troubleshooting
- Complete details
- Advanced configuration

View File

@@ -1,296 +0,0 @@
# Code Review Setup - Prowler UI
Guide to set up automatic code validation with Claude Code in the pre-commit hook.
## Overview
The code review system works like this:
1. **When you enable `CODE_REVIEW_ENABLED=true` in `.env`**
- When you `git commit`, the pre-commit hook runs
- Only validates TypeScript/JavaScript files you're committing
- Uses Claude Code to check if they comply with AGENTS.md
- If there are violations → **BLOCKS the commit**
- If everything is fine → Continues normally
2. **When `CODE_REVIEW_ENABLED=false` (default)**
- The pre-commit hook does not run validation
- No standards validation
- Developers can commit without restrictions
## Installation
### 1. Ensure Claude Code is in your PATH
```bash
# Verify that claude is available in terminal
which claude
# If it doesn't appear, check your Claude Code CLI installation
```
### 2. Enable validation in `.env`
In `/ui/.env`, find the "Code Review Configuration" section:
```bash
#### Code Review Configuration ####
# Enable Claude Code standards validation on pre-commit hook
# Set to 'true' to validate changes against AGENTS.md standards via Claude Code
# Set to 'false' to skip validation
CODE_REVIEW_ENABLED=false # ← Change this to 'true'
```
**Options:**
- `CODE_REVIEW_ENABLED=true` → Enables validation
- `CODE_REVIEW_ENABLED=false` → Disables validation (default)
### 3. The hook is ready
The `.husky/pre-commit` file already contains the logic. You don't need to install anything else.
## How It Works
### Normal Flow (with validation enabled)
```bash
$ git commit -m "feat: add new component"
# Pre-commit hook executes automatically
🚀 Prowler UI - Pre-Commit Hook
Code Review Status: true
📋 Files to validate:
- components/new-feature.tsx
- types/new-feature.ts
📤 Sending to Claude Code for validation...
# Claude analyzes the files...
=== VALIDATION REPORT ===
STATUS: PASSED
All files comply with AGENTS.md standards.
✅ VALIDATION PASSED
# Commit continues ✅
```
### If There Are Violations
```bash
$ git commit -m "feat: add new component"
# Claude detects issues...
=== VALIDATION REPORT ===
STATUS: FAILED
- File: components/new-feature.tsx:15
Rule: React Imports
Issue: Using 'import * as React' instead of named imports
Expected: import { useState } from "react"
❌ VALIDATION FAILED
Please fix the violations before committing:
1. Review the violations listed above
2. Fix the code according to AGENTS.md standards
3. Commit your changes
4. Try again
# Commit is BLOCKED ❌
```
## What Gets Validated
The system verifies that files comply with:
### 1. React Imports
```typescript
// ❌ WRONG
import * as React from "react"
import React, { useState } from "react"
// ✅ CORRECT
import { useState } from "react"
```
### 2. TypeScript Type Patterns
```typescript
// ❌ WRONG
type SortOption = "high-low" | "low-high"
// ✅ CORRECT
const SORT_OPTIONS = {
HIGH_LOW: "high-low",
LOW_HIGH: "low-high",
} as const
type SortOption = typeof SORT_OPTIONS[keyof typeof SORT_OPTIONS]
```
### 3. Tailwind CSS
```typescript
// ❌ WRONG
className="bg-[var(--color)]"
className="text-[#ffffff]"
// ✅ CORRECT
className="bg-card-bg text-white"
```
### 4. cn() Utility
```typescript
// ❌ WRONG
className={cn("flex items-center")}
// ✅ CORRECT
className={cn("h-3 w-3", isCircle ? "rounded-full" : "rounded-sm")}
```
### 5. React 19 Hooks
```typescript
// ❌ WRONG
const memoized = useMemo(() => value, [])
// ✅ CORRECT
// Don't use useMemo (React Compiler handles it)
const value = expensiveCalculation()
```
### 6. Zod v4 Syntax
```typescript
// ❌ WRONG
z.string().email()
z.string().nonempty()
// ✅ CORRECT
z.email()
z.string().min(1)
```
### 7. File Organization
```
// ❌ WRONG
Code used by 2+ features in feature-specific folder
// ✅ CORRECT
Code used by 1 feature → local in that feature
Code used by 2+ features → in shared/global
```
### 8. Use Directives
```typescript
// ❌ WRONG
export async function updateUser() { } // Missing "use server"
// ✅ CORRECT
"use server"
export async function updateUser() { }
```
## Disable Temporarily
If you need to commit without validation temporarily:
```bash
# Option 1: Change in .env
CODE_REVIEW_ENABLED=false
git commit
# Option 2: Use git hook bypass
git commit --no-verify
# Option 3: Disable the hook
chmod -x .husky/pre-commit
git commit
chmod +x .husky/pre-commit
```
**⚠️ Note:** `--no-verify` skips ALL hooks.
## Troubleshooting
### "Claude Code CLI not found"
```
⚠️ Claude Code CLI not found in PATH
To enable: ensure Claude Code is in PATH and CODE_REVIEW_ENABLED=true
```
**Solution:**
```bash
# Check where claude-code is installed
which claude-code
# If not found, add to your ~/.zshrc:
export PATH="$HOME/.local/bin:$PATH" # or where it's installed
# Reload the terminal
source ~/.zshrc
```
### "Validation inconclusive"
If Claude Code cannot determine the status:
```
⚠️ Could not determine validation status
Allowing commit (validation inconclusive)
```
The commit is allowed automatically. If you want to be stricter, you can:
1. Manually review files against AGENTS.md
2. Report the analysis problem to Claude
### Build fails after validation
```
❌ Build failed
```
If validation passes but build fails:
1. Check the build error
2. Fix it locally
3. Commit and try again
## View the Full Report
Reports are saved in temporary files that are deleted afterward. To see the detailed report in real-time, watch the hook output:
```bash
git commit 2>&1 | tee commit-report.txt
```
This will save everything to `commit-report.txt`.
## For the Team
### Enable on your machine
```bash
cd ui
# Edit .env locally and set:
CODE_REVIEW_ENABLED=true
```
### Recommended Flow
1. **During development**: `CODE_REVIEW_ENABLED=false`
- Iterate faster
- Build check still runs
2. **Before final commit**: `CODE_REVIEW_ENABLED=true`
- Verify you meet standards
- Prevent PRs rejected for violations
3. **In CI/CD**: You could add additional validation
- (future) Server-side validation in GitHub Actions
## Questions?
If you have questions about the standards being validated, check:
- `AGENTS.md` - Complete architecture guide
- `CLAUDE.md` - Project-specific instructions

View File

@@ -1,241 +0,0 @@
# Code Review System Documentation
Complete documentation for the Claude Code-powered pre-commit validation system.
## Quick Navigation
**Want to get started in 3 steps?**
→ Read: [`CODE_REVIEW_QUICK_START.md`](./CODE_REVIEW_QUICK_START.md)
**Want complete technical details?**
→ Read: [`CODE_REVIEW_SETUP.md`](./CODE_REVIEW_SETUP.md)
---
## What This System Does
Automatically validates code against AGENTS.md standards when you commit using Claude Code.
```
git commit
(Optional) Claude Code validation
If violations found → Commit is BLOCKED ❌
If code complies → Commit continues ✅
```
**Key Feature:** Configurable with a single variable in `.env`
- `CODE_REVIEW_ENABLED=true` → Validates (recommended before commits)
- `CODE_REVIEW_ENABLED=false` → Skip validation (default, for iteration)
---
## File Guide
| File | Purpose | Read Time |
|------|---------|-----------|
| [`CODE_REVIEW_QUICK_START.md`](./CODE_REVIEW_QUICK_START.md) | 3-step setup & examples | 5 min |
| [`CODE_REVIEW_SETUP.md`](./CODE_REVIEW_SETUP.md) | Complete technical guide | 15 min |
---
## What Gets Validated
When validation is enabled, the system checks:
**React Imports**
- Must use: `import { useState } from "react"`
- Not: `import * as React` or `import React, {`
**TypeScript Types**
- Must use: `const STATUS = {...} as const; type Status = typeof STATUS[...]`
- Not: `type Status = "a" | "b"`
**Tailwind CSS**
- Must use: `className="bg-card-bg text-white"`
- Not: `className="bg-[var(...)]"` or `className="text-[#fff]"`
**cn() Utility**
- Must use for: `cn("h-3", isActive && "bg-blue")`
- Not for: `cn("static-class")`
**React 19 Hooks**
- No: `useMemo()` / `useCallback()` without documented reason
- Use: Nothing (React Compiler handles optimization)
**Zod v4 Syntax**
- Must use: `z.email()`, `.min(1)`
- Not: `z.string().email()`, `.nonempty()`
**File Organization**
- 1 feature uses → Keep local in feature folder
- 2+ features use → Move to shared/global
**Directives**
- Server Actions must have: `"use server"`
- Client Components must have: `"use client"`
---
## Installation (For Your Team)
### Step 1: Decide if you want validation
- **Optional:** Each developer decides
- **Team policy:** Consider making it standard before commits
### Step 2: Enable in your environment
```bash
# Edit ui/.env
CODE_REVIEW_ENABLED=true
```
### Step 3: Done!
Your next `git commit` will validate automatically.
---
## Support
| Question | Answer |
|----------|--------|
| How do I enable it? | Change `CODE_REVIEW_ENABLED=true` in `.env` |
| How do I disable it? | Change `CODE_REVIEW_ENABLED=false` in `.env` |
| How do I bypass? | Use `git commit --no-verify` (emergency only) |
| What if Claude Code isn't found? | Check PATH: `which claude` |
| What if hook doesn't run? | Check executable: `chmod +x .husky/pre-commit` |
| How do I test it? | Enable validation and commit code with violations to test |
| What if I don't have Claude Code? | Validation is skipped gracefully |
---
## Key Features
**No Setup Required**
- Uses Claude Code already in your PATH
- No API keys needed
- Works offline (if Claude Code supports it)
**Smart Validation**
- Only checks files being committed
- Not the entire codebase
- Fast: ~10-30 seconds with validation enabled
**Flexible**
- Can be enabled/disabled per developer
- Can be disabled temporarily with `git commit --no-verify`
- Default is disabled (doesn't interrupt workflow)
**Clear Feedback**
- Shows exactly what violates standards
- Shows file:line references
- Explains how to fix each issue
**Well Documented**
- 5 different documentation files
- For different needs and levels
- Examples and troubleshooting included
---
## Architecture
```
┌─────────────────────────────────────────┐
│ Developer commits code │
└────────────────┬────────────────────────┘
┌─────────────────┐
│ Pre-Commit Hook │
│ (.husky/pre-commit)
└────────┬────────┘
Read CODE_REVIEW_ENABLED from .env
┌──────────────────────────┐
│ If false (disabled) │
└────────┬─────────────────┘
exit 0 (OK)
Commit continues ✅
┌──────────────────────────┐
│ If true (enabled) │
└────────┬─────────────────┘
Extract staged files
(git diff --cached)
Build prompt with git diff
Send to: claude < prompt
Analyze against AGENTS.md
Return: STATUS: PASSED or FAILED
Parse with: grep "^STATUS:"
┌──────────────────┐
│ PASSED detected │
└────────┬─────────┘
exit 0 (OK)
Commit continues ✅
┌──────────────────┐
│ FAILED detected │
└────────┬─────────┘
Show violations
exit 1 (FAIL)
Commit is BLOCKED ❌
Developer fixes code
Developer commits again
```
---
## Getting Started
1. **Read:** [`CODE_REVIEW_QUICK_START.md`](./CODE_REVIEW_QUICK_START.md) (5 minutes)
2. **Enable:** Set `CODE_REVIEW_ENABLED=true` in your `ui/.env`
3. **Test:** Commit some code and see validation in action
4. **For help:** See the troubleshooting section in [`CODE_REVIEW_SETUP.md`](./CODE_REVIEW_SETUP.md)
---
## Implementation Details
- **Files Modified:** 1 (`.husky/pre-commit`)
- **Files Created:** 3 (documentation)
- **Hook Size:** ~120 lines of bash
- **Dependencies:** Claude Code CLI (already available)
- **Setup Time:** 1 minute
- **Default:** Disabled (no workflow interruption)
---
## Questions?
- **How to enable?** → `CODE_REVIEW_QUICK_START.md`
- **How does it work?** → `CODE_REVIEW_SETUP.md`
- **Troubleshooting?** → See troubleshooting section in `CODE_REVIEW_SETUP.md`
---
## Status
**Ready to Use**
The system is fully implemented, documented, and tested. You can enable it immediately with a single variable change.
---
**Last Updated:** November 6, 2024
**Status:** Complete Implementation

View File

@@ -14,7 +14,6 @@
"lint:fix": "eslint . --fix",
"format:check": "./node_modules/.bin/prettier --check ./app",
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
"prepare": "husky",
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
@@ -156,9 +155,7 @@
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-unused-imports": "4.3.0",
"globals": "17.0.0",
"husky": "9.1.7",
"jsdom": "27.4.0",
"lint-staged": "15.5.2",
"postcss": "8.4.38",
"prettier": "3.6.2",
"prettier-plugin-tailwindcss": "0.6.14",

284
ui/pnpm-lock.yaml generated
View File

@@ -414,15 +414,9 @@ importers:
globals:
specifier: 17.0.0
version: 17.0.0
husky:
specifier: 9.1.7
version: 9.1.7
jsdom:
specifier: 27.4.0
version: 27.4.0(@noble/hashes@1.8.0)
lint-staged:
specifier: 15.5.2
version: 15.5.2
postcss:
specifier: 8.4.38
version: 8.4.38
@@ -1895,155 +1889,183 @@ packages:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -2267,24 +2289,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.6':
resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@16.1.6':
resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@16.1.6':
resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.6':
resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
@@ -4135,66 +4161,79 @@ packages:
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
@@ -4663,24 +4702,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -5102,41 +5145,49 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -5340,10 +5391,6 @@ packages:
react: 19.2.4
react-dom: 19.2.4
ansi-escapes@7.2.0:
resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==}
engines: {node: '>=18'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -5596,10 +5643,6 @@ packages:
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
engines: {node: '>=6'}
cli-truncate@4.0.0:
resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
engines: {node: '>=18'}
cli-width@4.1.0:
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
engines: {node: '>= 12'}
@@ -5648,9 +5691,6 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
@@ -5658,10 +5698,6 @@ packages:
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
engines: {node: '>=16'}
commander@13.1.0:
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
engines: {node: '>=18'}
commander@14.0.2:
resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
engines: {node: '>=20'}
@@ -6114,10 +6150,6 @@ packages:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
environment@1.1.0:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'}
error-ex@1.3.4:
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
@@ -6401,10 +6433,6 @@ packages:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
execa@9.6.1:
resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==}
engines: {node: ^18.19.0 || >=20.5.0}
@@ -6613,10 +6641,6 @@ packages:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
get-stream@9.0.1:
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
engines: {node: '>=18'}
@@ -6798,19 +6822,10 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
human-signals@8.0.1:
resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
engines: {node: '>=18.18.0'}
husky@9.1.7:
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
engines: {node: '>=18'}
hasBin: true
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@@ -6947,14 +6962,6 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-fullwidth-code-point@4.0.0:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
engines: {node: '>=12'}
is-fullwidth-code-point@5.1.0:
resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
engines: {node: '>=18'}
is-generator-function@1.1.2:
resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==}
engines: {node: '>= 0.4'}
@@ -7039,10 +7046,6 @@ packages:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
is-stream@3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
is-stream@4.0.1:
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
engines: {node: '>=18'}
@@ -7291,24 +7294,28 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -7326,22 +7333,9 @@ packages:
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
engines: {node: '>= 12.0.0'}
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
lint-staged@15.5.2:
resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==}
engines: {node: '>=18.12.0'}
hasBin: true
listr2@8.3.3:
resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==}
engines: {node: '>=18.0.0'}
loader-runner@4.3.1:
resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==}
engines: {node: '>=6.11.5'}
@@ -7369,10 +7363,6 @@ packages:
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
engines: {node: '>=18'}
log-update@6.1.0:
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
engines: {node: '>=18'}
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
@@ -7644,10 +7634,6 @@ packages:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
mimic-fn@4.0.0:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
mimic-function@5.0.1:
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
engines: {node: '>=18'}
@@ -7813,10 +7799,6 @@ packages:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
npm-run-path@5.3.0:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
npm-run-path@6.0.0:
resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
engines: {node: '>=18'}
@@ -7874,10 +7856,6 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
onetime@6.0.0:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
onetime@7.0.0:
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
engines: {node: '>=18'}
@@ -8038,11 +8016,6 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pidtree@0.6.0:
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
engines: {node: '>=0.10'}
hasBin: true
pixelmatch@7.1.0:
resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==}
hasBin: true
@@ -8489,9 +8462,6 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
@@ -8650,14 +8620,6 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
slice-ansi@5.0.0:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'}
slice-ansi@7.1.2:
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
engines: {node: '>=18'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -8705,10 +8667,6 @@ packages:
strict-event-emitter@0.5.1:
resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -8767,10 +8725,6 @@ packages:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
strip-final-newline@3.0.0:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
strip-final-newline@4.0.0:
resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
engines: {node: '>=18'}
@@ -9396,10 +9350,6 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
wrap-ansi@9.0.2:
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
engines: {node: '>=18'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@@ -16285,10 +16235,6 @@ snapshots:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
ansi-escapes@7.2.0:
dependencies:
environment: 1.1.0
ansi-regex@5.0.1: {}
ansi-regex@6.2.2: {}
@@ -16568,11 +16514,6 @@ snapshots:
cli-spinners@2.9.2: {}
cli-truncate@4.0.0:
dependencies:
slice-ansi: 5.0.0
string-width: 7.2.0
cli-width@4.1.0: {}
client-only@0.0.1: {}
@@ -16629,14 +16570,10 @@ snapshots:
color-convert: 2.0.1
color-string: 1.9.1
colorette@2.0.20: {}
comma-separated-tokens@2.0.3: {}
commander@11.1.0: {}
commander@13.1.0: {}
commander@14.0.2: {}
commander@2.20.3: {}
@@ -17069,8 +17006,6 @@ snapshots:
env-paths@2.2.1: {}
environment@1.1.0: {}
error-ex@1.3.4:
dependencies:
is-arrayish: 0.2.1
@@ -17513,18 +17448,6 @@ snapshots:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
execa@8.0.1:
dependencies:
cross-spawn: 7.0.6
get-stream: 8.0.1
human-signals: 5.0.0
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.3.0
onetime: 6.0.0
signal-exit: 4.1.0
strip-final-newline: 3.0.0
execa@9.6.1:
dependencies:
'@sindresorhus/merge-streams': 4.0.0
@@ -17758,8 +17681,6 @@ snapshots:
get-stream@6.0.1: {}
get-stream@8.0.1: {}
get-stream@9.0.1:
dependencies:
'@sec-ant/readable-stream': 0.4.1
@@ -18020,12 +17941,8 @@ snapshots:
human-signals@2.1.0: {}
human-signals@5.0.0: {}
human-signals@8.0.1: {}
husky@9.1.7: {}
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
@@ -18155,12 +18072,6 @@ snapshots:
is-fullwidth-code-point@3.0.0: {}
is-fullwidth-code-point@4.0.0: {}
is-fullwidth-code-point@5.1.0:
dependencies:
get-east-asian-width: 1.4.0
is-generator-function@1.1.2:
dependencies:
call-bound: 1.0.4
@@ -18227,8 +18138,6 @@ snapshots:
is-stream@2.0.1: {}
is-stream@3.0.0: {}
is-stream@4.0.1: {}
is-string@1.1.1:
@@ -18504,34 +18413,8 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.30.2
lightningcss-win32-x64-msvc: 1.30.2
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}
lint-staged@15.5.2:
dependencies:
chalk: 5.6.2
commander: 13.1.0
debug: 4.4.3
execa: 8.0.1
lilconfig: 3.1.3
listr2: 8.3.3
micromatch: 4.0.8
pidtree: 0.6.0
string-argv: 0.3.2
yaml: 2.8.2
transitivePeerDependencies:
- supports-color
listr2@8.3.3:
dependencies:
cli-truncate: 4.0.0
colorette: 2.0.20
eventemitter3: 5.0.3
log-update: 6.1.0
rfdc: 1.4.1
wrap-ansi: 9.0.2
loader-runner@4.3.1: {}
locate-path@6.0.0:
@@ -18553,14 +18436,6 @@ snapshots:
chalk: 5.6.2
is-unicode-supported: 1.3.0
log-update@6.1.0:
dependencies:
ansi-escapes: 7.2.0
cli-cursor: 5.0.0
slice-ansi: 7.1.2
strip-ansi: 7.1.2
wrap-ansi: 9.0.2
longest-streak@3.1.0: {}
loose-envify@1.4.0:
@@ -19061,8 +18936,6 @@ snapshots:
mimic-fn@2.1.0: {}
mimic-fn@4.0.0: {}
mimic-function@5.0.1: {}
min-indent@1.0.1: {}
@@ -19205,10 +19078,6 @@ snapshots:
dependencies:
path-key: 3.1.1
npm-run-path@5.3.0:
dependencies:
path-key: 4.0.0
npm-run-path@6.0.0:
dependencies:
path-key: 4.0.0
@@ -19274,10 +19143,6 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
onetime@6.0.0:
dependencies:
mimic-fn: 4.0.0
onetime@7.0.0:
dependencies:
mimic-function: 5.0.1
@@ -19441,8 +19306,6 @@ snapshots:
picomatch@4.0.3: {}
pidtree@0.6.0: {}
pixelmatch@7.1.0:
dependencies:
pngjs: 7.0.0
@@ -19940,8 +19803,6 @@ snapshots:
reusify@1.1.0: {}
rfdc@1.4.1: {}
robust-predicates@3.0.2: {}
rollup@4.59.0:
@@ -20267,16 +20128,6 @@ snapshots:
sisteransi@1.0.5: {}
slice-ansi@5.0.0:
dependencies:
ansi-styles: 6.2.3
is-fullwidth-code-point: 4.0.0
slice-ansi@7.1.2:
dependencies:
ansi-styles: 6.2.3
is-fullwidth-code-point: 5.1.0
source-map-js@1.2.1: {}
source-map-support@0.5.21:
@@ -20340,8 +20191,6 @@ snapshots:
strict-event-emitter@0.5.1: {}
string-argv@0.3.2: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -20433,8 +20282,6 @@ snapshots:
strip-final-newline@2.0.0: {}
strip-final-newline@3.0.0: {}
strip-final-newline@4.0.0: {}
strip-indent@3.0.0:
@@ -21085,12 +20932,6 @@ snapshots:
string-width: 5.1.2
strip-ansi: 7.1.2
wrap-ansi@9.0.2:
dependencies:
ansi-styles: 6.2.3
string-width: 7.2.0
strip-ansi: 7.1.2
wrappy@1.0.2: {}
ws@8.19.0: {}
@@ -21110,7 +20951,8 @@ snapshots:
yallist@3.1.1: {}
yaml@2.8.2: {}
yaml@2.8.2:
optional: true
yargs-parser@21.1.1: {}

View File

@@ -3,8 +3,8 @@
/**
* Setup Git Hooks for Prowler UI
*
* This script checks if Python pre-commit is managing git hooks.
* If not, it runs the repository's setup script to install pre-commit.
* This script checks if prek is managing git hooks.
* If not, it runs the repository's setup script to install prek.
*/
const { execSync } = require('child_process');
@@ -12,16 +12,16 @@ const fs = require('fs');
const path = require('path');
/**
* Check if Python pre-commit framework is managing git hooks
* Check if prek framework is managing git hooks
*/
function isPreCommitInstalled(gitRoot) {
function isPrekInstalled(gitRoot) {
const hookPath = path.join(gitRoot, '.git', 'hooks', 'pre-commit');
try {
if (!fs.existsSync(hookPath)) return false;
const content = fs.readFileSync(hookPath, 'utf8');
return content.includes('pre-commit') || content.includes('INSTALL_PYTHON');
return content.includes('prek') || content.includes('pre-commit') || content.includes('INSTALL_PYTHON');
} catch {
return false;
}
@@ -72,23 +72,24 @@ if (!gitRoot) {
process.exit(0);
}
if (isPreCommitInstalled(gitRoot)) {
console.log('✅ Git hooks managed by Python pre-commit framework');
console.log(' Husky hooks will be called automatically for UI files');
if (isPrekInstalled(gitRoot)) {
console.log('✅ Git hooks managed by prek framework');
console.log(' UI hooks will be called automatically for UI files');
process.exit(0);
}
// Pre-commit not installed - set it up
console.log('⚠️ Pre-commit hooks not installed');
console.log('📦 Installing pre-commit hooks from project dependencies...');
// Prek not installed - set it up
console.log('⚠️ Prek hooks not installed');
console.log('📦 Installing prek hooks...');
console.log('');
try {
runSetupScript(gitRoot);
console.log('');
console.log('✅ Pre-commit hooks installed successfully');
console.log('✅ Prek hooks installed successfully');
} catch (error) {
console.error('❌ Failed to setup git hooks');
console.error(' Please run manually from repo root: ./scripts/setup-git-hooks.sh');
console.error(' Or install prek manually: https://prek.j178.dev/installation/');
process.exit(1);
}