diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..2674a00 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,44 @@ +name: Deploy docs to GitHub Pages + +on: + push: + branches: [main] + paths: [product-docs/**] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install dependencies + run: npm ci + working-directory: product-docs + - name: Build docs + run: npm run build + working-directory: product-docs + - uses: actions/upload-pages-artifact@v3 + with: + path: product-docs/dist + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index b9b08d2..241a389 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,11 @@ Thumbs.db # Test coverage coverage/ +# Documentation site build artifacts +product-docs/node_modules/ +product-docs/dist/ +product-docs/.astro/ + **/CLAUDE.md docs/* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ee8b3c..068ecd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,58 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] - 2026-04-06 + +### Added + +- **MCP Server** — `vibecop serve` starts an MCP server on stdio transport with 3 tools: + - `vibecop_scan` — scan a directory for AI code quality issues + - `vibecop_check` — check a single file for issues + - `vibecop_explain` — explain what a detector checks for, its severity, and category +- New dependency: `@modelcontextprotocol/sdk` for MCP protocol support +- Engine API: exported `scan()` and `checkFile()` from `engine.ts` for programmatic use +- Docs: Tier 3 MCP setup in `docs/agent-integration.md`, Phase 3.5 in `docs/design.md` + +### Fixed + +- **debug-console-in-prod:** auto-detect CLI/server projects via `package.json` `bin` field and skip — servers legitimately use console.log. Reduced default flagged methods to `log` and `debug` only (was 8 methods). Made configurable via `.vibecop.yml` `methods` array +- **undeclared-import:** skip packages importing themselves — reads `package.json` `name` field for JS/TS, normalizes hyphens to underscores for Python pyproject.toml matching +- **placeholder-in-production:** skip fixture, example, sample, mock, demo directories and `.md` files + +### Changed + +- Refactored `cli.ts` — scan/check orchestration moved to `engine.ts`, CLI is now a thin layer + +## [0.3.1] - 2026-04-04 + +### Fixed + +- Add native binding `optionalDependencies` and install verification test + +## [0.3.0] - 2026-04-04 + +### Added + +- 6 test quality detectors: assertion-roulette, sleepy-test, snapshot-only-test, empty-test, conditional-test-logic, no-error-path-test +- Custom YAML rules via `.vibecop/rules/*.yaml` using ast-grep pattern syntax +- `vibecop test-rules` command to validate custom rules against inline examples +- GCC output format (`--format gcc`) for editor/IDE integration +- Test utility refactor: shared `findTestFunctions()`, `countJsAssertions()`, `countPyAssertions()` + +### Fixed + +- `double-type-assertion` detector rewritten to use AST (was regex-based, false positives on strings) + +## [0.2.0] - 2026-04-03 + +### Added + +- Agent integration: `vibecop init` auto-detects 7 AI coding tools and generates config files +- 6 new LLM/agent safety detectors: unsafe-shell-exec, llm-call-no-timeout, dynamic-code-exec, llm-unpinned-model, llm-no-system-message, llm-temperature-not-set +- Hallucinated package detector with bundled npm top-5K allowlist +- Agent output format (`--format agent`): one finding per line, token-efficient +- Engine dedup with `DetectorMeta.priority` — keeps highest-priority finding per file:line + ## [0.1.2] - 2026-04-02 ### Fixed diff --git a/README.md b/README.md index b28b24e..9aacbc8 100644 --- a/README.md +++ b/README.md @@ -6,357 +6,122 @@ [![CI](https://github.com/bhvbhushan/vibecop/actions/workflows/ci.yml/badge.svg)](https://github.com/bhvbhushan/vibecop/actions/workflows/ci.yml) [![Playground](https://img.shields.io/badge/Try-Playground-orange)](https://vibecop-pg.bhvbhushan7.com/) -AI code quality toolkit — deterministic linter for the AI coding era. 28 detectors catch the bugs AI agents introduce: god functions, N+1 queries, unsafe shell exec, unpinned LLM models, and more. Runs automatically inside Claude Code, Cursor, Codex, Aider, and 3 other AI tools via `vibecop init`. +AI code quality toolkit — deterministic linter for the AI coding era. 35 detectors catch the bugs AI agents introduce: god functions, N+1 queries, unsafe shell exec, unpinned LLM models, and more. Runs automatically inside Claude Code, Cursor, Codex, Aider, and 7 other AI tools. Also available as an MCP server. Built on [ast-grep](https://ast-grep.github.io/) for fast, tree-sitter-based AST analysis. No LLM required — every finding is deterministic and reproducible. -## Try it Online - -**[Playground](https://vibecop-pg.bhvbhushan7.com/)** — paste code and scan instantly in your browser. +**[Documentation](https://bhvbhushan.github.io/vibecop/)** | **[Playground](https://vibecop-pg.bhvbhushan7.com/)** ## Install ```bash -# npm -npm install -g vibecop - -# bun (recommended) -bun add -g vibecop +npm install -g vibecop # or: bun add -g vibecop ``` -Requires Node.js >= 20 or Bun >= 1.0. - ## Quick Start ```bash -# Scan current directory -vibecop scan . - -# Scan specific directory with JSON output -vibecop scan src/ --format json - -# Check what detectors are available -vibecop check - -# CI mode — exit code 1 if errors found -vibecop scan . --format text - -# Scan with custom config -vibecop scan . --config .vibecop.yml +vibecop scan . # Scan current directory +vibecop scan src/ --format json # JSON output +vibecop scan . --diff HEAD # Only changed files +vibecop init # Auto-setup agent integration +vibecop serve # Start MCP server ``` ## Agent Integration -vibecop runs automatically inside your AI coding agent. Every time the agent edits a file, vibecop scans the change and blocks on findings — the agent reads the output and fixes the issue before proceeding. - -### Auto-setup (recommended) +vibecop runs inside your AI coding agent. Every edit triggers a scan — the agent reads findings and self-corrects. ```bash -npx vibecop init +npx vibecop init # Auto-detects tools, generates configs ``` -Detects which tools you have installed and generates the right config files: +| Tool | Integration | +|------|-------------| +| **Claude Code** | PostToolUse hook | +| **Cursor** | afterFileEdit hook + rules | +| **Codex CLI** | PostToolUse hook | +| **Aider** | Native `--lint-cmd` | +| **GitHub Copilot** | Custom instructions | +| **Windsurf** | Rules file | +| **Cline/Roo Code** | `.clinerules` | +| **Continue.dev / Amazon Q / Zed** | MCP server (`vibecop serve`) | ``` - vibecop — agent integration setup - - Detected tools: - ✓ Claude Code (.claude/ directory found) - ✓ Cursor (.cursor/ directory found) - ✓ Aider (aider installed) - ✗ Codex CLI (not found) - - Generated: - .claude/settings.json — PostToolUse hook (blocks on findings) - .cursor/hooks.json — afterFileEdit hook - .cursor/rules/vibecop.md — always-on lint rule - .aider.conf.yml — lint-cmd per language - - Done! vibecop will now run automatically in your agent workflow. +Agent writes code → vibecop hook fires → Findings? Agent fixes → Clean? Continue. ``` -### Supported tools - -| Tool | Integration | How it works | -|------|-------------|--------------| -| **Claude Code** | PostToolUse hook | Runs after every Edit/Write, exit 1 blocks and forces fix | -| **Cursor** | afterFileEdit hook + rules | Hook runs scan, rules file tells agent to fix findings | -| **Codex CLI** | PostToolUse hook | Same pattern as Claude Code | -| **Aider** | Native `--lint-cmd` | Built-in lint integration, runs after every edit | -| **GitHub Copilot** | Custom instructions | Instructions file tells agent to run vibecop | -| **Windsurf** | Rules file | `trigger: always_on` rule | -| **Cline/Roo Code** | `.clinerules` | Rules file tells agent to run vibecop | - -### Manual setup (Claude Code example) - -Add to `.claude/settings.json`: +## MCP Server ```json { - "hooks": { - "PostToolUse": [{ - "matcher": "Edit|Write|MultiEdit", - "hooks": [{ - "type": "command", - "command": "npx vibecop scan --diff HEAD --format agent" - }] - }] + "mcpServers": { + "vibecop": { + "command": "npx", + "args": ["vibecop", "serve"] + } } } ``` -### How the loop works - -``` -Agent writes code - → vibecop hook fires automatically - → Findings? Exit 1 → agent reads output, fixes code - → No findings? Exit 0 → agent continues -``` - -The `--format agent` output is token-efficient (one finding per line, ~30 tokens each): - -``` -src/api.ts:42:1 error unsafe-shell-exec: execSync() with template literal. Use execFile() with argument array instead. -src/llm.ts:18:5 warning llm-unpinned-model: Unpinned model alias "gpt-4o". Pin to a dated version like "gpt-4o-2024-08-06". -``` - -See [docs/agent-integration.md](docs/agent-integration.md) for full setup instructions and troubleshooting. +Three tools: `vibecop_scan`, `vibecop_check`, `vibecop_explain`. ## Benchmarks -### Vibe-coded vs established: finding density comparison - -All numbers below are real — run `vibecop scan` on any of these repos yourself to reproduce. Finding density = findings per 1,000 lines of code. - -**Established projects (professionally maintained):** - -| Project | Stars | Files | LOC | Findings | Density | -|---------|:-----:|:-----:|----:|:--------:|--------:| -| [**fastify**](https://github.com/fastify/fastify) | 65K | 275 | 74,428 | 124 | 1.7/kLOC | -| [**date-fns**](https://github.com/date-fns/date-fns) | 35K | 1,543 | 99,859 | 308 | 3.1/kLOC | -| [**TanStack/query**](https://github.com/TanStack/query) | 43K | 997 | 148,492 | 652 | 4.4/kLOC | -| [**express**](https://github.com/expressjs/express) | 66K | 141 | 21,346 | 123 | 5.8/kLOC | -| [**zod**](https://github.com/colinhacks/zod) | 35K | 356 | 70,886 | 964 | 13.6/kLOC | - -**Vibe-coded projects (AI-generated/assisted):** - -| Project | Stars | Files | LOC | Findings | Density | -|---------|:-----:|:-----:|----:|:--------:|--------:| -| [**dyad**](https://github.com/dyad-sh/dyad) | 20K | 956 | 147,284 | 1,179 | 8.0/kLOC | -| [**bolt.diy**](https://github.com/stackblitz-labs/bolt.diy) | 19.2K | 392 | 71,639 | 977 | 13.6/kLOC | -| [**code-review-graph**](https://github.com/tirth8205/code-review-graph) | 3.9K | 95 | 27,119 | 361 | 13.3/kLOC | -| [**context7**](https://github.com/upstash/context7) | 51.3K | 71 | 9,201 | 129 | 14.0/kLOC | -| [**vibe-check-mcp**](https://github.com/PV-Bhat/vibe-check-mcp-server) | 480 | 55 | 5,964 | 119 | 20.0/kLOC | -| [**magic-mcp**](https://github.com/21st-dev/magic-mcp) | 4.6K | 14 | 1,096 | 28 | 25.5/kLOC | -| [**browser-tools-mcp**](https://github.com/AgentDeskAI/browser-tools-mcp) | 7.2K | 12 | 8,346 | 414 | 49.6/kLOC | - -**Median density: established 4.4/kLOC vs vibe-coded 14.0/kLOC (3.2x higher).** Vibe-coded projects consistently trigger more findings per line of code. The v0.2 detectors found **157 additional issues** across vibe-coded repos that v0.1 missed: 63 unsafe shell executions, 53 unpinned LLM models, 39 missing system messages. - -> **Note:** Some established repos show higher-than-expected density for valid reasons — zod uses `any` deliberately for type gymnastics (634 of its 964 findings), date-fns has extensive JSDoc (218 comment-ratio findings). vibecop detects patterns, not intent. Use `.vibecop.yml` to tune or disable detectors for your codebase. +All numbers are real — run `vibecop scan` on any repo to reproduce. -### Example Output +**Established projects:** -``` -src/services/user.service.ts - 45:1 error Function 'processUserData' is too complex (232 lines, cyclomatic complexity 41, 3 params) god-function - 89:5 warning Database or API call inside a loop — potential N+1 query n-plus-one-query - 145:5 warning Database mutation result is not checked — errors will be silently ignored unchecked-db-result - -src/components/PaymentModal.tsx - 1:1 warning Component has too many hooks (8 useState, 3 useEffect, 593 lines) god-component - 201:9 warning dangerouslySetInnerHTML can lead to XSS attacks if the content is not sanitized dangerous-inner-html +| Project | Density | +|---------|--------:| +| [fastify](https://github.com/fastify/fastify) (65K stars) | 1.7/kLOC | +| [date-fns](https://github.com/date-fns/date-fns) (35K stars) | 3.1/kLOC | +| [TanStack/query](https://github.com/TanStack/query) (43K stars) | 4.4/kLOC | +| [express](https://github.com/expressjs/express) (66K stars) | 5.8/kLOC | -src/config/auth.ts - 12:5 error Placeholder placeholder domain found: "yourdomain.com" placeholder-in-production - 18:5 error Auth token stored in localStorage — vulnerable to XSS token-in-localstorage +**Vibe-coded projects:** -src/utils/api.ts - 34:12 warning Double type assertion (as unknown as X) bypasses TypeScript's type safety double-type-assertion - 67:1 info TODO comment in production code (security-related) todo-in-production - -✖ 9 problems (3 errors, 5 warnings, 1 info) -``` +| Project | Density | +|---------|--------:| +| [dyad](https://github.com/dyad-sh/dyad) (20K stars) | 8.0/kLOC | +| [bolt.diy](https://github.com/stackblitz-labs/bolt.diy) (19K stars) | 13.6/kLOC | +| [context7](https://github.com/upstash/context7) (51K stars) | 14.0/kLOC | +| [browser-tools-mcp](https://github.com/AgentDeskAI/browser-tools-mcp) (7K stars) | 49.6/kLOC | -## Detectors (28 total) - -### Quality (16 detectors) - -| ID | Detector | Description | Severity | -|----|----------|-------------|----------| -| `god-function` | God Function | Functions exceeding line, complexity, or parameter thresholds | error/warning | -| `god-component` | God Component | React components with too many hooks, lines, or imports | warning | -| `n-plus-one-query` | N+1 Query | DB/API calls inside loops or `.map(async ...)` callbacks | warning | -| `unbounded-query` | Unbounded Query | `findMany`/`findAll` without a `take`/`limit` clause | info | -| `debug-console-in-prod` | Debug Console in Prod | `console.log`/`console.debug` left in production code | warning | -| `dead-code-path` | Dead Code Path | Identical if/else branches, unreachable code after return/throw | warning | -| `double-type-assertion` | Double Type Assertion | `as unknown as X` patterns that bypass TypeScript type safety | warning | -| `excessive-any` | Excessive Any | Files with 4+ `any` type annotations | warning | -| `todo-in-production` | TODO in Production | TODO/FIXME/HACK comments, escalated if security-related | info/warning | -| `empty-error-handler` | Empty Error Handler | Catch/except blocks that silently swallow errors | warning | -| `excessive-comment-ratio` | Excessive Comment Ratio | Files with >50% comment lines | info | -| `over-defensive-coding` | Over-Defensive Coding | Redundant null checks on values that can't be null | info | -| `llm-call-no-timeout` | LLM Call No Timeout | `new OpenAI()`/`new Anthropic()` without timeout, `.create()` without max_tokens | warning | -| `llm-unpinned-model` | LLM Unpinned Model | Moving model aliases like `"gpt-4o"` that silently change behavior | warning | -| `llm-temperature-not-set` | LLM Temperature Not Set | LLM `.create()` calls without explicit `temperature` parameter | info | -| `llm-no-system-message` | LLM No System Message | Chat API calls without a `role: "system"` message | info | - -### Security (7 detectors) - -| ID | Detector | Description | Severity | -|----|----------|-------------|----------| -| `sql-injection` | SQL Injection | Template literals or string concatenation in SQL query methods | error | -| `dangerous-inner-html` | Dangerous innerHTML | `dangerouslySetInnerHTML` usage without sanitization | warning | -| `token-in-localstorage` | Token in localStorage | Auth/JWT tokens stored in XSS-accessible storage | error | -| `placeholder-in-production` | Placeholder in Production | `yourdomain.com`, `changeme`, `xxx` left in config | error | -| `insecure-defaults` | Insecure Defaults | `eval()`, `rejectUnauthorized: false`, hardcoded credentials | error | -| `unsafe-shell-exec` | Unsafe Shell Exec | `exec()`/`execSync()` with dynamic args, `subprocess` with `shell=True` | error | -| `dynamic-code-exec` | Dynamic Code Exec | `eval(variable)`, `new Function(variable)` with non-literal arguments | error | - -### Correctness (4 detectors) - -| ID | Detector | Description | Severity | -|----|----------|-------------|----------| -| `unchecked-db-result` | Unchecked DB Result | Fire-and-forget database mutations (insert/update/delete) | warning | -| `undeclared-import` | Undeclared Import | Imports not declared in package.json/requirements.txt | error | -| `mixed-concerns` | Mixed Concerns | Files importing both UI frameworks and database/server libraries | warning | -| `hallucinated-package` | Hallucinated Package | Dependencies not in top-5K npm allowlist (potential AI hallucination) | info | - -### Testing (2 detectors) - -| ID | Detector | Description | Severity | -|----|----------|-------------|----------| -| `trivial-assertion` | Trivial Assertion | `expect(true).toBe(true)` and similar no-op tests | info | -| `over-mocking` | Over-Mocking | Test files with excessive mock/spy usage | info | +**Median: established 4.4/kLOC vs vibe-coded 14.0/kLOC (3.2x higher).** ## GitHub Action -Add vibecop as a PR gate that posts inline review comments on changed lines: - ```yaml -# .github/workflows/vibecop.yml -name: vibecop -on: [pull_request] - -jobs: - scan: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: bhvbhushan/vibecop@main - with: - on-failure: comment-only # or: request-changes, label, auto-close - severity-threshold: warning - max-findings: 50 +- uses: bhvbhushan/vibecop@main + with: + on-failure: comment-only + severity-threshold: warning ``` -### Action Inputs - -| Input | Description | Default | -|-------|-------------|---------| -| `github-token` | GitHub token for API access | `${{ github.token }}` | -| `config` | Path to `.vibecop.yml` config file | `.vibecop.yml` | -| `on-failure` | Action on findings: `comment-only`, `request-changes`, `label`, `auto-close` | `comment-only` | -| `label` | Label to apply when `on-failure` is `label` | `vibecop:needs-review` | -| `max-findings` | Maximum findings to report (0 = unlimited) | `50` | -| `severity-threshold` | Minimum severity for inline comments (`error`, `warning`, `info`) | `warning` | -| `working-directory` | Directory to scan (relative to repo root) | `.` | - -### Action Outputs - -| Output | Description | -|--------|-------------| -| `findings-count` | Total number of findings | -| `errors-count` | Number of error-severity findings | -| `warnings-count` | Number of warning-severity findings | -| `has-findings` | Whether any findings were detected (`true`/`false`) | -| `scan-time-ms` | Scan duration in milliseconds | - -## Configuration - -Create `.vibecop.yml` in your project root: - -```yaml -rules: - god-function: - severity: warning - debug-console-in-prod: - severity: "off" # disable a detector - excessive-any: - severity: warning - -ignore: - - "**/dist/**" - - "**/vendor/**" - - "**/generated/**" - -pr-gate: - on-failure: request-changes - severity-threshold: warning - max-findings: 50 - label: "vibecop:needs-review" -``` - -## CLI Options - -| Flag | Description | Default | -|------|-------------|---------| -| `--format` | Output format: `text`, `json`, `html`, `sarif`, `github`, `agent` | `text` | -| `--config` | Path to config file | `.vibecop.yml` | -| `--no-config` | Ignore config file | | -| `--max-findings` | Maximum findings to report | `100` | -| `--output` | Write report to file | | +## Detectors (35) -## Languages +4 categories: **Quality** (16), **Security** (7), **Correctness** (4), **Testing** (8). -| Language | Extensions | Detectors | -|----------|-----------|-----------| -| TypeScript | `.ts`, `.tsx` | All 28 | -| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | 24 (excludes TS-specific) | -| Python | `.py` | 14 (correctness, quality, security) | +Catches: god functions, N+1 queries, unsafe shell exec, SQL injection, hardcoded secrets, trivial assertions, empty tests, unpinned LLM models, hallucinated packages, and more. -## Architecture - -``` -vibecop CLI (Commander) -+-- Scan Engine -- discovers files, loads AST, runs detectors, dedup by priority -+-- Init Wizard -- auto-detects AI tools, generates hook/rule configs -+-- Config Loader (Zod) -- validates .vibecop.yml, merges defaults, per-rule config -+-- Detectors (28) -- AST pattern matching via ast-grep (@ast-grep/napi) -+-- Formatters (6) -- text, json, html, sarif, github, agent output -+-- Project Analyzer -- parses package.json, requirements.txt, lockfiles -+-- GitHub Action -- diff parser, finding filter, PR review poster -``` - -## Versioning - -vibecop follows [Semantic Versioning](https://semver.org/): - -- **0.x.y** ... pre-1.0, the API may change between minor versions -- **PATCH** (0.x.Y) ... bug fixes, new detectors, doc updates -- **MINOR** (0.X.0) ... new detector categories, output formats, config options -- **MAJOR** (X.0.0) ... breaking CLI changes, removed detectors, config format changes +[Full detector reference →](https://bhvbhushan.github.io/vibecop/detectors/overview/) ## Roadmap -- [x] **Phase 1**: Core scanner with 7 detectors, 5 output formats, `.vibecop.yml` config -- [x] **Phase 2**: PR Gate GitHub Action, 15 new detectors (7 → 22), real-world validation -- [x] **Phase 2.5**: Agent integration (7 tools), 6 LLM/agent detectors (22 → 28), `vibecop init`, `--format agent` -- [ ] **Phase 3**: MCP server, VS Code extension, cross-file analysis -- [ ] **Phase 4**: LLM-powered deep review mode (separation of concerns, semantic duplication) - -## Contributing - -See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code standards, and how to add new detectors. - -## Security - -See [SECURITY.md](SECURITY.md) for reporting vulnerabilities. - -## Code of Conduct - -See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). - -## License - -[MIT](LICENSE) +- [x] **Phase 1**: Core scanner — 7 detectors, 5 output formats +- [x] **Phase 2**: PR Gate GitHub Action, 15 new detectors +- [x] **Phase 2.5**: Agent integration (7 tools), 6 LLM/agent detectors, `vibecop init` +- [x] **Phase 3**: Test quality detectors, custom YAML rules (28 → 35) +- [x] **Phase 3.5**: MCP server with scan/check/explain tools +- [ ] **Phase 4**: Context optimization (Read tool interception, AST skeleton caching) +- [ ] **Phase 5**: VS Code extension, cross-file analysis + +## Links + +- **[Documentation](https://bhvbhushan.github.io/vibecop/)** +- **[Playground](https://vibecop-pg.bhvbhushan7.com/)** +- [Contributing](CONTRIBUTING.md) +- [Security](SECURITY.md) +- [Changelog](CHANGELOG.md) +- [License](LICENSE) (MIT) diff --git a/bun.lock b/bun.lock index da29bb3..65f1c40 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "dependencies": { "@ast-grep/lang-python": "^0.0.6", "@ast-grep/napi": "^0.42.0", + "@modelcontextprotocol/sdk": "^1.29.0", "commander": "^13.1.0", "yaml": "^2.7.1", "zod": "^3.24.4", @@ -19,6 +20,17 @@ "bun-types": "^1.3.11", "typescript": "^5.8.3", }, + "optionalDependencies": { + "@ast-grep/napi-darwin-arm64": "^0.42.0", + "@ast-grep/napi-darwin-x64": "^0.42.0", + "@ast-grep/napi-linux-arm64-gnu": "^0.42.0", + "@ast-grep/napi-linux-arm64-musl": "^0.42.0", + "@ast-grep/napi-linux-x64-gnu": "^0.42.0", + "@ast-grep/napi-linux-x64-musl": "^0.42.0", + "@ast-grep/napi-win32-arm64-msvc": "^0.42.0", + "@ast-grep/napi-win32-ia32-msvc": "^0.42.0", + "@ast-grep/napi-win32-x64-msvc": "^0.42.0", + }, }, }, "packages": { @@ -56,6 +68,10 @@ "@ast-grep/setup-lang": ["@ast-grep/setup-lang@0.0.6", "", {}, "sha512-aSYZNF5nGQMxnwiA3Lcc8zi0AtpGlug47ADgqCgP/2n7p922FbnW7vo1rbW4kKjW72B/3mDdN7uvYidv8JnPnw=="], + "@hono/node-server": ["@hono/node-server@1.19.12", "", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], @@ -82,18 +98,184 @@ "@vercel/ncc": ["@vercel/ncc@0.38.4", "", { "bin": { "ncc": "dist/ncc/cli.js" } }, "sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.3.2", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="], + "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hono": ["hono@4.12.11", "", {}, "sha512-r4xbIa3mGGGoH9nN4A14DOg2wx7y2oQyJEb5O57C/xzETG/qx4c7CVDQ5WMeKHZ7ORk2W0hZ/sQKXTav3cmYBA=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-with-bigint": ["json-with-bigint@3.5.8", "", {}, "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici": ["undici@6.24.1", "", {}, "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA=="], @@ -102,10 +284,20 @@ "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + "@actions/github/@actions/http-client": ["@actions/http-client@3.0.2", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^6.23.0" } }, "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA=="], } } diff --git a/docs/agent-integration.md b/docs/agent-integration.md deleted file mode 100644 index 5fb3032..0000000 --- a/docs/agent-integration.md +++ /dev/null @@ -1,296 +0,0 @@ -# Agent Integration - -vibecop integrates with AI coding agents as an automatic linter that runs after every code edit. The agent reads findings from stdout and self-corrects before proceeding. - -## Quick Start - -Run the setup wizard to auto-detect your tools and generate config files: - -```bash -npx vibecop init -``` - -This detects installed/active tools and writes the appropriate config files to your project. For manual setup, copy the relevant files from the [`examples/`](../examples/) directory. - -## How It Works - -vibecop plugs into agent hook systems. After each file edit, the hook fires `npx vibecop scan`, which outputs findings the agent reads and resolves: - -``` -Agent generates code - → Hook fires: npx vibecop scan --diff HEAD --format agent - → stdout: one-per-line findings (exit 1) - → Agent reads findings, auto-corrects code - → Hook re-runs: clean (exit 0) → proceed -``` - -This creates a tight feedback loop: the agent never moves on while there are unresolved findings. - -## Exit Codes - -| Code | Meaning | -|------|---------| -| `0` | No findings — clean | -| `1` | One or more findings found | -| `2` | Scan error (bad args, git error, etc.) | - -## Output Format - -`--format agent` produces one finding per line, suitable for agent parsing: - -``` -file:line:col severity detector-id: message. suggestion -``` - -Example: - -``` -src/auth.ts:42:5 error no-hardcoded-secrets: Hardcoded secret detected. Move to environment variable. -src/utils.ts:18:1 warning dead-code: Unreachable code after return statement. Remove or restructure. -``` - -Fields: -- `file:line:col` — location -- `severity` — `error`, `warning`, or `info` -- `detector-id` — machine-readable rule ID -- `message` — human-readable description -- `suggestion` — how to fix it - -## Tier 1 Tools — Deterministic Hooks - -These tools support native hook execution. vibecop runs synchronously after each edit and blocks the agent until findings are resolved. - -### Claude Code - -Hook type: `PostToolUse` — fires after any `Edit`, `Write`, or `MultiEdit` tool call. - -Create `.claude/settings.json`: - -```json -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|MultiEdit", - "hooks": [ - { - "type": "command", - "command": "npx vibecop scan --diff HEAD --format agent" - } - ] - } - ] - } -} -``` - -Or copy from [`examples/claude-code/`](../examples/claude-code/). - -### Cursor - -Hook type: `afterFileEdit` — fires after any file save. - -Create `.cursor/hooks.json`: - -```json -{ - "hooks": { - "afterFileEdit": [ - { - "command": "npx vibecop scan --diff HEAD --format agent" - } - ] - } -} -``` - -Also create `.cursor/rules/vibecop.md` to reinforce via the rules system: - -```markdown ---- -trigger: always_on ---- - -After every code edit, review vibecop findings and fix issues before proceeding. -Run: npx vibecop scan --diff HEAD --format agent -``` - -Or copy from [`examples/cursor/`](../examples/cursor/). - -### Codex CLI - -Hook type: `PostToolUse` — same structure as Claude Code. - -Create `.codex/hooks.json`: - -```json -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|MultiEdit", - "hooks": [ - { - "type": "command", - "command": "npx vibecop scan --diff HEAD --format agent" - } - ] - } - ] - } -} -``` - -Or copy from [`examples/codex/`](../examples/codex/). - -### Aider - -Aider supports a `lint-cmd` config that runs after each edit. vibecop acts as the linter for TypeScript, JavaScript, and Python files. - -Create `.aider.conf.yml`: - -```yaml -lint-cmd: - - "typescript: npx vibecop scan --format text" - - "javascript: npx vibecop scan --format text" - - "python: npx vibecop scan --format text" -auto-lint: true -``` - -Note: Aider uses `--format text` (not `agent`) since it reads linter output differently from streaming agents. - -Or copy from [`examples/aider/`](../examples/aider/). - -## Tier 2 Tools — LLM-Mediated Instructions - -These tools do not have deterministic hook execution. Instead, vibecop is injected as a persistent instruction into the agent's context. The LLM follows the instruction voluntarily. - -### GitHub Copilot - -Add to `.github/copilot-instructions.md`: - -```markdown -## vibecop - -After every code edit, run `npx vibecop scan --diff HEAD --format agent` and fix any findings before proceeding. -``` - -If the file already exists, append the `## vibecop` section. `vibecop init` handles this automatically. - -Or copy from [`examples/copilot/`](../examples/copilot/). - -### Windsurf - -Create `.windsurf/rules/vibecop.md`: - -```markdown ---- -trigger: always_on ---- - -After every code edit, review vibecop findings and fix issues before proceeding. -Run: npx vibecop scan --diff HEAD --format agent -``` - -Or copy from [`examples/windsurf/`](../examples/windsurf/). - -### Cline - -Create `.clinerules`: - -``` -After every code edit, run `npx vibecop scan --diff HEAD --format agent` and fix any findings before proceeding. -``` - -Or copy from [`examples/cline/`](../examples/cline/). - -## Troubleshooting - -### vibecop not found - -If `npx vibecop` fails with "command not found", install it globally: - -```bash -npm install -g vibecop -# then use: vibecop scan --diff HEAD --format agent -``` - -Or add it as a dev dependency: - -```bash -npm install --save-dev vibecop -# npx will resolve it from node_modules -``` - -### No findings reported - -Verify vibecop can see your changes: - -```bash -git diff HEAD # should show modified files -npx vibecop scan --diff HEAD --format agent -``` - -If there are no staged/unstaged changes, the scan has nothing to check. Make sure your agent's edits are tracked by git (the project must be a git repo). - -### Hook timeout - -If your hook system has a timeout (common in Cursor), vibecop should complete within a few seconds on most codebases. For large repos, scope the scan: - -```bash -npx vibecop scan --diff HEAD --format agent --path src/ -``` - -### Permission issues - -On some systems, `npx` may require a PATH that includes the global npm bin. If the hook fires but `npx` is not found, use the full path: - -```bash -$(npm root -g)/.bin/vibecop scan --diff HEAD --format agent -``` - -Or use `node_modules/.bin/vibecop` if installed locally. - -## Configuration - -Customize vibecop behavior via `.vibecop.yml` in your project root. - -### Disable specific rules - -```yaml -rules: - no-hardcoded-secrets: off - dead-code: warn -``` - -### Change severity - -```yaml -rules: - large-function: error # escalate from warning - console-log: off # silence entirely -``` - -### Ignore paths - -```yaml -ignore: - - "**/*.test.ts" - - "dist/**" - - "node_modules/**" -``` - -### Full example - -```yaml -rules: - no-hardcoded-secrets: error - dead-code: warn - large-function: off - -ignore: - - "**/*.test.ts" - - "dist/**" -``` - -Changes to `.vibecop.yml` take effect immediately — no restart needed. diff --git a/docs/benchmarks.md b/docs/benchmarks.md deleted file mode 100644 index c4acfff..0000000 --- a/docs/benchmarks.md +++ /dev/null @@ -1,102 +0,0 @@ -# vibecop Benchmarks - -**Date:** 2026-04-03 -**vibecop version:** 0.2.0 -**Detectors active:** 28 - ---- - -## Overview - -Finding density comparison between established, professionally maintained open-source projects and vibe-coded (AI-generated/assisted) projects. All numbers are real — run `vibecop scan` on any repo to reproduce. - -**Finding density** = findings per 1,000 lines of code (findings/kLOC). Normalises for project size. - ---- - -## Methodology - -### How findings are counted - -`vibecop scan --format json --no-config --max-findings 2000` on each target. `summary.total` from JSON output. `--no-config` ensures no per-project suppression. - -### How LOC is counted - -`wc -l` across all `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.py` files (excluding `node_modules`, `dist`, `build`). - ---- - -## Results - -### Established projects (professionally maintained) - -| Project | Stars | Files | LOC | Findings | Density | -|---------|:-----:|:-----:|----:|:--------:|--------:| -| [fastify](https://github.com/fastify/fastify) | 65K | 275 | 74,428 | 124 | 1.7/kLOC | -| [date-fns](https://github.com/date-fns/date-fns) | 35K | 1,543 | 99,859 | 308 | 3.1/kLOC | -| [TanStack/query](https://github.com/TanStack/query) | 43K | 997 | 148,492 | 652 | 4.4/kLOC | -| [express](https://github.com/expressjs/express) | 66K | 141 | 21,346 | 123 | 5.8/kLOC | -| [zod](https://github.com/colinhacks/zod) | 35K | 356 | 70,886 | 964 | 13.6/kLOC | - -**Median: 4.4/kLOC | Average: 5.7/kLOC** - -### Vibe-coded projects (AI-generated/assisted) - -| Project | Stars | Files | LOC | Findings | Density | -|---------|:-----:|:-----:|----:|:--------:|--------:| -| [dyad](https://github.com/dyad-sh/dyad) | 20K | 956 | 147,284 | 1,179 | 8.0/kLOC | -| [code-review-graph](https://github.com/tirth8205/code-review-graph) | 3.9K | 95 | 27,119 | 361 | 13.3/kLOC | -| [bolt.diy](https://github.com/stackblitz-labs/bolt.diy) | 19.2K | 392 | 71,639 | 977 | 13.6/kLOC | -| [context7](https://github.com/upstash/context7) | 51.3K | 71 | 9,201 | 129 | 14.0/kLOC | -| [vibe-check-mcp](https://github.com/PV-Bhat/vibe-check-mcp-server) | 480 | 55 | 5,964 | 119 | 20.0/kLOC | -| [magic-mcp](https://github.com/21st-dev/magic-mcp) | 4.6K | 14 | 1,096 | 28 | 25.5/kLOC | -| [browser-tools-mcp](https://github.com/AgentDeskAI/browser-tools-mcp) | 7.2K | 12 | 8,346 | 414 | 49.6/kLOC | - -**Median: 14.0/kLOC | Average: 20.6/kLOC** - -### Comparison - -| Metric | Established | Vibe-coded | Ratio | -|--------|:-----------:|:----------:|:-----:| -| Median density | 4.4/kLOC | 14.0/kLOC | **3.2x** | -| Average density | 5.7/kLOC | 20.6/kLOC | **3.6x** | - ---- - -## Notes on established repo findings - -Some established repos show higher density for valid reasons: - -- **zod** (13.6/kLOC): 634 of 964 findings are `excessive-any`. Zod deliberately uses `any` for TypeScript type gymnastics — this is intentional, not a code smell. -- **date-fns** (3.1/kLOC): 218 of 308 findings are `excessive-comment-ratio`. date-fns has extensive JSDoc documentation — by design, not an AI pattern. -- **express** (5.8/kLOC): 73 of 123 findings are `placeholder-in-production`. Express uses example domains in comments. - -vibecop detects patterns, not intent. Use `.vibecop.yml` to tune or disable detectors for your codebase. - ---- - -## v0.2 new detector impact - -The 6 new LLM/agent safety detectors found **157 additional issues** across the vibe-coded repos that the v0.1 detectors missed entirely: - -| Detector | Findings | Example repo | -|----------|:--------:|--------------| -| unsafe-shell-exec | 63 | dyad (47), context7 (6), code-review-graph (5) | -| llm-unpinned-model | 53 | bolt.diy (32), dyad (12), vibe-check-mcp (6) | -| llm-no-system-message | 39 | dyad (31), bolt.diy (7), vibe-check-mcp (1) | -| llm-call-no-timeout | 2 | vibe-check-mcp (2) | - -Zero v0.2 detector findings on the 5 established repos (they don't use LLM APIs or dynamic shell execution). - ---- - -## Reproducing - -```bash -# Fixture benchmark (committed to repo) -bash scripts/benchmark.sh - -# Full open-source benchmark (clones repos) -git clone --depth 1 https://github.com/fastify/fastify /tmp/fastify -vibecop scan /tmp/fastify --format json --no-config --max-findings 2000 -``` diff --git a/docs/design.md b/docs/design.md deleted file mode 100644 index 98b2ef7..0000000 --- a/docs/design.md +++ /dev/null @@ -1,487 +0,0 @@ -# vibecop — AI Code Quality Toolkit: Design & Implementation Plan - -**Date:** 2026-04-03 -**Status:** Engineering Review Complete / Ready for Implementation | v0.2 Implementation Complete - ---- - -## Overview - -**vibecop** — the open-source linter purpose-built for the AI coding era. Detects AI-specific code antipatterns that traditional tools miss: hallucinated imports, over-mocking, trivial assertions, insecure defaults, excessive comments, LLM API misuse, and unsafe shell execution. Ships 28 detectors, integrates with 7+ AI coding tools via `vibecop init`, and runs in CI in under 60 seconds. Requires no API keys, fully deterministic, zero network calls. - -## Problem Statement - -- AI-generated code has 1.7x more issues per PR than human code (CodeRabbit, 470 PRs) -- 4x maintenance costs by year 2 for unmanaged AI code -- 19.7% of AI-suggested packages are hallucinations (USENIX Security 2025) -- 90%+ of AI code issues are code smells -- OSS maintainers drowning in AI slop PRs (Curl, Jazzband, Godot, tldraw affected) - -## Gaps Addressed - -- **Gap 1 (AI Slop Defense):** PR quality gate for OSS maintainers -- **Gap 2 (AI Code Debt Scanner):** Codebase scanner for AI-generated tech debt -- **Gap 6 (AI Test Quality Evaluator):** Meaningful coverage scoring for AI-generated tests (Phase 3) - ---- - -## Competitive Analysis - -| Tool | Code Analysis | AI-Specific | PR Gate | CLI Scan | Test Quality | OSS | -|------|:-:|:-:|:-:|:-:|:-:|:-:| -| anti-slop | No | Metadata only | Yes | No | No | Yes | -| AI-SLOP-Detector | Yes | Yes (Python) | No | Yes | No | Yes | -| SonarQube CE | Yes | No (paid only) | Partial | Yes | No | Yes | -| Qodo/PR-Agent | LLM-based | No | Yes | No | Partial | Yes | -| Semgrep | Yes | No catalog | No | Yes | No | Yes | -| **vibecop** | **Yes** | **Yes (core)** | **Yes** | **Yes** | **Yes** | **Yes** | - -### Key Competitor Details - -**Anti-Slop (peakoss/anti-slop):** 31 checks across 8 categories — ALL metadata-level (PR title, description, account age, commit format). Zero code content analysis. Built by Coolify maintainers handling 120+ slop PRs/month. - -**AI-SLOP-Detector (flamehaven01):** Python CLI using Logic Density Ratio, Buzzword Inflation, Unused Dependencies metrics. Self-calibrating weights. Python-primary only, no PR gate. - -**SonarQube:** AI Code Assurance exists but is paid-only (Server/Cloud editions). Community Edition has zero AI-specific features. - -**PR-Agent (Qodo):** LLM-powered = non-deterministic, costly, latency. Highest F1 (60.1%) on code review benchmark but generates noise. - ---- - -## Academic Research Foundation - -1. **SpecDetect4AI** (arxiv 2509.20491) — 22 AI-specific code smells, 88.66% precision. Declarative DSL with first-order AST predicates. **Architecture reference.** -2. **SpecDetect4LLM** (arxiv 2512.18020) — 5 LLM-specific code smells, 60.50% of systems affected, 86.06% precision. -3. **Slopsquatting** (USENIX Security 2025) — 576K code samples, 16 LLMs: 19.7% hallucinated packages. 43% consistently hallucinated across prompts. -4. **AI Detection Unreliable** (arxiv 2411.04299) — Stylometric authorship detection fails. Focus on quality patterns, not authorship. - -### SpecDetect4LLM Mapping (v0.2) - -| SpecDetect4LLM ID | Smell | vibecop Detector | Status | -|-------------------|-------|------------------|--------| -| UMM | Unbounded Max Metrics | llm-call-no-timeout | Implemented | -| NMVP | No Model Version Pinning | llm-unpinned-model | Implemented | -| NSM | No System Message | llm-no-system-message | Implemented | -| TNES | Temperature Not Explicitly Set | llm-temperature-not-set | Implemented | -| NSO | No Structured Output | — | Deferred (requires downstream parse detection) | - ---- - -## Engineering Review Decisions - -The following 10 decisions were finalized during engineering and CEO review and supersede all earlier design notes: - -1. **Parser: @ast-grep/napi** — 13K stars, 9 platform binaries, 10-50x faster than raw tree-sitter, ships pre-built native binaries via napi-rs. tree-sitter is NOT used directly. -2. **Single package** — No monorepo, no `@vibecop/cli` / `@vibecop/core` split. Single `vibecop` npm package. -3. **Bun for build/test/run** — `bun test` (not Vitest), `bun build` (not tsup). Node.js 20+ for end-user runtime compatibility. -4. **Text-default output** — Text is the default `--format`. SARIF is optional, not primary. -5. **7 detectors for v0.1, 28 in v0.2** — v0.1 scope: undeclared-import, empty-error-handler, trivial-assertion, excessive-comment-ratio, over-defensive-coding, insecure-defaults, over-mocking. v0.2 adds 21 additional detectors. -6. **Lock-file validation (no network)** — Parse `package.json`, lock files, and `requirements.txt` to answer "Is this import declared?" Zero network calls, zero overhead. -7. **Deferred detectors** — hallucinated-api-call, copy-paste-duplication, tautological-test, over-abstraction, buzzword-comments are explicitly out of v0.1 scope. -8. **No YAML rule format** — All detectors are implemented in TypeScript. No YAML DSL. -9. **Config: `.vibecop.yml`** — Loaded at startup, validated with Zod, minimal schema. -10. **Languages: JS, TS, TSX, Python** — JS/TS/TSX built-in to @ast-grep/napi; Python via `@ast-grep/lang-python` + `registerDynamicLanguage()`. - ---- - -## Architecture - -### File Layout - -``` -vibecop/ ← single npm package -├── src/ -│ ├── cli.ts ← Commander.js entry (scan, check, init commands) -│ ├── engine.ts ← File discovery, detector runner, dedup, report builder -│ ├── config.ts ← .vibecop.yml loading + Zod validation -│ ├── project.ts ← Parse package.json, lock files, manifests → ProjectInfo -│ ├── init.ts ← vibecop init setup wizard -│ ├── formatters/ -│ │ ├── text.ts ← Default: stylish terminal output -│ │ ├── json.ts ← Programmatic JSON -│ │ ├── sarif.ts ← Optional SARIF 2.1.0 (~80 LOC hand-rolled) -│ │ ├── github.ts ← ::error annotations + GITHUB_STEP_SUMMARY -│ │ ├── html.ts ← Single-file HTML report -│ │ └── agent.ts ← Agent output format (token-efficient, one finding per line) -│ ├── detectors/ -│ │ ├── utils.ts ← makeFinding/makeLineFinding helpers -│ │ ├── undeclared-import.ts -│ │ ├── empty-error-handler.ts -│ │ ├── trivial-assertion.ts -│ │ ├── excessive-comment-ratio.ts -│ │ ├── over-defensive-coding.ts -│ │ ├── insecure-defaults.ts -│ │ ├── over-mocking.ts -│ │ └── ... (21 additional detectors) -│ ├── data/ -│ │ └── known-packages.json ← Bundled npm allowlist for hallucinated-package detection -│ └── types.ts ← Detector, DetectionContext, Finding, ProjectInfo, etc. -├── test/ -├── examples/ ← Example configs for 7 AI coding tools -├── package.json -├── tsconfig.json -├── .vibecop.yml ← Self-dogfood config -└── docs/ - └── design.md ← This file -``` - -### Engine Dedup - -When multiple detectors flag the same file:line, the engine keeps only the highest-priority finding. Priority is set via `DetectorMeta.priority` (default: 0). New agent/LLM detectors use `priority: 10`. - -### Tech Stack - -| Component | Choice | Rationale | -|-----------|--------|-----------| -| Runtime | Node.js 20+ / bun | bun for dev, Node for broad end-user compat | -| Language | TypeScript | Type safety for AST operations | -| Parser | @ast-grep/napi | 13K stars, 9 platform binaries, 10-50x faster than raw tree-sitter | -| Lang: Python | @ast-grep/lang-python | One import + `registerDynamicLanguage()` | -| CLI | Commander.js | Standard, widely understood | -| Config validation | Zod | Schema validation with good error messages | -| Output: SARIF | @types/sarif (types only) | Hand-roll ~80 LOC serializer, no runtime dep | -| Test | bun test | Built-in, no extra dependency | -| Build | bun build | Built-in | -| Distribution | npm (`npx vibecop scan`) | Largest reach | - ---- - -## Core Interfaces - -```typescript -interface Detector { - id: string; - meta: DetectorMeta; - detect(ctx: DetectionContext): Finding[]; -} - -interface DetectorMeta { - name: string; - description: string; - severity: 'error' | 'warning' | 'info'; - category: 'correctness' | 'quality' | 'security' | 'testing'; - languages: Lang[]; - priority?: number; // Higher priority wins in dedup (default: 0) -} - -interface DetectionContext { - file: FileInfo; - root: SgRoot; // ast-grep root node - source: string; // raw file text - project: ProjectInfo; // dependencies, lock file data - config: RuleConfig; // per-rule config overrides -} - -interface Finding { - detectorId: string; - message: string; - severity: 'error' | 'warning' | 'info'; - file: string; - line: number; - column: number; - endLine?: number; - endColumn?: number; - suggestion?: string; -} - -interface ProjectInfo { - dependencies: Set; - devDependencies: Set; - manifests: string[]; -} -``` - ---- - -## Data Flow - -``` -CLI args - → loadConfig(.vibecop.yml or defaults) - → loadProjectInfo(package.json, lock files, requirements.txt, pyproject.toml) - → discoverFiles(path, config.ignore, .gitignore) - → for each file: - → parse with ast-grep (language auto-detected from extension) - → run each enabled detector(ctx) → Finding[] - → isolate: if detector throws, log error, continue - → aggregate all findings - → dedupFindings: group by file:line, keep highest priority - → apply --max-findings N cap (default 50) - → format(findings, --format flag) - → exit(findings.length > 0 ? 1 : 0) -``` - ---- - -## CLI Commands (v0.2) - -``` -vibecop scan [path] - # Scan directory (default: cwd) - --format text|json|github|sarif|html|agent (default: text) - --config - --no-config - --max-findings N (default: 50) - --verbose (timing summary) - --diff (scan only changed files vs git ref) - --stdin-files (read file list from stdin) - -vibecop check - # Single file scan - --format text|json|github|sarif|html|agent - --max-findings N - --verbose - -vibecop init - # Auto-detect AI coding tools and generate integration configs -``` - ---- - -## Detectors (28 total) - -### Original v0.1 Detectors (7) - -| # | Detector | Category | Severity | Key Pattern | -|---|----------|----------|----------|-------------| -| 1 | undeclared-import | correctness | error | Import not in package.json/lock file/requirements.txt | -| 2 | empty-error-handler | quality | warning | `catch(e) { console.log(e) }`, bare `except: pass` | -| 3 | trivial-assertion | testing | warning | `expect(true).toBe(true)`, `assert True` | -| 4 | excessive-comment-ratio | quality | info | comment LOC / code LOC > threshold (default 0.5) | -| 5 | over-defensive-coding | quality | info | Redundant null checks, unnecessary try/catch | -| 6 | insecure-defaults | security | error | Hardcoded secrets, `rejectUnauthorized: false`, `eval()` | -| 7 | over-mocking | testing | warning | mock/spy count > assertion count in test files | - -### v0.1.x Additional Detectors (14) - -| # | Detector | Category | Severity | Key Pattern | -|---|----------|----------|----------|-------------| -| 8 | dead-code-path | quality | warning | Unreachable code after return/throw | -| 9 | debug-console-in-prod | quality | warning | `console.log/debug` in non-test files | -| 10 | double-type-assertion | quality | warning | `as unknown as T` double casts | -| 11 | excessive-any | quality | warning | Overuse of `any` type annotation | -| 12 | god-component | quality | warning | React component with too many responsibilities | -| 13 | god-function | quality | warning | Function exceeding line/complexity threshold | -| 14 | sql-injection | security | error | Dynamic SQL string concatenation | -| 15 | dangerous-inner-html | security | error | `dangerouslySetInnerHTML` with dynamic content | -| 16 | unbounded-query | security | warning | DB query without LIMIT clause | -| 17 | mixed-concerns | quality | info | Business logic mixed with UI in component | -| 18 | n-plus-one-query | quality | warning | DB query inside loop | -| 19 | token-in-localstorage | security | warning | Auth token stored in localStorage | -| 20 | placeholder-in-production | quality | error | Placeholder/TODO text in user-facing strings | -| 21 | todo-in-production | quality | info | TODO/FIXME comments in production code | - -### v0.2 Agent Safety + LLM Detectors (6) - -| # | Detector | Source | Category | Severity | Pattern | -|---|----------|--------|----------|----------|---------| -| 22 | unsafe-shell-exec | OWASP Agentic | security | error | `exec()`/`execSync()` with dynamic arg, subprocess with `shell=True` | -| 23 | llm-call-no-timeout | SpecDetect4LLM UMM | quality | warning | OpenAI/Anthropic constructor without timeout | -| 24 | dynamic-code-exec | OWASP Agentic | security | error | `eval()`/`new Function()` with variable arg | -| 25 | llm-unpinned-model | SpecDetect4LLM NMVP | quality | warning | Moving model aliases like `"gpt-4o"` | -| 26 | llm-no-system-message | SpecDetect4LLM NSM | quality | info | Chat API call without system message | -| 27 | llm-temperature-not-set | SpecDetect4LLM TNES | quality | info | LLM API call without temperature | - -### v0.2 Package Verification (1) - -| # | Detector | Source | Category | Severity | Pattern | -|---|----------|--------|----------|----------|---------| -| 28 | hallucinated-package | USENIX Security 2025 | correctness | info | Dependencies not in bundled npm allowlist | - -### Detector Implementation Notes - -**undeclared-import:** Parses `package.json` deps, lock files (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`), and `requirements.txt` / `pyproject.toml`. Answers "Is this import declared?" with zero network calls. Registry cross-reference is explicitly out of scope. - -**insecure-defaults:** Pattern-match only — no network lookups, no external validation. - -**excessive-comment-ratio:** Configurable threshold via `.vibecop.yml`. Default 0.5 (50% comment lines triggers warning). - -**hallucinated-package:** Cross-references imports against `src/data/known-packages.json`, a bundled allowlist derived from npm top packages. Zero network calls. - -All detectors are implemented in TypeScript. No YAML rule format. - ---- - -## Configuration (.vibecop.yml) - -Minimal schema, validated with Zod at startup. Example: - -```yaml -ignore: - - "node_modules/**" - - "dist/**" - - "**/*.d.ts" - -rules: - excessive-comment-ratio: - threshold: 0.5 - undeclared-import: - severity: error - insecure-defaults: - severity: error -``` - -Config is optional — vibecop runs with safe defaults if no `.vibecop.yml` is present (`--no-config` skips loading entirely). - ---- - -## Output Formats - -| Format | Flag | Use Case | -|--------|------|----------| -| text | `--format text` | **Default.** Stylish terminal output, human-readable | -| json | `--format json` | Programmatic consumption, CI pipelines | -| github | `--format github` | `::error` annotations + `GITHUB_STEP_SUMMARY` | -| sarif | `--format sarif` | Optional SARIF 2.1.0, GitHub Security tab upload | -| html | `--format html` | Single-file HTML report | -| agent | `--format agent` | AI coding tool hooks — one finding per line, no color, token-efficient | - -SARIF is optional, not the primary format. Hand-rolled ~80 LOC serializer, `@types/sarif` for types only. - -VS Code problem matcher pattern will be included in `package.json` for terminal integration. - ---- - -## Agent Integration Architecture (v0.2) - -vibecop integrates with 7+ AI coding tools across 3 tiers: - -``` -TIER 1 — Deterministic hooks: Claude Code, Cursor, Codex CLI, Aider -TIER 2 — LLM-mediated instructions: GitHub Copilot, Windsurf, Cline -TIER 3 — MCP tools (deferred to v0.3): Continue.dev, Amazon Q, Zed -``` - -Data flow: - -``` -Agent generates code - → Hook fires: npx vibecop scan --diff HEAD --format agent - → stdout: one-per-line findings (exit 1) - → Agent reads findings, auto-corrects - → Hook re-runs: clean (exit 0) → proceed -``` - -`vibecop init` auto-detects installed tools and generates config files. See `docs/agent-integration.md` for full setup instructions. - ---- - -## Error Handling - -The engine handles the following error conditions explicitly: - -- **EACCES** — Permission denied on file/directory: log warning, skip, continue -- **ELOOP** — Symlink loop: log warning, skip, continue -- **Detector throw** — Isolated per-detector: log error with detector id + file, continue with remaining detectors -- **Detector timeout** — Per-detector timeout enforced; exceeded detectors are skipped with a warning -- **EPIPE** — Piped output closed early (e.g. `vibecop scan | head`): exit cleanly, no stack trace -- **Git errors** — `--diff` ref not found or not a git repo: clear error message, exit 1 - ---- - -## Language Support - -| Language | Support | How | -|----------|---------|-----| -| JavaScript | Built-in | @ast-grep/napi | -| TypeScript | Built-in | @ast-grep/napi | -| TSX | Built-in | @ast-grep/napi | -| Python | Included | `@ast-grep/lang-python` + `registerDynamicLanguage()` | - -Language is auto-detected from file extension. Tier 2 languages (Go, Java, Rust) and community languages are deferred to later phases. - ---- - -## What is NOT in v0.2 - -The following are explicitly deferred: - -- **Monorepo package structure** (`@vibecop/cli`, `@vibecop/core`, etc.) -- **YAML rule format** — detectors are TypeScript-only -- **Test Quality Evaluator** — Phase 3 -- **MCP server** — deferred to v0.3 (Continue.dev, Amazon Q, Zed) -- **VS Code LSP integration** -- **Deferred detectors:** unhandled-tool-exec, llm-no-structured-output, copy-paste-duplication, tautological-test, over-abstraction, buzzword-comments -- **SpecDetect4AI Python ML smells** -- **Custom rules / user-defined detectors** -- **Auto-fix / code rewriting** -- **`vibecop rules`, `vibecop explain` commands** -- **Scoring weights / quality score** -- **Online registry verification** (npm/PyPI cross-reference) -- **poetry.lock, uv.lock, Pipfile.lock** parsing -- **Tier 2+ language support** (Go, Java, Rust, etc.) - ---- - -## Phased Roadmap - -### Phase 1: Core Scanner CLI — v0.1 (complete) - -7 detectors, single package, JS/TS/TSX/Python, text-default output, `.vibecop.yml` config, `vibecop scan` + `vibecop check` commands. `--diff`, `--stdin-files`, `--max-findings`, `--verbose`, `--format html` included per CEO review. - -### Phase 2: PR Gate GitHub Action — DONE - -- Wraps core scanner -- Diff-only analysis for speed target <60s -- Inline PR comments + summary comment -- Actions on failure: comment-only (default), request-changes, label, auto-close (opt-in) -- Configuration extends `.vibecop.yml` with `pr-gate:` section - -### Phase 2.5: Agent Integration + LLM/Safety Detectors + Package Verification — DONE (v0.2) - -- 7 new detectors: 6 agent/LLM safety detectors (unsafe-shell-exec, llm-call-no-timeout, dynamic-code-exec, llm-unpinned-model, llm-no-system-message, llm-temperature-not-set) + hallucinated-package -- Agent output format (`--format agent`): token-efficient, one finding per line, no color -- `vibecop init` wizard: auto-detects 7 AI coding tools and generates integration configs -- Engine dedup with `DetectorMeta.priority` field — keeps highest-priority finding per file:line -- `makeFinding`/`makeLineFinding` DRY refactor in `src/detectors/utils.ts` -- Control benchmarks: precision/recall measured against labeled test suite -- SpecDetect4LLM mapping: UMM, NMVP, NSM, TNES implemented; NSO deferred - -### Phase 3: Test Quality Evaluator - -8 test-specific detectors including tautological-test, over-mocking (already in v0.1), missing-error-path-test, redundant-test, no-boundary-test, snapshot-only-test, implementation-coupled-test. - -Optional StrykerJS/Cosmic Ray mutation testing integration. - -Meaningful Coverage Score (0-100): -``` -Score = weighted_average( - assertion_quality * 0.30, - mutation_score * 0.25, - error_path_coverage * 0.20, - boundary_coverage * 0.15, - independence_score * 0.10, -) - -80-100: "Strong" 60-79: "Moderate" 40-59: "Weak" 0-39: "Cosmetic" -``` - ---- - -## Positioning - -> **vibecop** — the open-source linter for the AI coding era. Deterministic, free, offline. Not an AI detector. Not an LLM-based reviewer. The quality tool SonarQube and ESLint weren't designed to be. - -## What This Is NOT - -- Not an AI authorship detector -- Not an LLM-based reviewer (deterministic, reproducible, free) -- Not a replacement for SonarQube/ESLint (complements them) -- Not a code generation tool - ---- - -## Sources - -- [Anti-Slop GitHub](https://github.com/peakoss/anti-slop) -- [AI-SLOP-Detector GitHub](https://github.com/flamehaven01/AI-SLOP-Detector) -- [SpecDetect4AI (arxiv 2509.20491)](https://arxiv.org/abs/2509.20491) -- [Slopsquatting (USENIX Security 2025)](https://www.aikido.dev/blog/slopsquatting-ai-package-hallucination-attacks) -- [CodeRabbit AI vs Human Code](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report) -- [ast-grep](https://ast-grep.github.io/) -- [@ast-grep/napi](https://www.npmjs.com/package/@ast-grep/napi) -- [GitHub SARIF Integration](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github) -- [StrykerJS](https://stryker-mutator.io/) -- [SonarQube AI Code Detection](https://docs.sonarsource.com/sonarqube-server/2025.2/ai-capabilities/autodetect-ai-code) -- [Qodo 2.0](https://www.qodo.ai/blog/introducing-qodo-2-0-agentic-code-review/) -- [Semgrep Multimodal](https://www.helpnetsecurity.com/2026/03/20/semgrep-multimodal-code-security/) diff --git a/docs/market-research.md b/docs/market-research.md deleted file mode 100644 index f3ff2d5..0000000 --- a/docs/market-research.md +++ /dev/null @@ -1,136 +0,0 @@ -# AIQT Market Research — AI Code Quality Gap Analysis - -**Date:** 2026-03-31 -**Source:** Deep research across 4 parallel agents + 15+ web searches - ---- - -## The Problem (Quantified) - -| Metric | Value | Source | -|--------|-------|--------| -| AI code issues per PR vs human | 1.7x more (10.83 vs 6.45) | CodeRabbit, 470 PRs | -| AI PR acceptance rate vs human | 32.7% vs 84.4% | LinearB, 8.1M PRs | -| XSS vulnerabilities in AI code | 2.74x more likely | CodeRabbit | -| Insecure object references | 1.91x more likely | CodeRabbit | -| Excessive I/O operations | ~8x more common | CodeRabbit | -| Maintenance costs by year 2 | 4x traditional levels | BuildMVPFast | -| First-year cost overhead | 12% higher (9% review + 1.7x testing + 2x churn) | Industry analysis | -| Code smells in AI code | 90%+ of issues | Multiple studies | -| Hallucinated packages | 19.7% of AI suggestions | USENIX Security 2025 | -| Developers trusting AI output | Only 29% (down from 40%) | Stack Overflow 2025 | -| PR review time increase with AI | +91% | LinearB benchmarks | -| AI making experienced devs slower | 19% slower (METR study) | Peer-reviewed, 246 tasks | -| Projected AI tech debt | $1.5 trillion by 2027 | Industry forecast | - -## The Maintainer Crisis - -- **Curl** killed its bug bounty program (Jan 2026) — only 5% of submissions genuine -- **Jazzband** shut down entirely — "flood of AI-generated spam PRs" -- **Godot** calls it "draining and demoralizing" -- **tldraw** auto-closes ALL external PRs -- **GitHub** building a PR kill switch for AI slop -- A reviewer spends **12x longer** reviewing an AI PR than it took to generate -- **60%** of OSS maintainers are unpaid volunteers - -Sources: -- [96% rely on OSS, AI slop puts them at risk](https://thenewstack.io/ai-slop-open-source/) -- [GitHub kill switch](https://www.theregister.com/2026/02/03/github_kill_switch_pull_requests_ai/) -- [Curl ends bug bounty](https://www.bleepingcomputer.com/news/security/curl-ending-bug-bounty-program-after-flood-of-ai-slop-reports/) -- [Godot maintainer burnout](https://www.devclass.com/ai-ml/2026/02/19/github-itself-to-blame-for-ai-slop-prs-say-devs/4091420) - -## The Vibe Coding Debt Crisis - -- Code churn: 5.5% → 7.9% (2020-2024, GitClear) -- Code cloning: 8.3% → 12.3% -- Refactoring: 25% → under 10% -- 72% of orgs report production incidents from AI code (Harness) -- 67% spend MORE time debugging AI code than before -- "Rescue engineering" predicted as hottest discipline in 2026 - -Sources: -- [Salesforce Ben: 2026 year of tech debt](https://www.salesforceben.com/2026-predictions-its-the-year-of-technical-debt-thanks-to-vibe-coding/) -- [arxiv: Vibe Coding in Practice](https://arxiv.org/abs/2512.11922) -- [IBM: Reducing technical debt 2026](https://www.ibm.com/think/insights/reduce-technical-debt) -- [AI debt crisis 2026-2027](https://www.pixelmojo.io/blogs/vibe-coding-technical-debt-crisis-2026-2027) -- [GitClear research](https://www.gitclear.com/ai_assistant_code_quality_2025_research) - -## Competitive Landscape (Detailed) - -### Anti-Slop (peakoss/anti-slop) -- GitHub Action, <15 seconds execution -- 31 checks across 8 categories: ALL metadata-level -- PR Branch (4), PR Quality (2), PR Title (1), PR Description (6), PR Template (3), Commit Messages (4), File Checks (3), User Signals (8) -- 54 configuration options -- Claims 98% slop PR detection in early testing -- From Coolify maintainers (50K+ stars, 120+ slop PRs/month) -- **Gap:** ZERO code content analysis - -### AI-SLOP-Detector (flamehaven01) -- Python CLI, pip-installable -- Logic Density Ratio, Buzzword Inflation, Unused Dependencies metrics -- Combined Deficit Score (0-100) -- Optional JS/TS tree-sitter analysis -- Self-calibrating weights -- **Gap:** Python-primary, no PR gate, no hallucinated API detection - -### SonarQube Community Edition -- 10,300+ stars, 21 languages -- AI Code Assurance in 2025.1/2026.1 — BUT paid only (Server/Cloud) -- Community Edition has ZERO AI-specific features -- Auto-detects Copilot code only (misses Claude, Cursor, etc.) -- **Gap:** No community AI features, no hallucination detection - -### PR-Agent (Qodo) -- 10,500 stars, 1,300 forks -- LLM-powered multi-agent review system -- Highest F1 (60.1%) on code review benchmark -- VS Code 842K+ installs -- **Gap:** LLM-dependent (cost, latency, non-deterministic), generates noise - -### Semgrep -- Rule-based static analysis, rules look like source code -- Multimodal (March 2026) = AI + rules, 8x more true positives -- **Gap:** No AI-specific rule catalog. Multimodal is commercial. - -### CodeScene -- Commercial behavioral code analysis -- CodeHealth metric (1-10 scale, 25+ factors) -- Claims 6x more accurate than SonarQube -- **Gap:** Proprietary, expensive, not AI-pattern-specific - -## Academic Foundation - -1. **SpecDetect4AI** (arxiv 2509.20491, Sep 2025) - - 22 AI-specific code smells defined - - 826 systems analyzed (20M LOC) - - 88.66% precision, 88.89% recall - - Declarative DSL (EBNF grammar + first-order AST predicates) - - **Key reference for rule architecture** - -2. **SpecDetect4LLM** (arxiv 2512.18020, Dec 2025) - - 5 LLM-specific code smells (tied to LLM inference) - - 60.50% of analyzed systems affected - - 86.06% detection precision - -3. **Slopsquatting** (USENIX Security 2025, Spracklen et al.) - - 576,000 code samples, 16 LLMs - - 19.7% of suggested packages were hallucinations (205,474 unique fake names) - - 43% of hallucinated packages appeared consistently across prompts - - "huggingface-cli" hallucinated package got 30,000+ real downloads on PyPI - -4. **AI Detection Unreliable** (arxiv 2411.04299, Nov 2024) - - All existing AI detection tools perform poorly - - Lack generalizability across models - - **Conclusion:** Focus on quality patterns, NOT authorship detection - -## Funding Landscape - -| Category | Funding | Notes | -|----------|---------|-------| -| Code Generation/IDEs | $10B+ | Cursor $3.4B, Replit $650M, Cognition $900M | -| Code Review | $208M | CodeRabbit $88M, Qodo $120M | -| AI Testing (pure-play) | ~$5M | Nearly nothing | -| AI Code Quality (OSS) | ~$0 | Wide open | - -**The imbalance:** $10B+ in code generation vs $208M in code review vs ~$0 in AI-specific code quality tools. The gap is enormous. diff --git a/package.json b/package.json index 089c500..d84cd4c 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "dependencies": { "@ast-grep/lang-python": "^0.0.6", "@ast-grep/napi": "^0.42.0", + "@modelcontextprotocol/sdk": "^1.29.0", "commander": "^13.1.0", "yaml": "^2.7.1", "zod": "^3.24.4" diff --git a/product-docs/astro.config.mjs b/product-docs/astro.config.mjs new file mode 100644 index 0000000..ca2275f --- /dev/null +++ b/product-docs/astro.config.mjs @@ -0,0 +1,70 @@ +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; + +export default defineConfig({ + integrations: [ + starlight({ + title: 'vibecop', + description: 'AI code quality toolkit — deterministic linter for the AI coding era', + social: [ + { icon: 'github', label: 'GitHub', href: 'https://github.com/bhvbhushan/vibecop' }, + ], + editLink: { + baseUrl: 'https://github.com/bhvbhushan/vibecop/edit/main/product-docs/', + }, + sidebar: [ + { + label: 'Getting Started', + items: [ + { label: 'Introduction', slug: 'getting-started/introduction' }, + { label: 'Installation', slug: 'getting-started/installation' }, + { label: 'Quick Start', slug: 'getting-started/quick-start' }, + ], + }, + { + label: 'Agent Integration', + items: [ + { label: 'Overview', slug: 'agent-integration/overview' }, + { label: 'Claude Code', slug: 'agent-integration/claude-code' }, + { label: 'Cursor', slug: 'agent-integration/cursor' }, + { label: 'Other Tools', slug: 'agent-integration/other-tools' }, + { label: 'MCP Server', slug: 'agent-integration/mcp-server' }, + ], + }, + { + label: 'Detectors', + items: [ + { label: 'Overview', slug: 'detectors/overview' }, + { label: 'Quality', slug: 'detectors/quality' }, + { label: 'Security', slug: 'detectors/security' }, + { label: 'Correctness', slug: 'detectors/correctness' }, + { label: 'Testing', slug: 'detectors/testing' }, + ], + }, + { + label: 'Configuration', + items: [ + { label: 'Config File', slug: 'configuration/config-file' }, + { label: 'Custom Rules', slug: 'configuration/custom-rules' }, + { label: 'CLI Reference', slug: 'configuration/cli-reference' }, + ], + }, + { + label: 'GitHub Action', + slug: 'github-action', + }, + { + label: 'Benchmarks', + slug: 'benchmarks', + }, + { + label: 'Architecture', + slug: 'architecture', + }, + ], + customCss: ['./src/styles/custom.css'], + }), + ], + site: 'https://bhvbhushan.github.io', + base: '/vibecop', +}); diff --git a/product-docs/package-lock.json b/product-docs/package-lock.json new file mode 100644 index 0000000..5e8e209 --- /dev/null +++ b/product-docs/package-lock.json @@ -0,0 +1,6451 @@ +{ + "name": "vibecop-docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vibecop-docs", + "version": "1.0.0", + "dependencies": { + "@astrojs/starlight": "^0.33.2", + "astro": "5.7", + "zod": "^3.23.8" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", + "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", + "license": "MIT" + }, + "node_modules/@astrojs/markdown-remark": { + "version": "6.3.11", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", + "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.7.6", + "@astrojs/prism": "3.3.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "shiki": "^3.21.0", + "smol-toml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz", + "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "6.3.11", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.15.0", + "es-module-lexer": "^1.7.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + }, + "peerDependencies": { + "astro": "^5.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", + "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.2.tgz", + "integrity": "sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/sitemap/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.33.2.tgz", + "integrity": "sha512-UpvPBMtZrP/x17uQmdOxm8lUTtmEJ0csTprQT8fd8HSHDn/pSK69fOsSjl6tk83ROMOARC5/DivExSxxJADNSA==", + "license": "MIT", + "dependencies": { + "@astrojs/mdx": "^4.2.3", + "@astrojs/sitemap": "^3.3.0", + "@pagefind/default-ui": "^1.3.0", + "@types/hast": "^3.0.4", + "@types/js-yaml": "^4.0.9", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.41.1", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.1", + "hast-util-select": "^6.0.2", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", + "i18next": "^23.11.5", + "js-yaml": "^4.1.0", + "klona": "^2.0.6", + "mdast-util-directive": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.3.0", + "rehype": "^13.0.1", + "rehype-format": "^5.0.0", + "remark-directive": "^3.0.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.2" + }, + "peerDependencies": { + "astro": "^5.1.5" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.2.1.tgz", + "integrity": "sha512-SSVM820Jqc6wjsn7qYfV9qfeQvePtVc1nSofhyap7l0/iakUKywj3hfy3UJAOV4sGV4Q/u450RD4AaCaFvNPlg==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.2.0", + "debug": "^4.4.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "is-docker": "^3.0.0", + "is-wsl": "^3.1.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@capsizecss/unpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", + "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", + "license": "MIT", + "dependencies": { + "blob-to-buffer": "^1.2.8", + "cross-fetch": "^3.0.4", + "fontkit": "^2.0.2" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", + "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", + "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", + "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "shiki": "^3.2.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", + "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.0.tgz", + "integrity": "sha512-OXQVlxALU9+Lz/LxkAa+RvaxY1cnRKUDCuwl9o8PY5Lg/znP573y4WIbVOOIz8Bv7uj7r00TGy7pD+NSLMJGBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.0.tgz", + "integrity": "sha512-+LK1Xq5n/B0hHc08DW61SnfIlfLKyXZ1oKcbfZ1MimE7Rn0Q6R0VI/TlC04f/JDPm+67zAOwPGizzvefOi5vqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.0.tgz", + "integrity": "sha512-C8VZ5pDz1Kc89GicXsWZiIlAwTVwUtFDOzh0RzJt5FmbkJzsmPVICPkLUfOsWgBCyFAH/vYR+lUTaGPDxZ4IXw==", + "license": "MIT" + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.0.tgz", + "integrity": "sha512-kicDfUF9gn/z06NimTwNlZXF8z3pLsN3BIPPt6N8unuh0n55fr64tVs2p3a5RKYmQkJGjPfOE/C9GI5YTEpURg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.0.tgz", + "integrity": "sha512-e5rDB3wPm89bcSLiatKBDTrVTbsMQrrtkXRaAoUJYU0C1suXVvEzZfjmMvrUDvYhZBx/Ls8hGuGxlqSJBz3gDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.0.tgz", + "integrity": "sha512-vh52DcBiF/mRMmq+Rwt3M3RgEWgl00jFk/M5NWhLEHJFq4+papQXwbyKbi7cNlxaeYrKx6wOfW3fm9cftfc/Kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.0.tgz", + "integrity": "sha512-kg+szZwffZdyWn6SL6RHjAYjhSvJ2bT4qkv3KepGsbmD9fuSHUSC+2kydDneDVUA9qEDRf9uSFoEAsXsp1/JKA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.0.tgz", + "integrity": "sha512-8eOCmB8lnpyvwz+HrcTXLuBxhj7UseAFh6KGEXRe8UCcAfVQih+qPy/4akJRezViI+ONijz9oi7HpMkw9rdtBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", + "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/fontkit": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@types/fontkit/-/fontkit-2.0.9.tgz", + "integrity": "sha512-qNYerFky3muCmZPq+R+B3cUDRA5OONw/oh6aGGFxx2LOBz6yu8eamKusrhkHnC6rc2fm76+G9z9QoWSB2SaQaw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "5.7.14", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.7.14.tgz", + "integrity": "sha512-DfuDD49f7mbHB7ygLm8KXEC6QQtpLoNrmoylcMLKdl1ahXNdiw+mgW8ApEMyHTUyVrqEUnr4gZCKSlZ9POCHjQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.11.0", + "@astrojs/internal-helpers": "0.6.1", + "@astrojs/markdown-remark": "6.3.1", + "@astrojs/telemetry": "3.2.1", + "@capsizecss/unpack": "^2.4.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.1.4", + "acorn": "^8.14.1", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "boxen": "8.0.1", + "ci-info": "^4.2.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^1.0.1", + "cookie": "^1.0.2", + "cssesc": "^3.0.0", + "debug": "^4.4.0", + "deterministic-object-hash": "^2.0.2", + "devalue": "^5.1.1", + "diff": "^5.2.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^1.6.0", + "esbuild": "^0.25.0", + "estree-walker": "^3.0.3", + "flattie": "^1.1.1", + "fontace": "~0.3.0", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.1.1", + "import-meta-resolve": "^4.1.0", + "js-yaml": "^4.1.0", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "p-limit": "^6.2.0", + "p-queue": "^8.1.0", + "package-manager-detector": "^1.1.0", + "picomatch": "^4.0.2", + "prompts": "^2.4.2", + "rehype": "^13.0.2", + "semver": "^7.7.1", + "shiki": "^3.2.1", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.12", + "tsconfck": "^3.1.5", + "ultrahtml": "^1.6.0", + "unifont": "~0.5.0", + "unist-util-visit": "^5.0.0", + "unstorage": "^1.15.0", + "vfile": "^6.0.3", + "vite": "^6.3.4", + "vitefu": "^1.0.6", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^21.1.1", + "yocto-spinner": "^0.2.1", + "zod": "^3.24.2", + "zod-to-json-schema": "^3.24.5", + "zod-to-ts": "^1.2.0" + }, + "bin": { + "astro": "astro.js" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=22.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.33.3" + } + }, + "node_modules/astro-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", + "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", + "license": "MIT", + "dependencies": { + "rehype-expressive-code": "^0.41.7" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + } + }, + "node_modules/astro/node_modules/@astrojs/internal-helpers": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.6.1.tgz", + "integrity": "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A==", + "license": "MIT" + }, + "node_modules/astro/node_modules/@astrojs/markdown-remark": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.1.tgz", + "integrity": "sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.6.1", + "@astrojs/prism": "3.2.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.1.0", + "js-yaml": "^4.1.0", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "remark-smartypants": "^3.0.2", + "shiki": "^3.0.0", + "smol-toml": "^1.3.1", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1", + "vfile": "^6.0.3" + } + }, + "node_modules/astro/node_modules/@astrojs/prism": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.2.0.tgz", + "integrity": "sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.29.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/astro/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/astro/node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/astro/node_modules/zod-to-ts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", + "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "peerDependencies": { + "typescript": "^4.9.4 || ^5.0.2", + "zod": "^3" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcp-47": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", + "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/blob-to-buffer": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", + "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "optional": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "license": "ISC" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", + "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/deterministic-object-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", + "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", + "license": "MIT", + "dependencies": { + "base-64": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/devalue": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", + "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", + "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "@expressive-code/plugin-frames": "^0.41.7", + "@expressive-code/plugin-shiki": "^0.41.7", + "@expressive-code/plugin-text-markers": "^0.41.7" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.3.1.tgz", + "integrity": "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg==", + "license": "MIT", + "dependencies": { + "@types/fontkit": "^2.0.8", + "fontkit": "^2.0.4" + } + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT", + "optional": true + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.1.tgz", + "integrity": "sha512-Y71HWT4hydF1IAG/2OPync4dgQ/J2iWye7eg6CuzJHI+E97tvqFPlADzxiNnjH6WSljg8ecfXMr9k6bfFuqA5w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", + "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, + "node_modules/pagefind": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.0.tgz", + "integrity": "sha512-7vQ2xh0ZmjPjsuWONR68nqzb+QNfpPh7pdT6n6YDAthWAQiUkSACVegSswY5zPNONGYFWebFVgdnS5/m/Qqn+w==", + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.5.0", + "@pagefind/darwin-x64": "1.5.0", + "@pagefind/freebsd-x64": "1.5.0", + "@pagefind/linux-arm64": "1.5.0", + "@pagefind/linux-x64": "1.5.0", + "@pagefind/windows-arm64": "1.5.0", + "@pagefind/windows-x64": "1.5.0" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", + "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", + "license": "MIT", + "dependencies": { + "expressive-code": "^0.41.7" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/sitemap/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", + "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", + "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0", + "ofetch": "^1.4.1", + "ohash": "^2.0.0" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-spinner": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", + "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", + "license": "MIT", + "dependencies": { + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18.19" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/product-docs/package.json b/product-docs/package.json new file mode 100644 index 0000000..68c8a2e --- /dev/null +++ b/product-docs/package.json @@ -0,0 +1,18 @@ +{ + "name": "vibecop-docs", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "dependencies": { + "@astrojs/starlight": "^0.33.2", + "astro": "~5.7.0", + "zod": "3.23.8" + }, + "overrides": { + "zod": "3.23.8" + } +} diff --git a/product-docs/src/content.config.ts b/product-docs/src/content.config.ts new file mode 100644 index 0000000..a0b6abc --- /dev/null +++ b/product-docs/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsSchema } from '@astrojs/starlight/schema'; +import { docsLoader } from '@astrojs/starlight/loaders'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/product-docs/src/content/docs/agent-integration/claude-code.mdx b/product-docs/src/content/docs/agent-integration/claude-code.mdx new file mode 100644 index 0000000..5b48b4b --- /dev/null +++ b/product-docs/src/content/docs/agent-integration/claude-code.mdx @@ -0,0 +1,101 @@ +--- +title: Claude Code +description: Set up vibecop as a PostToolUse hook in Claude Code +--- + +Claude Code supports `PostToolUse` hooks that fire after every tool call. vibecop uses this to scan files after each `Edit`, `Write`, or `MultiEdit` operation. + +## Auto Setup + +```bash +npx vibecop init +``` + +If a `.claude/` directory is detected, `vibecop init` generates the settings file automatically. + +## Manual Setup + +Create `.claude/settings.json` in your project root: + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "npx vibecop scan --diff HEAD --format agent" + } + ] + } + ] + } +} +``` + +### How It Works + +1. The agent calls `Edit`, `Write`, or `MultiEdit` to modify a file +2. The `PostToolUse` hook fires and runs `npx vibecop scan --diff HEAD --format agent` +3. vibecop scans only the changed files (via `--diff HEAD`) +4. If findings are detected, vibecop exits with code 1 and prints findings to stdout +5. Claude Code reads the findings and the agent auto-corrects the code +6. The hook runs again -- if clean (exit 0), the agent continues + +### The `matcher` Field + +The `matcher` uses a regex pattern to match tool names. `Edit|Write|MultiEdit` catches all file-editing tools. You can narrow this if needed: + +- `Edit` -- only on file edits +- `Write` -- only on new file creation +- `Edit|Write|MultiEdit` -- all file modifications (recommended) + +### The `--format agent` Flag + +This produces token-efficient output (one finding per line, ~30 tokens each), minimizing context window usage: + +``` +src/api.ts:42:1 error unsafe-shell-exec: execSync() with template literal. Use execFile() with argument array instead. +``` + +### The `--diff HEAD` Flag + +This scans only files with uncommitted changes, making the hook fast even in large repositories. Without this flag, vibecop would scan the entire project on every edit. + +## Existing Settings + +If `.claude/settings.json` already exists, `vibecop init` will skip it to avoid overwriting your configuration. In that case, add the `PostToolUse` hook manually by merging the JSON above into your existing settings. + +## Troubleshooting + +### vibecop not found + +If `npx vibecop` fails, install it as a dev dependency: + +```bash +npm install --save-dev vibecop +``` + +Or install globally: + +```bash +npm install -g vibecop +``` + +### Hook not firing + +Verify the hook is configured correctly: + +1. Check that `.claude/settings.json` exists in your project root +2. Confirm the `hooks.PostToolUse` array contains the vibecop entry +3. Test the command manually: `npx vibecop scan --diff HEAD --format agent` + +### Slow scans + +If the hook takes too long, scope the scan to a specific directory: + +```bash +npx vibecop scan --diff HEAD --format agent --path src/ +``` diff --git a/product-docs/src/content/docs/agent-integration/cursor.mdx b/product-docs/src/content/docs/agent-integration/cursor.mdx new file mode 100644 index 0000000..d029f38 --- /dev/null +++ b/product-docs/src/content/docs/agent-integration/cursor.mdx @@ -0,0 +1,82 @@ +--- +title: Cursor +description: Set up vibecop as an afterFileEdit hook in Cursor +--- + +Cursor supports `afterFileEdit` hooks and rules files. vibecop uses both for a two-layer integration: the hook runs the scan, and the rules file reinforces the fix behavior. + +## Auto Setup + +```bash +npx vibecop init +``` + +If a `.cursor/` directory is detected, `vibecop init` generates both the hooks file and the rules file. + +## Manual Setup + +### Step 1: Create the Hook + +Create `.cursor/hooks.json` in your project root: + +```json +{ + "hooks": { + "afterFileEdit": [ + { + "command": "npx vibecop scan --diff HEAD --format agent" + } + ] + } +} +``` + +### Step 2: Create the Rules File + +Create `.cursor/rules/vibecop.md`: + +```markdown +--- +trigger: always_on +--- + +After every code edit, review vibecop findings and fix issues before proceeding. +Run: npx vibecop scan --diff HEAD --format agent +``` + +### How It Works + +1. Cursor edits a file +2. The `afterFileEdit` hook fires and runs `npx vibecop scan --diff HEAD --format agent` +3. vibecop scans only the changed files +4. If findings are detected, the agent reads the output +5. The rules file reinforces the instruction to fix findings before continuing +6. The agent corrects the code, and the hook runs again + +### Why Both Hook and Rules? + +The hook provides deterministic execution -- vibecop runs automatically after every edit. The rules file provides LLM-level reinforcement, ensuring the agent understands it must fix findings rather than ignore them. Together, they form a reliable feedback loop. + +## Troubleshooting + +### Hook timeout + +If Cursor has a timeout for hooks, vibecop should complete within a few seconds on most codebases. For large repos, scope the scan: + +```bash +npx vibecop scan --diff HEAD --format agent --path src/ +``` + +### Permission issues + +On some systems, `npx` may not be in the PATH when hooks execute. Use the full path: + +```bash +$(npm root -g)/.bin/vibecop scan --diff HEAD --format agent +``` + +Or use the local installation: + +```bash +node_modules/.bin/vibecop scan --diff HEAD --format agent +``` diff --git a/product-docs/src/content/docs/agent-integration/mcp-server.mdx b/product-docs/src/content/docs/agent-integration/mcp-server.mdx new file mode 100644 index 0000000..244df3b --- /dev/null +++ b/product-docs/src/content/docs/agent-integration/mcp-server.mdx @@ -0,0 +1,147 @@ +--- +title: MCP Server +description: Use vibecop as an MCP server for Continue.dev, Amazon Q, Zed, and other MCP-compatible tools +--- + +vibecop includes a built-in MCP (Model Context Protocol) server that exposes scan, check, and explain tools over stdio transport. This enables integration with any MCP-compatible AI coding tool. + +## Starting the Server + +```bash +vibecop serve +``` + +The server starts on stdio (standard input/output) and logs status messages to stderr. It runs until terminated with SIGINT or SIGTERM. + +## MCP Client Configuration + +Configure your MCP client to connect to vibecop: + +```json +{ + "mcpServers": { + "vibecop": { + "command": "npx", + "args": ["vibecop", "serve"] + } + } +} +``` + +## Available Tools + +The MCP server exposes three tools: + +### vibecop_scan + +Scan a directory for AI code quality issues. + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|:--------:|---------|-------------| +| `path` | string | No | Current working directory | Directory to scan | +| `maxFindings` | number | No | 50 | Maximum findings to return | + +**Response:** JSON object with `findings`, `filesScanned`, and `errors` arrays. + +### vibecop_check + +Check a single file for AI code quality issues. + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|:--------:|---------|-------------| +| `file_path` | string | Yes | -- | Absolute or relative path to the file | +| `maxFindings` | number | No | 50 | Maximum findings to return | + +**Response:** JSON object with `findings`, `filesScanned`, and `errors` arrays. + +### vibecop_explain + +Get detailed information about what a specific detector checks for. + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|:--------:|-------------| +| `detector_id` | string | Yes | The detector ID (e.g., `unsafe-shell-exec`, `god-function`) | + +**Response:** JSON object with `id`, `name`, `description`, `severity`, `category`, and `languages`. + +If the detector ID is not found, returns an error with the list of all available detector IDs. + +## Tool-Specific Setup + +### Continue.dev + +Add to `.continue/config.json`: + +```json +{ + "mcpServers": [ + { + "name": "vibecop", + "command": "npx", + "args": ["vibecop", "serve"] + } + ] +} +``` + +### Amazon Q + +Configure the MCP server in your Amazon Q developer settings: + +```json +{ + "mcpServers": { + "vibecop": { + "command": "npx", + "args": ["vibecop", "serve"] + } + } +} +``` + +### Zed + +Add to your Zed MCP settings: + +```json +{ + "mcpServers": { + "vibecop": { + "command": "npx", + "args": ["vibecop", "serve"] + } + } +} +``` + +## Architecture + +The MCP server is built on the `@modelcontextprotocol/sdk` package and uses stdio transport (JSON-RPC over stdin/stdout). It reuses the same `scan()` and `checkFile()` engine functions as the CLI. + +``` +MCP Client (Continue.dev / Amazon Q / Zed) + ↕ JSON-RPC over stdio +vibecop MCP Server + → vibecop_scan → engine.scan() + → vibecop_check → engine.checkFile() + → vibecop_explain → detector metadata lookup +``` + +The server handles graceful shutdown on SIGINT and SIGTERM. All logging goes to stderr so it does not interfere with the MCP protocol on stdout. + +## When to Use MCP vs Hooks + +| Criteria | MCP Server (Tier 3) | Hooks (Tier 1) | +|----------|:-------------------:|:--------------:| +| Tool support | Any MCP client | Tool-specific hooks | +| Invocation | On-demand by agent | Automatic on file edit | +| Blocking | Agent-controlled | Deterministic (exit code) | +| Best for | MCP-native tools | Claude Code, Cursor | + +Use the MCP server when your tool supports MCP natively (Continue.dev, Amazon Q, Zed). Use hooks when your tool supports them (Claude Code, Cursor, Codex CLI) -- hooks provide stronger guarantees because they execute deterministically. diff --git a/product-docs/src/content/docs/agent-integration/other-tools.mdx b/product-docs/src/content/docs/agent-integration/other-tools.mdx new file mode 100644 index 0000000..f95d253 --- /dev/null +++ b/product-docs/src/content/docs/agent-integration/other-tools.mdx @@ -0,0 +1,112 @@ +--- +title: Other Tools +description: Set up vibecop with Codex CLI, Aider, GitHub Copilot, Windsurf, and Cline +--- + +vibecop supports a range of AI coding tools beyond Claude Code and Cursor. This page covers setup for each. + +## Tier 1: Deterministic Hooks + +### Codex CLI + +Codex CLI uses the same `PostToolUse` hook pattern as Claude Code. + +Create `.codex/hooks.json`: + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "npx vibecop scan --diff HEAD --format agent" + } + ] + } + ] + } +} +``` + +### Aider + +Aider has a native `lint-cmd` config that runs after each edit. vibecop acts as the linter for TypeScript, JavaScript, and Python files. + +Create `.aider.conf.yml`: + +```yaml +lint-cmd: + - "typescript: npx vibecop scan --format text" + - "javascript: npx vibecop scan --format text" + - "python: npx vibecop scan --format text" +auto-lint: true +``` + +Note: Aider uses `--format text` (not `agent`) since it reads linter output differently from streaming agents. + +## Tier 2: LLM-Mediated Instructions + +These tools do not have deterministic hook execution. vibecop is injected as a persistent instruction into the agent's context. The LLM follows the instruction voluntarily. + +### GitHub Copilot + +Add to `.github/copilot-instructions.md`: + +```markdown +## vibecop + +After every code edit, run `npx vibecop scan --diff HEAD --format agent` and fix any findings before proceeding. +``` + +If the file already exists, append the `## vibecop` section. `vibecop init` handles this automatically -- it detects existing content and appends without overwriting. + +### Windsurf + +Create `.windsurf/rules/vibecop.md`: + +```markdown +--- +trigger: always_on +--- + +After every code edit, review vibecop findings and fix issues before proceeding. +Run: npx vibecop scan --diff HEAD --format agent +``` + +### Cline / Roo Code + +Create `.clinerules`: + +``` +After every code edit, run `npx vibecop scan --diff HEAD --format agent` and fix any findings before proceeding. +``` + +## Tier 1 vs Tier 2 Comparison + +| Aspect | Tier 1 (Hooks) | Tier 2 (Instructions) | +|--------|:--------------:|:---------------------:| +| Execution | Deterministic | LLM-dependent | +| Blocking | Yes (exit code) | No (voluntary) | +| Reliability | 100% | ~90% (depends on LLM) | +| Setup | Config file | Instructions file | + +Tier 1 integrations are more reliable because they execute deterministically via hooks. Tier 2 integrations depend on the LLM reading and following the instruction. Both approaches work in practice, but Tier 1 provides stronger guarantees. + +## Auto Setup for All Tools + +```bash +npx vibecop init +``` + +`vibecop init` detects which tools you have installed and generates the correct config files for each. It checks for: + +- `.claude/` directory (Claude Code) +- `.cursor/` directory (Cursor) +- `.codex/` directory (Codex CLI) +- `aider` in PATH (Aider) +- `.windsurf/` directory (Windsurf) +- `.github/` directory (GitHub Copilot) +- `.cline/` directory or `.clinerules` file (Cline) diff --git a/product-docs/src/content/docs/agent-integration/overview.mdx b/product-docs/src/content/docs/agent-integration/overview.mdx new file mode 100644 index 0000000..a21b6fc --- /dev/null +++ b/product-docs/src/content/docs/agent-integration/overview.mdx @@ -0,0 +1,110 @@ +--- +title: Agent Integration Overview +description: How vibecop integrates with AI coding agents across three tiers +--- + +vibecop integrates with AI coding agents as an automatic linter that runs after every code edit. The agent reads findings from stdout and self-corrects before proceeding. + +## Quick Setup + +Run the setup wizard to auto-detect your tools and generate config files: + +```bash +npx vibecop init +``` + +This detects installed/active tools and writes the appropriate config files: + +``` + vibecop — agent integration setup + + Detected tools: + ✓ Claude Code (.claude/ directory found) + ✓ Cursor (.cursor/ directory found) + ✓ Aider (aider installed) + ✗ Codex CLI (not found) + + Generated: + .claude/settings.json — PostToolUse hook (blocks on findings) + .cursor/hooks.json — afterFileEdit hook + .cursor/rules/vibecop.md — always-on lint rule + .aider.conf.yml — lint-cmd per language + + Done! vibecop will now run automatically in your agent workflow. +``` + +## The Three Tiers + +vibecop supports 10+ AI coding tools across three integration tiers: + +### Tier 1 -- Deterministic Hooks + +These tools support native hook execution. vibecop runs synchronously after each edit and blocks the agent until findings are resolved. + +| Tool | Hook Type | Behavior | +|------|-----------|----------| +| **Claude Code** | `PostToolUse` | Fires after Edit/Write/MultiEdit, exit 1 blocks | +| **Cursor** | `afterFileEdit` + rules | Hook runs scan, rules file reinforces fix behavior | +| **Codex CLI** | `PostToolUse` | Same pattern as Claude Code | +| **Aider** | Native `--lint-cmd` | Built-in lint integration, runs after every edit | + +### Tier 2 -- LLM-Mediated Instructions + +These tools do not have deterministic hook execution. Instead, vibecop is injected as a persistent instruction into the agent's context. The LLM follows the instruction voluntarily. + +| Tool | Integration | +|------|-------------| +| **GitHub Copilot** | Custom instructions file | +| **Windsurf** | Rules file with `trigger: always_on` | +| **Cline/Roo Code** | `.clinerules` file | + +### Tier 3 -- MCP Server + +These tools connect to vibecop via the Model Context Protocol. The agent calls vibecop tools directly through the MCP interface. + +| Tool | Integration | +|------|-------------| +| **Continue.dev** | MCP server config | +| **Amazon Q** | MCP server support | +| **Zed** | MCP settings | + +## How the Loop Works + +``` +Agent writes code + → vibecop hook fires automatically + → Findings? Exit 1 → agent reads output, fixes code + → No findings? Exit 0 → agent continues +``` + +This creates a tight feedback loop: the agent never moves on while there are unresolved findings. + +## Exit Codes + +| Code | Meaning | +|------|---------| +| `0` | No findings -- clean | +| `1` | One or more findings found | +| `2` | Scan error (bad args, git error, etc.) | + +## Output Format for Agents + +The `--format agent` output is token-efficient (one finding per line, ~30 tokens each): + +``` +file:line:col severity detector-id: message. suggestion +``` + +Example: + +``` +src/api.ts:42:1 error unsafe-shell-exec: execSync() with template literal. Use execFile() with argument array instead. +src/llm.ts:18:5 warning llm-unpinned-model: Unpinned model alias "gpt-4o". Pin to a dated version like "gpt-4o-2024-08-06". +``` + +## Tool-Specific Setup + +- [Claude Code](/vibecop/agent-integration/claude-code/) +- [Cursor](/vibecop/agent-integration/cursor/) +- [Other Tools](/vibecop/agent-integration/other-tools/) (Codex, Aider, Copilot, Windsurf, Cline) +- [MCP Server](/vibecop/agent-integration/mcp-server/) (Continue.dev, Amazon Q, Zed) diff --git a/product-docs/src/content/docs/architecture.mdx b/product-docs/src/content/docs/architecture.mdx new file mode 100644 index 0000000..4e60a2e --- /dev/null +++ b/product-docs/src/content/docs/architecture.mdx @@ -0,0 +1,171 @@ +--- +title: Architecture +description: How vibecop works internally — engine data flow, detector patterns, and tech stack +--- + +## High-Level Architecture + +``` +vibecop CLI (Commander.js) +├── Scan Engine — discovers files, loads AST, runs detectors, dedup by priority +├── MCP Server — stdio transport, 3 tools (scan, check, explain) +├── Init Wizard — auto-detects AI tools, generates hook/rule configs +├── Custom Rules Engine — loads .vibecop/rules/*.yaml, validates with Zod, runs via ast-grep +├── Config Loader (Zod) — validates .vibecop.yml, merges defaults, per-rule config +├── Detectors (35) — AST pattern matching via ast-grep (@ast-grep/napi) +├── Formatters (7) — text, json, html, sarif, github, agent, gcc output +├── Project Analyzer — parses package.json, requirements.txt, lockfiles +└── GitHub Action — diff parser, finding filter, PR review poster +``` + +## File Layout + +``` +vibecop/ +├── src/ +│ ├── cli.ts — Commander.js entry (scan, check, init, serve, test-rules) +│ ├── engine.ts — File discovery, detector runner, dedup, report builder +│ ├── config.ts — .vibecop.yml loading + Zod validation +│ ├── project.ts — Parse package.json, lock files, manifests → ProjectInfo +│ ├── init.ts — vibecop init setup wizard +│ ├── custom-rules.ts — YAML rule loader + Zod validation + ast-grep execution +│ ├── test-rules.ts — Test runner for custom rule examples +│ ├── types.ts — Detector, DetectionContext, Finding, ProjectInfo, etc. +│ ├── formatters/ +│ │ ├── index.ts — Formatter registry +│ │ ├── text.ts — Default: stylish terminal output +│ │ ├── json.ts — Structured JSON output +│ │ ├── sarif.ts — SARIF 2.1.0 format (~80 LOC hand-rolled) +│ │ ├── github.ts — ::error annotations + GITHUB_STEP_SUMMARY +│ │ ├── html.ts — Single-file HTML report +│ │ ├── agent.ts — Token-efficient one-per-line for AI hooks +│ │ └── gcc.ts — GCC-style for editor integration +│ ├── mcp/ +│ │ ├── index.ts — MCP module entry, server creation + transport +│ │ └── server.ts — Tool handlers: scan, check, explain +│ ├── detectors/ +│ │ ├── index.ts — Detector registry (all 35 built-in detectors) +│ │ ├── utils.ts — makeFinding/makeLineFinding helpers +│ │ └── *.ts — One file per detector +│ ├── action/ +│ │ ├── main.ts — GitHub Action entry point +│ │ ├── diff.ts — PR diff parser +│ │ ├── filter.ts — Finding filter (only changed lines) +│ │ ├── review.ts — PR review comment poster +│ │ └── summary.ts — Summary comment generator +│ └── data/ +│ └── known-packages.json — Bundled npm allowlist for hallucinated-package +├── test/ — bun test suite +├── examples/ — Example configs for 7 AI coding tools +├── docs/ — Design docs, benchmarks, agent integration guide +├── package.json +├── tsconfig.json +└── .vibecop.yml — Self-dogfood config +``` + +## Engine Data Flow + +``` +CLI args + → loadConfig(.vibecop.yml or defaults) + → loadProjectInfo(package.json, lock files, requirements.txt, pyproject.toml) + → discoverFiles(path, config.ignore, .gitignore) + → for each file: + → parse with ast-grep (language auto-detected from extension) + → run each enabled detector(ctx) → Finding[] + → isolate: if detector throws, log error, continue + → aggregate all findings + → dedupFindings: group by file:line, keep highest priority + → apply --max-findings N cap (default 50) + → format(findings, --format flag) + → exit(findings.length > 0 ? 1 : 0) +``` + +### Engine Dedup + +When multiple detectors flag the same `file:line`, the engine keeps only the highest-priority finding. Priority is set via `DetectorMeta.priority` (default: 0). The LLM/agent safety detectors use `priority: 10` so they take precedence over general quality detectors. + +### Detector Isolation + +If a detector throws during execution, the engine catches the error, logs it (with the detector ID and file path), and continues running the remaining detectors. A single broken detector never crashes the entire scan. + +## Core Interfaces + +```typescript +interface Detector { + id: string; + meta: DetectorMeta; + detect(ctx: DetectionContext): Finding[]; +} + +interface DetectorMeta { + name: string; + description: string; + severity: 'error' | 'warning' | 'info'; + category: 'correctness' | 'quality' | 'security' | 'testing'; + languages: Lang[]; + priority?: number; // Higher priority wins in dedup (default: 0) +} + +interface DetectionContext { + file: FileInfo; + root: SgRoot; // ast-grep root node + source: string; // raw file text + project: ProjectInfo; // dependencies, lock file data + config: RuleConfig; // per-rule config overrides +} + +interface Finding { + detectorId: string; + message: string; + severity: 'error' | 'warning' | 'info'; + file: string; + line: number; + column: number; + endLine?: number; + endColumn?: number; + suggestion?: string; +} +``` + +## Tech Stack + +| Component | Choice | Rationale | +|-----------|--------|-----------| +| Runtime | Node.js 20+ / Bun | Bun for development, Node.js for broad end-user compatibility | +| Language | TypeScript | Type safety for AST operations | +| Parser | @ast-grep/napi | 13K stars, 9 platform binaries, 10-50x faster than raw tree-sitter | +| Python support | @ast-grep/lang-python | One import + `registerDynamicLanguage()` | +| CLI framework | Commander.js | Standard, widely understood | +| Config validation | Zod | Schema validation with clear error messages | +| SARIF output | @types/sarif (types only) | Hand-rolled ~80 LOC serializer, no runtime dependency | +| MCP server | @modelcontextprotocol/sdk | Official MCP SDK, stdio transport | +| Test runner | bun test | Built-in, no extra dependency | +| Build tool | bun build | Built-in, produces single executable | +| Distribution | npm | `npx vibecop scan` -- largest reach | + +## Error Handling + +The engine handles these error conditions explicitly: + +| Error | Behavior | +|-------|----------| +| **EACCES** (permission denied) | Log warning, skip file, continue | +| **ELOOP** (symlink loop) | Log warning, skip file, continue | +| **Detector throw** | Isolated per-detector: log error with detector ID + file, continue | +| **Detector timeout** | Per-detector timeout enforced; exceeded detectors are skipped with a warning | +| **EPIPE** (piped output closed) | Exit cleanly, no stack trace | +| **Git errors** | `--diff` ref not found or not a git repo: clear error message, exit 2 | + +## MCP Server Architecture + +``` +MCP Client (Continue.dev / Amazon Q / Zed) + ↕ JSON-RPC over stdio +vibecop MCP Server (@modelcontextprotocol/sdk) + → vibecop_scan → engine.scan() + → vibecop_check → engine.checkFile() + → vibecop_explain → detector metadata lookup +``` + +The MCP server reuses the same `scan()` and `checkFile()` functions as the CLI. Tool input schemas are defined with Zod. The server handles graceful shutdown on SIGINT/SIGTERM. diff --git a/product-docs/src/content/docs/benchmarks.mdx b/product-docs/src/content/docs/benchmarks.mdx new file mode 100644 index 0000000..3637a8f --- /dev/null +++ b/product-docs/src/content/docs/benchmarks.mdx @@ -0,0 +1,109 @@ +--- +title: Benchmarks +description: Finding density comparison between established and vibe-coded projects +--- + +All numbers below are real -- run `vibecop scan` on any of these repos yourself to reproduce. Finding density = findings per 1,000 lines of code (findings/kLOC). + +## Methodology + +### How Findings Are Counted + +```bash +vibecop scan --format json --no-config --max-findings 2000 +``` + +`summary.total` from JSON output. `--no-config` ensures no per-project suppression. + +### How LOC Is Counted + +`wc -l` across all `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.py` files (excluding `node_modules`, `dist`, `build`). + +## Results + +### Established Projects (Professionally Maintained) + +| Project | Stars | Files | LOC | Findings | Density | +|---------|:-----:|:-----:|----:|:--------:|--------:| +| [**fastify**](https://github.com/fastify/fastify) | 65K | 275 | 74,428 | 124 | 1.7/kLOC | +| [**date-fns**](https://github.com/date-fns/date-fns) | 35K | 1,543 | 99,859 | 308 | 3.1/kLOC | +| [**TanStack/query**](https://github.com/TanStack/query) | 43K | 997 | 148,492 | 652 | 4.4/kLOC | +| [**express**](https://github.com/expressjs/express) | 66K | 141 | 21,346 | 123 | 5.8/kLOC | +| [**zod**](https://github.com/colinhacks/zod) | 35K | 356 | 70,886 | 964 | 13.6/kLOC | + +**Median: 4.4/kLOC | Average: 5.7/kLOC** + +### Vibe-Coded Projects (AI-Generated/Assisted) + +| Project | Stars | Files | LOC | Findings | Density | +|---------|:-----:|:-----:|----:|:--------:|--------:| +| [**dyad**](https://github.com/dyad-sh/dyad) | 20K | 956 | 147,284 | 1,179 | 8.0/kLOC | +| [**bolt.diy**](https://github.com/stackblitz-labs/bolt.diy) | 19.2K | 392 | 71,639 | 977 | 13.6/kLOC | +| [**code-review-graph**](https://github.com/tirth8205/code-review-graph) | 3.9K | 95 | 27,119 | 361 | 13.3/kLOC | +| [**context7**](https://github.com/upstash/context7) | 51.3K | 71 | 9,201 | 129 | 14.0/kLOC | +| [**vibe-check-mcp**](https://github.com/PV-Bhat/vibe-check-mcp-server) | 480 | 55 | 5,964 | 119 | 20.0/kLOC | +| [**magic-mcp**](https://github.com/21st-dev/magic-mcp) | 4.6K | 14 | 1,096 | 28 | 25.5/kLOC | +| [**browser-tools-mcp**](https://github.com/AgentDeskAI/browser-tools-mcp) | 7.2K | 12 | 8,346 | 414 | 49.6/kLOC | + +**Median: 14.0/kLOC | Average: 20.6/kLOC** + +### Comparison + +| Metric | Established | Vibe-coded | Ratio | +|--------|:-----------:|:----------:|:-----:| +| Median density | 4.4/kLOC | 14.0/kLOC | **3.2x** | +| Average density | 5.7/kLOC | 20.6/kLOC | **3.6x** | + +Vibe-coded projects consistently trigger more findings per line of code. + +## Notes on Established Repo Findings + +Some established repos show higher density for valid reasons: + +- **zod** (13.6/kLOC): 634 of 964 findings are `excessive-any`. Zod deliberately uses `any` for TypeScript type gymnastics -- this is intentional, not a code smell. +- **date-fns** (3.1/kLOC): 218 of 308 findings are `excessive-comment-ratio`. date-fns has extensive JSDoc documentation -- by design, not an AI pattern. +- **express** (5.8/kLOC): 73 of 123 findings are `placeholder-in-production`. Express uses example domains in comments. + +vibecop detects patterns, not intent. Use `.vibecop.yml` to tune or disable detectors for your codebase. + +## v0.2 New Detector Impact + +The 6 LLM/agent safety detectors added in v0.2 found **157 additional issues** across the vibe-coded repos that v0.1 detectors missed entirely: + +| Detector | Findings | Example Repos | +|----------|:--------:|--------------| +| `unsafe-shell-exec` | 63 | dyad (47), context7 (6), code-review-graph (5) | +| `llm-unpinned-model` | 53 | bolt.diy (32), dyad (12), vibe-check-mcp (6) | +| `llm-no-system-message` | 39 | dyad (31), bolt.diy (7), vibe-check-mcp (1) | +| `llm-call-no-timeout` | 2 | vibe-check-mcp (2) | + +Zero v0.2 detector findings on the 5 established repos -- they do not use LLM APIs or dynamic shell execution. + +## Reproducing + +### Fixture Benchmark + +Run the benchmark script included in the vibecop repository: + +```bash +bash scripts/benchmark.sh +``` + +### Full Open-Source Benchmark + +Clone any repo and scan it: + +```bash +# Clone a repo +git clone --depth 1 https://github.com/fastify/fastify /tmp/fastify + +# Scan with no config and high finding cap +vibecop scan /tmp/fastify --format json --no-config --max-findings 2000 +``` + +The JSON output includes a `summary.total` field with the finding count. Count LOC with: + +```bash +find /tmp/fastify -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' \ + | grep -v node_modules | xargs wc -l +``` diff --git a/product-docs/src/content/docs/configuration/cli-reference.mdx b/product-docs/src/content/docs/configuration/cli-reference.mdx new file mode 100644 index 0000000..61826dd --- /dev/null +++ b/product-docs/src/content/docs/configuration/cli-reference.mdx @@ -0,0 +1,188 @@ +--- +title: CLI Reference +description: All vibecop commands and options +--- + +## Commands + +### vibecop scan + +Scan a directory for code quality issues. + +```bash +vibecop scan [path] +``` + +**Arguments:** + +| Argument | Description | Default | +|----------|-------------|---------| +| `[path]` | Directory to scan | `.` (current directory) | + +**Options:** + +| Flag | Description | Default | +|------|-------------|---------| +| `-f, --format ` | Output format: `text`, `json`, `github`, `sarif`, `html`, `agent`, `gcc` | `text` | +| `-c, --config ` | Path to config file | `.vibecop.yml` | +| `--no-config` | Disable config file loading | | +| `--max-findings ` | Maximum number of findings to report | `50` | +| `--verbose` | Show timing information | `false` | +| `--diff ` | Scan only files changed vs git ref | | +| `--stdin-files` | Read file list from stdin (one path per line) | `false` | +| `--group-by ` | Group findings by `file` or `rule` | `file` | + +**Examples:** + +```bash +# Scan current directory +vibecop scan . + +# Scan with JSON output +vibecop scan src/ --format json + +# Scan only changed files +vibecop scan --diff HEAD + +# Scan with custom config +vibecop scan . --config .vibecop.yml + +# Unlimited findings with timing info +vibecop scan . --max-findings 0 --verbose + +# Read file list from stdin +git diff --name-only HEAD | vibecop scan --stdin-files + +# Group findings by rule instead of file +vibecop scan . --group-by rule +``` + +### vibecop check + +Check a single file for code quality issues. + +```bash +vibecop check +``` + +**Arguments:** + +| Argument | Description | +|----------|-------------| +| `` | File to check (required) | + +**Options:** + +| Flag | Description | Default | +|------|-------------|---------| +| `-f, --format ` | Output format: `text`, `json`, `github`, `sarif`, `html`, `agent`, `gcc` | `text` | +| `--max-findings ` | Maximum number of findings to report | `50` | +| `--verbose` | Show timing information | `false` | +| `--group-by ` | Group findings by `file` or `rule` | `file` | + +**Examples:** + +```bash +# Check a single file +vibecop check src/utils/api.ts + +# Check with JSON output +vibecop check src/auth.ts --format json +``` + +### vibecop init + +Auto-detect AI coding tools and generate integration config files. + +```bash +vibecop init +``` + +No arguments or options. Detects tools by checking for: + +- `.claude/` directory (Claude Code) +- `.cursor/` directory (Cursor) +- `.codex/` directory (Codex CLI) +- `aider` in PATH (Aider) +- `.windsurf/` directory (Windsurf) +- `.github/` directory (GitHub Copilot) +- `.cline/` or `.clinerules` (Cline/Roo Code) + +Generates the appropriate config files for each detected tool. See [Agent Integration](/vibecop/agent-integration/overview/) for details. + +### vibecop serve + +Start the MCP server on stdio transport. + +```bash +vibecop serve +``` + +No arguments or options. The server exposes three tools (`vibecop_scan`, `vibecop_check`, `vibecop_explain`) via the Model Context Protocol. See [MCP Server](/vibecop/agent-integration/mcp-server/) for client configuration. + +### vibecop test-rules + +Validate custom YAML rules against their inline examples. + +```bash +vibecop test-rules [--rules-dir ] +``` + +**Options:** + +| Flag | Description | Default | +|------|-------------|---------| +| `--rules-dir ` | Path to custom rules directory | `.vibecop/rules` | + +**Examples:** + +```bash +# Test rules in default directory +vibecop test-rules + +# Test rules in custom directory +vibecop test-rules --rules-dir my-rules/ +``` + +Exits with code 1 if any rules fail validation or example testing. + +## Exit Codes + +| Code | Meaning | +|------|---------| +| `0` | No findings (clean) | +| `1` | One or more findings found | +| `2` | Scan error (bad arguments, file not found, git error, etc.) | + +## Output Formats + +| Format | Description | +|--------|-------------| +| `text` | Default. Stylish terminal output grouped by file, with colors | +| `json` | Structured JSON with `findings`, `filesScanned`, `errors`, and optional `timing` | +| `github` | GitHub Actions `::error` annotations + `GITHUB_STEP_SUMMARY` markdown | +| `sarif` | SARIF 2.1.0 format for GitHub Security tab upload | +| `html` | Self-contained single-file HTML report | +| `agent` | Token-efficient one-finding-per-line format for AI agent hooks | +| `gcc` | GCC-style `file:line:col: severity: message [rule-id]` for editor integration | + +### Agent Format Example + +``` +src/api.ts:42:1 error unsafe-shell-exec: execSync() with template literal. Use execFile() with argument array instead. +src/llm.ts:18:5 warning llm-unpinned-model: Unpinned model alias "gpt-4o". Pin to a dated version like "gpt-4o-2024-08-06". +``` + +### GCC Format Example + +``` +src/api.ts:42:1: error: execSync() with template literal [unsafe-shell-exec] +src/llm.ts:18:5: warning: Unpinned model alias "gpt-4o" [llm-unpinned-model] +``` + +## Global Options + +| Flag | Description | +|------|-------------| +| `--version` | Print version number | +| `--help` | Print help text | diff --git a/product-docs/src/content/docs/configuration/config-file.mdx b/product-docs/src/content/docs/configuration/config-file.mdx new file mode 100644 index 0000000..b85910e --- /dev/null +++ b/product-docs/src/content/docs/configuration/config-file.mdx @@ -0,0 +1,202 @@ +--- +title: Config File +description: Full .vibecop.yml configuration reference +--- + +vibecop is configured via a `.vibecop.yml` file in your project root. The config file is optional -- vibecop runs with safe defaults if no config is present. + +## Loading Behavior + +1. vibecop looks for `.vibecop.yml` in the current working directory +2. If not found, defaults are used (all detectors enabled, standard ignore patterns) +3. Use `--config ` to specify a different config file +4. Use `--no-config` to skip config loading entirely + +## Full Reference + +```yaml +# Per-rule configuration +rules: + god-function: + severity: warning # override default severity + debug-console-in-prod: + severity: "off" # disable this detector entirely + excessive-any: + severity: warning + excessive-comment-ratio: + threshold: 0.5 # custom threshold (50% comment lines) + +# File/directory ignore patterns (glob syntax) +ignore: + - "**/node_modules/**" + - "**/dist/**" + - "**/build/**" + - "**/.next/**" + - "**/docs/**" + - "**/vendor/**" + - "**/*.min.js" + - "**/*.d.ts" + +# PR gate configuration (used by GitHub Action) +pr-gate: + on-failure: request-changes # comment-only | request-changes | label | auto-close + severity-threshold: warning # error | warning | info + max-findings: 50 # 0 = unlimited + label: "vibecop:needs-review" # label to apply when on-failure is "label" + +# Custom rules directory +custom-rules-dir: .vibecop/rules +``` + +## Rules Section + +The `rules` section configures individual detectors. Each key is a detector ID. + +### Setting Severity + +Override the default severity for any detector: + +```yaml +rules: + god-function: + severity: error # escalate to error + todo-in-production: + severity: warning # escalate from info + debug-console-in-prod: + severity: info # downgrade to info +``` + +Valid severity values: `error`, `warning`, `info`, `off`. + +### Disabling a Detector + +Set the severity to `off`: + +```yaml +rules: + excessive-comment-ratio: + severity: "off" + over-defensive-coding: + severity: "off" +``` + +### Custom Thresholds + +Some detectors support additional configuration: + +```yaml +rules: + excessive-comment-ratio: + threshold: 0.6 # 60% comment lines (default: 0.5) +``` + +## Ignore Section + +The `ignore` section specifies glob patterns for files and directories to skip during scanning. When specified, user patterns **replace** the defaults entirely. + +### Default Ignore Patterns + +If no `ignore` section is provided, these patterns are used: + +```yaml +ignore: + - "**/node_modules/**" + - "**/dist/**" + - "**/build/**" + - "**/.next/**" + - "**/docs/**" + - "**/vendor/**" + - "**/*.min.js" + - "**/*.d.ts" +``` + +### Custom Ignore Patterns + +```yaml +ignore: + - "**/node_modules/**" + - "**/dist/**" + - "**/generated/**" + - "**/*.test.ts" # skip test files + - "src/legacy/**" # skip legacy code +``` + +Note: When you specify `ignore`, the defaults are **replaced**, not merged. Include any default patterns you want to keep. + +## PR Gate Section + +The `pr-gate` section configures behavior for the GitHub Action. See [GitHub Action](/vibecop/github-action/) for full details. + +```yaml +pr-gate: + on-failure: comment-only # what to do when findings are detected + severity-threshold: warning # minimum severity to report + max-findings: 50 # cap on findings per PR + label: "vibecop:needs-review" # label for on-failure: label mode +``` + +### on-failure Options + +| Value | Behavior | +|-------|----------| +| `comment-only` | Post inline comments on findings (default) | +| `request-changes` | Post comments and request changes on the PR | +| `label` | Apply a label to the PR | +| `auto-close` | Close the PR (opt-in, use carefully) | + +## Custom Rules Directory + +Point to a directory containing custom YAML rules: + +```yaml +custom-rules-dir: .vibecop/rules +``` + +See [Custom Rules](/vibecop/configuration/custom-rules/) for the YAML rule format. + +## Validation + +The config file is validated with Zod at startup. Invalid configs produce clear error messages: + +``` +Invalid config in .vibecop.yml: + - rules.god-function.severity: Invalid enum value. Expected 'error' | 'warning' | 'info' | 'off', received 'critical' +``` + +## Examples + +### Minimal Config + +```yaml +rules: + debug-console-in-prod: + severity: "off" +``` + +### Strict Config + +```yaml +rules: + god-function: + severity: error + excessive-any: + severity: error + todo-in-production: + severity: warning + +ignore: + - "**/node_modules/**" + - "**/dist/**" +``` + +### Library Config + +For libraries that deliberately use `any` (like Zod): + +```yaml +rules: + excessive-any: + severity: "off" + double-type-assertion: + severity: "off" +``` diff --git a/product-docs/src/content/docs/configuration/custom-rules.mdx b/product-docs/src/content/docs/configuration/custom-rules.mdx new file mode 100644 index 0000000..3ba7364 --- /dev/null +++ b/product-docs/src/content/docs/configuration/custom-rules.mdx @@ -0,0 +1,219 @@ +--- +title: Custom Rules +description: Write your own vibecop rules using YAML and ast-grep patterns +--- + +vibecop supports custom rules defined in YAML files. Custom rules use [ast-grep pattern syntax](https://ast-grep.github.io/guide/rule-config.html) for AST-based matching, giving you the full power of tree-sitter pattern matching without writing TypeScript. + +## Directory Setup + +Create a `.vibecop/rules/` directory in your project root: + +``` +.vibecop/ + rules/ + no-console-error.yaml + require-error-boundary.yaml +``` + +Then reference it in `.vibecop.yml`: + +```yaml +custom-rules-dir: .vibecop/rules +``` + +If `custom-rules-dir` is not set, vibecop looks for `.vibecop/rules/` by default. + +## YAML Rule Format + +Each `.yaml` or `.yml` file in the rules directory defines one rule: + +```yaml +id: no-console-error +name: No Console Error +description: Disallow console.error in production code +severity: warning +category: quality +languages: + - typescript + - javascript +message: "console.error() found. Use a structured logger instead." +suggestion: "Replace with logger.error() from your logging library." +rule: + pattern: console.error($$$ARGS) +examples: + valid: + - "logger.error('something failed', error);" + invalid: + - "console.error('something failed', error);" +``` + +## Schema Reference + +| Field | Type | Required | Description | +|-------|------|:--------:|-------------| +| `id` | string | Yes | Unique rule ID (lowercase, hyphens, starts with letter) | +| `name` | string | Yes | Human-readable name | +| `description` | string | Yes | What the rule detects | +| `severity` | `error` / `warning` / `info` | Yes | Finding severity | +| `category` | `correctness` / `quality` / `security` / `testing` | Yes | Rule category | +| `languages` | array | Yes | Languages to check: `javascript`, `typescript`, `tsx`, `python` | +| `message` | string | Yes | Message shown in findings | +| `suggestion` | string | No | How to fix the issue | +| `rule` | object | Yes | ast-grep rule object (pattern matching config) | +| `examples` | object | No | Test cases for validation | + +### The `id` Field + +Must match the regex `^[a-z][a-z0-9-]*$`. Examples: `no-console-error`, `require-auth-check`, `ban-eval`. + +### The `rule` Field + +The `rule` field is an ast-grep rule object. It supports the full ast-grep rule configuration syntax: + +```yaml +# Simple pattern match +rule: + pattern: console.error($$$ARGS) + +# Match with constraints +rule: + pattern: fetch($URL) + not: + inside: + pattern: try { $$$ } catch ($ERR) { $$$ } + +# Match specific node kinds +rule: + kind: call_expression + has: + pattern: eval($ARG) +``` + +See the [ast-grep rule reference](https://ast-grep.github.io/reference/rule.html) for the complete rule syntax. + +### The `examples` Field + +Provide test cases to validate your rule: + +```yaml +examples: + valid: + - "logger.error('something failed');" + - "console.log('debug info');" + invalid: + - "console.error('something failed');" + - "console.error(error);" +``` + +- `valid` examples should **not** trigger the rule +- `invalid` examples **should** trigger the rule + +## Testing Custom Rules + +Run `vibecop test-rules` to validate your custom rules against their inline examples: + +```bash +vibecop test-rules +``` + +Or specify a custom rules directory: + +```bash +vibecop test-rules --rules-dir .vibecop/rules +``` + +Output: + +``` +✓ no-console-error: 2 invalid examples matched, 2 valid examples clean +✓ require-error-boundary: 1 invalid example matched, 1 valid example clean +✗ ban-eval: invalid example 1 did not match (rule may be wrong) + +2 rules passed, 1 rule failed +``` + +The command exits with code 1 if any rules fail, making it suitable for CI. + +## Example Rules + +### Ban `setTimeout` in Non-Test Files + +```yaml +id: no-settimeout +name: No setTimeout +description: Disallow setTimeout in production code +severity: warning +category: quality +languages: + - typescript + - javascript +message: "setTimeout() found. Use a proper scheduling mechanism." +suggestion: "Use a task queue or cron job for delayed execution." +rule: + pattern: setTimeout($CALLBACK, $DELAY) +examples: + valid: + - "scheduleTask(callback, delay);" + invalid: + - "setTimeout(() => { process(); }, 1000);" +``` + +### Require `try/catch` Around Fetch + +```yaml +id: unhandled-fetch +name: Unhandled Fetch +description: fetch() calls without error handling +severity: warning +category: correctness +languages: + - typescript + - javascript +message: "fetch() without try/catch. Network requests can fail." +suggestion: "Wrap in try/catch or add .catch() handler." +rule: + pattern: await fetch($$$ARGS) + not: + inside: + kind: try_statement +examples: + invalid: + - | + async function getData() { + const res = await fetch('/api/data'); + return res.json(); + } +``` + +### Ban `any` Type Annotation + +```yaml +id: no-any-type +name: No Any Type +description: Disallow explicit any type annotations +severity: warning +category: quality +languages: + - typescript + - tsx +message: "Explicit 'any' type used. Provide a specific type." +suggestion: "Replace with a proper type or use 'unknown' if the type is truly unknown." +rule: + pattern: "$NAME: any" +examples: + valid: + - "const value: string = 'hello';" + invalid: + - "const value: any = getData();" +``` + +## How Custom Rules Work + +1. At startup, vibecop loads all `.yaml`/`.yml` files from the custom rules directory +2. Each file is parsed and validated against the `CustomRuleSchema` (Zod) +3. Valid rules are converted to `Detector` objects +4. During scanning, custom rule detectors run alongside built-in detectors +5. Custom rules respect the same ignore patterns and severity overrides as built-in rules + +Invalid rule files produce warnings but do not stop the scan -- other rules and built-in detectors continue to run. diff --git a/product-docs/src/content/docs/detectors/correctness.mdx b/product-docs/src/content/docs/detectors/correctness.mdx new file mode 100644 index 0000000..f3dfe3c --- /dev/null +++ b/product-docs/src/content/docs/detectors/correctness.mdx @@ -0,0 +1,105 @@ +--- +title: Correctness Detectors +description: All 4 correctness detectors with descriptions and examples +--- + +Correctness detectors identify bugs that cause runtime failures: undeclared imports, unchecked database results, mixed concerns, and hallucinated packages. + +## unchecked-db-result + +**Severity:** warning + +Fire-and-forget database mutations (insert, update, delete) where the result is not checked. + +**What it catches:** + +```typescript +await prisma.user.update({ + where: { id: userId }, + data: { name: newName }, +}); +// result is not captured or checked +``` + +**Fix:** Check the result: + +```typescript +const updated = await prisma.user.update({ + where: { id: userId }, + data: { name: newName }, +}); +if (!updated) { + throw new Error('User not found'); +} +``` + +**Why it matters:** AI agents generate database mutations without checking whether they succeeded. A failed update or delete silently does nothing, leading to data inconsistency. + +## undeclared-import + +**Severity:** error + +Imports that are not declared in `package.json`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `requirements.txt`, or `pyproject.toml`. + +**What it catches:** + +```typescript +import { z } from 'zod'; // if 'zod' is not in package.json +import express from 'express'; // if 'express' is not in package.json +``` + +```python +import pandas # if 'pandas' is not in requirements.txt +``` + +**How it works:** vibecop parses `package.json` dependencies, lock files, and Python manifest files at startup. For each import statement, it checks whether the package is declared. Zero network calls -- everything is local. + +**Why it matters:** AI agents hallucinate package names or import packages that are not installed. This causes runtime `MODULE_NOT_FOUND` errors that only surface when the code actually runs. + +**Note:** Built-in modules (like `fs`, `path`, `os` in Node.js, or `json`, `sys` in Python) are automatically excluded. + +## mixed-concerns + +**Severity:** warning + +Files that import both UI frameworks (React, Vue, Svelte) and database/server libraries (Prisma, Mongoose, pg, Express). + +**What it catches:** + +```typescript +import React from 'react'; +import { PrismaClient } from '@prisma/client'; + +function UserPage() { + const prisma = new PrismaClient(); + const users = await prisma.user.findMany(); + return
{users.map(u => {u.name})}
; +} +``` + +**Why it matters:** AI agents often put database queries directly inside React components, violating the separation between UI and data access layers. This makes code untestable and creates tight coupling. + +## hallucinated-package + +**Severity:** info + +Dependencies in `package.json` that are not in the bundled top-5K npm packages allowlist. + +**What it catches:** + +```json +{ + "dependencies": { + "react": "^18.0.0", + "super-fast-validator": "^1.0.0" + } +} +``` + +If `super-fast-validator` is not in the top-5K npm packages, vibecop flags it as a potential hallucination. + +**How it works:** vibecop ships a bundled allowlist (`src/data/known-packages.json`) derived from npm's most popular packages. Packages not on the list get flagged. Zero network calls. + +**Why it matters:** Research shows 19.7% of AI-suggested packages are hallucinations (USENIX Security 2025). These non-existent packages can be typosquatted by attackers who publish malicious packages with the hallucinated names ("slopsquatting"). + +**Note:** This detector has a high false-positive rate by design -- legitimate but less-popular packages will also be flagged. Use `.vibecop.yml` to suppress findings for known-good packages. diff --git a/product-docs/src/content/docs/detectors/overview.mdx b/product-docs/src/content/docs/detectors/overview.mdx new file mode 100644 index 0000000..c97b369 --- /dev/null +++ b/product-docs/src/content/docs/detectors/overview.mdx @@ -0,0 +1,89 @@ +--- +title: Detector Overview +description: All 35 vibecop detectors in a single reference +--- + +vibecop ships 35 detectors across 4 categories. Every detector is deterministic -- same input always produces the same output, with zero LLM calls. + +## All Detectors + +### Quality (16 detectors) + +| ID | Name | Description | Severity | +|----|------|-------------|----------| +| `god-function` | God Function | Functions exceeding line, complexity, or parameter thresholds | error/warning | +| `god-component` | God Component | React components with too many hooks, lines, or imports | warning | +| `n-plus-one-query` | N+1 Query | DB/API calls inside loops or `.map(async ...)` callbacks | warning | +| `unbounded-query` | Unbounded Query | `findMany`/`findAll` without a `take`/`limit` clause | info | +| `debug-console-in-prod` | Debug Console in Prod | `console.log`/`console.debug` left in production code | warning | +| `dead-code-path` | Dead Code Path | Identical if/else branches, unreachable code after return/throw | warning | +| `double-type-assertion` | Double Type Assertion | `as unknown as X` patterns that bypass TypeScript type safety | warning | +| `excessive-any` | Excessive Any | Files with 4+ `any` type annotations | warning | +| `todo-in-production` | TODO in Production | TODO/FIXME/HACK comments, escalated if security-related | info/warning | +| `empty-error-handler` | Empty Error Handler | Catch/except blocks that silently swallow errors | warning | +| `excessive-comment-ratio` | Excessive Comment Ratio | Files with >50% comment lines | info | +| `over-defensive-coding` | Over-Defensive Coding | Redundant null checks on values that cannot be null | info | +| `llm-call-no-timeout` | LLM Call No Timeout | `new OpenAI()`/`new Anthropic()` without timeout, `.create()` without max_tokens | warning | +| `llm-unpinned-model` | LLM Unpinned Model | Moving model aliases like `"gpt-4o"` that silently change behavior | warning | +| `llm-temperature-not-set` | LLM Temperature Not Set | LLM `.create()` calls without explicit `temperature` parameter | info | +| `llm-no-system-message` | LLM No System Message | Chat API calls without a `role: "system"` message | info | + +### Security (7 detectors) + +| ID | Name | Description | Severity | +|----|------|-------------|----------| +| `sql-injection` | SQL Injection | Template literals or string concatenation in SQL query methods | error | +| `dangerous-inner-html` | Dangerous innerHTML | `dangerouslySetInnerHTML` usage without sanitization | warning | +| `token-in-localstorage` | Token in localStorage | Auth/JWT tokens stored in XSS-accessible storage | error | +| `placeholder-in-production` | Placeholder in Production | `yourdomain.com`, `changeme`, `xxx` left in config | error | +| `insecure-defaults` | Insecure Defaults | `eval()`, `rejectUnauthorized: false`, hardcoded credentials | error | +| `unsafe-shell-exec` | Unsafe Shell Exec | `exec()`/`execSync()` with dynamic args, `subprocess` with `shell=True` | error | +| `dynamic-code-exec` | Dynamic Code Exec | `eval(variable)`, `new Function(variable)` with non-literal arguments | error | + +### Correctness (4 detectors) + +| ID | Name | Description | Severity | +|----|------|-------------|----------| +| `unchecked-db-result` | Unchecked DB Result | Fire-and-forget database mutations (insert/update/delete) | warning | +| `undeclared-import` | Undeclared Import | Imports not declared in package.json/requirements.txt | error | +| `mixed-concerns` | Mixed Concerns | Files importing both UI frameworks and database/server libraries | warning | +| `hallucinated-package` | Hallucinated Package | Dependencies not in top-5K npm allowlist (potential AI hallucination) | info | + +### Testing (8 detectors) + +| ID | Name | Description | Severity | +|----|------|-------------|----------| +| `trivial-assertion` | Trivial Assertion | `expect(true).toBe(true)` and similar no-op tests | info | +| `over-mocking` | Over-Mocking | Test files with excessive mock/spy usage | info | +| `assertion-roulette` | Assertion Roulette | Tests with too many assertions (default >5) | warning | +| `sleepy-test` | Sleepy Test | `setTimeout`/`time.sleep` in tests causing flaky CI | warning | +| `snapshot-only-test` | Snapshot-Only Test | Test files where 100% of assertions are snapshots | info | +| `empty-test` | Empty Test | Test functions with zero assertions | info | +| `conditional-test-logic` | Conditional Test Logic | Control flow in tests where assertions may not execute | info | +| `no-error-path-test` | No Error Path Test | Test files with 3+ tests but no error path testing | info | + +## Category Breakdown + +| Category | Count | Focus | +|----------|:-----:|-------| +| Quality | 16 | Code structure, complexity, LLM API usage | +| Security | 7 | Injection, unsafe execution, credential storage | +| Correctness | 4 | Missing dependencies, unchecked results, hallucinated packages | +| Testing | 8 | Test quality, assertion validity, mock overuse | + +## Severity Levels + +| Severity | Meaning | Exit Code | +|----------|---------|:---------:| +| `error` | Must fix -- security vulnerability or correctness bug | 1 | +| `warning` | Should fix -- code quality or maintainability issue | 1 | +| `info` | Consider fixing -- style or best practice suggestion | 1 | + +All severity levels cause exit code 1. Use `.vibecop.yml` to change severity levels or disable specific detectors. + +## Detailed Pages + +- [Quality Detectors](/vibecop/detectors/quality/) -- all 16 quality detectors with examples +- [Security Detectors](/vibecop/detectors/security/) -- all 7 security detectors with examples +- [Correctness Detectors](/vibecop/detectors/correctness/) -- all 4 correctness detectors with examples +- [Testing Detectors](/vibecop/detectors/testing/) -- all 8 testing detectors with examples diff --git a/product-docs/src/content/docs/detectors/quality.mdx b/product-docs/src/content/docs/detectors/quality.mdx new file mode 100644 index 0000000..7de4ddb --- /dev/null +++ b/product-docs/src/content/docs/detectors/quality.mdx @@ -0,0 +1,289 @@ +--- +title: Quality Detectors +description: All 16 quality detectors with descriptions and examples +--- + +Quality detectors identify code structure and maintainability issues common in AI-generated code. These include god functions, N+1 queries, dead code, excessive `any` usage, and LLM API misuse. + +## god-function + +**Severity:** error (>200 lines or complexity >40) / warning (>100 lines or complexity >20) + +Functions exceeding line count, cyclomatic complexity, or parameter thresholds. + +**What it catches:** + +```typescript +// 232-line function with cyclomatic complexity 41 +async function processUserData(data: any, options: any, config: any) { + // ... 232 lines of nested logic +} +``` + +**Why it matters:** AI agents tend to generate monolithic functions that handle multiple responsibilities. These are hard to test, review, and maintain. + +**Configuration:** Thresholds can be tuned via `.vibecop.yml`. + +## god-component + +**Severity:** warning + +React components with too many hooks, lines, or imports. + +**What it catches:** + +```tsx +function PaymentModal() { + const [step, setStep] = useState(0); + const [amount, setAmount] = useState(0); + const [currency, setCurrency] = useState('USD'); + // ... 8 useState, 3 useEffect, 593 lines total +} +``` + +**Why it matters:** AI agents often pack all logic into a single component rather than composing smaller ones. + +## n-plus-one-query + +**Severity:** warning + +Database or API calls inside loops or `.map(async ...)` callbacks. + +**What it catches:** + +```typescript +for (const user of users) { + const orders = await db.orders.findMany({ where: { userId: user.id } }); +} +``` + +**Why it matters:** Each iteration executes a separate query. With 100 users, that's 101 queries instead of 1. AI agents frequently generate this pattern when iterating over collections. + +## unbounded-query + +**Severity:** info + +`findMany`/`findAll` without a `take`/`limit` clause. + +**What it catches:** + +```typescript +const allUsers = await prisma.user.findMany(); +``` + +**Why it matters:** Without a limit, this fetches every row in the table. On production databases, this can cause memory exhaustion and slow responses. + +## debug-console-in-prod + +**Severity:** warning + +`console.log`/`console.debug` statements left in production code (non-test files). + +**What it catches:** + +```typescript +console.log('user data:', userData); +console.debug('response:', response); +``` + +**Why it matters:** AI agents scatter `console.log` statements throughout generated code for debugging, then forget to remove them. + +## dead-code-path + +**Severity:** warning + +Identical if/else branches, unreachable code after return/throw. + +**What it catches:** + +```typescript +if (condition) { + return processData(data); +} else { + return processData(data); // identical branch +} +``` + +**Why it matters:** AI agents sometimes generate conditional branches that do the same thing, or leave code after early returns. + +## double-type-assertion + +**Severity:** warning + +`as unknown as X` patterns that bypass TypeScript type safety. + +**What it catches:** + +```typescript +const user = response as unknown as User; +``` + +**Why it matters:** Double assertions silence TypeScript completely. AI agents use this shortcut instead of fixing the actual type mismatch. + +## excessive-any + +**Severity:** warning + +Files with 4+ `any` type annotations. + +**What it catches:** + +```typescript +function processData(input: any, config: any): any { + const result: any = transform(input); + return result; +} +``` + +**Why it matters:** AI agents default to `any` when they cannot infer the correct type, defeating the purpose of TypeScript. + +## todo-in-production + +**Severity:** info (general) / warning (security-related) + +TODO/FIXME/HACK comments left in production code. + +**What it catches:** + +```typescript +// TODO: add input validation before database query +// FIXME: this is vulnerable to SQL injection +// HACK: hardcoded API key for now +``` + +**Why it matters:** AI agents leave TODO comments as placeholders for unimplemented logic. Security-related TODOs are escalated to warning severity. + +## empty-error-handler + +**Severity:** warning + +Catch/except blocks that silently swallow errors. + +**What it catches:** + +```typescript +try { + await saveUser(data); +} catch (e) { + console.log(e); // logs but does not handle +} +``` + +```python +try: + save_user(data) +except: + pass # silently swallowed +``` + +**Why it matters:** AI agents wrap code in try/catch but do not implement meaningful error handling. Errors disappear silently. + +## excessive-comment-ratio + +**Severity:** info + +Files where more than 50% of lines are comments. + +**What it catches:** Files with extensive AI-generated documentation comments that inflate the codebase without adding value. + +**Why it matters:** AI agents tend to over-document with verbose JSDoc and inline comments, sometimes generating more comments than code. + +**Configuration:** The threshold (default 0.5) can be tuned in `.vibecop.yml`. + +## over-defensive-coding + +**Severity:** info + +Redundant null checks on values that cannot be null. + +**What it catches:** + +```typescript +const items = getItems(); // returns Item[], never null +if (items !== null && items !== undefined) { + // unnecessary null check +} +``` + +**Why it matters:** AI agents add defensive null checks everywhere, even where the type system guarantees a value exists. + +## llm-call-no-timeout + +**Severity:** warning + +`new OpenAI()` or `new Anthropic()` constructors without a `timeout` option, and `.create()` calls without `max_tokens`. + +**What it catches:** + +```typescript +const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); +// missing: timeout option + +const response = await openai.chat.completions.create({ + model: 'gpt-4o', + messages: [{ role: 'user', content: prompt }], + // missing: max_tokens +}); +``` + +**Why it matters:** Without timeouts, LLM calls can hang indefinitely. Without `max_tokens`, responses can be unexpectedly large and expensive. Source: SpecDetect4LLM (UMM pattern). + +## llm-unpinned-model + +**Severity:** warning + +Moving model aliases like `"gpt-4o"` or `"claude-3-sonnet"` that silently change behavior when the provider updates the alias. + +**What it catches:** + +```typescript +const response = await openai.chat.completions.create({ + model: 'gpt-4o', // alias — could point to different model tomorrow +}); +``` + +**Fix:** Pin to a dated version: + +```typescript +model: 'gpt-4o-2024-08-06' +``` + +**Why it matters:** Model aliases are moving targets. Your application's behavior can change without any code change. Source: SpecDetect4LLM (NMVP pattern). + +## llm-temperature-not-set + +**Severity:** info + +LLM `.create()` calls without an explicit `temperature` parameter. + +**What it catches:** + +```typescript +const response = await openai.chat.completions.create({ + model: 'gpt-4o-2024-08-06', + messages: [...], + // missing: temperature +}); +``` + +**Why it matters:** The default temperature varies by provider and model. Explicit temperature ensures consistent output behavior. Source: SpecDetect4LLM (TNES pattern). + +## llm-no-system-message + +**Severity:** info + +Chat API calls without a `role: "system"` message in the messages array. + +**What it catches:** + +```typescript +const response = await openai.chat.completions.create({ + model: 'gpt-4o-2024-08-06', + messages: [ + { role: 'user', content: 'Summarize this document' }, + // missing: system message + ], +}); +``` + +**Why it matters:** System messages set the behavior, constraints, and output format for the model. Without them, the model operates without guardrails. Source: SpecDetect4LLM (NSM pattern). diff --git a/product-docs/src/content/docs/detectors/security.mdx b/product-docs/src/content/docs/detectors/security.mdx new file mode 100644 index 0000000..d7c551b --- /dev/null +++ b/product-docs/src/content/docs/detectors/security.mdx @@ -0,0 +1,170 @@ +--- +title: Security Detectors +description: All 7 security detectors with descriptions and examples +--- + +Security detectors identify vulnerabilities that AI agents frequently introduce: SQL injection, unsafe shell execution, dynamic code evaluation, hardcoded credentials, and XSS vectors. + +## sql-injection + +**Severity:** error + +Template literals or string concatenation in SQL query methods. + +**What it catches:** + +```typescript +const query = `SELECT * FROM users WHERE id = '${userId}'`; +await db.query(query); +``` + +```typescript +const result = await db.raw("SELECT * FROM orders WHERE status = '" + status + "'"); +``` + +**Fix:** Use parameterized queries: + +```typescript +const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]); +``` + +**Why it matters:** AI agents frequently interpolate user input directly into SQL strings. This is the most common web application vulnerability (OWASP A03). + +## dangerous-inner-html + +**Severity:** warning + +`dangerouslySetInnerHTML` usage without sanitization. + +**What it catches:** + +```tsx +
+``` + +**Fix:** Sanitize the content first: + +```tsx +import DOMPurify from 'dompurify'; +
+``` + +**Why it matters:** AI agents use `dangerouslySetInnerHTML` to render HTML content without considering XSS risks. Any user-controlled content becomes an injection vector. + +## token-in-localstorage + +**Severity:** error + +Auth/JWT tokens stored in browser `localStorage`, which is accessible to any JavaScript on the page. + +**What it catches:** + +```typescript +localStorage.setItem('authToken', token); +localStorage.setItem('jwt', response.token); +localStorage.setItem('access_token', data.access_token); +``` + +**Fix:** Use `httpOnly` cookies instead: + +```typescript +// Server-side: set the token as an httpOnly cookie +res.cookie('token', jwt, { httpOnly: true, secure: true, sameSite: 'strict' }); +``` + +**Why it matters:** `localStorage` is accessible to any JavaScript running on the page, including XSS payloads. AI agents commonly use `localStorage` for token storage because it's the simplest approach. + +## placeholder-in-production + +**Severity:** error + +Placeholder values like `yourdomain.com`, `changeme`, `xxx`, `TODO`, or `example.com` left in configuration code. + +**What it catches:** + +```typescript +const config = { + domain: 'yourdomain.com', + apiKey: 'changeme', + secret: 'xxx', +}; +``` + +**Why it matters:** AI agents generate code with placeholder values that developers forget to replace. These can expose configuration details or cause silent failures in production. + +## insecure-defaults + +**Severity:** error + +`eval()` usage, `rejectUnauthorized: false` (disabling TLS verification), and hardcoded credentials. + +**What it catches:** + +```typescript +// Disabling TLS verification +const agent = new https.Agent({ rejectUnauthorized: false }); + +// eval with user input +eval(userInput); + +// Hardcoded credentials +const password = 'admin123'; +``` + +**Why it matters:** AI agents reach for the simplest solution: `eval()` for dynamic code, disabled TLS for HTTPS errors, and hardcoded credentials for quick testing. All of these are serious security vulnerabilities. + +## unsafe-shell-exec + +**Severity:** error + +`exec()`, `execSync()`, or `spawn()` with template literals or string concatenation, and Python's `subprocess` with `shell=True`. + +**What it catches:** + +```typescript +// JavaScript/TypeScript +execSync(`git clone ${repoUrl}`); +exec(`rm -rf ${userPath}`); +``` + +```python +# Python +subprocess.run(f"git clone {repo_url}", shell=True) +os.system(f"rm -rf {user_path}") +``` + +**Fix:** Use argument arrays: + +```typescript +execFileSync('git', ['clone', repoUrl]); +``` + +```python +subprocess.run(['git', 'clone', repo_url], shell=False) +``` + +**Why it matters:** AI coding agents frequently generate shell commands with interpolated variables. If any variable contains user input, this enables arbitrary command execution. This is especially dangerous in agentic workflows where the AI tool has write access to the filesystem. Source: OWASP Agentic Security guidelines. + +## dynamic-code-exec + +**Severity:** error + +`eval(variable)`, `new Function(variable)`, or `Function(variable)` with non-literal arguments. + +**What it catches:** + +```typescript +eval(userProvidedCode); +const fn = new Function(dynamicCode); +const result = Function('return ' + expression)(); +``` + +**Fix:** Avoid dynamic code execution entirely. If needed, use a sandboxed VM: + +```typescript +import { VM } from 'vm2'; +const vm = new VM(); +const result = vm.run(sandboxedCode); +``` + +**Why it matters:** AI agents use `eval()` and `new Function()` for dynamic behavior (e.g., evaluating user expressions, running generated code). This enables arbitrary code execution if the input is not trusted. Source: OWASP Agentic Security guidelines. diff --git a/product-docs/src/content/docs/detectors/testing.mdx b/product-docs/src/content/docs/detectors/testing.mdx new file mode 100644 index 0000000..46c5dd3 --- /dev/null +++ b/product-docs/src/content/docs/detectors/testing.mdx @@ -0,0 +1,222 @@ +--- +title: Testing Detectors +description: All 8 testing detectors with descriptions and examples +--- + +Testing detectors evaluate the quality of test code. AI agents frequently generate tests that pass but do not actually verify behavior: trivial assertions, excessive mocking, snapshot-only coverage, and missing error paths. + +## trivial-assertion + +**Severity:** info + +Assertions that always pass and test nothing meaningful. + +**What it catches:** + +```typescript +expect(true).toBe(true); +expect(1).toBe(1); +expect('hello').toBe('hello'); +assert.ok(true); +``` + +```python +assert True +assert 1 == 1 +``` + +**Why it matters:** AI agents generate these assertions to make tests pass. They inflate test counts without actually verifying any behavior. + +## over-mocking + +**Severity:** info + +Test files where the number of `jest.mock()`, `vi.mock()`, `jest.spyOn()`, or `vi.spyOn()` calls exceeds the number of assertions. + +**What it catches:** + +```typescript +jest.mock('../services/auth'); +jest.mock('../services/db'); +jest.mock('../services/email'); +jest.mock('../services/queue'); +jest.mock('../utils/logger'); + +test('should process user', () => { + const result = processUser(mockData); + expect(result).toBeDefined(); // 5 mocks, 1 weak assertion +}); +``` + +**Why it matters:** When mocks outnumber assertions, the test is verifying the mocking setup rather than the actual behavior. AI agents mock aggressively to avoid dealing with dependencies. + +## assertion-roulette + +**Severity:** warning + +Tests with more than 5 assertions (configurable). When a test has many assertions and one fails, it is hard to determine which behavior broke. + +**What it catches:** + +```typescript +test('should handle user flow', () => { + expect(user.name).toBe('Alice'); + expect(user.email).toBe('alice@example.com'); + expect(user.role).toBe('admin'); + expect(user.verified).toBe(true); + expect(user.loginCount).toBe(5); + expect(user.lastLogin).toBeDefined(); + expect(user.preferences.theme).toBe('dark'); + // 7 assertions in one test +}); +``` + +**Fix:** Split into focused tests: + +```typescript +test('should set user identity', () => { + expect(user.name).toBe('Alice'); + expect(user.email).toBe('alice@example.com'); +}); + +test('should set user role', () => { + expect(user.role).toBe('admin'); + expect(user.verified).toBe(true); +}); +``` + +**Why it matters:** AI agents generate monolithic tests with many assertions. When one fails, all subsequent assertions are skipped, hiding additional failures. + +## sleepy-test + +**Severity:** warning + +`setTimeout`, `sleep`, or `time.sleep` calls in test files that introduce timing dependencies. + +**What it catches:** + +```typescript +test('should update after delay', async () => { + triggerUpdate(); + await new Promise(resolve => setTimeout(resolve, 2000)); + expect(getState()).toBe('updated'); +}); +``` + +```python +def test_async_update(): + trigger_update() + time.sleep(2) + assert get_state() == 'updated' +``` + +**Fix:** Use proper async utilities: + +```typescript +test('should update after delay', async () => { + triggerUpdate(); + await waitFor(() => expect(getState()).toBe('updated')); +}); +``` + +**Why it matters:** Sleep-based tests are flaky -- they pass on fast machines and fail on slow CI runners. AI agents use sleep as a simple way to wait for async operations. + +## snapshot-only-test + +**Severity:** info + +Test files where 100% of assertions are snapshot assertions (`toMatchSnapshot`, `toMatchInlineSnapshot`). + +**What it catches:** + +```typescript +test('renders user card', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); + +test('renders empty state', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); +// entire file is snapshots +``` + +**Why it matters:** Snapshots verify that output has not changed, but they do not verify correctness. AI agents default to snapshot tests because they are easy to generate. A file with only snapshots provides no behavioral verification. + +## empty-test + +**Severity:** info + +Test functions with zero assertions. + +**What it catches:** + +```typescript +test('should handle edge case', () => { + const result = processData(edgeCaseInput); + // no assertion — test always passes +}); +``` + +```typescript +it('should validate input', async () => { + await validateInput(testData); + // no expect() call +}); +``` + +**Why it matters:** Tests without assertions always pass. AI agents sometimes generate test scaffolding (the `test()` block and setup code) without adding the actual verification step. + +## conditional-test-logic + +**Severity:** info + +Control flow (`if`, `switch`, ternary) inside test functions where assertions may not execute depending on the branch taken. + +**What it catches:** + +```typescript +test('should handle response', () => { + const response = fetchData(); + if (response.ok) { + expect(response.data).toBeDefined(); + } + // if response is not ok, no assertion runs +}); +``` + +**Fix:** Test each path explicitly: + +```typescript +test('should handle successful response', () => { + const response = fetchData({ mockSuccess: true }); + expect(response.data).toBeDefined(); +}); + +test('should handle error response', () => { + const response = fetchData({ mockError: true }); + expect(response.error).toBeDefined(); +}); +``` + +**Why it matters:** Conditional logic in tests means assertions might be skipped entirely. The test passes, but nothing was actually verified. AI agents generate conditional tests to handle multiple scenarios in a single test function. + +## no-error-path-test + +**Severity:** info + +Test files with 3 or more tests but zero tests for error/exception paths. + +**What it catches:** A test file like: + +```typescript +describe('UserService', () => { + test('should create user', () => { /* ... */ }); + test('should update user', () => { /* ... */ }); + test('should delete user', () => { /* ... */ }); + // no tests for: invalid input, not found, permission denied, network error +}); +``` + +**Why it matters:** AI agents generate happy-path tests but rarely test error conditions. Error handling is where most production bugs hide. A test suite without error path coverage gives false confidence. diff --git a/product-docs/src/content/docs/getting-started/installation.mdx b/product-docs/src/content/docs/getting-started/installation.mdx new file mode 100644 index 0000000..99d422c --- /dev/null +++ b/product-docs/src/content/docs/getting-started/installation.mdx @@ -0,0 +1,61 @@ +--- +title: Installation +description: How to install vibecop +--- + +## Requirements + +- **Node.js >= 20** or **Bun >= 1.0** + +## Install Globally + +```bash +# npm +npm install -g vibecop + +# bun (recommended) +bun add -g vibecop +``` + +## Install as Dev Dependency + +```bash +# npm +npm install --save-dev vibecop + +# bun +bun add -d vibecop +``` + +When installed as a dev dependency, use `npx vibecop` to run it. This is the recommended approach for CI pipelines and team projects, since the version is pinned in `package.json`. + +## Use Without Installing + +```bash +npx vibecop scan . +``` + +This downloads and runs vibecop on demand. Useful for one-off scans or trying it out. + +## Verify Installation + +```bash +vibecop --version +``` + +You should see the installed version number. + +## Platform Support + +vibecop uses `@ast-grep/napi` which ships pre-built native binaries for: + +- macOS (x64, arm64) +- Linux (x64, arm64) +- Windows (x64) + +The native binaries are installed automatically as optional dependencies. If your platform is not supported, installation will fail at the native binding step. + +## Next Steps + +- [Run your first scan](/vibecop/getting-started/quick-start/) +- [Set up agent integration](/vibecop/agent-integration/overview/) diff --git a/product-docs/src/content/docs/getting-started/introduction.mdx b/product-docs/src/content/docs/getting-started/introduction.mdx new file mode 100644 index 0000000..26b1fd6 --- /dev/null +++ b/product-docs/src/content/docs/getting-started/introduction.mdx @@ -0,0 +1,67 @@ +--- +title: Introduction +description: What vibecop is and the problem it solves +--- + +## What is vibecop? + +vibecop is an open-source code quality toolkit purpose-built for the AI coding era. It ships 35 detectors that catch the bugs AI agents introduce: god functions, N+1 queries, unsafe shell execution, unpinned LLM models, hallucinated packages, and more. + +Built on [ast-grep](https://ast-grep.github.io/) for fast, tree-sitter-based AST analysis. No LLM required -- every finding is deterministic and reproducible. + +## The Problem + +AI-generated code has specific quality issues that traditional linters miss: + +- **1.7x more issues per PR** than human code (CodeRabbit, 470 PRs) +- **4x maintenance costs** by year 2 for unmanaged AI code +- **19.7% of AI-suggested packages** are hallucinations (USENIX Security 2025) +- **90%+ of AI code issues** are code smells +- OSS maintainers are drowning in AI slop PRs (Curl, Jazzband, Godot, tldraw affected) + +vibecop addresses these gaps: + +1. **AI Slop Defense** -- PR quality gate for OSS maintainers +2. **AI Code Debt Scanner** -- codebase scanner for AI-generated tech debt +3. **AI Test Quality Evaluator** -- meaningful coverage scoring for AI-generated tests + +## Three Pillars + +### 1. Linting + +35 detectors across 4 categories (quality, security, correctness, testing) scan your code for AI-specific antipatterns. Run as a CLI tool or in CI. + +### 2. Agent Integration + +vibecop runs automatically inside your AI coding agent. Every time the agent edits a file, vibecop scans the change and blocks on findings -- the agent reads the output and fixes the issue before proceeding. Supports Claude Code, Cursor, Codex, Aider, GitHub Copilot, Windsurf, and Cline. + +### 3. MCP Server + +For MCP-compatible tools like Continue.dev, Amazon Q, and Zed, vibecop exposes three tools via the Model Context Protocol: `vibecop_scan`, `vibecop_check`, and `vibecop_explain`. + +## Supported Languages + +| Language | Extensions | Detectors | +|----------|-----------|-----------| +| TypeScript | `.ts`, `.tsx` | All 28 | +| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | 24 (excludes TS-specific) | +| Python | `.py` | 14 (correctness, quality, security) | + +Language is auto-detected from file extension. Parser: `@ast-grep/napi` with `@ast-grep/lang-python` for Python support. + +## What vibecop is NOT + +- **Not an AI authorship detector** -- it detects quality patterns, not who wrote the code +- **Not an LLM-based reviewer** -- deterministic, reproducible, free, zero API keys +- **Not a replacement for SonarQube/ESLint** -- it complements them with AI-specific detectors +- **Not a code generation tool** -- it reviews code, it does not write it + +## Try it Online + +**[Playground](https://vibecop-pg.bhvbhushan7.com/)** -- paste code and scan instantly in your browser. + +## Next Steps + +- [Install vibecop](/vibecop/getting-started/installation/) +- [Run your first scan](/vibecop/getting-started/quick-start/) +- [Set up agent integration](/vibecop/agent-integration/overview/) diff --git a/product-docs/src/content/docs/getting-started/quick-start.mdx b/product-docs/src/content/docs/getting-started/quick-start.mdx new file mode 100644 index 0000000..755199c --- /dev/null +++ b/product-docs/src/content/docs/getting-started/quick-start.mdx @@ -0,0 +1,114 @@ +--- +title: Quick Start +description: Run your first vibecop scan in 60 seconds +--- + +## Scan a Directory + +```bash +# Scan current directory +vibecop scan . + +# Scan a specific directory +vibecop scan src/ +``` + +vibecop discovers all `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, and `.py` files, parses them with tree-sitter via ast-grep, runs all 35 detectors, and reports findings. + +## Reading the Output + +The default text output groups findings by file: + +``` +src/services/user.service.ts + 45:1 error Function 'processUserData' is too complex (232 lines, cyclomatic complexity 41, 3 params) god-function + 89:5 warning Database or API call inside a loop — potential N+1 query n-plus-one-query + 145:5 warning Database mutation result is not checked — errors will be silently ignored unchecked-db-result + +src/components/PaymentModal.tsx + 1:1 warning Component has too many hooks (8 useState, 3 useEffect, 593 lines) god-component + 201:9 warning dangerouslySetInnerHTML can lead to XSS attacks if the content is not sanitized dangerous-inner-html + +src/config/auth.ts + 12:5 error Placeholder placeholder domain found: "yourdomain.com" placeholder-in-production + 18:5 error Auth token stored in localStorage — vulnerable to XSS token-in-localstorage + +✖ 7 problems (3 errors, 3 warnings, 1 info) +``` + +Each finding shows: +- **Location** -- `file:line:column` +- **Severity** -- `error`, `warning`, or `info` +- **Message** -- human-readable description of the issue +- **Detector ID** -- machine-readable rule name (e.g., `god-function`) + +## Check a Single File + +```bash +vibecop check src/utils/api.ts +``` + +## JSON Output + +```bash +vibecop scan src/ --format json +``` + +Returns structured JSON with `findings`, `filesScanned`, and `errors` fields. Useful for CI pipelines and programmatic consumption. + +## Scan Only Changed Files + +```bash +# Scan only files changed vs HEAD (git diff) +vibecop scan --diff HEAD + +# Scan files changed vs a branch +vibecop scan --diff main +``` + +This is the most common usage in agent hooks -- scan only the files the agent just changed. + +## CI Mode + +vibecop exits with code 1 if any findings are found, making it suitable for CI gates: + +```bash +vibecop scan . --format text +# Exit code 0 = clean, 1 = findings found, 2 = scan error +``` + +## Output Formats + +| Format | Flag | Use Case | +|--------|------|----------| +| text | `--format text` | Default. Human-readable terminal output | +| json | `--format json` | Programmatic consumption, CI pipelines | +| github | `--format github` | `::error` annotations + `GITHUB_STEP_SUMMARY` | +| sarif | `--format sarif` | GitHub Security tab upload (SARIF 2.1.0) | +| html | `--format html` | Single-file HTML report | +| agent | `--format agent` | AI coding tool hooks -- one finding per line, no color | +| gcc | `--format gcc` | GCC-style output for editor integration | + +## Configuration + +Create `.vibecop.yml` in your project root to customize behavior: + +```yaml +rules: + god-function: + severity: warning + debug-console-in-prod: + severity: "off" + +ignore: + - "**/dist/**" + - "**/vendor/**" +``` + +See [Configuration](/vibecop/configuration/config-file/) for the full reference. + +## Next Steps + +- [Set up agent integration](/vibecop/agent-integration/overview/) to run vibecop automatically +- [Browse all 35 detectors](/vibecop/detectors/overview/) +- [Configure rules and ignores](/vibecop/configuration/config-file/) diff --git a/product-docs/src/content/docs/github-action.mdx b/product-docs/src/content/docs/github-action.mdx new file mode 100644 index 0000000..fcf30b3 --- /dev/null +++ b/product-docs/src/content/docs/github-action.mdx @@ -0,0 +1,182 @@ +--- +title: GitHub Action +description: Use vibecop as a PR gate with inline review comments +--- + +vibecop ships a GitHub Action that scans pull requests and posts inline review comments on changed lines. Use it as a PR quality gate to catch AI-generated code issues before they merge. + +## Quick Setup + +Add to `.github/workflows/vibecop.yml`: + +```yaml +name: vibecop +on: [pull_request] + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bhvbhushan/vibecop@main + with: + on-failure: comment-only + severity-threshold: warning + max-findings: 50 +``` + +## Inputs + +| Input | Description | Default | +|-------|-------------|---------| +| `github-token` | GitHub token for API access | `${{ github.token }}` | +| `config` | Path to `.vibecop.yml` config file | `.vibecop.yml` | +| `on-failure` | Action on findings: `comment-only`, `request-changes`, `label`, `auto-close` | `comment-only` | +| `label` | Label to apply when `on-failure` is `label` | `vibecop:needs-review` | +| `max-findings` | Maximum findings to report (0 = unlimited) | `50` | +| `severity-threshold` | Minimum severity for inline comments: `error`, `warning`, `info` | `warning` | +| `working-directory` | Directory to scan (relative to repo root) | `.` | + +## Outputs + +| Output | Description | +|--------|-------------| +| `findings-count` | Total number of findings | +| `errors-count` | Number of error-severity findings | +| `warnings-count` | Number of warning-severity findings | +| `has-findings` | Whether any findings were detected (`true`/`false`) | +| `scan-time-ms` | Scan duration in milliseconds | + +## on-failure Modes + +### comment-only (default) + +Posts inline review comments on the affected lines. The PR is not blocked -- maintainers review the findings alongside the code. + +```yaml +with: + on-failure: comment-only +``` + +### request-changes + +Posts inline comments and marks the review as "Request Changes". The PR cannot be merged until the review is dismissed or resolved. + +```yaml +with: + on-failure: request-changes +``` + +### label + +Applies a label to the PR. Useful for triaging PRs that need additional review. + +```yaml +with: + on-failure: label + label: "vibecop:needs-review" +``` + +### auto-close + +Closes the PR automatically. Use with caution -- primarily for OSS repositories that want to auto-reject PRs with critical findings. + +```yaml +with: + on-failure: auto-close +``` + +## Severity Threshold + +Control which findings generate inline comments: + +```yaml +with: + severity-threshold: error # only errors + severity-threshold: warning # errors + warnings (default) + severity-threshold: info # everything +``` + +## Configuration + +The GitHub Action reads `.vibecop.yml` from your repository. The `pr-gate` section in the config file provides additional control: + +```yaml +# .vibecop.yml +pr-gate: + on-failure: request-changes + severity-threshold: warning + max-findings: 50 + label: "vibecop:needs-review" +``` + +Action inputs override config file settings when both are specified. + +## How It Works + +1. The action checks out the PR branch +2. It runs `vibecop scan` with `--diff` mode to scan only changed files +3. Findings are filtered to only lines that were modified in the PR +4. Inline review comments are posted on the affected lines +5. A summary comment is posted with the total finding count +6. The action sets outputs and exits + +The diff-only approach keeps scan times fast (typically under 60 seconds) regardless of repository size. + +## Examples + +### Strict Gate for OSS Projects + +```yaml +name: vibecop +on: [pull_request] + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bhvbhushan/vibecop@main + with: + on-failure: request-changes + severity-threshold: warning + max-findings: 100 +``` + +### Soft Gate with Labels + +```yaml +name: vibecop +on: [pull_request] + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bhvbhushan/vibecop@main + id: vibecop + with: + on-failure: label + label: "needs-quality-review" + - name: Report + if: steps.vibecop.outputs.has-findings == 'true' + run: echo "Found ${{ steps.vibecop.outputs.findings-count }} issues" +``` + +### Scan Specific Directory + +```yaml +name: vibecop +on: [pull_request] + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bhvbhushan/vibecop@main + with: + working-directory: src/ + severity-threshold: error +``` diff --git a/product-docs/src/content/docs/index.mdx b/product-docs/src/content/docs/index.mdx new file mode 100644 index 0000000..b2a3b57 --- /dev/null +++ b/product-docs/src/content/docs/index.mdx @@ -0,0 +1,70 @@ +--- +title: vibecop Documentation +description: AI code quality toolkit — deterministic linter for the AI coding era +template: splash +hero: + tagline: 35 detectors catch the bugs AI agents introduce. Deterministic, fast, zero LLM required. + actions: + - text: Get Started + link: /vibecop/getting-started/introduction/ + icon: right-arrow + variant: primary + - text: View on GitHub + link: https://github.com/bhvbhushan/vibecop + icon: external + variant: minimal +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; + +## Why vibecop? + +AI coding agents produce code fast, but they also introduce patterns that traditional linters miss: god functions, N+1 queries, unsafe shell execution, unpinned LLM models, hallucinated packages, and trivial test assertions. vibecop catches these problems deterministically, with zero LLM calls. + + + + Quality, security, correctness, and testing detectors purpose-built for AI-generated code patterns. + + + Runs automatically inside Claude Code, Cursor, Codex, Aider, and 7 other AI tools via `vibecop init`. + + + Available as an MCP server for Continue.dev, Amazon Q, Zed, and any MCP-compatible tool. + + + Built on ast-grep for tree-sitter-based AST analysis. No API keys, no network calls, fully reproducible. + + + +## Quick Example + +```bash +# Install +npm install -g vibecop + +# Scan your project +vibecop scan . + +# Auto-setup for your AI coding tool +vibecop init +``` + +## What It Catches + +``` +src/services/user.service.ts + 45:1 error Function 'processUserData' is too complex (232 lines) god-function + 89:5 warning Database or API call inside a loop — potential N+1 n-plus-one-query + 145:5 warning Database mutation result is not checked unchecked-db-result + +src/config/auth.ts + 12:5 error Placeholder domain found: "yourdomain.com" placeholder-in-production + 18:5 error Auth token stored in localStorage — vulnerable to XSS token-in-localstorage + +src/utils/api.ts + 34:12 warning Double type assertion bypasses TypeScript type safety double-type-assertion +``` + +## Benchmarks + +Median finding density: **established projects 4.4/kLOC vs vibe-coded 14.0/kLOC (3.2x higher)**. Vibe-coded projects consistently trigger more findings per line of code. See the [full benchmark data](/vibecop/benchmarks/). diff --git a/product-docs/src/styles/custom.css b/product-docs/src/styles/custom.css new file mode 100644 index 0000000..f8128fd --- /dev/null +++ b/product-docs/src/styles/custom.css @@ -0,0 +1,6 @@ +:root { + --sl-color-accent-low: #1a1a2e; + --sl-color-accent: #6366f1; + --sl-color-accent-high: #a5b4fc; + --sl-font-system: ui-sans-serif, system-ui, -apple-system, sans-serif; +} diff --git a/product-docs/tsconfig.json b/product-docs/tsconfig.json new file mode 100644 index 0000000..bcbf8b5 --- /dev/null +++ b/product-docs/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} diff --git a/src/cli.ts b/src/cli.ts index c93e74c..39f78ef 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,16 +1,12 @@ #!/usr/bin/env node import { execSync } from "node:child_process"; -import { existsSync, readFileSync } from "node:fs"; -import { extname, relative, resolve } from "node:path"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; import { Command } from "commander"; -import { loadConfig, DEFAULT_CONFIG } from "./config.js"; -import { loadCustomRules } from "./custom-rules.js"; -import { builtinDetectors } from "./detectors/index.js"; -import { EXTENSION_MAP, discoverFiles, pathsToFileInfos, runDetectors } from "./engine.js"; +import { scan, checkFile } from "./engine.js"; import { getFormatter } from "./formatters/index.js"; -import { loadProjectInfo } from "./project.js"; -import type { VibeCopConfig, FileInfo } from "./types.js"; +import type { ScanResult } from "./types.js"; /** Read version from package.json */ function getVersion(): string { @@ -109,67 +105,38 @@ interface CheckOptions { groupBy: string; } +/** Resolve file list for scan: stdin, git diff, or auto-discover */ +async function resolveFiles( + options: ScanOptions, + scanRoot: string, +): Promise { + if (options.stdinFiles) { + return readStdinFiles(); + } + if (options.diff) { + return getGitDiffFiles(options.diff, scanRoot); + } + return undefined; +} + /** Execute the scan command */ async function scanAction( scanPath: string | undefined, options: ScanOptions, ): Promise { const scanRoot = resolve(scanPath ?? "."); - - // Load config - // Commander's --no-config sets options.config to false - let config: VibeCopConfig; - if (options.config === false) { - config = { ...DEFAULT_CONFIG }; - } else { - config = loadConfig(options.config || undefined); - } - - // Load project info - const project = loadProjectInfo(scanRoot); - - // Discover files - let files: FileInfo[]; - - if (options.stdinFiles) { - const stdinPaths = await readStdinFiles(); - files = pathsToFileInfos(stdinPaths, scanRoot); - } else if (options.diff) { - const diffPaths = getGitDiffFiles(options.diff, scanRoot); - files = pathsToFileInfos(diffPaths, scanRoot); - } else { - files = discoverFiles(scanRoot, config); - } - - // Load custom rules and merge with builtins - const customDetectors = loadCustomRules( - resolve(scanRoot, config["custom-rules-dir"] ?? ".vibecop/rules"), - ); - const allDetectors = [...builtinDetectors, ...customDetectors]; - - // Run detectors const maxFindings = Number.parseInt(options.maxFindings, 10); - const result = runDetectors(files, allDetectors, project, config, { - verbose: options.verbose, + const files = await resolveFiles(options, scanRoot); + + const result = await scan({ + scanPath: scanRoot, + config: options.config, maxFindings: Number.isNaN(maxFindings) ? 50 : maxFindings, + verbose: options.verbose, + files, }); - // Format and output - const groupBy = options.groupBy === "rule" ? "rule" : "file"; - let formatter: (r: typeof result) => string; - try { - formatter = getFormatter(options.format, { groupBy }); - } catch (err: unknown) { - process.stderr.write( - `${err instanceof Error ? err.message : String(err)}\n`, - ); - process.exit(2); - } - - writeOutput(formatter(result)); - - // Exit code: 1 if findings, 0 if clean - process.exit(result.findings.length > 0 ? 1 : 0); + formatAndExit(result, options.format, options.groupBy); } /** Execute the check command (single file) */ @@ -177,47 +144,34 @@ function checkAction( filePath: string, options: CheckOptions, ): void { - const absolutePath = resolve(filePath); - if (!existsSync(absolutePath)) { - process.stderr.write(`Error: File not found: ${filePath}\n`); - process.exit(2); - } + const maxFindings = Number.parseInt(options.maxFindings, 10); - const ext = extname(absolutePath); - const language = EXTENSION_MAP[ext]; - if (!language) { + let result: ScanResult; + try { + result = checkFile(filePath, { + verbose: options.verbose, + maxFindings: Number.isNaN(maxFindings) ? 50 : maxFindings, + }); + } catch (err: unknown) { process.stderr.write( - `Error: Unsupported file type: ${ext}. Supported: ${Object.keys(EXTENSION_MAP).join(", ")}\n`, + `Error: ${err instanceof Error ? err.message : String(err)}\n`, ); process.exit(2); } - const scanRoot = resolve("."); - const fileInfo: FileInfo = { - path: relative(scanRoot, absolutePath), - absolutePath, - language, - extension: ext, - }; - - const config = { ...DEFAULT_CONFIG }; - const project = loadProjectInfo(scanRoot); - const maxFindings = Number.parseInt(options.maxFindings, 10); - - const customDetectors = loadCustomRules( - resolve(scanRoot, config["custom-rules-dir"] ?? ".vibecop/rules"), - ); - const allDetectors = [...builtinDetectors, ...customDetectors]; - - const result = runDetectors([fileInfo], allDetectors, project, config, { - verbose: options.verbose, - maxFindings: Number.isNaN(maxFindings) ? 50 : maxFindings, - }); + formatAndExit(result, options.format, options.groupBy); +} - const groupBy = options.groupBy === "rule" ? "rule" : "file"; - let formatter: (r: typeof result) => string; +/** Format scan results and exit with appropriate code */ +function formatAndExit( + result: ScanResult, + format: string, + groupBy: string, +): never { + const groupByMode = groupBy === "rule" ? "rule" : "file"; + let formatter: (r: ScanResult) => string; try { - formatter = getFormatter(options.format, { groupBy }); + formatter = getFormatter(format, { groupBy: groupByMode }); } catch (err: unknown) { process.stderr.write( `${err instanceof Error ? err.message : String(err)}\n`, @@ -288,6 +242,14 @@ function main(): void { await runInit(); }); + program + .command("serve") + .description("Start MCP server (stdio transport)") + .action(async () => { + const { startServer } = await import("./mcp/index.js"); + await startServer(); + }); + program .command("test-rules") .description("Validate custom rules against their inline examples") diff --git a/src/detectors/debug-console-in-prod.ts b/src/detectors/debug-console-in-prod.ts index 81008ff..471e531 100644 --- a/src/detectors/debug-console-in-prod.ts +++ b/src/detectors/debug-console-in-prod.ts @@ -1,17 +1,60 @@ +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; import type { Detector, DetectionContext, Finding } from "../types.js"; import { makeFinding } from "./utils.js"; const TEST_FILE_RE = /(?:[\\/](?:test|tests|__tests__|__test__|spec|__spec__|__mocks__|fixtures|__fixtures__|scripts)[\\/]|\.(?:test|spec|e2e)\.[^.]+$|[\\/]test_[^/\\]+\.py$|[\\/][^/\\]+_test\.py$)/i; -const DEBUG_METHODS = new Set(["log", "debug", "info", "dir", "table", "trace", "group", "groupEnd"]); +/** Files in CLI/server paths are expected to have console output */ +const CLI_SERVER_RE = /(?:(?:^|[\\/])(?:scripts|bin|cli|server|daemon)[\\/]|[\\/](?:cli|server|main|index)\.[^.]+$)/i; + +/** + * Detect if the project is a CLI tool or server by checking for `bin` field + * in the nearest package.json. CLI/server projects legitimately use console.log + * for output — flagging it is noise. + */ +const binProjectCache = new Map(); +function isCliOrServerProject(filePath: string): boolean { + let dir = dirname(filePath); + if (binProjectCache.has(dir)) return binProjectCache.get(dir)!; + + const startDir = dir; + while (dir.length > 1) { + const pkgPath = join(dir, "package.json"); + try { + if (existsSync(pkgPath)) { + const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); + const hasBin = pkg.bin !== undefined && pkg.bin !== null; + binProjectCache.set(startDir, hasBin); + return hasBin; + } + } catch { /* skip */ } + const parent = dirname(dir); + if (parent === dir) break; + dir = parent; + } + binProjectCache.set(startDir, false); + return false; +} + +const DEFAULT_DEBUG_METHODS = new Set(["log", "debug"]); -// Methods that are legitimate in production -const PROD_METHODS = new Set(["error", "warn"]); +/** Resolve which methods to flag: configurable via ctx.config.methods */ +function resolveDebugMethods(ctx: DetectionContext): Set { + const configMethods = ctx.config.methods; + if (Array.isArray(configMethods) && configMethods.length > 0) { + return new Set(configMethods.filter((m): m is string => typeof m === "string")); + } + return DEFAULT_DEBUG_METHODS; +} function detectJavaScript(ctx: DetectionContext): Finding[] { const findings: Finding[] = []; if (TEST_FILE_RE.test(ctx.file.path)) return findings; + if (CLI_SERVER_RE.test(ctx.file.path)) return findings; + if (isCliOrServerProject(ctx.file.absolutePath)) return findings; + const debugMethods = resolveDebugMethods(ctx); const root = ctx.root.root(); const callExprs = root.findAll({ rule: { kind: "call_expression" } }); @@ -26,7 +69,7 @@ function detectJavaScript(ctx: DetectionContext): Finding[] { if (object.text() !== "console") continue; const method = property.text(); - if (!DEBUG_METHODS.has(method)) continue; + if (!debugMethods.has(method)) continue; findings.push(makeFinding( "debug-console-in-prod", diff --git a/src/detectors/placeholder-in-production.ts b/src/detectors/placeholder-in-production.ts index 44b62e6..a2c08ad 100644 --- a/src/detectors/placeholder-in-production.ts +++ b/src/detectors/placeholder-in-production.ts @@ -1,7 +1,7 @@ import type { Detector, DetectionContext, Finding } from "../types.js"; import { makeLineFinding } from "./utils.js"; -const TEST_FILE_RE = /(?:[\\/](?:test|tests|__tests__|__test__|spec|__spec__|__mocks__|fixtures|__fixtures__)[\\/]|\.(?:test|spec|e2e)\.[^.]+$)/i; +const SKIP_FILE_RE = /(?:(?:^|[\\/])(?:test|tests|__tests__|__test__|spec|__spec__|__mocks__|fixtures|__fixtures__|examples|example|samples|sample|mock|mocks|stubs|demo|demos)[\\/]|\.(?:test|spec|e2e|fixture|example|mock|sample)\.[^.]+$|\.example$|\.md$)/i; const PLACEHOLDER_PATTERNS: Array<{ pattern: RegExp; description: string }> = [ { pattern: /["'].*yourdomain\.com.*["']/, description: "placeholder domain" }, @@ -21,7 +21,7 @@ const CONFIG_CONTEXTS = /(?:domain|host|url|endpoint|origin|cookie|cors|config|e function detect(ctx: DetectionContext): Finding[] { const findings: Finding[] = []; - if (TEST_FILE_RE.test(ctx.file.path)) return findings; + if (SKIP_FILE_RE.test(ctx.file.path)) return findings; const lines = ctx.source.split("\n"); diff --git a/src/detectors/undeclared-import.ts b/src/detectors/undeclared-import.ts index f1a6b3b..a4e0d47 100644 --- a/src/detectors/undeclared-import.ts +++ b/src/detectors/undeclared-import.ts @@ -298,9 +298,40 @@ function findPyProjectRoot(fileDir: string, scanRoot: string): string { return result; } +/** Normalize a Python package name: hyphens become underscores */ +function normalizePyName(name: string): string { + return name.replace(/-/g, "_"); +} + +/** Read the [project] name field from pyproject.toml, if present */ +function readPyprojectName(projectRoot: string): string | null { + const tomlPath = join(projectRoot, "pyproject.toml"); + try { + if (!existsSync(tomlPath)) return null; + const raw = readFileSync(tomlPath, "utf-8"); + const lines = raw.split("\n"); + let inProject = false; + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed.startsWith("[")) { + inProject = trimmed === "[project]"; + continue; + } + if (inProject) { + const m = trimmed.match(/^name\s*=\s*"([^"]+)"/); + if (m?.[1]) return m[1]; + } + } + } catch { + // skip + } + return null; +} + /** * Find local Python packages at a given project root. * Checks for directories with __init__.py and standalone .py files. + * Also reads pyproject.toml [project] name and normalizes hyphens. * Results cached per project root. */ function findLocalPyPackages(filePath: string, scanRoot: string): Set { @@ -329,7 +360,17 @@ function findLocalPyPackages(filePath: string, scanRoot: string): Set { // Not readable } - locals.add(basename(projectRoot)); + // Add project root basename (normalized: hyphens -> underscores) + const rootName = basename(projectRoot); + locals.add(rootName); + locals.add(normalizePyName(rootName)); + + // Read pyproject.toml [project] name and add it (normalized) + const pyprojectName = readPyprojectName(projectRoot); + if (pyprojectName) { + locals.add(pyprojectName); + locals.add(normalizePyName(pyprojectName)); + } localPackageCache.set(projectRoot, locals); return locals; @@ -359,8 +400,47 @@ function isPythonImportDeclared(topLevel: string, ctx: DetectionContext, scanRoo return false; } -function isDeclaredJs(packageName: string, ctx: DetectionContext): boolean { - return ctx.project.dependencies.has(packageName) || ctx.project.devDependencies.has(packageName); +/** Cache for project name lookups from package.json */ +const projectNameCache = new Map(); + +/** Read the "name" field from the nearest package.json */ +function getJsProjectName(filePath: string, scanRoot: string): string | null { + let dir = dirname(filePath); + if (projectNameCache.has(dir)) { + return projectNameCache.get(dir)!; + } + + let result: string | null = null; + while (dir.length >= scanRoot.length) { + const pkgPath = join(dir, "package.json"); + try { + if (existsSync(pkgPath)) { + const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); + if (typeof pkg.name === "string" && pkg.name) { + result = pkg.name; + } + break; + } + } catch { + // skip + } + const parent = dirname(dir); + if (parent === dir) break; + dir = parent; + } + + projectNameCache.set(dir, result); + return result; +} + +function isDeclaredJs(packageName: string, ctx: DetectionContext, scanRoot: string): boolean { + if (ctx.project.dependencies.has(packageName) || ctx.project.devDependencies.has(packageName)) { + return true; + } + // A package importing itself is not undeclared + const projectName = getJsProjectName(ctx.file.absolutePath, scanRoot); + if (projectName && packageName === projectName) return true; + return false; } function getScanRoot(ctx: DetectionContext): string { @@ -396,7 +476,7 @@ function detectJavaScriptUndeclaredImports(ctx: DetectionContext): Finding[] { const packageName = extractJsPackageName(specifier); if (!packageName) continue; - if (isDeclaredJs(packageName, ctx)) continue; + if (isDeclaredJs(packageName, ctx, scanRoot)) continue; // Check nearest package.json in monorepo setups const nearestDeps = findNearestJsDependencies(ctx.file.absolutePath, scanRoot); @@ -440,7 +520,7 @@ function detectJavaScriptUndeclaredImports(ctx: DetectionContext): Finding[] { const packageName = extractJsPackageName(specifier); if (!packageName) continue; - if (isDeclaredJs(packageName, ctx)) continue; + if (isDeclaredJs(packageName, ctx, scanRoot)) continue; // Check nearest package.json in monorepo setups const nearestDeps = findNearestJsDependencies(ctx.file.absolutePath, scanRoot); diff --git a/src/engine.ts b/src/engine.ts index 7f730d7..0ec8949 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -2,7 +2,13 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { createRequire } from "node:module"; import { extname, join, relative, resolve } from "node:path"; import { parse, Lang as SgLang, registerDynamicLanguage } from "@ast-grep/napi"; +import { loadConfig, DEFAULT_CONFIG } from "./config.js"; +import { loadCustomRules } from "./custom-rules.js"; +import { builtinDetectors } from "./detectors/index.js"; +import { loadProjectInfo } from "./project.js"; import type { + CheckFileOptions, + EngineScanOptions, VibeCopConfig, DetectionContext, Detector, @@ -582,3 +588,96 @@ export function isTestFile(filePath: string): boolean { function isNodeError(err: unknown): err is NodeJS.ErrnoException { return err instanceof Error && "code" in err; } + +/** + * Load config, resolve detectors (builtins + custom rules). + * Shared setup for scan() and checkFile(). + */ +function loadDetectors( + scanRoot: string, + config: VibeCopConfig, +): Detector[] { + const customDetectors = loadCustomRules( + resolve(scanRoot, config["custom-rules-dir"] ?? ".vibecop/rules"), + ); + return [...builtinDetectors, ...customDetectors]; +} + +/** Resolve config from EngineScanOptions */ +function resolveConfig(options: EngineScanOptions): VibeCopConfig { + if (options.config === false) { + return { ...DEFAULT_CONFIG }; + } + return loadConfig(options.config || undefined); +} + +/** + * Run a full scan and return structured results. + * This is the main orchestration point for both CLI and MCP server. + * + * Does NOT format output or call process.exit(). + */ +export async function scan(options: EngineScanOptions = {}): Promise { + const scanRoot = resolve(options.scanPath ?? "."); + const config = resolveConfig(options); + const project = loadProjectInfo(scanRoot); + + // Discover files + let files: FileInfo[]; + if (options.files) { + files = pathsToFileInfos(options.files, scanRoot); + } else { + files = discoverFiles(scanRoot, config); + } + + const allDetectors = loadDetectors(scanRoot, config); + const maxFindings = options.maxFindings ?? 50; + + return runDetectors(files, allDetectors, project, config, { + verbose: options.verbose, + maxFindings, + }); +} + +/** + * Check a single file and return structured results. + * This is the single-file scanning point for both CLI and MCP server. + * + * Does NOT format output or call process.exit(). + * Throws if the file does not exist or has an unsupported extension. + */ +export function checkFile( + filePath: string, + options: CheckFileOptions = {}, +): ScanResult { + const absolutePath = resolve(filePath); + if (!existsSync(absolutePath)) { + throw new Error(`File not found: ${filePath}`); + } + + const ext = extname(absolutePath); + const language = EXTENSION_MAP[ext]; + if (!language) { + throw new Error( + `Unsupported file type: ${ext}. Supported: ${Object.keys(EXTENSION_MAP).join(", ")}`, + ); + } + + const scanRoot = resolve("."); + const fileInfo: FileInfo = { + path: relative(scanRoot, absolutePath), + absolutePath, + language, + extension: ext, + }; + + const config = { ...DEFAULT_CONFIG }; + const project = loadProjectInfo(scanRoot); + const allDetectors = loadDetectors(scanRoot, config); + const maxFindings = options.maxFindings ?? 50; + + return runDetectors([fileInfo], allDetectors, project, config, { + verbose: options.verbose, + maxFindings, + }); +} diff --git a/src/mcp/index.ts b/src/mcp/index.ts new file mode 100644 index 0000000..a334683 --- /dev/null +++ b/src/mcp/index.ts @@ -0,0 +1,73 @@ +import { readFileSync } from "node:fs"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + scanInputSchema, + handleScan, + checkInputSchema, + handleCheck, + explainInputSchema, + handleExplain, +} from "./server.js"; + +/** Read version from package.json */ +function getVersion(): string { + try { + const pkgPath = new URL("../../package.json", import.meta.url); + const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { + version: string; + }; + return pkg.version; + } catch { + return "0.0.0"; + } +} + +/** Create and configure the MCP server with all vibecop tools */ +export function createServer(): McpServer { + const version = getVersion(); + + const server = new McpServer({ + name: "vibecop", + version, + }); + + server.registerTool("vibecop_scan", { + description: + "Scan a directory for AI code quality issues. Returns findings with file, line, severity, and suggestions.", + inputSchema: scanInputSchema, + }, handleScan); + + server.registerTool("vibecop_check", { + description: "Check a single file for AI code quality issues.", + inputSchema: checkInputSchema, + }, handleCheck); + + server.registerTool("vibecop_explain", { + description: + "Explain what a vibecop detector checks for, its severity, and category.", + inputSchema: explainInputSchema, + }, handleExplain); + + return server; +} + +/** Start the MCP server with stdio transport */ +export async function startServer(): Promise { + const server = createServer(); + const transport = new StdioServerTransport(); + + // Graceful shutdown + process.on("SIGINT", async () => { + await server.close(); + process.exit(0); + }); + process.on("SIGTERM", async () => { + await server.close(); + process.exit(0); + }); + + await server.connect(transport); + // Log to stderr so it doesn't interfere with the MCP protocol on stdout + console.error("vibecop MCP server running on stdio"); +} diff --git a/src/mcp/server.ts b/src/mcp/server.ts new file mode 100644 index 0000000..9e054f6 --- /dev/null +++ b/src/mcp/server.ts @@ -0,0 +1,119 @@ +import { z } from "zod"; +import { scan, checkFile } from "../engine.js"; +import { builtinDetectors } from "../detectors/index.js"; +import type { ScanResult } from "../types.js"; + +/** Format a ScanResult as a JSON text content block for MCP */ +function formatScanResult(result: ScanResult) { + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { + findings: result.findings, + filesScanned: result.filesScanned, + errors: result.errors, + }, + null, + 2, + ), + }, + ], + }; +} + +/** Input schema for vibecop_scan */ +export const scanInputSchema = { + path: z.string().optional().describe("Directory to scan. Defaults to current working directory."), + maxFindings: z.number().optional().describe("Maximum findings to return. Default 50."), +}; + +/** Handler for vibecop_scan tool */ +export async function handleScan(args: { + path?: string; + maxFindings?: number; +}) { + try { + const result = await scan({ + scanPath: args.path, + maxFindings: args.maxFindings ?? 50, + }); + return formatScanResult(result); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + return { + content: [{ type: "text" as const, text: `Error scanning: ${message}` }], + isError: true, + }; + } +} + +/** Input schema for vibecop_check */ +export const checkInputSchema = { + file_path: z.string().describe("Absolute or relative path to the file to check."), + maxFindings: z.number().optional().describe("Maximum findings to return. Default 50."), +}; + +/** Handler for vibecop_check tool */ +export async function handleCheck(args: { + file_path: string; + maxFindings?: number; +}) { + try { + const result = checkFile(args.file_path, { + maxFindings: args.maxFindings ?? 50, + }); + return formatScanResult(result); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + return { + content: [{ type: "text" as const, text: `Error checking file: ${message}` }], + isError: true, + }; + } +} + +/** Input schema for vibecop_explain */ +export const explainInputSchema = { + detector_id: z.string().describe('The detector ID (e.g., "unsafe-shell-exec", "god-function").'), +}; + +/** Handler for vibecop_explain tool */ +export async function handleExplain(args: { detector_id: string }) { + const detector = builtinDetectors.find((d) => d.id === args.detector_id); + + if (!detector) { + const availableIds = builtinDetectors.map((d) => d.id).sort(); + return { + content: [ + { + type: "text" as const, + text: `Unknown detector: "${args.detector_id}". Available detectors:\n${availableIds.map((id) => ` - ${id}`).join("\n")}`, + }, + ], + isError: true, + }; + } + + const { meta } = detector; + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { + id: detector.id, + name: meta.name, + description: meta.description, + severity: meta.severity, + category: meta.category, + languages: meta.languages, + }, + null, + 2, + ), + }, + ], + }; +} diff --git a/src/types.ts b/src/types.ts index d1591e5..e9315af 100644 --- a/src/types.ts +++ b/src/types.ts @@ -102,3 +102,25 @@ export interface TimingInfo { totalMs: number; perDetector: Record; } + +/** Options for the scan() engine function */ +export interface EngineScanOptions { + /** Directory to scan (defaults to ".") */ + scanPath?: string; + /** Path to config file, or false to disable config loading */ + config?: string | false; + /** Maximum number of findings to report (default 50, 0 = unlimited) */ + maxFindings?: number; + /** Show timing information */ + verbose?: boolean; + /** Explicit file paths to scan (skips file discovery when provided) */ + files?: string[]; +} + +/** Options for the checkFile() engine function */ +export interface CheckFileOptions { + /** Maximum number of findings to report (default 50, 0 = unlimited) */ + maxFindings?: number; + /** Show timing information */ + verbose?: boolean; +} diff --git a/test/detectors/debug-console-in-prod.test.ts b/test/detectors/debug-console-in-prod.test.ts index 404cd97..9954852 100644 --- a/test/detectors/debug-console-in-prod.test.ts +++ b/test/detectors/debug-console-in-prod.test.ts @@ -29,6 +29,7 @@ function makeCtx( options: { language?: "typescript" | "javascript" | "python"; filePath?: string; + config?: Record; } = {}, ): DetectionContext { const language = options.language ?? "typescript"; @@ -51,7 +52,7 @@ function makeCtx( language, extension: extMap[language], }; - return { file, root, source, project: EMPTY_PROJECT, config: {} }; + return { file, root, source, project: EMPTY_PROJECT, config: options.config ?? {} }; } describe("debug-console-in-prod", () => { @@ -108,18 +109,16 @@ describe("debug-console-in-prod", () => { expect(findings.length).toBe(0); }); - test("detects console.dir in production", () => { + test("does NOT flag console.dir by default (diagnostic tool)", () => { const ctx = makeCtx(`console.dir(myObject);`); const findings = debugConsoleInProd.detect(ctx); - expect(findings.length).toBe(1); - expect(findings[0].message).toContain("console.dir"); + expect(findings.length).toBe(0); }); - test("detects console.table in production", () => { + test("does NOT flag console.table by default (diagnostic tool)", () => { const ctx = makeCtx(`console.table(data);`); const findings = debugConsoleInProd.detect(ctx); - expect(findings.length).toBe(1); - expect(findings[0].message).toContain("console.table"); + expect(findings.length).toBe(0); }); test("detects multiple console calls", () => { @@ -131,6 +130,116 @@ describe("debug-console-in-prod", () => { const findings = debugConsoleInProd.detect(ctx); expect(findings.length).toBe(2); }); + + test("does NOT flag console.info by default (structured logging)", () => { + const ctx = makeCtx(`console.info("server started on port 3000");`); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.trace by default", () => { + const ctx = makeCtx(`console.trace("call stack");`); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.group/groupEnd by default", () => { + const ctx = makeCtx(`console.group("section");\nconsole.groupEnd();`); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + describe("CLI/server path skipping", () => { + test("does NOT flag console.log in bin/ directory", () => { + const ctx = makeCtx(`console.log("starting...");`, { + filePath: "bin/run.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in cli/ directory", () => { + const ctx = makeCtx(`console.log("usage: ...");`, { + filePath: "src/cli/main.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in server/ directory", () => { + const ctx = makeCtx(`console.log("listening on port 8080");`, { + filePath: "src/server/app.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in cli.ts entry point", () => { + const ctx = makeCtx(`console.log("version 1.0.0");`, { + filePath: "src/cli.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in server.js entry point", () => { + const ctx = makeCtx(`console.log("server ready");`, { + filePath: "src/server.js", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in index.ts entry point", () => { + const ctx = makeCtx(`console.log("init");`, { + filePath: "src/index.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag console.log in daemon/ directory", () => { + const ctx = makeCtx(`console.log("daemon started");`, { + filePath: "src/daemon/worker.ts", + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + }); + + describe("configurable methods", () => { + test("flags console.info when configured via methods", () => { + const ctx = makeCtx(`console.info("info message");`, { + config: { methods: ["log", "debug", "info"] }, + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(1); + expect(findings[0].message).toContain("console.info"); + }); + + test("flags console.dir when configured via methods", () => { + const ctx = makeCtx(`console.dir({ a: 1 });`, { + config: { methods: ["dir"] }, + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(1); + expect(findings[0].message).toContain("console.dir"); + }); + + test("does NOT flag console.log when not in configured methods", () => { + const ctx = makeCtx(`console.log("test");`, { + config: { methods: ["debug"] }, + }); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("uses defaults when methods config is empty", () => { + const ctx = makeCtx(`console.log("test");\nconsole.debug("d");`); + const findings = debugConsoleInProd.detect(ctx); + expect(findings.length).toBe(2); + }); + }); }); describe("Python", () => { diff --git a/test/detectors/placeholder-in-production.test.ts b/test/detectors/placeholder-in-production.test.ts index 8bc2ec9..6858592 100644 --- a/test/detectors/placeholder-in-production.test.ts +++ b/test/detectors/placeholder-in-production.test.ts @@ -132,4 +132,123 @@ describe("placeholder-in-production", () => { expect(findings.length).toBe(1); }); }); + + describe("fixture/example/mock directory skipping", () => { + test("does NOT flag placeholder in fixtures/ directory", () => { + const ctx = makeCtx( + `const config = { domain: "yourdomain.com" };`, + { filePath: "test/fixtures/config-data.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in __fixtures__/ directory", () => { + const ctx = makeCtx( + `const config = { domain: "yourdomain.com" };`, + { filePath: "src/__fixtures__/mock-config.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in examples/ directory", () => { + const ctx = makeCtx( + `const config = { url: "https://example.com/api" };`, + { filePath: "examples/basic-setup.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in example/ directory", () => { + const ctx = makeCtx( + `const config = { key: "your_api_key" };`, + { filePath: "example/config.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in samples/ directory", () => { + const ctx = makeCtx( + `const config = { password: "changeme" };`, + { filePath: "samples/auth-config.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in mocks/ directory", () => { + const ctx = makeCtx( + `const config = { token: "your_token" };`, + { filePath: "src/mocks/api-responses.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in stubs/ directory", () => { + const ctx = makeCtx( + `const config = { domain: "yourdomain.com" };`, + { filePath: "test/stubs/config.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in demo/ directory", () => { + const ctx = makeCtx( + `const config = { password: "changeme" };`, + { filePath: "demo/setup.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in .fixture.ts file", () => { + const ctx = makeCtx( + `const config = { domain: "yourdomain.com" };`, + { filePath: "src/config.fixture.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in .example.ts file", () => { + const ctx = makeCtx( + `const config = { key: "your_api_key" };`, + { filePath: "src/config.example.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in .example file", () => { + const ctx = makeCtx( + `SECRET_KEY="changeme"`, + { filePath: ".env.example" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("does NOT flag placeholder in .md file", () => { + const ctx = makeCtx( + `Set your url to "https://example.com/api"`, + { filePath: "docs/setup.md" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("STILL flags placeholder in regular src/ file", () => { + const ctx = makeCtx( + `const config = { domain: "yourdomain.com" };`, + { filePath: "src/config.ts" }, + ); + const findings = placeholderInProduction.detect(ctx); + expect(findings.length).toBe(1); + }); + }); }); diff --git a/test/detectors/undeclared-import.test.ts b/test/detectors/undeclared-import.test.ts index 9ad3300..868a1e1 100644 --- a/test/detectors/undeclared-import.test.ts +++ b/test/detectors/undeclared-import.test.ts @@ -1,6 +1,9 @@ import { describe, expect, test } from "bun:test"; import { parse, Lang, registerDynamicLanguage } from "@ast-grep/napi"; import { createRequire } from "node:module"; +import { mkdirSync, writeFileSync, mkdtempSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; import { undeclaredImport } from "../../src/detectors/undeclared-import.js"; import type { DetectionContext, FileInfo, ProjectInfo } from "../../src/types.js"; @@ -46,6 +49,7 @@ function makeCtx( source: string, project: ProjectInfo, language: "typescript" | "javascript" | "python" = "typescript", + options?: { filePath?: string; absolutePath?: string }, ): DetectionContext { const langMap: Record = { typescript: Lang.TypeScript, @@ -57,10 +61,11 @@ function makeCtx( javascript: ".js", python: ".py", }; + const defaultRelPath = `src/app.${extMap[language].slice(1)}`; const root = parse(langMap[language] as Lang, source); const file: FileInfo = { - path: `src/app.${extMap[language].slice(1)}`, - absolutePath: `/src/app.${extMap[language].slice(1)}`, + path: options?.filePath ?? defaultRelPath, + absolutePath: options?.absolutePath ?? `/${options?.filePath ?? defaultRelPath}`, language, extension: extMap[language], }; @@ -281,4 +286,95 @@ describe("undeclared-import", () => { expect(findings.length).toBe(0); }); }); + + describe("self-referencing package imports", () => { + test("JS/TS: does NOT flag import of own package name", () => { + // Create a temp directory with a package.json + const tmp = mkdtempSync(join(tmpdir(), "vibecop-test-")); + const srcDir = join(tmp, "src"); + mkdirSync(srcDir, { recursive: true }); + writeFileSync(join(tmp, "package.json"), JSON.stringify({ + name: "my-cool-package", + dependencies: {}, + })); + + const project: ProjectInfo = { + dependencies: new Set(), + devDependencies: new Set(), + manifests: ["package.json"], + }; + + const source = `import { helper } from 'my-cool-package';`; + const root = parse(Lang.TypeScript, source); + const file: FileInfo = { + path: "src/utils.ts", + absolutePath: join(tmp, "src/utils.ts"), + language: "typescript", + extension: ".ts", + }; + const ctx: DetectionContext = { file, root, source, project, config: {} }; + const findings = undeclaredImport.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("JS/TS: does NOT flag require() of own package name", () => { + const tmp = mkdtempSync(join(tmpdir(), "vibecop-test-")); + const srcDir = join(tmp, "src"); + mkdirSync(srcDir, { recursive: true }); + writeFileSync(join(tmp, "package.json"), JSON.stringify({ + name: "@my-org/my-lib", + dependencies: {}, + })); + + const project: ProjectInfo = { + dependencies: new Set(), + devDependencies: new Set(), + manifests: ["package.json"], + }; + + const source = `const lib = require('@my-org/my-lib');`; + const root = parse(Lang.TypeScript, source); + const file: FileInfo = { + path: "src/index.ts", + absolutePath: join(tmp, "src/index.ts"), + language: "typescript", + extension: ".ts", + }; + const ctx: DetectionContext = { file, root, source, project, config: {} }; + const findings = undeclaredImport.detect(ctx); + expect(findings.length).toBe(0); + }); + + test("Python: does NOT flag import of own package with hyphens", () => { + // Create a temp directory simulating mcp-atlassian project + const tmp = mkdtempSync(join(tmpdir(), "vibecop-test-")); + const srcDir = join(tmp, "src"); + const pkgDir = join(tmp, "src", "mcp_atlassian"); + mkdirSync(pkgDir, { recursive: true }); + writeFileSync(join(pkgDir, "__init__.py"), ""); + writeFileSync(join(tmp, "pyproject.toml"), [ + "[project]", + 'name = "mcp-atlassian"', + "dependencies = []", + ].join("\n")); + + const project: ProjectInfo = { + dependencies: new Set(), + devDependencies: new Set(), + manifests: ["pyproject.toml"], + }; + + const source = `import mcp_atlassian\n`; + const root = parse("python" as Lang, source); + const file: FileInfo = { + path: "src/app.py", + absolutePath: join(tmp, "src/app.py"), + language: "python", + extension: ".py", + }; + const ctx: DetectionContext = { file, root, source, project, config: {} }; + const findings = undeclaredImport.detect(ctx); + expect(findings.length).toBe(0); + }); + }); }); diff --git a/test/mcp/server.test.ts b/test/mcp/server.test.ts new file mode 100644 index 0000000..113b1f6 --- /dev/null +++ b/test/mcp/server.test.ts @@ -0,0 +1,159 @@ +import { describe, expect, test } from "bun:test"; +import { join } from "node:path"; +import { + handleScan, + handleCheck, + handleExplain, +} from "../../src/mcp/server.js"; +import { builtinDetectors } from "../../src/detectors/index.js"; + +const FIXTURES_DIR = join(import.meta.dir, "..", "fixtures", "benchmark", "vibe-coded-1"); +const FIXTURE_FILE = join(FIXTURES_DIR, "app.ts"); + +describe("vibecop_scan", () => { + test("returns findings for a directory with issues", async () => { + const result = await handleScan({ path: FIXTURES_DIR, maxFindings: 10 }); + + expect(result.isError).toBeUndefined(); + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe("text"); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.findings).toBeInstanceOf(Array); + expect(parsed.findings.length).toBeGreaterThan(0); + expect(parsed.filesScanned).toBeGreaterThan(0); + + // Each finding has expected shape + const finding = parsed.findings[0]; + expect(finding).toHaveProperty("detectorId"); + expect(finding).toHaveProperty("message"); + expect(finding).toHaveProperty("severity"); + expect(finding).toHaveProperty("file"); + expect(finding).toHaveProperty("line"); + }); + + test("respects maxFindings limit", async () => { + const result = await handleScan({ path: FIXTURES_DIR, maxFindings: 2 }); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.findings.length).toBeLessThanOrEqual(2); + }); + + test("returns error for invalid path", async () => { + const result = await handleScan({ path: "/nonexistent/path/that/does/not/exist" }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain("Error scanning"); + }); + + test("defaults to cwd when no path specified", async () => { + // Should not throw - scans current working directory + const result = await handleScan({}); + expect(result.content).toHaveLength(1); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed).toHaveProperty("findings"); + expect(parsed).toHaveProperty("filesScanned"); + }); + + test("returns empty findings for a directory with no issues", async () => { + // Scan an empty/clean fixtures directory + const emptyDir = join(import.meta.dir, "..", "fixtures", "engine", "ignored"); + const result = await handleScan({ path: emptyDir, maxFindings: 50 }); + + // Should not error + expect(result.isError).toBeUndefined(); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.findings).toBeInstanceOf(Array); + expect(parsed.findings.length).toBe(0); + }); +}); + +describe("vibecop_check", () => { + test("returns findings for a file with issues", async () => { + const result = await handleCheck({ file_path: FIXTURE_FILE }); + + expect(result.isError).toBeUndefined(); + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe("text"); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.findings).toBeInstanceOf(Array); + expect(parsed.findings.length).toBeGreaterThan(0); + expect(parsed.filesScanned).toBe(1); + }); + + test("respects maxFindings limit", async () => { + const result = await handleCheck({ file_path: FIXTURE_FILE, maxFindings: 1 }); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.findings.length).toBeLessThanOrEqual(1); + }); + + test("returns error for non-existent file", async () => { + const result = await handleCheck({ file_path: "/nonexistent/file.ts" }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain("Error checking file"); + expect(result.content[0].text).toContain("File not found"); + }); + + test("returns error for unsupported file type", async () => { + // package.json is not a supported file type + const result = await handleCheck({ + file_path: join(import.meta.dir, "..", "..", "package.json"), + }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain("Unsupported file type"); + }); +}); + +describe("vibecop_explain", () => { + test("returns metadata for a valid detector ID", async () => { + const result = await handleExplain({ detector_id: "god-function" }); + + expect(result.isError).toBeUndefined(); + expect(result.content).toHaveLength(1); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.id).toBe("god-function"); + expect(parsed).toHaveProperty("name"); + expect(parsed).toHaveProperty("description"); + expect(parsed).toHaveProperty("severity"); + expect(parsed).toHaveProperty("category"); + expect(parsed).toHaveProperty("languages"); + expect(parsed.languages).toBeInstanceOf(Array); + }); + + test("returns error with available IDs for unknown detector", async () => { + const result = await handleExplain({ detector_id: "nonexistent-detector" }); + + expect(result.isError).toBe(true); + const text = result.content[0].text; + expect(text).toContain('Unknown detector: "nonexistent-detector"'); + expect(text).toContain("Available detectors:"); + + // Should list at least some real detector IDs + expect(text).toContain("god-function"); + expect(text).toContain("unsafe-shell-exec"); + }); + + test("returns correct metadata fields for unsafe-shell-exec", async () => { + const result = await handleExplain({ detector_id: "unsafe-shell-exec" }); + + const parsed = JSON.parse(result.content[0].text); + expect(parsed.id).toBe("unsafe-shell-exec"); + expect(parsed.category).toBe("security"); + expect(parsed.severity).toBe("error"); + expect(parsed.languages).toContain("typescript"); + }); + + test("lists all available detectors in error message", async () => { + const result = await handleExplain({ detector_id: "no-such-id" }); + + const text = result.content[0].text; + // Every builtin detector ID should appear in the error message + for (const detector of builtinDetectors) { + expect(text).toContain(detector.id); + } + }); +});