diff --git a/.claude/skills/extending-catalog/SKILL.md b/.claude/skills/extending-catalog/SKILL.md new file mode 100644 index 0000000..31b2bfc --- /dev/null +++ b/.claude/skills/extending-catalog/SKILL.md @@ -0,0 +1,41 @@ +--- +name: extending-catalog +description: Extends the ADE catalog with new facets, options, or practices. Use when adding a new architecture (e.g. Next.js), a new practice (e.g. Trunk-Based Development), or a new facet to the catalog. +--- + +# Extending the ADE catalog + +Catalog: `packages/core/src/catalog/`. Facets: one file each under `facets/`. Types: `packages/core/src/types.ts`. + +## Adding an option + +Single-file change. Append an `Option` to the facet's `options` array. Read the existing facet file for the shape — follow the pattern already there. + +- Architecture options go in `facets/architecture.ts` (single-select) +- Practice options go in `facets/practices.ts` (multi-select, stack-independent) +- Each option's `recipe` uses existing writers: `skills`, `workflows`, `instruction`, `mcp-server`, `knowledge`, `installable` +- Inline skill bodies should be concise — only add context Claude doesn't already have +- For third-party skills use `ExternalSkill`: `{ name, source }` instead of `{ name, description, body }` +- `docsets` are optional — only add repos with genuinely useful reference docs + +## Adding a facet + +1. Create `facets/.ts` — export a `Facet` object. Read an existing facet for the shape +2. Register it in `packages/core/src/catalog/index.ts` — add to the `facets` array +3. The CLI auto-discovers facets from the array — no UI changes needed + +Key decisions: `required` (must every project choose?), `multiSelect` (composable or mutually exclusive?), `dependsOn` (resolved after which facets?). + +## Adding a provision writer + +Expensive — touches types, registry, resolver, and every harness writer. Avoid unless existing writers cannot express the output. + +1. Extend `ProvisionWriter` union in `types.ts` +2. Implement `ProvisionWriterDef` with `write(config, context) → Partial` +3. Register in `WriterRegistry` +4. If needed, extend `LogicalConfig` and update merge logic in `resolver.ts` +5. Update every harness writer in `packages/harnesses/src/writers/` + +## Resolution flow + +`UserConfig.choices` → `resolve()` iterates facets → matches options → runs each provision's writer → merges into `LogicalConfig` → harness writers emit agent-specific files. See `packages/core/src/resolver.ts`. diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..1e6e3c6 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,38 @@ +name: PR + +on: + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: "22" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Check formatting + run: pnpm run format:check:all + + - name: Run linting + run: pnpm run lint:all + + - name: Build project + run: pnpm run build + + - name: Run tests + run: pnpm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cfa826a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,124 @@ +name: Release and Publish + +on: + push: + branches: [main] + +permissions: + contents: write + packages: write + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: "22" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Check formatting + run: pnpm run format:check:all + + - name: Run linting + run: pnpm run lint:all + + - name: Build project + run: pnpm run build + + - name: Run tests + run: pnpm test + + release: + needs: test + runs-on: ubuntu-latest + steps: + - name: Generate GitHub App Token + id: generate_token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ vars.VERSION_BUMPER_APPID }} + private_key: ${{ secrets.VERSION_BUMPER_SECRET }} + + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ steps.generate_token.outputs.token }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: "22" + cache: "pnpm" + registry-url: "https://registry.npmjs.org" + + - name: Install dependencies + run: pnpm install + + - name: Build project + run: pnpm run build + + - name: Bump version and create tag + id: version + uses: mathieudutour/github-tag-action@v6.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + default_bump: patch + tag_prefix: v + + - name: Update package.json version + if: steps.version.outputs.new_tag + run: | + NEW_VERSION=${{ steps.version.outputs.new_version }} + + # Update root package.json + pnpm version $NEW_VERSION --no-git-tag-version + + # Update all workspace package versions + pnpm -r exec pnpm version $NEW_VERSION --no-git-tag-version + + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add package.json packages/*/package.json pnpm-lock.yaml + git commit -m "chore: bump version to $NEW_VERSION [skip ci]" || exit 0 + git push + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + + - name: Create GitHub Release + if: steps.version.outputs.new_tag + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.version.outputs.new_tag }} + name: Release ${{ steps.version.outputs.new_tag }} + body: ${{ steps.version.outputs.changelog }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to npm + if: steps.version.outputs.new_tag + run: | + # Publish all packages in workspace + # pnpm -r publishes packages in topological order (dependencies first) + # Root package is private and will be skipped automatically + pnpm -r publish --no-git-checks + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de62c3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Generated files +node_modules +dist +.DS_Store +.crowd + +# Typescript +*.tsbuildinfo +*.d.ts + +# Turborepo +.turbo + +# Jest +coverage +.coverage + +# Env files +.env* +!.env.example + +# vitepress +docs/.vitepress/cache \ No newline at end of file diff --git a/.husky/post-checkout b/.husky/post-checkout new file mode 100755 index 0000000..e6f0881 --- /dev/null +++ b/.husky/post-checkout @@ -0,0 +1,2 @@ +# Automate and ensure dependencies are installed/synced with the branch's codebase +pnpm install \ No newline at end of file diff --git a/.husky/post-merge b/.husky/post-merge new file mode 100755 index 0000000..e6f0881 --- /dev/null +++ b/.husky/post-merge @@ -0,0 +1,2 @@ +# Automate and ensure dependencies are installed/synced with the branch's codebase +pnpm install \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..e3416cf --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +# Ensure files are linted before commit +pnpm lint-staged \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..f957683 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,8 @@ +# Ensure Typescript files have no errors before pushing +# Ensure there is no linting or formatting errors before pushing +pnpm run lint:all +pnpm run format:check:all +pnpm run typecheck:all + +# Run unit tests +pnpm test diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 0000000..3a8942d --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,4 @@ +export default { + "*": "prettier --write --ignore-unknown", + "*.js,*.ts": "eslint --fix" +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..5fa4d5d --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,3 @@ +tabWidth: 2 +trailingComma: "none" +useTabs: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b216df2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Luke Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 2054d90..62dfb25 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ADE — Agentic Development Environment -> A technology-agnostic information architecture for coding agents that enables -> consistent, professional-grade agentic engineering at team scale. +> A structured information architecture for harness engineering — organizing what +> coding agents know into composable, team-shared layers. ## The alignment problem @@ -63,7 +63,12 @@ flowchart TD end subgraph Conventions ["Conventions · Skills"] - S["Project-specific standards
selected per team or project

Provided on demand"] + subgraph Architecture ["Architecture (single-select)"] + A["Stack & framework conventions
shapes project structure, patterns, libraries

e. g. TanStack, Next.js, FastAPI"] + end + subgraph Practices ["Practices (multi-select)"] + PR["Composable development disciplines
mix and match regardless of stack

e. g. TDD, ADR, Conventional Commits"] + end end subgraph Documentation ["Documentation · Text files"] @@ -71,21 +76,31 @@ flowchart TD end Process -->|"invokes — e. g. "use your design skill""| Conventions - Conventions -->|"points to — e. g. "check .docs/tanstack""| Documentation + Architecture -->|"points to — e. g. "check .docs/tanstack""| Documentation + Practices -->|"points to — e. g. "check docs/adr/""| Documentation ``` ### Why this is needed There is no secret ingredient to interacting with coding agents. We just have to -instruct them properly. But this "properly" can be achieve in many ways. -Where do we put write down this steering? In the `AGENTS.md`? In Skills? Move it to a prompt, -potentially exposed by an MCP server? And how do we share it across team mates? -It has become good practice to check this into the repo, but honestly: +instruct them properly. The emerging practice of +[harness engineering](https://www.humanlayer.dev/blog/skill-issue-harness-engineering-for-coding-agents) +— leveraging agent configuration points to improve output quality and +reliability — has shown that most agent failures are configuration problems, not +capability problems. But _how_ do you configure well? + +Where do you write down the steering? In the `AGENTS.md`? In skills? Move it to +a prompt, potentially exposed by an MCP server? And how do you share it across +team mates? It has become good practice to check this into the repo, but +honestly: Most `AGENTS.md` files are snowflakes: ad-hoc, project-specific, unstructured. They mix process instructions with coding conventions and documentation fragments in a single flat file. Rule files and skills improve reusability but still lack a -coherent taxonomy. +coherent taxonomy. The ETH Zurich study on agentfiles confirmed what +practitioners already knew: LLM-generated ones hurt performance, bloated ones +waste instruction budget, and codebase overviews add nothing — agents discover +repository structure on their own. ADE brings structure to this space. By separating the three layers explicitly and binding each to a specific artifact type, it makes information easier to find, @@ -105,24 +120,40 @@ direction. Process invokes conventions. Conventions point to documentation. > _"When in the plan phase, use your `design` skill."_ The agent system prompt defines and enforces the workflow. At the right step, it -delegates to a skill that encodes the team"s specific approach — keeping process +delegates to a skill that encodes the team's specific approach — keeping process universal and conventions local. -**Skills reference documentation on demand:** +**Conventions come in two flavours — architecture and practices:** + +The conventions layer is split into two complementary sub-categories that reflect +how teams actually think about project-level decisions: + +- **Architecture** (single-select) — Stack and framework conventions that shape + your project structure, patterns, and libraries. You pick one architecture + (e.g. TanStack, Next.js, FastAPI) and it constrains everything downstream. + + > _"We are using React with TanStack Query for backend interactions. Check_ + > _`.docs/tanstack` when implementing data fetching."_ + +- **Practices** (multi-select) — Composable development disciplines that apply + regardless of your stack. You can combine TDD, ADR, and Conventional Commits + freely — they are orthogonal to each other and to your architecture choice. -> _"We are using React with TanStack Query for backend interactions. Check_ -> _`.docs/tanstack` when implementing data fetching. Check `.docs/components` for_ -> _details on available reusable components."_ + > _"Use London-style TDD. Follow the Red-Green-Refactor cycle. Write ADRs for_ + > _significant decisions."_ + +**Skills reference documentation on demand:** The skill encodes the convention — which libraries, which patterns. It surfaces the exact documentation needed at the moment it is relevant, rather than loading everything upfront. This composability is what makes ADE scale. Process is written once and shared -across every project. Skills are curated per team or per project context — ADE -provides a mechanism to select and compose skill sets, so the right conventions -are available for the right context. Documentation lives where it belongs — in -the codebase — and is surfaced precisely when needed. +across every project. Convention skills — both architecture and practices — are +curated per team or per project context. ADE provides a mechanism to select and +compose them, so the right conventions are available for the right context. +Documentation lives where it belongs — in the codebase — and is surfaced +precisely when needed. **Sounds intuitive?** Hopefully, it does. Because **this framework only works, if you as human join the team**. @@ -145,10 +176,20 @@ capture technology choices, architectural patterns, and design decisions in a fo the agent applies on demand. ADE provides a mechanism to select and share **skill sets** — curated collections -of skills that match a team"s context. A frontend team, a backend team, and a -platform team each activate the skill set appropriate to their work. Skills can -be shared across projects, versioned, and evolved independently of the process -layer. +of skills that match a team's context. Within the conventions layer, skills are +organized into two sub-categories: + +- **Architecture** (single-select) — Stack and framework conventions that shape + your project structure. You pick one (e.g. TanStack, Next.js, FastAPI) and it + constrains patterns, libraries, and project organization. + +- **Practices** (multi-select) — Composable development disciplines that apply + regardless of your stack. TDD, ADR, Conventional Commits — mix and match + freely. + +A frontend team, a backend team, and a platform team each activate the +combination appropriate to their work. Skills can be shared across projects, +versioned, and evolved independently of the process layer. ### Documentation sharing @@ -162,14 +203,53 @@ reference knowledge. ### Coding agent agnostic setup tooling -ADE will :soon: include a simple cli to setup your coding agent. All configuration -can be placed into your repo, so that you can check it in. +ADE includes a CLI (`ade setup`) that generates the correct configuration for +whichever coding agent you use. All configuration is placed into your repo so +you can check it in. + +We use STDIO-based MCP servers to expose process guidance, conventions, and docs +to coding agents. By using the Model Context Protocol — optimized for +discoverability — you get a consistent experience regardless of your agent. -We're using STDIO based MCP-servers to expose process guidance, conventions and docs -to coding agents. There are other proprietary ways to do this, but by using the -well-established Model Context Protocol which is optimized for discoverability, we -make sure that you get a similar experience, no matter whether you are using Claude -Code, Copilot or Kiro. +The CLI supports a growing list of agents. See the +[harness writers source](packages/harnesses/src/writers) for the current set. + +## Where ADE fits in harness engineering + +A coding agent's harness has many configuration levers. ADE addresses the +**information levers** — the ones that determine _what the agent knows_: + +| Harness lever | ADE layer | Artifact | +| ---------------------------- | ----------------- | --------------------------------- | +| System prompt / agentfile | **Process** | `AGENTS.md` | +| Skills / instruction modules | **Conventions** | Skills (architecture + practices) | +| Reference knowledge | **Documentation** | Text files, read on demand | + +Practitioners have identified additional **runtime levers** that complement the +information architecture: + +- **Sub-agents** — context firewalls that encapsulate discrete tasks in isolated + context windows, preventing intermediate noise from accumulating in the parent + thread. This keeps the orchestrating agent in the "smart zone" and enables + coherent work across many sessions. + +- **Hooks** — user-defined scripts triggered at lifecycle events (tool calls, + stop events) that add deterministic control flow: auto-approving or denying + dangerous commands, surfacing build errors before the agent finishes, or + notifying the team on completion. + +- **Back-pressure** — verification mechanisms (typechecks, tests, coverage + gates) that let the agent check its own work. The likelihood of successfully + solving a problem with a coding agent is strongly correlated with the agent's + ability to verify its output. Context-efficient verification — where success is + silent and only failures surface — keeps the context window clean. + +ADE focuses on the information side because that is where most teams struggle +first: without a coherent taxonomy, every project re-invents its agentfile from +scratch. The runtime levers are powerful complements — and ADE's process layer +can reference them (e.g. _"delegate research to a sub-agent"_, _"verify with the +build hook before committing"_) — but they are orthogonal to the information +architecture itself. ## Core principles @@ -193,9 +273,19 @@ stack. What transfers across projects is the structure itself. ## Customization -All artifacts, that are produced by the CLI, are by default adaptable: You can provide -own workflows, your own skills, your own docs. It should work out of the box. -If this is still too opinionated for you, you can swap out each layer. +All artifacts produced by the CLI are adaptable: you can provide your own +workflows, your own skills, your own docs. It should work out of the box. If +this is still too opinionated for you, you can swap out each layer. + +Bias towards shipping. Start simple and add configuration only when the agent +actually fails — then engineer a solution so it does not fail that way again. +The goal is not the ideal harness; it is shipping high-quality code faster. + +After all: there is no secret ingredient. It is only about getting relevant +information into the conversation context. + +## Further reading -After all: there is no secret ingredient, it only about getting relevant information -into the conversation context. +- [Skill Issue: Harness Engineering for Coding Agents](https://www.humanlayer.dev/blog/skill-issue-harness-engineering-for-coding-agents) + — HumanLayer's practical guide to harness engineering, covering skills, + sub-agents, hooks, and back-pressure mechanisms. diff --git a/docs/CLI-PRD.md b/docs/CLI-PRD.md new file mode 100644 index 0000000..977eef9 --- /dev/null +++ b/docs/CLI-PRD.md @@ -0,0 +1,251 @@ +# ADE CLI — Product Requirements Document + +> **Scope.** This document covers the **ADE CLI** (`packages/cli`) — the +> setup and configuration tool. It does not cover the broader ADE information +> architecture (process, practices, documentation layers) or the runtime +> MCP servers. For the overall ADE vision, see the project README. + +## Problem + +Coding agents (Claude Code, Copilot, Kiro, etc.) each require their own +configuration format to wire in MCP servers, instructions, and documentation. +Teams manually maintain these per-agent config files, leading to drift, +duplication, and onboarding friction. Adding a new MCP server or skill means +editing multiple agent-specific files by hand. + +ADE's information architecture (process, practices, documentation) is +agent-agnostic, but the last mile — getting it into an agent's config — is not. + +## Goal + +Provide a single CLI that lets engineers declare _what_ their project needs +(workflows, skills, knowledge, tools) in one place, and generates the correct +agent-specific configuration for whichever coding agent they use. + +## Users + +- **Individual developers** setting up a project for agentic development. +- **Tech leads** standardizing agent configuration across a team. +- **CI/CD pipelines** that need reproducible agent environments. + +## Core Concepts + +### Facet + +A user-facing configuration question representing a single concern (e.g. +"Which workflow framework?" or "Which architecture stack?"). Each facet offers +a set of options, exactly one of which is selected (or multiple, if the facet +allows multi-select). Facets can be skippable (no selection = no provisions +from that facet). + +### Option + +One possible answer to a facet. Each option carries a recipe and optionally +a list of recommended docsets. + +### Recipe + +A list of provisions that an option brings into the project. A recipe is +never referenced directly by the user — it is the payload behind an option. + +A single option often produces **multiple provisions targeting different +writers**. For example, the "codemcp-workflows" option's recipe contains both +a `workflows` provision (registers the MCP server) and an `instruction` +provision (adds workflow guidance to the agent's instructions). This is how +one logical concept (e.g. "use codemcp workflows") materializes as both +runtime config and agent instructions. + +### Docset + +Documentation sources recommended by an option. Docsets are a **weak entity +on Option** — they are always implied by an upstream selection (e.g. picking +"TanStack" implies TanStack Router/Query/Form/Table docs). The TUI presents +all implied docsets as pre-selected defaults and allows the user to deselect +(opt-out, not opt-in). The resolver collects docsets from all selected +options, deduplicates by id, filters by `excluded_docsets`, and maps them to +`knowledge_sources` in LogicalConfig. When any knowledge sources are present, +the resolver automatically adds a `@codemcp/knowledge-server` MCP server +entry. + +### Provision + +An atomic unit of configuration. Each provision names a **writer** and +carries writer-specific config. Provision types: + +| Writer | What it produces | +| ------------- | ------------------------------------------------------------ | +| `workflows` | MCP server entry for `@codemcp/workflows-server` | +| `skills` | Skill definitions (inline or external) for `@codemcp/skills` | +| `knowledge` | Knowledge source entry for `@codemcp/knowledge` | +| `instruction` | Raw instruction text for the agent | + +### KnowledgeSource + +Describes the origin of documentation content (a git repository URL ending +in `.git`). The `@codemcp/knowledge` package manages the physical docset +artifacts via its programmatic API (`createDocset` + `initDocset`); ADE +tracks the sources in LogicalConfig. + +### LogicalConfig (intermediate representation) + +Agent-agnostic resolved configuration. This is the contract between the +resolution step and the agent writers: + +``` +mcp_servers: [{ref, command, args, env}] +instructions: [string] +skills: [SkillDefinition] +knowledge_sources: [{name, origin, description}] +``` + +### Agent Writer + +Translates LogicalConfig into agent-specific files. ADE owns the knowledge of +every supported agent's config format. When an agent changes its format, only +its writer needs updating. + +Supported agents (v1): + +| Agent | Output files | +| ----------- | ------------------------------------------------- | +| Claude Code | `.claude/settings.json`, `AGENTS.md`, skill files | + +## User-Facing Files + +### `config.yaml` (checked into repo) + +Records facet selections. The CLI manages most of this file via commands; +users may add manual entries in the `custom` section. + +```yaml +choices: + process: codemcp-workflows # single-select facet + architecture: tanstack # single-select facet + practices: # multi-select facet + - conventional-commits + - tdd-london +excluded_docsets: # docsets the user opted out of + - tanstack-table-docs +custom: # user-managed section (not touched by CLI) + mcp_servers: + - ref: custom-server + command: npx + args: ["-y", "@acme/mcp-server"] + instructions: + - "Always use pnpm, never npm." +``` + +The `custom` section is the only part users edit by hand. All other sections +are maintained exclusively through CLI commands, which simplifies merge +conflicts and keeps the file structure predictable. + +### `config.lock.yaml` (checked into repo) + +Fully resolved LogicalConfig snapshot. Deterministic — same `config.yaml` +always produces the same lock file. Enables diffing what actually changed +when a facet selection or catalog version is updated. + +## CLI Commands + +``` +ade setup Interactive TUI: walk through facets, confirm docsets, + write config.yaml + config.lock.yaml + agent files, + install skills and knowledge sources. + Re-running setup on an existing project pre-selects + previous choices as defaults. Warns if a previous + selection references an option no longer in the catalog. + +ade install Apply config.lock.yaml → agent files + skills + knowledge. + Non-interactive. Idempotent. Does not re-resolve — uses + the lock file as-is. +``` + +## Catalog + +Facets, options, and recipes live in a **catalog** — TypeScript code shipped +with ADE. Using code (not data files) gives us type safety, registry +patterns, and explicit references between options. The catalog is the single +place that knows which provisions each option requires, and it versions +naturally with the ADE package. + +## V1 Catalog + +Three facets ship in v1: + +### 1. Process Guidance (`process`) + +How the agent receives workflow and process instructions. + +| Option | Description | +| ------------------- | ------------------------------------------------------------------ | +| `codemcp-workflows` | Uses `@codemcp/workflows-server` MCP for structured EPCC workflows | +| `native-agents-md` | Uses `AGENTS.md` with inline EPCC instructions (no MCP dependency) | + +### 2. Architecture (`architecture`) + +Stack and framework conventions that shape the project structure. + +| Option | Description | +| ---------- | ---------------------------------------------------------------- | +| `tanstack` | Full-stack conventions for TanStack (Router, Query, Form, Table) | + +Each architecture option carries inline skills (conventions, design patterns, +code style, testing) and recommended docsets (git repos for each library's +documentation). + +### 3. Practices (`practices`) — multi-select + +Composable development practices. Multiple selections allowed. + +| Option | Description | +| ---------------------- | ------------------------------------------------------------------ | +| `conventional-commits` | Structured commit messages following the Conventional Commits spec | +| `tdd-london` | London-school (mockist) Test-Driven Development | +| `adr-nygard` | Architecture Decision Records following Nygard's template | + +Practices with associated documentation (e.g. Conventional Commits) carry +docsets that are collected alongside architecture docsets. + +### Documentation Layer (derived) + +Documentation is **not** a standalone facet. Instead, each option in +architecture and practices declares recommended `docsets[]`. The setup TUI +collects all implied docsets and presents them as an opt-out confirmation +step. Accepted docsets become `knowledge_sources` in LogicalConfig, which +triggers: + +1. Automatic addition of the `@codemcp/knowledge-server` MCP server entry +2. Installation via `@codemcp/knowledge` API (`createDocset` + `initDocset`) + +## Non-Goals (initial release) + +- Runtime agent behavior (that is the MCP servers' job). +- Managing MCP server lifecycles or health checks. +- Supporting agent-specific features beyond config file generation. +- Plugin API for third-party provision writers (keep it internal first). + +## Key Design Decisions + +1. **ADE CLI owns agent config format knowledge.** MCP servers are pure + runtime; they do not know or care which agent invoked them. + +2. **Provision writers may call package APIs directly.** The `skills` and + `knowledge` writers import `@codemcp/skills` and `@codemcp/knowledge` as + TypeScript dependencies. This gives type safety over subprocess invocation. + CLI fallback remains an option where direct import is impractical. + +3. **LogicalConfig is the stable contract.** Provision writers produce it, + agent writers consume it. Neither side knows about the other. + +4. **Lock file is mandatory.** It makes the resolved state explicit, + reviewable, and reproducible. + +5. **User edits are confined to `custom`.** The rest of `config.yaml` is + CLI-managed, eliminating merge conflicts in the structured sections. + +6. **Docsets are a weak entity on Option, not a separate facet.** Documentation + sources are always implied by an upstream selection. Making documentation a + standalone facet would create a hollow indirection whose options just mirror + upstream choices 1:1. Config stores `excluded_docsets` (what the user opted + out of) rather than selected docsets, keeping the common case (accept all + recommendations) zero-config. diff --git a/docs/CLI-design.md b/docs/CLI-design.md new file mode 100644 index 0000000..81a981a --- /dev/null +++ b/docs/CLI-design.md @@ -0,0 +1,646 @@ +# ADE CLI — Design Document + +> **Scope.** This document covers the **ADE CLI** — the setup and +> configuration tool. It does not cover the runtime MCP servers +> (`@codemcp/workflows-server`, `@codemcp/knowledge-server`) or the broader +> ADE information architecture. For the overall ADE vision, see the project +> README. + +## Package Structure + +Two packages with clear responsibilities: + +### `@ade/core` (`packages/core`) + +All types, logic, and built-in writers. No CLI framework, no TUI, no user +interaction. Independently importable for programmatic use (CI scripts, +other tools). + +``` +core/src/ + types.ts # all interfaces and type definitions + config.ts # read/write config.yaml and config.lock.yaml + resolver.ts # config + catalog → LogicalConfig + registry.ts # writer registry (provision + agent) + catalog/ + index.ts # catalog assembly, exports all facets + facets/ + process.ts # workflow delivery method + architecture.ts # stack-specific conventions (e.g. TanStack) + practices.ts # composable practices (commits, TDD, ADR) + writers/ # built-in provision writers + workflows.ts + skills.ts + knowledge.ts + instruction.ts + agents/ # built-in agent writers + claude-code.ts # AGENTS.md, .claude/settings.json, skill files +``` + +### `@ade/cli` (`packages/cli`) + +Thin shell: CLI framework wiring and interactive TUI. All business logic +lives in core; CLI commands are thin handlers that parse args and delegate. + +``` +cli/src/ + index.ts # entry point, arg parser, command routing + skills-installer.ts # calls @codemcp/skills API to install skills + knowledge-installer.ts # calls @codemcp/knowledge API to install docsets + commands/ + setup.ts # interactive TUI setup + install.ts # resolve + generate (idempotent) +``` + +`@ade/cli` depends on `@ade/core`. Nothing depends on `@ade/cli`. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────────────────┐ +│ @ade/cli │ +│ ade setup · ade install │ +│ TUI prompts · skills-installer · knowledge-installer │ +└──────────────────────────┬───────────────────────────────────┘ + │ delegates to +┌──────────────────────────▼───────────────────────────────────┐ +│ @ade/core │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────────────────────┐ │ +│ │ Catalog │──▶│ Resolver │──▶│ Writer Registry │ │ +│ │ (facets) │ │ │ │ │ │ +│ └──────────┘ └────┬─────┘ │ provision: Map │ │ +│ │ │ agents: Map │ │ +│ ▼ └───────────┬────────────┘ │ +│ ┌──────────────┐ │ │ +│ │ LogicalConfig│◀────────────┘ │ +│ └──────┬───────┘ merge fragments │ +│ │ │ +│ ▼ │ +│ agent-specific files │ +└──────────────────────────────────────────────────────────────┘ +``` + +## Data Flow + +### 1. Setup: TUI → config.yaml + config.lock.yaml + agent files + +``` +read existing config.yaml (if any) for default selections + → walk facets interactively: + → pre-select previous choice as default (if still valid) + → warn if previous choice references a stale option + → collect new user choices + → collect docsets from all selected options + → present docset confirmation (opt-out multiselect) + → resolve choices + catalog → LogicalConfig + → write config.yaml (user choices) + → write config.lock.yaml (resolved LogicalConfig snapshot) + → run agent writer (generate AGENTS.md, settings.json, etc.) + → install skills via @codemcp/skills API + → install knowledge via @codemcp/knowledge API +``` + +Resolution expands each selected option's recipe provisions into +LogicalConfig fragments, deduplicates docsets by id, filters by +`excluded_docsets`, maps enabled docsets to `knowledge_sources`, adds the +`@codemcp/knowledge-server` MCP entry if knowledge sources are present, +merges the custom section, and deduplicates MCP servers by ref. + +For **multi-select facets**, each selected option's recipe is resolved +independently and their LogicalConfig fragments are merged. + +### 2. Install: config.lock.yaml → agent files (idempotent) + +``` +read config.lock.yaml + → select agent writer (default: claude-code) + → apply logical_config from lock file (no re-resolution) + → run agent writer + → install skills + → install knowledge +``` + +`ade install` does **not** re-resolve from `config.yaml`. It treats the +lock file as the source of truth, like `npm ci` treats `package-lock.json`. +To change selections, re-run `ade setup`. + +The target agent is a **generation-time parameter** (`--agent` flag), +not stored in `config.yaml`. There is no auto-detection. This keeps the +config agent-agnostic — the same choices can produce output for any +supported agent. + +### 3. Package API calls from CLI installers + +Skills and knowledge installation delegates to sibling packages. ADE imports +them as TypeScript dependencies rather than shelling out, giving type safety +and avoiding CLI flag contracts. + +``` +skills-installer: + → import { runAdd } from "@codemcp/skills/api" + → for each skill: runAdd([source], { yes: true, all: true }) + → skills package writes SKILL.md files and skills-lock.json + +knowledge-installer: + → import { createDocset, initDocset } + from "@codemcp/knowledge/packages/cli/dist/exports.js" + → for each knowledge_source: + createDocset({ id, name, preset: "git-repo", url: origin }, { cwd }) + initDocset({ docsetId: id, cwd }) + → knowledge package manages .knowledge/ directory and docset artifacts +``` + +Where direct import is impractical (e.g. the dependency isn't TypeScript or +has incompatible runtimes), CLI subprocess invocation is the fallback. + +## Entity Model + +### Catalog Structure + +The catalog is TypeScript code, not YAML. This gives us type safety, registry +patterns, and explicit references between options. + +```typescript +interface Catalog { + facets: Facet[]; +} + +interface Facet { + id: string; // e.g. "process" + label: string; // e.g. "Process Guidance" + description: string; + required: boolean; // false = skippable + multiSelect?: boolean; // true = user can pick multiple options + options: Option[]; +} + +interface Option { + id: string; // e.g. "codemcp" + label: string; // e.g. "CodeMCP Workflows" + description: string; + recipe: Provision[]; // multiple provisions per option is common + docsets?: DocsetDef[]; // recommended documentation for this option +} + +// Documentation as a weak entity on Option. Docsets are derived from +// upstream selections — picking "TanStack" in architecture implies +// TanStack docs, picking "GitHub Actions CI/CD" in practices implies +// GH Actions docs. The TUI presents all implied docsets as pre-selected +// defaults and allows the user to deselect. This is opt-out, not opt-in. +// +// The resolver collects docsets from all selected options, deduplicates +// by id, filters by excluded_docsets from UserConfig, and maps enabled +// docsets directly to knowledge_sources in LogicalConfig. +interface DocsetDef { + id: string; // unique key for dedup, e.g. "tanstack-query-docs" + label: string; // display name, e.g. "TanStack Query Reference" + origin: string; // URL, path, or package ref + description: string; // shown in TUI +} + +// A recipe typically contains multiple provisions for different writers. +// Example: the "codemcp" workflow option produces: +// 1. workflows provision → registers @codemcp/workflows-server as MCP server +// 2. instruction provision → adds workflow usage guidance to agent instructions +// This is how one logical concept materializes across different output channels. + +interface Provision { + writer: string; // references a registered ProvisionWriterDef.id + config: Record; // writer-specific, validated at boundary +} + +// Passed to provision writers for future cross-facet context. +// Currently passed as { resolved: {} }. +interface ResolutionContext { + resolved: Record; +} +``` + +### LogicalConfig (intermediate representation) + +```typescript +interface LogicalConfig { + mcp_servers: McpServerEntry[]; + instructions: string[]; + skills: SkillDefinition[]; + knowledge_sources: KnowledgeSource[]; +} + +interface McpServerEntry { + ref: string; // unique key for dedup/update + command: string; // e.g. "npx" + args: string[]; // e.g. ["-y", "@codemcp/workflows-server"] + env: Record; +} + +interface KnowledgeSource { + name: string; // e.g. "tanstack" + origin: string; // URL, path, or package ref + description: string; +} +``` + +### Config Files + +```typescript +// config.yaml — mostly CLI-managed, agent-agnostic +interface UserConfig { + choices: Record; // single-select: string, multi-select: string[] + excluded_docsets?: string[]; // docset IDs the user opted out of + custom?: { + // user-managed section + mcp_servers?: McpServerEntry[]; + instructions?: string[]; + }; +} + +// config.lock.yaml — generated, never hand-edited +interface LockFile { + version: 1; + generated_at: string; // ISO timestamp + choices: Record; // snapshot of selections + logical_config: LogicalConfig; +} +``` + +## Extensibility and Type Safety + +### Design Tension + +Provision and agent writers need two properties that pull in opposite +directions: + +1. **Type safety** — built-in writers should have typed configs, not + `Record` everywhere. +2. **Runtime extensibility** — future packages must be able to register + new writers without modifying core's source. + +### Solution: Interfaces for Contracts, Registries for Dispatch + +Writers are defined as **interfaces** (open contracts, implementable by +anyone) and collected in **runtime registries** (`Map`-based, open for +insertion). Built-in writers get typed configs internally while conforming +to the open interface at the boundary. + +```typescript +// --- Writer contracts (open, any package can implement) --- + +interface ProvisionWriterDef { + id: string; + write( + config: Record, + context: ResolutionContext + ): Promise>; +} + +interface AgentWriterDef { + id: string; + install(config: LogicalConfig, projectRoot: string): Promise; +} + +// --- Writer registry (open at runtime) --- + +interface WriterRegistry { + provisions: Map; + agents: Map; +} +``` + +### How Built-In Writers Get Type Safety + +Each built-in writer defines a typed config interface and validates/narrows +at the boundary. The registry doesn't care — it passes +`Record` through. The writer narrows internally: + +```typescript +// writers/workflows.ts +interface WorkflowsConfig { + package: string; + env?: Record; +} + +export const workflowsWriter: ProvisionWriterDef = { + id: "workflows", + async write(config, _context) { + const c = config as WorkflowsConfig; // validated at boundary + return { + mcp_servers: [ + { + ref: c.package, + command: "npx", + args: ["-y", c.package], + env: c.env ?? {} + } + ] + }; + } +}; +``` + +The catalog definitions reference writers by string ID, not by import. +This is what makes the system open — a provision `{ writer: "my-custom", config: {...} }` +works as long as `"my-custom"` is registered before resolution runs. + +### Registry Lifecycle + +Core ships a `createDefaultRegistry()` that pre-registers all built-in +writers. The CLI calls this at startup. A future plugin would call +`registry.provisions.set("my-writer", myWriter)` before resolution. + +```typescript +function createDefaultRegistry(): WriterRegistry { + const registry = createRegistry(); + + registerProvisionWriter(registry, instructionWriter); + registerProvisionWriter(registry, workflowsWriter); + registerProvisionWriter(registry, skillsWriter); + registerProvisionWriter(registry, knowledgeWriter); + + registerAgentWriter(registry, claudeCodeWriter); + + return registry; +} +``` + +### Why Not Pure Functions + Discriminated Unions? + +A discriminated union (`type Provision = { writer: "workflows", config: WorkflowsConfig } | ...`) +gives excellent compile-time safety but is a **closed set**. Adding a writer +from another package means modifying the union in core, which defeats +extensibility. + +The interface-based registry trades compile-time exhaustiveness for runtime +openness. The `Provision.writer` field is `string`, not a union — the +registry validates at resolution time that the writer exists. Built-in +writers still get internal type safety via their own config interfaces. + +### Built-In Provision Config Types + +For reference, the typed configs used internally by built-in writers: + +```typescript +interface WorkflowsConfig { + package: string; + ref?: string; + env?: Record; +} + +interface SkillsConfig { + skills: SkillDefinition[]; +} + +interface KnowledgeConfig { + name: string; + origin: string; // must be a valid .git URL + description: string; +} + +interface InstructionConfig { + text: string; +} +``` + +These are not exported as part of the public contract. They are +implementation details of the built-in writers. + +## Agent Writers + +Each agent writer implements `AgentWriterDef`. The writer has full ownership +of how to translate LogicalConfig into agent-specific files. It reads +existing files when needed to perform incremental updates. + +### Claude Code Writer (v1) + +Produces agent-specific config files for Claude Code: + +- **`AGENTS.md`** — ADE-managed section with resolved instructions +- **`.claude/settings.json`** — MCP server entries (merged with existing) +- **`.ade/skills//SKILL.md`** — Inline skill files (staging area for + `@codemcp/skills` installation) + +Future agent writers: OpenCode, Copilot, Kiro. + +### ADE-Managed Section Delimiters + +Agent writers that produce markdown or text files (instructions, AGENTS.md, +etc.) use delimiters to mark ADE-managed sections. This allows the writer to +update its sections without clobbering user-authored content. + +```markdown + + +(ADE-managed content — do not edit manually) +... + + +``` + +For JSON config files (e.g. settings.json), the writer manages a top-level +key or object scope and merges with existing content. + +## Provision Writers + +Each provision writer implements `ProvisionWriterDef`. Writers receive a +`ResolutionContext` containing the resolved options from dependent facets, +allowing them to adapt output based on sibling selections. + +### `workflows` writer + +```typescript +// provision config +{ package: "@codemcp/workflows-server", env: { WORKFLOW_DIR: "./workflows" } } +``` + +Produces: one `McpServerEntry` with `command: "npx"`, +`args: ["-y", "@codemcp/workflows-server"]`, and the given env vars. + +### `skills` writer + +```typescript +{ + skills: [ + { name: "tanstack-architecture", description: "...", body: "..." }, + { + name: "playwright-cli", + source: "microsoft/playwright-cli/skills/playwright-cli" + } + ]; +} +``` + +Passes skill definitions (inline or external) through to LogicalConfig. +Inline skills include a `body` field; external skills reference a `source`. +The actual installation (writing SKILL.md files and calling `@codemcp/skills` +API) is handled by the agent writer and CLI's `skills-installer`. + +### `knowledge` writer + +```typescript +{ name: "tanstack-query-docs", origin: "https://github.com/TanStack/query.git", description: "Server state management" } +``` + +Produces a `KnowledgeSource` entry in LogicalConfig. The actual installation +(calling `@codemcp/knowledge` API) is handled by the CLI's +`knowledge-installer`. Origins must be valid `.git` URLs for the `git-repo` +preset. + +### `mcp-server` writer + +```typescript +{ ref: "my-server", command: "npx", args: ["-y", "@acme/mcp-server"], env: { API_KEY: "${API_KEY}" } } +``` + +Pass-through: produces one `McpServerEntry` directly. + +### `instruction` writer + +```typescript +{ + text: "Always use pnpm, never npm."; +} +``` + +Produces: one `instructions` entry. + +## V1 Catalog (TypeScript) + +Example of how the catalog is defined in code: + +```typescript +// catalog/facets/process.ts +export const processFacet: Facet = { + id: "process", + label: "Process Guidance", + description: "How the agent receives workflow and process instructions", + required: false, + options: [ + { + id: "codemcp-workflows", + label: "CodeMCP Workflows", + description: + "Structured EPCC workflows via @codemcp/workflows-server MCP", + recipe: [ + { + writer: "workflows", + config: { package: "@codemcp/workflows-server" } + }, + { + writer: "instruction", + config: { text: "Use the workflows MCP server..." } + } + ] + }, + { + id: "native-agents-md", + label: "Native AGENTS.md", + description: "Inline EPCC instructions in AGENTS.md, no MCP dependency", + recipe: [ + { + writer: "instruction", + config: { text: "Follow the EPCC workflow..." } + } + ] + } + ] +}; + +// catalog/facets/architecture.ts — options carry skills + docsets +export const architectureFacet: Facet = { + id: "architecture", + label: "Architecture", + description: + "Stack and framework conventions that shape your project structure", + required: false, + options: [ + { + id: "tanstack", + label: "TanStack", + description: + "Full-stack conventions for TanStack (Router, Query, Form, Table)", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "tanstack-architecture", + description: "...", + body: "..." + }, + { + name: "playwright-cli", + source: "microsoft/playwright-cli/skills/playwright-cli" + } + ] + } + }, + { + writer: "instruction", + config: { text: "This project follows TanStack conventions..." } + } + ], + docsets: [ + { + id: "tanstack-router-docs", + label: "TanStack Router", + origin: "https://github.com/TanStack/router.git", + description: "File-based routing, loaders, and search params" + }, + { + id: "tanstack-query-docs", + label: "TanStack Query", + origin: "https://github.com/TanStack/query.git", + description: "Server state management, caching, and mutations" + } + // ... form, table + ] + } + ] +}; +``` + +## Design Decisions + +1. **Two packages: `@ade/core` + `@ade/cli`.** Core owns all types, logic, + catalog, and writers. CLI is a thin shell for arg parsing and TUI. Core + is independently importable for programmatic use. No MCP server package — + runtime MCP servers are separate projects. + +2. **Interfaces for contracts, registries for dispatch.** Writer contracts + are open interfaces (`ProvisionWriterDef`, `AgentWriterDef`). Dispatch + uses `Map`-based registries, open at runtime. This enables future + extensibility from other packages without modifying core. + +3. **Built-in writers get internal type safety.** Each built-in writer + defines its own typed config interface and narrows from + `Record` at the boundary. The registry contract stays + generic; the implementation is specific. + +4. **Catalog is TypeScript code.** No YAML catalog files. Facets, options, + and recipes are defined as typed objects in `core/src/catalog/`. This + gives type safety, IDE support, and natural versioning with the package. + Kept inside core for now; extractable to a separate package later along + the `Catalog` interface seam. + +5. **Direct package imports over CLI subprocesses.** The CLI's installers + import `@codemcp/skills` and `@codemcp/knowledge` as TypeScript + dependencies and call their programmatic APIs (`runAdd`, `createDocset`, + `initDocset`). CLI subprocess invocation is the fallback for + non-TypeScript or cross-runtime cases. + +6. **`custom` section isolates user edits.** Only the `custom` block in + `config.yaml` is user-managed. The rest is CLI-managed. This eliminates + merge conflicts: the CLI never touches `custom`, and users never touch + the rest. Agent writers merge both sections when generating output. + +7. **Docsets are a weak entity on Option, not a separate facet.** Documentation + sources are always implied by an upstream selection (architecture or + practices). Making documentation a standalone facet would create a hollow + indirection whose options just mirror upstream choices 1:1. Instead, each + `Option` declares its recommended `docsets[]`. The resolver collects and + deduplicates them; the TUI presents them as a confirmation step (opt-out, + not opt-in). Config stores `excluded_docsets` (what the user opted out of) + rather than selected docsets, keeping the common case (accept all + recommendations) zero-config. When knowledge sources are present, the + resolver automatically adds a `@codemcp/knowledge-server` MCP server entry. diff --git a/docs/adrs/0001-tui-framework-selection.md b/docs/adrs/0001-tui-framework-selection.md new file mode 100644 index 0000000..1623a4f --- /dev/null +++ b/docs/adrs/0001-tui-framework-selection.md @@ -0,0 +1,77 @@ +# ADR 0001: TUI Framework Selection for ADE CLI + +## Status + +Accepted + +## Context + +ADE (Agentic Development Environment) is a CLI tool that guides users through setup and configuration via interactive terminal prompts. The CLI needs to: + +- Present guided wizard-style flows (select, multiselect, confirm, text input) +- Display streaming output from LLM/agent processes +- Look visually polished and modern ("world-class-nerdy") while remaining enterprise-appropriate +- Terminate after completion (not a long-lived interactive TUI) +- Run in standard terminals including CI environments +- Be ESM-compatible (the project is `"type": "module"`) +- Integrate cleanly into a TypeScript monorepo (Node >= 22, pnpm) + +We evaluated the actively-maintained Node.js TUI/prompt frameworks available as of March 2026. Legacy libraries (blessed, neo-blessed, enquirer, prompts by terkelg) were excluded upfront due to abandonment or CJS-only distribution. + +## Decision + +We will use **@clack/prompts** as the TUI framework for the ADE CLI. + +## Evaluation: Weighted Pugh Matrix + +Criteria were weighted on a 3-point scale (1 = nice-to-have, 2 = important, 3 = critical). Each candidate was scored relative to a baseline of 0 (meets expectations), with +1 (better) and -1 (worse). + +| # | Criterion | Weight | @clack/prompts | @inquirer/prompts | Ink + @inkjs/ui | +| --- | ---------------------------------- | ------ | -------------- | ----------------- | --------------- | +| 1 | Visual polish out-of-the-box | 3 | +1 | -1 | +1 | +| 2 | Wizard/prompt flow suitability | 3 | +1 | +1 | 0 | +| 3 | ESM-native compatibility | 3 | +1 | 0 | 0 | +| 4 | LLM/streaming output support | 2 | +1 | -1 | +1 | +| 5 | Learning curve / simplicity | 2 | +1 | 0 | -1 | +| 6 | Bundle size / dependency footprint | 2 | +1 | 0 | -1 | +| 7 | Ecosystem / plugin breadth | 1 | -1 | +1 | 0 | +| 8 | Community size / adoption | 1 | 0 | +1 | +1 | +| 9 | Custom prompt authoring | 1 | 0 | +1 | +1 | + +**Weighted totals:** + +| Candidate | Calculation | Total | +| --------------------- | ------------------------------------------------------------------- | ------- | +| **@clack/prompts** | 3(+1) + 3(+1) + 3(+1) + 2(+1) + 2(+1) + 2(+1) + 1(-1) + 1(0) + 1(0) | **+14** | +| **@inquirer/prompts** | 3(-1) + 3(+1) + 3(0) + 2(-1) + 2(0) + 2(0) + 1(+1) + 1(+1) + 1(+1) | **+1** | +| **Ink + @inkjs/ui** | 3(+1) + 3(0) + 3(0) + 2(+1) + 2(-1) + 2(-1) + 1(0) + 1(+1) + 1(+1) | **+5** | + +@clack/prompts scores highest by a significant margin. + +## Rationale + +**@clack/prompts wins on the criteria that matter most to ADE:** + +1. **Visual polish (weight 3):** Clack's pre-styled prompts are the most visually striking of any Node.js prompt library. Unicode box-drawing, colored indicators, and thoughtful spacing produce a premium feel with zero configuration. @inquirer/prompts looks functional but plain; Ink can match Clack's aesthetics but requires manual styling. + +2. **Wizard suitability (weight 3):** ADE's CLI is a terminating wizard, not a persistent dashboard. Clack was purpose-built for sequential prompt flows with `intro()`, `outro()`, `group()`, and `spinner()`. Ink is designed for persistent, React-rendered UIs — architectural overkill for a flow that collects answers and exits. + +3. **ESM-native (weight 3):** Clack is ESM-only, aligning perfectly with ADE's `"type": "module"` configuration. No dual-format complications, no CJS shims. + +4. **LLM streaming (weight 2):** Clack includes native `stream` utilities designed for rendering LLM/agent output in the terminal — a direct match for ADE's agentic use case. Inquirer has no equivalent. + +5. **Simplicity (weight 2):** Clack's API is a flat set of async functions (`select()`, `text()`, `confirm()`, `spinner()`). No React knowledge required, no component tree to manage. This lowers the contribution barrier and reduces maintenance surface. + +6. **Footprint (weight 2):** Clack uses Node's built-in `styleText` instead of external color libraries. Minimal transitive dependencies. Ink pulls in React, Yoga (native binary), and a reconciler. + +**Where Clack is weaker — and why it doesn't matter:** + +- _Ecosystem breadth (weight 1):_ Inquirer has more community plugins (table prompts, file selectors, i18n). ADE's current scope doesn't require these, and `@clack/core` allows building custom prompts if needed. +- _Persistent UI:_ Clack can't render a dashboard or split-pane view. ADE doesn't need one — it's a wizard that terminates. + +## Consequences + +- The `@ade/cli` package will add `@clack/prompts` as a production dependency. +- All interactive CLI flows (setup, configuration, MCP server management) will use Clack's prompt primitives. +- If a future requirement emerges for persistent/dashboard-style terminal UI (e.g., a live agent monitoring view), we can evaluate adding Ink as a complementary dependency at that time. The two libraries are not mutually exclusive. +- Custom prompts beyond Clack's built-in set will be authored using `@clack/core`. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..296beed --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,38 @@ +import js from "@eslint/js"; +import { parser, configs } from "typescript-eslint"; +import prettier from "eslint-config-prettier"; + +export default [ + js.configs.recommended, + ...configs.recommended, + prettier, + { + // Config for TypeScript files + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser, + parserOptions: { + project: [ + "./tsconfig.json", + "./docs/.vitepress/tsconfig.json", + "./packages/*/tsconfig.json" + ] + } + } + }, + { + // Config for JavaScript files - no TypeScript parsing + files: ["**/*.{js,jsx}"], + ...js.configs.recommended + }, + { + ignores: [ + "**/node_modules/**", + "**/dist/**", + ".pnpm-store/**", + "pnpm-lock.yaml", + "/packages/**", + "docs/.vitepress/cache/**" + ] + } +]; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c64d153 --- /dev/null +++ b/package.json @@ -0,0 +1,76 @@ +{ + "name": "ade", + "version": "0.0.0-development", + "private": true, + "description": "ADE CLI — Agentic Development Environment setup and configuration tool", + "license": "MIT", + "keywords": [ + "ade", + "agentic", + "development", + "cli", + "mcp" + ], + "repository": { + "type": "git", + "url": "https://github.com/codemcp/ade" + }, + "engines": { + "node": ">=22", + "pnpm": ">=9.0.0" + }, + "type": "module", + "scripts": { + "build": "turbo run build", + "build:clean": "turbo run clean:build", + "dev": "turbo run dev", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs", + "test": "turbo run --parallel test", + "test:watch": "turbo run --parallel test:watch", + "lint:all": "turbo run --parallel lint", + "lint:fix:all": "turbo run --parallel lint:fix", + "format:check:all": "turbo run --parallel format", + "format:all": "turbo run --parallel format:fix", + "typecheck:all": "turbo run --parallel typecheck", + "lint": "eslint", + "lint:fix": "eslint --fix", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "prepare": "husky" + }, + "devDependencies": { + "@braintree/sanitize-url": "7.1.1", + "@eslint/js": "9.18.0", + "@swc/core": "^1.10.9", + "@tsconfig/node22": "22.0.0", + "@tsconfig/strictest": "2.0.5", + "@types/eslint-config-prettier": "6.11.3", + "@types/node": "^22.10.7", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", + "@vitest/coverage-v8": "^3.0.3", + "cytoscape": "3.31.0", + "cytoscape-cose-bilkent": "4.1.0", + "dayjs": "1.11.13", + "debug": "4.4.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "husky": "^9.1.7", + "lint-staged": "^15.4.1", + "nodemon": "^3.1.9", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "turbo": "^2.3.3", + "typescript": "^5.7.3", + "typescript-eslint": "8.21.0", + "vitepress": "1.6.2", + "vitepress-plugin-mermaid": "2.0.17", + "vitest": "^3.0.3" + }, + "packageManager": "pnpm@9.14.2", + "dependencies": { + "yaml": "^2.8.2" + } +} diff --git a/packages/cli/.prettierignore b/packages/cli/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/packages/cli/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/packages/cli/eslint.config.mjs b/packages/cli/eslint.config.mjs new file mode 100644 index 0000000..1483555 --- /dev/null +++ b/packages/cli/eslint.config.mjs @@ -0,0 +1,40 @@ +import js from "@eslint/js"; +import { parser, configs } from "typescript-eslint"; +import prettier from "eslint-config-prettier"; + +export default [ + js.configs.recommended, + ...configs.recommended, + prettier, + { + // Config for TypeScript files + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser, + parserOptions: { + project: ["./tsconfig.json", "./tsconfig.vitest.json"] + } + } + }, + { + // Config for JavaScript files - no TypeScript parsing + files: ["**/*.{js,jsx}"], + ...js.configs.recommended + }, + { + // Relaxed rules for test files + files: ["**/*.test.ts", "**/*.spec.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off" + } + }, + { + ignores: [ + "**/node_modules/**", + "**/dist/**", + ".pnpm-store/**", + "pnpm-lock.yaml" + ] + } +]; diff --git a/packages/cli/nodemon.json b/packages/cli/nodemon.json new file mode 100644 index 0000000..e5d466d --- /dev/null +++ b/packages/cli/nodemon.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json.schemastore.org/nodemon.json", + "watch": ["./src/**", "./node_modules/@mme/**/dist/**"], + "ignoreRoot": [], + "ext": "ts,js", + "exec": "pnpm typecheck && pnpm build" +} diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000..634fca9 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,39 @@ +{ + "name": "@ade/cli", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "bin": { + "ade": "dist/index.js" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clean:build": "rimraf ./dist", + "dev": "nodemon", + "lint": "eslint .", + "lint:fix": "eslint --fix .", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "test": "vitest --run", + "test:watch": "vitest", + "typecheck": "tsc" + }, + "dependencies": { + "@ade/core": "workspace:*", + "@ade/harnesses": "workspace:*", + "@clack/prompts": "^1.1.0" + }, + "devDependencies": { + "@codemcp/knowledge": "2.1.0", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "typescript": "^5.7.3" + } +} diff --git a/packages/cli/src/commands/conventions.integration.spec.ts b/packages/cli/src/commands/conventions.integration.spec.ts new file mode 100644 index 0000000..258d0f2 --- /dev/null +++ b/packages/cli/src/commands/conventions.integration.spec.ts @@ -0,0 +1,265 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile, access } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + select: vi.fn(), + multiselect: vi.fn(), + confirm: vi.fn(), + isCancel: vi.fn().mockReturnValue(false), + cancel: vi.fn(), + spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() }) +})); + +import * as clack from "@clack/prompts"; +import { runSetup } from "./setup.js"; +import { readUserConfig, readLockFile } from "@ade/core"; +import { getDefaultCatalog } from "../../../core/src/catalog/index.js"; + +describe("architecture and practices facets integration", () => { + let dir: string; + + beforeEach(async () => { + vi.clearAllMocks(); + dir = await mkdtemp(join(tmpdir(), "ade-conventions-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it( + "writes SKILL.md files and installs inline skills for tanstack architecture", + { timeout: 60_000 }, + async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("tanstack"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce([]) // docsets: deselect all + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Inline skills should have SKILL.md in .ade/skills/ (staging area) + for (const skill of [ + "tanstack-architecture", + "tanstack-design", + "tanstack-code", + "tanstack-testing" + ]) { + const skillMd = await readFile( + join(dir, ".ade", "skills", skill, "SKILL.md"), + "utf-8" + ); + expect(skillMd).toContain(`name: ${skill}`); + expect(skillMd).toContain("---"); + } + + // Inline skills should also be installed to .agentskills/skills/ by runAdd + for (const skill of [ + "tanstack-architecture", + "tanstack-design", + "tanstack-code", + "tanstack-testing" + ]) { + const installed = await readFile( + join(dir, ".agentskills", "skills", skill, "SKILL.md"), + "utf-8" + ); + expect(installed).toContain(`name: ${skill}`); + } + + // skills-lock.json should be created by runAdd + const lockRaw = await readFile(join(dir, "skills-lock.json"), "utf-8"); + const skillsLock = JSON.parse(lockRaw); + expect(skillsLock.skills).toBeDefined(); + + // skills-server MCP server should be in .mcp.json + const mcpJson = JSON.parse( + await readFile(join(dir, ".mcp.json"), "utf-8") + ); + expect(mcpJson.mcpServers["agentskills"]).toMatchObject({ + command: "npx", + args: ["-y", "@codemcp/skills-server"] + }); + } + ); + + it("writes skills for multiple selected practices", async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture: skip + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["conventional-commits", "tdd-london"]) // practices + .mockResolvedValueOnce([]) // docsets: deselect all (conventional-commits has docset) + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Both inline skills should exist in .ade/skills/ (staging) + const commits = await readFile( + join(dir, ".ade", "skills", "conventional-commits", "SKILL.md"), + "utf-8" + ); + expect(commits).toContain("name: conventional-commits"); + expect(commits).toContain("Conventional Commits"); + + const tdd = await readFile( + join(dir, ".ade", "skills", "tdd-london", "SKILL.md"), + "utf-8" + ); + expect(tdd).toContain("name: tdd-london"); + expect(tdd).toContain("London"); + + // Both should be installed to .agentskills/skills/ + await expect( + access(join(dir, ".agentskills", "skills", "conventional-commits")) + ).resolves.toBeUndefined(); + await expect( + access(join(dir, ".agentskills", "skills", "tdd-london")) + ).resolves.toBeUndefined(); + + // config.yaml should have array of choices under practices + const config = await readUserConfig(dir); + expect(config!.choices.practices).toEqual([ + "conventional-commits", + "tdd-london" + ]); + + // Lock file should reflect both + const lock = await readLockFile(dir); + expect(lock!.logical_config.skills.length).toBeGreaterThanOrEqual(2); + }); + + it("writes ADR skill with template content", async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture: skip + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["adr-nygard"]) + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + const adr = await readFile( + join(dir, ".ade", "skills", "adr-nygard", "SKILL.md"), + "utf-8" + ); + expect(adr).toContain("name: adr-nygard"); + expect(adr).toContain("## Context"); + expect(adr).toContain("## Decision"); + expect(adr).toContain("## Consequences"); + }); + + it("skips both architecture and practices when none selected", async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture: skip + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // No .ade directory should exist + await expect(access(join(dir, ".ade"))).rejects.toThrow(); + + // config.yaml should not have architecture or practices keys + const config = await readUserConfig(dir); + expect(config!.choices).not.toHaveProperty("architecture"); + expect(config!.choices).not.toHaveProperty("practices"); + }); + + it("exposes practices as skills, not instructions", async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture: skip + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["tdd-london"]) + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Practice produces a skill, not an instruction + const skillMd = await readFile( + join(dir, ".ade", "skills", "tdd-london", "SKILL.md"), + "utf-8" + ); + expect(skillMd).toContain("name: tdd-london"); + + // Lock file should have skill but no practice-specific instructions + const lock = await readLockFile(dir); + expect(lock!.logical_config.skills.length).toBeGreaterThanOrEqual(1); + // Only process-facet instructions should be present (from native-agents-md) + for (const instruction of lock!.logical_config.instructions) { + expect(instruction).not.toContain("tdd-london"); + } + }); + + it( + "combines architecture and practices selections", + { timeout: 60_000 }, + async () => { + const catalog = getDefaultCatalog(); + + // Facet order: process (select), architecture (select), practices (multiselect) + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("tanstack"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["tdd-london", "conventional-commits"]) // practices + .mockResolvedValueOnce([]) // docsets: deselect all + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Architecture skills should exist + const archSkill = await readFile( + join(dir, ".ade", "skills", "tanstack-architecture", "SKILL.md"), + "utf-8" + ); + expect(archSkill).toContain("name: tanstack-architecture"); + + // Practice skills should exist + const tddSkill = await readFile( + join(dir, ".ade", "skills", "tdd-london", "SKILL.md"), + "utf-8" + ); + expect(tddSkill).toContain("name: tdd-london"); + + const commitsSkill = await readFile( + join(dir, ".ade", "skills", "conventional-commits", "SKILL.md"), + "utf-8" + ); + expect(commitsSkill).toContain("name: conventional-commits"); + + // config.yaml should have both architecture and practices + const config = await readUserConfig(dir); + expect(config!.choices.architecture).toBe("tanstack"); + expect(config!.choices.practices).toEqual([ + "tdd-london", + "conventional-commits" + ]); + } + ); +}); diff --git a/packages/cli/src/commands/install.integration.spec.ts b/packages/cli/src/commands/install.integration.spec.ts new file mode 100644 index 0000000..9eceb85 --- /dev/null +++ b/packages/cli/src/commands/install.integration.spec.ts @@ -0,0 +1,123 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +// Mock only the TUI — everything else is real +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, + select: vi.fn(), + multiselect: vi.fn(), + isCancel: vi.fn().mockReturnValue(false), + cancel: vi.fn(), + spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() }) +})); + +import * as clack from "@clack/prompts"; +import { runSetup } from "./setup.js"; +import { runInstall } from "./install.js"; +import { getDefaultCatalog } from "../../../core/src/catalog/index.js"; + +describe("install integration (real temp dir)", () => { + let dir: string; + + beforeEach(async () => { + vi.clearAllMocks(); + dir = await mkdtemp(join(tmpdir(), "ade-install-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("applies lock file to regenerate agent files without re-resolving", async () => { + const catalog = getDefaultCatalog(); + + // Step 1: Run setup to create config.yaml + config.lock.yaml + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + await runSetup(dir, catalog); + + // Step 2: Delete agent output files to simulate a fresh clone + await rm(join(dir, ".mcp.json")); + await rm(join(dir, ".claude"), { recursive: true, force: true }); + + // Step 3: Run install — should regenerate from config.lock.yaml + await runInstall(dir, ["claude-code"]); + + // Agent files should be back + const agentMd = await readFile( + join(dir, ".claude", "agents", "ade.md"), + "utf-8" + ); + expect(agentMd).toContain("Call whats_next()"); + + const mcpJson = JSON.parse(await readFile(join(dir, ".mcp.json"), "utf-8")); + expect(mcpJson.mcpServers["workflows"]).toMatchObject({ + command: "npx", + args: ["@codemcp/workflows-server@latest"] + }); + }); + + it("does not modify the lock file", async () => { + const catalog = getDefaultCatalog(); + + // Setup first + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + await runSetup(dir, catalog); + + const lockRawBefore = await readFile( + join(dir, "config.lock.yaml"), + "utf-8" + ); + + // Re-install + await runInstall(dir, ["claude-code"]); + + const lockRawAfter = await readFile(join(dir, "config.lock.yaml"), "utf-8"); + // Lock file should be byte-identical (install doesn't rewrite it) + expect(lockRawAfter).toBe(lockRawBefore); + }); + + it("fails when no config.lock.yaml exists", async () => { + await expect(runInstall(dir, ["claude-code"])).rejects.toThrow( + /config\.lock\.yaml not found/i + ); + }); + + it("works with native-agents-md option", async () => { + const catalog = getDefaultCatalog(); + + // Setup with native-agents-md + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + await runSetup(dir, catalog); + + // Delete agent output + await rm(join(dir, ".claude"), { recursive: true, force: true }); + + // Re-install + await runInstall(dir, ["claude-code"]); + + const agentMd = await readFile( + join(dir, ".claude", "agents", "ade.md"), + "utf-8" + ); + expect(agentMd).toContain("AGENTS.md"); + }); +}); diff --git a/packages/cli/src/commands/install.spec.ts b/packages/cli/src/commands/install.spec.ts new file mode 100644 index 0000000..d0aeb74 --- /dev/null +++ b/packages/cli/src/commands/install.spec.ts @@ -0,0 +1,166 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import type { LogicalConfig } from "@ade/core"; + +// ── Mocks ──────────────────────────────────────────────────────────────────── + +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() } +})); + +const mockLogical: LogicalConfig = { + mcp_servers: [], + instructions: ["test instruction"], + cli_actions: [], + knowledge_sources: [], + skills: [] +}; + +vi.mock("@ade/core", async (importOriginal) => { + const actual = (await importOriginal()) as typeof import("@ade/core"); + return { + ...actual, + readLockFile: vi.fn() + }; +}); + +const mockInstall = vi.fn().mockResolvedValue(undefined); + +vi.mock("@ade/harnesses", () => ({ + getHarnessWriter: vi.fn().mockImplementation((id: string) => { + if (id === "universal" || id === "claude-code" || id === "cursor") { + return { id, install: mockInstall }; + } + return undefined; + }), + getHarnessIds: vi + .fn() + .mockReturnValue([ + "universal", + "claude-code", + "cursor", + "copilot", + "windsurf", + "cline", + "roo-code", + "kiro", + "opencode" + ]), + installSkills: vi.fn().mockResolvedValue(undefined) +})); + +import * as clack from "@clack/prompts"; +import { readLockFile } from "@ade/core"; +import { runInstall } from "./install.js"; + +// ── Tests ──────────────────────────────────────────────────────────────────── + +describe("runInstall", () => { + beforeEach(async () => { + vi.clearAllMocks(); + // Re-set the default implementation after clearAllMocks + const { getHarnessWriter } = await import("@ade/harnesses"); + vi.mocked(getHarnessWriter).mockImplementation((id: string) => { + if (id === "universal" || id === "claude-code" || id === "cursor") { + return { + id, + label: id, + description: "test", + install: mockInstall + }; + } + return undefined; + }); + }); + + it("reads config.lock.yaml and applies logical config", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + logical_config: mockLogical + }); + + await runInstall("/tmp/project"); + + expect(readLockFile).toHaveBeenCalledWith("/tmp/project"); + }); + + it("defaults to universal harness when none specified", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + logical_config: mockLogical + }); + + await runInstall("/tmp/project"); + + expect(mockInstall).toHaveBeenCalledWith(mockLogical, "/tmp/project"); + }); + + it("uses harnesses from lock file when present", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + harnesses: ["claude-code", "cursor"], + logical_config: mockLogical + }); + + await runInstall("/tmp/project"); + + expect(mockInstall).toHaveBeenCalledTimes(2); + }); + + it("uses explicit harness ids when provided", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + harnesses: ["claude-code"], + logical_config: mockLogical + }); + + await runInstall("/tmp/project", ["cursor"]); + + // Explicit takes priority over lock file + expect(mockInstall).toHaveBeenCalledTimes(1); + }); + + it("throws when config.lock.yaml is missing", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce(null); + + await expect(runInstall("/tmp/project")).rejects.toThrow( + /config\.lock\.yaml not found/i + ); + }); + + it("throws when harness id is unknown", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + logical_config: mockLogical + }); + + await expect(runInstall("/tmp/project", ["unknown-agent"])).rejects.toThrow( + /unknown harness/i + ); + }); + + it("shows intro and outro messages", async () => { + vi.mocked(readLockFile).mockResolvedValueOnce({ + version: 1, + generated_at: "2024-01-01T00:00:00.000Z", + choices: { process: "codemcp-workflows" }, + logical_config: mockLogical + }); + + await runInstall("/tmp/project"); + + expect(clack.intro).toHaveBeenCalled(); + expect(clack.outro).toHaveBeenCalled(); + }); +}); diff --git a/packages/cli/src/commands/install.ts b/packages/cli/src/commands/install.ts new file mode 100644 index 0000000..49e797a --- /dev/null +++ b/packages/cli/src/commands/install.ts @@ -0,0 +1,49 @@ +import * as clack from "@clack/prompts"; +import { readLockFile } from "@ade/core"; +import { getHarnessWriter, getHarnessIds, installSkills } from "@ade/harnesses"; + +export async function runInstall( + projectRoot: string, + harnessIds?: string[] +): Promise { + clack.intro("ade install"); + + const lockFile = await readLockFile(projectRoot); + if (!lockFile) { + throw new Error("config.lock.yaml not found. Run `ade setup` first."); + } + + // Determine which harnesses to install for: + // 1. --harness flag (comma-separated) + // 2. harnesses saved in the lock file + // 3. default: universal + const ids = harnessIds ?? lockFile.harnesses ?? ["universal"]; + + const validIds = getHarnessIds(); + for (const id of ids) { + if (!validIds.includes(id)) { + throw new Error( + `Unknown harness "${id}". Available: ${validIds.join(", ")}` + ); + } + } + + const logicalConfig = lockFile.logical_config; + + for (const id of ids) { + const writer = getHarnessWriter(id); + if (writer) { + await writer.install(logicalConfig, projectRoot); + } + } + + await installSkills(logicalConfig.skills, projectRoot); + + if (logicalConfig.knowledge_sources.length > 0) { + clack.log.info( + "Knowledge sources configured. Initialize them separately:\n npx @codemcp/knowledge init" + ); + } + + clack.outro("Install complete!"); +} diff --git a/packages/cli/src/commands/knowledge.integration.spec.ts b/packages/cli/src/commands/knowledge.integration.spec.ts new file mode 100644 index 0000000..a1a4680 --- /dev/null +++ b/packages/cli/src/commands/knowledge.integration.spec.ts @@ -0,0 +1,127 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + select: vi.fn(), + multiselect: vi.fn(), + confirm: vi.fn(), + isCancel: vi.fn().mockReturnValue(false), + cancel: vi.fn(), + log: { info: vi.fn(), warn: vi.fn() }, + spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() }) +})); + +import * as clack from "@clack/prompts"; +import { runSetup } from "./setup.js"; +import { readLockFile } from "@ade/core"; +import { getDefaultCatalog } from "../../../core/src/catalog/index.js"; + +describe("knowledge integration", () => { + let dir: string; + + beforeEach(async () => { + vi.clearAllMocks(); + dir = await mkdtemp(join(tmpdir(), "ade-knowledge-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it( + "records knowledge sources in lock file when tanstack is selected", + { timeout: 60_000 }, + async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("tanstack"); // architecture + + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce([ + "tanstack-router-docs", + "tanstack-query-docs", + "tanstack-form-docs", + "tanstack-table-docs" + ]) + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Lock file should contain knowledge_sources + const lock = await readLockFile(dir); + expect(lock!.logical_config.knowledge_sources).toHaveLength(4); + expect(lock!.logical_config.knowledge_sources.map((s) => s.name)).toEqual( + expect.arrayContaining([ + "tanstack-router-docs", + "tanstack-query-docs", + "tanstack-form-docs", + "tanstack-table-docs" + ]) + ); + + // MCP server entry for knowledge should be in .mcp.json + const mcpJson = JSON.parse( + await readFile(join(dir, ".mcp.json"), "utf-8") + ); + expect(mcpJson.mcpServers["knowledge"]).toMatchObject({ + command: "npx", + args: ["-y", "@codemcp/knowledge-server"] + }); + + // Knowledge init is deferred — user should see a hint + expect(clack.log.info).toHaveBeenCalledWith( + expect.stringContaining("npx @codemcp/knowledge init") + ); + } + ); + + it( + "excludes deselected docsets from lock file", + { timeout: 60_000 }, + async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("tanstack"); // architecture + + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["tanstack-router-docs", "tanstack-query-docs"]) + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Lock file should only have the 2 selected sources + const lock = await readLockFile(dir); + expect(lock!.logical_config.knowledge_sources).toHaveLength(2); + expect(lock!.logical_config.knowledge_sources.map((s) => s.name)).toEqual( + expect.arrayContaining(["tanstack-router-docs", "tanstack-query-docs"]) + ); + } + ); + + it("does not show knowledge hint when no docsets are implied", async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture: skip + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["tdd-london"]) // practices: no docsets + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + expect(clack.log.info).not.toHaveBeenCalledWith( + expect.stringContaining("npx @codemcp/knowledge init") + ); + }); +}); diff --git a/packages/cli/src/commands/setup.integration.spec.ts b/packages/cli/src/commands/setup.integration.spec.ts new file mode 100644 index 0000000..6e77cf2 --- /dev/null +++ b/packages/cli/src/commands/setup.integration.spec.ts @@ -0,0 +1,148 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +// Mock only the TUI — everything else (catalog, registry, resolver, config I/O) is real +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + select: vi.fn(), + multiselect: vi.fn(), + confirm: vi.fn(), + isCancel: vi.fn().mockReturnValue(false), + cancel: vi.fn(), + spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() }) +})); + +import * as clack from "@clack/prompts"; +import { runSetup } from "./setup.js"; +import { readUserConfig, readLockFile } from "@ade/core"; +import { getDefaultCatalog } from "../../../core/src/catalog/index.js"; + +describe("setup integration (real temp dir)", () => { + let dir: string; + + beforeEach(async () => { + vi.clearAllMocks(); + dir = await mkdtemp(join(tmpdir(), "ade-setup-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("writes config.yaml and config.lock.yaml for codemcp-workflows", async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // ── config.yaml ────────────────────────────────────────────────────── + const config = await readUserConfig(dir); + expect(config).not.toBeNull(); + expect(config!.choices).toEqual({ process: "codemcp-workflows" }); + + // ── config.lock.yaml ───────────────────────────────────────────────── + const lock = await readLockFile(dir); + expect(lock).not.toBeNull(); + expect(lock!.version).toBe(1); + expect(lock!.choices).toEqual({ process: "codemcp-workflows" }); + expect(lock!.generated_at).toBeTruthy(); + + // LogicalConfig was produced by the real resolver with real writers + const lc = lock!.logical_config; + expect(lc.mcp_servers).toHaveLength(1); + expect(lc.mcp_servers[0].ref).toBe("workflows"); + expect(lc.instructions.length).toBeGreaterThan(0); + + // ── Agent output: .mcp.json ───────────────────────────────────────── + const { readFile } = await import("node:fs/promises"); + const mcpJson = JSON.parse(await readFile(join(dir, ".mcp.json"), "utf-8")); + expect(mcpJson.mcpServers["workflows"]).toMatchObject({ + command: "npx", + args: ["@codemcp/workflows-server@latest"] + }); + + // ── Agent output: .claude/agents/ade.md ──────────────────────────── + const agentMd = await readFile( + join(dir, ".claude", "agents", "ade.md"), + "utf-8" + ); + expect(agentMd).toContain("Call whats_next()"); + }); + + it("writes config.yaml, lock, and AGENTS.md for native-agents-md", async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("native-agents-md") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + const config = await readUserConfig(dir); + expect(config!.choices).toEqual({ process: "native-agents-md" }); + + const lock = await readLockFile(dir); + expect(lock!.choices).toEqual({ process: "native-agents-md" }); + expect(lock!.logical_config.instructions.length).toBeGreaterThan(0); + + // Agent output: .claude/agents/ade.md is written with instruction text + const { readFile } = await import("node:fs/promises"); + const agentMd = await readFile( + join(dir, ".claude", "agents", "ade.md"), + "utf-8" + ); + expect(agentMd).toContain("AGENTS.md"); + }); + + it("does not write any files when user cancels", async () => { + const catalog = getDefaultCatalog(); + const cancelSymbol = Symbol("cancel"); + + vi.mocked(clack.select).mockResolvedValueOnce(cancelSymbol); + vi.mocked(clack.isCancel).mockReturnValue(true); + + await runSetup(dir, catalog); + + const config = await readUserConfig(dir); + expect(config).toBeNull(); + + const lock = await readLockFile(dir); + expect(lock).toBeNull(); + }); + + it("produces valid YAML that roundtrips through read", async () => { + const catalog = getDefaultCatalog(); + + vi.mocked(clack.select) + .mockResolvedValueOnce("codemcp-workflows") // process + .mockResolvedValueOnce("__skip__"); // architecture + vi.mocked(clack.multiselect) + .mockResolvedValueOnce([]) // practices: none + .mockResolvedValueOnce(["claude-code"]); // harnesses + + await runSetup(dir, catalog); + + // Read the raw file and re-parse to ensure valid YAML + const { readFile } = await import("node:fs/promises"); + const rawConfig = await readFile(join(dir, "config.yaml"), "utf-8"); + const rawLock = await readFile(join(dir, "config.lock.yaml"), "utf-8"); + + // Both files should be non-empty valid YAML (not "undefined" or empty) + expect(rawConfig.length).toBeGreaterThan(0); + expect(rawLock.length).toBeGreaterThan(0); + expect(rawConfig).toContain("codemcp-workflows"); + expect(rawLock).toContain("codemcp-workflows"); + }); +}); diff --git a/packages/cli/src/commands/setup.spec.ts b/packages/cli/src/commands/setup.spec.ts new file mode 100644 index 0000000..1c3380d --- /dev/null +++ b/packages/cli/src/commands/setup.spec.ts @@ -0,0 +1,413 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import type { Catalog, LogicalConfig } from "@ade/core"; + +// ── Mocks ──────────────────────────────────────────────────────────────────── + +vi.mock("@clack/prompts", () => ({ + intro: vi.fn(), + outro: vi.fn(), + select: vi.fn(), + multiselect: vi.fn(), + confirm: vi.fn(), + isCancel: vi.fn().mockReturnValue(false), + cancel: vi.fn(), + log: { warn: vi.fn(), info: vi.fn() }, + spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() }) +})); + +vi.mock("@ade/core", async (importOriginal) => { + const actual = (await importOriginal()) as typeof import("@ade/core"); + return { + ...actual, + readUserConfig: vi.fn().mockResolvedValue(null), + writeUserConfig: vi.fn().mockResolvedValue(undefined), + writeLockFile: vi.fn().mockResolvedValue(undefined), + resolve: vi.fn().mockResolvedValue({ + mcp_servers: [], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + } satisfies LogicalConfig), + collectDocsets: actual.collectDocsets + }; +}); + +vi.mock("@ade/harnesses", () => ({ + allHarnessWriters: [ + { + id: "claude-code", + label: "Claude Code", + description: "test", + install: vi.fn().mockResolvedValue(undefined) + } + ], + getHarnessWriter: vi.fn().mockReturnValue({ + id: "claude-code", + label: "Claude Code", + description: "test", + install: vi.fn().mockResolvedValue(undefined) + }), + getHarnessIds: vi.fn().mockReturnValue(["claude-code"]), + installSkills: vi.fn().mockResolvedValue(undefined) +})); + +import * as clack from "@clack/prompts"; +import { + readUserConfig, + writeUserConfig, + writeLockFile, + resolve +} from "@ade/core"; +import { runSetup } from "./setup.js"; + +// ── Test catalog fixture ───────────────────────────────────────────────────── + +const testCatalog: Catalog = { + facets: [ + { + id: "process", + label: "Process", + description: "How your agent works", + required: true, + options: [ + { + id: "workflow-a", + label: "Workflow A", + description: "First workflow option", + recipe: [] + }, + { + id: "workflow-b", + label: "Workflow B", + description: "Second workflow option", + recipe: [] + } + ] + }, + { + id: "testing", + label: "Testing", + description: "Testing strategy", + required: false, + options: [ + { + id: "vitest", + label: "Vitest", + description: "Use vitest", + recipe: [] + }, + { + id: "jest", + label: "Jest", + description: "Use jest", + recipe: [] + } + ] + } + ] +}; + +const docsetCatalog: Catalog = { + facets: [ + { + id: "arch", + label: "Architecture", + description: "Stack", + required: true, + options: [ + { + id: "react", + label: "React", + description: "React framework", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "Official React docs" + }, + { + id: "react-tutorial", + label: "React Tutorial", + origin: "https://github.com/reactjs/react.dev.git", + description: "React learn guide" + } + ] + } + ] + } + ] +}; + +// ── Tests ──────────────────────────────────────────────────────────────────── + +describe("runSetup", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("prompts for each catalog facet and writes user config", async () => { + // User selects "workflow-a" for process, "vitest" for testing + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + // Harness multiselect + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + // select() called once per facet + expect(clack.select).toHaveBeenCalledTimes(2); + + // writeUserConfig called with collected choices + expect(writeUserConfig).toHaveBeenCalledWith( + "/tmp/test-project", + expect.objectContaining({ + choices: { process: "workflow-a", testing: "vitest" } + }) + ); + }); + + it("resolves the config and writes the lock file", async () => { + const mockLogical: LogicalConfig = { + mcp_servers: [], + instructions: ["do stuff"], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + vi.mocked(resolve).mockResolvedValueOnce(mockLogical); + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + // resolve() called with the user config, catalog, and a registry + expect(resolve).toHaveBeenCalledOnce(); + const resolveArgs = vi.mocked(resolve).mock.calls[0]; + expect(resolveArgs[0]).toMatchObject({ + choices: { process: "workflow-a", testing: "vitest" } + }); + + // writeLockFile called with the resolved logical config + expect(writeLockFile).toHaveBeenCalledWith( + "/tmp/test-project", + expect.objectContaining({ + version: 1, + logical_config: mockLogical + }) + ); + }); + + it("excludes skipped facets from choices", async () => { + // User selects workflow-a for process, skips testing (returns null sentinel) + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("__skip__"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + expect(writeUserConfig).toHaveBeenCalledWith( + "/tmp/test-project", + expect.objectContaining({ + choices: { process: "workflow-a" } + }) + ); + }); + + it("aborts without writing files when user cancels", async () => { + // First select returns a cancel symbol + const cancelSymbol = Symbol("cancel"); + vi.mocked(clack.select).mockResolvedValueOnce(cancelSymbol); + vi.mocked(clack.isCancel).mockReturnValue(true); + + await runSetup("/tmp/test-project", testCatalog); + + expect(writeUserConfig).not.toHaveBeenCalled(); + expect(writeLockFile).not.toHaveBeenCalled(); + expect(clack.cancel).toHaveBeenCalled(); + }); + + describe("docset confirmation step", () => { + it("presents implied docsets as a multiselect after facet selection", async () => { + vi.mocked(clack.select).mockResolvedValueOnce("react"); + // User accepts all docsets (returns all ids), then harness selection + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["react-docs", "react-tutorial"]) + .mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", docsetCatalog); + + // multiselect should have been called for docsets + expect(clack.multiselect).toHaveBeenCalledWith( + expect.objectContaining({ + message: expect.stringContaining("Documentation") + }) + ); + }); + + it("stores deselected docsets as excluded_docsets in user config", async () => { + vi.mocked(clack.select).mockResolvedValueOnce("react"); + // User deselects react-tutorial, keeps only react-docs; then harness + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["react-docs"]) + .mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", docsetCatalog); + + expect(writeUserConfig).toHaveBeenCalledWith( + "/tmp/test-project", + expect.objectContaining({ + excluded_docsets: ["react-tutorial"] + }) + ); + }); + + it("does not set excluded_docsets when all docsets are accepted", async () => { + vi.mocked(clack.select).mockResolvedValueOnce("react"); + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["react-docs", "react-tutorial"]) + .mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", docsetCatalog); + + const configArg = vi.mocked(writeUserConfig).mock.calls[0][1]; + expect(configArg.excluded_docsets).toBeUndefined(); + }); + + it("skips docset prompt when no options have docsets", async () => { + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + // Only the harness multiselect should be called (no docsets in testCatalog) + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + // multiselect should have been called exactly once (for harnesses only) + expect(clack.multiselect).toHaveBeenCalledTimes(1); + expect(clack.multiselect).toHaveBeenCalledWith( + expect.objectContaining({ + message: expect.stringContaining("Harnesses") + }) + ); + }); + }); + + it("calls intro and outro from @clack/prompts", async () => { + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + expect(clack.intro).toHaveBeenCalled(); + expect(clack.outro).toHaveBeenCalled(); + }); + + describe("re-run with existing config", () => { + it("passes existing single-select choice as initialValue", async () => { + vi.mocked(readUserConfig).mockResolvedValueOnce({ + choices: { process: "workflow-b", testing: "jest" } + }); + + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-b") + .mockResolvedValueOnce("jest"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + // First select (process) should receive initialValue "workflow-b" + expect(clack.select).toHaveBeenCalledWith( + expect.objectContaining({ initialValue: "workflow-b" }) + ); + // Second select (testing) should receive initialValue "jest" + expect(clack.select).toHaveBeenCalledWith( + expect.objectContaining({ initialValue: "jest" }) + ); + }); + + it("passes existing multi-select choices as initialValues", async () => { + const multiCatalog: Catalog = { + facets: [ + { + id: "practices", + label: "Practices", + description: "Dev practices", + required: false, + multiSelect: true, + options: [ + { + id: "tdd", + label: "TDD", + description: "Test-driven dev", + recipe: [] + }, + { + id: "adr", + label: "ADR", + description: "Architecture decisions", + recipe: [] + } + ] + } + ] + }; + + vi.mocked(readUserConfig).mockResolvedValueOnce({ + choices: { practices: ["tdd", "adr"] } + }); + + vi.mocked(clack.multiselect) + .mockResolvedValueOnce(["tdd", "adr"]) + .mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", multiCatalog); + + expect(clack.multiselect).toHaveBeenCalledWith( + expect.objectContaining({ initialValues: ["tdd", "adr"] }) + ); + }); + + it("warns when existing choice references a stale option", async () => { + vi.mocked(readUserConfig).mockResolvedValueOnce({ + choices: { process: "workflow-a", testing: "mocha" } // "mocha" doesn't exist + }); + + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + expect(clack.log.warn).toHaveBeenCalledWith( + expect.stringContaining("mocha") + ); + }); + + it("does not set initialValue for stale option", async () => { + vi.mocked(readUserConfig).mockResolvedValueOnce({ + choices: { process: "deleted-option" } + }); + + vi.mocked(clack.select) + .mockResolvedValueOnce("workflow-a") + .mockResolvedValueOnce("vitest"); + vi.mocked(clack.multiselect).mockResolvedValueOnce(["claude-code"]); + + await runSetup("/tmp/test-project", testCatalog); + + // First select (process) should NOT have initialValue set + const firstCall = vi.mocked(clack.select).mock.calls[0][0]; + expect(firstCall).not.toHaveProperty("initialValue"); + }); + }); +}); diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts new file mode 100644 index 0000000..bd464e7 --- /dev/null +++ b/packages/cli/src/commands/setup.ts @@ -0,0 +1,229 @@ +import * as clack from "@clack/prompts"; +import { + type Catalog, + type Facet, + type UserConfig, + type LockFile, + readUserConfig, + writeUserConfig, + writeLockFile, + resolve, + collectDocsets, + createDefaultRegistry, + getFacet, + getOption +} from "@ade/core"; +import { + allHarnessWriters, + getHarnessWriter, + installSkills +} from "@ade/harnesses"; + +export async function runSetup( + projectRoot: string, + catalog: Catalog +): Promise { + clack.intro("ade setup"); + + const existingConfig = await readUserConfig(projectRoot); + const existingChoices = existingConfig?.choices ?? {}; + + // Warn about stale choices that reference options no longer in the catalog + for (const [facetId, value] of Object.entries(existingChoices)) { + const facet = getFacet(catalog, facetId); + if (!facet) continue; + + const ids = Array.isArray(value) ? value : [value]; + for (const optionId of ids) { + if (!getOption(facet, optionId)) { + clack.log.warn( + `Previously selected option "${optionId}" is no longer available in facet "${facet.label}".` + ); + } + } + } + + const choices: Record = {}; + + for (const facet of catalog.facets) { + if (facet.multiSelect) { + const selected = await promptMultiSelect(facet, existingChoices); + if (typeof selected === "symbol") { + clack.cancel("Setup cancelled."); + return; + } + if (selected.length > 0) { + choices[facet.id] = selected; + } + } else { + const selected = await promptSelect(facet, existingChoices); + if (typeof selected === "symbol") { + clack.cancel("Setup cancelled."); + return; + } + if (selected !== "__skip__") { + choices[facet.id] = selected as string; + } + } + } + + // Docset confirmation step: collect implied docsets, let user deselect + const impliedDocsets = collectDocsets(choices, catalog); + let excludedDocsets: string[] | undefined; + + if (impliedDocsets.length > 0) { + const selected = await clack.multiselect({ + message: "Documentation — deselect any you don't need", + options: impliedDocsets.map((d) => ({ + value: d.id, + label: d.label, + hint: d.description + })), + initialValues: impliedDocsets.map((d) => d.id), + required: false + }); + + if (typeof selected === "symbol") { + clack.cancel("Setup cancelled."); + return; + } + + const selectedSet = new Set(selected as string[]); + const excluded = impliedDocsets + .filter((d) => !selectedSet.has(d.id)) + .map((d) => d.id); + if (excluded.length > 0) { + excludedDocsets = excluded; + } + } + + // Harness selection — multi-select from all available harnesses + const existingHarnesses = existingConfig?.harnesses; + const harnessOptions = allHarnessWriters.map((w) => ({ + value: w.id, + label: w.label, + hint: w.description + })); + + const validExistingHarnesses = existingHarnesses?.filter((h) => + allHarnessWriters.some((w) => w.id === h) + ); + + const selectedHarnesses = await clack.multiselect({ + message: "Harnesses — which coding agents should receive config?", + options: harnessOptions, + initialValues: + validExistingHarnesses && validExistingHarnesses.length > 0 + ? validExistingHarnesses + : ["universal"], + required: false + }); + + if (typeof selectedHarnesses === "symbol") { + clack.cancel("Setup cancelled."); + return; + } + + const harnesses = selectedHarnesses as string[]; + + const userConfig: UserConfig = { + choices, + ...(excludedDocsets && { excluded_docsets: excludedDocsets }), + ...(harnesses.length > 0 && { harnesses }) + }; + const registry = createDefaultRegistry(); + const logicalConfig = await resolve(userConfig, catalog, registry); + + await writeUserConfig(projectRoot, userConfig); + + const lockFile: LockFile = { + version: 1, + generated_at: new Date().toISOString(), + choices: userConfig.choices, + ...(harnesses.length > 0 && { harnesses }), + logical_config: logicalConfig + }; + await writeLockFile(projectRoot, lockFile); + + // Install to all selected harnesses + for (const harnessId of harnesses) { + const writer = getHarnessWriter(harnessId); + if (writer) { + await writer.install(logicalConfig, projectRoot); + } + } + + await installSkills(logicalConfig.skills, projectRoot); + + if (logicalConfig.knowledge_sources.length > 0) { + clack.log.info( + "Knowledge sources selected. Initialize them separately:\n npx @codemcp/knowledge init" + ); + } + + clack.outro("Setup complete!"); +} + +function getValidInitialValue( + facet: Facet, + existingChoices: Record +): string | undefined { + const value = existingChoices[facet.id]; + if (typeof value !== "string") return undefined; + // Only set initialValue if the option still exists in the catalog + return facet.options.some((o) => o.id === value) ? value : undefined; +} + +function getValidInitialValues( + facet: Facet, + existingChoices: Record +): string[] | undefined { + const value = existingChoices[facet.id]; + if (!Array.isArray(value)) return undefined; + // Only include options that still exist in the catalog + const valid = value.filter((v) => facet.options.some((o) => o.id === v)); + return valid.length > 0 ? valid : undefined; +} + +function promptSelect( + facet: Facet, + existingChoices: Record +) { + const options = facet.options.map((o) => ({ + value: o.id, + label: o.label, + hint: o.description + })); + + if (!facet.required) { + options.push({ value: "__skip__", label: "Skip", hint: "" }); + } + + const initialValue = getValidInitialValue(facet, existingChoices); + + return clack.select({ + message: facet.label, + options, + ...(initialValue !== undefined && { initialValue }) + }); +} + +function promptMultiSelect( + facet: Facet, + existingChoices: Record +) { + const options = facet.options.map((o) => ({ + value: o.id, + label: o.label, + hint: o.description + })); + + const initialValues = getValidInitialValues(facet, existingChoices); + + return clack.multiselect({ + message: facet.label, + options, + required: false, + ...(initialValues !== undefined && { initialValues }) + }); +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 0000000..e49ffa1 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,52 @@ +#!/usr/bin/env node + +import { version } from "./version.js"; +import { runSetup } from "./commands/setup.js"; +import { runInstall } from "./commands/install.js"; +import { getDefaultCatalog } from "@ade/core"; +import { getHarnessIds } from "@ade/harnesses"; + +const args = process.argv.slice(2); +const command = args[0]; + +if (command === "setup") { + const projectRoot = args[1] ?? process.cwd(); + const catalog = getDefaultCatalog(); + await runSetup(projectRoot, catalog); +} else if (command === "install") { + const projectRoot = args[1] ?? process.cwd(); + + let harnessIds: string[] | undefined; + + // Support --harness flag (comma-separated) + if (args.includes("--harness")) { + const val = args[args.indexOf("--harness") + 1]; + if (val) { + harnessIds = val.split(",").map((s) => s.trim()); + } + } + + await runInstall(projectRoot, harnessIds); +} else if (command === "--version" || command === "-v") { + console.log(version); +} else { + const allIds = getHarnessIds(); + console.log(`ade v${version}`); + console.log(); + console.log("Usage: ade [options]"); + console.log(); + console.log("Commands:"); + console.log( + " setup [dir] Interactive setup wizard (re-run to change selections)" + ); + console.log( + " install [dir] Apply lock file to generate agent files (idempotent)" + ); + console.log(); + console.log("Options:"); + console.log( + ` --harness Comma-separated harnesses (${allIds.join(", ")})` + ); + console.log(" -v, --version Show version"); + process.exitCode = command ? 1 : 0; +} diff --git a/packages/cli/src/knowledge-installer.spec.ts b/packages/cli/src/knowledge-installer.spec.ts new file mode 100644 index 0000000..c00bb32 --- /dev/null +++ b/packages/cli/src/knowledge-installer.spec.ts @@ -0,0 +1,111 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import type { KnowledgeSource } from "@ade/core"; + +vi.mock("@codemcp/knowledge/packages/cli/dist/exports.js", () => ({ + createDocset: vi.fn().mockResolvedValue({ + docset: {}, + configPath: ".knowledge/config.yaml", + configCreated: false + }), + initDocset: vi.fn().mockResolvedValue({ alreadyInitialized: false }) +})); + +import { + createDocset, + initDocset +} from "@codemcp/knowledge/packages/cli/dist/exports.js"; +import { installKnowledge } from "./knowledge-installer.js"; + +describe("installKnowledge", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("does nothing when knowledge_sources is empty", async () => { + await installKnowledge([], "/tmp/project"); + + expect(createDocset).not.toHaveBeenCalled(); + expect(initDocset).not.toHaveBeenCalled(); + }); + + it("calls createDocset for each knowledge source", async () => { + const sources: KnowledgeSource[] = [ + { + name: "react-docs", + origin: "https://github.com/facebook/react.git", + description: "React documentation" + }, + { + name: "tanstack-query-docs", + origin: "https://github.com/TanStack/query.git", + description: "TanStack Query docs" + } + ]; + + await installKnowledge(sources, "/tmp/project"); + + expect(createDocset).toHaveBeenCalledTimes(2); + expect(createDocset).toHaveBeenCalledWith( + expect.objectContaining({ + id: "react-docs", + name: "React documentation", + preset: "git-repo", + url: "https://github.com/facebook/react.git" + }), + expect.objectContaining({ cwd: "/tmp/project" }) + ); + }); + + it("calls initDocset for each knowledge source after creation", async () => { + const sources: KnowledgeSource[] = [ + { + name: "react-docs", + origin: "https://github.com/facebook/react.git", + description: "React documentation" + } + ]; + + await installKnowledge(sources, "/tmp/project"); + + expect(initDocset).toHaveBeenCalledTimes(1); + expect(initDocset).toHaveBeenCalledWith( + expect.objectContaining({ + docsetId: "react-docs", + cwd: "/tmp/project" + }) + ); + }); + + it("continues with remaining sources when one fails", async () => { + vi.mocked(createDocset) + .mockRejectedValueOnce(new Error("already exists")) + .mockResolvedValueOnce({ + docset: {}, + configPath: ".knowledge/config.yaml", + configCreated: false + }); + + const sources: KnowledgeSource[] = [ + { + name: "failing", + origin: "https://github.com/fail/fail.git", + description: "Will fail" + }, + { + name: "succeeding", + origin: "https://github.com/ok/ok.git", + description: "Will succeed" + } + ]; + + await installKnowledge(sources, "/tmp/project"); + + // Should have attempted both + expect(createDocset).toHaveBeenCalledTimes(2); + // initDocset only called for the successful one + expect(initDocset).toHaveBeenCalledTimes(1); + expect(initDocset).toHaveBeenCalledWith( + expect.objectContaining({ docsetId: "succeeding" }) + ); + }); +}); diff --git a/packages/cli/src/knowledge-installer.ts b/packages/cli/src/knowledge-installer.ts new file mode 100644 index 0000000..bfabd80 --- /dev/null +++ b/packages/cli/src/knowledge-installer.ts @@ -0,0 +1,54 @@ +import type { KnowledgeSource } from "@ade/core"; +import { + createDocset, + initDocset +} from "@codemcp/knowledge/packages/cli/dist/exports.js"; + +/** + * Install knowledge sources using the @codemcp/knowledge programmatic API. + * + * For each knowledge source: + * 1. Creates a docset config entry via `createDocset` + * 2. Initializes (downloads) the docset via `initDocset` + * + * Errors on individual sources are logged and skipped so that one failure + * doesn't block the rest. + */ +export async function installKnowledge( + sources: KnowledgeSource[], + projectRoot: string +): Promise { + if (sources.length === 0) return; + + for (const source of sources) { + try { + await createDocset( + { + id: source.name, + name: source.description, + preset: "git-repo" as const, + url: source.origin + }, + { cwd: projectRoot } + ); + } catch (err) { + console.warn( + `Warning: failed to create docset "${source.name}":`, + err instanceof Error ? err.message : err + ); + continue; + } + + try { + await initDocset({ + docsetId: source.name, + cwd: projectRoot + }); + } catch (err) { + console.warn( + `Warning: failed to initialize docset "${source.name}":`, + err instanceof Error ? err.message : err + ); + } + } +} diff --git a/packages/cli/src/version.ts b/packages/cli/src/version.ts new file mode 100644 index 0000000..64d1c68 --- /dev/null +++ b/packages/cli/src/version.ts @@ -0,0 +1 @@ +export const version = "0.0.0-development"; diff --git a/packages/cli/tsconfig.build.json b/packages/cli/tsconfig.build.json new file mode 100644 index 0000000..7cbd949 --- /dev/null +++ b/packages/cli/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts"] +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000..d905c61 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@ade/core": ["../core/src/index.ts"] + } + }, + "include": ["src/**/*"] +} diff --git a/packages/cli/tsconfig.vitest.json b/packages/cli/tsconfig.vitest.json new file mode 100644 index 0000000..f8add23 --- /dev/null +++ b/packages/cli/tsconfig.vitest.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "moduleResolution": "bundler" + }, + "include": ["vitest.config.ts"] +} diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts new file mode 100644 index 0000000..30c6dca --- /dev/null +++ b/packages/cli/vitest.config.ts @@ -0,0 +1,14 @@ +// @ts-check +/** @type {import("vitest/config").defineConfig} */ + +import { resolve } from "path"; +const baseConfig = await import("../../vitest.config.js"); + +export default { + ...baseConfig.default, + resolve: { + alias: { + "@ade/core": resolve(__dirname, "../core/src/index.ts") + } + } +}; diff --git a/packages/core/.prettierignore b/packages/core/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/packages/core/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/packages/core/eslint.config.mjs b/packages/core/eslint.config.mjs new file mode 100644 index 0000000..1483555 --- /dev/null +++ b/packages/core/eslint.config.mjs @@ -0,0 +1,40 @@ +import js from "@eslint/js"; +import { parser, configs } from "typescript-eslint"; +import prettier from "eslint-config-prettier"; + +export default [ + js.configs.recommended, + ...configs.recommended, + prettier, + { + // Config for TypeScript files + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser, + parserOptions: { + project: ["./tsconfig.json", "./tsconfig.vitest.json"] + } + } + }, + { + // Config for JavaScript files - no TypeScript parsing + files: ["**/*.{js,jsx}"], + ...js.configs.recommended + }, + { + // Relaxed rules for test files + files: ["**/*.test.ts", "**/*.spec.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off" + } + }, + { + ignores: [ + "**/node_modules/**", + "**/dist/**", + ".pnpm-store/**", + "pnpm-lock.yaml" + ] + } +]; diff --git a/packages/core/nodemon.json b/packages/core/nodemon.json new file mode 100644 index 0000000..e5d466d --- /dev/null +++ b/packages/core/nodemon.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json.schemastore.org/nodemon.json", + "watch": ["./src/**", "./node_modules/@mme/**/dist/**"], + "ignoreRoot": [], + "ext": "ts,js", + "exec": "pnpm typecheck && pnpm build" +} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..7547de7 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,33 @@ +{ + "name": "@ade/core", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clean:build": "rimraf ./dist", + "dev": "nodemon", + "lint": "eslint .", + "lint:fix": "eslint --fix .", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "test": "vitest --run", + "test:watch": "vitest", + "typecheck": "tsc" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "typescript": "^5.7.3" + }, + "dependencies": { + "yaml": "^2.8.2" + } +} diff --git a/packages/core/src/catalog/catalog.spec.ts b/packages/core/src/catalog/catalog.spec.ts new file mode 100644 index 0000000..9fbaaf1 --- /dev/null +++ b/packages/core/src/catalog/catalog.spec.ts @@ -0,0 +1,194 @@ +import { describe, it, expect } from "vitest"; +import { getDefaultCatalog, getFacet, getOption } from "./index.js"; +import { createDefaultRegistry, getProvisionWriter } from "../registry.js"; + +describe("catalog", () => { + describe("getDefaultCatalog", () => { + it("returns a catalog containing at least the 'process' facet", () => { + const catalog = getDefaultCatalog(); + const process = getFacet(catalog, "process"); + expect(process).toBeDefined(); + expect(process!.id).toBe("process"); + }); + }); + + describe("getFacet / getOption", () => { + it("process facet's 'codemcp-workflows' option has a recipe referencing the 'workflows' writer", () => { + const catalog = getDefaultCatalog(); + const process = getFacet(catalog, "process")!; + const option = getOption(process, "codemcp-workflows"); + + expect(option).toBeDefined(); + expect(option!.recipe.some((p) => p.writer === "workflows")).toBe(true); + }); + + it("process facet's 'native-agents-md' option has a recipe referencing only the 'instruction' writer", () => { + const catalog = getDefaultCatalog(); + const process = getFacet(catalog, "process")!; + const option = getOption(process, "native-agents-md"); + + expect(option).toBeDefined(); + const writers = option!.recipe.map((p) => p.writer); + expect(writers).toEqual(["instruction"]); + }); + + it("returns undefined for a nonexistent facet id", () => { + const catalog = getDefaultCatalog(); + expect(getFacet(catalog, "nonexistent")).toBeUndefined(); + }); + + it("returns undefined for a nonexistent option id", () => { + const catalog = getDefaultCatalog(); + const process = getFacet(catalog, "process")!; + expect(getOption(process, "nonexistent")).toBeUndefined(); + }); + }); + + describe("architecture facet", () => { + it("exists in the default catalog", () => { + const catalog = getDefaultCatalog(); + const architecture = getFacet(catalog, "architecture"); + expect(architecture).toBeDefined(); + expect(architecture!.required).toBe(false); + }); + + it("is single-select", () => { + const catalog = getDefaultCatalog(); + const architecture = getFacet(catalog, "architecture")!; + expect(architecture.multiSelect).toBe(false); + }); + + it("has tanstack option with skills for architecture, design, code, testing, and playwright", () => { + const catalog = getDefaultCatalog(); + const architecture = getFacet(catalog, "architecture")!; + const tanstack = getOption(architecture, "tanstack"); + + expect(tanstack).toBeDefined(); + const skillsProvisions = tanstack!.recipe.filter( + (p) => p.writer === "skills" + ); + expect(skillsProvisions).toHaveLength(1); + + const skills = ( + skillsProvisions[0].config as { skills: { name: string }[] } + ).skills; + const names = skills.map((s) => s.name); + expect(names).toContain("tanstack-architecture"); + expect(names).toContain("tanstack-design"); + expect(names).toContain("tanstack-code"); + expect(names).toContain("tanstack-testing"); + expect(names).toContain("playwright-cli"); + + // playwright-cli should be an external skill (has source, no body) + const playwright = skills.find( + (s: Record) => s.name === "playwright-cli" + ) as Record; + expect(playwright.source).toBe( + "microsoft/playwright-cli/skills/playwright-cli" + ); + expect(playwright).not.toHaveProperty("body"); + }); + }); + + describe("architecture facet docsets", () => { + it("tanstack option declares docsets for Router, Query, Form, and Table", () => { + const catalog = getDefaultCatalog(); + const architecture = getFacet(catalog, "architecture")!; + const tanstack = getOption(architecture, "tanstack")!; + + expect(tanstack.docsets).toBeDefined(); + const ids = tanstack.docsets!.map((d) => d.id); + expect(ids).toContain("tanstack-router-docs"); + expect(ids).toContain("tanstack-query-docs"); + expect(ids).toContain("tanstack-form-docs"); + expect(ids).toContain("tanstack-table-docs"); + }); + + it("each docset has required fields", () => { + const catalog = getDefaultCatalog(); + const architecture = getFacet(catalog, "architecture")!; + const tanstack = getOption(architecture, "tanstack")!; + + for (const docset of tanstack.docsets!) { + expect(docset.id).toBeTruthy(); + expect(docset.label).toBeTruthy(); + expect(docset.origin).toMatch(/^https:\/\//); + expect(docset.description).toBeTruthy(); + } + }); + }); + + describe("practices facet", () => { + it("exists in the default catalog", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices"); + expect(practices).toBeDefined(); + expect(practices!.required).toBe(false); + }); + + it("is multi-select", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices")!; + expect(practices.multiSelect).toBe(true); + }); + + it("has conventional-commits option with a single skill", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices")!; + const option = getOption(practices, "conventional-commits"); + + expect(option).toBeDefined(); + const skills = ( + option!.recipe.find((p) => p.writer === "skills")!.config as { + skills: { name: string }[]; + } + ).skills; + expect(skills).toHaveLength(1); + expect(skills[0].name).toBe("conventional-commits"); + }); + + it("conventional-commits option declares the spec docset", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices")!; + const option = getOption(practices, "conventional-commits")!; + + expect(option.docsets).toBeDefined(); + expect(option.docsets).toHaveLength(1); + expect(option.docsets![0].id).toBe("conventional-commits-spec"); + }); + + it("has tdd-london option with a single skill", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices")!; + const option = getOption(practices, "tdd-london"); + + expect(option).toBeDefined(); + }); + + it("has adr-nygard option with a single skill", () => { + const catalog = getDefaultCatalog(); + const practices = getFacet(catalog, "practices")!; + const option = getOption(practices, "adr-nygard"); + + expect(option).toBeDefined(); + }); + }); + + describe("catalog + registry integration", () => { + it("every recipe provision references a writer that exists in the default registry", () => { + const catalog = getDefaultCatalog(); + const registry = createDefaultRegistry(); + + for (const facet of catalog.facets) { + for (const option of facet.options) { + for (const provision of option.recipe) { + expect( + getProvisionWriter(registry, provision.writer), + `writer "${provision.writer}" referenced in ${facet.id}/${option.id} must exist in default registry` + ).toBeDefined(); + } + } + } + }); + }); +}); diff --git a/packages/core/src/catalog/facets/architecture.ts b/packages/core/src/catalog/facets/architecture.ts new file mode 100644 index 0000000..5462250 --- /dev/null +++ b/packages/core/src/catalog/facets/architecture.ts @@ -0,0 +1,148 @@ +import type { Facet } from "../../types.js"; + +export const architectureFacet: Facet = { + id: "architecture", + label: "Architecture", + description: + "Stack and framework conventions that shape your project structure", + required: false, + multiSelect: false, + options: [ + { + id: "tanstack", + label: "TanStack", + description: + "Full-stack conventions for TanStack (Router, Query, Form, Table)", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "tanstack-architecture", + description: + "Architecture conventions for TanStack applications", + body: [ + "# TanStack Architecture Conventions", + "", + "## Project Structure", + "- Use file-based routing with TanStack Router (`routes/` directory)", + "- Colocate route components with their loaders and actions", + "- Organize by feature, not by type (e.g. `features/auth/`, not `components/auth/`)", + "", + "## Data Flow", + "- Use TanStack Query for all server state management", + "- Use TanStack Router loaders for route-level data requirements", + "- Keep client state minimal — prefer server state via Query", + "- Use `queryOptions()` factory pattern for reusable query definitions", + "", + "## Module Boundaries", + "- Each feature exports a public API via `index.ts`", + "- Features must not import from other features' internals", + "- Shared code goes in `lib/` or `shared/`" + ].join("\n") + }, + { + name: "tanstack-design", + description: "Design patterns for TanStack applications", + body: [ + "# TanStack Design Patterns", + "", + "## Query Patterns", + "- Define query options as standalone functions: `export const userQueryOptions = (id: string) => queryOptions({ queryKey: ['user', id], queryFn: () => fetchUser(id) })`", + "- Use `useSuspenseQuery` in route components paired with `loader` for prefetching", + "- Use `useMutation` with `onSettled` for cache invalidation", + "", + "## Router Patterns", + "- Define routes using `createFileRoute` for type-safe file-based routing", + "- Use `beforeLoad` for auth guards and redirects", + "- Use search params validation with `zodSearchValidator` for type-safe URL state", + "", + "## Form Patterns", + "- Use TanStack Form with Zod validators for form state and validation", + "- Prefer field-level validation over form-level where possible", + "- Connect form submission to `useMutation` for server sync" + ].join("\n") + }, + { + name: "tanstack-code", + description: "Code style conventions for TanStack applications", + body: [ + "# TanStack Code Conventions", + "", + "## TypeScript", + "- Enable strict mode in tsconfig", + "- Infer types from TanStack APIs rather than writing manual type annotations", + "- Use `satisfies` operator for type-safe object literals", + "", + "## Naming", + "- Query keys: `['entity', ...params]` (e.g. `['user', userId]`)", + "- Query option factories: `entityQueryOptions` (e.g. `userQueryOptions`)", + "- Route files: `$param` for dynamic segments (e.g. `users/$userId.tsx`)", + "- Loaders: export as named `loader` from route file", + "", + "## Imports", + "- Import from `@tanstack/react-query`, `@tanstack/react-router`, etc.", + "- Never import internal modules from TanStack packages", + "- Use path aliases for project imports (`@/features/...`)" + ].join("\n") + }, + { + name: "tanstack-testing", + description: "Testing conventions for TanStack applications", + body: [ + "# TanStack Testing Conventions", + "", + "## Query Testing", + "- Wrap components in `QueryClientProvider` with a fresh `QueryClient` per test", + "- Use `@testing-library/react` with `renderHook` for testing custom query hooks", + "- Mock at the network level with MSW, not at the query level", + "", + "## Router Testing", + "- Use `createMemoryHistory` and `createRouter` for route testing", + "- Test route loaders independently as plain async functions", + "- Test search param validation with unit tests on the validator schema", + "", + "## Integration Tests", + "- Test full user flows through route transitions", + "- Assert on visible UI state, not internal query cache state", + "- Use `waitFor` for async query resolution in component tests" + ].join("\n") + }, + { + name: "playwright-cli", + source: "microsoft/playwright-cli/skills/playwright-cli" + } + ] + } + } + ], + docsets: [ + { + id: "tanstack-router-docs", + label: "TanStack Router", + origin: "https://github.com/TanStack/router.git", + description: "File-based routing, loaders, and search params" + }, + { + id: "tanstack-query-docs", + label: "TanStack Query", + origin: "https://github.com/TanStack/query.git", + description: "Server state management, caching, and mutations" + }, + { + id: "tanstack-form-docs", + label: "TanStack Form", + origin: "https://github.com/TanStack/form.git", + description: "Type-safe form state and validation" + }, + { + id: "tanstack-table-docs", + label: "TanStack Table", + origin: "https://github.com/TanStack/table.git", + description: "Headless table and datagrid utilities" + } + ] + } + ] +}; diff --git a/packages/core/src/catalog/facets/practices.ts b/packages/core/src/catalog/facets/practices.ts new file mode 100644 index 0000000..51d1ef9 --- /dev/null +++ b/packages/core/src/catalog/facets/practices.ts @@ -0,0 +1,173 @@ +import type { Facet } from "../../types.js"; + +export const practicesFacet: Facet = { + id: "practices", + label: "Practices", + description: + "Composable development practices — mix and match regardless of stack", + required: false, + multiSelect: true, + options: [ + { + id: "conventional-commits", + label: "Conventional Commits", + description: + "Structured commit messages following the Conventional Commits specification", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "conventional-commits", + description: + "Conventional Commits specification for structured commit messages", + body: [ + "# Conventional Commits", + "", + "## Format", + "```", + "[optional scope]: ", + "", + "[optional body]", + "", + "[optional footer(s)]", + "```", + "", + "## Types", + "- `feat`: A new feature (correlates with MINOR in SemVer)", + "- `fix`: A bug fix (correlates with PATCH in SemVer)", + "- `docs`: Documentation only changes", + "- `style`: Changes that do not affect the meaning of the code", + "- `refactor`: A code change that neither fixes a bug nor adds a feature", + "- `perf`: A code change that improves performance", + "- `test`: Adding missing tests or correcting existing tests", + "- `chore`: Changes to the build process or auxiliary tools", + "", + "## Rules", + "- Subject line must not exceed 72 characters", + '- Use imperative mood in the subject line ("add" not "added")', + "- Do not end the subject line with a period", + "- Separate subject from body with a blank line", + "- Use the body to explain what and why, not how", + "- `BREAKING CHANGE:` footer or `!` after type/scope for breaking changes" + ].join("\n") + } + ] + } + } + ], + docsets: [ + { + id: "conventional-commits-spec", + label: "Conventional Commits Spec", + origin: + "https://github.com/conventional-commits/conventionalcommits.org.git", + description: "The Conventional Commits specification" + } + ] + }, + { + id: "tdd-london", + label: "TDD (London Style)", + description: + "Test-Driven Development using the London school (mockist) approach", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "tdd-london", + description: + "London-school TDD methodology with outside-in design", + body: [ + "# TDD — London Style (Mockist)", + "", + "## Core Cycle", + "1. **Red** — Write a failing test for the next behavior", + "2. **Green** — Write the minimum code to make the test pass", + "3. **Refactor** — Improve the code while keeping tests green", + "", + "## London School Principles", + "- Work **outside-in**: start from the outermost layer (API / UI) and drive inward", + "- **Mock collaborators**: each unit test isolates the unit under test by mocking its direct dependencies", + "- Discover interfaces through tests — let the test define the collaborator contract before implementing it", + "- Prefer **role-based interfaces** over concrete classes", + "", + "## Test Structure", + "- **Arrange**: Set up mocks and the unit under test", + "- **Act**: Call the method being tested", + "- **Assert**: Verify the unit's output and interactions with mocks", + "", + "## Guidelines", + "- One logical assertion per test", + '- Test names describe behavior, not methods (e.g. "notifies user when order is placed")', + "- Only mock types you own — wrap third-party APIs in adapters and mock those", + "- Use the test doubles: stubs for queries, mocks for commands", + "- Do not test implementation details — test observable behavior", + "- Refactor step is mandatory, not optional" + ].join("\n") + } + ] + } + } + ] + }, + { + id: "adr-nygard", + label: "ADR (Nygard)", + description: + "Architecture Decision Records following Michael Nygard's template", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "adr-nygard", + description: + "Architecture Decision Records following Nygard's lightweight template", + body: [ + "# Architecture Decision Records (Nygard)", + "", + "## When to Write an ADR", + "- When making a significant architectural decision", + "- When choosing between multiple viable options", + "- When the decision will be hard to reverse", + '- When future developers will ask "why did we do this?"', + "", + "## Template", + "Store ADRs in `docs/adr/` as numbered markdown files: `NNNN-title-with-dashes.md`", + "", + "```markdown", + "# N. Title", + "", + "## Status", + "Proposed | Accepted | Deprecated | Superseded by [ADR-NNNN]", + "", + "## Context", + "What is the issue that we're seeing that is motivating this decision or change?", + "", + "## Decision", + "What is the change that we're proposing and/or doing?", + "", + "## Consequences", + "What becomes easier or more difficult to do because of this change?", + "```", + "", + "## Rules", + "- ADRs are immutable once accepted — supersede, don't edit", + "- Keep context focused on forces at play at the time of the decision", + "- Write consequences as both positive and negative impacts", + "- Number sequentially, never reuse numbers", + '- Title should be a short noun phrase (e.g. "Use PostgreSQL for persistence")' + ].join("\n") + } + ] + } + } + ] + } + ] +}; diff --git a/packages/core/src/catalog/facets/process.ts b/packages/core/src/catalog/facets/process.ts new file mode 100644 index 0000000..4274c3d --- /dev/null +++ b/packages/core/src/catalog/facets/process.ts @@ -0,0 +1,50 @@ +import type { Facet } from "../../types.js"; + +export const processFacet: Facet = { + id: "process", + label: "Process", + description: "How your AI agent receives and executes tasks", + required: true, + options: [ + { + id: "codemcp-workflows", + label: "CodeMCP Workflows", + description: + "Use @codemcp/workflows to drive agent tasks with structured engineering workflows", + recipe: [ + { + writer: "workflows", + config: { + package: "@codemcp/workflows-server@latest", + ref: "workflows" + } + }, + { + writer: "instruction", + config: { + text: [ + "You are an AI assistant that helps users develop software features using the workflows server.", + "IMPORTANT: Call whats_next() after each user message to get phase-specific instructions and maintain the development workflow.", + 'Each tool call returns a JSON response with an "instructions" field. Follow these instructions immediately after you receive them.', + "Use the development plan which you will retrieve via whats_next() to record important insights and decisions as per the structure of the plan.", + "Do not use your own task management tools." + ].join("\n") + } + } + ] + }, + { + id: "native-agents-md", + label: "Native agents.md", + description: "Use a plain agents.md instruction file", + recipe: [ + { + writer: "instruction", + config: { + text: "Read AGENTS.md for project conventions and task instructions." + } + } + ] + } + ] +}; diff --git a/packages/core/src/catalog/index.ts b/packages/core/src/catalog/index.ts new file mode 100644 index 0000000..6d17d81 --- /dev/null +++ b/packages/core/src/catalog/index.ts @@ -0,0 +1,18 @@ +import type { Catalog, Facet, Option } from "../types.js"; +import { processFacet } from "./facets/process.js"; +import { architectureFacet } from "./facets/architecture.js"; +import { practicesFacet } from "./facets/practices.js"; + +export function getDefaultCatalog(): Catalog { + return { + facets: [processFacet, architectureFacet, practicesFacet] + }; +} + +export function getFacet(catalog: Catalog, id: string): Facet | undefined { + return catalog.facets.find((f) => f.id === id); +} + +export function getOption(facet: Facet, id: string): Option | undefined { + return facet.options.find((o) => o.id === id); +} diff --git a/packages/core/src/config.spec.ts b/packages/core/src/config.spec.ts new file mode 100644 index 0000000..771a9e6 --- /dev/null +++ b/packages/core/src/config.spec.ts @@ -0,0 +1,163 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { parse as parseYaml } from "yaml"; + +import { + readUserConfig, + writeUserConfig, + readLockFile, + writeLockFile +} from "./config.js"; + +import type { UserConfig, LockFile } from "./types.js"; + +describe("config", () => { + let tempDir: string; + + beforeEach(async () => { + tempDir = await mkdtemp(join(tmpdir(), "ade-config-test-")); + }); + + afterEach(async () => { + await rm(tempDir, { recursive: true, force: true }); + }); + + describe("UserConfig roundtrip", () => { + it("write then read produces identical data", async () => { + const config: UserConfig = { + choices: { + language: "typescript", + framework: "react" + } + }; + + await writeUserConfig(tempDir, config); + const result = await readUserConfig(tempDir); + + expect(result).toEqual(config); + }); + + it("returns null when config.yaml does not exist", async () => { + const result = await readUserConfig(tempDir); + expect(result).toBeNull(); + }); + + it("multi-select choices (string[]) survive roundtrip", async () => { + const config: UserConfig = { + choices: { + language: "typescript", + plugins: ["eslint", "prettier", "vitest"] + } + }; + + await writeUserConfig(tempDir, config); + const result = await readUserConfig(tempDir); + + expect(result).toEqual(config); + expect(Array.isArray(result!.choices.plugins)).toBe(true); + expect(result!.choices.plugins).toEqual(["eslint", "prettier", "vitest"]); + }); + + it("custom section with mcp_servers and instructions survives roundtrip", async () => { + const config: UserConfig = { + choices: { + language: "python" + }, + custom: { + mcp_servers: [ + { + ref: "my-server", + command: "npx", + args: ["-y", "my-mcp-server"], + env: { API_KEY: "test-key" } + } + ], + instructions: ["Always use type hints", "Follow PEP 8 style guide"] + } + }; + + await writeUserConfig(tempDir, config); + const result = await readUserConfig(tempDir); + + expect(result).toEqual(config); + expect(result!.custom!.mcp_servers).toHaveLength(1); + expect(result!.custom!.mcp_servers![0].ref).toBe("my-server"); + expect(result!.custom!.instructions).toEqual([ + "Always use type hints", + "Follow PEP 8 style guide" + ]); + }); + }); + + describe("LockFile roundtrip", () => { + it("write then read produces identical data", async () => { + const lock: LockFile = { + version: 1, + generated_at: "2026-03-14T00:00:00.000Z", + choices: { + language: "typescript", + framework: "react" + }, + logical_config: { + mcp_servers: [ + { + ref: "typescript-server", + command: "npx", + args: ["-y", "ts-server"], + env: {} + } + ], + instructions: ["Use strict TypeScript"], + cli_actions: [ + { + command: "npm", + args: ["install"], + phase: "install" + } + ], + knowledge_sources: [ + { + name: "ts-docs", + origin: "https://typescriptlang.org", + description: "TypeScript documentation" + } + ], + skills: [] + } + }; + + await writeLockFile(tempDir, lock); + const result = await readLockFile(tempDir); + + expect(result).toEqual(lock); + }); + + it("returns null when config.lock.yaml does not exist", async () => { + const result = await readLockFile(tempDir); + expect(result).toBeNull(); + }); + }); + + describe("YAML validity", () => { + it("config file written is valid YAML", async () => { + const config: UserConfig = { + choices: { + language: "typescript", + tools: ["eslint", "prettier"] + }, + custom: { + instructions: ["Be concise"] + } + }; + + await writeUserConfig(tempDir, config); + + const raw = await readFile(join(tempDir, "config.yaml"), "utf-8"); + const parsed = parseYaml(raw); + + expect(parsed).toEqual(config); + }); + }); +}); diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts new file mode 100644 index 0000000..07dd5ac --- /dev/null +++ b/packages/core/src/config.ts @@ -0,0 +1,39 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { parse, stringify } from "yaml"; +import type { UserConfig, LockFile } from "./types.js"; + +const CONFIG_FILE = "config.yaml"; +const LOCK_FILE = "config.lock.yaml"; + +export async function readUserConfig(dir: string): Promise { + try { + const raw = await readFile(join(dir, CONFIG_FILE), "utf-8"); + return parse(raw) as UserConfig; + } catch { + return null; + } +} + +export async function writeUserConfig( + dir: string, + config: UserConfig +): Promise { + await writeFile(join(dir, CONFIG_FILE), stringify(config), "utf-8"); +} + +export async function readLockFile(dir: string): Promise { + try { + const raw = await readFile(join(dir, LOCK_FILE), "utf-8"); + return parse(raw) as LockFile; + } catch { + return null; + } +} + +export async function writeLockFile( + dir: string, + lock: LockFile +): Promise { + await writeFile(join(dir, LOCK_FILE), stringify(lock), "utf-8"); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..29ccfea --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,42 @@ +export { + type Catalog, + type Facet, + type Option, + type Provision, + type DocsetDef +} from "./types.js"; +export { + type LogicalConfig, + type McpServerEntry, + type CliAction, + type KnowledgeSource, + type SkillDefinition, + type InlineSkill, + type ExternalSkill +} from "./types.js"; +export { type ResolutionContext, type ResolvedFacet } from "./types.js"; +export { type UserConfig, type LockFile } from "./types.js"; +export { type ProvisionWriter } from "./types.js"; +export { + readUserConfig, + writeUserConfig, + readLockFile, + writeLockFile +} from "./config.js"; +export { + type ProvisionWriterDef, + type AgentWriterDef, + type WriterRegistry +} from "./types.js"; +export { + createRegistry, + registerProvisionWriter, + getProvisionWriter, + registerAgentWriter, + getAgentWriter, + createDefaultRegistry +} from "./registry.js"; +export { resolve, collectDocsets } from "./resolver.js"; +export { getDefaultCatalog, getFacet, getOption } from "./catalog/index.js"; +export { skillsWriter } from "./writers/skills.js"; +export { knowledgeWriter } from "./writers/knowledge.js"; diff --git a/packages/core/src/registry.spec.ts b/packages/core/src/registry.spec.ts new file mode 100644 index 0000000..4ff05bc --- /dev/null +++ b/packages/core/src/registry.spec.ts @@ -0,0 +1,140 @@ +import { describe, it, expect, vi } from "vitest"; +import { + createRegistry, + registerProvisionWriter, + registerAgentWriter, + getProvisionWriter, + getAgentWriter, + createDefaultRegistry +} from "./registry.js"; +import type { + ProvisionWriterDef, + AgentWriterDef, + LogicalConfig, + ResolutionContext +} from "./types.js"; + +describe("registry", () => { + describe("createRegistry", () => { + it("returns a registry with empty provisions and agents maps", () => { + const registry = createRegistry(); + expect(registry.provisions.size).toBe(0); + expect(registry.agents.size).toBe(0); + }); + }); + + describe("registerProvisionWriter / getProvisionWriter", () => { + it("registers a provision writer and retrieves it by id", async () => { + const registry = createRegistry(); + + const mockFragment: Partial = { + instructions: ["use typescript strict mode"] + }; + + const writer: ProvisionWriterDef = { + id: "skills", + write: vi.fn().mockResolvedValue(mockFragment) + }; + + registerProvisionWriter(registry, writer); + + const found = getProvisionWriter(registry, "skills"); + expect(found).toBeDefined(); + expect(found!.id).toBe("skills"); + + // Behavioral: actually call write() and verify the result + const context: ResolutionContext = { resolved: {} }; + const result = await found!.write({ lang: "ts" }, context); + expect(result).toEqual(mockFragment); + expect(writer.write).toHaveBeenCalledWith({ lang: "ts" }, context); + }); + + it("overwrites a writer when registering with the same id", async () => { + const registry = createRegistry(); + + const first: ProvisionWriterDef = { + id: "workflows", + write: vi.fn().mockResolvedValue({ instructions: ["first"] }) + }; + const second: ProvisionWriterDef = { + id: "workflows", + write: vi.fn().mockResolvedValue({ instructions: ["second"] }) + }; + + registerProvisionWriter(registry, first); + registerProvisionWriter(registry, second); + + const found = getProvisionWriter(registry, "workflows"); + const result = await found!.write({}, { resolved: {} }); + expect(result).toEqual({ instructions: ["second"] }); + expect(first.write).not.toHaveBeenCalled(); + }); + + it("returns undefined for a non-existent provision writer", () => { + const registry = createRegistry(); + const found = getProvisionWriter(registry, "does-not-exist"); + expect(found).toBeUndefined(); + }); + }); + + describe("registerAgentWriter / getAgentWriter", () => { + it("registers an agent writer and can call install()", async () => { + const registry = createRegistry(); + + const mockInstall = vi.fn().mockResolvedValue(undefined); + const agent: AgentWriterDef = { + id: "opencode", + install: mockInstall + }; + + registerAgentWriter(registry, agent); + + const found = getAgentWriter(registry, "opencode"); + expect(found).toBeDefined(); + expect(found!.id).toBe("opencode"); + + // Behavioral: call install() and verify it was invoked correctly + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["be helpful"], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + await found!.install(config, "/tmp/my-project"); + expect(mockInstall).toHaveBeenCalledWith(config, "/tmp/my-project"); + }); + + it("returns undefined for a non-existent agent writer", () => { + const registry = createRegistry(); + const found = getAgentWriter(registry, "nope"); + expect(found).toBeUndefined(); + }); + }); + + describe("createDefaultRegistry", () => { + it("has all 6 built-in provision writer IDs registered", () => { + const registry = createDefaultRegistry(); + const expectedIds = [ + "workflows", + "skills", + "knowledge", + "mcp-server", + "instruction", + "installable" + ]; + for (const id of expectedIds) { + expect( + getProvisionWriter(registry, id), + `expected provision writer "${id}" to be registered` + ).toBeDefined(); + } + expect(registry.provisions.size).toBe(6); + }); + + it("has no agent writers by default (moved to @ade/harnesses)", () => { + const registry = createDefaultRegistry(); + expect(registry.agents.size).toBe(0); + }); + }); +}); diff --git a/packages/core/src/registry.ts b/packages/core/src/registry.ts new file mode 100644 index 0000000..90d7ace --- /dev/null +++ b/packages/core/src/registry.ts @@ -0,0 +1,64 @@ +import type { + WriterRegistry, + ProvisionWriterDef, + AgentWriterDef +} from "./types.js"; +import { instructionWriter } from "./writers/instruction.js"; +import { workflowsWriter } from "./writers/workflows.js"; +import { skillsWriter } from "./writers/skills.js"; +import { knowledgeWriter } from "./writers/knowledge.js"; + +export function createRegistry(): WriterRegistry { + return { + provisions: new Map(), + agents: new Map() + }; +} + +export function registerProvisionWriter( + registry: WriterRegistry, + writer: ProvisionWriterDef +): void { + registry.provisions.set(writer.id, writer); +} + +export function getProvisionWriter( + registry: WriterRegistry, + id: string +): ProvisionWriterDef | undefined { + return registry.provisions.get(id); +} + +export function registerAgentWriter( + registry: WriterRegistry, + agent: AgentWriterDef +): void { + registry.agents.set(agent.id, agent); +} + +export function getAgentWriter( + registry: WriterRegistry, + id: string +): AgentWriterDef | undefined { + return registry.agents.get(id); +} + +export function createDefaultRegistry(): WriterRegistry { + const registry = createRegistry(); + + registerProvisionWriter(registry, instructionWriter); + registerProvisionWriter(registry, workflowsWriter); + registerProvisionWriter(registry, skillsWriter); + + registerProvisionWriter(registry, knowledgeWriter); + + // Stub writers for types not yet implemented + for (const id of ["mcp-server", "installable"]) { + registerProvisionWriter(registry, { + id, + write: async () => ({}) + }); + } + + return registry; +} diff --git a/packages/core/src/resolver.spec.ts b/packages/core/src/resolver.spec.ts new file mode 100644 index 0000000..5970e85 --- /dev/null +++ b/packages/core/src/resolver.spec.ts @@ -0,0 +1,540 @@ +import { describe, it, expect } from "vitest"; +import { resolve, collectDocsets } from "./resolver.js"; +import { getDefaultCatalog } from "./catalog/index.js"; +import { createRegistry, registerProvisionWriter } from "./registry.js"; +import { instructionWriter } from "./writers/instruction.js"; +import { workflowsWriter } from "./writers/workflows.js"; +import { skillsWriter } from "./writers/skills.js"; +import type { UserConfig, WriterRegistry, Catalog } from "./types.js"; + +function buildRegistry(): WriterRegistry { + const registry = createRegistry(); + registerProvisionWriter(registry, instructionWriter); + registerProvisionWriter(registry, workflowsWriter); + registerProvisionWriter(registry, skillsWriter); + return registry; +} + +describe("resolve", () => { + let catalog: Catalog; + let registry: WriterRegistry; + + beforeEach(() => { + catalog = getDefaultCatalog(); + registry = buildRegistry(); + }); + + describe("single-select resolution", () => { + it("resolves codemcp-workflows to LogicalConfig with mcp_servers", async () => { + const userConfig: UserConfig = { + choices: { process: "codemcp-workflows" } + }; + + const result = await resolve(userConfig, catalog, registry); + + // workflows writer produces mcp_servers + expect(result.mcp_servers).toBeDefined(); + expect(result.mcp_servers.length).toBeGreaterThanOrEqual(1); + // Should have all LogicalConfig fields + expect(result).toHaveProperty("instructions"); + expect(result).toHaveProperty("cli_actions"); + expect(result).toHaveProperty("knowledge_sources"); + }); + }); + + describe("different option selection", () => { + it("resolves native-agents-md to LogicalConfig with instructions but no mcp_servers", async () => { + const userConfig: UserConfig = { + choices: { process: "native-agents-md" } + }; + + const result = await resolve(userConfig, catalog, registry); + + // instruction writer produces instructions + expect(result.instructions).toBeDefined(); + // native-agents-md has no workflows provision, so no mcp_servers + expect(result.mcp_servers).toEqual([]); + }); + }); + + describe("empty choices", () => { + it("returns an empty LogicalConfig when no choices are provided", async () => { + const userConfig: UserConfig = { + choices: {} + }; + + const result = await resolve(userConfig, catalog, registry); + + expect(result).toEqual({ + mcp_servers: [], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }); + }); + }); + + describe("custom section merge", () => { + it("merges custom instructions and mcp_servers into the output", async () => { + const userConfig: UserConfig = { + choices: {}, + custom: { + instructions: ["Always use TypeScript strict mode"], + mcp_servers: [ + { + ref: "my-custom-server", + command: "node", + args: ["server.js"], + env: {} + } + ] + } + }; + + const result = await resolve(userConfig, catalog, registry); + + expect(result.instructions).toContain( + "Always use TypeScript strict mode" + ); + expect(result.mcp_servers).toContainEqual( + expect.objectContaining({ ref: "my-custom-server" }) + ); + }); + + it("merges custom section with recipe-produced config", async () => { + const userConfig: UserConfig = { + choices: { process: "codemcp-workflows" }, + custom: { + instructions: ["Extra instruction"] + } + }; + + const result = await resolve(userConfig, catalog, registry); + + // Should have both recipe mcp_servers and custom instructions + expect(result.mcp_servers.length).toBeGreaterThanOrEqual(1); + expect(result.instructions).toContain("Extra instruction"); + }); + }); + + describe("unknown facet in choices", () => { + it("ignores unknown facet ids without throwing", async () => { + const userConfig: UserConfig = { + choices: { "nonexistent-facet": "some-option" } + }; + + // Should not throw + const result = await resolve(userConfig, catalog, registry); + + expect(result).toEqual({ + mcp_servers: [], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }); + }); + }); + + describe("unknown option in choices", () => { + it("throws when facet exists but option id does not", async () => { + const userConfig: UserConfig = { + choices: { process: "nonexistent-option" } + }; + + await expect(resolve(userConfig, catalog, registry)).rejects.toThrow(); + }); + }); + + describe("skills merging", () => { + it("merges skills from provision writers into the output", async () => { + // Use a custom catalog with a facet that produces skills + const skillsCatalog: Catalog = { + facets: [ + { + id: "conventions", + label: "Conventions", + description: "Team conventions", + required: false, + options: [ + { + id: "test-conv", + label: "Test Convention", + description: "A test convention with skills", + recipe: [ + { + writer: "skills", + config: { + skills: [ + { + name: "test-skill", + description: "A test skill", + body: "Do the thing." + } + ] + } + } + ] + } + ] + } + ] + }; + + const userConfig: UserConfig = { + choices: { conventions: "test-conv" } + }; + + const result = await resolve(userConfig, skillsCatalog, registry); + + expect(result.skills).toHaveLength(1); + expect(result.skills[0].name).toBe("test-skill"); + + // Resolver should auto-add agentskills MCP entry when skills are present + const agentskills = result.mcp_servers.find( + (s) => s.ref === "agentskills" + ); + expect(agentskills).toBeDefined(); + expect(agentskills!.command).toBe("npx"); + expect(agentskills!.args).toContain("@codemcp/skills-server"); + }); + + it("does not add agentskills MCP entry when no skills", async () => { + const userConfig: UserConfig = { + choices: { process: "native-agents-md" } + }; + const result = await resolve(userConfig, catalog, registry); + + const agentskills = result.mcp_servers.find( + (s) => s.ref === "agentskills" + ); + expect(agentskills).toBeUndefined(); + }); + }); + + describe("docset collection", () => { + it("collects docsets from selected options into knowledge_sources", async () => { + const docsetCatalog: Catalog = { + facets: [ + { + id: "arch", + label: "Architecture", + description: "Stack", + required: false, + options: [ + { + id: "react", + label: "React", + description: "React framework", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "Official React documentation" + } + ] + } + ] + } + ] + }; + + const userConfig: UserConfig = { choices: { arch: "react" } }; + const result = await resolve(userConfig, docsetCatalog, registry); + + expect(result.knowledge_sources).toHaveLength(1); + expect(result.knowledge_sources[0]).toEqual({ + name: "react-docs", + origin: "https://github.com/facebook/react.git", + description: "Official React documentation" + }); + }); + + it("deduplicates docsets by id across multiple options", async () => { + const docsetCatalog: Catalog = { + facets: [ + { + id: "stack", + label: "Stack", + description: "Tech stack", + required: false, + multiSelect: true, + options: [ + { + id: "react", + label: "React", + description: "React", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "React docs" + } + ] + }, + { + id: "nextjs", + label: "Next.js", + description: "Next.js", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "React docs" + }, + { + id: "nextjs-docs", + label: "Next.js Docs", + origin: "https://nextjs.org/docs", + description: "Next.js docs" + } + ] + } + ] + } + ] + }; + + const userConfig: UserConfig = { + choices: { stack: ["react", "nextjs"] } + }; + const result = await resolve(userConfig, docsetCatalog, registry); + + expect(result.knowledge_sources).toHaveLength(2); + const ids = result.knowledge_sources.map((ks) => ks.name); + expect(ids).toContain("react-docs"); + expect(ids).toContain("nextjs-docs"); + }); + + it("filters out excluded_docsets", async () => { + const docsetCatalog: Catalog = { + facets: [ + { + id: "arch", + label: "Architecture", + description: "Stack", + required: false, + options: [ + { + id: "react", + label: "React", + description: "React", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "React docs" + }, + { + id: "react-tutorial", + label: "React Tutorial", + origin: "https://github.com/reactjs/react.dev.git", + description: "React tutorial" + } + ] + } + ] + } + ] + }; + + const userConfig: UserConfig = { + choices: { arch: "react" }, + excluded_docsets: ["react-tutorial"] + }; + const result = await resolve(userConfig, docsetCatalog, registry); + + expect(result.knowledge_sources).toHaveLength(1); + expect(result.knowledge_sources[0].name).toBe("react-docs"); + }); + + it("adds knowledge-server MCP entry when knowledge_sources are present", async () => { + const docsetCatalog: Catalog = { + facets: [ + { + id: "arch", + label: "Architecture", + description: "Stack", + required: false, + options: [ + { + id: "react", + label: "React", + description: "React", + recipe: [], + docsets: [ + { + id: "react-docs", + label: "React Reference", + origin: "https://github.com/facebook/react.git", + description: "React docs" + } + ] + } + ] + } + ] + }; + + const userConfig: UserConfig = { choices: { arch: "react" } }; + const result = await resolve(userConfig, docsetCatalog, registry); + + const knowledgeServer = result.mcp_servers.find( + (s) => s.ref === "knowledge" + ); + expect(knowledgeServer).toBeDefined(); + expect(knowledgeServer!.command).toBe("npx"); + expect(knowledgeServer!.args).toContain("@codemcp/knowledge-server"); + }); + + it("does not add knowledge-server MCP entry when no knowledge_sources", async () => { + const userConfig: UserConfig = { + choices: { process: "native-agents-md" } + }; + const result = await resolve(userConfig, catalog, registry); + + const knowledgeServer = result.mcp_servers.find( + (s) => s.ref === "knowledge" + ); + expect(knowledgeServer).toBeUndefined(); + }); + + it("produces no knowledge_sources when option has no docsets", async () => { + const userConfig: UserConfig = { + choices: { process: "native-agents-md" } + }; + const result = await resolve(userConfig, catalog, registry); + + expect(result.knowledge_sources).toEqual([]); + }); + }); + + describe("collectDocsets", () => { + it("returns deduplicated docsets for given choices", () => { + const docsetCatalog: Catalog = { + facets: [ + { + id: "stack", + label: "Stack", + description: "Stack", + required: false, + multiSelect: true, + options: [ + { + id: "a", + label: "A", + description: "A", + recipe: [], + docsets: [ + { + id: "shared", + label: "Shared", + origin: "https://x", + description: "shared" + }, + { + id: "a-only", + label: "A Only", + origin: "https://a", + description: "a" + } + ] + }, + { + id: "b", + label: "B", + description: "B", + recipe: [], + docsets: [ + { + id: "shared", + label: "Shared", + origin: "https://x", + description: "shared" + }, + { + id: "b-only", + label: "B Only", + origin: "https://b", + description: "b" + } + ] + } + ] + } + ] + }; + + const result = collectDocsets({ stack: ["a", "b"] }, docsetCatalog); + + expect(result).toHaveLength(3); + const ids = result.map((d) => d.id); + expect(ids).toContain("shared"); + expect(ids).toContain("a-only"); + expect(ids).toContain("b-only"); + }); + + it("returns empty array when no options have docsets", () => { + const result = collectDocsets({ process: "native-agents-md" }, catalog); + expect(result).toEqual([]); + }); + }); + + describe("MCP server dedup by ref", () => { + it("deduplicates mcp_servers by ref, keeping the last one", async () => { + // Create a custom registry with a writer that produces duplicate refs + const dedupRegistry = createRegistry(); + registerProvisionWriter(dedupRegistry, { + id: "workflows", + async write() { + return { + mcp_servers: [ + { + ref: "duplicate-server", + command: "npx", + args: ["-y", "pkg-a"], + env: {} + } + ] + }; + } + }); + registerProvisionWriter(dedupRegistry, instructionWriter); + + // Also add a custom mcp_server with the same ref but different args + const userConfig: UserConfig = { + choices: { process: "codemcp-workflows" }, + custom: { + mcp_servers: [ + { + ref: "duplicate-server", + command: "node", + args: ["custom-server.js"], + env: { CUSTOM: "true" } + } + ] + } + }; + + const result = await resolve(userConfig, catalog, dedupRegistry); + + // Should only have one entry with ref "duplicate-server" + const duplicates = result.mcp_servers.filter( + (s) => s.ref === "duplicate-server" + ); + expect(duplicates).toHaveLength(1); + // Last one wins — the custom one should survive + expect(duplicates[0].command).toBe("node"); + expect(duplicates[0].env).toEqual({ CUSTOM: "true" }); + }); + }); +}); diff --git a/packages/core/src/resolver.ts b/packages/core/src/resolver.ts new file mode 100644 index 0000000..4f42ba4 --- /dev/null +++ b/packages/core/src/resolver.ts @@ -0,0 +1,162 @@ +import type { + UserConfig, + Catalog, + WriterRegistry, + LogicalConfig, + McpServerEntry, + ResolutionContext, + DocsetDef +} from "./types.js"; +import { getFacet, getOption } from "./catalog/index.js"; +import { getProvisionWriter } from "./registry.js"; + +export async function resolve( + userConfig: UserConfig, + catalog: Catalog, + registry: WriterRegistry +): Promise { + const result: LogicalConfig = { + mcp_servers: [], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + const context: ResolutionContext = { resolved: {} }; + + for (const [facetId, optionId] of Object.entries(userConfig.choices)) { + const facet = getFacet(catalog, facetId); + if (!facet) { + continue; + } + + const selectedIds = Array.isArray(optionId) ? optionId : [optionId]; + + for (const selectedId of selectedIds) { + const option = getOption(facet, selectedId); + if (!option) { + throw new Error( + `Unknown option "${selectedId}" for facet "${facetId}"` + ); + } + + context.resolved[facetId] = { optionId: selectedId, option }; + + for (const provision of option.recipe) { + const writer = getProvisionWriter(registry, provision.writer); + if (!writer) { + continue; + } + const partial = await writer.write(provision.config, context); + if (partial.mcp_servers) { + result.mcp_servers.push(...partial.mcp_servers); + } + if (partial.instructions) { + result.instructions.push(...partial.instructions); + } + if (partial.cli_actions) { + result.cli_actions.push(...partial.cli_actions); + } + if (partial.knowledge_sources) { + result.knowledge_sources.push(...partial.knowledge_sources); + } + if (partial.skills) { + result.skills.push(...partial.skills); + } + } + } + } + + // Collect docsets from all selected options, dedup by id, filter exclusions + const seenDocsets = new Map(); + for (const [facetId, optionId] of Object.entries(userConfig.choices)) { + const facet = getFacet(catalog, facetId); + if (!facet) continue; + const selectedIds = Array.isArray(optionId) ? optionId : [optionId]; + for (const selectedId of selectedIds) { + const option = getOption(facet, selectedId); + if (!option?.docsets) continue; + for (const docset of option.docsets) { + if (!seenDocsets.has(docset.id)) { + seenDocsets.set(docset.id, docset); + } + } + } + } + + const excludedSet = new Set(userConfig.excluded_docsets ?? []); + for (const [id, docset] of seenDocsets) { + if (excludedSet.has(id)) continue; + result.knowledge_sources.push({ + name: docset.id, + origin: docset.origin, + description: docset.description + }); + } + + // Add knowledge-server MCP entry if any knowledge_sources were collected + if (result.knowledge_sources.length > 0) { + result.mcp_servers.push({ + ref: "knowledge", + command: "npx", + args: ["-y", "@codemcp/knowledge-server"], + env: {} + }); + } + + // Add skills-server MCP entry if any skills were collected + if (result.skills.length > 0) { + result.mcp_servers.push({ + ref: "agentskills", + command: "npx", + args: ["-y", "@codemcp/skills-server"], + env: {} + }); + } + + // Merge custom section + if (userConfig.custom) { + if (userConfig.custom.instructions) { + result.instructions.push(...userConfig.custom.instructions); + } + if (userConfig.custom.mcp_servers) { + result.mcp_servers.push(...userConfig.custom.mcp_servers); + } + } + + // Dedup mcp_servers by ref (last wins) + const serversByRef = new Map(); + for (const server of result.mcp_servers) { + serversByRef.set(server.ref, server); + } + result.mcp_servers = Array.from(serversByRef.values()); + + return result; +} + +/** + * Collect all unique docsets implied by the given choices. + * Used by the TUI to present docsets for confirmation before resolution. + */ +export function collectDocsets( + choices: Record, + catalog: Catalog +): DocsetDef[] { + const seen = new Map(); + for (const [facetId, optionId] of Object.entries(choices)) { + const facet = getFacet(catalog, facetId); + if (!facet) continue; + const selectedIds = Array.isArray(optionId) ? optionId : [optionId]; + for (const selectedId of selectedIds) { + const option = getOption(facet, selectedId); + if (!option?.docsets) continue; + for (const docset of option.docsets) { + if (!seen.has(docset.id)) { + seen.set(docset.id, docset); + } + } + } + } + return Array.from(seen.values()); +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000..d79513f --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,141 @@ +// --- Catalog types --- + +export interface Catalog { + facets: Facet[]; +} + +export interface Facet { + id: string; + label: string; + description: string; + required: boolean; + multiSelect?: boolean; + dependsOn?: string[]; + options: Option[]; +} + +export interface Option { + id: string; + label: string; + description: string; + recipe: Provision[]; + docsets?: DocsetDef[]; +} + +export interface DocsetDef { + id: string; + label: string; + origin: string; + description: string; +} + +export interface Provision { + writer: ProvisionWriter; + config: Record; +} + +export type ProvisionWriter = + | "workflows" + | "skills" + | "knowledge" + | "mcp-server" + | "instruction" + | "installable"; + +// --- LogicalConfig types --- + +export interface InlineSkill { + name: string; + description: string; + body: string; +} + +export interface ExternalSkill { + name: string; + source: string; +} + +export type SkillDefinition = InlineSkill | ExternalSkill; + +export interface LogicalConfig { + mcp_servers: McpServerEntry[]; + instructions: string[]; + cli_actions: CliAction[]; + knowledge_sources: KnowledgeSource[]; + skills: SkillDefinition[]; +} + +export interface McpServerEntry { + ref: string; + command: string; + args: string[]; + env: Record; + /** + * Tool names the agent is pre-approved to use from this server. + * Defaults to `["*"]` (all tools) when not specified. + */ + allowedTools?: string[]; +} + +export interface CliAction { + command: string; + args: string[]; + phase: "setup" | "install"; +} + +export interface KnowledgeSource { + name: string; + origin: string; + description: string; +} + +// --- Resolution context --- + +export interface ResolutionContext { + resolved: Record; +} + +export interface ResolvedFacet { + optionId: string; + option: Option; +} + +// --- Config file types --- + +export interface UserConfig { + choices: Record; + excluded_docsets?: string[]; + harnesses?: string[]; + custom?: { + mcp_servers?: McpServerEntry[]; + instructions?: string[]; + }; +} + +export interface LockFile { + version: 1; + generated_at: string; + choices: Record; + harnesses?: string[]; + logical_config: LogicalConfig; +} + +// --- Writer contracts (open, any package can implement) --- + +export interface ProvisionWriterDef { + id: string; + write( + config: Record, + context: ResolutionContext + ): Promise>; +} + +export interface AgentWriterDef { + id: string; + install(config: LogicalConfig, projectRoot: string): Promise; +} + +export interface WriterRegistry { + provisions: Map; + agents: Map; +} diff --git a/packages/core/src/writers/instruction.spec.ts b/packages/core/src/writers/instruction.spec.ts new file mode 100644 index 0000000..51c846b --- /dev/null +++ b/packages/core/src/writers/instruction.spec.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from "vitest"; +import { instructionWriter } from "./instruction.js"; +import type { ResolutionContext } from "../types.js"; + +describe("instructionWriter", () => { + const context: ResolutionContext = { resolved: {} }; + + it("has id 'instruction'", () => { + expect(instructionWriter.id).toBe("instruction"); + }); + + it("returns the text wrapped in an instructions array", async () => { + const result = await instructionWriter.write( + { text: "Always use strict mode" }, + context + ); + expect(result).toEqual({ instructions: ["Always use strict mode"] }); + }); + + it("passes through the exact text without modification", async () => { + const verbatim = " leading spaces and trailing spaces "; + const result = await instructionWriter.write({ text: verbatim }, context); + expect(result).toEqual({ instructions: [verbatim] }); + }); + + it("only returns instructions, not other LogicalConfig keys", async () => { + const result = await instructionWriter.write( + { text: "some instruction" }, + context + ); + expect(Object.keys(result)).toEqual(["instructions"]); + expect(result).not.toHaveProperty("mcp_servers"); + expect(result).not.toHaveProperty("cli_actions"); + expect(result).not.toHaveProperty("knowledge_sources"); + }); + + it("handles multi-line text correctly", async () => { + const multiLine = "Line one\nLine two\nLine three"; + const result = await instructionWriter.write({ text: multiLine }, context); + expect(result).toEqual({ instructions: [multiLine] }); + }); +}); diff --git a/packages/core/src/writers/instruction.ts b/packages/core/src/writers/instruction.ts new file mode 100644 index 0000000..c3d3ab2 --- /dev/null +++ b/packages/core/src/writers/instruction.ts @@ -0,0 +1,8 @@ +import type { ProvisionWriterDef } from "../types.js"; + +export const instructionWriter: ProvisionWriterDef = { + id: "instruction", + async write(config) { + return { instructions: [(config as { text: string }).text] }; + } +}; diff --git a/packages/core/src/writers/knowledge.spec.ts b/packages/core/src/writers/knowledge.spec.ts new file mode 100644 index 0000000..5a46fe7 --- /dev/null +++ b/packages/core/src/writers/knowledge.spec.ts @@ -0,0 +1,26 @@ +import { describe, it, expect } from "vitest"; +import { knowledgeWriter } from "./knowledge.js"; + +describe("knowledgeWriter", () => { + it("has id 'knowledge'", () => { + expect(knowledgeWriter.id).toBe("knowledge"); + }); + + it("produces a knowledge_sources entry from config", async () => { + const result = await knowledgeWriter.write( + { + name: "react-docs", + origin: "https://github.com/facebook/react.git", + description: "Official React documentation" + }, + { resolved: {} } + ); + + expect(result.knowledge_sources).toHaveLength(1); + expect(result.knowledge_sources![0]).toEqual({ + name: "react-docs", + origin: "https://github.com/facebook/react.git", + description: "Official React documentation" + }); + }); +}); diff --git a/packages/core/src/writers/knowledge.ts b/packages/core/src/writers/knowledge.ts new file mode 100644 index 0000000..04fa488 --- /dev/null +++ b/packages/core/src/writers/knowledge.ts @@ -0,0 +1,15 @@ +import type { ProvisionWriterDef } from "../types.js"; + +export const knowledgeWriter: ProvisionWriterDef = { + id: "knowledge", + async write(config) { + const { name, origin, description } = config as { + name: string; + origin: string; + description: string; + }; + return { + knowledge_sources: [{ name, origin, description }] + }; + } +}; diff --git a/packages/core/src/writers/skills.spec.ts b/packages/core/src/writers/skills.spec.ts new file mode 100644 index 0000000..e969d37 --- /dev/null +++ b/packages/core/src/writers/skills.spec.ts @@ -0,0 +1,109 @@ +import { describe, it, expect } from "vitest"; +import { skillsWriter } from "./skills.js"; + +describe("skillsWriter", () => { + const emptyContext = { resolved: {} }; + + it("returns skills from config", async () => { + const result = await skillsWriter.write( + { + skills: [ + { + name: "my-skill", + description: "A test skill", + body: "Do the thing." + } + ] + }, + emptyContext + ); + + expect(result.skills).toHaveLength(1); + expect(result.skills![0]).toEqual({ + name: "my-skill", + description: "A test skill", + body: "Do the thing." + }); + }); + + it("returns multiple skills", async () => { + const result = await skillsWriter.write( + { + skills: [ + { name: "skill-a", description: "First", body: "Body A" }, + { name: "skill-b", description: "Second", body: "Body B" } + ] + }, + emptyContext + ); + + expect(result.skills).toHaveLength(2); + expect(result.skills!.map((s) => s.name)).toEqual(["skill-a", "skill-b"]); + }); + + it("returns only the skills key", async () => { + const result = await skillsWriter.write( + { + skills: [{ name: "x", description: "desc", body: "body" }] + }, + emptyContext + ); + + expect(Object.keys(result)).toEqual(["skills"]); + }); + + it("preserves multi-line body content", async () => { + const body = + "# Architecture\n\nUse layered architecture.\n\n## Rules\n- Rule 1\n- Rule 2"; + const result = await skillsWriter.write( + { + skills: [{ name: "arch", description: "Architecture", body }] + }, + emptyContext + ); + + expect(result.skills![0]).toMatchObject({ body }); + }); + + it("returns external skills with source reference", async () => { + const result = await skillsWriter.write( + { + skills: [ + { + name: "playwright-cli", + source: "microsoft/playwright-cli/skills/playwright-cli" + } + ] + }, + emptyContext + ); + + expect(result.skills).toHaveLength(1); + expect(result.skills![0]).toEqual({ + name: "playwright-cli", + source: "microsoft/playwright-cli/skills/playwright-cli" + }); + }); + + it("handles mixed inline and external skills", async () => { + const result = await skillsWriter.write( + { + skills: [ + { name: "my-skill", description: "Inline", body: "Do stuff." }, + { name: "ext-skill", source: "org/repo/skills/ext" } + ] + }, + emptyContext + ); + + expect(result.skills).toHaveLength(2); + expect(result.skills![0]).toMatchObject({ + name: "my-skill", + body: "Do stuff." + }); + expect(result.skills![1]).toMatchObject({ + name: "ext-skill", + source: "org/repo/skills/ext" + }); + }); +}); diff --git a/packages/core/src/writers/skills.ts b/packages/core/src/writers/skills.ts new file mode 100644 index 0000000..ca7c818 --- /dev/null +++ b/packages/core/src/writers/skills.ts @@ -0,0 +1,9 @@ +import type { ProvisionWriterDef, SkillDefinition } from "../types.js"; + +export const skillsWriter: ProvisionWriterDef = { + id: "skills", + async write(config) { + const { skills } = config as { skills: SkillDefinition[] }; + return { skills }; + } +}; diff --git a/packages/core/src/writers/workflows.spec.ts b/packages/core/src/writers/workflows.spec.ts new file mode 100644 index 0000000..b8e51e3 --- /dev/null +++ b/packages/core/src/writers/workflows.spec.ts @@ -0,0 +1,72 @@ +import { describe, it, expect } from "vitest"; +import { workflowsWriter } from "./workflows.js"; +import type { ResolutionContext } from "../types.js"; + +describe("workflowsWriter", () => { + const context: ResolutionContext = { resolved: {} }; + + it("has id 'workflows'", () => { + expect(workflowsWriter.id).toBe("workflows"); + }); + + it("returns mcp_servers with correct ref, command, and args for a given package", async () => { + const result = await workflowsWriter.write( + { package: "@codemcp/workflows-server" }, + context + ); + expect(result).toEqual({ + mcp_servers: [ + { + ref: "@codemcp/workflows-server", + command: "npx", + args: ["@codemcp/workflows-server"], + env: {} + } + ] + }); + }); + + it("uses ref override when provided", async () => { + const result = await workflowsWriter.write( + { package: "@codemcp/workflows-server@latest", ref: "workflows" }, + context + ); + expect(result.mcp_servers![0].ref).toBe("workflows"); + expect(result.mcp_servers![0].args).toEqual([ + "@codemcp/workflows-server@latest" + ]); + }); + + it("includes env in the entry when env is specified", async () => { + const result = await workflowsWriter.write( + { + package: "@codemcp/workflows-server", + env: { API_KEY: "secret", NODE_ENV: "production" } + }, + context + ); + expect(result.mcp_servers![0].env).toEqual({ + API_KEY: "secret", + NODE_ENV: "production" + }); + }); + + it("defaults env to an empty object when not specified", async () => { + const result = await workflowsWriter.write( + { package: "@codemcp/workflows-server" }, + context + ); + expect(result.mcp_servers![0].env).toEqual({}); + }); + + it("only returns mcp_servers, not other LogicalConfig keys", async () => { + const result = await workflowsWriter.write( + { package: "@codemcp/workflows-server" }, + context + ); + expect(Object.keys(result)).toEqual(["mcp_servers"]); + expect(result).not.toHaveProperty("instructions"); + expect(result).not.toHaveProperty("cli_actions"); + expect(result).not.toHaveProperty("knowledge_sources"); + }); +}); diff --git a/packages/core/src/writers/workflows.ts b/packages/core/src/writers/workflows.ts new file mode 100644 index 0000000..a11ccfd --- /dev/null +++ b/packages/core/src/writers/workflows.ts @@ -0,0 +1,26 @@ +import type { ProvisionWriterDef } from "../types.js"; + +export const workflowsWriter: ProvisionWriterDef = { + id: "workflows", + async write(config) { + const { + package: pkg, + ref, + env + } = config as { + package: string; + ref?: string; + env?: Record; + }; + return { + mcp_servers: [ + { + ref: ref ?? pkg, + command: "npx", + args: [pkg], + env: env ?? {} + } + ] + }; + } +}; diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json new file mode 100644 index 0000000..7cbd949 --- /dev/null +++ b/packages/core/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts"] +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..c17b099 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "." + }, + "include": ["src/**/*"] +} diff --git a/packages/core/tsconfig.vitest.json b/packages/core/tsconfig.vitest.json new file mode 100644 index 0000000..f8add23 --- /dev/null +++ b/packages/core/tsconfig.vitest.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "moduleResolution": "bundler" + }, + "include": ["vitest.config.ts"] +} diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts new file mode 100644 index 0000000..7b62873 --- /dev/null +++ b/packages/core/vitest.config.ts @@ -0,0 +1,5 @@ +// @ts-check +/** @type {import("vitest.config.ts").defineConfig} */ + +const baseConfig = await import("../../vitest.config.js"); +export default baseConfig.default; diff --git a/packages/harnesses/.prettierignore b/packages/harnesses/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/packages/harnesses/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/packages/harnesses/eslint.config.mjs b/packages/harnesses/eslint.config.mjs new file mode 100644 index 0000000..1483555 --- /dev/null +++ b/packages/harnesses/eslint.config.mjs @@ -0,0 +1,40 @@ +import js from "@eslint/js"; +import { parser, configs } from "typescript-eslint"; +import prettier from "eslint-config-prettier"; + +export default [ + js.configs.recommended, + ...configs.recommended, + prettier, + { + // Config for TypeScript files + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser, + parserOptions: { + project: ["./tsconfig.json", "./tsconfig.vitest.json"] + } + } + }, + { + // Config for JavaScript files - no TypeScript parsing + files: ["**/*.{js,jsx}"], + ...js.configs.recommended + }, + { + // Relaxed rules for test files + files: ["**/*.test.ts", "**/*.spec.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off" + } + }, + { + ignores: [ + "**/node_modules/**", + "**/dist/**", + ".pnpm-store/**", + "pnpm-lock.yaml" + ] + } +]; diff --git a/packages/harnesses/package.json b/packages/harnesses/package.json new file mode 100644 index 0000000..4f13825 --- /dev/null +++ b/packages/harnesses/package.json @@ -0,0 +1,34 @@ +{ + "name": "@ade/harnesses", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clean:build": "rimraf ./dist", + "dev": "nodemon", + "lint": "eslint .", + "lint:fix": "eslint --fix .", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "test": "vitest --run", + "test:watch": "vitest", + "typecheck": "tsc" + }, + "dependencies": { + "@ade/core": "workspace:*", + "@codemcp/skills": "^2.3.0" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "typescript": "^5.7.3" + } +} diff --git a/packages/harnesses/src/index.spec.ts b/packages/harnesses/src/index.spec.ts new file mode 100644 index 0000000..e3970fe --- /dev/null +++ b/packages/harnesses/src/index.spec.ts @@ -0,0 +1,45 @@ +import { describe, it, expect } from "vitest"; +import { allHarnessWriters, getHarnessWriter, getHarnessIds } from "./index.js"; + +describe("harness registry", () => { + it("exports all harness writers", () => { + expect(allHarnessWriters).toHaveLength(9); + const ids = allHarnessWriters.map((w) => w.id); + expect(ids).toContain("universal"); + expect(ids).toContain("claude-code"); + expect(ids).toContain("cursor"); + expect(ids).toContain("copilot"); + expect(ids).toContain("windsurf"); + expect(ids).toContain("cline"); + expect(ids).toContain("roo-code"); + expect(ids).toContain("kiro"); + expect(ids).toContain("opencode"); + }); + + it("looks up harness by id", () => { + expect(getHarnessWriter("cursor")?.label).toBe("Cursor"); + expect(getHarnessWriter("nonexistent")).toBeUndefined(); + }); + + it("returns all harness ids", () => { + const ids = getHarnessIds(); + expect(ids).toEqual([ + "universal", + "claude-code", + "cursor", + "copilot", + "windsurf", + "cline", + "roo-code", + "kiro", + "opencode" + ]); + }); + + it("all writers have label and description", () => { + for (const w of allHarnessWriters) { + expect(w.label).toBeTruthy(); + expect(w.description).toBeTruthy(); + } + }); +}); diff --git a/packages/harnesses/src/index.ts b/packages/harnesses/src/index.ts new file mode 100644 index 0000000..9087d1e --- /dev/null +++ b/packages/harnesses/src/index.ts @@ -0,0 +1,46 @@ +export type { HarnessWriter } from "./types.js"; +export { installSkills } from "./skills-installer.js"; + +export { universalWriter } from "./writers/universal.js"; +export { claudeCodeWriter } from "./writers/claude-code.js"; +export { cursorWriter } from "./writers/cursor.js"; +export { copilotWriter } from "./writers/copilot.js"; +export { windsurfWriter } from "./writers/windsurf.js"; +export { clineWriter } from "./writers/cline.js"; +export { rooCodeWriter } from "./writers/roo-code.js"; +export { kiroWriter } from "./writers/kiro.js"; +export { opencodeWriter } from "./writers/opencode.js"; + +import type { HarnessWriter } from "./types.js"; +import { universalWriter } from "./writers/universal.js"; +import { claudeCodeWriter } from "./writers/claude-code.js"; +import { cursorWriter } from "./writers/cursor.js"; +import { copilotWriter } from "./writers/copilot.js"; +import { windsurfWriter } from "./writers/windsurf.js"; +import { clineWriter } from "./writers/cline.js"; +import { rooCodeWriter } from "./writers/roo-code.js"; +import { kiroWriter } from "./writers/kiro.js"; +import { opencodeWriter } from "./writers/opencode.js"; + +/** All built-in harness writers, ordered for wizard display. */ +export const allHarnessWriters: HarnessWriter[] = [ + universalWriter, + claudeCodeWriter, + cursorWriter, + copilotWriter, + windsurfWriter, + clineWriter, + rooCodeWriter, + kiroWriter, + opencodeWriter +]; + +/** Look up a harness writer by id. */ +export function getHarnessWriter(id: string): HarnessWriter | undefined { + return allHarnessWriters.find((w) => w.id === id); +} + +/** All valid harness IDs. */ +export function getHarnessIds(): string[] { + return allHarnessWriters.map((w) => w.id); +} diff --git a/packages/harnesses/src/skills-installer.ts b/packages/harnesses/src/skills-installer.ts new file mode 100644 index 0000000..93257f4 --- /dev/null +++ b/packages/harnesses/src/skills-installer.ts @@ -0,0 +1,54 @@ +import { join } from "node:path"; +import type { SkillDefinition, InlineSkill } from "@ade/core"; +import { runAdd } from "@codemcp/skills/api"; + +function isInlineSkill(skill: SkillDefinition): skill is InlineSkill { + return "body" in skill; +} + +/** + * Install skills using the @codemcp/skills programmatic API. + * + * Inline skills are expected to already exist as SKILL.md files under + * `/.ade/skills//` (written by the agent writer). + * This function calls `runAdd` with the local path for inline skills + * and the remote source for external skills. + * + * Note: `runAdd` uses `process.cwd()` to determine the install destination. + * This function changes cwd to `projectRoot` before calling `runAdd`. + */ +export async function installSkills( + skills: SkillDefinition[], + projectRoot: string +): Promise { + if (skills.length === 0) return; + + const originalCwd = process.cwd(); + process.chdir(projectRoot); + + try { + for (const skill of skills) { + const source = isInlineSkill(skill) + ? join(projectRoot, ".ade", "skills", skill.name) + : skill.source; + + try { + await runAdd([source], { yes: true, all: true }); + } catch (err) { + // runAdd may throw on network errors for external skills. + // Log and continue — inline skills should always succeed. + console.warn( + `Warning: failed to install skill "${skill.name}" from ${source}:`, + err instanceof Error ? err.message : err + ); + } + } + } finally { + // Restore cwd only if the original directory still exists + try { + process.chdir(originalCwd); + } catch { + // Original cwd may have been removed (e.g. in tests) + } + } +} diff --git a/packages/harnesses/src/types.ts b/packages/harnesses/src/types.ts new file mode 100644 index 0000000..7291d9f --- /dev/null +++ b/packages/harnesses/src/types.ts @@ -0,0 +1,12 @@ +import type { AgentWriterDef } from "@ade/core"; + +/** + * A harness writer extends AgentWriterDef with metadata for display in the + * setup wizard and CLI help. + */ +export interface HarnessWriter extends AgentWriterDef { + /** Human-readable label for the wizard (e.g. "Claude Code") */ + label: string; + /** Short description shown as hint in the wizard */ + description: string; +} diff --git a/packages/harnesses/src/util.ts b/packages/harnesses/src/util.ts new file mode 100644 index 0000000..39a2ee3 --- /dev/null +++ b/packages/harnesses/src/util.ts @@ -0,0 +1,188 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import type { LogicalConfig, McpServerEntry } from "@ade/core"; + +// --------------------------------------------------------------------------- +// JSON helpers +// --------------------------------------------------------------------------- + +/** Read a JSON file, returning `{}` if missing or unparseable. */ +export async function readJsonOrEmpty( + path: string +): Promise> { + try { + return JSON.parse(await readFile(path, "utf-8")); + } catch { + return {}; + } +} + +/** Write a JSON object with trailing newline. Creates parent dirs. */ +export async function writeJson(path: string, data: unknown): Promise { + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8"); +} + +// --------------------------------------------------------------------------- +// Server entry transform — each harness overrides only what differs +// --------------------------------------------------------------------------- + +/** Minimal MCP entry: command + args + optional env. */ +function baseEntry(server: McpServerEntry) { + return { + command: server.command, + args: server.args, + ...(Object.keys(server.env).length > 0 ? { env: server.env } : {}) + }; +} + +export type ServerTransform = ( + server: McpServerEntry +) => Record; + +/** Standard mcpServers entry (cursor, universal, claude-code). */ +export const standardEntry: ServerTransform = baseEntry; + +/** Adds `type: "stdio"` (copilot). */ +export const stdioEntry: ServerTransform = (s) => ({ + type: "stdio", + ...baseEntry(s) +}); + +/** Adds `alwaysAllow` (cline, roo-code, windsurf). */ +export const alwaysAllowEntry: ServerTransform = (s) => ({ + ...baseEntry(s), + alwaysAllow: s.allowedTools ?? ["*"] +}); + +// --------------------------------------------------------------------------- +// MCP JSON writer — covers 7 of 9 harnesses +// --------------------------------------------------------------------------- + +interface WriteMcpServersOpts { + /** Full path to the JSON file. */ + path: string; + /** Key in the JSON that holds the server map. Default: `"mcpServers"`. */ + key?: string; + /** Transform each McpServerEntry into the harness-specific shape. */ + transform?: ServerTransform; + /** Extra top-level fields to merge (e.g. `$schema`). */ + defaults?: Record; +} + +/** + * Merge MCP server entries into an existing JSON config file. + * Creates the file (and parent dirs) if missing. + */ +export async function writeMcpServers( + servers: McpServerEntry[], + opts: WriteMcpServersOpts +): Promise { + if (servers.length === 0) return; + + const key = opts.key ?? "mcpServers"; + const transform = opts.transform ?? standardEntry; + + const existing = await readJsonOrEmpty(opts.path); + const map = (existing[key] as Record) ?? {}; + + for (const server of servers) { + map[server.ref] = transform(server); + } + + const result = { ...(opts.defaults ?? {}), ...existing, [key]: map }; + await writeJson(opts.path, result); +} + +// --------------------------------------------------------------------------- +// Instructions → flat rules file (windsurf, cline, roo-code) +// --------------------------------------------------------------------------- + +/** + * Write instructions as a plain text rules file. + * Skips if no instructions. + */ +export async function writeRulesFile( + instructions: string[], + path: string +): Promise { + if (instructions.length === 0) return; + const lines = instructions.flatMap((i) => [i, ""]); + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, lines.join("\n"), "utf-8"); +} + +// --------------------------------------------------------------------------- +// Instructions → agent markdown with YAML frontmatter +// --------------------------------------------------------------------------- + +interface AgentMdOpts { + /** Full path to the .md file. */ + path: string; + /** Extra YAML frontmatter lines (after name/description, before `---`). */ + extraFrontmatter?: string[]; + /** Fallback body when instructions are empty. */ + fallbackBody?: string; +} + +/** + * Write an agent markdown file with YAML frontmatter. + * Shared by claude-code, copilot, and opencode. + */ +export async function writeAgentMd( + config: LogicalConfig, + opts: AgentMdOpts +): Promise { + if (config.instructions.length === 0 && config.mcp_servers.length === 0) + return; + + const fm: string[] = [ + "---", + "name: ade", + "description: ADE — Agentic Development Environment agent with project conventions and tools" + ]; + + if (opts.extraFrontmatter) { + fm.push(...opts.extraFrontmatter); + } + + fm.push("---"); + + const body = + config.instructions.length > 0 + ? config.instructions.join("\n\n") + : (opts.fallbackBody ?? ""); + + const content = fm.join("\n") + "\n\n" + body + "\n"; + await mkdir(dirname(opts.path), { recursive: true }); + await writeFile(opts.path, content, "utf-8"); +} + +// --------------------------------------------------------------------------- +// Inline skill SKILL.md writer (used by claude-code) +// --------------------------------------------------------------------------- + +export async function writeInlineSkills( + config: LogicalConfig, + projectRoot: string +): Promise { + for (const skill of config.skills) { + if (!("body" in skill)) continue; + + const skillDir = join(projectRoot, ".ade", "skills", skill.name); + await mkdir(skillDir, { recursive: true }); + + const frontmatter = [ + "---", + `name: ${skill.name}`, + `description: ${skill.description}`, + "---" + ].join("\n"); + + await writeFile( + join(skillDir, "SKILL.md"), + `${frontmatter}\n\n${skill.body}\n`, + "utf-8" + ); + } +} diff --git a/packages/harnesses/src/writers/claude-code.spec.ts b/packages/harnesses/src/writers/claude-code.spec.ts new file mode 100644 index 0000000..b2e0b6c --- /dev/null +++ b/packages/harnesses/src/writers/claude-code.spec.ts @@ -0,0 +1,152 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { claudeCodeWriter } from "./claude-code.js"; + +describe("claudeCodeWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-cc-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(claudeCodeWriter.id).toBe("claude-code"); + expect(claudeCodeWriter.label).toBe("Claude Code"); + expect(claudeCodeWriter.description).toBeTruthy(); + }); + + it("writes .claude/agents/ade.md custom agent", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: ["Use workflow files.", "Follow conventions."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await claudeCodeWriter.install(config, dir); + + const content = await readFile( + join(dir, ".claude", "agents", "ade.md"), + "utf-8" + ); + expect(content).toContain("name: ade"); + expect(content).toContain("description:"); + expect(content).toContain("Use workflow files."); + expect(content).toContain("Follow conventions."); + }); + + it("writes .mcp.json with MCP servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "@codemcp/workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await claudeCodeWriter.install(config, dir); + + const raw = await readFile(join(dir, ".mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["@codemcp/workflows"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/workflows"] + }); + }); + + it("writes .claude/settings.json with MCP tool permissions", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await claudeCodeWriter.install(config, dir); + + const raw = await readFile(join(dir, ".claude", "settings.json"), "utf-8"); + const settings = JSON.parse(raw); + expect(settings.permissions.allow).toContain("MCP(workflows:*)"); + }); + + it("includes agentskills server from mcp_servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "agentskills", + command: "npx", + args: ["-y", "@codemcp/skills-server"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [{ name: "my-skill", description: "A skill", body: "Do stuff." }] + }; + + await claudeCodeWriter.install(config, dir); + + const raw = await readFile(join(dir, ".mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["agentskills"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/skills-server"] + }); + }); + + it("writes inline SKILL.md files", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [ + { + name: "tanstack-architecture", + description: "TanStack architecture conventions", + body: "# Architecture\n\nUse file-based routing." + } + ] + }; + + await claudeCodeWriter.install(config, dir); + + const skillMd = await readFile( + join(dir, ".ade", "skills", "tanstack-architecture", "SKILL.md"), + "utf-8" + ); + expect(skillMd).toContain("name: tanstack-architecture"); + expect(skillMd).toContain("# Architecture"); + }); +}); diff --git a/packages/harnesses/src/writers/claude-code.ts b/packages/harnesses/src/writers/claude-code.ts new file mode 100644 index 0000000..0052734 --- /dev/null +++ b/packages/harnesses/src/writers/claude-code.ts @@ -0,0 +1,62 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { + readJsonOrEmpty, + writeJson, + writeMcpServers, + writeAgentMd, + writeInlineSkills +} from "../util.js"; + +export const claudeCodeWriter: HarnessWriter = { + id: "claude-code", + label: "Claude Code", + description: + "Anthropic's CLI agent — .claude/agents/ade.md + .mcp.json + .claude/settings.json", + async install(config: LogicalConfig, projectRoot: string) { + await writeAgentMd(config, { + path: join(projectRoot, ".claude", "agents", "ade.md"), + fallbackBody: "ADE — Agentic Development Environment agent." + }); + + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".mcp.json") + }); + + await writeClaudeSettings(config, projectRoot); + await writeInlineSkills(config, projectRoot); + } +}; + +async function writeClaudeSettings( + config: LogicalConfig, + projectRoot: string +): Promise { + const servers = config.mcp_servers; + if (servers.length === 0) return; + + const settingsPath = join(projectRoot, ".claude", "settings.json"); + const existing = await readJsonOrEmpty(settingsPath); + + const allowRules: string[] = []; + for (const server of servers) { + const allowed = server.allowedTools ?? ["*"]; + if (allowed.includes("*")) { + allowRules.push(`MCP(${server.ref}:*)`); + } else { + for (const tool of allowed) { + allowRules.push(`MCP(${server.ref}:${tool})`); + } + } + } + + const existingPerms = (existing.permissions as Record) ?? {}; + const existingAllow = (existingPerms.allow as string[]) ?? []; + const mergedAllow = [...new Set([...existingAllow, ...allowRules])]; + + await writeJson(settingsPath, { + ...existing, + permissions: { ...existingPerms, allow: mergedAllow } + }); +} diff --git a/packages/harnesses/src/writers/cline.spec.ts b/packages/harnesses/src/writers/cline.spec.ts new file mode 100644 index 0000000..8b1828c --- /dev/null +++ b/packages/harnesses/src/writers/cline.spec.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { clineWriter } from "./cline.js"; + +describe("clineWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-cline-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(clineWriter.id).toBe("cline"); + expect(clineWriter.label).toBe("Cline"); + }); + + it("writes .cline/mcp.json with MCP servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await clineWriter.install(config, dir); + + const raw = await readFile(join(dir, ".cline", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["workflows"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/workflows"], + alwaysAllow: ["*"] + }); + }); + + it("writes .clinerules with instructions", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["Follow TDD."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await clineWriter.install(config, dir); + + const content = await readFile(join(dir, ".clinerules"), "utf-8"); + expect(content).toContain("Follow TDD."); + }); +}); diff --git a/packages/harnesses/src/writers/cline.ts b/packages/harnesses/src/writers/cline.ts new file mode 100644 index 0000000..3c89388 --- /dev/null +++ b/packages/harnesses/src/writers/cline.ts @@ -0,0 +1,18 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers, alwaysAllowEntry, writeRulesFile } from "../util.js"; + +export const clineWriter: HarnessWriter = { + id: "cline", + label: "Cline", + description: "VS Code AI agent — .cline/mcp.json + .clinerules", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".cline", "mcp.json"), + transform: alwaysAllowEntry + }); + + await writeRulesFile(config.instructions, join(projectRoot, ".clinerules")); + } +}; diff --git a/packages/harnesses/src/writers/copilot.spec.ts b/packages/harnesses/src/writers/copilot.spec.ts new file mode 100644 index 0000000..1076e7a --- /dev/null +++ b/packages/harnesses/src/writers/copilot.spec.ts @@ -0,0 +1,96 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { copilotWriter } from "./copilot.js"; + +describe("copilotWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-copilot-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(copilotWriter.id).toBe("copilot"); + expect(copilotWriter.label).toBe("GitHub Copilot"); + }); + + it("writes .vscode/mcp.json with 'servers' key and type field", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await copilotWriter.install(config, dir); + + const raw = await readFile(join(dir, ".vscode", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + // Copilot uses "servers", not "mcpServers" + expect(parsed.servers["workflows"]).toEqual({ + type: "stdio", + command: "npx", + args: ["-y", "@codemcp/workflows"] + }); + }); + + it("does not write copilot-instructions.md (prefers agent definition)", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["Follow TDD."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await copilotWriter.install(config, dir); + + await expect( + readFile(join(dir, ".github", "copilot-instructions.md"), "utf-8") + ).rejects.toThrow(); + }); + + it("writes dedicated .github/agents/ade.agent.md with agent definition", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: ["Follow TDD."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await copilotWriter.install(config, dir); + + const content = await readFile( + join(dir, ".github", "agents", "ade.agent.md"), + "utf-8" + ); + expect(content).toContain("name: ade"); + expect(content).toContain("tools:"); + expect(content).toContain(" - workflows/*"); + expect(content).toContain(" - edit"); + expect(content).toContain("Follow TDD."); + }); +}); diff --git a/packages/harnesses/src/writers/copilot.ts b/packages/harnesses/src/writers/copilot.ts new file mode 100644 index 0000000..17e61b6 --- /dev/null +++ b/packages/harnesses/src/writers/copilot.ts @@ -0,0 +1,32 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers, stdioEntry, writeAgentMd } from "../util.js"; + +export const copilotWriter: HarnessWriter = { + id: "copilot", + label: "GitHub Copilot", + description: "VS Code + CLI — .vscode/mcp.json + .github/agents/ade.agent.md", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".vscode", "mcp.json"), + key: "servers", + transform: stdioEntry + }); + + const tools = [ + "edit", + "search", + "runCommands", + "runTasks", + "fetch", + "githubRepo", + ...config.mcp_servers.map((s) => `${s.ref}/*`) + ]; + + await writeAgentMd(config, { + path: join(projectRoot, ".github", "agents", "ade.agent.md"), + extraFrontmatter: ["tools:", ...tools.map((t) => ` - ${t}`)] + }); + } +}; diff --git a/packages/harnesses/src/writers/cursor.spec.ts b/packages/harnesses/src/writers/cursor.spec.ts new file mode 100644 index 0000000..6e8995f --- /dev/null +++ b/packages/harnesses/src/writers/cursor.spec.ts @@ -0,0 +1,108 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { cursorWriter } from "./cursor.js"; + +describe("cursorWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-cursor-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(cursorWriter.id).toBe("cursor"); + expect(cursorWriter.label).toBe("Cursor"); + }); + + it("writes .cursor/mcp.json with MCP servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await cursorWriter.install(config, dir); + + const raw = await readFile(join(dir, ".cursor", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["workflows"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/workflows"] + }); + }); + + it("writes .cursor/rules/ade.mdc with instructions", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["Follow TDD.", "Use conventional commits."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await cursorWriter.install(config, dir); + + const content = await readFile( + join(dir, ".cursor", "rules", "ade.mdc"), + "utf-8" + ); + expect(content).toContain("description: ADE project conventions"); + expect(content).toContain("Follow TDD."); + expect(content).toContain("Use conventional commits."); + }); + + it("includes agentskills server from mcp_servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "agentskills", + command: "npx", + args: ["-y", "@codemcp/skills-server"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [{ name: "my-skill", description: "A skill", body: "content" }] + }; + + await cursorWriter.install(config, dir); + + const raw = await readFile(join(dir, ".cursor", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["agentskills"]).toBeDefined(); + }); + + it("skips mcp.json when no servers and no skills", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["hello"], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await cursorWriter.install(config, dir); + + await expect( + readFile(join(dir, ".cursor", "mcp.json"), "utf-8") + ).rejects.toThrow(); + }); +}); diff --git a/packages/harnesses/src/writers/cursor.ts b/packages/harnesses/src/writers/cursor.ts new file mode 100644 index 0000000..321cbac --- /dev/null +++ b/packages/harnesses/src/writers/cursor.ts @@ -0,0 +1,32 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers } from "../util.js"; + +export const cursorWriter: HarnessWriter = { + id: "cursor", + label: "Cursor", + description: "AI code editor — .cursor/mcp.json + .cursor/rules/", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".cursor", "mcp.json") + }); + + if (config.instructions.length > 0) { + const rulesDir = join(projectRoot, ".cursor", "rules"); + await mkdir(rulesDir, { recursive: true }); + + const content = [ + "---", + "description: ADE project conventions", + "globs: *", + "---", + "", + ...config.instructions.flatMap((i) => [i, ""]) + ].join("\n"); + + await writeFile(join(rulesDir, "ade.mdc"), content, "utf-8"); + } + } +}; diff --git a/packages/harnesses/src/writers/kiro.ts b/packages/harnesses/src/writers/kiro.ts new file mode 100644 index 0000000..8f00c73 --- /dev/null +++ b/packages/harnesses/src/writers/kiro.ts @@ -0,0 +1,51 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { standardEntry, writeJson } from "../util.js"; + +export const kiroWriter: HarnessWriter = { + id: "kiro", + label: "Kiro", + description: "AWS AI IDE — .kiro/agents/ade.json", + async install(config: LogicalConfig, projectRoot: string) { + const servers = config.mcp_servers; + if (servers.length === 0 && config.instructions.length === 0) return; + + const mcpServers: Record = {}; + for (const s of servers) { + mcpServers[s.ref] = standardEntry(s); + } + + const tools: string[] = [ + "execute_bash", + "fs_read", + "fs_write", + "knowledge", + "thinking", + ...Object.keys(mcpServers).map((n) => `@${n}`) + ]; + + const allowedTools: string[] = []; + for (const s of servers) { + const explicit = s.allowedTools; + if (explicit && !explicit.includes("*")) { + for (const tool of explicit) { + allowedTools.push(`@${s.ref}/${tool}`); + } + } else { + allowedTools.push(`@${s.ref}/*`); + } + } + + await writeJson(join(projectRoot, ".kiro", "agents", "ade.json"), { + name: "ade", + prompt: + config.instructions.length > 0 + ? config.instructions.join("\n\n") + : "ADE — Agentic Development Environment agent", + mcpServers, + tools, + allowedTools + }); + } +}; diff --git a/packages/harnesses/src/writers/opencode.ts b/packages/harnesses/src/writers/opencode.ts new file mode 100644 index 0000000..a25e311 --- /dev/null +++ b/packages/harnesses/src/writers/opencode.ts @@ -0,0 +1,53 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers, writeAgentMd } from "../util.js"; + +export const opencodeWriter: HarnessWriter = { + id: "opencode", + label: "OpenCode", + description: "Terminal AI agent — opencode.json + .opencode/agents/", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, "opencode.json"), + key: "mcp", + transform: (s) => ({ + type: "local", + command: [s.command, ...s.args], + ...(Object.keys(s.env).length > 0 ? { env: s.env } : {}) + }), + defaults: { $schema: "https://opencode.ai/config.json" } + }); + + const servers = config.mcp_servers; + const extraFm: string[] = [ + "tools:", + " read: true", + " edit: approve", + " bash: approve" + ]; + + if (servers.length > 0) { + extraFm.push("mcp_servers:"); + for (const s of servers) { + extraFm.push(` ${s.ref}:`); + extraFm.push( + ` command: [${[s.command, ...s.args].map((a) => `"${a}"`).join(", ")}]` + ); + if (Object.keys(s.env).length > 0) { + extraFm.push(" env:"); + for (const [k, v] of Object.entries(s.env)) { + extraFm.push(` ${k}: "${v}"`); + } + } + } + } + + await writeAgentMd(config, { + path: join(projectRoot, ".opencode", "agents", "ade.md"), + extraFrontmatter: extraFm, + fallbackBody: + "ADE — Agentic Development Environment agent with project conventions and tools." + }); + } +}; diff --git a/packages/harnesses/src/writers/roo-code.spec.ts b/packages/harnesses/src/writers/roo-code.spec.ts new file mode 100644 index 0000000..c9dee42 --- /dev/null +++ b/packages/harnesses/src/writers/roo-code.spec.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { rooCodeWriter } from "./roo-code.js"; + +describe("rooCodeWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-roo-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(rooCodeWriter.id).toBe("roo-code"); + expect(rooCodeWriter.label).toBe("Roo Code"); + }); + + it("writes .roo/mcp.json with MCP servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: {} + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await rooCodeWriter.install(config, dir); + + const raw = await readFile(join(dir, ".roo", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["workflows"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/workflows"], + alwaysAllow: ["*"] + }); + }); + + it("writes .roorules with instructions", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["Follow TDD."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await rooCodeWriter.install(config, dir); + + const content = await readFile(join(dir, ".roorules"), "utf-8"); + expect(content).toContain("Follow TDD."); + }); +}); diff --git a/packages/harnesses/src/writers/roo-code.ts b/packages/harnesses/src/writers/roo-code.ts new file mode 100644 index 0000000..a1d008d --- /dev/null +++ b/packages/harnesses/src/writers/roo-code.ts @@ -0,0 +1,18 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers, alwaysAllowEntry, writeRulesFile } from "../util.js"; + +export const rooCodeWriter: HarnessWriter = { + id: "roo-code", + label: "Roo Code", + description: "AI coding agent — .roo/mcp.json + .roorules", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".roo", "mcp.json"), + transform: alwaysAllowEntry + }); + + await writeRulesFile(config.instructions, join(projectRoot, ".roorules")); + } +}; diff --git a/packages/harnesses/src/writers/universal.ts b/packages/harnesses/src/writers/universal.ts new file mode 100644 index 0000000..29ab3d4 --- /dev/null +++ b/packages/harnesses/src/writers/universal.ts @@ -0,0 +1,30 @@ +import { join } from "node:path"; +import { writeFile } from "node:fs/promises"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers } from "../util.js"; + +export const universalWriter: HarnessWriter = { + id: "universal", + label: "Universal (AGENTS.md + .mcp.json)", + description: + "Cross-tool standard — AGENTS.md + .mcp.json (works with any agent)", + async install(config: LogicalConfig, projectRoot: string) { + if (config.instructions.length > 0) { + const lines = [ + "# AGENTS", + "", + ...config.instructions.flatMap((i) => [i, ""]) + ]; + await writeFile( + join(projectRoot, "AGENTS.md"), + lines.join("\n"), + "utf-8" + ); + } + + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".mcp.json") + }); + } +}; diff --git a/packages/harnesses/src/writers/windsurf.spec.ts b/packages/harnesses/src/writers/windsurf.spec.ts new file mode 100644 index 0000000..2c72620 --- /dev/null +++ b/packages/harnesses/src/writers/windsurf.spec.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdtemp, rm, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import { windsurfWriter } from "./windsurf.js"; + +describe("windsurfWriter", () => { + let dir: string; + + beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), "ade-harness-windsurf-")); + }); + + afterEach(async () => { + await rm(dir, { recursive: true, force: true }); + }); + + it("has correct metadata", () => { + expect(windsurfWriter.id).toBe("windsurf"); + expect(windsurfWriter.label).toBe("Windsurf"); + }); + + it("writes .windsurf/mcp.json with MCP servers", async () => { + const config: LogicalConfig = { + mcp_servers: [ + { + ref: "workflows", + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: { API_KEY: "test" } + } + ], + instructions: [], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await windsurfWriter.install(config, dir); + + const raw = await readFile(join(dir, ".windsurf", "mcp.json"), "utf-8"); + const parsed = JSON.parse(raw); + expect(parsed.mcpServers["workflows"]).toEqual({ + command: "npx", + args: ["-y", "@codemcp/workflows"], + env: { API_KEY: "test" }, + alwaysAllow: ["*"] + }); + }); + + it("writes .windsurfrules with instructions", async () => { + const config: LogicalConfig = { + mcp_servers: [], + instructions: ["Follow TDD."], + cli_actions: [], + knowledge_sources: [], + skills: [] + }; + + await windsurfWriter.install(config, dir); + + const content = await readFile(join(dir, ".windsurfrules"), "utf-8"); + expect(content).toContain("Follow TDD."); + }); +}); diff --git a/packages/harnesses/src/writers/windsurf.ts b/packages/harnesses/src/writers/windsurf.ts new file mode 100644 index 0000000..e03d298 --- /dev/null +++ b/packages/harnesses/src/writers/windsurf.ts @@ -0,0 +1,21 @@ +import { join } from "node:path"; +import type { LogicalConfig } from "@ade/core"; +import type { HarnessWriter } from "../types.js"; +import { writeMcpServers, alwaysAllowEntry, writeRulesFile } from "../util.js"; + +export const windsurfWriter: HarnessWriter = { + id: "windsurf", + label: "Windsurf", + description: "Codeium's AI IDE — .windsurf/mcp.json + .windsurfrules", + async install(config: LogicalConfig, projectRoot: string) { + await writeMcpServers(config.mcp_servers, { + path: join(projectRoot, ".windsurf", "mcp.json"), + transform: alwaysAllowEntry + }); + + await writeRulesFile( + config.instructions, + join(projectRoot, ".windsurfrules") + ); + } +}; diff --git a/packages/harnesses/tsconfig.build.json b/packages/harnesses/tsconfig.build.json new file mode 100644 index 0000000..7cbd949 --- /dev/null +++ b/packages/harnesses/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts"] +} diff --git a/packages/harnesses/tsconfig.json b/packages/harnesses/tsconfig.json new file mode 100644 index 0000000..c17b099 --- /dev/null +++ b/packages/harnesses/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "." + }, + "include": ["src/**/*"] +} diff --git a/packages/harnesses/tsconfig.vitest.json b/packages/harnesses/tsconfig.vitest.json new file mode 100644 index 0000000..f8add23 --- /dev/null +++ b/packages/harnesses/tsconfig.vitest.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "moduleResolution": "bundler" + }, + "include": ["vitest.config.ts"] +} diff --git a/packages/harnesses/vitest.config.ts b/packages/harnesses/vitest.config.ts new file mode 100644 index 0000000..7b62873 --- /dev/null +++ b/packages/harnesses/vitest.config.ts @@ -0,0 +1,5 @@ +// @ts-check +/** @type {import("vitest.config.ts").defineConfig} */ + +const baseConfig = await import("../../vitest.config.js"); +export default baseConfig.default; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0ec574d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,9584 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + yaml: + specifier: ^2.8.2 + version: 2.8.2 + devDependencies: + "@braintree/sanitize-url": + specifier: 7.1.1 + version: 7.1.1 + "@eslint/js": + specifier: 9.18.0 + version: 9.18.0 + "@swc/core": + specifier: ^1.10.9 + version: 1.15.11 + "@tsconfig/node22": + specifier: 22.0.0 + version: 22.0.0 + "@tsconfig/strictest": + specifier: 2.0.5 + version: 2.0.5 + "@types/eslint-config-prettier": + specifier: 6.11.3 + version: 6.11.3 + "@types/node": + specifier: ^22.10.7 + version: 22.19.11 + "@typescript-eslint/eslint-plugin": + specifier: ^8.21.0 + version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/parser": + specifier: ^8.21.0 + version: 8.56.0(eslint@9.39.2)(typescript@5.9.3) + "@vitest/coverage-v8": + specifier: ^3.0.3 + version: 3.2.4(vitest@3.2.4(@types/node@22.19.11)(yaml@2.8.2)) + cytoscape: + specifier: 3.31.0 + version: 3.31.0 + cytoscape-cose-bilkent: + specifier: 4.1.0 + version: 4.1.0(cytoscape@3.31.0) + dayjs: + specifier: 1.11.13 + version: 1.11.13 + debug: + specifier: 4.4.0 + version: 4.4.0(supports-color@5.5.0) + eslint: + specifier: ^9.18.0 + version: 9.39.2 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.1.8(eslint@9.39.2) + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^15.4.1 + version: 15.5.2 + nodemon: + specifier: ^3.1.9 + version: 3.1.11 + prettier: + specifier: ^3.4.2 + version: 3.8.1 + rimraf: + specifier: ^6.0.1 + version: 6.1.3 + turbo: + specifier: ^2.3.3 + version: 2.8.10 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + typescript-eslint: + specifier: 8.21.0 + version: 8.21.0(eslint@9.39.2)(typescript@5.9.3) + vitepress: + specifier: 1.6.2 + version: 1.6.2(@algolia/client-search@5.49.0)(@types/node@22.19.11)(postcss@8.5.6)(search-insights@2.17.3)(typescript@5.9.3) + vitepress-plugin-mermaid: + specifier: 2.0.17 + version: 2.0.17(mermaid@11.4.1)(vitepress@1.6.2(@algolia/client-search@5.49.0)(@types/node@22.19.11)(postcss@8.5.6)(search-insights@2.17.3)(typescript@5.9.3)) + vitest: + specifier: ^3.0.3 + version: 3.2.4(@types/node@22.19.11)(yaml@2.8.2) + + packages/cli: + dependencies: + "@ade/core": + specifier: workspace:* + version: link:../core + "@ade/harnesses": + specifier: workspace:* + version: link:../harnesses + "@clack/prompts": + specifier: ^1.1.0 + version: 1.1.0 + devDependencies: + "@codemcp/knowledge": + specifier: 2.1.0 + version: 2.1.0(zod@4.3.6) + "@typescript-eslint/eslint-plugin": + specifier: ^8.21.0 + version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/parser": + specifier: ^8.21.0 + version: 8.56.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.18.0 + version: 9.39.2 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.1.8(eslint@9.39.2) + prettier: + specifier: ^3.4.2 + version: 3.8.1 + rimraf: + specifier: ^6.0.1 + version: 6.1.3 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + + packages/core: + dependencies: + yaml: + specifier: ^2.8.2 + version: 2.8.2 + devDependencies: + "@typescript-eslint/eslint-plugin": + specifier: ^8.21.0 + version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/parser": + specifier: ^8.21.0 + version: 8.56.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.18.0 + version: 9.39.2 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.1.8(eslint@9.39.2) + prettier: + specifier: ^3.4.2 + version: 3.8.1 + rimraf: + specifier: ^6.0.1 + version: 6.1.3 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + + packages/harnesses: + dependencies: + "@ade/core": + specifier: workspace:* + version: link:../core + "@codemcp/skills": + specifier: ^2.3.0 + version: 2.3.0 + devDependencies: + "@typescript-eslint/eslint-plugin": + specifier: ^8.21.0 + version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/parser": + specifier: ^8.21.0 + version: 8.56.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.18.0 + version: 9.39.2 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.1.8(eslint@9.39.2) + prettier: + specifier: ^3.4.2 + version: 3.8.1 + rimraf: + specifier: ^6.0.1 + version: 6.1.3 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + +packages: + "@algolia/abtesting@1.15.0": + resolution: + { + integrity: sha512-D1QZ8dQx5zC9yrxNao9ER9bojmmzUdL1i2P9waIRiwnZ5fI26YswcCd6VHR/Q4W3PASfVf2My4YQ2FhGGDewTQ== + } + engines: { node: ">= 14.0.0" } + + "@algolia/autocomplete-core@1.17.9": + resolution: + { + integrity: sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ== + } + + "@algolia/autocomplete-plugin-algolia-insights@1.17.9": + resolution: + { + integrity: sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ== + } + peerDependencies: + search-insights: ">= 1 < 3" + + "@algolia/autocomplete-preset-algolia@1.17.9": + resolution: + { + integrity: sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ== + } + peerDependencies: + "@algolia/client-search": ">= 4.9.1 < 6" + algoliasearch: ">= 4.9.1 < 6" + + "@algolia/autocomplete-shared@1.17.9": + resolution: + { + integrity: sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ== + } + peerDependencies: + "@algolia/client-search": ">= 4.9.1 < 6" + algoliasearch: ">= 4.9.1 < 6" + + "@algolia/client-abtesting@5.49.0": + resolution: + { + integrity: sha512-Q1MSRhh4Du9WeLIl1S9O+BDUMaL01uuQtmzCyEzOBtu1xBDr3wvqrTJtfEceEkA5/Nw1BdGSHa6sDT3xTAF90A== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-analytics@5.49.0": + resolution: + { + integrity: sha512-v50elhC80oyQw+8o8BwM+VvPuOo36+3W8VCfR4hsHoafQtGbMtP63U5eNcUydbVsM0py3JLoBaL1yKBK4L01sg== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-common@5.49.0": + resolution: + { + integrity: sha512-BDmVDtpDvymfLE5YQ2cPnfWJUVTDJqwpJa03Fsb7yJFJmbeKsUOGsnRkYsTbdzf0FfcvyvBB5zdcbrAIL249bg== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-insights@5.49.0": + resolution: + { + integrity: sha512-lDCXsnZDx7zQ5GzSi1EL3l07EbksjrdpMgixFRCdi2QqeBe42HIQJfPPqdWtwrAXjORRopsPx2z+gGYJP/79Uw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-personalization@5.49.0": + resolution: + { + integrity: sha512-5k/KB+DsnesNKvMUEwTKSzExOf5zYbiPg7DVO7g1Y/+bhMb3wmxp9RFwfqwPfmoRTjptqvwhR6a0593tWVkmAw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-query-suggestions@5.49.0": + resolution: + { + integrity: sha512-pjHNcrdjn7p3RQ5Ql1Baiwfdn9bkS+z4gqONJJP8kuZFqYP8Olthy4G7fl5bCB29UjdUj5EWlaElQKCtPluCtQ== + } + engines: { node: ">= 14.0.0" } + + "@algolia/client-search@5.49.0": + resolution: + { + integrity: sha512-uGv2P3lcviuaZy8ZOAyN60cZdhOVyjXwaDC27a1qdp3Pb5Azn+lLSJwkHU4TNRpphHmIei9HZuUxwQroujdPjw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/ingestion@1.49.0": + resolution: + { + integrity: sha512-sH10mftYlmvfGbvAgTtHYbCIstmNUdiAkX//0NAyBcJRB6NnZmNsdLxdFGbE8ZqlGXzoe0zcUIau+DxKpXtqCw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/monitoring@1.49.0": + resolution: + { + integrity: sha512-RqhGcVVxLpK+lA0GZKywlQIXsI704flc12nv/hOdrwiuk/Uyhxs46KLM4ngip7wutU+7t0PYZWiVayrqBPN/ZQ== + } + engines: { node: ">= 14.0.0" } + + "@algolia/recommend@5.49.0": + resolution: + { + integrity: sha512-kg8omGRvmIPhhqtUqSIpS3regFKWuoWh3WqyUhGk27N4T7q8I++8TsDYsV8vK7oBEzw706m2vUBtN5fw2fDjmw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/requester-browser-xhr@5.49.0": + resolution: + { + integrity: sha512-BaZ6NTI9VdSbDcsMucdKhTuFFxv6B+3dAZZBozX12fKopYsELh7dBLfZwm8evDCIicmNjIjobi4VNnNshrCSuw== + } + engines: { node: ">= 14.0.0" } + + "@algolia/requester-fetch@5.49.0": + resolution: + { + integrity: sha512-2nxISxS5xO5DLAj6QzMImgJv6CqpZhJVkhcTFULESR/k4IpbkJTEHmViVTxw9MlrU8B5GfwHevFd7vKL3a7MXQ== + } + engines: { node: ">= 14.0.0" } + + "@algolia/requester-node-http@5.49.0": + resolution: + { + integrity: sha512-S/B94C6piEUXGpN3y5ysmNKMEqdfNVAXYY+FxivEAV5IGJjbEuLZfT8zPPZUWGw9vh6lgP80Hye2G5aVBNIa8Q== + } + engines: { node: ">= 14.0.0" } + + "@ampproject/remapping@2.3.0": + resolution: + { + integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + } + engines: { node: ">=6.0.0" } + + "@antfu/install-pkg@1.1.0": + resolution: + { + integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== + } + + "@antfu/utils@8.1.1": + resolution: + { + integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ== + } + + "@babel/helper-string-parser@7.27.1": + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.28.5": + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + } + engines: { node: ">=6.9.0" } + + "@babel/parser@7.29.0": + resolution: + { + integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== + } + engines: { node: ">=6.0.0" } + hasBin: true + + "@babel/types@7.29.0": + resolution: + { + integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + } + engines: { node: ">=6.9.0" } + + "@bcoe/v8-coverage@1.0.2": + resolution: + { + integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + } + engines: { node: ">=18" } + + "@braintree/sanitize-url@6.0.4": + resolution: + { + integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== + } + + "@braintree/sanitize-url@7.1.1": + resolution: + { + integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw== + } + + "@chevrotain/cst-dts-gen@11.0.3": + resolution: + { + integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ== + } + + "@chevrotain/gast@11.0.3": + resolution: + { + integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q== + } + + "@chevrotain/regexp-to-ast@11.0.3": + resolution: + { + integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA== + } + + "@chevrotain/types@11.0.3": + resolution: + { + integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ== + } + + "@chevrotain/utils@11.0.3": + resolution: + { + integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== + } + + "@clack/core@1.1.0": + resolution: + { + integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA== + } + + "@clack/prompts@1.1.0": + resolution: + { + integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g== + } + + "@codemcp/knowledge@2.1.0": + resolution: + { + integrity: sha512-n+p0oFHHkYY7RUNtoPgh3/SuLHMafwFbZyeKMoBIDfS01atJJRJpLlpri5rpc0a7khEGocvU1/WI3P0OC7TgvA== + } + engines: { node: ">=20.0.0", pnpm: ">=9.0.0" } + hasBin: true + + "@codemcp/skills@2.3.0": + resolution: + { + integrity: sha512-5tc5i0FtWeOFfCGvhBmqX83mTQehTFKW5/EynH0VxByXjGZ4lmPFsM4WXutqe2vj7DxqAbw/XjlPIffSh859cQ== + } + engines: { node: ">=18" } + hasBin: true + + "@docsearch/css@3.9.0": + resolution: + { + integrity: sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA== + } + + "@docsearch/js@3.9.0": + resolution: + { + integrity: sha512-4bKHcye6EkLgRE8ze0vcdshmEqxeiJM77M0JXjef7lrYZfSlMunrDOCqyLjiZyo1+c0BhUqA2QpFartIjuHIjw== + } + + "@docsearch/react@3.9.0": + resolution: + { + integrity: sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ== + } + peerDependencies: + "@types/react": ">= 16.8.0 < 20.0.0" + react: ">= 16.8.0 < 20.0.0" + react-dom: ">= 16.8.0 < 20.0.0" + search-insights: ">= 1 < 3" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + "@esbuild/aix-ppc64@0.21.5": + resolution: + { + integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + } + engines: { node: ">=12" } + cpu: [ppc64] + os: [aix] + + "@esbuild/aix-ppc64@0.27.3": + resolution: + { + integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [aix] + + "@esbuild/android-arm64@0.21.5": + resolution: + { + integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + } + engines: { node: ">=12" } + cpu: [arm64] + os: [android] + + "@esbuild/android-arm64@0.27.3": + resolution: + { + integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [android] + + "@esbuild/android-arm@0.21.5": + resolution: + { + integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + } + engines: { node: ">=12" } + cpu: [arm] + os: [android] + + "@esbuild/android-arm@0.27.3": + resolution: + { + integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA== + } + engines: { node: ">=18" } + cpu: [arm] + os: [android] + + "@esbuild/android-x64@0.21.5": + resolution: + { + integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + } + engines: { node: ">=12" } + cpu: [x64] + os: [android] + + "@esbuild/android-x64@0.27.3": + resolution: + { + integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ== + } + engines: { node: ">=18" } + cpu: [x64] + os: [android] + + "@esbuild/darwin-arm64@0.21.5": + resolution: + { + integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + } + engines: { node: ">=12" } + cpu: [arm64] + os: [darwin] + + "@esbuild/darwin-arm64@0.27.3": + resolution: + { + integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [darwin] + + "@esbuild/darwin-x64@0.21.5": + resolution: + { + integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + } + engines: { node: ">=12" } + cpu: [x64] + os: [darwin] + + "@esbuild/darwin-x64@0.27.3": + resolution: + { + integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg== + } + engines: { node: ">=18" } + cpu: [x64] + os: [darwin] + + "@esbuild/freebsd-arm64@0.21.5": + resolution: + { + integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + } + engines: { node: ">=12" } + cpu: [arm64] + os: [freebsd] + + "@esbuild/freebsd-arm64@0.27.3": + resolution: + { + integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [freebsd] + + "@esbuild/freebsd-x64@0.21.5": + resolution: + { + integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + } + engines: { node: ">=12" } + cpu: [x64] + os: [freebsd] + + "@esbuild/freebsd-x64@0.27.3": + resolution: + { + integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [freebsd] + + "@esbuild/linux-arm64@0.21.5": + resolution: + { + integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + } + engines: { node: ">=12" } + cpu: [arm64] + os: [linux] + + "@esbuild/linux-arm64@0.27.3": + resolution: + { + integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [linux] + + "@esbuild/linux-arm@0.21.5": + resolution: + { + integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + } + engines: { node: ">=12" } + cpu: [arm] + os: [linux] + + "@esbuild/linux-arm@0.27.3": + resolution: + { + integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw== + } + engines: { node: ">=18" } + cpu: [arm] + os: [linux] + + "@esbuild/linux-ia32@0.21.5": + resolution: + { + integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + } + engines: { node: ">=12" } + cpu: [ia32] + os: [linux] + + "@esbuild/linux-ia32@0.27.3": + resolution: + { + integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [linux] + + "@esbuild/linux-loong64@0.21.5": + resolution: + { + integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + } + engines: { node: ">=12" } + cpu: [loong64] + os: [linux] + + "@esbuild/linux-loong64@0.27.3": + resolution: + { + integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA== + } + engines: { node: ">=18" } + cpu: [loong64] + os: [linux] + + "@esbuild/linux-mips64el@0.21.5": + resolution: + { + integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + } + engines: { node: ">=12" } + cpu: [mips64el] + os: [linux] + + "@esbuild/linux-mips64el@0.27.3": + resolution: + { + integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw== + } + engines: { node: ">=18" } + cpu: [mips64el] + os: [linux] + + "@esbuild/linux-ppc64@0.21.5": + resolution: + { + integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + } + engines: { node: ">=12" } + cpu: [ppc64] + os: [linux] + + "@esbuild/linux-ppc64@0.27.3": + resolution: + { + integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [linux] + + "@esbuild/linux-riscv64@0.21.5": + resolution: + { + integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + } + engines: { node: ">=12" } + cpu: [riscv64] + os: [linux] + + "@esbuild/linux-riscv64@0.27.3": + resolution: + { + integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ== + } + engines: { node: ">=18" } + cpu: [riscv64] + os: [linux] + + "@esbuild/linux-s390x@0.21.5": + resolution: + { + integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + } + engines: { node: ">=12" } + cpu: [s390x] + os: [linux] + + "@esbuild/linux-s390x@0.27.3": + resolution: + { + integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw== + } + engines: { node: ">=18" } + cpu: [s390x] + os: [linux] + + "@esbuild/linux-x64@0.21.5": + resolution: + { + integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + } + engines: { node: ">=12" } + cpu: [x64] + os: [linux] + + "@esbuild/linux-x64@0.27.3": + resolution: + { + integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [linux] + + "@esbuild/netbsd-arm64@0.27.3": + resolution: + { + integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [netbsd] + + "@esbuild/netbsd-x64@0.21.5": + resolution: + { + integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + } + engines: { node: ">=12" } + cpu: [x64] + os: [netbsd] + + "@esbuild/netbsd-x64@0.27.3": + resolution: + { + integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [netbsd] + + "@esbuild/openbsd-arm64@0.27.3": + resolution: + { + integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openbsd] + + "@esbuild/openbsd-x64@0.21.5": + resolution: + { + integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + } + engines: { node: ">=12" } + cpu: [x64] + os: [openbsd] + + "@esbuild/openbsd-x64@0.27.3": + resolution: + { + integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ== + } + engines: { node: ">=18" } + cpu: [x64] + os: [openbsd] + + "@esbuild/openharmony-arm64@0.27.3": + resolution: + { + integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openharmony] + + "@esbuild/sunos-x64@0.21.5": + resolution: + { + integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + } + engines: { node: ">=12" } + cpu: [x64] + os: [sunos] + + "@esbuild/sunos-x64@0.27.3": + resolution: + { + integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [sunos] + + "@esbuild/win32-arm64@0.21.5": + resolution: + { + integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + } + engines: { node: ">=12" } + cpu: [arm64] + os: [win32] + + "@esbuild/win32-arm64@0.27.3": + resolution: + { + integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [win32] + + "@esbuild/win32-ia32@0.21.5": + resolution: + { + integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + } + engines: { node: ">=12" } + cpu: [ia32] + os: [win32] + + "@esbuild/win32-ia32@0.27.3": + resolution: + { + integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [win32] + + "@esbuild/win32-x64@0.21.5": + resolution: + { + integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + } + engines: { node: ">=12" } + cpu: [x64] + os: [win32] + + "@esbuild/win32-x64@0.27.3": + resolution: + { + integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [win32] + + "@eslint-community/eslint-utils@4.9.1": + resolution: + { + integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + "@eslint-community/regexpp@4.12.2": + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + "@eslint/config-array@0.21.1": + resolution: + { + integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/config-helpers@0.4.2": + resolution: + { + integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/core@0.17.0": + resolution: + { + integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/eslintrc@3.3.3": + resolution: + { + integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/js@9.18.0": + resolution: + { + integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/js@9.39.2": + resolution: + { + integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/object-schema@2.1.7": + resolution: + { + integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/plugin-kit@0.4.1": + resolution: + { + integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@gar/promise-retry@1.0.2": + resolution: + { + integrity: sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@hono/node-server@1.19.11": + resolution: + { + integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g== + } + engines: { node: ">=18.14.1" } + peerDependencies: + hono: ^4 + + "@humanfs/core@0.19.1": + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + } + engines: { node: ">=18.18.0" } + + "@humanfs/node@0.16.7": + resolution: + { + integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + } + engines: { node: ">=18.18.0" } + + "@humanwhocodes/module-importer@1.0.1": + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + } + engines: { node: ">=12.22" } + + "@humanwhocodes/retry@0.4.3": + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + } + engines: { node: ">=18.18" } + + "@iconify-json/simple-icons@1.2.71": + resolution: + { + integrity: sha512-rNoDFbq1fAYiEexBvrw613/xiUOPEu5MKVV/X8lI64AgdTzLQUUemr9f9fplxUMPoxCBP2rWzlhOEeTHk/Sf0Q== + } + + "@iconify/types@2.0.0": + resolution: + { + integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== + } + + "@iconify/utils@2.3.0": + resolution: + { + integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA== + } + + "@isaacs/cliui@8.0.2": + resolution: + { + integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + } + engines: { node: ">=12" } + + "@isaacs/fs-minipass@4.0.1": + resolution: + { + integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + } + engines: { node: ">=18.0.0" } + + "@istanbuljs/schema@0.1.3": + resolution: + { + integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + } + engines: { node: ">=8" } + + "@jridgewell/gen-mapping@0.3.13": + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + } + + "@jridgewell/resolve-uri@3.1.2": + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + } + engines: { node: ">=6.0.0" } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + } + + "@jridgewell/trace-mapping@0.3.31": + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + } + + "@kwsites/file-exists@1.1.1": + resolution: + { + integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== + } + + "@kwsites/promise-deferred@1.1.1": + resolution: + { + integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== + } + + "@mermaid-js/mermaid-mindmap@9.3.0": + resolution: + { + integrity: sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw== + } + + "@mermaid-js/parser@0.3.0": + resolution: + { + integrity: sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA== + } + + "@modelcontextprotocol/sdk@1.27.1": + resolution: + { + integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA== + } + engines: { node: ">=18" } + peerDependencies: + "@cfworker/json-schema": ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + "@cfworker/json-schema": + optional: true + + "@nodelib/fs.scandir@2.1.5": + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + } + engines: { node: ">= 8" } + + "@nodelib/fs.stat@2.0.5": + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + } + engines: { node: ">= 8" } + + "@nodelib/fs.walk@1.2.8": + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + } + engines: { node: ">= 8" } + + "@npmcli/agent@4.0.0": + resolution: + { + integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/fs@5.0.0": + resolution: + { + integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/git@7.0.2": + resolution: + { + integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/installed-package-contents@4.0.0": + resolution: + { + integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + hasBin: true + + "@npmcli/node-gyp@5.0.0": + resolution: + { + integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/package-json@7.0.5": + resolution: + { + integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/promise-spawn@9.0.1": + resolution: + { + integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/redact@4.0.0": + resolution: + { + integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@npmcli/run-script@10.0.4": + resolution: + { + integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@pkgjs/parseargs@0.11.0": + resolution: + { + integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + } + engines: { node: ">=14" } + + "@rollup/rollup-android-arm-eabi@4.57.1": + resolution: + { + integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg== + } + cpu: [arm] + os: [android] + + "@rollup/rollup-android-arm64@4.57.1": + resolution: + { + integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w== + } + cpu: [arm64] + os: [android] + + "@rollup/rollup-darwin-arm64@4.57.1": + resolution: + { + integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg== + } + cpu: [arm64] + os: [darwin] + + "@rollup/rollup-darwin-x64@4.57.1": + resolution: + { + integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w== + } + cpu: [x64] + os: [darwin] + + "@rollup/rollup-freebsd-arm64@4.57.1": + resolution: + { + integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug== + } + cpu: [arm64] + os: [freebsd] + + "@rollup/rollup-freebsd-x64@4.57.1": + resolution: + { + integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q== + } + cpu: [x64] + os: [freebsd] + + "@rollup/rollup-linux-arm-gnueabihf@4.57.1": + resolution: + { + integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw== + } + cpu: [arm] + os: [linux] + + "@rollup/rollup-linux-arm-musleabihf@4.57.1": + resolution: + { + integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw== + } + cpu: [arm] + os: [linux] + + "@rollup/rollup-linux-arm64-gnu@4.57.1": + resolution: + { + integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g== + } + cpu: [arm64] + os: [linux] + + "@rollup/rollup-linux-arm64-musl@4.57.1": + resolution: + { + integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q== + } + cpu: [arm64] + os: [linux] + + "@rollup/rollup-linux-loong64-gnu@4.57.1": + resolution: + { + integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA== + } + cpu: [loong64] + os: [linux] + + "@rollup/rollup-linux-loong64-musl@4.57.1": + resolution: + { + integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw== + } + cpu: [loong64] + os: [linux] + + "@rollup/rollup-linux-ppc64-gnu@4.57.1": + resolution: + { + integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w== + } + cpu: [ppc64] + os: [linux] + + "@rollup/rollup-linux-ppc64-musl@4.57.1": + resolution: + { + integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw== + } + cpu: [ppc64] + os: [linux] + + "@rollup/rollup-linux-riscv64-gnu@4.57.1": + resolution: + { + integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A== + } + cpu: [riscv64] + os: [linux] + + "@rollup/rollup-linux-riscv64-musl@4.57.1": + resolution: + { + integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw== + } + cpu: [riscv64] + os: [linux] + + "@rollup/rollup-linux-s390x-gnu@4.57.1": + resolution: + { + integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg== + } + cpu: [s390x] + os: [linux] + + "@rollup/rollup-linux-x64-gnu@4.57.1": + resolution: + { + integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg== + } + cpu: [x64] + os: [linux] + + "@rollup/rollup-linux-x64-musl@4.57.1": + resolution: + { + integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw== + } + cpu: [x64] + os: [linux] + + "@rollup/rollup-openbsd-x64@4.57.1": + resolution: + { + integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw== + } + cpu: [x64] + os: [openbsd] + + "@rollup/rollup-openharmony-arm64@4.57.1": + resolution: + { + integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ== + } + cpu: [arm64] + os: [openharmony] + + "@rollup/rollup-win32-arm64-msvc@4.57.1": + resolution: + { + integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ== + } + cpu: [arm64] + os: [win32] + + "@rollup/rollup-win32-ia32-msvc@4.57.1": + resolution: + { + integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew== + } + cpu: [ia32] + os: [win32] + + "@rollup/rollup-win32-x64-gnu@4.57.1": + resolution: + { + integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ== + } + cpu: [x64] + os: [win32] + + "@rollup/rollup-win32-x64-msvc@4.57.1": + resolution: + { + integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA== + } + cpu: [x64] + os: [win32] + + "@shikijs/core@2.5.0": + resolution: + { + integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg== + } + + "@shikijs/engine-javascript@2.5.0": + resolution: + { + integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w== + } + + "@shikijs/engine-oniguruma@2.5.0": + resolution: + { + integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw== + } + + "@shikijs/langs@2.5.0": + resolution: + { + integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w== + } + + "@shikijs/themes@2.5.0": + resolution: + { + integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw== + } + + "@shikijs/transformers@2.5.0": + resolution: + { + integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg== + } + + "@shikijs/types@2.5.0": + resolution: + { + integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw== + } + + "@shikijs/vscode-textmate@10.0.2": + resolution: + { + integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + } + + "@sigstore/bundle@4.0.0": + resolution: + { + integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@sigstore/core@3.1.0": + resolution: + { + integrity: sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@sigstore/protobuf-specs@0.5.0": + resolution: + { + integrity: sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA== + } + engines: { node: ^18.17.0 || >=20.5.0 } + + "@sigstore/sign@4.1.0": + resolution: + { + integrity: sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@sigstore/tuf@4.0.1": + resolution: + { + integrity: sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@sigstore/verify@3.1.0": + resolution: + { + integrity: sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@swc/core-darwin-arm64@1.15.11": + resolution: + { + integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg== + } + engines: { node: ">=10" } + cpu: [arm64] + os: [darwin] + + "@swc/core-darwin-x64@1.15.11": + resolution: + { + integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA== + } + engines: { node: ">=10" } + cpu: [x64] + os: [darwin] + + "@swc/core-linux-arm-gnueabihf@1.15.11": + resolution: + { + integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg== + } + engines: { node: ">=10" } + cpu: [arm] + os: [linux] + + "@swc/core-linux-arm64-gnu@1.15.11": + resolution: + { + integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA== + } + engines: { node: ">=10" } + cpu: [arm64] + os: [linux] + + "@swc/core-linux-arm64-musl@1.15.11": + resolution: + { + integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w== + } + engines: { node: ">=10" } + cpu: [arm64] + os: [linux] + + "@swc/core-linux-x64-gnu@1.15.11": + resolution: + { + integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ== + } + engines: { node: ">=10" } + cpu: [x64] + os: [linux] + + "@swc/core-linux-x64-musl@1.15.11": + resolution: + { + integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw== + } + engines: { node: ">=10" } + cpu: [x64] + os: [linux] + + "@swc/core-win32-arm64-msvc@1.15.11": + resolution: + { + integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA== + } + engines: { node: ">=10" } + cpu: [arm64] + os: [win32] + + "@swc/core-win32-ia32-msvc@1.15.11": + resolution: + { + integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw== + } + engines: { node: ">=10" } + cpu: [ia32] + os: [win32] + + "@swc/core-win32-x64-msvc@1.15.11": + resolution: + { + integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw== + } + engines: { node: ">=10" } + cpu: [x64] + os: [win32] + + "@swc/core@1.15.11": + resolution: + { + integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w== + } + engines: { node: ">=10" } + peerDependencies: + "@swc/helpers": ">=0.5.17" + peerDependenciesMeta: + "@swc/helpers": + optional: true + + "@swc/counter@0.1.3": + resolution: + { + integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== + } + + "@swc/types@0.1.25": + resolution: + { + integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g== + } + + "@tsconfig/node22@22.0.0": + resolution: + { + integrity: sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg== + } + + "@tsconfig/strictest@2.0.5": + resolution: + { + integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg== + } + + "@tufjs/canonical-json@2.0.0": + resolution: + { + integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA== + } + engines: { node: ^16.14.0 || >=18.0.0 } + + "@tufjs/models@4.1.0": + resolution: + { + integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + "@types/chai@5.2.3": + resolution: + { + integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== + } + + "@types/d3-array@3.2.2": + resolution: + { + integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== + } + + "@types/d3-axis@3.0.6": + resolution: + { + integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== + } + + "@types/d3-brush@3.0.6": + resolution: + { + integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== + } + + "@types/d3-chord@3.0.6": + resolution: + { + integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== + } + + "@types/d3-color@3.1.3": + resolution: + { + integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + } + + "@types/d3-contour@3.0.6": + resolution: + { + integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== + } + + "@types/d3-delaunay@6.0.4": + resolution: + { + integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw== + } + + "@types/d3-dispatch@3.0.7": + resolution: + { + integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA== + } + + "@types/d3-drag@3.0.7": + resolution: + { + integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== + } + + "@types/d3-dsv@3.0.7": + resolution: + { + integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== + } + + "@types/d3-ease@3.0.2": + resolution: + { + integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + } + + "@types/d3-fetch@3.0.7": + resolution: + { + integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== + } + + "@types/d3-force@3.0.10": + resolution: + { + integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== + } + + "@types/d3-format@3.0.4": + resolution: + { + integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== + } + + "@types/d3-geo@3.1.0": + resolution: + { + integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== + } + + "@types/d3-hierarchy@3.1.7": + resolution: + { + integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== + } + + "@types/d3-interpolate@3.0.4": + resolution: + { + integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + } + + "@types/d3-path@3.1.1": + resolution: + { + integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== + } + + "@types/d3-polygon@3.0.2": + resolution: + { + integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== + } + + "@types/d3-quadtree@3.0.6": + resolution: + { + integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== + } + + "@types/d3-random@3.0.3": + resolution: + { + integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== + } + + "@types/d3-scale-chromatic@3.1.0": + resolution: + { + integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== + } + + "@types/d3-scale@4.0.9": + resolution: + { + integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== + } + + "@types/d3-selection@3.0.11": + resolution: + { + integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== + } + + "@types/d3-shape@3.1.8": + resolution: + { + integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w== + } + + "@types/d3-time-format@4.0.3": + resolution: + { + integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== + } + + "@types/d3-time@3.0.4": + resolution: + { + integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + } + + "@types/d3-timer@3.0.2": + resolution: + { + integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + } + + "@types/d3-transition@3.0.9": + resolution: + { + integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== + } + + "@types/d3-zoom@3.0.8": + resolution: + { + integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== + } + + "@types/d3@7.4.3": + resolution: + { + integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== + } + + "@types/deep-eql@4.0.2": + resolution: + { + integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + } + + "@types/eslint-config-prettier@6.11.3": + resolution: + { + integrity: sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ== + } + + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + } + + "@types/geojson@7946.0.16": + resolution: + { + integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + } + + "@types/hast@3.0.4": + resolution: + { + integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + } + + "@types/json-schema@7.0.15": + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + } + + "@types/linkify-it@5.0.0": + resolution: + { + integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + } + + "@types/markdown-it@14.1.2": + resolution: + { + integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + } + + "@types/mdast@4.0.4": + resolution: + { + integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + } + + "@types/mdurl@2.0.0": + resolution: + { + integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + } + + "@types/node@22.19.11": + resolution: + { + integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w== + } + + "@types/trusted-types@2.0.7": + resolution: + { + integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + } + + "@types/unist@3.0.3": + resolution: + { + integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + } + + "@types/web-bluetooth@0.0.21": + resolution: + { + integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== + } + + "@typescript-eslint/eslint-plugin@8.21.0": + resolution: + { + integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + + "@typescript-eslint/eslint-plugin@8.56.0": + resolution: + { + integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/parser": ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/parser@8.21.0": + resolution: + { + integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + + "@typescript-eslint/parser@8.56.0": + resolution: + { + integrity: sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/project-service@8.56.0": + resolution: + { + integrity: sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/scope-manager@8.21.0": + resolution: + { + integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/scope-manager@8.56.0": + resolution: + { + integrity: sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/tsconfig-utils@8.56.0": + resolution: + { + integrity: sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/type-utils@8.21.0": + resolution: + { + integrity: sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + + "@typescript-eslint/type-utils@8.56.0": + resolution: + { + integrity: sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/types@8.21.0": + resolution: + { + integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/types@8.56.0": + resolution: + { + integrity: sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@8.21.0": + resolution: + { + integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <5.8.0" + + "@typescript-eslint/typescript-estree@8.56.0": + resolution: + { + integrity: sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/utils@8.21.0": + resolution: + { + integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + + "@typescript-eslint/utils@8.56.0": + resolution: + { + integrity: sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/visitor-keys@8.21.0": + resolution: + { + integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/visitor-keys@8.56.0": + resolution: + { + integrity: sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@ungap/structured-clone@1.3.0": + resolution: + { + integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + } + + "@vitejs/plugin-vue@5.2.4": + resolution: + { + integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA== + } + engines: { node: ^18.0.0 || >=20.0.0 } + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + "@vitest/coverage-v8@3.2.4": + resolution: + { + integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ== + } + peerDependencies: + "@vitest/browser": 3.2.4 + vitest: 3.2.4 + peerDependenciesMeta: + "@vitest/browser": + optional: true + + "@vitest/expect@3.2.4": + resolution: + { + integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== + } + + "@vitest/mocker@3.2.4": + resolution: + { + integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== + } + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + "@vitest/pretty-format@3.2.4": + resolution: + { + integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== + } + + "@vitest/runner@3.2.4": + resolution: + { + integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + } + + "@vitest/snapshot@3.2.4": + resolution: + { + integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + } + + "@vitest/spy@3.2.4": + resolution: + { + integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== + } + + "@vitest/utils@3.2.4": + resolution: + { + integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== + } + + "@vue/compiler-core@3.5.28": + resolution: + { + integrity: sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ== + } + + "@vue/compiler-dom@3.5.28": + resolution: + { + integrity: sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA== + } + + "@vue/compiler-sfc@3.5.28": + resolution: + { + integrity: sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g== + } + + "@vue/compiler-ssr@3.5.28": + resolution: + { + integrity: sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g== + } + + "@vue/devtools-api@7.7.9": + resolution: + { + integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g== + } + + "@vue/devtools-kit@7.7.9": + resolution: + { + integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA== + } + + "@vue/devtools-shared@7.7.9": + resolution: + { + integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA== + } + + "@vue/reactivity@3.5.28": + resolution: + { + integrity: sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw== + } + + "@vue/runtime-core@3.5.28": + resolution: + { + integrity: sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ== + } + + "@vue/runtime-dom@3.5.28": + resolution: + { + integrity: sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA== + } + + "@vue/server-renderer@3.5.28": + resolution: + { + integrity: sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg== + } + peerDependencies: + vue: 3.5.28 + + "@vue/shared@3.5.28": + resolution: + { + integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ== + } + + "@vueuse/core@12.8.2": + resolution: + { + integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ== + } + + "@vueuse/integrations@12.8.2": + resolution: + { + integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g== + } + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + "@vueuse/metadata@12.8.2": + resolution: + { + integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A== + } + + "@vueuse/shared@12.8.2": + resolution: + { + integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w== + } + + abbrev@4.0.0: + resolution: + { + integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + accepts@2.0.0: + resolution: + { + integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + } + engines: { node: ">= 0.6" } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: + { + integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + } + engines: { node: ">=0.4.0" } + hasBin: true + + adm-zip@0.5.16: + resolution: + { + integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ== + } + engines: { node: ">=12.0" } + + agent-base@7.1.4: + resolution: + { + integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + } + engines: { node: ">= 14" } + + ajv-formats@3.0.1: + resolution: + { + integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + } + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: + { + integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + } + + ajv@8.18.0: + resolution: + { + integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + } + + algoliasearch@5.49.0: + resolution: + { + integrity: sha512-Tse7vx7WOvbU+kpq/L3BrBhSWTPbtMa59zIEhMn+Z2NoxZlpcCRUDCRxQ7kDFs1T3CHxDgvb+mDuILiBBpBaAA== + } + engines: { node: ">= 14.0.0" } + + ansi-escapes@7.3.0: + resolution: + { + integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== + } + engines: { node: ">=18" } + + ansi-regex@5.0.1: + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + } + engines: { node: ">=8" } + + ansi-regex@6.2.2: + resolution: + { + integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + } + engines: { node: ">=12" } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + } + engines: { node: ">=8" } + + ansi-styles@6.2.3: + resolution: + { + integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + } + engines: { node: ">=12" } + + anymatch@3.1.3: + resolution: + { + integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + } + engines: { node: ">= 8" } + + argparse@1.0.10: + resolution: + { + integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + } + + argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + } + + assertion-error@2.0.1: + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + } + engines: { node: ">=12" } + + ast-v8-to-istanbul@0.3.11: + resolution: + { + integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw== + } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + } + + balanced-match@4.0.3: + resolution: + { + integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g== + } + engines: { node: 20 || >=22 } + + binary-extensions@2.3.0: + resolution: + { + integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + } + engines: { node: ">=8" } + + birpc@2.9.0: + resolution: + { + integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== + } + + body-parser@2.2.2: + resolution: + { + integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + } + engines: { node: ">=18" } + + brace-expansion@1.1.12: + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + } + + brace-expansion@2.0.2: + resolution: + { + integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + } + + brace-expansion@5.0.2: + resolution: + { + integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw== + } + engines: { node: 20 || >=22 } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + } + engines: { node: ">=8" } + + bytes@3.1.2: + resolution: + { + integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + } + engines: { node: ">= 0.8" } + + cac@6.7.14: + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + } + engines: { node: ">=8" } + + cacache@20.0.3: + resolution: + { + integrity: sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + call-bind-apply-helpers@1.0.2: + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + } + engines: { node: ">= 0.4" } + + call-bound@1.0.4: + resolution: + { + integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + } + engines: { node: ">= 0.4" } + + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + } + engines: { node: ">=6" } + + ccount@2.0.1: + resolution: + { + integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + } + + chai@5.3.3: + resolution: + { + integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw== + } + engines: { node: ">=18" } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + } + engines: { node: ">=10" } + + chalk@5.6.2: + resolution: + { + integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + } + engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } + + character-entities-html4@2.1.0: + resolution: + { + integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + } + + character-entities-legacy@3.0.0: + resolution: + { + integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + } + + check-error@2.1.3: + resolution: + { + integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA== + } + engines: { node: ">= 16" } + + chevrotain-allstar@0.3.1: + resolution: + { + integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw== + } + peerDependencies: + chevrotain: ^11.0.0 + + chevrotain@11.0.3: + resolution: + { + integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw== + } + + chokidar@3.6.0: + resolution: + { + integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + } + engines: { node: ">= 8.10.0" } + + chownr@3.0.0: + resolution: + { + integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + } + engines: { node: ">=18" } + + cli-cursor@5.0.0: + resolution: + { + integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + } + engines: { node: ">=18" } + + cli-truncate@4.0.0: + resolution: + { + integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + } + engines: { node: ">=18" } + + color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + } + engines: { node: ">=7.0.0" } + + color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + } + + colorette@2.0.20: + resolution: + { + integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + } + + comma-separated-tokens@2.0.3: + resolution: + { + integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + } + + commander@12.1.0: + resolution: + { + integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + } + engines: { node: ">=18" } + + commander@13.1.0: + resolution: + { + integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== + } + engines: { node: ">=18" } + + commander@7.2.0: + resolution: + { + integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + } + engines: { node: ">= 10" } + + commander@8.3.0: + resolution: + { + integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + } + engines: { node: ">= 12" } + + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + } + + confbox@0.1.8: + resolution: + { + integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + } + + confbox@0.2.4: + resolution: + { + integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ== + } + + content-disposition@1.0.1: + resolution: + { + integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + } + engines: { node: ">=18" } + + content-type@1.0.5: + resolution: + { + integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + } + engines: { node: ">= 0.6" } + + cookie-signature@1.2.2: + resolution: + { + integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + } + engines: { node: ">=6.6.0" } + + cookie@0.7.2: + resolution: + { + integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + } + engines: { node: ">= 0.6" } + + copy-anything@4.0.5: + resolution: + { + integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA== + } + engines: { node: ">=18" } + + cors@2.8.6: + resolution: + { + integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + } + engines: { node: ">= 0.10" } + + cose-base@1.0.3: + resolution: + { + integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + } + + cose-base@2.2.0: + resolution: + { + integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== + } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + } + engines: { node: ">= 8" } + + csstype@3.2.3: + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + } + + cytoscape-cose-bilkent@4.1.0: + resolution: + { + integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + } + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: + { + integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== + } + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.31.0: + resolution: + { + integrity: sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw== + } + engines: { node: ">=0.10" } + + d3-array@2.12.1: + resolution: + { + integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + } + + d3-array@3.2.4: + resolution: + { + integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + } + engines: { node: ">=12" } + + d3-axis@3.0.0: + resolution: + { + integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + } + engines: { node: ">=12" } + + d3-brush@3.0.0: + resolution: + { + integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + } + engines: { node: ">=12" } + + d3-chord@3.0.1: + resolution: + { + integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + } + engines: { node: ">=12" } + + d3-color@3.1.0: + resolution: + { + integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + } + engines: { node: ">=12" } + + d3-contour@4.0.2: + resolution: + { + integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + } + engines: { node: ">=12" } + + d3-delaunay@6.0.4: + resolution: + { + integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + } + engines: { node: ">=12" } + + d3-dispatch@3.0.1: + resolution: + { + integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + } + engines: { node: ">=12" } + + d3-drag@3.0.0: + resolution: + { + integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + } + engines: { node: ">=12" } + + d3-dsv@3.0.1: + resolution: + { + integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + } + engines: { node: ">=12" } + hasBin: true + + d3-ease@3.0.1: + resolution: + { + integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + } + engines: { node: ">=12" } + + d3-fetch@3.0.1: + resolution: + { + integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + } + engines: { node: ">=12" } + + d3-force@3.0.0: + resolution: + { + integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + } + engines: { node: ">=12" } + + d3-format@3.1.2: + resolution: + { + integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg== + } + engines: { node: ">=12" } + + d3-geo@3.1.1: + resolution: + { + integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== + } + engines: { node: ">=12" } + + d3-hierarchy@3.1.2: + resolution: + { + integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + } + engines: { node: ">=12" } + + d3-interpolate@3.0.1: + resolution: + { + integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + } + engines: { node: ">=12" } + + d3-path@1.0.9: + resolution: + { + integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + } + + d3-path@3.1.0: + resolution: + { + integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + } + engines: { node: ">=12" } + + d3-polygon@3.0.1: + resolution: + { + integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + } + engines: { node: ">=12" } + + d3-quadtree@3.0.1: + resolution: + { + integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + } + engines: { node: ">=12" } + + d3-random@3.0.1: + resolution: + { + integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + } + engines: { node: ">=12" } + + d3-sankey@0.12.3: + resolution: + { + integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== + } + + d3-scale-chromatic@3.1.0: + resolution: + { + integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + } + engines: { node: ">=12" } + + d3-scale@4.0.2: + resolution: + { + integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + } + engines: { node: ">=12" } + + d3-selection@3.0.0: + resolution: + { + integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + } + engines: { node: ">=12" } + + d3-shape@1.3.7: + resolution: + { + integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + } + + d3-shape@3.2.0: + resolution: + { + integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + } + engines: { node: ">=12" } + + d3-time-format@4.1.0: + resolution: + { + integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + } + engines: { node: ">=12" } + + d3-time@3.1.0: + resolution: + { + integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + } + engines: { node: ">=12" } + + d3-timer@3.0.1: + resolution: + { + integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + } + engines: { node: ">=12" } + + d3-transition@3.0.1: + resolution: + { + integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + } + engines: { node: ">=12" } + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: + { + integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + } + engines: { node: ">=12" } + + d3@7.9.0: + resolution: + { + integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + } + engines: { node: ">=12" } + + dagre-d3-es@7.0.11: + resolution: + { + integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw== + } + + dayjs@1.11.13: + resolution: + { + integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + } + + debug@4.4.0: + resolution: + { + integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: + { + integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + } + engines: { node: ">=6" } + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + } + + delaunator@5.0.1: + resolution: + { + integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== + } + + depd@2.0.0: + resolution: + { + integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + } + engines: { node: ">= 0.8" } + + dequal@2.0.3: + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + } + engines: { node: ">=6" } + + devlop@1.1.0: + resolution: + { + integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + } + + dompurify@3.3.1: + resolution: + { + integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q== + } + + dunder-proto@1.0.1: + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + } + engines: { node: ">= 0.4" } + + eastasianwidth@0.2.0: + resolution: + { + integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + } + + ee-first@1.1.1: + resolution: + { + integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + } + + emoji-regex-xs@1.0.0: + resolution: + { + integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + } + + emoji-regex@10.6.0: + resolution: + { + integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + } + + emoji-regex@8.0.0: + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + } + + emoji-regex@9.2.2: + resolution: + { + integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + } + + encodeurl@2.0.0: + resolution: + { + integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + } + engines: { node: ">= 0.8" } + + entities@7.0.1: + resolution: + { + integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + } + engines: { node: ">=0.12" } + + env-paths@2.2.1: + resolution: + { + integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + } + engines: { node: ">=6" } + + environment@1.1.0: + resolution: + { + integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + } + engines: { node: ">=18" } + + err-code@2.0.3: + resolution: + { + integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + } + + es-define-property@1.0.1: + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + } + engines: { node: ">= 0.4" } + + es-errors@1.3.0: + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + } + engines: { node: ">= 0.4" } + + es-module-lexer@1.7.0: + resolution: + { + integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + } + + es-object-atoms@1.1.1: + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + } + engines: { node: ">= 0.4" } + + esbuild@0.21.5: + resolution: + { + integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + } + engines: { node: ">=12" } + hasBin: true + + esbuild@0.27.3: + resolution: + { + integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg== + } + engines: { node: ">=18" } + hasBin: true + + escape-html@1.0.3: + resolution: + { + integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + } + engines: { node: ">=10" } + + eslint-config-prettier@10.1.8: + resolution: + { + integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== + } + hasBin: true + peerDependencies: + eslint: ">=7.0.0" + + eslint-scope@8.4.0: + resolution: + { + integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: + { + integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@5.0.0: + resolution: + { + integrity: sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q== + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@9.39.2: + resolution: + { + integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: + { + integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esprima@4.0.1: + resolution: + { + integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + } + engines: { node: ">=4" } + hasBin: true + + esquery@1.7.0: + resolution: + { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== + } + engines: { node: ">=0.10" } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + } + engines: { node: ">=4.0" } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + } + engines: { node: ">=4.0" } + + estree-walker@2.0.2: + resolution: + { + integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + } + + estree-walker@3.0.3: + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + } + engines: { node: ">=0.10.0" } + + etag@1.8.1: + resolution: + { + integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + } + engines: { node: ">= 0.6" } + + eventemitter3@5.0.4: + resolution: + { + integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== + } + + eventsource-parser@3.0.6: + resolution: + { + integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + } + engines: { node: ">=18.0.0" } + + eventsource@3.0.7: + resolution: + { + integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + } + engines: { node: ">=18.0.0" } + + execa@8.0.1: + resolution: + { + integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + } + engines: { node: ">=16.17" } + + expect-type@1.3.0: + resolution: + { + integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== + } + engines: { node: ">=12.0.0" } + + exponential-backoff@3.1.3: + resolution: + { + integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA== + } + + express-rate-limit@8.3.1: + resolution: + { + integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw== + } + engines: { node: ">= 16" } + peerDependencies: + express: ">= 4.11" + + express@5.2.1: + resolution: + { + integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + } + engines: { node: ">= 18" } + + exsolve@1.0.8: + resolution: + { + integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== + } + + extend-shallow@2.0.1: + resolution: + { + integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + } + engines: { node: ">=0.10.0" } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + } + engines: { node: ">=8.6.0" } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + } + + fast-uri@3.1.0: + resolution: + { + integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + } + + fastq@1.20.1: + resolution: + { + integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== + } + + fdir@6.5.0: + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + } + engines: { node: ">=12.0.0" } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + } + engines: { node: ">=16.0.0" } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + } + engines: { node: ">=8" } + + finalhandler@2.1.1: + resolution: + { + integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + } + engines: { node: ">= 18.0.0" } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + } + engines: { node: ">=10" } + + flat-cache@4.0.1: + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + } + engines: { node: ">=16" } + + flatted@3.3.3: + resolution: + { + integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + } + + focus-trap@7.8.0: + resolution: + { + integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA== + } + + foreground-child@3.3.1: + resolution: + { + integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + } + engines: { node: ">=14" } + + forwarded@0.2.0: + resolution: + { + integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + } + engines: { node: ">= 0.6" } + + fresh@2.0.0: + resolution: + { + integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + } + engines: { node: ">= 0.8" } + + fs-minipass@3.0.3: + resolution: + { + integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + } + + get-east-asian-width@1.5.0: + resolution: + { + integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + } + engines: { node: ">=18" } + + get-intrinsic@1.3.0: + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + } + engines: { node: ">= 0.4" } + + get-proto@1.0.1: + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + } + engines: { node: ">= 0.4" } + + get-stream@8.0.1: + resolution: + { + integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + } + engines: { node: ">=16" } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + } + engines: { node: ">= 6" } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + } + engines: { node: ">=10.13.0" } + + glob@10.5.0: + resolution: + { + integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + } + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@13.0.5: + resolution: + { + integrity: sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw== + } + engines: { node: 20 || >=22 } + + globals@14.0.0: + resolution: + { + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + } + engines: { node: ">=18" } + + globals@15.15.0: + resolution: + { + integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + } + engines: { node: ">=18" } + + gopd@1.2.0: + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + } + engines: { node: ">= 0.4" } + + graceful-fs@4.2.11: + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + } + + graphemer@1.4.0: + resolution: + { + integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + } + + gray-matter@4.0.3: + resolution: + { + integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + } + engines: { node: ">=6.0" } + + hachure-fill@0.5.2: + resolution: + { + integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg== + } + + has-flag@3.0.0: + resolution: + { + integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + } + engines: { node: ">=4" } + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + } + engines: { node: ">=8" } + + has-symbols@1.1.0: + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + } + engines: { node: ">= 0.4" } + + hasown@2.0.2: + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + } + engines: { node: ">= 0.4" } + + hast-util-to-html@9.0.5: + resolution: + { + integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + } + + hast-util-whitespace@3.0.0: + resolution: + { + integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + } + + hono@4.12.8: + resolution: + { + integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A== + } + engines: { node: ">=16.9.0" } + + hookable@5.5.3: + resolution: + { + integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + } + + hosted-git-info@9.0.2: + resolution: + { + integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + html-escaper@2.0.2: + resolution: + { + integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + } + + html-void-elements@3.0.0: + resolution: + { + integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + } + + http-cache-semantics@4.2.0: + resolution: + { + integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== + } + + http-errors@2.0.1: + resolution: + { + integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + } + engines: { node: ">= 0.8" } + + http-proxy-agent@7.0.2: + resolution: + { + integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + } + engines: { node: ">= 14" } + + https-proxy-agent@7.0.6: + resolution: + { + integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + } + engines: { node: ">= 14" } + + human-signals@5.0.0: + resolution: + { + integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + } + engines: { node: ">=16.17.0" } + + husky@9.1.7: + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== + } + engines: { node: ">=18" } + hasBin: true + + iconv-lite@0.6.3: + resolution: + { + integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + } + engines: { node: ">=0.10.0" } + + iconv-lite@0.7.2: + resolution: + { + integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + } + engines: { node: ">=0.10.0" } + + ignore-by-default@1.0.1: + resolution: + { + integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + } + + ignore-walk@8.0.0: + resolution: + { + integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + } + engines: { node: ">= 4" } + + ignore@7.0.5: + resolution: + { + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + } + engines: { node: ">= 4" } + + import-fresh@3.3.1: + resolution: + { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + } + engines: { node: ">=6" } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + } + engines: { node: ">=0.8.19" } + + inherits@2.0.4: + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + } + + ini@6.0.0: + resolution: + { + integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + internmap@1.0.1: + resolution: + { + integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + } + + internmap@2.0.3: + resolution: + { + integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + } + engines: { node: ">=12" } + + ip-address@10.1.0: + resolution: + { + integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q== + } + engines: { node: ">= 12" } + + ipaddr.js@1.9.1: + resolution: + { + integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + } + engines: { node: ">= 0.10" } + + is-binary-path@2.1.0: + resolution: + { + integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + } + engines: { node: ">=8" } + + is-extendable@0.1.1: + resolution: + { + integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + } + engines: { node: ">=0.10.0" } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + } + engines: { node: ">=0.10.0" } + + is-fullwidth-code-point@3.0.0: + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + } + engines: { node: ">=8" } + + is-fullwidth-code-point@4.0.0: + resolution: + { + integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + } + engines: { node: ">=12" } + + is-fullwidth-code-point@5.1.0: + resolution: + { + integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== + } + engines: { node: ">=18" } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + } + engines: { node: ">=0.10.0" } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + } + engines: { node: ">=0.12.0" } + + is-promise@4.0.0: + resolution: + { + integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + } + + is-stream@3.0.0: + resolution: + { + integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + is-what@5.5.0: + resolution: + { + integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw== + } + engines: { node: ">=18" } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + } + + isexe@4.0.0: + resolution: + { + integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw== + } + engines: { node: ">=20" } + + istanbul-lib-coverage@3.2.2: + resolution: + { + integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + } + engines: { node: ">=8" } + + istanbul-lib-report@3.0.1: + resolution: + { + integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + } + engines: { node: ">=10" } + + istanbul-lib-source-maps@5.0.6: + resolution: + { + integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + } + engines: { node: ">=10" } + + istanbul-reports@3.2.0: + resolution: + { + integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + } + engines: { node: ">=8" } + + jackspeak@3.4.3: + resolution: + { + integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + } + + jose@6.2.1: + resolution: + { + integrity: sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw== + } + + js-tokens@10.0.0: + resolution: + { + integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q== + } + + js-tokens@9.0.1: + resolution: + { + integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + } + + js-yaml@3.14.2: + resolution: + { + integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== + } + hasBin: true + + js-yaml@4.1.1: + resolution: + { + integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + } + hasBin: true + + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + } + + json-parse-even-better-errors@5.0.0: + resolution: + { + integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + } + + json-schema-traverse@1.0.0: + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + } + + json-schema-typed@8.0.2: + resolution: + { + integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + } + + jsonparse@1.3.1: + resolution: + { + integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + } + engines: { "0": node >= 0.2.0 } + + katex@0.16.28: + resolution: + { + integrity: sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg== + } + hasBin: true + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + } + + khroma@2.1.0: + resolution: + { + integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== + } + + kind-of@6.0.3: + resolution: + { + integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + } + engines: { node: ">=0.10.0" } + + kolorist@1.8.0: + resolution: + { + integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + } + + langium@3.0.0: + resolution: + { + integrity: sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg== + } + engines: { node: ">=16.0.0" } + + layout-base@1.0.2: + resolution: + { + integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + } + + layout-base@2.0.1: + resolution: + { + integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== + } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + } + engines: { node: ">= 0.8.0" } + + lilconfig@3.1.3: + resolution: + { + integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + } + engines: { node: ">=14" } + + lint-staged@15.5.2: + resolution: + { + integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w== + } + engines: { node: ">=18.12.0" } + hasBin: true + + listr2@8.3.3: + resolution: + { + integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ== + } + engines: { node: ">=18.0.0" } + + local-pkg@1.1.2: + resolution: + { + integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== + } + engines: { node: ">=14" } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + } + engines: { node: ">=10" } + + lodash-es@4.17.21: + resolution: + { + integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + } + + lodash-es@4.17.23: + resolution: + { + integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== + } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + } + + log-update@6.1.0: + resolution: + { + integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== + } + engines: { node: ">=18" } + + loupe@3.2.1: + resolution: + { + integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ== + } + + lru-cache@10.4.3: + resolution: + { + integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + } + + lru-cache@11.2.6: + resolution: + { + integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ== + } + engines: { node: 20 || >=22 } + + magic-string@0.30.21: + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + } + + magicast@0.3.5: + resolution: + { + integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + } + + make-dir@4.0.0: + resolution: + { + integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + } + engines: { node: ">=10" } + + make-fetch-happen@15.0.4: + resolution: + { + integrity: sha512-vM2sG+wbVeVGYcCm16mM3d5fuem9oC28n436HjsGO3LcxoTI8LNVa4rwZDn3f76+cWyT4GGJDxjTYU1I2nr6zw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + mark.js@8.11.1: + resolution: + { + integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + } + + marked@13.0.3: + resolution: + { + integrity: sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA== + } + engines: { node: ">= 18" } + hasBin: true + + math-intrinsics@1.1.0: + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + } + engines: { node: ">= 0.4" } + + mdast-util-to-hast@13.2.1: + resolution: + { + integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== + } + + media-typer@1.1.0: + resolution: + { + integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + } + engines: { node: ">= 0.8" } + + merge-descriptors@2.0.0: + resolution: + { + integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + } + engines: { node: ">=18" } + + merge-stream@2.0.0: + resolution: + { + integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + } + engines: { node: ">= 8" } + + mermaid@11.4.1: + resolution: + { + integrity: sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A== + } + + micromark-util-character@2.1.1: + resolution: + { + integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + } + + micromark-util-encode@2.0.1: + resolution: + { + integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + } + + micromark-util-sanitize-uri@2.0.1: + resolution: + { + integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + } + + micromark-util-symbol@2.0.1: + resolution: + { + integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + } + + micromark-util-types@2.0.2: + resolution: + { + integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + } + engines: { node: ">=8.6" } + + mime-db@1.54.0: + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + } + engines: { node: ">= 0.6" } + + mime-types@3.0.2: + resolution: + { + integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + } + engines: { node: ">=18" } + + mimic-fn@4.0.0: + resolution: + { + integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + } + engines: { node: ">=12" } + + mimic-function@5.0.1: + resolution: + { + integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + } + engines: { node: ">=18" } + + minimatch@10.2.1: + resolution: + { + integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A== + } + engines: { node: 20 || >=22 } + + minimatch@3.1.2: + resolution: + { + integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + } + + minimatch@9.0.5: + resolution: + { + integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + } + engines: { node: ">=16 || 14 >=14.17" } + + minipass-collect@2.0.1: + resolution: + { + integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== + } + engines: { node: ">=16 || 14 >=14.17" } + + minipass-fetch@5.0.2: + resolution: + { + integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + minipass-flush@1.0.5: + resolution: + { + integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + } + engines: { node: ">= 8" } + + minipass-pipeline@1.2.4: + resolution: + { + integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + } + engines: { node: ">=8" } + + minipass-sized@2.0.0: + resolution: + { + integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA== + } + engines: { node: ">=8" } + + minipass@3.3.6: + resolution: + { + integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + } + engines: { node: ">=8" } + + minipass@7.1.3: + resolution: + { + integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + } + engines: { node: ">=16 || 14 >=14.17" } + + minisearch@7.2.0: + resolution: + { + integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg== + } + + minizlib@3.1.0: + resolution: + { + integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== + } + engines: { node: ">= 18" } + + mitt@3.0.1: + resolution: + { + integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + } + + mlly@1.8.0: + resolution: + { + integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== + } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + } + + nanoid@3.3.11: + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + } + + negotiator@1.0.0: + resolution: + { + integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + } + engines: { node: ">= 0.6" } + + node-gyp@12.2.0: + resolution: + { + integrity: sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + hasBin: true + + nodemon@3.1.11: + resolution: + { + integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g== + } + engines: { node: ">=10" } + hasBin: true + + non-layered-tidy-tree-layout@2.0.2: + resolution: + { + integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw== + } + + nopt@9.0.0: + resolution: + { + integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + hasBin: true + + normalize-path@3.0.0: + resolution: + { + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + } + engines: { node: ">=0.10.0" } + + npm-bundled@5.0.0: + resolution: + { + integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-install-checks@8.0.0: + resolution: + { + integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-normalize-package-bin@5.0.0: + resolution: + { + integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-package-arg@13.0.2: + resolution: + { + integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-packlist@10.0.4: + resolution: + { + integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-pick-manifest@11.0.3: + resolution: + { + integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-registry-fetch@19.1.1: + resolution: + { + integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + npm-run-path@5.3.0: + resolution: + { + integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + object-assign@4.1.1: + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + } + engines: { node: ">=0.10.0" } + + object-inspect@1.13.4: + resolution: + { + integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + } + engines: { node: ">= 0.4" } + + on-finished@2.4.1: + resolution: + { + integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + } + engines: { node: ">= 0.8" } + + once@1.4.0: + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + } + + onetime@6.0.0: + resolution: + { + integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + } + engines: { node: ">=12" } + + onetime@7.0.0: + resolution: + { + integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + } + engines: { node: ">=18" } + + oniguruma-to-es@3.1.1: + resolution: + { + integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ== + } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + } + engines: { node: ">= 0.8.0" } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + } + engines: { node: ">=10" } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + } + engines: { node: ">=10" } + + p-map@7.0.4: + resolution: + { + integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ== + } + engines: { node: ">=18" } + + package-json-from-dist@1.0.1: + resolution: + { + integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + } + + package-manager-detector@1.6.0: + resolution: + { + integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA== + } + + pacote@21.3.1: + resolution: + { + integrity: sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + hasBin: true + + parent-module@1.0.1: + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + } + engines: { node: ">=6" } + + parseurl@1.3.3: + resolution: + { + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + } + engines: { node: ">= 0.8" } + + path-data-parser@0.1.0: + resolution: + { + integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== + } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + } + engines: { node: ">=8" } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + } + engines: { node: ">=8" } + + path-key@4.0.0: + resolution: + { + integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + } + engines: { node: ">=12" } + + path-scurry@1.11.1: + resolution: + { + integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + } + engines: { node: ">=16 || 14 >=14.18" } + + path-scurry@2.0.1: + resolution: + { + integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA== + } + engines: { node: 20 || >=22 } + + path-to-regexp@8.3.0: + resolution: + { + integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + } + + pathe@2.0.3: + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + } + + pathval@2.0.1: + resolution: + { + integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== + } + engines: { node: ">= 14.16" } + + perfect-debounce@1.0.0: + resolution: + { + integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + } + + picomatch@2.3.1: + resolution: + { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + } + engines: { node: ">=8.6" } + + picomatch@4.0.3: + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + } + engines: { node: ">=12" } + + pidtree@0.6.0: + resolution: + { + integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + } + engines: { node: ">=0.10" } + hasBin: true + + pkce-challenge@5.0.1: + resolution: + { + integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + } + engines: { node: ">=16.20.0" } + + pkg-types@1.3.1: + resolution: + { + integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + } + + pkg-types@2.3.0: + resolution: + { + integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + } + + points-on-curve@0.2.0: + resolution: + { + integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A== + } + + points-on-path@0.2.1: + resolution: + { + integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g== + } + + postcss@8.5.6: + resolution: + { + integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + } + engines: { node: ^10 || ^12 || >=14 } + + preact@10.28.4: + resolution: + { + integrity: sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ== + } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + } + engines: { node: ">= 0.8.0" } + + prettier@3.8.1: + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + } + engines: { node: ">=14" } + hasBin: true + + proc-log@6.1.0: + resolution: + { + integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + promise-retry@2.0.1: + resolution: + { + integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + } + engines: { node: ">=10" } + + property-information@7.1.0: + resolution: + { + integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + } + + proxy-addr@2.0.7: + resolution: + { + integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + } + engines: { node: ">= 0.10" } + + pstree.remy@1.1.8: + resolution: + { + integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + } + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + } + engines: { node: ">=6" } + + qs@6.15.0: + resolution: + { + integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== + } + engines: { node: ">=0.6" } + + quansync@0.2.11: + resolution: + { + integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== + } + + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + } + + range-parser@1.2.1: + resolution: + { + integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + } + engines: { node: ">= 0.6" } + + raw-body@3.0.2: + resolution: + { + integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + } + engines: { node: ">= 0.10" } + + readdirp@3.6.0: + resolution: + { + integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + } + engines: { node: ">=8.10.0" } + + regex-recursion@6.0.2: + resolution: + { + integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== + } + + regex-utilities@2.3.0: + resolution: + { + integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + } + + regex@6.1.0: + resolution: + { + integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== + } + + require-from-string@2.0.2: + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + } + engines: { node: ">=0.10.0" } + + resolve-from@4.0.0: + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + } + engines: { node: ">=4" } + + restore-cursor@5.1.0: + resolution: + { + integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + } + engines: { node: ">=18" } + + retry@0.12.0: + resolution: + { + integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + } + engines: { node: ">= 4" } + + retry@0.13.1: + resolution: + { + integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + } + engines: { node: ">= 4" } + + reusify@1.1.0: + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + } + engines: { iojs: ">=1.0.0", node: ">=0.10.0" } + + rfdc@1.4.1: + resolution: + { + integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + } + + rimraf@6.1.3: + resolution: + { + integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA== + } + engines: { node: 20 || >=22 } + hasBin: true + + robust-predicates@3.0.2: + resolution: + { + integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + } + + rollup@4.57.1: + resolution: + { + integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A== + } + engines: { node: ">=18.0.0", npm: ">=8.0.0" } + hasBin: true + + roughjs@4.6.6: + resolution: + { + integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ== + } + + router@2.2.0: + resolution: + { + integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + } + engines: { node: ">= 18" } + + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + } + + rw@1.3.3: + resolution: + { + integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + } + + safer-buffer@2.1.2: + resolution: + { + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + } + + search-insights@2.17.3: + resolution: + { + integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ== + } + + section-matter@1.0.0: + resolution: + { + integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + } + engines: { node: ">=4" } + + semver@7.7.4: + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + } + engines: { node: ">=10" } + hasBin: true + + send@1.2.1: + resolution: + { + integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + } + engines: { node: ">= 18" } + + serve-static@2.2.1: + resolution: + { + integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + } + engines: { node: ">= 18" } + + setprototypeof@1.2.0: + resolution: + { + integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + } + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + } + engines: { node: ">=8" } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + } + engines: { node: ">=8" } + + shiki@2.5.0: + resolution: + { + integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ== + } + + side-channel-list@1.0.0: + resolution: + { + integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + } + engines: { node: ">= 0.4" } + + side-channel-map@1.0.1: + resolution: + { + integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + } + engines: { node: ">= 0.4" } + + side-channel-weakmap@1.0.2: + resolution: + { + integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + } + engines: { node: ">= 0.4" } + + side-channel@1.1.0: + resolution: + { + integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + } + engines: { node: ">= 0.4" } + + siginfo@2.0.0: + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + } + + signal-exit@4.1.0: + resolution: + { + integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + } + engines: { node: ">=14" } + + sigstore@4.1.0: + resolution: + { + integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + simple-git@3.33.0: + resolution: + { + integrity: sha512-D4V/tGC2sjsoNhoMybKyGoE+v8A60hRawKQ1iFRA1zwuDgGZCBJ4ByOzZ5J8joBbi4Oam0qiPH+GhzmSBwbJng== + } + + simple-update-notifier@2.0.0: + resolution: + { + integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + } + engines: { node: ">=10" } + + sisteransi@1.0.5: + resolution: + { + integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + } + + slice-ansi@5.0.0: + resolution: + { + integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + } + engines: { node: ">=12" } + + slice-ansi@7.1.2: + resolution: + { + integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== + } + engines: { node: ">=18" } + + smart-buffer@4.2.0: + resolution: + { + integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + } + engines: { node: ">= 6.0.0", npm: ">= 3.0.0" } + + socks-proxy-agent@8.0.5: + resolution: + { + integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== + } + engines: { node: ">= 14" } + + socks@2.8.7: + resolution: + { + integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A== + } + engines: { node: ">= 10.0.0", npm: ">= 3.0.0" } + + source-map-js@1.2.1: + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + } + engines: { node: ">=0.10.0" } + + space-separated-tokens@2.0.2: + resolution: + { + integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + } + + spdx-exceptions@2.5.0: + resolution: + { + integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + } + + spdx-expression-parse@4.0.0: + resolution: + { + integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== + } + + spdx-license-ids@3.0.23: + resolution: + { + integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== + } + + speakingurl@14.0.1: + resolution: + { + integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== + } + engines: { node: ">=0.10.0" } + + sprintf-js@1.0.3: + resolution: + { + integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + } + + ssri@13.0.1: + resolution: + { + integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + stackback@0.0.2: + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + } + + statuses@2.0.2: + resolution: + { + integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + } + engines: { node: ">= 0.8" } + + std-env@3.10.0: + resolution: + { + integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + } + + string-argv@0.3.2: + resolution: + { + integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + } + engines: { node: ">=0.6.19" } + + string-width@4.2.3: + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + } + engines: { node: ">=8" } + + string-width@5.1.2: + resolution: + { + integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + } + engines: { node: ">=12" } + + string-width@7.2.0: + resolution: + { + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + } + engines: { node: ">=18" } + + stringify-entities@4.0.4: + resolution: + { + integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + } + + strip-ansi@6.0.1: + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + } + engines: { node: ">=8" } + + strip-ansi@7.1.2: + resolution: + { + integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + } + engines: { node: ">=12" } + + strip-bom-string@1.0.0: + resolution: + { + integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + } + engines: { node: ">=0.10.0" } + + strip-final-newline@3.0.0: + resolution: + { + integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + } + engines: { node: ">=12" } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + } + engines: { node: ">=8" } + + strip-literal@3.1.0: + resolution: + { + integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + } + + stylis@4.3.6: + resolution: + { + integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + } + + superjson@2.2.6: + resolution: + { + integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA== + } + engines: { node: ">=16" } + + supports-color@5.5.0: + resolution: + { + integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + } + engines: { node: ">=4" } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + } + engines: { node: ">=8" } + + tabbable@6.4.0: + resolution: + { + integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg== + } + + tar@7.5.11: + resolution: + { + integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ== + } + engines: { node: ">=18" } + + test-exclude@7.0.1: + resolution: + { + integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + } + engines: { node: ">=18" } + + tinybench@2.9.0: + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + } + + tinyexec@0.3.2: + resolution: + { + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + } + + tinyexec@1.0.2: + resolution: + { + integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== + } + engines: { node: ">=18" } + + tinyglobby@0.2.15: + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + } + engines: { node: ">=12.0.0" } + + tinypool@1.1.1: + resolution: + { + integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + } + engines: { node: ^18.0.0 || >=20.0.0 } + + tinyrainbow@2.0.0: + resolution: + { + integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + } + engines: { node: ">=14.0.0" } + + tinyspy@4.0.4: + resolution: + { + integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q== + } + engines: { node: ">=14.0.0" } + + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + } + engines: { node: ">=8.0" } + + toidentifier@1.0.1: + resolution: + { + integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + } + engines: { node: ">=0.6" } + + touch@3.1.1: + resolution: + { + integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + } + hasBin: true + + trim-lines@3.0.1: + resolution: + { + integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + } + + ts-api-utils@2.4.0: + resolution: + { + integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== + } + engines: { node: ">=18.12" } + peerDependencies: + typescript: ">=4.8.4" + + ts-dedent@2.2.0: + resolution: + { + integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + } + engines: { node: ">=6.10" } + + tuf-js@4.1.0: + resolution: + { + integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + turbo-darwin-64@2.8.10: + resolution: + { + integrity: sha512-A03fXh+B7S8mL3PbdhTd+0UsaGrhfyPkODvzBDpKRY7bbeac4MDFpJ7I+Slf2oSkCEeSvHKR7Z4U71uKRUfX7g== + } + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.8.10: + resolution: + { + integrity: sha512-sidzowgWL3s5xCHLeqwC9M3s9M0i16W1nuQF3Mc7fPHpZ+YPohvcbVFBB2uoRRHYZg6yBnwD4gyUHKTeXfwtXA== + } + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.8.10: + resolution: + { + integrity: sha512-YK9vcpL3TVtqonB021XwgaQhY9hJJbKKUhLv16osxV0HkcQASQWUqR56yMge7puh6nxU67rQlTq1b7ksR1T3KA== + } + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.8.10: + resolution: + { + integrity: sha512-3+j2tL0sG95iBJTm+6J8/45JsETQABPqtFyYjVjBbi6eVGdtNTiBmHNKrbvXRlQ3ZbUG75bKLaSSDHSEEN+btQ== + } + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.8.10: + resolution: + { + integrity: sha512-hdeF5qmVY/NFgiucf8FW0CWJWtyT2QPm5mIsX0W1DXAVzqKVXGq+Zf+dg4EUngAFKjDzoBeN6ec2Fhajwfztkw== + } + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.8.10: + resolution: + { + integrity: sha512-QGdr/Q8LWmj+ITMkSvfiz2glf0d7JG0oXVzGL3jxkGqiBI1zXFj20oqVY0qWi+112LO9SVrYdpHS0E/oGFrMbQ== + } + cpu: [arm64] + os: [win32] + + turbo@2.8.10: + resolution: + { + integrity: sha512-OxbzDES66+x7nnKGg2MwBA1ypVsZoDTLHpeaP4giyiHSixbsiTaMyeJqbEyvBdp5Cm28fc+8GG6RdQtic0ijwQ== + } + hasBin: true + + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + } + engines: { node: ">= 0.8.0" } + + type-is@2.0.1: + resolution: + { + integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + } + engines: { node: ">= 0.6" } + + typescript-eslint@8.21.0: + resolution: + { + integrity: sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw== + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + + typescript@5.9.3: + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + } + engines: { node: ">=14.17" } + hasBin: true + + ufo@1.6.3: + resolution: + { + integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q== + } + + undefsafe@2.0.5: + resolution: + { + integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + } + + undici-types@6.21.0: + resolution: + { + integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + } + + unique-filename@5.0.0: + resolution: + { + integrity: sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + unique-slug@6.0.0: + resolution: + { + integrity: sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + unist-util-is@6.0.1: + resolution: + { + integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g== + } + + unist-util-position@5.0.0: + resolution: + { + integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + } + + unist-util-stringify-position@4.0.0: + resolution: + { + integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + } + + unist-util-visit-parents@6.0.2: + resolution: + { + integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ== + } + + unist-util-visit@5.1.0: + resolution: + { + integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== + } + + unpipe@1.0.0: + resolution: + { + integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + } + engines: { node: ">= 0.8" } + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + } + + uuid@9.0.1: + resolution: + { + integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + } + hasBin: true + + validate-npm-package-name@7.0.2: + resolution: + { + integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A== + } + engines: { node: ^20.17.0 || >=22.9.0 } + + vary@1.1.2: + resolution: + { + integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + } + engines: { node: ">= 0.8" } + + vfile-message@4.0.3: + resolution: + { + integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== + } + + vfile@6.0.3: + resolution: + { + integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + } + + vite-node@3.2.4: + resolution: + { + integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + } + engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } + hasBin: true + + vite@5.4.21: + resolution: + { + integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + } + engines: { node: ^18.0.0 || >=20.0.0 } + hasBin: true + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@7.3.1: + resolution: + { + integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA== + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + 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 + + vitepress-plugin-mermaid@2.0.17: + resolution: + { + integrity: sha512-IUzYpwf61GC6k0XzfmAmNrLvMi9TRrVRMsUyCA8KNXhg/mQ1VqWnO0/tBVPiX5UoKF1mDUwqn5QV4qAJl6JnUg== + } + peerDependencies: + mermaid: 10 || 11 + vitepress: ^1.0.0 || ^1.0.0-alpha + + vitepress@1.6.2: + resolution: + { + integrity: sha512-pKAgner8wqetwyL6VyjhJnrw/Kwc8hNlwaS/efUlEBhQsRiCvjIsKqjWyjyUMa6u39ktMle16nYAUOcZ6MhV6Q== + } + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vitest@3.2.4: + resolution: + { + integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + } + engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } + hasBin: true + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.4 + "@vitest/ui": 3.2.4 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-jsonrpc@8.2.0: + resolution: + { + integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== + } + engines: { node: ">=14.0.0" } + + vscode-languageserver-protocol@3.17.5: + resolution: + { + integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== + } + + vscode-languageserver-textdocument@1.0.12: + resolution: + { + integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== + } + + vscode-languageserver-types@3.17.5: + resolution: + { + integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== + } + + vscode-languageserver@9.0.1: + resolution: + { + integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== + } + hasBin: true + + vscode-uri@3.0.8: + resolution: + { + integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + } + + vue@3.5.28: + resolution: + { + integrity: sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg== + } + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + } + engines: { node: ">= 8" } + hasBin: true + + which@6.0.1: + resolution: + { + integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg== + } + engines: { node: ^20.17.0 || >=22.9.0 } + hasBin: true + + why-is-node-running@2.3.0: + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + } + engines: { node: ">=8" } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + } + engines: { node: ">=0.10.0" } + + wrap-ansi@7.0.0: + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + } + engines: { node: ">=10" } + + wrap-ansi@8.1.0: + resolution: + { + integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + } + engines: { node: ">=12" } + + wrap-ansi@9.0.2: + resolution: + { + integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== + } + engines: { node: ">=18" } + + wrappy@1.0.2: + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + } + + yallist@4.0.0: + resolution: + { + integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + } + + yallist@5.0.0: + resolution: + { + integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + } + engines: { node: ">=18" } + + yaml@2.8.2: + resolution: + { + integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== + } + engines: { node: ">= 14.6" } + hasBin: true + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + } + engines: { node: ">=10" } + + zod-to-json-schema@3.25.1: + resolution: + { + integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + } + peerDependencies: + zod: ^3.25 || ^4 + + zod@4.3.6: + resolution: + { + integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== + } + + zwitch@2.0.4: + resolution: + { + integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== + } + +snapshots: + "@algolia/abtesting@1.15.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)(search-insights@2.17.3)": + dependencies: + "@algolia/autocomplete-plugin-algolia-insights": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)(search-insights@2.17.3) + "@algolia/autocomplete-shared": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0) + transitivePeerDependencies: + - "@algolia/client-search" + - algoliasearch + - search-insights + + "@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)(search-insights@2.17.3)": + dependencies: + "@algolia/autocomplete-shared": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - "@algolia/client-search" + - algoliasearch + + "@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)": + dependencies: + "@algolia/autocomplete-shared": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0) + "@algolia/client-search": 5.49.0 + algoliasearch: 5.49.0 + + "@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)": + dependencies: + "@algolia/client-search": 5.49.0 + algoliasearch: 5.49.0 + + "@algolia/client-abtesting@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/client-analytics@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/client-common@5.49.0": {} + + "@algolia/client-insights@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/client-personalization@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/client-query-suggestions@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/client-search@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/ingestion@1.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/monitoring@1.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/recommend@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + "@algolia/requester-browser-xhr@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + + "@algolia/requester-fetch@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + + "@algolia/requester-node-http@5.49.0": + dependencies: + "@algolia/client-common": 5.49.0 + + "@ampproject/remapping@2.3.0": + dependencies: + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 + + "@antfu/install-pkg@1.1.0": + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.0.2 + + "@antfu/utils@8.1.1": {} + + "@babel/helper-string-parser@7.27.1": {} + + "@babel/helper-validator-identifier@7.28.5": {} + + "@babel/parser@7.29.0": + dependencies: + "@babel/types": 7.29.0 + + "@babel/types@7.29.0": + dependencies: + "@babel/helper-string-parser": 7.27.1 + "@babel/helper-validator-identifier": 7.28.5 + + "@bcoe/v8-coverage@1.0.2": {} + + "@braintree/sanitize-url@6.0.4": + optional: true + + "@braintree/sanitize-url@7.1.1": {} + + "@chevrotain/cst-dts-gen@11.0.3": + dependencies: + "@chevrotain/gast": 11.0.3 + "@chevrotain/types": 11.0.3 + lodash-es: 4.17.21 + + "@chevrotain/gast@11.0.3": + dependencies: + "@chevrotain/types": 11.0.3 + lodash-es: 4.17.21 + + "@chevrotain/regexp-to-ast@11.0.3": {} + + "@chevrotain/types@11.0.3": {} + + "@chevrotain/utils@11.0.3": {} + + "@clack/core@1.1.0": + dependencies: + sisteransi: 1.0.5 + + "@clack/prompts@1.1.0": + dependencies: + "@clack/core": 1.1.0 + sisteransi: 1.0.5 + + "@codemcp/knowledge@2.1.0(zod@4.3.6)": + dependencies: + "@modelcontextprotocol/sdk": 1.27.1(zod@4.3.6) + adm-zip: 0.5.16 + commander: 12.1.0 + transitivePeerDependencies: + - "@cfworker/json-schema" + - supports-color + - zod + + "@codemcp/skills@2.3.0": + dependencies: + ajv: 8.18.0 + gray-matter: 4.0.3 + js-yaml: 4.1.1 + pacote: 21.3.1 + simple-git: 3.33.0 + transitivePeerDependencies: + - supports-color + + "@docsearch/css@3.9.0": {} + + "@docsearch/js@3.9.0(@algolia/client-search@5.49.0)(search-insights@2.17.3)": + dependencies: + "@docsearch/react": 3.9.0(@algolia/client-search@5.49.0)(search-insights@2.17.3) + preact: 10.28.4 + transitivePeerDependencies: + - "@algolia/client-search" + - "@types/react" + - react + - react-dom + - search-insights + + "@docsearch/react@3.9.0(@algolia/client-search@5.49.0)(search-insights@2.17.3)": + dependencies: + "@algolia/autocomplete-core": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0)(search-insights@2.17.3) + "@algolia/autocomplete-preset-algolia": 1.17.9(@algolia/client-search@5.49.0)(algoliasearch@5.49.0) + "@docsearch/css": 3.9.0 + algoliasearch: 5.49.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - "@algolia/client-search" + + "@esbuild/aix-ppc64@0.21.5": + optional: true + + "@esbuild/aix-ppc64@0.27.3": + optional: true + + "@esbuild/android-arm64@0.21.5": + optional: true + + "@esbuild/android-arm64@0.27.3": + optional: true + + "@esbuild/android-arm@0.21.5": + optional: true + + "@esbuild/android-arm@0.27.3": + optional: true + + "@esbuild/android-x64@0.21.5": + optional: true + + "@esbuild/android-x64@0.27.3": + optional: true + + "@esbuild/darwin-arm64@0.21.5": + optional: true + + "@esbuild/darwin-arm64@0.27.3": + optional: true + + "@esbuild/darwin-x64@0.21.5": + optional: true + + "@esbuild/darwin-x64@0.27.3": + optional: true + + "@esbuild/freebsd-arm64@0.21.5": + optional: true + + "@esbuild/freebsd-arm64@0.27.3": + optional: true + + "@esbuild/freebsd-x64@0.21.5": + optional: true + + "@esbuild/freebsd-x64@0.27.3": + optional: true + + "@esbuild/linux-arm64@0.21.5": + optional: true + + "@esbuild/linux-arm64@0.27.3": + optional: true + + "@esbuild/linux-arm@0.21.5": + optional: true + + "@esbuild/linux-arm@0.27.3": + optional: true + + "@esbuild/linux-ia32@0.21.5": + optional: true + + "@esbuild/linux-ia32@0.27.3": + optional: true + + "@esbuild/linux-loong64@0.21.5": + optional: true + + "@esbuild/linux-loong64@0.27.3": + optional: true + + "@esbuild/linux-mips64el@0.21.5": + optional: true + + "@esbuild/linux-mips64el@0.27.3": + optional: true + + "@esbuild/linux-ppc64@0.21.5": + optional: true + + "@esbuild/linux-ppc64@0.27.3": + optional: true + + "@esbuild/linux-riscv64@0.21.5": + optional: true + + "@esbuild/linux-riscv64@0.27.3": + optional: true + + "@esbuild/linux-s390x@0.21.5": + optional: true + + "@esbuild/linux-s390x@0.27.3": + optional: true + + "@esbuild/linux-x64@0.21.5": + optional: true + + "@esbuild/linux-x64@0.27.3": + optional: true + + "@esbuild/netbsd-arm64@0.27.3": + optional: true + + "@esbuild/netbsd-x64@0.21.5": + optional: true + + "@esbuild/netbsd-x64@0.27.3": + optional: true + + "@esbuild/openbsd-arm64@0.27.3": + optional: true + + "@esbuild/openbsd-x64@0.21.5": + optional: true + + "@esbuild/openbsd-x64@0.27.3": + optional: true + + "@esbuild/openharmony-arm64@0.27.3": + optional: true + + "@esbuild/sunos-x64@0.21.5": + optional: true + + "@esbuild/sunos-x64@0.27.3": + optional: true + + "@esbuild/win32-arm64@0.21.5": + optional: true + + "@esbuild/win32-arm64@0.27.3": + optional: true + + "@esbuild/win32-ia32@0.21.5": + optional: true + + "@esbuild/win32-ia32@0.27.3": + optional: true + + "@esbuild/win32-x64@0.21.5": + optional: true + + "@esbuild/win32-x64@0.27.3": + optional: true + + "@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)": + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.2": {} + + "@eslint/config-array@0.21.1": + dependencies: + "@eslint/object-schema": 2.1.7 + debug: 4.4.0(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + "@eslint/config-helpers@0.4.2": + dependencies: + "@eslint/core": 0.17.0 + + "@eslint/core@0.17.0": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/eslintrc@3.3.3": + dependencies: + ajv: 6.12.6 + debug: 4.4.0(supports-color@5.5.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + "@eslint/js@9.18.0": {} + + "@eslint/js@9.39.2": {} + + "@eslint/object-schema@2.1.7": {} + + "@eslint/plugin-kit@0.4.1": + dependencies: + "@eslint/core": 0.17.0 + levn: 0.4.1 + + "@gar/promise-retry@1.0.2": + dependencies: + retry: 0.13.1 + + "@hono/node-server@1.19.11(hono@4.12.8)": + dependencies: + hono: 4.12.8 + + "@humanfs/core@0.19.1": {} + + "@humanfs/node@0.16.7": + dependencies: + "@humanfs/core": 0.19.1 + "@humanwhocodes/retry": 0.4.3 + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/retry@0.4.3": {} + + "@iconify-json/simple-icons@1.2.71": + dependencies: + "@iconify/types": 2.0.0 + + "@iconify/types@2.0.0": {} + + "@iconify/utils@2.3.0": + dependencies: + "@antfu/install-pkg": 1.1.0 + "@antfu/utils": 8.1.1 + "@iconify/types": 2.0.0 + debug: 4.4.0(supports-color@5.5.0) + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.2 + mlly: 1.8.0 + transitivePeerDependencies: + - supports-color + + "@isaacs/cliui@8.0.2": + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + "@isaacs/fs-minipass@4.0.1": + dependencies: + minipass: 7.1.3 + + "@istanbuljs/schema@0.1.3": {} + + "@jridgewell/gen-mapping@0.3.13": + dependencies: + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping": 0.3.31 + + "@jridgewell/resolve-uri@3.1.2": {} + + "@jridgewell/sourcemap-codec@1.5.5": {} + + "@jridgewell/trace-mapping@0.3.31": + dependencies: + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 + + "@kwsites/file-exists@1.1.1": + dependencies: + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + "@kwsites/promise-deferred@1.1.1": {} + + "@mermaid-js/mermaid-mindmap@9.3.0": + dependencies: + "@braintree/sanitize-url": 6.0.4 + cytoscape: 3.31.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.31.0) + cytoscape-fcose: 2.2.0(cytoscape@3.31.0) + d3: 7.9.0 + khroma: 2.1.0 + non-layered-tidy-tree-layout: 2.0.2 + optional: true + + "@mermaid-js/parser@0.3.0": + dependencies: + langium: 3.0.0 + + "@modelcontextprotocol/sdk@1.27.1(zod@4.3.6)": + dependencies: + "@hono/node-server": 1.19.11(hono@4.12.8) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.1(express@5.2.1) + hono: 4.12.8 + jose: 6.2.1 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.3.6 + zod-to-json-schema: 3.25.1(zod@4.3.6) + transitivePeerDependencies: + - supports-color + + "@nodelib/fs.scandir@2.1.5": + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: 1.2.0 + + "@nodelib/fs.stat@2.0.5": {} + + "@nodelib/fs.walk@1.2.8": + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: 1.20.1 + + "@npmcli/agent@4.0.0": + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 11.2.6 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + "@npmcli/fs@5.0.0": + dependencies: + semver: 7.7.4 + + "@npmcli/git@7.0.2": + dependencies: + "@gar/promise-retry": 1.0.2 + "@npmcli/promise-spawn": 9.0.1 + ini: 6.0.0 + lru-cache: 11.2.6 + npm-pick-manifest: 11.0.3 + proc-log: 6.1.0 + semver: 7.7.4 + which: 6.0.1 + + "@npmcli/installed-package-contents@4.0.0": + dependencies: + npm-bundled: 5.0.0 + npm-normalize-package-bin: 5.0.0 + + "@npmcli/node-gyp@5.0.0": {} + + "@npmcli/package-json@7.0.5": + dependencies: + "@npmcli/git": 7.0.2 + glob: 13.0.5 + hosted-git-info: 9.0.2 + json-parse-even-better-errors: 5.0.0 + proc-log: 6.1.0 + semver: 7.7.4 + spdx-expression-parse: 4.0.0 + + "@npmcli/promise-spawn@9.0.1": + dependencies: + which: 6.0.1 + + "@npmcli/redact@4.0.0": {} + + "@npmcli/run-script@10.0.4": + dependencies: + "@npmcli/node-gyp": 5.0.0 + "@npmcli/package-json": 7.0.5 + "@npmcli/promise-spawn": 9.0.1 + node-gyp: 12.2.0 + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color + + "@pkgjs/parseargs@0.11.0": + optional: true + + "@rollup/rollup-android-arm-eabi@4.57.1": + optional: true + + "@rollup/rollup-android-arm64@4.57.1": + optional: true + + "@rollup/rollup-darwin-arm64@4.57.1": + optional: true + + "@rollup/rollup-darwin-x64@4.57.1": + optional: true + + "@rollup/rollup-freebsd-arm64@4.57.1": + optional: true + + "@rollup/rollup-freebsd-x64@4.57.1": + optional: true + + "@rollup/rollup-linux-arm-gnueabihf@4.57.1": + optional: true + + "@rollup/rollup-linux-arm-musleabihf@4.57.1": + optional: true + + "@rollup/rollup-linux-arm64-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-arm64-musl@4.57.1": + optional: true + + "@rollup/rollup-linux-loong64-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-loong64-musl@4.57.1": + optional: true + + "@rollup/rollup-linux-ppc64-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-ppc64-musl@4.57.1": + optional: true + + "@rollup/rollup-linux-riscv64-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-riscv64-musl@4.57.1": + optional: true + + "@rollup/rollup-linux-s390x-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-x64-gnu@4.57.1": + optional: true + + "@rollup/rollup-linux-x64-musl@4.57.1": + optional: true + + "@rollup/rollup-openbsd-x64@4.57.1": + optional: true + + "@rollup/rollup-openharmony-arm64@4.57.1": + optional: true + + "@rollup/rollup-win32-arm64-msvc@4.57.1": + optional: true + + "@rollup/rollup-win32-ia32-msvc@4.57.1": + optional: true + + "@rollup/rollup-win32-x64-gnu@4.57.1": + optional: true + + "@rollup/rollup-win32-x64-msvc@4.57.1": + optional: true + + "@shikijs/core@2.5.0": + dependencies: + "@shikijs/engine-javascript": 2.5.0 + "@shikijs/engine-oniguruma": 2.5.0 + "@shikijs/types": 2.5.0 + "@shikijs/vscode-textmate": 10.0.2 + "@types/hast": 3.0.4 + hast-util-to-html: 9.0.5 + + "@shikijs/engine-javascript@2.5.0": + dependencies: + "@shikijs/types": 2.5.0 + "@shikijs/vscode-textmate": 10.0.2 + oniguruma-to-es: 3.1.1 + + "@shikijs/engine-oniguruma@2.5.0": + dependencies: + "@shikijs/types": 2.5.0 + "@shikijs/vscode-textmate": 10.0.2 + + "@shikijs/langs@2.5.0": + dependencies: + "@shikijs/types": 2.5.0 + + "@shikijs/themes@2.5.0": + dependencies: + "@shikijs/types": 2.5.0 + + "@shikijs/transformers@2.5.0": + dependencies: + "@shikijs/core": 2.5.0 + "@shikijs/types": 2.5.0 + + "@shikijs/types@2.5.0": + dependencies: + "@shikijs/vscode-textmate": 10.0.2 + "@types/hast": 3.0.4 + + "@shikijs/vscode-textmate@10.0.2": {} + + "@sigstore/bundle@4.0.0": + dependencies: + "@sigstore/protobuf-specs": 0.5.0 + + "@sigstore/core@3.1.0": {} + + "@sigstore/protobuf-specs@0.5.0": {} + + "@sigstore/sign@4.1.0": + dependencies: + "@sigstore/bundle": 4.0.0 + "@sigstore/core": 3.1.0 + "@sigstore/protobuf-specs": 0.5.0 + make-fetch-happen: 15.0.4 + proc-log: 6.1.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + + "@sigstore/tuf@4.0.1": + dependencies: + "@sigstore/protobuf-specs": 0.5.0 + tuf-js: 4.1.0 + transitivePeerDependencies: + - supports-color + + "@sigstore/verify@3.1.0": + dependencies: + "@sigstore/bundle": 4.0.0 + "@sigstore/core": 3.1.0 + "@sigstore/protobuf-specs": 0.5.0 + + "@swc/core-darwin-arm64@1.15.11": + optional: true + + "@swc/core-darwin-x64@1.15.11": + optional: true + + "@swc/core-linux-arm-gnueabihf@1.15.11": + optional: true + + "@swc/core-linux-arm64-gnu@1.15.11": + optional: true + + "@swc/core-linux-arm64-musl@1.15.11": + optional: true + + "@swc/core-linux-x64-gnu@1.15.11": + optional: true + + "@swc/core-linux-x64-musl@1.15.11": + optional: true + + "@swc/core-win32-arm64-msvc@1.15.11": + optional: true + + "@swc/core-win32-ia32-msvc@1.15.11": + optional: true + + "@swc/core-win32-x64-msvc@1.15.11": + optional: true + + "@swc/core@1.15.11": + dependencies: + "@swc/counter": 0.1.3 + "@swc/types": 0.1.25 + optionalDependencies: + "@swc/core-darwin-arm64": 1.15.11 + "@swc/core-darwin-x64": 1.15.11 + "@swc/core-linux-arm-gnueabihf": 1.15.11 + "@swc/core-linux-arm64-gnu": 1.15.11 + "@swc/core-linux-arm64-musl": 1.15.11 + "@swc/core-linux-x64-gnu": 1.15.11 + "@swc/core-linux-x64-musl": 1.15.11 + "@swc/core-win32-arm64-msvc": 1.15.11 + "@swc/core-win32-ia32-msvc": 1.15.11 + "@swc/core-win32-x64-msvc": 1.15.11 + + "@swc/counter@0.1.3": {} + + "@swc/types@0.1.25": + dependencies: + "@swc/counter": 0.1.3 + + "@tsconfig/node22@22.0.0": {} + + "@tsconfig/strictest@2.0.5": {} + + "@tufjs/canonical-json@2.0.0": {} + + "@tufjs/models@4.1.0": + dependencies: + "@tufjs/canonical-json": 2.0.0 + minimatch: 10.2.1 + + "@types/chai@5.2.3": + dependencies: + "@types/deep-eql": 4.0.2 + assertion-error: 2.0.1 + + "@types/d3-array@3.2.2": {} + + "@types/d3-axis@3.0.6": + dependencies: + "@types/d3-selection": 3.0.11 + + "@types/d3-brush@3.0.6": + dependencies: + "@types/d3-selection": 3.0.11 + + "@types/d3-chord@3.0.6": {} + + "@types/d3-color@3.1.3": {} + + "@types/d3-contour@3.0.6": + dependencies: + "@types/d3-array": 3.2.2 + "@types/geojson": 7946.0.16 + + "@types/d3-delaunay@6.0.4": {} + + "@types/d3-dispatch@3.0.7": {} + + "@types/d3-drag@3.0.7": + dependencies: + "@types/d3-selection": 3.0.11 + + "@types/d3-dsv@3.0.7": {} + + "@types/d3-ease@3.0.2": {} + + "@types/d3-fetch@3.0.7": + dependencies: + "@types/d3-dsv": 3.0.7 + + "@types/d3-force@3.0.10": {} + + "@types/d3-format@3.0.4": {} + + "@types/d3-geo@3.1.0": + dependencies: + "@types/geojson": 7946.0.16 + + "@types/d3-hierarchy@3.1.7": {} + + "@types/d3-interpolate@3.0.4": + dependencies: + "@types/d3-color": 3.1.3 + + "@types/d3-path@3.1.1": {} + + "@types/d3-polygon@3.0.2": {} + + "@types/d3-quadtree@3.0.6": {} + + "@types/d3-random@3.0.3": {} + + "@types/d3-scale-chromatic@3.1.0": {} + + "@types/d3-scale@4.0.9": + dependencies: + "@types/d3-time": 3.0.4 + + "@types/d3-selection@3.0.11": {} + + "@types/d3-shape@3.1.8": + dependencies: + "@types/d3-path": 3.1.1 + + "@types/d3-time-format@4.0.3": {} + + "@types/d3-time@3.0.4": {} + + "@types/d3-timer@3.0.2": {} + + "@types/d3-transition@3.0.9": + dependencies: + "@types/d3-selection": 3.0.11 + + "@types/d3-zoom@3.0.8": + dependencies: + "@types/d3-interpolate": 3.0.4 + "@types/d3-selection": 3.0.11 + + "@types/d3@7.4.3": + dependencies: + "@types/d3-array": 3.2.2 + "@types/d3-axis": 3.0.6 + "@types/d3-brush": 3.0.6 + "@types/d3-chord": 3.0.6 + "@types/d3-color": 3.1.3 + "@types/d3-contour": 3.0.6 + "@types/d3-delaunay": 6.0.4 + "@types/d3-dispatch": 3.0.7 + "@types/d3-drag": 3.0.7 + "@types/d3-dsv": 3.0.7 + "@types/d3-ease": 3.0.2 + "@types/d3-fetch": 3.0.7 + "@types/d3-force": 3.0.10 + "@types/d3-format": 3.0.4 + "@types/d3-geo": 3.1.0 + "@types/d3-hierarchy": 3.1.7 + "@types/d3-interpolate": 3.0.4 + "@types/d3-path": 3.1.1 + "@types/d3-polygon": 3.0.2 + "@types/d3-quadtree": 3.0.6 + "@types/d3-random": 3.0.3 + "@types/d3-scale": 4.0.9 + "@types/d3-scale-chromatic": 3.1.0 + "@types/d3-selection": 3.0.11 + "@types/d3-shape": 3.1.8 + "@types/d3-time": 3.0.4 + "@types/d3-time-format": 4.0.3 + "@types/d3-timer": 3.0.2 + "@types/d3-transition": 3.0.9 + "@types/d3-zoom": 3.0.8 + + "@types/deep-eql@4.0.2": {} + + "@types/eslint-config-prettier@6.11.3": {} + + "@types/estree@1.0.8": {} + + "@types/geojson@7946.0.16": {} + + "@types/hast@3.0.4": + dependencies: + "@types/unist": 3.0.3 + + "@types/json-schema@7.0.15": {} + + "@types/linkify-it@5.0.0": {} + + "@types/markdown-it@14.1.2": + dependencies: + "@types/linkify-it": 5.0.0 + "@types/mdurl": 2.0.0 + + "@types/mdast@4.0.4": + dependencies: + "@types/unist": 3.0.3 + + "@types/mdurl@2.0.0": {} + + "@types/node@22.19.11": + dependencies: + undici-types: 6.21.0 + + "@types/trusted-types@2.0.7": + optional: true + + "@types/unist@3.0.3": {} + + "@types/web-bluetooth@0.0.21": {} + + "@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/scope-manager": 8.21.0 + "@typescript-eslint/type-utils": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/utils": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.21.0 + eslint: 9.39.2 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 8.56.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/scope-manager": 8.56.0 + "@typescript-eslint/type-utils": 8.56.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/utils": 8.56.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.56.0 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@8.21.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/scope-manager": 8.21.0 + "@typescript-eslint/types": 8.21.0 + "@typescript-eslint/typescript-estree": 8.21.0(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.21.0 + debug: 4.4.0(supports-color@5.5.0) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@8.56.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/scope-manager": 8.56.0 + "@typescript-eslint/types": 8.56.0 + "@typescript-eslint/typescript-estree": 8.56.0(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.56.0 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/project-service@8.56.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/tsconfig-utils": 8.56.0(typescript@5.9.3) + "@typescript-eslint/types": 8.56.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/scope-manager@8.21.0": + dependencies: + "@typescript-eslint/types": 8.21.0 + "@typescript-eslint/visitor-keys": 8.21.0 + + "@typescript-eslint/scope-manager@8.56.0": + dependencies: + "@typescript-eslint/types": 8.56.0 + "@typescript-eslint/visitor-keys": 8.56.0 + + "@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)": + dependencies: + typescript: 5.9.3 + + "@typescript-eslint/type-utils@8.21.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/typescript-estree": 8.21.0(typescript@5.9.3) + "@typescript-eslint/utils": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.0(supports-color@5.5.0) + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/type-utils@8.56.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/types": 8.56.0 + "@typescript-eslint/typescript-estree": 8.56.0(typescript@5.9.3) + "@typescript-eslint/utils": 8.56.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/types@8.21.0": {} + + "@typescript-eslint/types@8.56.0": {} + + "@typescript-eslint/typescript-estree@8.21.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/types": 8.21.0 + "@typescript-eslint/visitor-keys": 8.21.0 + debug: 4.4.0(supports-color@5.5.0) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.4 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/project-service": 8.56.0(typescript@5.9.3) + "@typescript-eslint/tsconfig-utils": 8.56.0(typescript@5.9.3) + "@typescript-eslint/types": 8.56.0 + "@typescript-eslint/visitor-keys": 8.56.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@8.21.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.2) + "@typescript-eslint/scope-manager": 8.21.0 + "@typescript-eslint/types": 8.21.0 + "@typescript-eslint/typescript-estree": 8.21.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@8.56.0(eslint@9.39.2)(typescript@5.9.3)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.2) + "@typescript-eslint/scope-manager": 8.56.0 + "@typescript-eslint/types": 8.56.0 + "@typescript-eslint/typescript-estree": 8.56.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/visitor-keys@8.21.0": + dependencies: + "@typescript-eslint/types": 8.21.0 + eslint-visitor-keys: 4.2.1 + + "@typescript-eslint/visitor-keys@8.56.0": + dependencies: + "@typescript-eslint/types": 8.56.0 + eslint-visitor-keys: 5.0.0 + + "@ungap/structured-clone@1.3.0": {} + + "@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@22.19.11))(vue@3.5.28(typescript@5.9.3))": + dependencies: + vite: 5.4.21(@types/node@22.19.11) + vue: 3.5.28(typescript@5.9.3) + + "@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@22.19.11)(yaml@2.8.2))": + dependencies: + "@ampproject/remapping": 2.3.0 + "@bcoe/v8-coverage": 1.0.2 + ast-v8-to-istanbul: 0.3.11 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magic-string: 0.30.21 + magicast: 0.3.5 + std-env: 3.10.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/node@22.19.11)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + + "@vitest/expect@3.2.4": + dependencies: + "@types/chai": 5.2.3 + "@vitest/spy": 3.2.4 + "@vitest/utils": 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + "@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.11)(yaml@2.8.2))": + dependencies: + "@vitest/spy": 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@22.19.11)(yaml@2.8.2) + + "@vitest/pretty-format@3.2.4": + dependencies: + tinyrainbow: 2.0.0 + + "@vitest/runner@3.2.4": + dependencies: + "@vitest/utils": 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + "@vitest/snapshot@3.2.4": + dependencies: + "@vitest/pretty-format": 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + "@vitest/spy@3.2.4": + dependencies: + tinyspy: 4.0.4 + + "@vitest/utils@3.2.4": + dependencies: + "@vitest/pretty-format": 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + "@vue/compiler-core@3.5.28": + dependencies: + "@babel/parser": 7.29.0 + "@vue/shared": 3.5.28 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + "@vue/compiler-dom@3.5.28": + dependencies: + "@vue/compiler-core": 3.5.28 + "@vue/shared": 3.5.28 + + "@vue/compiler-sfc@3.5.28": + dependencies: + "@babel/parser": 7.29.0 + "@vue/compiler-core": 3.5.28 + "@vue/compiler-dom": 3.5.28 + "@vue/compiler-ssr": 3.5.28 + "@vue/shared": 3.5.28 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + "@vue/compiler-ssr@3.5.28": + dependencies: + "@vue/compiler-dom": 3.5.28 + "@vue/shared": 3.5.28 + + "@vue/devtools-api@7.7.9": + dependencies: + "@vue/devtools-kit": 7.7.9 + + "@vue/devtools-kit@7.7.9": + dependencies: + "@vue/devtools-shared": 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + "@vue/devtools-shared@7.7.9": + dependencies: + rfdc: 1.4.1 + + "@vue/reactivity@3.5.28": + dependencies: + "@vue/shared": 3.5.28 + + "@vue/runtime-core@3.5.28": + dependencies: + "@vue/reactivity": 3.5.28 + "@vue/shared": 3.5.28 + + "@vue/runtime-dom@3.5.28": + dependencies: + "@vue/reactivity": 3.5.28 + "@vue/runtime-core": 3.5.28 + "@vue/shared": 3.5.28 + csstype: 3.2.3 + + "@vue/server-renderer@3.5.28(vue@3.5.28(typescript@5.9.3))": + dependencies: + "@vue/compiler-ssr": 3.5.28 + "@vue/shared": 3.5.28 + vue: 3.5.28(typescript@5.9.3) + + "@vue/shared@3.5.28": {} + + "@vueuse/core@12.8.2(typescript@5.9.3)": + dependencies: + "@types/web-bluetooth": 0.0.21 + "@vueuse/metadata": 12.8.2 + "@vueuse/shared": 12.8.2(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + "@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@5.9.3)": + dependencies: + "@vueuse/core": 12.8.2(typescript@5.9.3) + "@vueuse/shared": 12.8.2(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) + optionalDependencies: + focus-trap: 7.8.0 + transitivePeerDependencies: + - typescript + + "@vueuse/metadata@12.8.2": {} + + "@vueuse/shared@12.8.2(typescript@5.9.3)": + dependencies: + vue: 3.5.28(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + abbrev@4.0.0: {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + adm-zip@0.5.16: {} + + agent-base@7.1.4: {} + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + algoliasearch@5.49.0: + dependencies: + "@algolia/abtesting": 1.15.0 + "@algolia/client-abtesting": 5.49.0 + "@algolia/client-analytics": 5.49.0 + "@algolia/client-common": 5.49.0 + "@algolia/client-insights": 5.49.0 + "@algolia/client-personalization": 5.49.0 + "@algolia/client-query-suggestions": 5.49.0 + "@algolia/client-search": 5.49.0 + "@algolia/ingestion": 1.49.0 + "@algolia/monitoring": 1.49.0 + "@algolia/recommend": 5.49.0 + "@algolia/requester-browser-xhr": 5.49.0 + "@algolia/requester-fetch": 5.49.0 + "@algolia/requester-node-http": 5.49.0 + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@0.3.11: + dependencies: + "@jridgewell/trace-mapping": 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + balanced-match@1.0.2: {} + + balanced-match@4.0.3: {} + + binary-extensions@2.3.0: {} + + birpc@2.9.0: {} + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.2: + dependencies: + balanced-match: 4.0.3 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + bytes@3.1.2: {} + + cac@6.7.14: {} + + cacache@20.0.3: + dependencies: + "@npmcli/fs": 5.0.0 + fs-minipass: 3.0.3 + glob: 13.0.5 + lru-cache: 11.2.6 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 13.0.1 + unique-filename: 5.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + ccount@2.0.1: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + check-error@2.1.3: {} + + chevrotain-allstar@0.3.1(chevrotain@11.0.3): + dependencies: + chevrotain: 11.0.3 + lodash-es: 4.17.23 + + chevrotain@11.0.3: + dependencies: + "@chevrotain/cst-dts-gen": 11.0.3 + "@chevrotain/gast": 11.0.3 + "@chevrotain/regexp-to-ast": 11.0.3 + "@chevrotain/types": 11.0.3 + "@chevrotain/utils": 11.0.3 + lodash-es: 4.17.21 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@3.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + comma-separated-tokens@2.0.3: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + confbox@0.2.4: {} + + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.31.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.31.0 + + cytoscape-fcose@2.2.0(cytoscape@3.31.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.31.0 + + cytoscape@3.31.0: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.11: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.23 + + dayjs@1.11.13: {} + + debug@4.4.0(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + depd@2.0.0: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dompurify@3.3.1: + optionalDependencies: + "@types/trusted-types": 2.0.7 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ee-first@1.1.1: {} + + emoji-regex-xs@1.0.0: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + entities@7.0.1: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + err-code@2.0.3: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.21.5: + optionalDependencies: + "@esbuild/aix-ppc64": 0.21.5 + "@esbuild/android-arm": 0.21.5 + "@esbuild/android-arm64": 0.21.5 + "@esbuild/android-x64": 0.21.5 + "@esbuild/darwin-arm64": 0.21.5 + "@esbuild/darwin-x64": 0.21.5 + "@esbuild/freebsd-arm64": 0.21.5 + "@esbuild/freebsd-x64": 0.21.5 + "@esbuild/linux-arm": 0.21.5 + "@esbuild/linux-arm64": 0.21.5 + "@esbuild/linux-ia32": 0.21.5 + "@esbuild/linux-loong64": 0.21.5 + "@esbuild/linux-mips64el": 0.21.5 + "@esbuild/linux-ppc64": 0.21.5 + "@esbuild/linux-riscv64": 0.21.5 + "@esbuild/linux-s390x": 0.21.5 + "@esbuild/linux-x64": 0.21.5 + "@esbuild/netbsd-x64": 0.21.5 + "@esbuild/openbsd-x64": 0.21.5 + "@esbuild/sunos-x64": 0.21.5 + "@esbuild/win32-arm64": 0.21.5 + "@esbuild/win32-ia32": 0.21.5 + "@esbuild/win32-x64": 0.21.5 + + esbuild@0.27.3: + optionalDependencies: + "@esbuild/aix-ppc64": 0.27.3 + "@esbuild/android-arm": 0.27.3 + "@esbuild/android-arm64": 0.27.3 + "@esbuild/android-x64": 0.27.3 + "@esbuild/darwin-arm64": 0.27.3 + "@esbuild/darwin-x64": 0.27.3 + "@esbuild/freebsd-arm64": 0.27.3 + "@esbuild/freebsd-x64": 0.27.3 + "@esbuild/linux-arm": 0.27.3 + "@esbuild/linux-arm64": 0.27.3 + "@esbuild/linux-ia32": 0.27.3 + "@esbuild/linux-loong64": 0.27.3 + "@esbuild/linux-mips64el": 0.27.3 + "@esbuild/linux-ppc64": 0.27.3 + "@esbuild/linux-riscv64": 0.27.3 + "@esbuild/linux-s390x": 0.27.3 + "@esbuild/linux-x64": 0.27.3 + "@esbuild/netbsd-arm64": 0.27.3 + "@esbuild/netbsd-x64": 0.27.3 + "@esbuild/openbsd-arm64": 0.27.3 + "@esbuild/openbsd-x64": 0.27.3 + "@esbuild/openharmony-arm64": 0.27.3 + "@esbuild/sunos-x64": 0.27.3 + "@esbuild/win32-arm64": 0.27.3 + "@esbuild/win32-ia32": 0.27.3 + "@esbuild/win32-x64": 0.27.3 + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.2): + dependencies: + eslint: 9.39.2 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.0: {} + + eslint@9.39.2: + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.2) + "@eslint-community/regexpp": 4.12.2 + "@eslint/config-array": 0.21.1 + "@eslint/config-helpers": 0.4.2 + "@eslint/core": 0.17.0 + "@eslint/eslintrc": 3.3.3 + "@eslint/js": 9.39.2 + "@eslint/plugin-kit": 0.4.1 + "@humanfs/node": 0.16.7 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.3 + "@types/estree": 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0(supports-color@5.5.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + "@types/estree": 1.0.8 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@5.0.4: {} + + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expect-type@1.3.0: {} + + exponential-backoff@3.1.3: {} + + express-rate-limit@8.3.1(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.1.0 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0(supports-color@5.5.0) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + exsolve@1.0.8: {} + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.0(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + focus-trap@7.8.0: + dependencies: + tabbable: 6.4.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-east-asian-width@1.5.0: {} + + 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 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@8.0.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@13.0.5: + dependencies: + minimatch: 10.2.1 + minipass: 7.1.3 + path-scurry: 2.0.1 + + globals@14.0.0: {} + + globals@15.15.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + hachure-fill@0.5.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + "@types/hast": 3.0.4 + + hono@4.12.8: {} + + hookable@5.5.3: {} + + hosted-git-info@9.0.2: + dependencies: + lru-cache: 11.2.6 + + html-escaper@2.0.2: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + + 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 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ignore-by-default@1.0.1: {} + + ignore-walk@8.0.0: + dependencies: + minimatch: 10.2.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + ini@6.0.0: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + ip-address@10.1.0: {} + + ipaddr.js@1.9.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extendable@0.1.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-stream@3.0.0: {} + + is-what@5.5.0: {} + + isexe@2.0.0: {} + + isexe@4.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + "@jridgewell/trace-mapping": 0.3.31 + debug: 4.4.0(supports-color@5.5.0) + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + "@isaacs/cliui": 8.0.2 + optionalDependencies: + "@pkgjs/parseargs": 0.11.0 + + jose@6.2.1: {} + + js-tokens@10.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@5.0.0: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonparse@1.3.1: {} + + katex@0.16.28: + dependencies: + commander: 8.3.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + khroma@2.1.0: {} + + kind-of@6.0.3: {} + + kolorist@1.8.0: {} + + langium@3.0.0: + dependencies: + chevrotain: 11.0.3 + chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lint-staged@15.5.2: + dependencies: + chalk: 5.6.2 + commander: 13.1.0 + debug: 4.4.0(supports-color@5.5.0) + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.2 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + local-pkg@1.1.2: + dependencies: + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.17.21: {} + + lodash-es@4.17.23: {} + + lodash.merge@4.6.2: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@11.2.6: {} + + magic-string@0.30.21: + dependencies: + "@jridgewell/sourcemap-codec": 1.5.5 + + magicast@0.3.5: + dependencies: + "@babel/parser": 7.29.0 + "@babel/types": 7.29.0 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + make-fetch-happen@15.0.4: + dependencies: + "@gar/promise-retry": 1.0.2 + "@npmcli/agent": 4.0.0 + cacache: 20.0.3 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 6.1.0 + ssri: 13.0.1 + transitivePeerDependencies: + - supports-color + + mark.js@8.11.1: {} + + marked@13.0.3: {} + + math-intrinsics@1.1.0: {} + + mdast-util-to-hast@13.2.1: + dependencies: + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@ungap/structured-clone": 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + mermaid@11.4.1: + dependencies: + "@braintree/sanitize-url": 7.1.1 + "@iconify/utils": 2.3.0 + "@mermaid-js/parser": 0.3.0 + "@types/d3": 7.4.3 + cytoscape: 3.31.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.31.0) + cytoscape-fcose: 2.2.0(cytoscape@3.31.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.11 + dayjs: 1.11.13 + dompurify: 3.3.1 + katex: 0.16.28 + khroma: 2.1.0 + lodash-es: 4.17.23 + marked: 13.0.3 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.2.1: + dependencies: + brace-expansion: 5.0.2 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@5.0.2: + dependencies: + minipass: 7.1.3 + minipass-sized: 2.0.0 + minizlib: 3.1.0 + optionalDependencies: + iconv-lite: 0.7.2 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@2.0.0: + dependencies: + minipass: 7.1.3 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@7.1.3: {} + + minisearch@7.2.0: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mitt@3.0.1: {} + + mlly@1.8.0: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + node-gyp@12.2.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + make-fetch-happen: 15.0.4 + nopt: 9.0.0 + proc-log: 6.1.0 + semver: 7.7.4 + tar: 7.5.11 + tinyglobby: 0.2.15 + which: 6.0.1 + transitivePeerDependencies: + - supports-color + + nodemon@3.1.11: + dependencies: + chokidar: 3.6.0 + debug: 4.4.0(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.7.4 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + non-layered-tidy-tree-layout@2.0.2: + optional: true + + nopt@9.0.0: + dependencies: + abbrev: 4.0.0 + + normalize-path@3.0.0: {} + + npm-bundled@5.0.0: + dependencies: + npm-normalize-package-bin: 5.0.0 + + npm-install-checks@8.0.0: + dependencies: + semver: 7.7.4 + + npm-normalize-package-bin@5.0.0: {} + + npm-package-arg@13.0.2: + dependencies: + hosted-git-info: 9.0.2 + proc-log: 6.1.0 + semver: 7.7.4 + validate-npm-package-name: 7.0.2 + + npm-packlist@10.0.4: + dependencies: + ignore-walk: 8.0.0 + proc-log: 6.1.0 + + npm-pick-manifest@11.0.3: + dependencies: + npm-install-checks: 8.0.0 + npm-normalize-package-bin: 5.0.0 + npm-package-arg: 13.0.2 + semver: 7.7.4 + + npm-registry-fetch@19.1.1: + dependencies: + "@npmcli/redact": 4.0.0 + jsonparse: 1.3.1 + make-fetch-happen: 15.0.4 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minizlib: 3.1.0 + npm-package-arg: 13.0.2 + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.1.0 + regex-recursion: 6.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@7.0.4: {} + + package-json-from-dist@1.0.1: {} + + package-manager-detector@1.6.0: {} + + pacote@21.3.1: + dependencies: + "@npmcli/git": 7.0.2 + "@npmcli/installed-package-contents": 4.0.0 + "@npmcli/package-json": 7.0.5 + "@npmcli/promise-spawn": 9.0.1 + "@npmcli/run-script": 10.0.4 + cacache: 20.0.3 + fs-minipass: 3.0.3 + minipass: 7.1.3 + npm-package-arg: 13.0.2 + npm-packlist: 10.0.4 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1 + proc-log: 6.1.0 + promise-retry: 2.0.1 + sigstore: 4.1.0 + ssri: 13.0.1 + tar: 7.5.11 + transitivePeerDependencies: + - supports-color + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseurl@1.3.3: {} + + path-data-parser@0.1.0: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.6 + minipass: 7.1.3 + + path-to-regexp@8.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pkce-challenge@5.0.1: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.28.4: {} + + prelude-ls@1.2.1: {} + + prettier@3.8.1: {} + + proc-log@6.1.0: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + property-information@7.1.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pstree.remy@1.1.8: {} + + punycode@2.3.1: {} + + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.12.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@6.1.3: + dependencies: + glob: 13.0.5 + package-json-from-dist: 1.0.1 + + robust-predicates@3.0.2: {} + + rollup@4.57.1: + dependencies: + "@types/estree": 1.0.8 + optionalDependencies: + "@rollup/rollup-android-arm-eabi": 4.57.1 + "@rollup/rollup-android-arm64": 4.57.1 + "@rollup/rollup-darwin-arm64": 4.57.1 + "@rollup/rollup-darwin-x64": 4.57.1 + "@rollup/rollup-freebsd-arm64": 4.57.1 + "@rollup/rollup-freebsd-x64": 4.57.1 + "@rollup/rollup-linux-arm-gnueabihf": 4.57.1 + "@rollup/rollup-linux-arm-musleabihf": 4.57.1 + "@rollup/rollup-linux-arm64-gnu": 4.57.1 + "@rollup/rollup-linux-arm64-musl": 4.57.1 + "@rollup/rollup-linux-loong64-gnu": 4.57.1 + "@rollup/rollup-linux-loong64-musl": 4.57.1 + "@rollup/rollup-linux-ppc64-gnu": 4.57.1 + "@rollup/rollup-linux-ppc64-musl": 4.57.1 + "@rollup/rollup-linux-riscv64-gnu": 4.57.1 + "@rollup/rollup-linux-riscv64-musl": 4.57.1 + "@rollup/rollup-linux-s390x-gnu": 4.57.1 + "@rollup/rollup-linux-x64-gnu": 4.57.1 + "@rollup/rollup-linux-x64-musl": 4.57.1 + "@rollup/rollup-openbsd-x64": 4.57.1 + "@rollup/rollup-openharmony-arm64": 4.57.1 + "@rollup/rollup-win32-arm64-msvc": 4.57.1 + "@rollup/rollup-win32-ia32-msvc": 4.57.1 + "@rollup/rollup-win32-x64-gnu": 4.57.1 + "@rollup/rollup-win32-x64-msvc": 4.57.1 + fsevents: 2.3.3 + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + router@2.2.0: + dependencies: + debug: 4.4.0(supports-color@5.5.0) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rw@1.3.3: {} + + safer-buffer@2.1.2: {} + + search-insights@2.17.3: {} + + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + + semver@7.7.4: {} + + 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 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@2.5.0: + dependencies: + "@shikijs/core": 2.5.0 + "@shikijs/engine-javascript": 2.5.0 + "@shikijs/engine-oniguruma": 2.5.0 + "@shikijs/langs": 2.5.0 + "@shikijs/themes": 2.5.0 + "@shikijs/types": 2.5.0 + "@shikijs/vscode-textmate": 10.0.2 + "@types/hast": 3.0.4 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sigstore@4.1.0: + dependencies: + "@sigstore/bundle": 4.0.0 + "@sigstore/core": 3.1.0 + "@sigstore/protobuf-specs": 0.5.0 + "@sigstore/sign": 4.1.0 + "@sigstore/tuf": 4.0.1 + "@sigstore/verify": 3.1.0 + transitivePeerDependencies: + - supports-color + + simple-git@3.33.0: + dependencies: + "@kwsites/file-exists": 1.1.1 + "@kwsites/promise-deferred": 1.1.1 + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.4 + + sisteransi@1.0.5: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.0(supports-color@5.5.0) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-license-ids@3.0.23: {} + + speakingurl@14.0.1: {} + + sprintf-js@1.0.3: {} + + ssri@13.0.1: + dependencies: + minipass: 7.1.3 + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.1.2 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom-string@1.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + stylis@4.3.6: {} + + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tabbable@6.4.0: {} + + tar@7.5.11: + dependencies: + "@isaacs/fs-minipass": 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + test-exclude@7.0.1: + dependencies: + "@istanbuljs/schema": 0.1.3 + glob: 10.5.0 + minimatch: 9.0.5 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + touch@3.1.1: {} + + trim-lines@3.0.1: {} + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-dedent@2.2.0: {} + + tuf-js@4.1.0: + dependencies: + "@tufjs/models": 4.1.0 + debug: 4.4.3 + make-fetch-happen: 15.0.4 + transitivePeerDependencies: + - supports-color + + turbo-darwin-64@2.8.10: + optional: true + + turbo-darwin-arm64@2.8.10: + optional: true + + turbo-linux-64@2.8.10: + optional: true + + turbo-linux-arm64@2.8.10: + optional: true + + turbo-windows-64@2.8.10: + optional: true + + turbo-windows-arm64@2.8.10: + optional: true + + turbo@2.8.10: + optionalDependencies: + turbo-darwin-64: 2.8.10 + turbo-darwin-arm64: 2.8.10 + turbo-linux-64: 2.8.10 + turbo-linux-arm64: 2.8.10 + turbo-windows-64: 2.8.10 + turbo-windows-arm64: 2.8.10 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typescript-eslint@8.21.0(eslint@9.39.2)(typescript@5.9.3): + dependencies: + "@typescript-eslint/eslint-plugin": 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/parser": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + "@typescript-eslint/utils": 8.21.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + ufo@1.6.3: {} + + undefsafe@2.0.5: {} + + undici-types@6.21.0: {} + + unique-filename@5.0.0: + dependencies: + unique-slug: 6.0.0 + + unique-slug@6.0.0: + dependencies: + imurmurhash: 0.1.4 + + unist-util-is@6.0.1: + dependencies: + "@types/unist": 3.0.3 + + unist-util-position@5.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + "@types/unist": 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + "@types/unist": 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + uuid@9.0.1: {} + + validate-npm-package-name@7.0.2: {} + + vary@1.1.2: {} + + vfile-message@4.0.3: + dependencies: + "@types/unist": 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + "@types/unist": 3.0.3 + vfile-message: 4.0.3 + + vite-node@3.2.4(@types/node@22.19.11)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.1(@types/node@22.19.11)(yaml@2.8.2) + transitivePeerDependencies: + - "@types/node" + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@5.4.21(@types/node@22.19.11): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.57.1 + optionalDependencies: + "@types/node": 22.19.11 + fsevents: 2.3.3 + + vite@7.3.1(@types/node@22.19.11)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + "@types/node": 22.19.11 + fsevents: 2.3.3 + yaml: 2.8.2 + + vitepress-plugin-mermaid@2.0.17(mermaid@11.4.1)(vitepress@1.6.2(@algolia/client-search@5.49.0)(@types/node@22.19.11)(postcss@8.5.6)(search-insights@2.17.3)(typescript@5.9.3)): + dependencies: + mermaid: 11.4.1 + vitepress: 1.6.2(@algolia/client-search@5.49.0)(@types/node@22.19.11)(postcss@8.5.6)(search-insights@2.17.3)(typescript@5.9.3) + optionalDependencies: + "@mermaid-js/mermaid-mindmap": 9.3.0 + + vitepress@1.6.2(@algolia/client-search@5.49.0)(@types/node@22.19.11)(postcss@8.5.6)(search-insights@2.17.3)(typescript@5.9.3): + dependencies: + "@docsearch/css": 3.9.0 + "@docsearch/js": 3.9.0(@algolia/client-search@5.49.0)(search-insights@2.17.3) + "@iconify-json/simple-icons": 1.2.71 + "@shikijs/core": 2.5.0 + "@shikijs/transformers": 2.5.0 + "@shikijs/types": 2.5.0 + "@types/markdown-it": 14.1.2 + "@vitejs/plugin-vue": 5.2.4(vite@5.4.21(@types/node@22.19.11))(vue@3.5.28(typescript@5.9.3)) + "@vue/devtools-api": 7.7.9 + "@vue/shared": 3.5.28 + "@vueuse/core": 12.8.2(typescript@5.9.3) + "@vueuse/integrations": 12.8.2(focus-trap@7.8.0)(typescript@5.9.3) + focus-trap: 7.8.0 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 2.5.0 + vite: 5.4.21(@types/node@22.19.11) + vue: 3.5.28(typescript@5.9.3) + optionalDependencies: + postcss: 8.5.6 + transitivePeerDependencies: + - "@algolia/client-search" + - "@types/node" + - "@types/react" + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vitest@3.2.4(@types/node@22.19.11)(yaml@2.8.2): + dependencies: + "@types/chai": 5.2.3 + "@vitest/expect": 3.2.4 + "@vitest/mocker": 3.2.4(vite@7.3.1(@types/node@22.19.11)(yaml@2.8.2)) + "@vitest/pretty-format": 3.2.4 + "@vitest/runner": 3.2.4 + "@vitest/snapshot": 3.2.4 + "@vitest/spy": 3.2.4 + "@vitest/utils": 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@22.19.11)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@22.19.11)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + "@types/node": 22.19.11 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.0.8: {} + + vue@3.5.28(typescript@5.9.3): + dependencies: + "@vue/compiler-dom": 3.5.28 + "@vue/compiler-sfc": 3.5.28 + "@vue/runtime-dom": 3.5.28 + "@vue/server-renderer": 3.5.28(vue@3.5.28(typescript@5.9.3)) + "@vue/shared": 3.5.28 + optionalDependencies: + typescript: 5.9.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@6.0.1: + dependencies: + isexe: 4.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@2.8.2: {} + + yocto-queue@0.1.0: {} + + zod-to-json-schema@3.25.1(zod@4.3.6): + dependencies: + zod: 4.3.6 + + zod@4.3.6: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..dee51e9 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - "packages/*" diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..3c5a00a --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,25 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + /* Basic Options */ + "incremental": true, + "declaration": true, + "allowJs": true, + "noEmit": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + /* Strict Type-Checking Options */ + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": false, + "noImplicitThis": true, + "alwaysStrict": true, + "resolveJsonModule": true, + + /* Experimental Options */ + "experimentalDecorators": true, + "emitDecoratorMetadata": false + } +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..f43754d --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "incremental": false, + "noEmit": false + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bf218bf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["**/*", ".*.js"], + "compilerOptions": { + "types": ["vitest/globals"], + "baseUrl": ".", + "paths": { + "@ade/core/*": ["packages/core/src/*"], + "@ade/cli/*": ["packages/cli/src/*"], + "@ade/harnesses/*": ["packages/harnesses/src/*"] + } + } +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..063db38 --- /dev/null +++ b/turbo.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "//#lint": { + "inputs": ["!.git/**", "!node_modules/**", "!packages/**"] + }, + "//#lint:fix": { + "inputs": ["!.git/**", "!node_modules/**", "!packages/**"] + }, + "//#format": { + "inputs": ["!.git/**", "!node_modules/**", "!packages/**"] + }, + "//#format:fix": { + "inputs": ["!.git/**", "!node_modules/**", "!packages/**"] + }, + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "tsconfig.tsbuildinfo"] + }, + "clean:build": { + "dependsOn": ["build"] + }, + "dev": { + "dependsOn": ["build"], + "cache": false, + "persistent": true + }, + "lint": {}, + "lint:fix": {}, + "format": {}, + "format:fix": {}, + "test": { + "dependsOn": ["build"] + }, + "typecheck": { + "outputs": ["tsconfig.tsbuildinfo"] + } + }, + "globalDependencies": [ + "eslint.config.mjs", + ".lintstagedrc.js", + ".prettierrc.yaml", + "tsconfig.base.json", + "tsconfig.build.json", + "tsconfig.json" + ] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..721ac73 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "node", + coverage: { + provider: "v8", + reporter: ["text", "json", "html"] + } + } +}); diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 0000000..e69de29