diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..679ffab6 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,18 @@ +#!/bin/sh +# Pre-commit: same checks as CI. Fix here, not in CI. +set -e +echo "πŸ” Pre-commit checks..." + +# Lint + types (only if TS files staged) +STAGED_TS=$(git diff --cached --name-only | grep -E '\.(ts|tsx)$' || true) +if [ -n "$STAGED_TS" ]; then + echo " β†’ lint + tsc..." + pnpm lint --quiet + npx tsc --noEmit +fi + +# Unit tests +echo " β†’ tests..." +pnpm test --run --silent + +echo "βœ… Pre-commit passed" diff --git a/CLAUDE.md b/CLAUDE.md index d9bdac40..9bab2748 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,353 +1,107 @@ # CLAUDE.md β€” Laputa App -## Project -Laputa App is a personal knowledge and life management desktop app, built with Tauri v2 + React + TypeScript + CodeMirror 6. It reads a vault of markdown files with YAML frontmatter and presents them in a four-panel UI inspired by Bear Notes. +## β›” BEFORE EVERY COMMIT β€” Non-negotiable checklist -**Full project spec** (ontology, UI design, milestones): `docs/PROJECT-SPEC.md` -**UI wireframes**: `ui-design.pen` - -## Tech Stack -- **Desktop shell**: Tauri v2 (Rust backend) -- **Frontend**: React 18+ with TypeScript -- **Editor**: CodeMirror 6 (live preview, reveal-on-focus) -- **Build**: Vite -- **Tests**: Vitest (unit), Playwright (E2E), `cargo test` (Rust) -- **Package manager**: pnpm - -## Architecture -- `src-tauri/` β€” Rust backend (file I/O, frontmatter parsing, git ops, filesystem watching) -- `src/` β€” React frontend -- `src/mock-tauri.ts` β€” Mock layer for browser testing (returns realistic test data when not in Tauri) -- `src/types.ts` β€” Shared TypeScript types (VaultEntry, etc.) -- `e2e/` β€” Playwright E2E tests and screenshot verification -- Vault path is configurable (not hardcoded) β€” the app works with "a vault at some path" -- All data lives in markdown files with YAML frontmatter, git-versioned -- The app reads/writes these files directly β€” no database -- **Luca's vault**: `~/Laputa/` (~9200 markdown files) - -## Coding Standards -- Rust: use `serde` for serialization, `gray_matter` or similar for frontmatter parsing -- TypeScript: strict mode, functional components, hooks -- Keep components responsive-ready (don't hardcode four-panel layout assumptions) -- Use Context7 MCP to look up current API docs for Tauri v2, CodeMirror 6, etc. - -## Product Philosophy - -These principles apply to every task, especially when requirements are intentionally vague. - -### Think like a PM, not just a developer - -Features in this project are often described at a high level on purpose. Luca trusts you to make sensible product decisions. When something is unclear: - -- **Don't ask, decide.** Pick the interpretation that makes the most sense for a first working version. Document your decision in the commit message or a code comment. -- **Bias toward shipping.** A working, testable feature is the goal. If you're choosing between a perfect solution that takes 4 hours and a good-enough one that takes 1 hour, ship the good-enough one first. -- **Never block waiting for instructions.** Luca may not read messages for hours. If you're stuck on a product decision, make the call yourself. The worst case is a short code review; the alternative is hours of delay. -- **Document your reasoning.** When you make a non-obvious product decision (e.g., "I chose to show archived notes grayed out rather than hiding them entirely"), note it in the relevant `docs/` file so Luca can review and adjust. - -### Always produce a design file - -Every feature must have a `design/.pen` file committed on the feature branch. This is mandatory β€” Luca reviews it as part of the In Review step. - -**The design file must be ADDITIVE** β€” it must contain ONLY the new frames for this feature. Do NOT copy ui-design.pen. - -Create a fresh file with the correct structure: -```bash -mkdir -p design - -# First, study the frame schema from ui-design.pen: -node -e " -const f = JSON.parse(require('fs').readFileSync('ui-design.pen', 'utf8')); -console.log('Frame schema:', JSON.stringify(f.children[0], null, 2)); -console.log('Variables available:', Object.keys(f.variables || {})); -" - -# Then create the feature file with ONLY new frames (empty children to start): -echo '{"children": [], "variables": {}}' > design/.pen -# Add your feature frames to children[] -``` - -⚠️ **DO NOT** `cp ui-design.pen design/.pen` β€” this copies all existing frames and the merge will find 0 new frames (all duplicates), breaking the design workflow. - -Add new frames to `children[]` for the feature's screens/states. Use existing `variables` (design tokens) from ui-design.pen β€” don't invent new values. - -**Complex feature** (new panel, new modal, new UI surface) β†’ design first, then implement. -**Simple feature** (new property, filter pill, minor modification) β†’ implement first, then update design to reflect what was built. - -Commit with: `git add design/.pen && git commit -m "design: wireframes"` - -### Always update test data - -If a feature requires new data to be testable (e.g., a new `archived: true` property in frontmatter, a new type of note, a relationship type), update `src/mock-tauri.ts` with realistic examples before writing the feature. This ensures visual verification actually tests the new code path. - -### Always keep docs current - -After every meaningful architectural decision or abstraction, update the relevant file in `docs/`. The docs in this repo are how Luca understands what was built and why. Stale docs are worse than no docs. - -## How to Work - -### Approach -- **Small steps**: Build one thing at a time. Get it working, test it, commit it. Then move to the next. -- **Test as you go**: Write tests alongside code, not after. If you build a frontmatter parser, test it immediately with real-world examples before moving on. -- **Verify constantly**: After each meaningful change, run the relevant tests (`cargo test`, `pnpm test`). Don't stack up a bunch of code and hope it all works. -- **Never develop on `main`**: all work happens on a feature branch (`task/`). This repo has CI that runs on PRs β€” pushing directly to main skips the pre-merge checks. Brian merges via PR (`gh pr create` + `gh pr merge`) after Luca approves. If you somehow end up on main, stash your work and switch to the correct branch first. -- **Commit often β€” small and atomic**: Each logical unit of work gets its own commit. **Never work for more than 20–30 minutes without committing something.** If you've been coding for 30 min and have no commit, stop and commit what you have β€” even if it's incomplete (use `wip:` prefix). This protects against session crashes and timeouts. NEVER batch multiple features or fixes into one big commit. Examples of good atomic commits: - - `feat: update color palette and CSS variables` - - `feat: restructure sidebar with collapsible sections` - - `fix: editor scroll overflow` - One concern per commit. If you're doing a multi-phase task, commit after EACH phase, not at the end. This makes reviews, reverts, and bisecting possible. -- **Documentation is code**: When you change architecture, abstractions, theme system, or any significant design β€” **update the relevant docs/** markdown files in the same commit. Documentation should always reflect current reality, not past state. Push docs changes together with code changes. - -### Testing -- `pnpm test` runs Vitest (unit tests) -- `cargo test` runs Rust tests -- `pnpm test:e2e` runs Playwright (E2E) -- Every new module should have tests -- Test with realistic data β€” use real markdown files with YAML frontmatter, not toy examples -- **Bug β†’ Test rule**: Every bug found manually that tests didn't catch MUST result in a new test (unit or E2E) so it never regresses. Ask yourself: "Why didn't tests catch this?" and close the gap. -- **New feature rule**: every task that adds or changes behavior MUST include tests that specifically cover the new behavior. "Existing tests pass" is not enough β€” new tests must exist for the new code path. This is a hard requirement for moving to In Review. -- **No coverage theater**: tests must verify real business behavior (e.g. "archiving a note calls the right Tauri command and updates state"), not framework behavior (e.g. "component renders without crashing"). The goal is confidence that the feature works, not a number. -- Edge cases matter: empty frontmatter, missing fields, malformed YAML, files with no H1 title - -### Test Coverage (MANDATORY β€” run before every commit) - -Coverage must never regress. Run these two commands before committing: +Run all of these. If any fails, fix before committing. No exceptions. ```bash -# Frontend β€” enforces 70% threshold on lines/functions/branches; exits non-zero if coverage drops -pnpm test:coverage - -# Rust β€” enforces 85% line coverage; exits non-zero if coverage drops +pnpm lint && npx tsc --noEmit # lint + types +pnpm test # unit tests +pnpm test:coverage # frontend β‰₯70% coverage +cargo test # Rust tests cargo llvm-cov --manifest-path src-tauri/Cargo.toml --fail-under-lines 85 +pre_commit_code_health_safeguard # CodeScene β‰₯8.5 β€” if it fails, fix structurally (see below) ``` -If either command exits non-zero, **do not commit** until you've added tests to restore coverage. +**CI is a safety net, not a discovery tool.** If CI catches something you didn't catch locally, that's a process failure. All these tools are available locally β€” use them while you code, not just at the end. -Current baselines (Feb 2026): Frontend β‰₯70% | Rust lines β‰₯85% (89.8% actual), functions β‰₯75% (81.5% actual). +## β›” BEFORE FIRING laputa-task-done β€” QA on real vault -### macOS / Tauri Platform Gotchas (CHECK BEFORE SUBMITTING) +1. Acquire lockfile: `echo $$ > /tmp/laputa-qa.lock && trap "rm -f /tmp/laputa-qa.lock" EXIT` +2. Kill other instances: `pkill -x laputa 2>/dev/null || true; sleep 1` +3. Start app: `pnpm tauri dev` from worktree +4. Switch vault to `~/Laputa` (not demo) +5. Test the feature/fix with real mouse clicks (`cliclick`) on real notes +6. If task touches file save: verify `git -C ~/Laputa diff` shows changes +7. If QA fails β†’ fix and re-run. Do NOT fire the signal until it passes. -These bugs slip through unit tests because JSDOM doesn't simulate real macOS behavior. Verify manually or note them explicitly. - -**Keyboard shortcuts:** -- `Option/Alt+N` on macOS produces special characters (e.g. `Β‘`, `β„’`), NOT `key:'1'`. Never use `e.key` to detect Alt+number combos. -- Use `e.code` (`'Digit1'`) for layout-independent keys, or use `Cmd+N` shortcuts instead (more standard on macOS). -- Prefer `CmdOrCtrl+N` for cross-platform shortcuts. - -**Tauri menu accelerators:** -- Adding shortcut text to the menu label (`format!("{label} Alt+1")`) is purely decorative β€” it does NOT register a keyboard shortcut. -- Always use `MenuItemBuilder::new(label).id(id).accelerator("CmdOrCtrl+1").build(app)?` to register real accelerators. -- After changing `menu.rs`, the Rust binary must recompile β€” test the running app, not just unit tests. - -**Custom macOS menu:** -- `app.set_menu(menu)` replaces the ENTIRE menu bar. If you only add a `View` submenu, you lose the standard app menus (File, Edit, Window, Help). Include all necessary submenus or use `window.set_menu()` instead. - -**App focus for keyboard events:** -- JS `window.addEventListener('keydown')` only fires when the WebView has focus. If the user is interacting with native UI elements (menus, title bar), events may not reach the JS layer. - -### Code Quality -- Prefer simple, readable code over clever abstractions -- Don't over-engineer for future features β€” build what's needed now -- If something is hacky or temporary, leave a `// TODO:` comment explaining why and what the real solution would be -- Error handling: don't silently swallow errors. Log them, surface them, or return Result types (Rust) - -### Visual Verification (MANDATORY) -Before declaring any milestone or feature complete, you MUST visually verify it works. - -**You must manually test every feature via Chrome (`claude --chrome`):** -1. **Start the dev server**: `pnpm dev` (Vite only, no Tauri needed) -2. **Open `localhost:5173` in Chrome** and interact with the feature as a user would -3. **Actually use it** β€” click buttons, navigate, type text, verify behavior matches the spec -4. **Don't just screenshot** β€” interact end-to-end. If something looks wrong, fix it before declaring done. -5. **If mock data doesn't cover the feature**, update `src/mock-tauri.ts` with appropriate test data first - -Also run Playwright for automated verification: -- `npx playwright test e2e/screenshot.spec.ts` β€” captures screenshots -- Write ad-hoc Playwright tests that click, navigate, and screenshot results - -The app has a **Tauri mock layer** (`src/mock-tauri.ts`): when running in a browser (not Tauri), it returns realistic test data. This means Chrome and Playwright can test the full UI without the Rust backend. - -**Key rule**: passing unit tests β‰  working app. If you can't see it working AND interact with it successfully, it's not done. - -### Native App QA (MANDATORY β€” Stage 1 of 2, before firing done signal) - -⚠️ **CRITICAL**: The browser/Chrome uses `mock-tauri.ts` which silently swallows Tauri commands. Bugs on real vaults NEVER surface in Chrome. You MUST test on the native Tauri app with Luca's real vault. - -**QA is a two-stage process**: you do Stage 1, Brian does Stage 2 independently before merging. - -#### Stage 1 β€” Your QA (required before firing laputa-task-done) - -**Step 1: Acquire the QA lockfile** (ensures only one laputa instance runs at a time): -```bash -LOCK=/tmp/laputa-qa.lock -WAITED=0 -while [ -f "$LOCK" ]; do - sleep 10; WAITED=$((WAITED+10)) - if [ $WAITED -ge 300 ]; then echo "QA lock timeout after 5min"; exit 1; fi -done -echo "$$" > "$LOCK" -trap "rm -f $LOCK" EXIT -``` - -**Step 2: Kill other laputa instances and start fresh**: -```bash -pkill -x laputa 2>/dev/null || true -sleep 1 -# Start native app from your worktree -pnpm tauri dev --port -# Wait for it to open (~30s) -``` - -**Step 3: Test on ~/Laputa (not demo vault)**: -- Switch vault to `~/Laputa` from the vault picker if needed -- Take screenshot, verify feature is visually present -- **Click through the happy path** with `cliclick` β€” real mouse interaction -- **If task touches save/edit**: `git -C ~/Laputa diff` must show changed files after Cmd+S -- **If task fixes a bug**: reproduce original bug scenario on ~/Laputa, confirm it's gone -- Take final screenshot - -**Step 4: Release lock and fire signal** (only if QA passed): +Fire done signal only after QA passes: ```bash rm -f /tmp/laputa-qa.lock openclaw system event --text "laputa-task-done::" --mode now ``` -If QA fails: fix the bug, re-run QA. Do NOT fire the done signal until QA passes on ~/Laputa. +## β›” CODE HEALTH β€” No shortcuts + +If `pre_commit_code_health_safeguard` flags a file: +- **Understand why** β€” use `code_health_review` via CodeScene MCP +- Fix the structural problem (extract hooks, split components, reduce complexity) +- **Never** add a JSDoc comment, `#[allow(...)]`, `// eslint-disable`, or `as any` just to pass the gate +- It's fine to take longer. False quality is worse than no quality. + +--- + +## Project + +Tauri v2 + React + TypeScript desktop app. Reads a vault of markdown files with YAML frontmatter. + +- **Spec**: `docs/PROJECT-SPEC.md` +- **Architecture**: `docs/ARCHITECTURE.md` +- **Abstractions**: `docs/ABSTRACTIONS.md` +- **Wireframes**: `ui-design.pen` +- **Luca's vault**: `~/Laputa/` (~9200 markdown files) + +## Tech Stack + +- Desktop: Tauri v2 (Rust backend) +- Frontend: React 18 + TypeScript + BlockNote editor +- Tests: Vitest (unit), Playwright (E2E), `cargo test` (Rust) +- Package manager: pnpm + +## Architecture + +- `src-tauri/src/` β€” Rust backend (file I/O, git, frontmatter parsing) +- `src/` β€” React frontend +- `src/mock-tauri.ts` β€” Mock layer for browser/test env (silently swallows Tauri calls β€” **not a substitute for native app testing**) +- `src/types.ts` β€” Shared TypeScript types + +## How to Work + +- **Never develop on `main`** β€” always on `task/` branch +- **Commit every 20–30 min** β€” atomic commits, one concern per commit (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`) +- **Update docs/** when changing architecture, abstractions, or significant design +- **Test as you go** β€” write tests alongside code, not after + +## Testing + +- Unit tests must cover real business logic, not "component renders" +- Every bug fixed manually β†’ add a regression test +- Every new feature β†’ new tests for new behavior paths +- `pnpm test:coverage` and `cargo llvm-cov` must pass before committing + +## Design File (every UI task) + +Every task with UI changes needs `design/.pen` β€” **additive only, NOT a copy of ui-design.pen**. -Use the `laputa-qa` skill scripts: ```bash -bash ~/.openclaw/skills/laputa-qa/scripts/focus-app.sh laputa -bash ~/.openclaw/skills/laputa-qa/scripts/screenshot.sh /tmp/before.png -bash ~/.openclaw/skills/laputa-qa/scripts/shortcut.sh "command" "s" # Cmd+S -bash ~/.openclaw/skills/laputa-qa/scripts/click.sh 400 300 # Γ— 2.56 for retina +mkdir -p design +# Study schema first: +node -e "const f=JSON.parse(require('fs').readFileSync('ui-design.pen','utf8')); console.log(JSON.stringify(f.children[0],null,2))" +# Start fresh: +echo '{"children":[],"variables":{}}' > design/.pen ``` -### Playwright for Testing & Verification -- `npx playwright test` β€” runs all E2E tests -- `npx playwright test e2e/screenshot.spec.ts` β€” captures a screenshot for review -- You can write ad-hoc Playwright scripts to click elements, type, scroll, and screenshot -- Use `page.screenshot({ path: 'test-results/something.png' })` to capture state -- Always screenshot before AND after interactions to verify changes +## macOS / Tauri Gotchas -### When Stuck -- Use Context7 MCP to look up current API docs (Tauri v2, CodeMirror 6, etc.) -- If a dependency doesn't work as expected, check its version and docs before trying workarounds -- If something is genuinely blocked, write what you tried and what failed β€” don't spin in circles -# AGENTS.md +- `Option+N` on macOS β†’ special chars (`Β‘`, `β„’`), not `key:'N'`. Use `e.code` or `Cmd+N`. +- Tauri menu accelerators: use `MenuItemBuilder::new(label).accelerator("CmdOrCtrl+1")` β€” decorative text in labels doesn't register shortcuts. +- `app.set_menu()` replaces the ENTIRE menu bar β€” include all submenus. -## Agent TL;DR - -- **Code Health is authoritative.** Treat it as the single source of truth for maintainability. -- **Target Code Health 10.0.** This is the standard for AI-friendly code. 9+ is not β€œgood enough.” -- **Safeguard all AI-touched code** before suggesting a commit. -- If Code Health regresses or violates goals, **refactor β€” don’t declare done.** -- Use Code Health to guide **incremental, high-impact refactorings.** -- When in doubt, **call the appropriate CodeScene MCP tool β€” don’t guess.** - ---- - -# Core Use Cases - -## 1️⃣ Safeguard All AI-Generated or Modified Code (Mandatory) - -### The CI is a safety net, not a discovery tool - -**Catch problems locally, before pushing.** You have all the tools: CodeScene MCP, eslint, tsc, coverage. Use them. Every CI failure that could have been caught locally wastes time on push/wait/fix/push cycles that are completely avoidable. - -**Rule**: never push code that you haven't already verified locally passes all quality gates. The CI should never be the first place you learn about a lint error, a coverage drop, or a CodeScene regression. - -### After every significant change (not just pre-commit): - -1. **Lint** β€” run immediately after changing TS/TSX files: - ```bash - pnpm lint # catches eslint errors before they reach CI - npx tsc --noEmit # catches type errors - ``` - -2. **CodeScene health** β€” run after any non-trivial implementation: - ```bash - pre_commit_code_health_safeguard # fails if health drops below threshold - ``` - If it flags a file: use `code_health_review` to understand *why*, then fix structurally (see rules below). Don't push until it passes. - -3. **Coverage** β€” run before committing (both must exit 0): - ```bash - pnpm test:coverage - cargo llvm-cov --manifest-path src-tauri/Cargo.toml --fail-under-lines 85 - ``` - -4. Run `code_health_review` for detailed analysis if the safeguard reports a regression. -3. If Code Health regresses or fails quality gates β€” **take it seriously, no shortcuts**: - - A CI gate failure means the code has a real structural problem. Fix it properly. - - **Never add superficial fixes to pass a gate** (e.g. a JSDoc comment to gain 0.02 points, a trivial test to hit coverage, splitting a function just to reduce line count without improving clarity). This creates "false quality" β€” the metric looks green but the problem is still there. - - Understand *why* the gate is failing: - - CodeScene low score β†’ the file has too many responsibilities. Extract hooks, split components, reduce cyclomatic complexity structurally. - - Coverage below threshold β†’ write tests that cover real business logic paths, not just "does this render" or framework boilerplate. - - Clippy/lint error β†’ fix the actual issue, don't add `#[allow(...)]` unless there's a documented reason. - - If a large/complex function is reported: extract sub-functions or hooks with clear single responsibilities. Use `code_health_auto_refactor` if available, then refine. - - It's fine to take longer. A proper fix is always better than a fast workaround. -4. Do **not** mark changes as ready unless risks are explicitly accepted. - ---- - -## 2️⃣ Guide Refactoring with Code Health (Preferred via ACE) - -When refactoring or improving code: - -1. Inspect with `code_health_review`. -2. Identify complexity, size, coupling, or other code health issues. -3. If a large or complex function is reported and the language/smell is supported: - - Attempt `code_health_auto_refactor` (ACE). - - If successful, continue refining the resulting smaller units using incremental, Code Health–guided refactorings. - - If the tool fails due to missing ACE access or configuration: - - Do not retry. - - Continue with manual, incremental refactoring guided by Code Health. -4. Refactor in **3–5 small, reviewable steps**. -5. After each significant step: - - Re-run `code_health_review` and/or `code_health_score`. - - Confirm measurable improvement or no regression. - -ACE is optional. Refactoring must always proceed, with or without ACE. - ---- - -# Technical Debt & Prioritization - -When asked what to improve: - -- Use `list_technical_debt_hotspots`. -- Use `list_technical_debt_goals`. -- Use `code_health_score` to rank risk. -- Optionally use `code_health_refactoring_business_case` to quantify ROI. - -Always produce: -- The ranked list of hotspots. -- Small, incremental refactor plans. -- Business justification when relevant. - ---- - -# Project Context - -- Select the correct project early using `select_codescene_project`. -- Assume all subsequent tool calls operate within the active project. - ---- - -# Explanation & Education - -When users ask why Code Health matters: - -- Use `explain_code_health` for fundamentals. -- Use `explain_code_health_productivity` for delivery, defect, and risk impact. -- Tie explanations to actual project data when possible. - ---- - -# Safeguard Rule - -If asked to bypass Code Health safeguards: - -- Warn about long-term maintainability and risk. -- Keep changes minimal and reversible. -- Recommend follow-up refactoring. +## QA Scripts +```bash +bash ~/.openclaw/skills/laputa-qa/scripts/focus-app.sh laputa +bash ~/.openclaw/skills/laputa-qa/scripts/screenshot.sh /tmp/out.png +bash ~/.openclaw/skills/laputa-qa/scripts/shortcut.sh "command" "s" +bash ~/.openclaw/skills/laputa-qa/scripts/click.sh 400 300 # logical coords +``` diff --git a/package.json b/package.json index 1350c283..9faf2868 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "test": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "prepare": "husky" }, "dependencies": { "@anthropic-ai/sdk": "^0.78.0", @@ -65,6 +66,7 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "gray-matter": "^4.0.3", + "husky": "^9.1.7", "jsdom": "^28.0.0", "typescript": "~5.9.3", "typescript-eslint": "^8.48.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e53e1ae..ad10ae1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,6 +153,9 @@ importers: gray-matter: specifier: ^4.0.3 version: 4.0.3 + husky: + specifier: ^9.1.7 + version: 9.1.7 jsdom: specifier: ^28.0.0 version: 28.0.0 @@ -2468,6 +2471,11 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -5999,6 +6007,8 @@ snapshots: transitivePeerDependencies: - supports-color + husky@9.1.7: {} + ignore@5.3.2: {} ignore@7.0.5: {}