diff --git a/.agents/docs/prd.md b/.agents/docs/prd.md index 4b1170e..c1dce88 100644 --- a/.agents/docs/prd.md +++ b/.agents/docs/prd.md @@ -45,7 +45,7 @@ Five operating principles: 1. **Tier-1 parity is the bar.** Same functionality across Claude Code, Codex, OpenCode, Pi Mono. TUI-grade fidelity (overlays, widgets) is the only allowed degradation. 2. **Codegen first, guided per-harness fallback second.** Where universal codegen can express a capability across all Tier-1, do that. Where a harness lacks the native primitive, check whether all Tier-1 can support it via a custom (escape-hatch) method, and if so guide the author — rather than dropping the feature. -3. **Keep a compat matrix.** The living [Tier-1 Capability Matrix](../../docs/reference/compat-matrix.md) (published at `/reference/compat-matrix`) records what's universal-codegen, guided-per-harness, and genuinely unsupported. It's the contract for "same functionality, different plumbing." +3. **Keep a compat matrix.** The living [Capability Matrix](../../docs/guide/capability-matrix.md) (published at `/guide/capability-matrix`) records what's universal-codegen, guided-per-harness, and genuinely unsupported. It's the contract for "same functionality, different plumbing." 4. **Lean, no global SDK.** Primitives express intent, not mechanism; each adapter owns its own plumbing. The escape hatch lets power users write native code against each harness's SDK. 5. **Multi-platform by default, platform-specific code allowed.** AgentPlugins gives plugin authors the foundations to distribute their plugin across all Tier-1 harnesses (Claude Code, Codex, OpenCode, Pi Mono). Authors are not forced to support every harness — they may target a single harness or ship harness-specific behavior (e.g., custom logging for one harness) when another harness lacks a needed primitive. The end goal is a distribution platform and utilities so authors can make a plugin usable in other harnesses without porting or rewriting the implementation. Think React Native or Rust: multi-platform by default, platform-specific code when you need it. @@ -69,8 +69,8 @@ _Draft — this section reflects the current product direction and will be final - **Distribution-first CLI** — `agentplugins add ` installs a plugin once; the universal store (`~/.agents/plugins//`) fans out via symlinks to every detected agent harness. - **Skills.sh compatibility** — plugins exposing `SKILL.md` are first-class citizens; our CLI reads both AgentPlugins and Skills.sh layouts. -- **JSON Schema** — `@agentplugins/schema` package + hosted JSON Schema (`agentplugins.dev/schema/v1.json`) for editor autocomplete and self-documenting manifests. -- **Tier-1 parity primitives** — every shipped capability works across Claude Code, Codex, OpenCode, and Pi Mono. The [Tier-1 Capability Matrix](../../docs/reference/compat-matrix.md) is the living contract. +- **JSON Schema** — `@agentplugins/schema` package + hosted JSON Schema (`agentplugins.pages.dev/schema/v1.json`) for editor autocomplete and self-documenting manifests. +- **Parity primitives** — every shipped capability works across Claude Code, Codex, OpenCode, and Pi Mono. The [Capability Matrix](../../docs/guide/capability-matrix.md) is the living contract. - **Security & audit guardrails** — `agentplugins audit` scores supply-chain risk (OSV, Scorecard, npm provenance); safe-fetch SSRF guard; lifecycle script policy. - **Public launch** — registry, docs site, examples, and a stable v1 manifest format. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..9ec12c0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ["https://buy.polar.sh/polar_cl_Mv1gdlG7bw3I70EC9IHtfeSHJj4PEKvA7JAUz23CFhj"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..22dbba8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,46 @@ +name: Bug Report +description: Something is broken +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Before filing: check the [capability matrix](https://agentplugins.pages.dev/guide/capability-matrix) — some behaviours are by design. + - type: textarea + id: description + attributes: + label: What happened? + description: A clear description of the bug. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + placeholder: | + 1. Run `agentplugins add ...` + 2. See error + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behaviour + validations: + required: true + - type: input + id: version + attributes: + label: AgentPlugins version + placeholder: "0.5.0" + validations: + required: true + - type: input + id: harness + attributes: + label: Agent harness(es) affected + placeholder: "Claude Code, Codex, ..." + - type: textarea + id: context + attributes: + label: Additional context diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9f01e85 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Documentation + url: https://agentplugins.pages.dev + about: Read the full docs before filing an issue. + - name: Capability Matrix + url: https://agentplugins.pages.dev/guide/capability-matrix + about: Check if a behaviour is by design (⚠️ guided per-harness). diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..f884151 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,30 @@ +name: Feature Request +description: Propose a new capability or improvement +labels: ["feature"] +body: + - type: textarea + id: problem + attributes: + label: Problem or motivation + description: What are you trying to do that you can't do today? + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + validations: + required: true + - type: textarea + id: harnesses + attributes: + label: Harnesses this should cover + placeholder: "Claude Code, Codex, OpenCode, Pi Mono (Tier-1) / Copilot, Gemini, Kimi (Tier-2)" + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + - type: textarea + id: context + attributes: + label: Additional context diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..41dbc6e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +## Summary + + + +## Changes + + + +## Test plan + +- [ ] `pnpm test` passes +- [ ] `pnpm -r exec tsc --noEmit` passes +- [ ] Capability matrix updated (if adapter behaviour changed) +- [ ] Linked to a refined issue in Project 14 + +## Notes + + diff --git a/.github/social-preview.snip.txt b/.github/social-preview.snip.txt new file mode 100644 index 0000000..1fcabdd --- /dev/null +++ b/.github/social-preview.snip.txt @@ -0,0 +1,16 @@ +$ agentplugins add sigilco/agentplugins-ponytail + + > AgentPlugins - write once, ship to every AI agent + + + Resolved sigilco/agentplugins-ponytail@0.4.0 + + ponytail: linked to ~/.agents/plugins/ponytail/ + + Detected 5 harnesses: + + claude-code ~/.claude/skills/ponytail + + opencode ~/.config/opencode/skills/ponytail + + codex ~/.codex/skills/ponytail + + pimono ~/.pi/skills/ponytail + + copilot ~/.copilot/skills/ponytail + +$ agentplugins list + agentplugins-ponytail v0.4.0 5/5 harnesses diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 722d67d..dde591d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -106,11 +106,8 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build workspace dependencies - run: pnpm --filter @agentplugins/core --filter @agentplugins/schema build - - - name: Build CLI (tsc) - run: pnpm --filter @agentplugins/cli build + - name: Build all packages (topological) + run: pnpm --filter './packages/**' build - name: Compile native binary run: | diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml new file mode 100644 index 0000000..c77b21d --- /dev/null +++ b/.github/workflows/triage.yml @@ -0,0 +1,34 @@ +name: Triage + +on: + issues: + types: [opened, reopened] + pull_request_target: + types: [opened, reopened] + +jobs: + label: + name: Auto-label + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Label issues by template + if: github.event_name == 'issues' + uses: actions/github-script@v7 + with: + script: | + const body = context.payload.issue.body ?? ''; + const title = context.payload.issue.title ?? ''; + // Templates already inject labels via YAML; this is a fallback. + // If no label was set, apply 'needs-triage'. + const existing = context.payload.issue.labels.map(l => l.name); + if (existing.length === 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + labels: ['needs-triage'], + }); + } diff --git a/.gitignore b/.gitignore index b065e29..4fb4f63 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ dist/ *.d.ts *.d.ts.map +# Exceptions: source declaration files (not build artifacts) +!docs/.vitepress/env.d.ts + # pnpm .pnpm-store/ .pnpm/ @@ -48,11 +51,22 @@ coverage/ storybook-static/ docs/.vitepress/cache/ docs/.vitepress/dist/ -docs/public/ +docs/public/* +!docs/public/robots.txt +!docs/public/favicon.svg +!docs/public/favicon.ico +!docs/public/og.png +!docs/public/img/ +!docs/public/img/logo-dark.png +!docs/public/img/logo-light.png # Misc *.local *.backup +# Session / scratch files +tmp/ + # Deepwork session state (ephemeral) .slim/ +.npmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..8fdd954 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index e70505b..48b8a41 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ GitHub Project is [https://github.com/users/espetro/projects/14](https://github. 1. **Tier-1 parity is the bar.** Every shipped capability must work across all four Tier-1 harnesses at the functionality level (not TUI level). 2. **Codegen first, guided per-harness fallback second.** Universal codegen where possible; otherwise guide the author via the escape hatch rather than dropping the feature. -3. **Compat matrix is the contract.** `docs/reference/compat-matrix.md` — universal-codegen / guided-per-harness / unsupported, per Tier-1 harness. Keep it current. +3. **Compat matrix is the contract.** `docs/guide/capability-matrix.md` — universal-codegen / guided-per-harness / unsupported, per harness. Keep it current. 4. **Lean, no global SDK.** Primitives express intent; each adapter owns its plumbing. 5. **Community plugins are ground-up rewrites** in `agentplugins-` sibling repos (e.g. `../agentplugins-caveman`), not mechanical ports. @@ -15,6 +15,6 @@ Full detail: [`prd.md`](.agents/docs/prd.md) ## Commit & branch conventions - Atomic conventional commits (`feat(adapter-pimono): fix subagentStop collision`) -- Feature work on `feat/v0.3.0-authoring-wins`; release branch `release/v0.3.0` cut from `develop` +- Feature work on `feat/*` branches off `develop`; merge to `develop`, then PR `develop → main` to release - All plans in `.agents/plans/-.md` before implementation begins - All work linked to a refined issue in [Project 14](https://github.com/users/espetro/projects/14/views/1) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 13cf6f0..d890751 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -3,6 +3,7 @@ AgentPlugins is a **ports-and-adapters (hexagonal) compiler pipeline**. Two orthogonal axes: + - **Capability axis** (hooks/commands/continueWith/…) — *layered*: modeled once in `contract`, emitted via the shared `compile` kernel. Cross-cutting capabilities hit every target, so the kernel handles them once. - **Target axis** (claude/codex/opencode/pimono/…) — *sliced*: each `adapter-*` is a self-contained vertical slice over the kernel, declaring only its event-name table and target config. @@ -19,6 +20,7 @@ graph TD CLI["@agentplugins/cli"] CORE["@agentplugins/core (facade)"] CONTRACT["@agentplugins/contract"] + PIPELINE["@agentplugins/pipeline"] COMPILE["@agentplugins/compile"] STORE["@agentplugins/store"] SCHEMA["@agentplugins/schema"] @@ -31,31 +33,78 @@ graph TD A_OC["adapter-opencode"] A_PI["adapter-pimono"] - CLI --> CORE + CLI --> CORE & PIPELINE CLI --> A_CLAUDE & A_CODEX & A_COPILOT & A_GEMINI & A_KIMI & A_OC & A_PI - CORE --> CONTRACT & COMPILE & STORE + CORE --> CONTRACT & COMPILE & STORE & PIPELINE A_CLAUDE & A_CODEX & A_COPILOT & A_GEMINI & A_KIMI & A_OC & A_PI --> COMPILE - COMPILE --> CONTRACT - STORE --> CONTRACT & SECURITY + COMPILE --> CONTRACT & PIPELINE + STORE --> CONTRACT & SECURITY & PIPELINE SCHEMA --> CONTRACT + PIPELINE --> CONTRACT style CONTRACT fill:#ffd700,color:#000 + style PIPELINE fill:#dda0dd,color:#000 style COMPILE fill:#87ceeb,color:#000 style CORE fill:#90ee90,color:#000 ``` **Allowed dependency directions (inward only):** -| Package | Single responsibility | Allowed deps | -|---|---|---| -| `contract` | Zod schema → TS types → JSON Schema. The single source of truth for the manifest model. | `zod` | -| `compile` | Shared codegen kernel: emit helpers, sanitizers, lint, validation, hook-wrapper. | `contract` | -| `store` | Fetch / install / link / fs isolation. Registry, symlink fanout, doctor. | `contract`, `security` | -| `schema` | Re-exports generated JSON Schema artifacts from `contract` build step. | `contract` | -| `security` | Safe-fetch, integrity verification, script-policy. No plugin dependencies. | — | -| `adapter-*` | Target config + event-name table + calls to kernel helpers. | `compile` (→ `contract`) | -| `core` | Re-export facade. Public API is unchanged; internal origin moves to sub-packages. | `contract`, `compile`, `store` | -| `cli` | Build/add/install/link commands, tty output. | `core`, `adapter-*` | + +| Package | Single responsibility | Allowed deps | +| ----------- | --------------------------------------------------------------------------------------- | ------------------------------------------ | +| `contract` | Zod schema → TS types → JSON Schema. The single source of truth for the manifest model. | `zod` | +| `pipeline` | Middleware kernel: Plugin, Middleware, contexts, createApp(). No business logic. | `contract` | +| `compile` | Shared codegen kernel: emit helpers, sanitizers, lint, validation, hook-wrapper. | `contract`, `pipeline` | +| `store` | Fetch / install / link / fs isolation. Registry, symlink fanout, doctor. | `contract`, `security`, `pipeline` | +| `schema` | Re-exports generated JSON Schema artifacts from `contract` build step. | `contract` | +| `security` | Safe-fetch, integrity verification, script-policy. No plugin dependencies. | — | +| `adapter-*` | Target config + event-name table + calls to kernel helpers. | `compile` (→ `contract`) | +| `core` | Re-export facade. Public API is unchanged; internal origin moves to sub-packages. | `contract`, `compile`, `store`, `pipeline` | +| `cli` | Build/add/install/link commands, tty output. | `core`, `pipeline`, `adapter-*` | + + +--- + +## Plugin bus (`@agentplugins/pipeline`) + +Every pipeline stage is middleware over a shared context with `next()`. Built-in adapters, lint rules, and emitters are registered as default `Plugin` objects; power users compose additional ones via `defineConfig`. + +```ts +// agentplugins.config.ts +import { defineConfig } from '@agentplugins/core'; +import myAdapter from './adapters/my-harness.js'; + +export default defineConfig({ + manifest: { name: 'my-plugin', version: '1.0.0', description: '...' }, + plugins: [ + { + name: 'my-harness', + adapter: myAdapter, // contributes a compile target + transformIR: async (ctx, next) => { // mutates the manifest IR before emit + ctx.manifest = { ...ctx.manifest, /* your transforms */ }; + await next(); + }, + }, + ], + targets: ['claude', 'my-harness'], +}); +``` + +`Plugin` interface (from `@agentplugins/pipeline`): + +| Field | Stage | Purpose | +| -------------- | --------------- | ---------------------------------------- | +| `adapter` | compile | Contribute a compile target | +| `lintRules` | preValidate | Add build-time checks | +| `emitters` | compile | Add code-generation languages | +| `preValidate` | before IR | Validate or reject the manifest | +| `transformIR` | after validate | Mutate the PluginManifest IR | +| `postEmit` | after compile | Inspect/rewrite emitted files per-target | +| `onInstall` | install | Gate or transform install steps | +| `onAudit` | audit | Run custom audit checks | + +`TargetPlatform` is now an open string type — built-in ids keep autocomplete, custom ids are valid as long as a matching adapter is registered. --- @@ -65,15 +114,18 @@ Plugin source → universal IR → per-target files. ```mermaid flowchart LR - SRC["plugin.ts\n(definePlugin)"] + SRC["plugin.ts\n(defineConfig / definePlugin)"] + PREVALI["preValidate\nmiddleware chain"] VALIDATE["validateUniversal()\n+ validateForPlatform()"] IR["PluginManifest IR"] + TRANSIR["transformIR\nmiddleware chain"] LINT["lint()\n(handler-safety, secrets, …)"] EMIT["adapter.compile()\n→ emitCommandHandler()\n→ emitInlineHandler()\n→ sanitizeName/Join()"] + POSTEMIT["postEmit\nmiddleware chain"] FILES["dist//\n*.ts / *.json / *.md"] NATIVE["nativeCopies\n(verbatim passthrough)"] - SRC --> VALIDATE --> IR --> LINT --> EMIT --> FILES + SRC --> PREVALI --> VALIDATE --> TRANSIR --> IR --> LINT --> EMIT --> POSTEMIT --> FILES IR -.->|nativeEntry set| NATIVE --> FILES ``` @@ -94,8 +146,8 @@ Source → fetch → install → link → harness discovers plugin. flowchart LR SRC["GitHub URL\n(or local path)"] CLONE["cloneRepo()\nor pullRepo()"] - STORE_DIR["~/.agents/plugins//"] - BUILD["agentplugins build\n→ dist//"] + STORE_DIR["~/.agents/plugins/{name}/"] + BUILD["agentplugins build\n→ dist/{target}/"] LINK["symlinkPlugin()\nlinkCompiledPlugin()\nlinkPluginSkills()"] HARNESS["Harness plugin dirs\n~/.config/opencode/plugins/\n~/.pi/agent/extensions/\n~/.claude/plugins/\n…"] @@ -130,8 +182,9 @@ interface PlatformAdapter { ``` Adapters declare **only**: + 1. `HOOK_MAPPING: Partial>` — universal → target event name 2. `validate()` — target-specific constraint checks (delegates to `compile` kernel) 3. `compile()` — calls kernel helpers to emit files; must not contain raw `shell: true` or string interpolation into commands -The kernel (`compile`) owns all security-sensitive emit logic. Adapters are thin mappings. +The kernel (`compile`) owns all security-sensitive emit logic. Adapters are thin mappings. \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7a5d958 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,27 @@ +# Code of Conduct + +## Our pledge + +We pledge to make participation in this project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our standards + +**Encouraged:** +- Using welcoming and inclusive language +- Respecting differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community + +**Not acceptable:** +- Trolling, insulting or derogatory comments, or personal attacks +- Public or private harassment +- Publishing others' private information without permission +- Other conduct reasonably considered inappropriate in a professional setting + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening a GitHub issue or contacting the maintainers privately. All complaints will be reviewed and investigated promptly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..46bc364 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing to AgentPlugins + +## Quick start + +```bash +git clone https://github.com/sigilco/agentplugins +cd agentplugins +pnpm install +pnpm --filter './packages/**' build +pnpm test +``` + +## Branch model + +- `main` — release branch; protected, CI-gated, no direct pushes +- `develop` — integration branch; all feature work merges here first +- `feat/` — feature branches off `develop` + +## Commit style + +Atomic [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat(adapter-opencode): emit mcpServers as mcp.servers +fix(cli): bump scaffolded devDeps to ^0.5.0 +docs(quick-start): correct version output +``` + +One logical change per commit. Keep commits independently revertable. + +## Adding a new adapter + +1. Create `packages/adapter-/` following the existing adapter structure. +2. Implement `validate()` and `compile()` from `@agentplugins/core/adapter`. +3. Register the adapter in `packages/cli/src/adapters.ts`. +4. Add a row to `docs/guide/capability-matrix.md`. +5. Ship a community plugin in a sibling repo `agentplugins-`. + +## Community plugins + +Community plugins live in separate repos (`agentplugins-`) — not in this monorepo. They are ground-up rewrites, not mechanical ports. See the [plugin authoring guide](https://agentplugins.pages.dev/guide/creating-plugins). + +## PR checklist + +- [ ] `pnpm test` passes +- [ ] `pnpm -r exec tsc --noEmit` passes +- [ ] Capability matrix updated if adapter behaviour changed +- [ ] Linked to a refined issue in [Project 14](https://github.com/users/espetro/projects/14/views/1) + +## Code of conduct + +See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). diff --git a/README.md b/README.md index 3bdee0c..389ec72 100644 --- a/README.md +++ b/README.md @@ -1,223 +1,63 @@ # AgentPlugins -> **Install any plugin into every AI agent with one command.** +

+ + + AgentPlugins + +

-AgentPlugins is a plugin manager for AI agent harnesses. Install a plugin once — it's symlinked into Claude, Codex, Copilot, Gemini, Kimi, OpenCode, and Pi Mono automatically. Includes a codegen toolkit for authors who want to write once and compile to all platforms. +> Write AI agent plugins once, ship to any harness. -```bash -agentplugins add user/awesome-plugin -# → Clones, parses manifest, symlinks to every detected agent -``` - -## Install - -**5 ways to install AgentPlugins:** +Install any plugin into every supported AI agent — **Tier-1:** Claude Code, Codex, OpenCode, Pi Mono. **Tier-2:** Copilot, Gemini, Kimi. ```bash -# 1. npm / npx / bunx npx @agentplugins/cli add user/awesome-plugin -bunx @agentplugins/cli add user/awesome-plugin - -# 2. Homebrew -brew install sigilco/tap-agentplugins/agentplugins - -# 3. curl (macOS + Linux) -curl -fsSL https://raw.githubusercontent.com/sigilco/agentplugins/main/scripts/install.sh | bash - -# 4. mise (via UBI) -mise use -g ubi:sigilco/agentplugins - -# 5. GitHub Releases (prebuilt binaries) -# → https://github.com/sigilco/agentplugins/releases -``` - -Verify installation: - -```bash -agentplugins doctor ``` -## Available Plugins - -Community plugins install with one command — see the [Ecosystem page](https://agentplugins.pages.dev/guide/ecosystem) for the full list: - -| Plugin | Description | -|---|---| -| [agentplugins-autoresearch](https://github.com/sigilco/agentplugins-autoresearch) | Autonomous experiment/optimization loop. | -| [agentplugins-goal](https://github.com/sigilco/agentplugins-goal) | Autonomous goal-completion loop across Tier-1. | -| [agentplugins-ponytail](https://github.com/sigilco/agentplugins-ponytail) | Lazy senior dev mode — minimal working solutions. | -| [agentplugins-caveman](https://github.com/sigilco/agentplugins-caveman) | Ultra-compressed token-savvy communication. | -| [agentplugins-btw](https://github.com/sigilco/agentplugins-btw) | Parallel side-thread thoughts. | - ```bash agentplugins add sigilco/agentplugins-ponytail ``` -## Quick Start - -### Install a Plugin +Or install the CLI globally first: ```bash -# From a GitHub URL or user/repo shorthand -agentplugins add user/my-plugin - -# List installed plugins -agentplugins list - -# Show details -agentplugins info my-plugin - -# Update to latest -agentplugins update my-plugin -# or update all -agentplugins update --all - -# Remove -agentplugins remove my-plugin +curl -fsSL https://agentplugins.pages.dev/install.sh | bash ``` -Plugins are stored in a universal store (`~/.agents/plugins//`) and symlinked into each agent's plugin directory. Skills.sh-compatible plugins are symlinked to `~/.agents/skills/` as well. +## Create a plugin -### Create a Plugin (Authors) +Scaffold a plugin from a template, write your manifest, build, and publish to GitHub: ```bash -# Interactive scaffold agentplugins init - -# With defaults -agentplugins init --yes - -# From a template -agentplugins init --template security-guard -``` - -Templates: `minimal`, `logger` (default), `security-guard`, `formatter`. - -### Build for All Platforms - -```bash -# Validate manifest -agentplugins validate - -# Lint for common issues -agentplugins lint - -# Preview compiled output without writing -agentplugins preview -agentplugins preview --diff - -# Build to dist/ agentplugins build ``` -## Commands - - -| Command | Description | -| --------------- | ----------------------------------------------------------- | -| `add ` | Install a plugin from GitHub URL or `user/repo` | -| `remove ` | Remove a plugin and unlink from all agents | -| `list` | List installed plugins (`--json` for machine output) | -| `info ` | Show plugin metadata, manifest, and symlink status | -| `update [name]` | Update plugin(s) from source (`--all` for all) | -| `doctor` | Diagnose store, symlinks, and agent detection | -| `init` | Scaffold a new plugin interactively (`--yes`, `--template`) | -| `build` | Compile plugin for all target platforms | -| `validate` | Validate manifest against schema | -| `lint` | Static analysis for common issues (`--json`) | -| `preview` | Preview compiled output (`--diff`, `--target`) | - - -## Supported Platforms - - -| Platform | Store Path | Handler Types | -| -------------- | ----------------------------- | ------------- | -| Claude Code | `~/.claude/skills/` | command, http | -| OpenAI Codex | `~/.codex/plugins/` | command | -| GitHub Copilot | `~/.config/github-copilot/` | command, http | -| Google Gemini | `~/.gemini/extensions/` | command | -| Kimi | `~/.kimi/plugins/` | command | -| OpenCode | `~/.config/opencode/plugins/` | inline (TS) | -| Pi Mono | `~/.pi/agent/extensions/` | inline (TS) | - - -## Packages - - -| Package | Description | -| ------------------------------------------ | ---------------------------------------------------------------------------- | -| `[@agentplugins/core](packages/core/)` | Universal types, validation, lint, codegen, store | -| `[@agentplugins/cli](packages/cli/)` | CLI binary (`agentplugins`) | -| `[@agentplugins/schema](packages/schema/)` | JSON Schema + TS types + Ajv validator | -| `[@agentplugins/adapter-*](packages/)` | 7 platform adapters (claude, codex, copilot, gemini, kimi, opencode, pimono) | - - -## Manifest - -Plugins declare a manifest in `agentplugins.config.json`, `manifest.json`, or the `agentplugins` field of `package.json`: - -```json -{ - "name": "my-plugin", - "version": "1.0.0", - "description": "Does something useful across agents", - "hooks": { - "preToolUse": { - "matcher": "bash", - "handler": { - "type": "command", - "command": "${PLUGIN_ROOT}/check.sh" - } - } - }, - "skills": [{ - "name": "my-skill", - "description": "A skill description", - "path": "./skills/my-skill.md" - }] -} -``` +Full guide → [agentplugins.pages.dev/guide/creating-plugins](https://agentplugins.pages.dev/guide/creating-plugins) -Full manifest spec: `[spec/v1/manifest.schema.json](spec/v1/manifest.schema.json)` · Docs: [agentplugins.pages.dev](https://agentplugins.pages.dev) +Porting an existing plugin? → [agentplugins.pages.dev/guide/porting](https://agentplugins.pages.dev/guide/porting) -## Development +## Supported agents -```bash -pnpm install -pnpm build # Build all packages -pnpm test # Run tests -pnpm typecheck # Type-check all packages +**Tier-1** (full capability parity): Claude Code, Codex, OpenCode, Pi Mono. -# Build native binaries (requires Bun) -pnpm build:binaries +**Tier-2** (skills + commands, subset of hooks): Copilot, Gemini, Kimi. -# Docs site -cd docs && pnpm install && pnpm dev -``` +Capability comparison → [agentplugins.pages.dev/guide/capability-matrix](https://agentplugins.pages.dev/guide/capability-matrix) ## Architecture -``` -Plugin Source (GitHub) - │ - ▼ -┌─────────────────────────────────┐ -│ AgentPlugins Store │ -│ ~/.agents/plugins// │ -└───────────┬─────────────────────┘ - │ symlinks - ┌────────┼────────┬────────┐ - ▼ ▼ ▼ ▼ - Claude Codex Copilot Gemini ... -``` +One manifest → seven adapters. Each adapter owns its output format; the `@agentplugins/core` compiler routes capability expressions to harness-native primitives and emits a WARN for any gap. -For codegen (power users): +Full detail → [ARCHITECTURE.md](./ARCHITECTURE.md) -``` -Manifest → Core (validate + compile) → Adapters → Platform-native output -``` +## Documentation + +Full docs → [agentplugins.pages.dev](https://agentplugins.pages.dev) + +LLMs.txt for AI agents → [agentplugins.pages.dev/llms.txt](https://agentplugins.pages.dev/llms.txt) -## License +--- -Apache-2.0 \ No newline at end of file +Apache-2.0 · [GitHub](https://github.com/sigilco/agentplugins) · [Sponsor](https://buy.polar.sh/polar_cl_Mv1gdlG7bw3I70EC9IHtfeSHJj4PEKvA7JAUz23CFhj) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..acaf299 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,33 @@ +# Security Policy + +## Supported versions + +| Version | Supported | +| ------- | --------- | +| 0.x | ✅ Yes | + +## Reporting a vulnerability + +**Do not open a public GitHub issue for security vulnerabilities.** + +Report security issues by emailing the maintainers directly or using [GitHub private vulnerability reporting](https://github.com/sigilco/agentplugins/security/advisories/new). + +Include: +- Description of the vulnerability +- Steps to reproduce +- Potential impact +- Suggested fix (if any) + +We aim to respond within 72 hours and will coordinate a fix and disclosure timeline with you. + +## Scope + +In scope: +- Remote code execution via plugin manifests or install flow +- Path traversal in adapter output generators +- Prototype pollution in manifest parsing +- Supply chain issues in published npm packages + +Out of scope: +- Issues in community plugins (report to their respective repos) +- Theoretical issues without a practical exploit path diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index e57cfaf..d0798d3 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -9,6 +9,7 @@ import { fileURLToPath } from 'url' const __dirname = dirname(fileURLToPath(import.meta.url)) const DOCS_SITE = process.env.DOCS_SITE ?? 'https://agentplugins.pages.dev' const GITHUB_SITE = 'https://github.com/sigilco/agentplugins' +const SPONSOR_SITE = 'https://buy.polar.sh/polar_cl_Mv1gdlG7bw3I70EC9IHtfeSHJj4PEKvA7JAUz23CFhj' // Serve scripts/install.sh at the site root (agentplugins.pages.dev/install.sh) // by mirroring it into docs/public/ at config load. scripts/install.sh is the @@ -28,8 +29,36 @@ export default withMermaid(defineConfig({ cleanUrls: true, lastUpdated: true, + sitemap: { + hostname: DOCS_SITE, + }, + head: [ ['meta', { name: 'theme-color', content: '#3c82f6' }], + ['link', { rel: 'icon', type: 'image/png', href: '/img/logo-dark.png' }], + ['link', { rel: 'alternate icon', href: '/favicon.ico' }], + ['meta', { name: 'keywords', content: 'ai agent plugin, claude code plugin, codex plugin, opencode plugin, universal agent manifest, ai harness plugin manager' }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:url', content: DOCS_SITE }], + ['meta', { property: 'og:title', content: 'AgentPlugins – Universal Plugin Standard for AI Agents' }], + ['meta', { property: 'og:description', content: 'Write AI agent plugins once, ship to Claude Code, Codex, OpenCode, Copilot, Gemini, Kimi, and Pi Mono. One manifest. Zero lock-in.' }], + ['meta', { property: 'og:image', content: `${DOCS_SITE}/og.png` }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + ['meta', { name: 'twitter:title', content: 'AgentPlugins – Universal Plugin Standard for AI Agents' }], + ['meta', { name: 'twitter:description', content: 'Write AI agent plugins once, ship to Claude Code, Codex, OpenCode, Copilot, Gemini, Kimi, and Pi Mono.' }], + ['meta', { name: 'twitter:image', content: `${DOCS_SITE}/og.png` }], + ['script', { type: 'application/ld+json' }, JSON.stringify({ + "@context": "https://schema.org", + "@type": "SoftwareApplication", + "name": "AgentPlugins", + "description": "Universal plugin standard for AI agents. Write once, ship to any harness.", + "url": DOCS_SITE, + "applicationCategory": "DeveloperApplication", + "operatingSystem": "All", + "offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }, + "license": "https://www.apache.org/licenses/LICENSE-2.0", + "codeRepository": "https://github.com/sigilco/agentplugins" + })], ], markdown: { @@ -43,14 +72,15 @@ export default withMermaid(defineConfig({ vite: { plugins: [ - llmstxt(), - groupIconVitePlugin(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + llmstxt() as any, + groupIconVitePlugin() as any, // Serve llms.txt / llms-full.txt from .vitepress/dist/ during dev { name: 'vitepress-dev-llms-txt', configureServer(server) { server.middlewares.use((req, res) => { - const url = req.url.split('?')[0] + const url = (req.url ?? '').split('?')[0] if (url === '/llms.txt' || url === '/llms-full.txt') { const distPath = resolve(__dirname, '.vitepress/dist' + url) if (existsSync(distPath)) { @@ -70,11 +100,16 @@ export default withMermaid(defineConfig({ mermaid: { theme: 'default' }, themeConfig: { + logo: { + light: '/img/logo-light.png', + dark: '/img/logo-dark.png', + alt: 'AgentPlugins', + }, nav: [ { text: 'Guide', link: '/guide/introduction' }, - { text: 'Commands', link: '/reference/commands' }, - { text: 'Schema', link: '/reference/schema' }, + { text: 'Reference', link: '/reference' }, { text: 'GitHub', link: GITHUB_SITE }, + { text: 'Sponsor', link: SPONSOR_SITE }, { text: 'LLMs', items: [ @@ -92,25 +127,27 @@ export default withMermaid(defineConfig({ { text: 'Introduction', link: '/guide/introduction' }, { text: 'Installation', link: '/guide/installation' }, { text: 'Quick Start', link: '/guide/quick-start' }, + { text: 'Capability Matrix', link: '/guide/capability-matrix' }, ], }, { - text: 'Guide', + text: 'Concepts', items: [ { text: 'Manifest', link: '/guide/manifest' }, { text: 'Hooks', link: '/guide/hooks' }, { text: 'Skills', link: '/guide/skills' }, { text: 'MCP Servers', link: '/guide/mcp-servers' }, { text: 'Tools', link: '/guide/tools' }, - { text: 'Creating Plugins', link: '/guide/creating-plugins' }, - { text: 'Linting', link: '/guide/linting' }, ], }, { - text: 'Community', + text: 'Authoring', items: [ - { text: 'Ecosystem', link: '/guide/ecosystem' }, + { text: 'Creating Plugins', link: '/guide/creating-plugins' }, { text: 'Porting an Existing Plugin', link: '/guide/porting' }, + { text: 'Extending the Build Pipeline', link: '/guide/extending' }, + { text: 'Linting', link: '/guide/linting' }, + { text: 'Ecosystem', link: '/guide/ecosystem' }, ], }, ], @@ -122,7 +159,6 @@ export default withMermaid(defineConfig({ { text: 'JSON Schema', link: '/reference/schema' }, { text: 'Agent Paths', link: '/reference/agent-paths' }, { text: 'Adapters', link: '/reference/adapters' }, - { text: 'Tier-1 Capability Matrix', link: '/reference/compat-matrix' }, ], }, ], @@ -137,7 +173,7 @@ export default withMermaid(defineConfig({ }, footer: { - message: 'Released under the MIT License.', + message: 'Released under the Apache License 2.0.', copyright: 'Copyright © AgentPlugins contributors', }, diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts new file mode 100644 index 0000000..0595031 --- /dev/null +++ b/docs/.vitepress/env.d.ts @@ -0,0 +1,2 @@ +declare module 'virtual:group-icons.css'; +declare module '*.css'; diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css new file mode 100644 index 0000000..784c7fe --- /dev/null +++ b/docs/.vitepress/theme/custom.css @@ -0,0 +1,24 @@ +/* ── Hero image glow — greyscale radial ──────────────────────────────────── */ + +:root { + --vp-home-hero-image-background-image: radial-gradient( + circle, + #9ca3af 0%, + #d1d5db 50%, + transparent 70% + ); + --vp-home-hero-image-filter: blur(64px); +} + +/* Dark/light logo swap via CSS content override */ +html:not(.dark) .VPHero .image-src { + content: url('/img/logo-light.png'); +} +html.dark .VPHero .image-src { + content: url('/img/logo-dark.png'); +} + +/* Give the hero image a bit more breathing room */ +.VPHero .image-container { + transform: scale(1.05); +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 364bd84..3a5876d 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,4 +1,5 @@ import DefaultTheme from 'vitepress/theme' import 'virtual:group-icons.css' +import './custom.css' export default DefaultTheme diff --git a/docs/guide/capability-matrix.md b/docs/guide/capability-matrix.md new file mode 100644 index 0000000..38203dd --- /dev/null +++ b/docs/guide/capability-matrix.md @@ -0,0 +1,66 @@ +--- +title: Capability Matrix +description: What each supported harness supports — universal codegen, guided per-harness, or unsupported. Includes the native escape hatch for every gap so you can ship on the native path today. +--- + +# Capability Matrix + +**Supported harnesses:** Claude Code · Codex · OpenCode · Pi Mono + +**Additional (tracked, not blocking):** Copilot · Gemini · Kimi + +Legend: + +- ✅ **Universal codegen** — adapter emits native output automatically from the manifest. +- ⚠️ **Guided per-harness** — no native primitive of its own; a documented escape-hatch pattern covers the gap. Emits a WARN (not error) on portable manifests. +- ❌ **Unsupported** — no viable path on this harness; recorded here, not blocking. +- n/a — not applicable (mechanism differs but functionality is covered). + +> **Reading the "Escape hatch" column:** for every ⚠️ cell there's a deliberate reason we don't emit a universal primitive for that capability on that harness yet — the work to express it universally is significant, and a native path already does the job. If you're shipping today, use the escape hatch. If you want a universal primitive, see the **Decision tree for authors** at the bottom. + +## Capability table + +| Capability | Claude Code | Codex | OpenCode | Pi Mono | Escape hatch | Notes | +| ------------------------ | :----------: | :----------: | :------: | :-----: | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `skills` | ✅ | ✅ | ✅ | ✅ | — | Universal codegen | +| `hooks` (lifecycle) | ✅ | ✅ | ✅ | ✅ | — | Universal codegen | +| `commands` | ✅ | ✅ | ✅ | ✅ | — | Universal codegen | +| `mcpServers` | ✅ | ✅ | ✅ | ⚠️ | Pi has no built-in MCP. Ship tools via native `tools[]` (emitted natively), or bridge an MCP server through a Pi extension via `nativeEntry.pimono`. | Universal on Claude · Codex · OpenCode. Pi Mono has no MCP; emits WARN when `mcpServers` is set. See [MCP on Pi](/guide/porting#mcp-on-pi). | +| `agents[]` | ✅ | ✅ | ✅ | ⚠️ | Pi has no named-agent declaration primitive. Use `nativeEntry.pimono` to spawn custom agents via a Pi extension. | Pi adapter emits nothing for `agents[]`; use `nativeEntry.pimono` for custom agent wiring on Pi. | +| `agents[].model` | ✅ | ⚠️ | ✅ | ⚠️ | Codex/Pi: use the harness's own per-agent model config (or simply omit and accept the harness default). | Claude + OpenCode emit `model:` frontmatter when set. Codex/Pi have no per-agent file concept; model unset → harness default. | +| `subagentStart` | ✅ | ✅ | ⚠️ | ✅ | OpenCode: intercept `subagent` tool calls with `preToolUse` matcher (subagents launch via the `subagent` tool). | Emits WARN on OpenCode. Pi maps to `agent.AgentStart` lifecycle event. | +| `subagentStop` | ✅ | ✅ | ⚠️ | ✅ | OpenCode: detect via `postToolUse` / `postToolUseFailure` on the `subagent` tool. | Emits WARN on OpenCode. Pi maps to `agent.AgentStop` lifecycle event. Pi `stop`↔`subagentStop` collision fixed in v0.3.0. | +| `tools[]` (first-class) | ⚠️ | ⚠️ | ✅ | ✅ | Claude/Codex: ship tools via `mcpServers` — works on all four harnesses (universal). | WARN emitted; OpenCode/Pi emit first-class `tools[]` natively. | +| `stop` / `continueWith` | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Each harness already has a `stop`-class lifecycle hook natively — emit nothing in portable manifests until v0.5.0. | New universal primitive — v0.5.0; all-harness design. | +| Native-entry passthrough | n/a (JSON) | n/a (JSON) | ⚠️ | ⚠️ | OpenCode: drop a `.ts` file directly into `~/.config/opencode/plugins//` — Bun runs it as ESM, no codegen needed. | `nativeEntry` escape hatch — ships in v0.5.0; OpenCode native modules must be `.ts` (file-drop path). | +| Inline hook handlers | ✅ auto-wrap | ✅ auto-wrap | ✅ | ✅ | — | Codex/Kimi: auto-wrapped as Node.js command scripts (v0.5.0). | + +## Additional harnesses + +| Capability | Copilot | Gemini | Kimi | Escape hatch | Notes | +| -------------------------------- | :-----: | :----: | :--: | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `skills` | ✅ | ✅ | ✅ | — | Universal. | +| `hooks` (lifecycle) | ⚠️ | ⚠️ | ❌ | Copilot/Gemini: use HTTP or `command` handlers (see [Hooks](/guide/hooks)). | Kimi supports a subset (see below). | +| `subagentStart` / `subagentStop` | ❌ | ❌ | ❌ | TUI fidelity only — implement per-harness via native plugin config if you really need it. | No universal primitive planned. | +| `tools[]` | ✅ | ✅ | ❌ | — | First-class tool emission. | +| `mcpServers` | ❌ | ❌ | ❌ | Wire the MCP server directly into the harness's native config — no agentplugins path needed. | Not on the manifest path; harness-level wiring only. | + +Kimi supported hooks: `preToolUse`, `userPromptSubmit`, `sessionStart`, `notification`, `permissionRequest`. Inline handlers auto-wrapped as Node.js command scripts (v0.5.0). + +## Decision tree for authors + +``` +Does universal codegen cover this capability across all four core harnesses? + YES → use it; adapter handles the rest + NO → does the native escape hatch (Escape hatch column above) cover it today? + YES → ship the escape hatch now; document the limitation in the manifest + NO → can you express it per-harness via nativeEntry + a hand-written module? + YES → use nativeEntry; emit WARN (not error) + NO → is the gap TUI-grade fidelity only? + YES → acceptable degradation; note in this matrix + NO → open a primitive proposal (v0.5.0+ scope) +``` + +--- + +*This matrix is the living contract for the project. Update it as capabilities land or gaps are discovered.* diff --git a/docs/guide/creating-plugins.md b/docs/guide/creating-plugins.md index 64fdbf0..7018583 100644 --- a/docs/guide/creating-plugins.md +++ b/docs/guide/creating-plugins.md @@ -1,5 +1,6 @@ --- -description: Build and publish a universal AgentPlugin +title: Creating Plugins – AgentPlugins +description: Step-by-step guide to authoring an AgentPlugins manifest and shipping to seven AI agent harnesses. --- # Creating Plugins @@ -25,7 +26,7 @@ You'll be prompted for: | Template | What you get | |---|---| -| `minimal` | Bare manifest + `SKILL.md`. Good starting point. | +| `minimal` | Bare manifest. Good starting point. | | `logger` | A plugin that logs every hook event to `${PLUGIN_DATA}/log.jsonl`. | | `security-guard` | A `preToolUse` block-list for dangerous commands. | | `formatter` | A `postToolUse` hook that runs your formatter of choice. | @@ -40,11 +41,14 @@ Pick `minimal` if you're not sure — you can add hooks later. ✓ Created my-plugin/ my-plugin/agentplugins.config.ts - my-plugin/SKILL.md - my-plugin/hooks/ + my-plugin/package.json + my-plugin/tsconfig.json + my-plugin/.gitignore my-plugin/README.md ``` +Hooks, skills, and commands are declared inline in `agentplugins.config.ts` — there is no separate `SKILL.md` or `hooks/` directory in the scaffold. Open `agentplugins.config.ts` to start editing. + ## 2. Write hooks Open `agentplugins.config.ts` and add hooks to the `hooks` object. See the [Hooks guide](/guide/hooks) for the 19 universal hooks and the three handler types. @@ -77,6 +81,34 @@ export default definePlugin({ }) ``` +### `defineConfig` — extended config format + +Use `defineConfig` instead of `definePlugin` when you need to: + +- Target a **subset of platforms** without editing the manifest +- Wire in a **private adapter** for an internal harness +- Add **build pipeline plugins** (custom lint rules, IR transforms, post-emit hooks) + +```typescript +import { defineConfig } from '@agentplugins/core' + +export default defineConfig({ + manifest: { + name: 'my-plugin', + version: '1.0.0', + description: 'Does awesome things across every agent', + hooks: { /* ... */ }, + }, + + // Override which targets are built — does not affect the manifest + targets: ['claude', 'codex'], +}) +``` + +`definePlugin` and `defineConfig` produce the same dist output for the same manifest. Pick `definePlugin` for simple cross-platform plugins; reach for `defineConfig` when you need the extras above. + +See [Extending the Build Pipeline](/guide/extending) for `plugins: [...]` and custom adapters. + ::: tip Place hook scripts under `hooks/` and reference them with `${PLUGIN_ROOT}/hooks/...`. The placeholder resolves to the plugin's directory in the universal store at runtime. ::: @@ -225,4 +257,5 @@ Tag releases with semver (`v1.0.0`, `v1.1.0`, ...). `agentplugins update` resolv - [Manifest reference](/guide/manifest) — every field. - [Hooks](/guide/hooks) — the 19 lifecycle events. +- [Extending the Build Pipeline](/guide/extending) — custom adapters, lint rules, and pipeline plugins. - [CLI reference](/reference/commands) — every command and flag. diff --git a/docs/guide/ecosystem.md b/docs/guide/ecosystem.md index 5508b89..017c25c 100644 --- a/docs/guide/ecosystem.md +++ b/docs/guide/ecosystem.md @@ -5,7 +5,7 @@ description: Community plugins built on the AgentPlugins universal manifest, ins # Ecosystem -Community plugins rewritten on the AgentPlugins universal manifest. Each targets **Tier-1 functional parity** — the same functionality across Claude Code, Codex, OpenCode, and Pi Mono. +Community plugins rewritten on the AgentPlugins universal manifest. Each targets functional parity across the four supported harnesses — the same functionality across Claude Code, Codex, OpenCode, and Pi Mono. Install any plugin with a single command: @@ -20,7 +20,7 @@ This installs the plugin into `~/.agents/plugins//` and symlinks it to eve ## Adding your plugin 1. Build on the AgentPlugins universal manifest (see [Creating Plugins](/guide/creating-plugins)) -2. Target all four Tier-1 harnesses and confirm functional parity +2. Target all four supported harnesses and confirm functional parity 3. Open a PR or issue to add your plugin to this page See [Rewriting for Tier-1 Parity](/guide/porting) for the step-by-step process. @@ -32,9 +32,9 @@ See [Rewriting for Tier-1 Parity](/guide/porting) for the step-by-step process. | Plugin | Description | |---|---| | [agentplugins-autoresearch](https://github.com/sigilco/agentplugins-autoresearch) | Autonomous experiment loop that gathers what to optimize and runs iterations until the target improves. | -| [agentplugins-goal](https://github.com/sigilco/agentplugins-goal) | Autonomous goal-completion loop — set a goal once, the agent iterates until done across all Tier-1 harnesses. | +| [agentplugins-goal](https://github.com/sigilco/agentplugins-goal) | Autonomous goal-completion loop — set a goal once, the agent iterates until done across all supported harnesses. | | [agentplugins-ponytail](https://github.com/sigilco/agentplugins-ponytail) | Lazy senior dev mode — forces the minimum solution that works: YAGNI → stdlib → native → one line. | | [agentplugins-caveman](https://github.com/sigilco/agentplugins-caveman) | Ultra-compressed communication mode — cuts ~75% of output tokens while preserving full technical accuracy. | | [agentplugins-btw](https://github.com/sigilco/agentplugins-btw) | Parallel side-thread thoughts — spin up side conversations without interrupting the main agent. | -> **Note:** Tier-1 capability status per plugin lives in each plugin's README. For the harness-level baseline see the [compat matrix](/reference/compat-matrix). +> **Note:** Capability status per plugin lives in each plugin's README. For the harness-level baseline see the [Capability Matrix](/guide/capability-matrix). diff --git a/docs/guide/extending.md b/docs/guide/extending.md new file mode 100644 index 0000000..9a750f6 --- /dev/null +++ b/docs/guide/extending.md @@ -0,0 +1,264 @@ +--- +title: Extending the Build Pipeline – AgentPlugins +description: Add custom adapters, lint rules, IR transforms, and post-emit hooks to the AgentPlugins build pipeline without forking the tool. +--- + +# Extending the Build Pipeline + +AgentPlugins ships seven built-in adapters. If you maintain an internal harness, want to add custom lint rules, or need to transform the manifest IR before code generation, the `plugins` field in `defineConfig` gives you full access to the build pipeline without forking anything. + +## When to use `plugins` + +| Need | Mechanism | +|---|---| +| Compile to a private/internal harness | `plugin.adapter` | +| Add project-specific lint checks | `plugin.lintRules` | +| Add a new emit language | `plugin.emitters` | +| Validate or reject the manifest before build | `plugin.preValidate` middleware | +| Mutate the manifest IR (e.g. inject metadata) | `plugin.transformIR` middleware | +| Inspect or rewrite emitted files per-target | `plugin.postEmit` middleware | +| Gate or audit install steps | `plugin.onInstall` / `plugin.onAudit` middleware | + +## Basic setup + +```typescript +// agentplugins.config.ts +import { defineConfig } from '@agentplugins/core' + +export default defineConfig({ + manifest: { + name: 'my-plugin', + version: '1.0.0', + description: 'My cross-platform plugin', + hooks: { /* ... */ }, + }, + + plugins: [ + { + name: 'my-extension', + // fields below — mix and match + }, + ], +}) +``` + +All `plugins` entries are composed on top of the built-in adapter set. Built-in adapters (claude, codex, …) are always registered first; your plugins run after and may override them by registering the same target name. + +--- + +## Custom adapter + +A `PlatformAdapter` tells the build system how to validate and compile the manifest into your harness's native format. + +```typescript +// src/my-harness-adapter.ts +import type { PlatformAdapter } from '@agentplugins/core' + +export const myHarnessAdapter: PlatformAdapter = { + name: 'my-harness', // target id — must match the targets[] list + displayName: 'My Harness', + supportedHooks: ['sessionStart', 'preToolUse', 'postToolUse'], + supportedHandlers: ['command', 'inline'], + manifestPath: 'my-harness.json', + manifestFormat: 'json', + + validate(plugin) { + // Return ValidationIssue[] — errors abort build, warnings are printed + return [] + }, + + compile(plugin) { + return { + files: [ + { + path: 'my-harness.json', + content: JSON.stringify({ name: plugin.name, version: plugin.version }, null, 2), + }, + ], + manifest: {}, + warnings: [], + issues: [], + } + }, +} +``` + +Wire it in via `defineConfig`: + +```typescript +import { defineConfig } from '@agentplugins/core' +import { myHarnessAdapter } from './src/my-harness-adapter.js' + +export default defineConfig({ + manifest: { name: 'my-plugin', version: '1.0.0', description: '…' }, + + plugins: [ + { name: 'my-harness-adapter', adapter: myHarnessAdapter }, + ], + + targets: ['claude', 'my-harness'], +}) +``` + +`my-harness` is now a valid target id. Unknown target ids that have no registered adapter are skipped at build time with a warning. + +::: tip Full working example +`plugins/example-custom-adapter/` in the repository shows this pattern end-to-end, producing `dist/claude/` and `dist/my-harness/` from one `agentplugins build` run. +::: + +--- + +## Custom lint rules + +Add build-time checks that run alongside the built-in lint rules: + +```typescript +import type { LintRule } from '@agentplugins/pipeline' + +const requireLicenseRule: LintRule = { + id: 'require-license', + description: 'All plugins in this org must declare a license', + run(ctx) { + if (!ctx.manifest.license) { + return [{ + severity: 'error', + field: 'license', + message: 'license is required for org plugins', + suggestion: 'Add license: "MIT" to your manifest', + }] + } + return [] + }, +} + +export default defineConfig({ + manifest: { /* … */ }, + plugins: [ + { name: 'org-rules', lintRules: [requireLicenseRule] }, + ], +}) +``` + +Custom rules run in strict mode by default — errors abort the build, warnings are printed. + +--- + +## Pipeline middleware + +Middleware functions follow the standard `(ctx, next) => Promise` onion pattern. Call `await next()` to proceed, or `ctx.abort(reason)` to stop the pipeline. + +### `preValidate` — reject before validation + +Runs before `validateUniversal()`. Use it to enforce org-wide manifest constraints: + +```typescript +{ + name: 'org-guard', + preValidate: async (ctx, next) => { + if (!ctx.manifest.name.startsWith('acme-')) { + ctx.abort('All ACME plugins must be named acme-*') + } + await next() + }, +} +``` + +### `transformIR` — mutate the manifest IR + +Runs after validation, before code generation. Use it to inject metadata or normalize fields: + +```typescript +{ + name: 'inject-build-metadata', + transformIR: async (ctx, next) => { + ctx.manifest = { + ...ctx.manifest, + description: `[${process.env.CI_COMMIT_SHA?.slice(0, 7) ?? 'local'}] ${ctx.manifest.description}`, + } + await next() + }, +} +``` + +### `postEmit` — inspect or rewrite emitted files + +Runs per-target after the adapter has produced its files. `ctx.files` is the mutable list of `{ path, content }` entries: + +```typescript +{ + name: 'add-banner', + postEmit: async (ctx, next) => { + ctx.files = ctx.files.map(f => ({ + ...f, + content: `// Built by ACME CI — do not edit\n${f.content}`, + })) + await next() + }, +} +``` + +### `onInstall` — gate or audit install + +Runs during `agentplugins add`. The built-in security checks run here — pinned `integrity` hash verification, then script policy evaluation. You may add your own checks after them: + +```typescript +{ + name: 'org-install-policy', + onInstall: async (ctx, next) => { + if (ctx.pluginName.startsWith('untrusted-')) { + ctx.abort(`Plugin "${ctx.pluginName}" is blocked by org policy`) + } + await next() + }, +} +``` + +::: warning +`onInstall` plugins run in the user's environment, not the plugin author's. Only ship install middleware as part of org-internal tooling, not in public plugins. +::: + +--- + +## Plugin interface reference + +```typescript +interface Plugin { + readonly name: string + + // Compile + adapter?: PlatformAdapter + lintRules?: LintRule[] + emitters?: Record + + // Build pipeline middleware + preValidate?: Middleware + transformIR?: Middleware + postEmit?: Middleware + + // Install pipeline middleware + onAudit?: Middleware + onInstall?: Middleware +} +``` + +Each field is optional — a plugin may contribute any combination. + +## Middleware execution order + +1. `validateUniversal()` — structural checks on the manifest +2. `lint()` — with merged lint rules from all plugins +3. All `preValidate` chains run — can abort before compilation +4. All `transformIR` chains run — may mutate `ctx.manifest` +5. Per-target: `validateForPlatform()` → `adapter.compile()` → all `postEmit` chains +6. Files written to `dist/` + +Install pipeline: + +1. All `onInstall` chains run (pinned-integrity and script-policy checks first) +2. Files linked into agent directories + +## See also + +- [Creating Plugins](/guide/creating-plugins) — manifest authoring from scratch +- [Adapters reference](/reference/adapters) — built-in adapter output formats +- [Linting](/guide/linting) — built-in lint rules diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md index 24f058b..dcc9c49 100644 --- a/docs/guide/introduction.md +++ b/docs/guide/introduction.md @@ -1,5 +1,6 @@ --- -description: Learn how AgentPlugins solves plugin fragmentation across AI agent frameworks +title: Introduction – AgentPlugins +description: Learn how AgentPlugins unifies AI agent plugins across Claude Code, Codex, OpenCode, and more with a single manifest file. --- # Introduction @@ -34,29 +35,41 @@ flowchart LR core --> a2["Codex CLI"] core --> a3["GitHub Copilot"] core --> a4["Gemini CLI"] - core --> a5["+ 3 more"] + core --> a5["+ more"] ``` ## Supported platforms -Seven harnesses are supported as first-class compile targets: +AgentPlugins targets four harnesses as primary compile targets with universal codegen — the same plugin behaviour across all four: -| Agent | Binary | Skill path | -| ------------------ | ---------- | --------------------------- | -| Claude Code | `claude` | `~/.claude/skills` | -| Codex CLI | `codex` | `~/.codex/skills` | -| GitHub Copilot CLI | `copilot` | `~/.copilot/skills` | -| Gemini CLI | `gemini` | `~/.gemini/skills` | -| Kimi | `kimi` | `~/.kimi/skills` | -| OpenCode | `opencode` | `~/.config/opencode/skills` | -| Pi Mono | `pi` | `~/.pi/extensions` | +| Agent | Binary | Skill path | +| ----------- | ---------- | --------------------------- | +| Claude Code | `claude` | `~/.claude/skills` | +| Codex CLI | `codex` | `~/.codex/skills` | +| OpenCode | `opencode` | `~/.config/opencode/skills` | +| Pi Mono | `pi` | `~/.pi/extensions` | + + +Three additional harnesses are tracked with decreasing capability coverage: + + +| Agent | Binary | Skill path | +| ------------------ | --------- | ------------------- | +| GitHub Copilot CLI | `copilot` | `~/.copilot/skills` | +| Gemini CLI | `gemini` | `~/.gemini/skills` | +| Kimi | `kimi` | `~/.kimi/skills` | See the [agent paths reference](/reference/agent-paths) for the full registry and the [adapters reference](/reference/adapters) for what each platform emits. +## Custom harnesses + +If you maintain an internal harness, you can add it as a custom compile target. Register a private adapter via the `plugins` field in `defineConfig` — see [Extending the Build Pipeline](/guide/extending) for the full guide. + ## Where to go next - [Install](/guide/installation) the CLI. - Walk through the [quick start](/guide/quick-start). - Learn the [manifest format](/guide/manifest). + diff --git a/docs/guide/manifest.md b/docs/guide/manifest.md index 24455c5..c3a32f3 100644 --- a/docs/guide/manifest.md +++ b/docs/guide/manifest.md @@ -193,6 +193,8 @@ agents: [ ::: warning EXPERIMENTAL `sidecar` is experimental. It is accepted by the schema and validated, but **no adapter currently starts or stops sidecar processes**. Do not rely on it for production plugins. + +**For a long-running background process today:** ship a stdio MCP server (consumed natively by Claude Code, Codex, and OpenCode via `mcpServers`) or declare a `setup` command that starts it. On Pi Mono (which has no MCP), ship a Pi extension via `nativeEntry.pimono` that starts the process using Pi's extension API. ::: ```typescript diff --git a/docs/guide/porting.md b/docs/guide/porting.md index e79d8c1..43682d3 100644 --- a/docs/guide/porting.md +++ b/docs/guide/porting.md @@ -1,14 +1,14 @@ --- title: Porting an Existing Plugin -description: Port an existing per-harness plugin to the AgentPlugins universal manifest with functional parity across Tier-1 harnesses. +description: Port an existing per-harness plugin to the AgentPlugins universal manifest with functional parity across all supported harnesses. --- # Porting an Existing Plugin This guide walks through porting an existing per-harness plugin to AgentPlugins so it delivers the **same functionality** everywhere — universal codegen first, per-harness escape hatch only when a capability has no universal primitive. -::: info Tier-1 parity is the bar -The four Tier-1 harnesses are **Claude Code, Codex, OpenCode, and Pi Mono**. A capability must work across all four at the *functionality* level — not necessarily with identical TUI chrome, but the same underlying behaviour. See the [Tier-1 Capability Matrix](/reference/compat-matrix) for the per-capability verdict. +::: info Parity is the bar +The four supported harnesses are **Claude Code, Codex, OpenCode, and Pi Mono**. A capability must work across all four at the *functionality* level — not necessarily with identical TUI chrome, but the same underlying behaviour. See the [Capability Matrix](/guide/capability-matrix) for the per-capability verdict. ::: --- @@ -18,11 +18,11 @@ The four Tier-1 harnesses are **Claude Code, Codex, OpenCode, and Pi Mono**. A c For each capability in your existing plugin, work through this tree: ``` -1. Does universal codegen cover it across all Tier-1? +1. Does universal codegen cover it across all four core harnesses? YES → declare it in the manifest; the adapter handles the rest NO → continue ↓ -2. Can all Tier-1 harnesses support it via a per-harness escape hatch? +2. Can all four harnesses support it via a per-harness escape hatch? YES → implement guided per-harness (see below); emit WARN on build NO → continue ↓ @@ -44,7 +44,7 @@ List every capability your plugin provides: | Custom command | `/reset` clears state | `commands[]` | | Overlay UI | Side panel in Pi TUI | Pi-specific | -For each row, check the [Tier-1 Capability Matrix](/reference/compat-matrix) to see whether it's universal-codegen, guided-per-harness, or unsupported. +For each row, check the [Capability Matrix](/guide/capability-matrix) to see whether it's universal-codegen, guided-per-harness, or unsupported. --- @@ -73,14 +73,14 @@ export default definePlugin({ { name: 'reset', description: 'Reset plugin state', command: './scripts/reset.sh' }, ], - // Tools via MCP — works on all Tier-1 + // Tools via MCP — works on all four mcpServers: { 'my-tools': { command: 'npx', args: ['my-tools-mcp-server'] }, }, }) ``` -Build and verify on each Tier-1: +Build and verify on each harness: ```bash agentplugins build @@ -118,7 +118,7 @@ hooks: { }, ``` -The WARN emitted on `agentplugins validate` for OpenCode points here. Record the gap in the [compat matrix](/reference/compat-matrix). +The WARN emitted on `agentplugins validate` for OpenCode points here. Record the gap in the [Capability Matrix](/guide/capability-matrix). **Per-harness fallback via `nativeEntry`:** @@ -142,6 +142,26 @@ You do not need to edit `~/.config/opencode/config.json`. The `.ts` file-drop is If you ship a `.mjs` source, `agentplugins` automatically links it under a `.ts` name (safety net) but emits a WARN. Rename the source to `.ts` to silence it. +### MCP on Pi {#mcp-on-pi} + +Pi Mono has no built-in MCP support ("No MCP" per the Pi README). When your manifest sets `mcpServers` and `pimono` is a target, `agentplugins validate` emits a WARNING pointing here. + +Two paths: + +**Option A — native `tools[]` (recommended for Pi):** Declare your tool shapes in `tools[]`. The Pi adapter emits `pi.registerTool()` calls natively. No MCP server needed on Pi. + +**Option B — MCP bridge via `nativeEntry.pimono`:** Write a Pi extension that spawns your MCP server as a child process and bridges it through Pi's `pi.tool()` API. Wire it with: + +```typescript +nativeEntry: { + pimono: './src/pi-mcp-bridge.ts', +} +``` + +The file is copied verbatim to `index.ts` in the Pi dist; codegen is skipped. The bridge has full access to Pi's extension API (`ExtensionAPI`) and can call your MCP server over stdio. + +Use Option A when your tools are already declared in `tools[]`. Use Option B when you have a shared MCP server that must run on all four harnesses and you want a single implementation. + --- ## Step 4 — TUI-only features (acceptable degradation) @@ -149,7 +169,7 @@ If you ship a `.mjs` source, `agentplugins` automatically links it under a `.ts` Overlays, side panels, and interactive widgets that use Pi's TUI system (`@earendil-works/pi-tui`) are Pi-only by nature. This is the **one allowed degradation** category: - Implement the TUI feature on Pi via `nativeEntry`. -- On other Tier-1: omit the widget; the underlying functionality (data, hooks, state) must still work. +- On other harnesses: omit the widget; the underlying functionality (data, hooks, state) must still work. - Record in compat matrix as "TUI fidelity — Pi only". --- @@ -183,7 +203,7 @@ Flags the user controls: `--yes` (skip the prompt, still denylist-gated), `--no- ## Step 6 — Verify parity -For each Tier-1 harness: +For each supported harness: ```bash # Build @@ -207,12 +227,13 @@ Confirm the **same observable behaviour** on all four: same hooks fire, same com - **Universal orchestration runtime** — don't build one. Subagent spawning = per-harness primitives + userland provider protocol. - **Mechanical ports** — don't translate existing config files 1:1. Rewrite on the manifest; let adapters generate the platform-native output. +- **Plugin composition / inheritance** — native composition (extending one plugin from another) is not a Tier-1 primitive on any supported harness. If you need to combine behavior from two plugins, express it per-harness via `nativeEntry` where the harness supports extension chaining, or simply declare the same hooks in both plugins and let the harness merge them. There is no universal `extends` or `compose` field in the manifest, and none is planned for v0.4.0. --- ## See also - [Ecosystem](/guide/ecosystem) — plugins already ported for Tier-1 parity -- [Tier-1 Capability Matrix](/reference/compat-matrix) — full capability table +- [Capability Matrix](/guide/capability-matrix) — full capability table - [Creating Plugins](/guide/creating-plugins) — authoring guide from scratch - [JSON Schema](/reference/schema) — manifest schema for editor autocomplete diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index e150046..c232b73 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -1,5 +1,6 @@ --- -description: Get a working AgentPlugin in five commands +title: Quick Start – AgentPlugins +description: Install AgentPlugins and create your first universal AI agent plugin in under 2 minutes. --- # Quick Start @@ -16,7 +17,7 @@ Verify `agentplugins` is on your `PATH`: ```bash agentplugins --version -# agentplugins 1.0.0 +# agentplugins 0.6.0 ``` ## 2. Add a plugin from GitHub @@ -72,7 +73,7 @@ agentplugins init ? Plugin name (kebab-case) › my-awesome-plugin ? Description › Does awesome things across every agent ? Template › - Use arrow-keys. Return to submit. -> minimal Bare manifest + SKILL.md +> minimal Bare manifest logger Logs every hook event security-guard preToolUse block-list formatter postToolUse auto-format @@ -80,8 +81,9 @@ agentplugins init ✓ Created my-awesome-plugin/ my-awesome-plugin/agentplugins.config.ts - my-awesome-plugin/SKILL.md - my-awesome-plugin/hooks/pre-tool-use.sh + my-awesome-plugin/package.json + my-awesome-plugin/tsconfig.json + my-awesome-plugin/.gitignore my-awesome-plugin/README.md ``` diff --git a/docs/guide/tools.md b/docs/guide/tools.md index ab2719c..5779df4 100644 --- a/docs/guide/tools.md +++ b/docs/guide/tools.md @@ -136,30 +136,32 @@ The manifest declares the **shape** of a tool. The implementation — what runs ## Per-platform behavior -`tools[]` is natively emitted by **OpenCode** and **Pi Mono** only. For all other platforms — including the Tier-1 harnesses Claude Code and Codex — **`mcpServers` is the recommended universal tool delivery mechanism**. +`tools[]` is natively emitted by **OpenCode** and **Pi Mono** only. For all other platforms — including Claude Code and Codex — **`mcpServers` is the recommended universal tool delivery mechanism**. | Platform | `tools[]` | `mcpServers` | Notes | |---|:---:|:---:|---| -| Claude Code | ⚠️ | ✅ | `tools[]` not emitted; use `mcpServers` — full Tier-1 tool parity | +| Claude Code | ⚠️ | ✅ | `tools[]` not emitted; use `mcpServers` | | Codex | ⚠️ | ✅ | Same as Claude Code | | OpenCode | ✅ | ✅ | First-class `tools[]` + `mcpServers` both supported | -| Pi Mono | ✅ | ✅ | First-class `tools[]` + `mcpServers` both supported | +| Pi Mono | ✅ | ⚠️ | Pi has no built-in MCP. Use first-class `tools[]` (emitted natively) or bridge via `nativeEntry.pimono`. See [MCP on Pi](/guide/porting#mcp-on-pi). | | Copilot | ⚠️ | ❌ | Neither emitted; Tier-2 only | | Gemini | ⚠️ | ❌ | Neither emitted; Tier-2 only | | Kimi | ⚠️ | ❌ | Neither emitted; Tier-2 only | > ⚠️ When `tools[]` is declared and the target platform does not natively emit it, `agentplugins validate` emits a **WARNING** (not an error) with a pointer to `mcpServers`. The build still succeeds. +> +> ⚠️ Pi Mono has no built-in MCP support. On Pi, the native `tools[]` emission is the recommended path. `agentplugins validate` emits a WARNING when `mcpServers` is set with `pimono` as a target. ### Recommended cross-harness pattern -For plugins targeting all Tier-1 harnesses, back tools with an MCP server: +For plugins targeting Claude, Codex, and OpenCode, back tools with an MCP server. For Pi Mono, declare `tools[]` directly (emitted natively) or use `nativeEntry.pimono` to bridge an MCP server through a Pi extension. ```typescript export default definePlugin({ name: 'my-tools', version: '1.0.0', - // Tier-1 universal tool path: all four harnesses consume MCP servers + // MCP path: consumed by Claude Code, Codex, OpenCode (not Pi Mono) mcpServers: { 'my-tools-server': { command: 'npx', @@ -184,10 +186,10 @@ export default definePlugin({ }) ``` -See the [Tier-1 Capability Matrix](/reference/compat-matrix) for full cross-harness tool support details. +See the [Capability Matrix](/guide/capability-matrix) for full cross-harness tool support details. ## Next steps -- [MCP Servers](/guide/mcp-servers) — recommended universal tool mechanism for all Tier-1 harnesses. +- [MCP Servers](/guide/mcp-servers) — recommended universal tool mechanism for all supported harnesses. - [Manifest reference](/guide/manifest) for the full `tools` schema. -- [Tier-1 Capability Matrix](/reference/compat-matrix) for cross-harness support details. +- [Capability Matrix](/guide/capability-matrix) for cross-harness support details. diff --git a/docs/index.md b/docs/index.md index 2e850e3..b828cd9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,11 +1,16 @@ --- +title: AgentPlugins – Universal Plugin Standard for AI Agents +description: Write AI agent plugins once, ship to Claude Code, Codex, OpenCode, Gemini, Copilot, Kimi, and Pi Mono. One manifest. Seven platforms. Zero lock-in. layout: home hero: name: "AgentPlugins" text: "Universal plugin standard for AI agents" tagline: "Write AI agent plugins once, ship to any harness." - description: "One manifest. Seven agent platforms. Universal store. Zero lock-in." + description: "One manifest. Tier-1: Claude Code, Codex, OpenCode, Pi Mono. Tier-2: Copilot, Gemini, Kimi. Zero lock-in." + image: + src: /img/logo-dark.png + alt: AgentPlugins actions: - theme: brand text: Get Started @@ -13,6 +18,9 @@ hero: - theme: alt text: GitHub link: https://github.com/sigilco/agentplugins + - theme: alt + text: Sponsor + link: https://buy.polar.sh/polar_cl_Mv1gdlG7bw3I70EC9IHtfeSHJj4PEKvA7JAUz23CFhj features: - title: "Manage any AI harness plugin" diff --git a/docs/package.json b/docs/package.json index c2d7f2a..67bec1a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,6 +9,7 @@ }, "devDependencies": { "@braintree/sanitize-url": "^7.1.2", + "@types/node": "catalog:", "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "dayjs": "^1.11.21", diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 0000000..eaabf64 Binary files /dev/null and b/docs/public/favicon.ico differ diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 0000000..edbabf2 --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1,4 @@ + + + A + diff --git a/docs/public/img/logo-dark.png b/docs/public/img/logo-dark.png new file mode 100644 index 0000000..7f61f39 Binary files /dev/null and b/docs/public/img/logo-dark.png differ diff --git a/docs/public/img/logo-light.png b/docs/public/img/logo-light.png new file mode 100644 index 0000000..2124803 Binary files /dev/null and b/docs/public/img/logo-light.png differ diff --git a/docs/public/og.png b/docs/public/og.png new file mode 100644 index 0000000..9b477f9 Binary files /dev/null and b/docs/public/og.png differ diff --git a/docs/public/robots.txt b/docs/public/robots.txt new file mode 100644 index 0000000..e55e71a --- /dev/null +++ b/docs/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://agentplugins.pages.dev/sitemap.xml diff --git a/docs/reference/adapters.md b/docs/reference/adapters.md index 3a5d5d9..bd93aa9 100644 --- a/docs/reference/adapters.md +++ b/docs/reference/adapters.md @@ -18,7 +18,7 @@ An adapter compiles the universal manifest into one target platform's native for Two families: **JSON-emitting** adapters (claude, codex, copilot, gemini, kimi) produce static manifest files the host reads at startup. **Code-emitting** adapters (opencode, pimono) produce real TypeScript modules the host imports and calls. -See the [Tier-1 Capability Matrix](/reference/compat-matrix) for full cross-harness details. +See the [Capability Matrix](/guide/capability-matrix) for full cross-harness details. ## JSON-emitting adapters @@ -191,12 +191,58 @@ exec bun "${PLUGIN_ROOT}/dist/handler.js" pre-tool-use "$@" This means inline handlers work everywhere — at the cost of a Bun startup on platforms that don't support them natively. For latency-sensitive hooks on Claude/Codex/Gemini/Kimi, prefer `command` handlers. -## Adding a new adapter +## Custom adapters -An adapter is any executable that implements the **JSON process ABI**: read a manifest (stdin or `--manifest `), compile platform-specific output, write files, and exit `0` (success) or non-zero (failure). This enables any-language adapters without SDK lock-in. See [`adapter.schema.json`](https://raw.githubusercontent.com/sigilco/agentplugins/main/spec/v1/adapter.schema.json) for the contract. +`TargetPlatform` is an open string type — any non-empty target id is valid. Register a custom adapter via the `plugins` field in `defineConfig` and add the target id to `targets`: + +```typescript +// agentplugins.config.ts +import { defineConfig } from '@agentplugins/core' +import { myHarnessAdapter } from './src/my-harness-adapter.js' + +export default defineConfig({ + manifest: { name: 'my-plugin', version: '1.0.0', description: '…' }, + plugins: [{ name: 'my-harness', adapter: myHarnessAdapter }], + targets: ['claude', 'my-harness'], +}) +``` + +### `PlatformAdapter` interface + +```typescript +interface PlatformAdapter { + readonly name: string // target id + readonly displayName: string // human label + readonly supportedHooks: UniversalHookName[] // used by lint + readonly supportedHandlers: HandlerType[] // used by lint + readonly manifestPath: string // primary output path (for install) + readonly manifestFormat: 'json' | 'toml' + + validate(plugin: PluginManifest): ValidationIssue[] + compile(plugin: PluginManifest, options?: CompileOptions): AdapterOutput +} + +interface AdapterOutput { + files: { path: string; content: string }[] + manifest: Record + warnings: string[] + issues: ValidationIssue[] + postInstall?: string[] // commands to run after install + nativeCopies?: NativeCopy[] // verbatim file passthrough +} +``` + +`validate()` returns `ValidationIssue[]` — severity `'error'` aborts the build; `'warning'` is printed and continues. + +`compile()` returns the full set of files to write into `dist//`. Paths are relative to that directory. + +For a working example, see `plugins/example-custom-adapter/` in the repository. + +See [Extending the Build Pipeline](/guide/extending) for the full guide including lint rules and middleware hooks. ## Next steps - [Manifest reference](/guide/manifest) — what every field means. - [Hooks](/guide/hooks) — the 19 universal events. +- [Extending the Build Pipeline](/guide/extending) — custom adapters and pipeline plugins. - [Agent paths](/reference/agent-paths) — where each adapter writes its output. diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 4402c87..c20b6a7 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -46,16 +46,22 @@ agentplugins add |---|---| | `owner/repo` | `agentplugins add user/my-plugin` | | Full GitHub URL | `agentplugins add https://github.com/user/my-plugin` | +| GitHub tree URL (monorepo subdir) | `agentplugins add https://github.com/org/repo/tree/main/plugins/my-plugin` | | `owner/repo@version` | `agentplugins add user/my-plugin@1.2.0` | | Local path | `agentplugins add ./my-plugin` | | `gist:` | `agentplugins add gist:abcdef123456` | +When a GitHub tree URL points to a subdirectory, only that subdirectory is used as the plugin root — the rest of the repository is cloned but ignored. This lets plugin collections live in a monorepo and be installed individually. + ### Examples ```bash # Latest release from GitHub agentplugins add user/my-plugin +# Plugin inside a monorepo +agentplugins add https://github.com/sigilco/agentplugins-roster/tree/main/plugins/roster + # Specific version agentplugins add user/my-plugin@1.2.0 diff --git a/docs/reference/compat-matrix.md b/docs/reference/compat-matrix.md deleted file mode 100644 index 0d13ad4..0000000 --- a/docs/reference/compat-matrix.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Tier-1 Capability Matrix -description: What each Tier-1 harness supports — universal codegen, guided per-harness, or unsupported. ---- - -# Tier-1 Capability Matrix - -**Tier-1 harnesses:** Claude Code · Codex · OpenCode · Pi Mono - -**Tier-2 harnesses (tracked, not blocking):** Copilot · Gemini · Kimi - -Legend: -- ✅ **Universal codegen** — adapter emits native output automatically from the manifest -- ⚠️ **Guided per-harness** — no native primitive; author follows a documented escape-hatch pattern; emits a WARN (not error) on portable manifests -- ❌ **Unsupported** — no viable path; recorded here, not blocking -- n/a — not applicable (mechanism differs but functionality is covered) - -## Capability table - -| Capability | Claude Code | Codex | OpenCode | Pi Mono | Notes | -|---|:---:|:---:|:---:|:---:|---| -| `skills` | ✅ | ✅ | ✅ | ✅ | Universal codegen | -| `hooks` (lifecycle) | ✅ | ✅ | ✅ | ✅ | Universal codegen | -| `commands` | ✅ | ✅ | ✅ | ✅ | Universal codegen | -| `mcpServers` | ✅ | ✅ | ✅ | ✅ | **Recommended universal tool path** | -| `agents[]` | ✅ | ✅ | ✅ | ✅ | Universal codegen | -| `subagentStart` | ✅ | ✅ | ⚠️ | ✅ | OpenCode: no native event; emits WARN; guided path: intercept via `preToolUse` for subagent tool | -| `subagentStop` | ✅ | ✅ | ⚠️ | ✅ | Same as above; Pi `stop`↔`subagentStop` collision fixed in v0.3.0 | -| `tools[]` (first-class) | ⚠️ | ⚠️ | ✅ | ✅ | WARN emitted; use `mcpServers` for Claude/Codex (Tier-1 universal tool path) | -| `stop` / `continueWith` | ⚠️ | ⚠️ | ⚠️ | ⚠️ | New primitive — v0.4.0; all-Tier-1 design | -| Native-entry passthrough | n/a (JSON) | n/a (JSON) | ⚠️ | ⚠️ | `nativeEntry` escape hatch — v0.4.0; OpenCode native modules must be `.ts` (file-drop path, no config.json edits needed) | -| Inline hook handlers | ✅ auto-wrap | ✅ auto-wrap | ✅ | ✅ | Codex/Kimi: auto-wrapped as Node.js command scripts — v0.4.0 | - -## Tier-2 footnotes - -| Capability | Copilot | Gemini | Kimi | -|---|:---:|:---:|:---:| -| `skills` | ✅ | ✅ | ✅ | -| `hooks` (lifecycle) | ⚠️ | ⚠️ | ❌ | -| `subagentStart` / `subagentStop` | ❌ | ❌ | ❌ | -| `tools[]` | ✅ | ✅ | ❌ | -| `mcpServers` | ❌ | ❌ | ❌ | - -Kimi supported hooks: `preToolUse`, `userPromptSubmit`, `sessionStart`, `notification`, `permissionRequest`. Inline handlers auto-wrapped as Node.js command scripts (v0.4.0). - -## Decision tree for authors - -``` -Does universal codegen cover this capability across all Tier-1? - YES → use it; adapter handles the rest - NO → is there a custom (escape-hatch) path on all Tier-1? - YES → follow the guided per-harness pattern (see "Rewriting for tier-1 parity" guide) - NO → is the gap TUI-grade fidelity only? - YES → acceptable degradation; note in this matrix - NO → open a primitive proposal (v0.4.0+ scope) -``` - ---- - -*This matrix is the living contract for the project. Update it as capabilities land or gaps are discovered. See the [PRD roadmap](/guide/introduction) for full context.* diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..53b3f70 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,37 @@ +--- +title: Reference +description: API reference for AgentPlugins — CLI commands, JSON schema, agent paths, and platform adapters. +--- + +# Reference + +Technical reference for the AgentPlugins toolchain. For guides, how-to articles, and concept explanations, see the [Guide](/guide/introduction). + +## CLI + +| Command | Description | +|---|---| +| [`add`](/reference/commands#add) | Install a plugin from GitHub or a local path | +| [`remove`](/reference/commands#remove) | Remove a plugin and unlink from all agents | +| [`list`](/reference/commands#list) | List installed plugins | +| [`update`](/reference/commands#update) | Update plugin(s) from source | +| [`info`](/reference/commands#info) | Show plugin metadata and symlink status | +| [`doctor`](/reference/commands#doctor) | Diagnose store, symlinks, and agent detection | +| [`init`](/reference/commands#init) | Scaffold a new plugin interactively | +| [`build`](/reference/commands#build) | Compile plugin for all target platforms | +| [`validate`](/reference/commands#validate) | Validate manifest against schema | +| [`lint`](/reference/commands#lint) | Static analysis for common issues | +| [`preview`](/reference/commands#preview) | Preview compiled output for a target | + +## Schema + +- [JSON Schema](/reference/schema) — manifest schema, TypeScript types, Ajv validator + +## Platform + +- [Agent Paths](/reference/agent-paths) — store layout, per-agent skill paths, symlink layout +- [Adapters](/reference/adapters) — what each platform adapter emits + +## Manifesto + +For platform compatibility and capability decisions, see the [Capability Matrix](/guide/capability-matrix) in the Guide. diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..e3bea12 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "./.vitepress/dist", + "rootDir": "." + }, + "include": [".vitepress/**/*.ts"], + "exclude": ["node_modules", ".vitepress/dist"] +} diff --git a/package.json b/package.json index 0397b3d..88f36e5 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,11 @@ ], "license": "Apache-2.0", "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "packageManager": "pnpm@11.0.0", "devDependencies": { - "@changesets/cli": "^2.31.0" + "@changesets/cli": "^2.31.0", + "vite": "^6.0.0" } } diff --git a/packages/adapter-claude/__tests__/agent-emission.test.ts b/packages/adapter-claude/__tests__/agent-emission.test.ts new file mode 100644 index 0000000..4715864 --- /dev/null +++ b/packages/adapter-claude/__tests__/agent-emission.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect } from 'vitest'; +import { createClaudeAdapter } from '../src/index.js'; +import type { PluginManifest } from '@agentplugins/core'; + +const adapter = createClaudeAdapter(); + +describe('Claude adapter — agent file emission', () => { + it('emits agents/.md when agents[] is present', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'explorer', description: 'Fast codebase recon', prompt: 'You are explorer.' }], + }; + const { files } = adapter.compile(manifest); + const agentFile = files.find((f) => f.path === 'agents/explorer.md'); + expect(agentFile).toBeDefined(); + expect(agentFile!.content).toContain('name: explorer'); + expect(agentFile!.content).toContain('You are explorer.'); + }); + + it('includes model: in frontmatter when agent.model is set', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'oracle', model: 'claude-opus-4-8', prompt: 'You are oracle.' }], + }; + const { files } = adapter.compile(manifest); + const agentFile = files.find((f) => f.path === 'agents/oracle.md'); + expect(agentFile).toBeDefined(); + expect(agentFile!.content).toContain('model: claude-opus-4-8'); + }); + + it('omits model: from frontmatter when agent.model is unset', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'explorer', prompt: 'You are explorer.' }], + }; + const { files } = adapter.compile(manifest); + const agentFile = files.find((f) => f.path === 'agents/explorer.md'); + expect(agentFile).toBeDefined(); + expect(agentFile!.content).not.toContain('model:'); + }); + + it('includes tools: when agent.tools is set', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'fixer', tools: ['Bash', 'Edit'], model: 'claude-sonnet-4-6' }], + }; + const { files } = adapter.compile(manifest); + const agentFile = files.find((f) => f.path === 'agents/fixer.md'); + expect(agentFile).toBeDefined(); + expect(agentFile!.content).toContain('tools: Bash, Edit'); + expect(agentFile!.content).toContain('model: claude-sonnet-4-6'); + }); + + it('emits no agent files when agents[] is absent', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + }; + const { files } = adapter.compile(manifest); + const agentFiles = files.filter((f) => f.path.startsWith('agents/')); + expect(agentFiles).toHaveLength(0); + }); +}); diff --git a/packages/adapter-claude/package.json b/packages/adapter-claude/package.json index 3a57c26..baa037e 100644 --- a/packages/adapter-claude/package.json +++ b/packages/adapter-claude/package.json @@ -3,27 +3,28 @@ "version": "0.5.0", "description": "AgentPlugins platform adapter for Claude Code", "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", - "dev": "tsup src/index.ts --format cjs,esm --dts --watch", - "typecheck": "tsc --noEmit" + "build": "tsdown src/index.ts --format cjs,esm --dts", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", + "typecheck": "tsc --noEmit", + "test": "vitest run --passWithNoTests" }, "dependencies": { "@agentplugins/core": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "tsup": "^8.0.0", - "typescript": "^5.5.0" + "@types/node": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "files": [ "dist", diff --git a/packages/adapter-claude/src/index.ts b/packages/adapter-claude/src/index.ts index c84b895..12fbbd9 100644 --- a/packages/adapter-claude/src/index.ts +++ b/packages/adapter-claude/src/index.ts @@ -379,6 +379,17 @@ class ClaudePlatformAdapter implements PlatformAdapter { } } + // ─── 4b. Generate agent files ─────────────────────────────────────────── + if (plugin.agents && plugin.agents.length > 0) { + for (const agent of plugin.agents) { + const agentContent = this.buildAgentFile(agent); + files.push({ + path: `agents/${agent.name}.md`, + content: agentContent, + }); + } + } + // ─── 5. Generate .mcp.json ────────────────────────────────────────────── if (plugin.mcpServers && Object.keys(plugin.mcpServers).length > 0) { const mcpConfig = this.buildMCPConfig(plugin); @@ -642,6 +653,21 @@ class ClaudePlatformAdapter implements PlatformAdapter { return frontmatterBlock + `\n# ${skill.name}\n\n${skill.description}\n`; } + private buildAgentFile(agent: { name: string; description?: string; prompt?: string; tools?: string[]; model?: string; fallbackModels?: string[] }): string { + const lines: string[] = ['---', `name: ${agent.name}`]; + if (agent.description) { + lines.push(`description: ${agent.description}`); + } + if (agent.tools && agent.tools.length > 0) { + lines.push(`tools: ${agent.tools.join(', ')}`); + } + if (agent.model) { + lines.push(`model: ${agent.model}`); + } + lines.push('---', '', agent.prompt ?? '', ''); + return lines.join('\n'); + } + /** * Build the .mcp.json configuration from universal MCP server definitions. */ diff --git a/packages/adapter-claude/vitest.config.ts b/packages/adapter-claude/vitest.config.ts new file mode 100644 index 0000000..5288c8d --- /dev/null +++ b/packages/adapter-claude/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['__tests__/**/*.test.ts'], + environment: 'node', + }, +}); diff --git a/packages/adapter-codex/package.json b/packages/adapter-codex/package.json index 48b3722..afbd805 100644 --- a/packages/adapter-codex/package.json +++ b/packages/adapter-codex/package.json @@ -2,18 +2,17 @@ "name": "@agentplugins/adapter-codex", "version": "0.5.0", "description": "AgentPlugins platform adapter for OpenAI Codex CLI", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.cjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", - "dev": "tsup src/index.ts --format cjs,esm --dts --watch", + "build": "tsdown src/index.ts --format cjs,esm --dts", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", "typecheck": "tsc --noEmit", "lint": "eslint src/**/*.ts", "test": "vitest run --passWithNoTests" @@ -22,10 +21,10 @@ "@agentplugins/core": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "tsup": "^8.0.0", - "typescript": "^5.3.0", - "vitest": "^1.0.0" + "@types/node": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "peerDependencies": { "@agentplugins/core": "^0.5.0" diff --git a/packages/adapter-copilot/package.json b/packages/adapter-copilot/package.json index 1e2dd35..e6d735a 100644 --- a/packages/adapter-copilot/package.json +++ b/packages/adapter-copilot/package.json @@ -2,13 +2,12 @@ "name": "@agentplugins/adapter-copilot", "version": "0.5.0", "description": "GitHub Copilot CLI platform adapter for AgentPlugins — compiles AgentPlugins plugins into Copilot-compatible manifests, hooks, skills, and MCP configuration", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.cjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "files": [ @@ -19,7 +18,7 @@ "access": "public" }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", + "build": "tsdown src/index.ts --format cjs,esm --dts", "typecheck": "tsc --noEmit", "lint": "eslint src/**/*.ts", "test": "vitest run --passWithNoTests" @@ -28,9 +27,9 @@ "@agentplugins/core": "workspace:*" }, "devDependencies": { - "tsup": "^8.0.0", - "typescript": "^5.3.0", - "vitest": "^1.0.0" + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "engines": { "node": ">=18.0.0" diff --git a/packages/adapter-copilot/src/index.ts b/packages/adapter-copilot/src/index.ts index f255d82..6b1b497 100644 --- a/packages/adapter-copilot/src/index.ts +++ b/packages/adapter-copilot/src/index.ts @@ -325,20 +325,14 @@ function validateHandler( switch (handler.type as string) { case "command": { - const cmd = handler.config as { + // Support both copilot-specific format (handler.config.script/shell) and + // universal contract format (handler.command directly on the handler object). + const cmd = (handler.config ?? handler) as { shell?: string; script?: string; command?: string; }; - if (!cmd.shell) { - issues.push( - issue( - Severity.ERROR, - `Command handler must specify "shell" (e.g. "bash" or "powershell").`, - `${handlerPath}.config.shell` - ) - ); - } else if (!["bash", "powershell"].includes(cmd.shell)) { + if (cmd.shell && !["bash", "powershell"].includes(cmd.shell)) { issues.push( issue( Severity.WARNING, @@ -571,8 +565,8 @@ function compileHookEntry( // Resolve the handler path / URL / template switch (handler.type as string) { case "command": { - const cfg = handler.config as { - shell: string; + const cfg = (handler.config ?? handler) as { + shell?: string; script?: string; command?: string; }; @@ -853,11 +847,12 @@ class CopilotAdapter implements PlatformAdapter { const entry = compileHookEntry(uHook, hookDef); if ((handler.type as string) === "command") { - const cfg = handler.config as { - shell: string; + const rawCfg = (handler.config ?? handler) as { + shell?: string; script?: string; command?: string; }; + const cfg = { shell: "bash", ...rawCfg }; // Generate a wrapper script for this hook const scriptName = `hook-${hookName}.${ diff --git a/packages/adapter-gemini/package.json b/packages/adapter-gemini/package.json index ba066a0..ff44139 100644 --- a/packages/adapter-gemini/package.json +++ b/packages/adapter-gemini/package.json @@ -3,26 +3,25 @@ "version": "0.5.0", "description": "AgentPlugins platform adapter for Google Gemini CLI extensions", "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", - "dev": "tsup src/index.ts --format cjs,esm --dts --watch", + "build": "tsdown src/index.ts --format cjs,esm --dts", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", "clean": "rm -rf dist" }, "dependencies": { "@agentplugins/core": "workspace:*" }, "devDependencies": { - "tsup": "^8.0.0", - "typescript": "^5.3.0" + "tsdown": "catalog:", + "typescript": "catalog:" }, "files": [ "dist", diff --git a/packages/adapter-gemini/src/index.ts b/packages/adapter-gemini/src/index.ts index 7384bf1..84f45f0 100644 --- a/packages/adapter-gemini/src/index.ts +++ b/packages/adapter-gemini/src/index.ts @@ -222,7 +222,7 @@ function validateGeminiManifest(plugin: PluginManifest): ValidationIssue[] { * stdout → JSON result (optional) * exit → 0 (allow), 2 (block), other (warning) */ -function wrapInlineHandler( +function _wrapInlineHandler( script: string, hookName: string, geminiEvent: string @@ -275,7 +275,7 @@ process.stdin.on("end", () => { * Wrap a file-based handler so it conforms to the Gemini protocol. * Returns a *new* inline script that proxies to the file. */ -function wrapFileHandler(filePath: string): string { +function _wrapFileHandler(filePath: string): string { return `#!/usr/bin/env node // Auto-generated Gemini file-handler proxy const { spawn } = require("child_process"); @@ -419,11 +419,10 @@ export class GeminiAdapter implements PlatformAdapter { ? ((handler as ReferenceHookHandler).source ?? (handler as ReferenceHookHandler).reference) : undefined; - let scriptContent: string; if (isReference && script) { - scriptContent = wrapFileHandler(script); + _wrapFileHandler(script); } else { - scriptContent = wrapInlineHandler( + _wrapInlineHandler( script ?? "// no-op", hookName, geminiEvent diff --git a/packages/adapter-kimi/package.json b/packages/adapter-kimi/package.json index c638f94..fc9ffb3 100644 --- a/packages/adapter-kimi/package.json +++ b/packages/adapter-kimi/package.json @@ -2,13 +2,12 @@ "name": "@agentplugins/adapter-kimi", "version": "0.5.0", "description": "AgentPlugins platform adapter for Kimi (Moonshot AI)", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.cjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "files": [ @@ -19,8 +18,8 @@ "access": "public" }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts --clean", - "dev": "tsup src/index.ts --format cjs,esm --dts --watch", + "build": "tsdown src/index.ts --format cjs,esm --dts --clean", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", "lint": "eslint src/**/*.ts", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests" @@ -29,9 +28,9 @@ "@agentplugins/core": "workspace:*" }, "devDependencies": { - "tsup": "^8.0.0", - "typescript": "^5.3.0", - "vitest": "^1.0.0" + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "peerDependencies": { "@agentplugins/core": "^0.5.0" diff --git a/packages/adapter-opencode/__tests__/agent-emission.test.ts b/packages/adapter-opencode/__tests__/agent-emission.test.ts new file mode 100644 index 0000000..1e47546 --- /dev/null +++ b/packages/adapter-opencode/__tests__/agent-emission.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect } from 'vitest'; +import { generateAgentFiles } from '../src/output-generators.js'; +import type { PluginManifest } from '@agentplugins/core'; + +describe('OpenCode adapter — agent file emission', () => { + it('emits agent/.md when agents[] is present', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'explorer', description: 'Fast recon', prompt: 'You are explorer.' }], + }; + const files = generateAgentFiles(manifest); + expect(files).toHaveLength(1); + expect(files[0].path).toBe('agent/explorer.md'); + expect(files[0].content).toContain('description: Fast recon'); + expect(files[0].content).toContain('You are explorer.'); + }); + + it('includes model: in frontmatter when agent.model is set', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'oracle', model: 'claude-opus-4-8', prompt: 'You are oracle.' }], + }; + const files = generateAgentFiles(manifest); + expect(files[0].content).toContain('model: claude-opus-4-8'); + }); + + it('omits model: from frontmatter when agent.model is unset', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + agents: [{ name: 'explorer', prompt: 'You are explorer.' }], + }; + const files = generateAgentFiles(manifest); + expect(files[0].content).not.toContain('model:'); + }); + + it('returns empty array when agents[] is absent', () => { + const manifest: PluginManifest = { + name: 'test-plugin', + version: '0.1.0', + description: 'test', + }; + const files = generateAgentFiles(manifest); + expect(files).toHaveLength(0); + }); +}); diff --git a/packages/adapter-opencode/__tests__/compile.integration.test.ts b/packages/adapter-opencode/__tests__/compile.integration.test.ts index 0e19fb3..c571703 100644 --- a/packages/adapter-opencode/__tests__/compile.integration.test.ts +++ b/packages/adapter-opencode/__tests__/compile.integration.test.ts @@ -357,6 +357,45 @@ describe("compile() integration", () => { }); }); + describe("mcpServers emission", () => { + it("emits mcp.servers in opencode.json when mcpServers defined", () => { + const manifest: PluginManifest = { + name: "mcp-plugin", + version: "1.0.0", + mcpServers: { + "my-server": { + command: "npx", + args: ["-y", "my-mcp-server"], + env: { MY_KEY: "value" }, + }, + }, + }; + + const output = adapter.compile(manifest); + + const manifestFile = output.files.find((f) => f.path === "opencode.json"); + expect(manifestFile).toBeDefined(); + const parsed = JSON.parse(manifestFile!.content); + expect(parsed.mcp).toBeDefined(); + expect(parsed.mcp.servers["my-server"].command).toBe("npx"); + expect(parsed.mcp.servers["my-server"].args).toEqual(["-y", "my-mcp-server"]); + expect(parsed.mcp.servers["my-server"].env).toEqual({ MY_KEY: "value" }); + }); + + it("omits mcp key when no mcpServers defined", () => { + const manifest: PluginManifest = { + name: "no-mcp-plugin", + version: "1.0.0", + }; + + const output = adapter.compile(manifest); + + const manifestFile = output.files.find((f) => f.path === "opencode.json"); + const parsed = JSON.parse(manifestFile!.content); + expect(parsed.mcp).toBeUndefined(); + }); + }); + describe("postInstall instructions", () => { it("includes postInstall commands", () => { const manifest: PluginManifest = { diff --git a/packages/adapter-opencode/package.json b/packages/adapter-opencode/package.json index 344a25a..b9bd06c 100644 --- a/packages/adapter-opencode/package.json +++ b/packages/adapter-opencode/package.json @@ -22,12 +22,12 @@ "dependencies": { "@agentplugins/core": "workspace:*", "@agentplugins/compile": "workspace:*", - "zod": "^3.22.0" + "zod": "catalog:" }, "devDependencies": { "tsup": "^8.0.0", - "typescript": "^5.3.0", - "vitest": "^1.0.0" + "typescript": "catalog:", + "vitest": "catalog:" }, "files": [ "dist", diff --git a/packages/adapter-opencode/src/factory.ts b/packages/adapter-opencode/src/factory.ts index 80befa5..0b9281c 100644 --- a/packages/adapter-opencode/src/factory.ts +++ b/packages/adapter-opencode/src/factory.ts @@ -23,15 +23,15 @@ import { type HookDefinition, } from "@agentplugins/core/adapter"; -import { createValidate } from "./validate"; +import { createValidate } from "./validate.js"; import { HOOK_MAPPING, EVENT_HOOKS, buildEventHookBlock, buildHookArgs, -} from "./hook-mapping"; -import { buildHandlerInvocation } from "./handler-invocation"; -import { generatePluginFile, generateManifest, generateCommandFiles, generateAgentFiles } from "./output-generators"; +} from "./hook-mapping.js"; +import { buildHandlerInvocation } from "./handler-invocation.js"; +import { generatePluginFile, generateManifest, generateCommandFiles, generateAgentFiles } from "./output-generators.js"; /** * The 8 hooks supported by OpenCode. diff --git a/packages/adapter-opencode/src/index.ts b/packages/adapter-opencode/src/index.ts index 8859692..bf377c3 100644 --- a/packages/adapter-opencode/src/index.ts +++ b/packages/adapter-opencode/src/index.ts @@ -5,11 +5,11 @@ */ // Re-export factory (main adapter creation) -export { createOpenCodeAdapter } from "./factory"; -export { default } from "./factory"; +export { createOpenCodeAdapter } from "./factory.js"; +export { default } from "./factory.js"; // Re-export Wave 2 module public APIs -export { HOOK_MAPPING, EVENT_TYPE_CONDITIONS, EVENT_HOOKS } from "./hook-mapping"; -export { buildHandlerInvocation } from "./handler-invocation"; -export { generatePluginFile, generateManifest } from "./output-generators"; -export { createValidate } from "./validate"; +export { HOOK_MAPPING, EVENT_TYPE_CONDITIONS, EVENT_HOOKS } from "./hook-mapping.js"; +export { buildHandlerInvocation } from "./handler-invocation.js"; +export { generatePluginFile, generateManifest } from "./output-generators.js"; +export { createValidate } from "./validate.js"; diff --git a/packages/adapter-opencode/src/output-generators.ts b/packages/adapter-opencode/src/output-generators.ts index a5fae3e..8fe9364 100644 --- a/packages/adapter-opencode/src/output-generators.ts +++ b/packages/adapter-opencode/src/output-generators.ts @@ -157,6 +157,24 @@ export function generateManifest( ): FileOutput { const configFileName = "opencode.json"; + const mcpServers = + manifest.mcpServers && Object.keys(manifest.mcpServers).length > 0 + ? { + mcp: { + servers: Object.fromEntries( + Object.entries(manifest.mcpServers).map(([name, cfg]) => [ + name, + { + command: cfg.command, + ...(cfg.args && cfg.args.length > 0 ? { args: cfg.args } : {}), + ...(cfg.env ? { env: cfg.env } : {}), + }, + ]) + ), + }, + } + : {}; + const opencodeConfig = { name: manifest.name, description: manifest.description ?? "", @@ -172,6 +190,7 @@ export function generateManifest( description: tool.description, parameters: tool.parameters, })) ?? [], + ...mcpServers, discovery: { paths: [".opencode/plugins/", "~/.config/opencode/plugins/"], }, @@ -212,6 +231,9 @@ export function generateAgentFiles(manifest: PluginManifest): FileOutput[] { if (agent.tools && agent.tools.length > 0) { lines.push(`tools: [${agent.tools.join(", ")}]`); } + if (agent.model) { + lines.push(`model: ${agent.model}`); + } lines.push("---", "", agent.prompt ?? "", ""); const content = lines.join("\n"); return { path: `agent/${agent.name}.md`, content }; diff --git a/packages/adapter-opencode/src/validate.ts b/packages/adapter-opencode/src/validate.ts index 4f31cd9..fd7a3fe 100644 --- a/packages/adapter-opencode/src/validate.ts +++ b/packages/adapter-opencode/src/validate.ts @@ -12,10 +12,6 @@ import { type PluginManifest, - type HookHandler, - type InlineHookHandler, - type CommandHookHandler, - type HttpHookHandler, Severity, } from "@agentplugins/core"; @@ -43,7 +39,7 @@ const SUPPORTED_HOOKS: readonly UniversalHookName[] = [ * guided per-harness escape-hatch. These emit a WARN (not an error) so that * portable manifests remain buildable; authors are pointed to the compat matrix. * - * See: docs/reference/compat-matrix.md — "subagentStart / subagentStop" + * See: docs/guide/capability-matrix.md — "subagentStart / subagentStop" */ const GUIDED_PERHARNESS_HOOKS: readonly UniversalHookName[] = [ "subagentStart", @@ -83,7 +79,7 @@ export function createValidate(): (plugin: PluginManifest) => ValidationIssue[] `Hook "${hookName}" has no native OpenCode event. ` + `OpenCode does not expose a child-session/subagent lifecycle. ` + `Use a per-harness nativeEntry or intercept via preToolUse/postToolUse for the subagent tool. ` + - `See docs/reference/compat-matrix.md for the guided per-harness path. ` + + `See docs/guide/capability-matrix.md for the guided per-harness path. ` + `This hook will be omitted from the OpenCode output.`, }); } else if (!SUPPORTED_HOOKS.includes(universalHook)) { diff --git a/packages/adapter-opencode/tsconfig.json b/packages/adapter-opencode/tsconfig.json index 2d881bb..faccec5 100644 --- a/packages/adapter-opencode/tsconfig.json +++ b/packages/adapter-opencode/tsconfig.json @@ -13,7 +13,8 @@ "declarationMap": true, "sourceMap": true, "moduleResolution": "bundler", - "resolveJsonModule": true + "resolveJsonModule": true, + "ignoreDeprecations": "6.0" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] diff --git a/packages/adapter-opencode/vitest.config.ts b/packages/adapter-opencode/vitest.config.ts index 715e157..5033d17 100644 --- a/packages/adapter-opencode/vitest.config.ts +++ b/packages/adapter-opencode/vitest.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ - include: ['__tests__/**/*.test.ts'], - environment: 'node', - globals: true, + test: { + include: ['__tests__/**/*.test.ts'], + environment: 'node', + globals: true, + }, }); diff --git a/packages/adapter-pimono/__tests__/validate.test.ts b/packages/adapter-pimono/__tests__/validate.test.ts new file mode 100644 index 0000000..4ef30d4 --- /dev/null +++ b/packages/adapter-pimono/__tests__/validate.test.ts @@ -0,0 +1,88 @@ +import { describe, it, expect } from "vitest"; +import type { PluginManifest } from "@agentplugins/core"; +import { Severity } from "@agentplugins/core"; +import { createPiMonoAdapter } from "../src/index"; + +const adapter = createPiMonoAdapter({ pluginRoot: "/tmp/test-plugin" }); + +describe("validatePlugin() — Pi Mono adapter", () => { + describe("mcpServers warning", () => { + it("emits WARNING when mcpServers is set", () => { + const manifest: PluginManifest = { + name: "test-plugin", + version: "1.0.0", + description: "A test plugin", + mcpServers: { + "my-server": { command: "npx", args: ["my-mcp-server"] }, + }, + }; + + const issues = adapter.validate(manifest); + expect(issues.filter((i) => i.severity === Severity.ERROR)).toHaveLength(0); + expect(issues).toContainEqual( + expect.objectContaining({ + severity: Severity.WARNING, + field: "mcpServers", + message: expect.stringContaining("Pi Mono has no built-in MCP support"), + }) + ); + }); + + it("WARNING message points to the escape-hatch docs", () => { + const manifest: PluginManifest = { + name: "test-plugin", + version: "1.0.0", + description: "A test plugin", + mcpServers: { server: { command: "node", args: ["server.js"] } }, + }; + + const issues = adapter.validate(manifest); + const warn = issues.find((i) => i.field === "mcpServers"); + expect(warn?.message).toContain("nativeEntry.pimono"); + expect(warn?.message).toContain("porting#mcp-on-pi"); + }); + + it("does not emit WARNING when mcpServers is empty", () => { + const manifest: PluginManifest = { + name: "test-plugin", + version: "1.0.0", + description: "A test plugin", + mcpServers: {}, + }; + + const issues = adapter.validate(manifest); + expect(issues.filter((i) => i.field === "mcpServers")).toHaveLength(0); + }); + + it("does not emit WARNING when mcpServers is absent", () => { + const manifest: PluginManifest = { + name: "test-plugin", + version: "1.0.0", + description: "A test plugin", + }; + + const issues = adapter.validate(manifest); + expect(issues.filter((i) => i.field === "mcpServers")).toHaveLength(0); + }); + + it("mcpServers WARNING coexists with other valid manifest fields", () => { + const manifest: PluginManifest = { + name: "test-plugin", + version: "1.0.0", + description: "A test plugin", + hooks: { + sessionStart: { + handler: { type: "inline", handler: async () => ({}) }, + }, + }, + mcpServers: { server: { command: "node", args: ["server.js"] } }, + }; + + const issues = adapter.validate(manifest); + expect(issues.filter((i) => i.severity === Severity.ERROR)).toHaveLength(0); + const warns = issues.filter((i) => i.severity === Severity.WARNING); + expect(warns).toHaveLength(1); + expect(warns[0].field).toBe("mcpServers"); + }); + }); +}); diff --git a/packages/adapter-pimono/package.json b/packages/adapter-pimono/package.json index 8fecdf3..6f0c2a7 100644 --- a/packages/adapter-pimono/package.json +++ b/packages/adapter-pimono/package.json @@ -3,30 +3,29 @@ "version": "0.5.0", "description": "Pi Mono platform adapter for AgentPlugins — generates TypeScript-native extensions for the Pi agent runtime", "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", - "dev": "tsup src/index.ts --format cjs,esm --dts --watch", + "build": "tsdown src/index.ts --format cjs,esm --dts", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", "clean": "rm -rf dist", "test": "vitest run --passWithNoTests", "test:run": "vitest run" }, "dependencies": { - "@agentplugins/core": "workspace:*", - "@agentplugins/compile": "workspace:*" + "@agentplugins/compile": "workspace:*", + "@agentplugins/core": "workspace:*" }, "devDependencies": { - "tsup": "^8.0.0", - "typescript": "^5.4.0", - "vitest": "^1.0.0" + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "files": [ "dist", diff --git a/packages/adapter-pimono/src/index.ts b/packages/adapter-pimono/src/index.ts index 280bea1..a2c952f 100644 --- a/packages/adapter-pimono/src/index.ts +++ b/packages/adapter-pimono/src/index.ts @@ -354,6 +354,19 @@ function validatePlugin(plugin: PluginManifest): ValidationIssue[] { } } + // ── mcpServers ── + if (plugin.mcpServers && Object.keys(plugin.mcpServers).length > 0) { + issues.push({ + severity: Severity.WARNING, + field: "mcpServers", + message: + `Pi Mono has no built-in MCP support — "mcpServers" will not be emitted for this target. ` + + `Use first-class "tools[]" (emitted natively via pi.registerTool()) or bridge an MCP server ` + + `through a Pi extension via "nativeEntry.pimono". ` + + `See docs/guide/porting#mcp-on-pi for the guided pattern.`, + }); + } + return issues; } diff --git a/packages/adapter-pimono/vitest.config.ts b/packages/adapter-pimono/vitest.config.ts index 715e157..5033d17 100644 --- a/packages/adapter-pimono/vitest.config.ts +++ b/packages/adapter-pimono/vitest.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ - include: ['__tests__/**/*.test.ts'], - environment: 'node', - globals: true, + test: { + include: ['__tests__/**/*.test.ts'], + environment: 'node', + globals: true, + }, }); diff --git a/packages/cli/package.json b/packages/cli/package.json index ff08407..30722e6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,6 +24,7 @@ "@agentplugins/contract": "workspace:*", "@agentplugins/core": "workspace:*", "@agentplugins/compile": "workspace:*", + "@agentplugins/pipeline": "workspace:*", "@agentplugins/ingest": "workspace:*", "@agentplugins/schema": "workspace:*", "@agentplugins/security": "workspace:*", @@ -34,15 +35,15 @@ "@agentplugins/adapter-kimi": "workspace:*", "@agentplugins/adapter-opencode": "workspace:*", "@agentplugins/adapter-pimono": "workspace:*", - "@clack/prompts": "^0.7.0", - "cac": "^6.7.14", - "chalk": "^5.3.0", - "jiti": "^1.21.6" + "@clack/prompts": "catalog:", + "@logtape/logtape": "catalog:", + "cleye": "catalog:", + "jiti": "catalog:" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.5.0", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "keywords": [ "agentplugins", @@ -57,4 +58,4 @@ "directory": "packages/cli" }, "homepage": "https://agentplugins.pages.dev/" -} +} \ No newline at end of file diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index b5d2466..76244b8 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -22,9 +22,9 @@ * agentplugins preview # Preview compile output without writing */ -import { cac } from 'cac'; -import chalk from 'chalk'; +import { cli, command } from 'cleye'; import pkg from '../package.json' with { type: 'json' }; +import { setupLogger, getCliLogger } from './logger.js'; import { build } from './commands/build.js'; import { validate } from './commands/validate.js'; import { init } from './commands/init.js'; @@ -41,230 +41,270 @@ import { importCommand } from './commands/import.js'; import { audit } from './commands/audit.js'; import { loadConfig } from './config.js'; -const cli = cac('agentplugins'); +await setupLogger(); +const logger = getCliLogger(); -// ─── Distribution Commands ─────────────────────────────────────────────────── +function formatError(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} -cli - .command('add ', 'Install a plugin from GitHub to the store + all agents') - .option('-y, --yes', 'Skip the setup trust prompt (still denylist-gated)') - .option('--no-setup', 'Do not run any setup script after install') - .action(async (source: string, options) => { - try { - await add({ source, yes: options.yes, noSetup: options.setup === false }); - } catch (err) { - console.error(chalk.red('Add failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); +cli({ + name: 'agentplugins', + version: pkg.version, + commands: [ + // ─── Distribution Commands ────────────────────────────────────────────── -cli - .command('setup ', "Run an installed plugin's setup script (re-runnable)") - .option('-f, --force', 'Re-prompt even if the setup command is unchanged/trusted') - .option('-y, --yes', 'Skip the trust prompt (still denylist-gated)') - .action(async (name: string, options) => { - try { - await setup({ name, force: options.force, yes: options.yes }); - } catch (err) { - console.error(chalk.red('Setup failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'add', + parameters: [''], + flags: { + yes: { type: Boolean, alias: 'y', description: 'Skip the setup trust prompt (still denylist-gated)' }, + noSetup: { type: Boolean, default: false, description: 'Do not run any setup script after install' }, + }, + help: { description: 'Install a plugin from GitHub to the store + all agents' }, + }, async ({ _, flags }) => { + try { + await add({ source: _.source, yes: flags.yes, noSetup: flags.noSetup }); + } catch (err) { + logger.error('Add failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('remove ', 'Remove a plugin from the store and unlink all symlinks') - .option('-f, --force', 'Skip confirmation', { default: false }) - .action(async (name: string, options) => { - try { - await remove({ name, force: options.force }); - } catch (err) { - console.error(chalk.red('Remove failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'setup', + parameters: [''], + flags: { + force: { type: Boolean, alias: 'f', description: 'Re-prompt even if the setup command is unchanged/trusted' }, + yes: { type: Boolean, alias: 'y', description: 'Skip the trust prompt (still denylist-gated)' }, + }, + help: { description: "Run an installed plugin's setup script (re-runnable)" }, + }, async ({ _, flags }) => { + try { + await setup({ name: _.name, force: flags.force, yes: flags.yes }); + } catch (err) { + logger.error('Setup failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('list', 'List installed plugins') - .option('--json', 'Output as JSON') - .action(async (options) => { - try { - await list({ json: options.json }); - } catch (err) { - console.error(chalk.red('List failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'remove', + parameters: [''], + flags: { + force: { type: Boolean, alias: 'f', default: false, description: 'Skip confirmation' }, + }, + help: { description: 'Remove a plugin from the store and unlink all symlinks' }, + }, async ({ _, flags }) => { + try { + await remove({ name: _.name, force: flags.force }); + } catch (err) { + logger.error('Remove failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('update [name]', 'Update plugin(s) from source') - .option('-a, --all', 'Update all installed plugins') - .action(async (name: string | undefined, options) => { - try { - await update({ name, all: options.all }); - } catch (err) { - console.error(chalk.red('Update failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'list', + flags: { + json: { type: Boolean, description: 'Output as JSON' }, + }, + help: { description: 'List installed plugins' }, + }, async ({ flags }) => { + try { + await list({ json: flags.json }); + } catch (err) { + logger.error('List failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('info ', 'Show detailed information about an installed plugin') - .option('--json', 'Output as JSON') - .action(async (name: string, options) => { - try { - await info({ name, json: options.json }); - } catch (err) { - console.error(chalk.red('Info failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'update', + parameters: ['[name]'], + flags: { + all: { type: Boolean, alias: 'a', description: 'Update all installed plugins' }, + }, + help: { description: 'Update plugin(s) from source' }, + }, async ({ _, flags }) => { + try { + await update({ name: _.name, all: flags.all }); + } catch (err) { + logger.error('Update failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('doctor', 'Run diagnostics on store, agents, and symlinks') - .option('--json', 'Output as JSON') - .action(async (options) => { - try { - await doctor({ json: options.json }); - } catch (err) { - console.error(chalk.red('Doctor failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'info', + parameters: [''], + flags: { + json: { type: Boolean, description: 'Output as JSON' }, + }, + help: { description: 'Show detailed information about an installed plugin' }, + }, async ({ _, flags }) => { + try { + await info({ name: _.name, json: flags.json }); + } catch (err) { + logger.error('Info failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('import ', 'Translate a community plugin (Claude Code, Codex, Skills.sh) into an AgentPlugins manifest') - .option('-o, --out ', 'Output manifest path (default: /agentplugins.imported.json)') - .option('--write', 'Install the generated manifest into the universal store', { default: false }) - .option('--no-vendor', 'Skip copying upstream files into .agentplugins-vendor/') - .option('-q, --quiet', 'Suppress warning output', { default: false }) - .action(async (format: string, source: string, options) => { - try { - await importCommand({ - format, - source, - out: options.out, - write: options.write, - vendor: options.vendor, - quiet: options.quiet, - }); - } catch (err) { - console.error(chalk.red('Import failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'doctor', + flags: { + json: { type: Boolean, description: 'Output as JSON' }, + }, + help: { description: 'Run diagnostics on store, agents, and symlinks' }, + }, async ({ flags }) => { + try { + await doctor({ json: flags.json }); + } catch (err) { + logger.error('Doctor failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('audit ', 'Audit a plugin source for installability and supply-chain risk without writing to the store') - .option('--json', 'Output the report as JSON', { default: false }) - .option('--no-scripts', 'Skip lifecycle script policy evaluation') - .action(async (source: string, options) => { - try { - const code = await audit({ source, json: options.json, scripts: options.scripts }); - process.exit(code); - } catch (err) { - console.error(chalk.red('Audit failed:'), err instanceof Error ? err.message : String(err)); - process.exit(2); - } - }); + command({ + name: 'import', + parameters: ['', ''], + flags: { + out: { type: String, alias: 'o', placeholder: '', description: 'Output manifest path (default: /agentplugins.imported.json)' }, + write: { type: Boolean, default: false, description: 'Install the generated manifest into the universal store' }, + noVendor: { type: Boolean, default: false, description: 'Skip copying upstream files into .agentplugins-vendor/' }, + quiet: { type: Boolean, alias: 'q', default: false, description: 'Suppress warning output' }, + }, + help: { description: 'Translate a community plugin (Claude Code, Codex, Skills.sh) into an AgentPlugins manifest' }, + }, async ({ _, flags }) => { + try { + await importCommand({ + format: _.format, + source: _.source, + out: flags.out, + write: flags.write, + vendor: !flags.noVendor, + quiet: flags.quiet, + }); + } catch (err) { + logger.error('Import failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -// ─── Codegen Commands ──────────────────────────────────────────────────────── + command({ + name: 'audit', + parameters: [''], + flags: { + json: { type: Boolean, default: false, description: 'Output the report as JSON' }, + noScripts: { type: Boolean, default: false, description: 'Skip lifecycle script policy evaluation' }, + }, + help: { description: 'Audit a plugin source for installability and supply-chain risk without writing to the store' }, + }, async ({ _, flags }) => { + try { + const code = await audit({ source: _.source, json: flags.json, scripts: !flags.noScripts }); + process.exit(code); + } catch (err) { + logger.error('Audit failed: {msg}', { msg: formatError(err) }); + process.exit(2); + } + }), -cli - .command('build', 'Build plugin for target platforms') - .option('-t, --target ', 'Comma-separated target platforms (claude,codex,copilot,gemini,kimi,opencode,pimono)') - .option('-o, --out-dir ', 'Output directory', { default: 'dist' }) - .option('--strict', 'Fail on warnings', { default: false }) - .option('--config ', 'Config file path', { default: 'agentplugins.config.ts' }) - .action(async (options) => { - try { - const config = await loadConfig(options.config); - const targets = options.target - ? options.target.split(',').map((t: string) => t.trim()) - : config.manifest.targets; + // ─── Codegen Commands ─────────────────────────────────────────────────── - await build({ - config, - targets, - outDir: options.outDir, - strict: options.strict, - }); - } catch (err) { - console.error(chalk.red('Build failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'build', + flags: { + target: { type: String, alias: 't', placeholder: '', description: 'Comma-separated target platforms (claude,codex,copilot,gemini,kimi,opencode,pimono)' }, + outDir: { type: String, alias: 'o', placeholder: '', default: 'dist', description: 'Output directory' }, + strict: { type: Boolean, default: false, description: 'Fail on warnings' }, + config: { type: String, placeholder: '', default: 'agentplugins.config.ts', description: 'Config file path' }, + }, + help: { description: 'Build plugin for target platforms' }, + }, async ({ flags }) => { + try { + const cfg = await loadConfig(flags.config); + const targets = flags.target ? flags.target.split(',').map((t: string) => t.trim()) : undefined; + await build({ config: cfg, targets, outDir: flags.outDir, strict: flags.strict }); + } catch (err) { + logger.error('Build failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('validate', 'Validate plugin configuration') - .option('--config ', 'Config file path', { default: 'agentplugins.config.ts' }) - .option('-t, --target ', 'Validate for specific targets only') - .action(async (options) => { - try { - const config = await loadConfig(options.config); - const targets = options.target - ? options.target.split(',').map((t: string) => t.trim()) - : undefined; + command({ + name: 'validate', + flags: { + config: { type: String, placeholder: '', default: 'agentplugins.config.ts', description: 'Config file path' }, + target: { type: String, alias: 't', placeholder: '', description: 'Validate for specific targets only' }, + }, + help: { description: 'Validate plugin configuration' }, + }, async ({ flags }) => { + try { + const cfg = await loadConfig(flags.config); + const targets = flags.target ? flags.target.split(',').map((t: string) => t.trim()) : undefined; + await validate({ config: cfg, targets }); + } catch (err) { + logger.error('Validation failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), - await validate({ config, targets }); - } catch (err) { - console.error(chalk.red('Validation failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'init', + parameters: ['[name]'], + flags: { + yes: { type: Boolean, alias: 'y', description: 'Skip prompts and use defaults' }, + template: { type: String, alias: 't', placeholder: '', description: 'Template: minimal | logger | security-guard | formatter' }, + target: { type: String, placeholder: '', description: 'Target platforms (comma-separated)' }, + }, + help: { description: 'Scaffold a new AgentPlugins plugin' }, + }, async ({ _, flags }) => { + try { + await init({ name: _.name, yes: flags.yes || false, template: flags.template, target: flags.target }); + } catch (err) { + logger.error('Init failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('init [name]', 'Scaffold a new AgentPlugins plugin') - .option('-y, --yes', 'Skip prompts and use defaults') - .option('-t, --template ', 'Template: minimal | logger | security-guard | formatter') - .option('--target ', 'Target platforms (comma-separated)') - .action(async (name: string | undefined, options) => { - try { - await init({ - name, - yes: options.yes || false, - template: options.template, - target: options.target, - }); - } catch (err) { - console.error(chalk.red('Init failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); + command({ + name: 'lint', + flags: { + config: { type: String, placeholder: '', default: 'agentplugins.config.ts', description: 'Config file path' }, + json: { type: Boolean, description: 'Output as JSON' }, + }, + help: { description: 'Static analysis of plugin manifest' }, + }, async ({ flags }) => { + try { + const cfg = await loadConfig(flags.config); + await lint({ config: cfg, json: flags.json || false }); + } catch (err) { + logger.error('Lint failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), -cli - .command('lint', 'Static analysis of plugin manifest') - .option('--config ', 'Config file path', { default: 'agentplugins.config.ts' }) - .option('--json', 'Output as JSON') - .action(async (options) => { - try { - const config = await loadConfig(options.config); - await lint({ config, json: options.json || false }); - } catch (err) { - console.error(chalk.red('Lint failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); - -cli - .command('preview', 'Preview compile output without writing to disk') - .option('--config ', 'Config file path', { default: 'agentplugins.config.ts' }) - .option('-t, --target ', 'Comma-separated target platforms') - .option('--diff', 'Show diff against existing dist/ output') - .action(async (options) => { - try { - const config = await loadConfig(options.config); - const targets = options.target - ? options.target.split(',').map((t: string) => t.trim()) - : undefined; - - await preview({ config, targets, diff: options.diff || false }); - } catch (err) { - console.error(chalk.red('Preview failed:'), err instanceof Error ? err.message : String(err)); - process.exit(1); - } - }); - -cli.help(); -cli.version(pkg.version); - -cli.parse(); + command({ + name: 'preview', + flags: { + config: { type: String, placeholder: '', default: 'agentplugins.config.ts', description: 'Config file path' }, + target: { type: String, alias: 't', placeholder: '', description: 'Comma-separated target platforms' }, + diff: { type: Boolean, description: 'Show diff against existing dist/ output' }, + }, + help: { description: 'Preview compile output without writing to disk' }, + }, async ({ flags }) => { + try { + const cfg = await loadConfig(flags.config); + const targets = flags.target ? flags.target.split(',').map((t: string) => t.trim()) : undefined; + await preview({ config: cfg, targets, diff: flags.diff || false }); + } catch (err) { + logger.error('Preview failed: {msg}', { msg: formatError(err) }); + process.exit(1); + } + }), + ], + help: { + description: 'Build AI agent plugins once, ship to any harness.', + }, +}); diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts index 6ca9968..a293dfb 100644 --- a/packages/cli/src/commands/add.ts +++ b/packages/cli/src/commands/add.ts @@ -5,24 +5,30 @@ * and symlinks it into every detected agent harness. */ -import chalk from 'chalk'; import { initStore, normalizeSource, extractRepoName, + parseSubdir, + parseBranch, cloneRepo, findManifestInDir, installPlugin, getDetectedAgents, getStorePath, + securityPlugin, } from '@agentplugins/core'; import { join } from 'node:path'; import { existsSync, rmSync } from 'node:fs'; +import { createJiti } from 'jiti'; import { compile } from './build.js'; import { runSetupFlow } from './setup.js'; -import { verifyIntegrity, evaluateManifestScripts } from '@agentplugins/security'; +import { createApp, createInstallCtx, AbortError } from '@agentplugins/pipeline'; +import { getCliLogger } from '../logger.js'; import type { PluginManifest } from '@agentplugins/core'; +const logger = getCliLogger(); + export interface AddOptions { source: string; yes?: boolean; @@ -30,12 +36,14 @@ export interface AddOptions { } export async function add(options: AddOptions): Promise { + const subdir = parseSubdir(options.source); + const branch = parseBranch(options.source); const source = normalizeSource(options.source); const repoName = extractRepoName(source); - console.log(chalk.bold('\n📥 AgentPlugins Add\n')); - console.log(chalk.gray(`Source: ${source}`)); - console.log(chalk.gray(`Store: ${getStorePath()}\n`)); + logger.info('\n📥 AgentPlugins Add\n'); + logger.info('Source: {source}', { source }); + logger.info('Store: {store}\n', { store: getStorePath() }); initStore(); @@ -43,29 +51,32 @@ export async function add(options: AddOptions): Promise { const tempDir = join(getStorePath(), `.tmp-${repoName}-${Date.now()}`); if (existsSync(tempDir)) rmSync(tempDir, { recursive: true, force: true }); - console.log(chalk.blue('Cloning repository...')); + logger.info('Cloning repository...'); let commit: string; try { - commit = cloneRepo(source, tempDir); + commit = cloneRepo(source, tempDir, branch); } catch (err) { const msg = err instanceof Error ? err.message : String(err); - console.error(chalk.red(`\nFailed to clone: ${msg}`)); + logger.error('\nFailed to clone: {msg}', { msg }); rmSync(tempDir, { recursive: true, force: true }); process.exit(1); } - console.log(chalk.gray(`Commit: ${commit}`)); + logger.info('Commit: {commit}', { commit }); + + // Resolve subdir — for monorepo tree URLs, look inside the subdirectory + const pluginDir = subdir ? join(tempDir, subdir) : tempDir; // Find manifest — try JSON/SKILL.md first - let manifestResult = findManifestInDir(tempDir); + let manifestResult = findManifestInDir(pluginDir); // Fallback: try TypeScript config via jiti if (!manifestResult) { - manifestResult = await tryTsConfig(tempDir); + manifestResult = await tryTsConfig(pluginDir); } if (!manifestResult) { - console.error(chalk.red('\nNo plugin manifest found in repository.')); - console.error(chalk.gray('Expected one of: agentplugins.config.ts, agentplugins.config.json, manifest.json, package.json, SKILL.md')); + logger.error('\nNo plugin manifest found in repository.'); + logger.error('Expected one of: agentplugins.config.ts, agentplugins.config.json, manifest.json, package.json, SKILL.md'); rmSync(tempDir, { recursive: true, force: true }); process.exit(1); } @@ -75,61 +86,60 @@ export async function add(options: AddOptions): Promise { const name = rawName.replace(/^@[^/]+\//, ''); const version = (manifestResult.manifest['version'] as string) || '0.0.0'; - console.log(chalk.cyan(`\nPlugin: ${name} v${version}`)); - console.log(chalk.gray(`Manifest: ${manifestResult.path} (${manifestResult.type})`)); + logger.info('\nPlugin: {name} v{version}', { name, version }); + logger.info('Manifest: {path} ({type})', { path: manifestResult.path, type: manifestResult.type }); - // B17: verify pinned integrity (opt-in — only if manifest has integrity field) - const integrity = manifestResult.manifest['integrity'] as string | undefined; - if (integrity && integrity.length > 0) { - const { match, reason } = verifyIntegrity(tempDir, integrity); - if (!match) { - console.error(chalk.red(`\nIntegrity check failed: ${reason}`)); + // Security: run pinned integrity check + script policy via pipeline + const installApp = createApp().use(securityPlugin); + const installCtx = createInstallCtx({ + pluginName: name, + installDir: tempDir, + manifest: manifestResult.manifest as unknown as PluginManifest, + meta: {}, + }); + try { + await installApp.runInstall(installCtx); + } catch (err) { + if (err instanceof AbortError) { + logger.error('\n{msg}', { msg: err.message }); rmSync(tempDir, { recursive: true, force: true }); process.exit(1); } - } - - // B18: evaluate lifecycle script policy - const scriptCheck = evaluateManifestScripts(manifestResult.manifest, name); - if (!scriptCheck.ok) { - for (const issue of scriptCheck.issues) { - const tag = issue.decision === 'deny' ? chalk.red('[error]') : chalk.yellow('[review]'); - console.error(` ${tag} ${issue.dependency} (${issue.phase}): ${issue.command}`); - for (const r of issue.reasons) console.error(chalk.gray(` ${r}`)); - } - console.error(chalk.red('\nRefusing to install: lifecycle script policy violation')); - rmSync(tempDir, { recursive: true, force: true }); - process.exit(1); + throw err; } // Detect agents const agents = getDetectedAgents(); if (agents.length === 0) { - console.log(chalk.yellow('\n⚠ No agent harnesses detected. Plugin will be stored but not symlinked.')); - console.log(chalk.gray('Install Claude, Codex, or another supported agent to enable symlinking.')); + logger.warn('\n⚠ No agent harnesses detected. Plugin will be stored but not symlinked.'); + logger.info('Install Claude, Codex, or another supported agent to enable symlinking.'); } else { - console.log(chalk.gray(`Detected ${agents.length} agent${agents.length > 1 ? 's' : ''}: ${agents.map((a) => a.displayName).join(', ')}`)); + logger.info('Detected {count} agent{plural}: {agents}', { + count: agents.length, + plural: agents.length > 1 ? 's' : '', + agents: agents.map((a) => a.displayName).join(', '), + }); } // Compile for harnesses that load compiled artifacts (opencode, pimono) const compilableAgents = agents.filter((a) => a.pluginPath); if (compilableAgents.length > 0) { const targets = compilableAgents.map((a) => a.name); - console.log(chalk.blue(`\nCompiling for ${targets.join(', ')}...`)); - const distDir = join(tempDir, '.agentplugins-dist'); + logger.info('\nCompiling for {targets}...', { targets: targets.join(', ') }); + const distDir = join(pluginDir, '.agentplugins-dist'); try { await compile({ manifest: manifestResult.manifest as unknown as PluginManifest, targets: targets as any, write: true, outDir: distDir, - pluginRoot: tempDir, + pluginRoot: pluginDir, silent: false, }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); - console.log(chalk.yellow(`\n⚠ Compilation failed: ${msg}`)); - console.log(chalk.gray('Plugin will be installed without compiled artifacts.')); + logger.warn('\n⚠ Compilation failed: {msg}', { msg }); + logger.info('Plugin will be installed without compiled artifacts.'); } } @@ -140,28 +150,29 @@ export async function add(options: AddOptions): Promise { commit, manifestPath: manifestResult.path, version, + ...(subdir ? { subdir } : {}), }); // Summary - console.log(chalk.green(`\n✅ Installed ${name} v${version}`)); - console.log(chalk.gray(` Store: ${getStorePath()}/${name}`)); + logger.info('\n✅ Installed {name} v{version}', { name, version }); + logger.info(' Store: {store}', { store: `${getStorePath()}/${name}` }); if (result.symlinks.length > 0) { - console.log(chalk.gray('\nInstalled to:')); + logger.info('\nInstalled to:'); for (const s of result.symlinks) { - console.log(chalk.gray(` ${s.agentDisplayName}: ${s.linkPath}`)); + logger.info(' {agent}: {path}', { agent: s.agentDisplayName, path: s.linkPath }); } } await runSetupFlow({ name, - pluginDir: tempDir, + pluginDir, manifest: manifestResult.manifest, yes: options.yes, noSetup: options.noSetup, }); - console.log(); + logger.info(''); } /** Try loading a TypeScript config via jiti */ @@ -171,12 +182,8 @@ async function tryTsConfig(dir: string): Promise<{ path: string; manifest: Recor const fullPath = join(dir, candidate); if (!existsSync(fullPath)) continue; try { - const jiti = (await import('jiti')).default as unknown as ( - filename: string, - opts?: Record - ) => { import: (id: string, opts?: Record) => Promise }; - const loader = jiti(fullPath, { interopDefault: true, esmResolve: true }); - const mod = await loader.import(fullPath, { default: true }); + const loader = createJiti(fullPath, { interopDefault: true }); + const mod = await loader.import(fullPath); const exported = (mod as Record)?.['default' as keyof typeof mod] ?? mod; const manifest = typeof exported === 'function' ? await (exported as () => Promise>)() @@ -190,4 +197,3 @@ async function tryTsConfig(dir: string): Promise<{ path: string; manifest: Recor } return null; } - diff --git a/packages/cli/src/commands/audit.ts b/packages/cli/src/commands/audit.ts index c27f15f..21588cb 100644 --- a/packages/cli/src/commands/audit.ts +++ b/packages/cli/src/commands/audit.ts @@ -22,7 +22,6 @@ import { resolve, join, basename } from 'node:path'; import { existsSync, statSync, readFileSync } from 'node:fs'; -import chalk from 'chalk'; import { isValidManifest } from '@agentplugins/schema'; import { hashDirectory, @@ -33,6 +32,9 @@ import { evaluateScriptPolicy, DEFAULT_POLICY, } from '@agentplugins/security'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface AuditOptions { source: string; @@ -107,10 +109,10 @@ export async function audit(options: AuditOptions): Promise { const resolved = resolveSource(source); if (resolved.kind === 'github-url') { - console.error(chalk.yellow('Note: GitHub URL auditing is best-effort in v0.3.0. Clone the repo locally for full checks.')); + logger.warn('Note: GitHub URL auditing is best-effort in v0.3.0. Clone the repo locally for full checks.'); } if (resolved.kind === 'npm-spec') { - console.error(chalk.yellow('Note: npm spec auditing in v0.3.0 only checks provenance + README. Clone the tarball locally for full checks.')); + logger.warn('Note: npm spec auditing in v0.3.0 only checks provenance + README. Clone the tarball locally for full checks.'); } const localPath = resolved.path; @@ -292,75 +294,83 @@ function readManifest(localPath: string): ManifestReadResult | null { } function printHuman(report: AuditReport): void { - const tag = report.summary.verdict === 'pass' ? chalk.green('PASS') : report.summary.verdict === 'warn' ? chalk.yellow('WARN') : chalk.red('FAIL'); - console.log(chalk.bold(`\n🔍 AgentPlugins Audit — ${tag}\n`)); - console.log(chalk.gray(`Source: ${report.source}`)); - console.log(chalk.gray(`Kind: ${report.resolved.kind}`)); - if (report.resolved.repo) console.log(chalk.gray(`Repo: ${report.resolved.repo}`)); - if (report.resolved.spec) console.log(chalk.gray(`Spec: ${report.resolved.spec}`)); - - console.log(chalk.bold('\n📋 Manifest')); - if (report.manifest?.name) console.log(` Name: ${report.manifest.name}`); - if (report.manifest?.version) console.log(` Version: ${report.manifest.version}`); - console.log(` Schema: ${report.schema.valid ? chalk.green('valid') : chalk.red('invalid')}`); - - console.log(chalk.bold('\n🔐 Integrity')); - console.log(` Actual: ${report.integrity.actual.slice(0, 20)}... (${report.integrity.files} files, ${report.integrity.bytes} bytes)`); + const tag = report.summary.verdict.toUpperCase(); + logger.info('\n🔍 AgentPlugins Audit — {tag}\n', { tag }); + logger.info('Source: {source}', { source: report.source }); + logger.info('Kind: {kind}', { kind: report.resolved.kind }); + if (report.resolved.repo) logger.info('Repo: {repo}', { repo: report.resolved.repo }); + if (report.resolved.spec) logger.info('Spec: {spec}', { spec: report.resolved.spec }); + + logger.info('\n📋 Manifest'); + if (report.manifest?.name) logger.info(' Name: {name}', { name: report.manifest.name as string }); + if (report.manifest?.version) logger.info(' Version: {version}', { version: report.manifest.version as string }); + logger.info(' Schema: {status}', { status: report.schema.valid ? 'valid' : 'invalid' }); + + logger.info('\n🔐 Integrity'); + logger.info(' Actual: {hash}... ({files} files, {bytes} bytes)', { + hash: report.integrity.actual.slice(0, 20), + files: report.integrity.files, + bytes: report.integrity.bytes, + }); if (report.integrity.expected) { - console.log(` Expected: ${report.integrity.expected.slice(0, 20)}...`); - console.log(` Match: ${report.integrity.matches ? chalk.green('yes') : chalk.red('NO')}`); + logger.info(' Expected: {hash}...', { hash: report.integrity.expected.slice(0, 20) }); + logger.info(' Match: {match}', { match: report.integrity.matches ? 'yes' : 'NO' }); } else { - console.log(chalk.gray(' No pinned integrity.')); + logger.info(' No pinned integrity.'); } if (report.scorecard) { - console.log(chalk.bold('\n📊 OpenSSF Scorecard')); + logger.info('\n📊 OpenSSF Scorecard'); if (report.scorecard.skipped) { - console.log(chalk.gray(` ${report.scorecard.note}`)); + logger.info(' {note}', { note: report.scorecard.note }); } else if (report.scorecard.score !== null) { - const color = report.scorecard.score >= 7 ? chalk.green : report.scorecard.score >= 5 ? chalk.yellow : chalk.red; - console.log(` Score: ${color(report.scorecard.score.toFixed(1) + ' / 10')}`); + logger.info(' Score: {score} / 10', { score: report.scorecard.score.toFixed(1) }); } } - console.log(chalk.bold('\n🛡 OSV')); + logger.info('\n🛡 OSV'); if (report.osv?.skipped) { - console.log(chalk.gray(` ${report.osv.note}`)); + logger.info(' {note}', { note: report.osv.note }); } else if (report.osv) { - console.log(` Findings: ${report.osv.findings.length}`); - if (report.osv.hasCriticalOrHigh) console.log(chalk.red(' ⚠ CRITICAL or HIGH findings present')); + logger.info(' Findings: {count}', { count: report.osv.findings.length }); + if (report.osv.hasCriticalOrHigh) logger.error(' ⚠ CRITICAL or HIGH findings present'); for (const f of report.osv.findings.slice(0, 5)) { - console.log(` - [${f.severity}] ${f.package}: ${f.advisory}`); + logger.info(' - [{severity}] {package}: {advisory}', { severity: f.severity, package: f.package, advisory: f.advisory }); } } if (report.provenance) { - console.log(chalk.bold('\n🔏 npm provenance')); + logger.info('\n🔏 npm provenance'); if (report.provenance.skipped) { - console.log(chalk.gray(` ${report.provenance.note}`)); + logger.info(' {note}', { note: report.provenance.note }); } else { - console.log(` Signed: ${report.provenance.signed === true ? chalk.green('yes') : report.provenance.signed === false ? chalk.red('NO') : chalk.gray('unknown')}`); + const signed = report.provenance.signed === true ? 'yes' : report.provenance.signed === false ? 'NO' : 'unknown'; + logger.info(' Signed: {signed}', { signed }); } } if (report.scripts) { - console.log(chalk.bold('\n📜 Lifecycle scripts')); - console.log(` Total: ${report.scripts.decisions.length}`); - console.log(` Denied: ${chalk.red(String(report.scripts.deniedCount))}`); - console.log(` Review: ${chalk.yellow(String(report.scripts.reviewCount))}`); + logger.info('\n📜 Lifecycle scripts'); + logger.info(' Total: {count}', { count: report.scripts.decisions.length }); + logger.info(' Denied: {count}', { count: report.scripts.deniedCount }); + logger.info(' Review: {count}', { count: report.scripts.reviewCount }); for (const d of report.scripts.decisions.slice(0, 5)) { - const color = d.decision === 'deny' ? chalk.red : d.decision === 'require-review' ? chalk.yellow : chalk.green; - console.log(` - [${color(d.decision)}] ${d.dependency} (${d.phase}): ${d.command.slice(0, 60)}`); + logger.info(' - [{decision}] {dependency} ({phase}): {command}', { + decision: d.decision, + dependency: d.dependency, + phase: d.phase, + command: d.command.slice(0, 60), + }); } } - console.log(chalk.bold(`\n📊 Summary`)); - console.log(` Errors: ${report.summary.errors}`); - console.log(` Warnings: ${report.summary.warnings}`); - console.log(` Notes: ${report.summary.notes}`); - console.log(` Verdict: ${tag}\n`); + logger.info('\n📊 Summary'); + logger.info(' Errors: {count}', { count: report.summary.errors }); + logger.info(' Warnings: {count}', { count: report.summary.warnings }); + logger.info(' Notes: {count}', { count: report.summary.notes }); + logger.info(' Verdict: {tag}\n', { tag }); } function printError(msg: string): void { - console.error(chalk.red(`\n✗ ${msg}\n`)); + logger.error('\n✗ {msg}\n', { msg }); } diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts index 52e1c5b..42a846b 100644 --- a/packages/cli/src/commands/build.ts +++ b/packages/cli/src/commands/build.ts @@ -6,7 +6,6 @@ import { resolve, join } from 'node:path'; import { mkdir, readFile, writeFile, rm } from 'node:fs/promises'; -import chalk from 'chalk'; import { validateUniversal, validateForPlatform, @@ -15,9 +14,71 @@ import { type PluginManifest, type CompileOptions as AdapterCompileOptions, } from '@agentplugins/core'; -import { sanitizeJoin, lint, type LintIssue } from '@agentplugins/compile'; +import { sanitizeJoin, lint, registerEmitter, type LintIssue } from '@agentplugins/compile'; +import { createApp, createBuildCtx, createTargetCtx, AbortError } from '@agentplugins/pipeline'; +import { getCliLogger } from '../logger.js'; +import type { App, Plugin } from '@agentplugins/pipeline'; import type { LoadedConfig } from '../config.js'; +const logger = getCliLogger(); + +// ─── Target resolution ──────────────────────────────────────────────────────── + +function resolveTargets( + cliTargets: string[] | undefined, + manifestTargets: string[] | undefined +): TargetPlatform[] { + return (cliTargets ?? manifestTargets ?? ALL_TARGETS) as TargetPlatform[]; +} + +// ─── Builtin adapter app ────────────────────────────────────────────────────── + +interface AdapterSpec { + platform: TargetPlatform; + pkg: string; + exportName: string; +} + +const BUILTIN_ADAPTER_SPECS: AdapterSpec[] = [ + { platform: 'claude', pkg: '@agentplugins/adapter-claude', exportName: 'createClaudeAdapter' }, + { platform: 'codex', pkg: '@agentplugins/adapter-codex', exportName: 'createCodexAdapter' }, + { platform: 'copilot', pkg: '@agentplugins/adapter-copilot', exportName: 'createCopilotAdapter' }, + { platform: 'gemini', pkg: '@agentplugins/adapter-gemini', exportName: 'createGeminiAdapter' }, + { platform: 'kimi', pkg: '@agentplugins/adapter-kimi', exportName: 'createKimiAdapter' }, + { platform: 'opencode', pkg: '@agentplugins/adapter-opencode', exportName: 'createOpenCodeAdapter' }, + { platform: 'pimono', pkg: '@agentplugins/adapter-pimono', exportName: 'createPiMonoAdapter' }, +]; + +async function buildApp(userPlugins: Plugin[] = []): Promise { + const app = createApp(); + + // Register builtin adapters first (lower precedence) + for (const { platform, pkg, exportName } of BUILTIN_ADAPTER_SPECS) { + try { + // @ts-ignore — loaded dynamically at runtime + const mod = await import(pkg); + const factory = mod[exportName] as (() => ReturnType) | undefined; + if (typeof factory === 'function') { + app.use({ name: platform, adapter: factory() }); + } + } catch { + // Adapter package not installed — skip silently + } + } + + // Register user plugins after builtins so they can override any builtin + for (const plugin of userPlugins) { + app.use(plugin); + } + + // Register custom code emitters into the global codegen registry + for (const [, emitter] of app.emitters) { + registerEmitter(emitter as Parameters[0]); + } + + return app; +} + // ─── Compile (extracted for reuse by preview) ────────────────────────────── export interface CompileFile { @@ -42,36 +103,10 @@ export interface CompileOptions { silent?: boolean; /** Plugin root directory — required to resolve nativeEntry source paths. */ pluginRoot?: string; -} - -type AdapterFactory = () => { compile: (manifest: any, options?: AdapterCompileOptions) => any }; - -async function getAdapterFactory(target: TargetPlatform): Promise { - switch (target) { - case 'claude': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-claude')).createClaudeAdapter; - case 'codex': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-codex')).createCodexAdapter; - case 'copilot': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-copilot')).createCopilotAdapter; - case 'gemini': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-gemini')).createGeminiAdapter; - case 'kimi': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-kimi')).createKimiAdapter; - case 'opencode': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-opencode')).createOpenCodeAdapter; - case 'pimono': - // @ts-ignore - adapter loaded dynamically at runtime - return (await import('@agentplugins/adapter-pimono')).createPiMonoAdapter; - default: - throw new Error(`Unknown target: ${target}`); - } + /** User-provided pipeline plugins from defineConfig. */ + plugins?: Plugin[]; + /** Pre-built app; skips buildApp() when provided (used by build() to avoid double-init). */ + _app?: App; } /** @@ -80,46 +115,67 @@ async function getAdapterFactory(target: TargetPlatform): Promise { - const { manifest, write = false, outDir, silent = false, pluginRoot } = options; - const targetList = (options.targets || manifest.targets || ALL_TARGETS) as TargetPlatform[]; + const { write = false, outDir, silent = false, pluginRoot, plugins = [] } = options; + const app = options._app ?? await buildApp(plugins); + + // Run preValidate + transformIR lifecycle hooks; use possibly mutated manifest + const buildCtx = createBuildCtx({ + manifest: options.manifest, + targets: (resolveTargets(options.targets, options.manifest.targets) as string[]), + outDir, + pluginRoot, + }); + await app.runBuild(buildCtx); + const manifest = buildCtx.manifest; + + const targetList = resolveTargets(options.targets, manifest.targets); const results: CompileResult[] = []; for (const target of targetList) { - let factory: AdapterFactory; - try { - factory = await getAdapterFactory(target); - } catch { + const adapter = app.adapters.get(target); + if (!adapter) { results.push({ target, files: [], warnings: [], skipped: true }); continue; } - if (!silent) console.log(chalk.blue(`\n📦 Building for ${target}...`)); + if (!silent) logger.info('\n📦 Building for {target}...', { target }); const platformIssues = validateForPlatform(manifest, target); const platformErrors = platformIssues.filter(i => i.severity === 'error'); if (platformErrors.length > 0) { const msg = `${platformErrors.length} validation error${platformErrors.length > 1 ? 's' : ''}`; - if (!silent) console.log(chalk.red(` ✗ Build failed for ${target} (${msg})`)); + if (!silent) logger.error(' ✗ Build failed for {target} ({msg})', { target, msg }); results.push({ target, files: [], warnings: [], skipped: true, error: msg }); continue; } try { - const adapter = factory(); - const output = adapter.compile(manifest, { pluginRoot }); + const output = adapter.compile(manifest, { pluginRoot } as AdapterCompileOptions); + + // Run postEmit hooks; plugins can append/rewrite files + const targetCtx = createTargetCtx({ manifest, target, pluginRoot }); + for (const file of output.files) targetCtx.addFile(file); + for (const w of output.warnings) targetCtx.addWarning(w); + if (output.nativeCopies) { + for (const copy of output.nativeCopies) targetCtx.addNativeCopy(copy); + } + if (output.postInstall) { + for (const step of output.postInstall) targetCtx.addPostInstall(step); + } + await app.runTarget(targetCtx); if (write && outDir) { const targetDir = join(resolve(outDir), target); await rm(targetDir, { recursive: true, force: true }); await mkdir(targetDir, { recursive: true }); - for (const file of output.files) { + for (const file of targetCtx.files) { const filePath = join(targetDir, file.path); await mkdir(resolve(filePath, '..'), { recursive: true }); await writeFile(filePath, file.content, 'utf-8'); } - if (output.nativeCopies && pluginRoot) { + if (targetCtx.nativeCopies.length > 0 && pluginRoot) { const resolvedRoot = resolve(pluginRoot); - for (const copy of output.nativeCopies) { + for (const copy of targetCtx.nativeCopies) { const srcPath = sanitizeJoin(resolvedRoot, copy.from); const dstPath = sanitizeJoin(targetDir, copy.to); await mkdir(resolve(dstPath, '..'), { recursive: true }); @@ -130,25 +186,29 @@ export async function compile(options: CompileOptions): Promise } if (!silent) { - console.log(chalk.green(` ✓ Built ${output.files.length} file${output.files.length > 1 ? 's' : ''}`)); - if (output.warnings.length > 0) { - for (const w of output.warnings) console.log(chalk.yellow(` ⚠ ${w}`)); + logger.info(' ✓ Built {count} file{plural}', { + count: targetCtx.files.length, + plural: targetCtx.files.length > 1 ? 's' : '', + }); + if (targetCtx.warnings.length > 0) { + for (const w of targetCtx.warnings) logger.warn(' ⚠ {warning}', { warning: w }); } - if (output.postInstall) { - console.log(chalk.cyan(` ⓘ ${output.postInstall.join('\n ⓘ ')}`)); + if (targetCtx.postInstall.length > 0) { + logger.info(' ⓘ {steps}', { steps: targetCtx.postInstall.join('\n ⓘ ') }); } } results.push({ target, - files: output.files, - warnings: output.warnings || [], - postInstall: output.postInstall, + files: targetCtx.files, + warnings: targetCtx.warnings, + postInstall: targetCtx.postInstall.length > 0 ? targetCtx.postInstall : undefined, skipped: false, }); } catch (err) { + if (err instanceof AbortError) throw err; const msg = err instanceof Error ? err.message : String(err); - if (!silent) console.log(chalk.red(` ✗ Build failed for ${target}: ${msg}`)); + if (!silent) logger.error(' ✗ Build failed for {target}: {msg}', { target, msg }); results.push({ target, files: [], warnings: [], skipped: true, error: msg }); } } @@ -168,39 +228,50 @@ export interface BuildOptions { export async function build(options: BuildOptions): Promise { const { config, outDir } = options; const manifest = config.manifest; - const targetList = (options.targets || manifest.targets || ALL_TARGETS) as TargetPlatform[]; + // CLI --target flag > defineConfig targets > manifest.targets > ALL_TARGETS + const targetList = resolveTargets( + options.targets as TargetPlatform[] | undefined + ?? config.configTargets as TargetPlatform[] | undefined, + manifest.targets + ); + + logger.info('\n🌉 AgentPlugins Build\n'); + logger.info('Plugin: {name} v{version}', { name: manifest.name, version: manifest.version }); + logger.info('Targets: {targets}', { targets: targetList.join(', ') }); + logger.info('Output: {out}\n', { out: resolve(outDir) }); - console.log(chalk.bold('\n🌉 AgentPlugins Build\n')); - console.log(chalk.gray(`Plugin: ${manifest.name} v${manifest.version}`)); - console.log(chalk.gray(`Targets: ${targetList.join(', ')}`)); - console.log(chalk.gray(`Output: ${resolve(outDir)}\n`)); + // Build the pipeline app once — reused for validation, lint, and compile + const app = await buildApp(config.plugins ?? []); + const knownTargets = [...ALL_TARGETS, ...app.adapters.keys()]; - // Universal validation - console.log(chalk.blue('🔍 Running universal validation...')); - const universalIssues = validateUniversal(manifest); + // Universal validation — custom adapter targets are not spuriously warned + logger.info('🔍 Running universal validation...'); + const universalIssues = validateUniversal(manifest, { knownTargets }); printIssues(universalIssues); const hasErrors = universalIssues.some(i => i.severity === 'error'); if (hasErrors) { throw new Error('Universal validation failed. Fix errors before building.'); } - // Lint - console.log(chalk.blue('🔍 Running lint...')); + // Lint — includes any lint rules from defineConfig plugins + logger.info('🔍 Running lint...'); const inlineSources = await collectInlineSources(manifest, config.root); - const lintIssues = lint({ manifest, inlineHandlerSource: inlineSources }); + const lintIssues = lint({ manifest, inlineHandlerSource: inlineSources, extraRules: [...app.lintRules] }); printLintIssues(lintIssues); const lintErrors = lintIssues.filter(i => i.severity === 'error'); if (options.strict && lintErrors.length > 0) { throw new Error(`Strict mode: ${lintErrors.length} lint error(s) found.`); } - // Compile + write + // Compile + write (pass pre-built app to avoid rebuilding) const results = await compile({ manifest, targets: targetList, write: true, outDir, pluginRoot: config.root, + plugins: config.plugins, + _app: app, }); // Strict mode: fail on warnings @@ -212,36 +283,46 @@ export async function build(options: BuildOptions): Promise { } // Summary - console.log(chalk.bold('\n✅ Build complete!\n')); - console.log(chalk.gray('Install your plugins:')); + logger.info('\n✅ Build complete!\n'); + logger.info('Install your plugins:'); for (const r of results) { if (r.skipped) continue; const cmd = getInstallCommand(r.target, manifest.name); - console.log(chalk.gray(` ${r.target}: ${cmd}`)); + logger.info(' {target}: {cmd}', { target: r.target, cmd }); } - console.log(); + logger.info(''); } function printLintIssues(issues: LintIssue[]): void { for (const issue of issues) { - const color = issue.severity === 'error' ? chalk.red : chalk.yellow; - const icon = issue.severity === 'error' ? '✗' : '⚠'; - const field = issue.field ? chalk.gray(`[${issue.field}] `) : ''; - console.log(color(` ${icon} ${field}${issue.message}`)); + const field = issue.field ? ` [${issue.field}]` : ''; + const rule = ` (${issue.rule})`; + const message = ` ${issue.severity === 'error' ? '✗' : '⚠'} ${issue.message}${field}${rule}`; + if (issue.severity === 'error') { + logger.error(message); + } else { + logger.warn(message); + } if (issue.suggestion) { - console.log(chalk.cyan(` → ${issue.suggestion}`)); + logger.info(' → {suggestion}', { suggestion: issue.suggestion }); } } } function printIssues(issues: Array<{ severity: string; field?: string; message: string; suggestion?: string }>): void { for (const issue of issues) { - const color = issue.severity === 'error' ? chalk.red : issue.severity === 'warning' ? chalk.yellow : chalk.gray; const icon = issue.severity === 'error' ? '✗' : issue.severity === 'warning' ? '⚠' : 'ℹ'; - const field = issue.field ? chalk.gray(`[${issue.field}] `) : ''; - console.log(color(` ${icon} ${field}${issue.message}`)); + const field = issue.field ? `[${issue.field}] ` : ''; + const message = ` ${icon} ${field}${issue.message}`; + if (issue.severity === 'error') { + logger.error(message); + } else if (issue.severity === 'warning') { + logger.warn(message); + } else { + logger.info(message); + } if (issue.suggestion) { - console.log(chalk.cyan(` → ${issue.suggestion}`)); + logger.info(' → {suggestion}', { suggestion: issue.suggestion }); } } } diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index 13686cc..4dfcd59 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/packages/cli/src/commands/doctor.ts @@ -4,8 +4,10 @@ * Runs diagnostics on the store, detected agents, and symlinks. */ -import chalk from 'chalk'; import { runDoctor } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface DoctorOptions { json?: boolean; @@ -19,56 +21,73 @@ export async function doctor(options: DoctorOptions): Promise { return; } - console.log(chalk.bold('\n🩺 AgentPlugins Doctor\n')); + logger.info('\n🩺 AgentPlugins Doctor\n'); // Store - console.log(chalk.cyan('Store')); - console.log(chalk.gray(` Path: ${result.storePath}`)); - console.log(chalk.gray(` Exists: ${result.storeExists ? chalk.green('yes') : chalk.red('no')}`)); - console.log(chalk.gray(` Skills: ${result.skillsCompatPath}`)); - console.log(chalk.gray(` Skills exists: ${result.skillsCompatExists ? chalk.green('yes') : chalk.red('no')}`)); + logger.info('Store'); + logger.info(' Path: {path}', { path: result.storePath }); + logger.info(' Exists: {exists}', { exists: result.storeExists ? 'yes' : 'no' }); + logger.info(' Skills: {path}', { path: result.skillsCompatPath }); + logger.info(' Skills exists: {exists}', { exists: result.skillsCompatExists ? 'yes' : 'no' }); // Agents - console.log(chalk.cyan('\nAgents')); + logger.info('\nAgents'); const detected = result.agents.filter((a) => a.binaryFound || a.skillPathExists); if (detected.length === 0) { - console.log(chalk.yellow(' No agents detected.')); + logger.warn(' No agents detected.'); } for (const agent of result.agents) { const detectedHere = agent.binaryFound || agent.skillPathExists; - const status = detectedHere ? chalk.green('✓') : chalk.gray('○'); - const binStatus = agent.binaryFound ? chalk.green('found') : chalk.gray('not found'); - const pathStatus = agent.skillPathExists ? chalk.green('exists') : chalk.gray('missing'); - console.log(chalk.gray(` ${status} ${agent.displayName.padEnd(22)} binary: ${binStatus} path: ${pathStatus}`)); + const status = detectedHere ? '✓' : '○'; + const binStatus = agent.binaryFound ? 'found' : 'not found'; + const pathStatus = agent.skillPathExists ? 'exists' : 'missing'; + logger.info(' {status} {name} binary: {bin} path: {path}', { + status, + name: agent.displayName.padEnd(22), + bin: binStatus, + path: pathStatus, + }); } // Plugins - console.log(chalk.cyan(`\nPlugins (${result.plugins.length})`)); + logger.info('\nPlugins ({count})', { count: result.plugins.length }); if (result.plugins.length === 0) { - console.log(chalk.gray(' No plugins installed.')); + logger.info(' No plugins installed.'); } for (const plugin of result.plugins) { const linkCount = plugin.symlinks.length; const broken = plugin.symlinks.filter((s) => !s.valid).length; - const status = broken > 0 ? chalk.yellow('⚠') : chalk.green('✓'); - console.log(chalk.gray(` ${status} ${plugin.meta.name.padEnd(24)} v${plugin.meta.version} links: ${linkCount}${broken > 0 ? ` (${broken} broken)` : ''}`)); + const status = broken > 0 ? '⚠' : '✓'; + logger.info(' {status} {name} v{version} links: {links}{broken}', { + status, + name: plugin.meta.name.padEnd(24), + version: plugin.meta.version, + links: linkCount, + broken: broken > 0 ? ` (${broken} broken)` : '', + }); } // Issues if (result.issues.length > 0) { const errors = result.issues.filter((i) => i.level === 'error'); - console.log(chalk.cyan(`\nIssues (${result.issues.length})`)); + logger.info('\nIssues ({count})', { count: result.issues.length }); for (const issue of result.issues) { - const icon = issue.level === 'error' ? chalk.red('✗') : issue.level === 'warning' ? chalk.yellow('⚠') : chalk.blue('ℹ'); - console.log(` ${icon} ${issue.message}`); + const icon = issue.level === 'error' ? '✗' : issue.level === 'warning' ? '⚠' : 'ℹ'; + if (issue.level === 'error') { + logger.error(' {icon} {message}', { icon, message: issue.message }); + } else if (issue.level === 'warning') { + logger.warn(' {icon} {message}', { icon, message: issue.message }); + } else { + logger.info(' {icon} {message}', { icon, message: issue.message }); + } } if (errors.length > 0) { - console.log(chalk.red(`\n${errors.length} error(s) found.`)); + logger.error('\n{count} error(s) found.', { count: errors.length }); } } else { - console.log(chalk.green('\n✅ All checks passed.')); + logger.info('\n✅ All checks passed.'); } - console.log(); + logger.info(''); } diff --git a/packages/cli/src/commands/import.ts b/packages/cli/src/commands/import.ts index 21415c2..df9aae6 100644 --- a/packages/cli/src/commands/import.ts +++ b/packages/cli/src/commands/import.ts @@ -11,11 +11,13 @@ import { resolve, join } from 'node:path'; import { existsSync, writeFileSync, copyFileSync, mkdirSync } from 'node:fs'; -import chalk from 'chalk'; import { ingest, type IngestFormat, type IngestResult } from '@agentplugins/ingest'; import { isValidManifest } from '@agentplugins/schema'; import { installPlugin, getStorePath } from '@agentplugins/core'; import { verifyIntegrity, evaluateManifestScripts } from '@agentplugins/security'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface ImportOptions { format: string; @@ -31,31 +33,34 @@ const SUPPORTED_FORMATS: readonly IngestFormat[] = ['claude-code', 'codex', 'ski export async function importCommand(options: ImportOptions): Promise { const format = options.format as IngestFormat; if (!SUPPORTED_FORMATS.includes(format)) { - console.error(chalk.red(`Unknown format "${options.format}". Supported: ${SUPPORTED_FORMATS.join(', ')}`)); + logger.error('Unknown format "{format}". Supported: {supported}', { + format: options.format, + supported: SUPPORTED_FORMATS.join(', '), + }); process.exit(2); } const source = resolve(options.source); if (!existsSync(source)) { - console.error(chalk.red(`Source path does not exist: ${source}`)); + logger.error('Source path does not exist: {source}', { source }); process.exit(2); } - console.log(chalk.bold(`\n📥 Importing ${format} plugin from ${source}\n`)); + logger.info('\n📥 Importing {format} plugin from {source}\n', { format, source }); let result: IngestResult; try { result = ingest(format, source); } catch (err) { - console.error(chalk.red('Import failed:'), err instanceof Error ? err.message : String(err)); + logger.error('Import failed: {msg}', { msg: err instanceof Error ? err.message : String(err) }); process.exit(1); } // Schema-validate the synthesized manifest const valid = isValidManifest(result.manifest); if (!valid) { - console.error(chalk.yellow('⚠ The synthesized manifest does not validate against the AgentPlugins v1 schema.')); - console.error(chalk.gray(' The output will still be written so you can fix the gaps manually.')); + logger.warn('⚠ The synthesized manifest does not validate against the AgentPlugins v1 schema.'); + logger.info(' The output will still be written so you can fix the gaps manually.'); } // Default destination: /agentplugins.imported.json @@ -64,7 +69,7 @@ export async function importCommand(options: ImportOptions): Promise { : join(source, 'agentplugins.imported.json'); writeFileSync(outPath, JSON.stringify(result.manifest, null, 2) + '\n', 'utf-8'); - console.log(chalk.green(`✓ Manifest written to ${outPath}`)); + logger.info('✓ Manifest written to {path}', { path: outPath }); // Vendor upstream files into /.agentplugins-vendor/ if (options.vendor !== false && result.vendorFiles.length > 0) { @@ -79,16 +84,29 @@ export async function importCommand(options: ImportOptions): Promise { // Non-fatal: vendor is best-effort } } - console.log(chalk.gray(` Vendored ${result.vendorFiles.length} file${result.vendorFiles.length === 1 ? '' : 's'} into ${vendorRoot}`)); + logger.info(' Vendored {count} file{plural} into {root}', { + count: result.vendorFiles.length, + plural: result.vendorFiles.length === 1 ? '' : 's', + root: vendorRoot, + }); } // Print warnings to stderr (or stdout in quiet mode) if (!options.quiet && result.warnings.length > 0) { - console.error(chalk.yellow(`\n⚠ ${result.warnings.length} warning${result.warnings.length === 1 ? '' : 's'} from ingestor:`)); + logger.warn('\n⚠ {count} warning{plural} from ingestor:', { + count: result.warnings.length, + plural: result.warnings.length === 1 ? '' : 's', + }); for (const w of result.warnings) { - const tag = w.severity === 'error' ? chalk.red('[error]') : w.severity === 'warning' ? chalk.yellow('[warn]') : chalk.gray('[info]'); - console.error(` ${tag} ${w.code}: ${w.message}`); - if (w.suggestion) console.error(chalk.gray(` → ${w.suggestion}`)); + const tag = w.severity === 'error' ? '[error]' : w.severity === 'warning' ? '[warn]' : '[info]'; + if (w.severity === 'error') { + logger.error(' {tag} {code}: {message}', { tag, code: w.code, message: w.message }); + } else if (w.severity === 'warning') { + logger.warn(' {tag} {code}: {message}', { tag, code: w.code, message: w.message }); + } else { + logger.info(' {tag} {code}: {message}', { tag, code: w.code, message: w.message }); + } + if (w.suggestion) logger.info(' → {suggestion}', { suggestion: w.suggestion }); } } @@ -102,7 +120,7 @@ export async function importCommand(options: ImportOptions): Promise { if (integrity && integrity.length > 0) { const { match, reason } = verifyIntegrity(source, integrity); if (!match) { - console.error(chalk.red(`\nIntegrity check failed: ${reason}`)); + logger.error('\nIntegrity check failed: {reason}', { reason }); process.exit(1); } } @@ -111,15 +129,29 @@ export async function importCommand(options: ImportOptions): Promise { const scriptCheck = evaluateManifestScripts(result.manifest as Record, name); if (!scriptCheck.ok) { for (const issue of scriptCheck.issues) { - const tag = issue.decision === 'deny' ? chalk.red('[error]') : chalk.yellow('[review]'); - console.error(` ${tag} ${issue.dependency} (${issue.phase}): ${issue.command}`); - for (const r of issue.reasons) console.error(chalk.gray(` ${r}`)); + const tag = issue.decision === 'deny' ? '[error]' : '[review]'; + if (issue.decision === 'deny') { + logger.error(' {tag} {dependency} ({phase}): {command}', { + tag, + dependency: issue.dependency, + phase: issue.phase, + command: issue.command, + }); + } else { + logger.warn(' {tag} {dependency} ({phase}): {command}', { + tag, + dependency: issue.dependency, + phase: issue.phase, + command: issue.command, + }); + } + for (const r of issue.reasons) logger.info(' {reason}', { reason: r }); } - console.error(chalk.red('\nRefusing to install: lifecycle script policy violation')); + logger.error('\nRefusing to install: lifecycle script policy violation'); process.exit(1); } - console.log(chalk.blue(`\nInstalling into store at ${getStorePath()}/${name} ...`)); + logger.info('\nInstalling into store at {path}/{name} ...', { path: getStorePath(), name }); try { installPlugin(source, { source: `import:${format}:${source}`, @@ -128,12 +160,12 @@ export async function importCommand(options: ImportOptions): Promise { manifestPath: 'agentplugins.imported.json', version, }); - console.log(chalk.green(`✓ Installed ${name} v${version}`)); + logger.info('✓ Installed {name} v{version}', { name, version }); } catch (err) { - console.error(chalk.red('Install failed:'), err instanceof Error ? err.message : String(err)); + logger.error('Install failed: {msg}', { msg: err instanceof Error ? err.message : String(err) }); process.exit(1); } } else { - console.log(chalk.gray('\nRe-run with --write to install into the universal store.')); + logger.info('\nRe-run with --write to install into the universal store.'); } } diff --git a/packages/cli/src/commands/info.ts b/packages/cli/src/commands/info.ts index c7bb5df..fde7329 100644 --- a/packages/cli/src/commands/info.ts +++ b/packages/cli/src/commands/info.ts @@ -4,8 +4,10 @@ * Shows detailed information about an installed plugin. */ -import chalk from 'chalk'; import { getPluginInfo } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface InfoOptions { name: string; @@ -16,7 +18,7 @@ export async function info(options: InfoOptions): Promise { const plugin = getPluginInfo(options.name); if (!plugin) { - console.error(chalk.red(`Plugin "${options.name}" is not installed.`)); + logger.error('Plugin "{name}" is not installed.', { name: options.name }); process.exit(1); } @@ -38,17 +40,17 @@ export async function info(options: InfoOptions): Promise { const m = plugin.meta; - console.log(chalk.bold(`\nℹ️ ${m.name}\n`)); + logger.info('\nℹ️ {name}\n', { name: m.name }); - console.log(chalk.cyan(' Metadata')); - console.log(chalk.gray(` Name: ${m.name}`)); - console.log(chalk.gray(` Version: ${m.version}`)); - console.log(chalk.gray(` Source: ${m.source}`)); - console.log(chalk.gray(` Commit: ${m.commit}`)); - console.log(chalk.gray(` Installed: ${m.installedAt}`)); - console.log(chalk.gray(` Updated: ${m.updatedAt}`)); - console.log(chalk.gray(` Manifest: ${m.manifestPath}`)); - console.log(chalk.gray(` Store path: ${plugin.path}`)); + logger.info(' Metadata'); + logger.info(' Name: {name}', { name: m.name }); + logger.info(' Version: {version}', { version: m.version }); + logger.info(' Source: {source}', { source: m.source }); + logger.info(' Commit: {commit}', { commit: m.commit }); + logger.info(' Installed: {installed}', { installed: m.installedAt }); + logger.info(' Updated: {updated}', { updated: m.updatedAt }); + logger.info(' Manifest: {manifest}', { manifest: m.manifestPath }); + logger.info(' Store path: {path}', { path: plugin.path }); // Manifest details if (plugin.manifest) { @@ -60,37 +62,41 @@ export async function info(options: InfoOptions): Promise { const skills = manifest['skills']; const tools = manifest['tools']; - console.log(chalk.cyan('\n Manifest')); - if (description) console.log(chalk.gray(` Description: ${description}`)); + logger.info('\n Manifest'); + if (description) logger.info(' Description: {description}', { description }); if (author) { const authorStr = typeof author === 'string' ? author : (author as Record)?.name || ''; - if (authorStr) console.log(chalk.gray(` Author: ${authorStr}`)); + if (authorStr) logger.info(' Author: {author}', { author: authorStr }); } - if (license) console.log(chalk.gray(` License: ${license}`)); + if (license) logger.info(' License: {license}', { license }); if (hooks && typeof hooks === 'object') { const hookNames = Object.keys(hooks as Record); - console.log(chalk.gray(` Hooks: ${hookNames.join(', ')}`)); + logger.info(' Hooks: {hooks}', { hooks: hookNames.join(', ') }); } if (Array.isArray(skills)) { - console.log(chalk.gray(` Skills: ${skills.length}`)); + logger.info(' Skills: {count}', { count: skills.length }); } if (Array.isArray(tools)) { - console.log(chalk.gray(` Tools: ${tools.length}`)); + logger.info(' Tools: {count}', { count: tools.length }); } } // Symlinks if (plugin.symlinks.length > 0) { - console.log(chalk.cyan('\n Symlinks')); + logger.info('\n Symlinks'); for (const s of plugin.symlinks) { - const status = s.valid ? chalk.green('✓') : chalk.red('✗'); - console.log(chalk.gray(` ${status} ${s.agentDisplayName.padEnd(20)} ${s.linkPath}`)); + const status = s.valid ? '✓' : '✗'; + logger.info(' {status} {agent} {path}', { + status, + agent: s.agentDisplayName.padEnd(20), + path: s.linkPath, + }); } } else { - console.log(chalk.cyan('\n Symlinks')); - console.log(chalk.gray(' (none — no agent harnesses detected)')); + logger.info('\n Symlinks'); + logger.info(' (none — no agent harnesses detected)'); } - console.log(); + logger.info(''); } diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 9692ec4..fce7b5b 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -7,8 +7,10 @@ import { resolve } from 'node:path'; import { mkdir, writeFile } from 'node:fs/promises'; -import chalk from 'chalk'; import * as p from '@clack/prompts'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface InitOptions { name?: string; @@ -68,8 +70,8 @@ const KEBAB_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/; export async function init(options: InitOptions): Promise { if (options.template && !TEMPLATES.includes(options.template as (typeof TEMPLATES)[number])) { - console.error(chalk.red(`Unknown template: ${options.template}`)); - console.error(chalk.gray(`Available templates: ${TEMPLATES.join(', ')}`)); + logger.error('Unknown template: {template}', { template: options.template }); + logger.error('Available templates: {templates}', { templates: TEMPLATES.join(', ') }); process.exit(1); } @@ -77,9 +79,9 @@ export async function init(options: InitOptions): Promise { if (options.yes) { answers = getDefaults(options); - console.log(chalk.bold(`\n🆕 Creating AgentPlugins plugin: ${answers.name}\n`)); + logger.info('\n🆕 Creating AgentPlugins plugin: {name}\n', { name: answers.name }); await generateFiles(answers); - console.log(chalk.green('✅ Plugin scaffolded!\n')); + logger.info('✅ Plugin scaffolded!\n'); } else { p.intro('AgentPlugins plugin scaffold'); answers = await runInteractive(options); @@ -87,11 +89,11 @@ export async function init(options: InitOptions): Promise { p.outro('Plugin scaffolded!'); } - console.log(chalk.gray('Next steps:')); - console.log(chalk.gray(` cd ${answers.name}`)); - console.log(chalk.gray(' npm install')); - console.log(chalk.gray(' npm run validate')); - console.log(chalk.gray(' npm run build\n')); + logger.info('Next steps:'); + logger.info(' cd {name}', { name: answers.name }); + logger.info(' npm install'); + logger.info(' npm run validate'); + logger.info(' npm run build\n'); } // ─── Interactive Flow ───────────────────────────────────────────────────────── @@ -106,7 +108,7 @@ async function runInteractive(opts: InitOptions): Promise { placeholder: 'my-plugin', defaultValue: DEFAULTS.name, validate: (v) => - v.length === 0 || !KEBAB_RE.test(v) + !v || v.length === 0 || !KEBAB_RE.test(v) ? 'Use kebab-case (lowercase letters, digits, hyphens)' : undefined, }); @@ -125,7 +127,7 @@ async function runInteractive(opts: InitOptions): Promise { message: 'Description', placeholder: 'Describe what your plugin does...', validate: (v) => - v.trim().length < 10 ? 'Description must be at least 10 characters' : undefined, + !v || v.trim().length < 10 ? 'Description must be at least 10 characters' : undefined, }), ); @@ -141,7 +143,7 @@ async function runInteractive(opts: InitOptions): Promise { targets = opts.target.split(',').map((t) => t.trim()).filter(Boolean); } else { const picked = assertValue( - await p.multiselect({ + await p.multiselect({ message: 'Target platforms', options: TARGET_OPTIONS, required: false, @@ -151,7 +153,7 @@ async function runInteractive(opts: InitOptions): Promise { } const pickedHooks = assertValue( - await p.multiselect({ + await p.multiselect({ message: 'Hook coverage', options: HOOK_OPTIONS, required: false, @@ -171,7 +173,7 @@ async function runInteractive(opts: InitOptions): Promise { message: 'Skill name', placeholder: `${name}-skill`, defaultValue: `${name}-skill`, - validate: (v) => (v.length === 0 ? 'Skill name is required' : undefined), + validate: (v) => (!v || v.length === 0 ? 'Skill name is required' : undefined), }), ); skillDescription = assertValue( @@ -179,7 +181,7 @@ async function runInteractive(opts: InitOptions): Promise { message: 'Skill description', placeholder: `Describe what the ${name} skill does...`, validate: (v) => - v.trim().length < 10 ? 'Description must be at least 10 characters' : undefined, + !v || v.trim().length < 10 ? 'Description must be at least 10 characters' : undefined, }), ); } @@ -363,8 +365,8 @@ function buildPackageJson(a: ScaffoldAnswers) { validate: 'agentplugins validate', }, devDependencies: { - '@agentplugins/core': '^0.2.0', - '@agentplugins/cli': '^0.2.0', + '@agentplugins/core': '^0.5.0', + '@agentplugins/cli': '^0.5.0', typescript: '^5.5.0', }, }; diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index 9d9047a..297ffe1 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -5,10 +5,12 @@ * Catches common pitfalls: naming, versioning, handler safety, secrets, etc. */ -import chalk from 'chalk'; import { lintManifest } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; import type { LoadedConfig } from '../config.js'; +const logger = getCliLogger(); + export interface LintOptions { config: LoadedConfig; json: boolean; @@ -31,11 +33,11 @@ export async function lint(options: LintOptions): Promise { return; } - console.log(chalk.bold('\n🔍 AgentPlugins Lint\n')); - console.log(chalk.gray(`Plugin: ${manifest.name} v${manifest.version}\n`)); + logger.info('\n🔍 AgentPlugins Lint\n'); + logger.info('Plugin: {name} v{version}\n', { name: manifest.name, version: manifest.version }); if (issues.length === 0) { - console.log(chalk.green(' ✅ No issues found.\n')); + logger.info(' ✅ No issues found.\n'); return; } @@ -43,24 +45,27 @@ export async function lint(options: LintOptions): Promise { const warnings = issues.filter(i => i.severity === 'warning'); for (const issue of issues) { - const color = issue.severity === 'error' ? chalk.red : chalk.yellow; - const icon = issue.severity === 'error' ? '✗' : '⚠'; - const field = issue.field ? chalk.gray(` [${issue.field}]`) : ''; - const rule = chalk.gray(` (${issue.rule})`); - console.log(color(` ${icon} ${issue.message}${field}${rule}`)); + const field = issue.field ? ` [${issue.field}]` : ''; + const rule = ` (${issue.rule})`; + const message = ` ${issue.severity === 'error' ? '✗' : '⚠'} ${issue.message}${field}${rule}`; + if (issue.severity === 'error') { + logger.error(message); + } else { + logger.warn(message); + } if (issue.suggestion) { - console.log(chalk.cyan(` → ${issue.suggestion}`)); + logger.info(' → {suggestion}', { suggestion: issue.suggestion }); } } - console.log(); - console.log(chalk.bold('Summary:')); - if (errors.length > 0) console.log(chalk.red(` Errors: ${errors.length}`)); - if (warnings.length > 0) console.log(chalk.yellow(` Warnings: ${warnings.length}`)); + logger.info(''); + logger.info('Summary:'); + if (errors.length > 0) logger.error(' Errors: {count}', { count: errors.length }); + if (warnings.length > 0) logger.warn(' Warnings: {count}', { count: warnings.length }); if (errors.length === 0 && warnings.length > 0) { - console.log(chalk.gray(' Run with --strict to fail on warnings.')); + logger.info(' Run with --strict to fail on warnings.'); } - console.log(); + logger.info(''); if (errors.length > 0) { process.exit(1); diff --git a/packages/cli/src/commands/list.ts b/packages/cli/src/commands/list.ts index 90b631d..266db12 100644 --- a/packages/cli/src/commands/list.ts +++ b/packages/cli/src/commands/list.ts @@ -4,8 +4,10 @@ * Lists all installed plugins in the universal store. */ -import chalk from 'chalk'; import { listPlugins } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface ListOptions { json?: boolean; @@ -26,12 +28,12 @@ export async function list(options: ListOptions): Promise { } if (plugins.length === 0) { - console.log(chalk.gray('\nNo plugins installed.')); - console.log(chalk.gray('Run `agentplugins add ` to install a plugin.\n')); + logger.info('\nNo plugins installed.'); + logger.info('Run `agentplugins add ` to install a plugin.\n'); return; } - console.log(chalk.bold(`\n📦 Installed Plugins (${plugins.length})\n`)); + logger.info('\n📦 Installed Plugins ({count})\n', { count: plugins.length }); for (const plugin of plugins) { const symlinkCount = plugin.symlinks.length; @@ -39,21 +41,23 @@ export async function list(options: ListOptions): Promise { const description = (plugin.manifest?.['description'] as string) ?? plugin.meta.source; - console.log(chalk.cyan(` ${plugin.meta.name}`) + chalk.gray(` v${plugin.meta.version}`)); - console.log(chalk.gray(` ${description}`)); + logger.info(' {name} v{version}', { name: plugin.meta.name, version: plugin.meta.version }); + logger.info(' {description}', { description }); if (plugin.meta.source && plugin.meta.source !== 'unknown') { - console.log(chalk.gray(` source: ${plugin.meta.source}`)); + logger.info(' source: {source}', { source: plugin.meta.source }); } - console.log(chalk.gray(` updated: ${plugin.meta.updatedAt}`)); + logger.info(' updated: {updated}', { updated: plugin.meta.updatedAt }); if (symlinkCount > 0) { const agentNames = plugin.symlinks.map((s) => s.agent).join(', '); - const status = brokenCount > 0 ? chalk.yellow(` (${brokenCount} broken)`) : ''; - console.log(chalk.gray(` linked: ${agentNames}`) + status); + logger.info(' linked: {agents}{broken}', { + agents: agentNames, + broken: brokenCount > 0 ? ` (${brokenCount} broken)` : '', + }); } else { - console.log(chalk.gray(' linked: (none)')); + logger.info(' linked: (none)'); } - console.log(); + logger.info(''); } } diff --git a/packages/cli/src/commands/preview.ts b/packages/cli/src/commands/preview.ts index 21eb7e7..3dc263e 100644 --- a/packages/cli/src/commands/preview.ts +++ b/packages/cli/src/commands/preview.ts @@ -7,14 +7,16 @@ import { resolve, join } from 'node:path'; import { existsSync, readFileSync } from 'node:fs'; -import chalk from 'chalk'; import { ALL_TARGETS, type TargetPlatform, } from '@agentplugins/core'; import { compile } from './build.js'; +import { getCliLogger } from '../logger.js'; import type { LoadedConfig } from '../config.js'; +const logger = getCliLogger(); + export interface PreviewOptions { config: LoadedConfig; targets?: string[]; @@ -26,10 +28,10 @@ export async function preview(options: PreviewOptions): Promise { const manifest = config.manifest; const targetList = (options.targets || manifest.targets || ALL_TARGETS) as TargetPlatform[]; - console.log(chalk.bold('\n👁 AgentPlugins Preview\n')); - console.log(chalk.gray(`Plugin: ${manifest.name} v${manifest.version}`)); - console.log(chalk.gray(`Targets: ${targetList.join(', ')}`)); - console.log(); + logger.info('\n👁 AgentPlugins Preview\n'); + logger.info('Plugin: {name} v{version}', { name: manifest.name, version: manifest.version }); + logger.info('Targets: {targets}', { targets: targetList.join(', ') }); + logger.info(''); const results = await compile({ manifest, @@ -44,11 +46,14 @@ export async function preview(options: PreviewOptions): Promise { for (const result of results) { if (result.skipped) { - console.log(chalk.yellow(` ${result.target}: skipped${result.error ? ` — ${result.error}` : ''}`)); + logger.warn(' {target}: skipped{error}', { + target: result.target, + error: result.error ? ` — ${result.error}` : '', + }); continue; } - console.log(chalk.bold(` 📦 ${result.target}/`)); + logger.info(' 📦 {target}/', { target: result.target }); totalFiles += result.files.length; for (const file of result.files) { @@ -59,37 +64,37 @@ export async function preview(options: PreviewOptions): Promise { const distPath = join('dist', result.target, file.path); const absDist = resolve(distPath); if (!existsSync(absDist)) { - suffix = chalk.green(' (new)'); + suffix = ' (new)'; newFiles++; } else { try { const existing = readFileSync(absDist, 'utf-8'); if (existing !== file.content) { - suffix = chalk.yellow(' (changed)'); + suffix = ' (changed)'; changedFiles++; } else { - suffix = chalk.gray(' (unchanged)'); + suffix = ' (unchanged)'; } } catch { - suffix = chalk.green(' (new)'); + suffix = ' (new)'; newFiles++; } } } - console.log(chalk.gray(` ${display}`) + suffix); + logger.info(' {display}{suffix}', { display, suffix }); } - console.log(); + logger.info(''); } - console.log(chalk.bold('Summary:')); - console.log(chalk.gray(` Files: ${totalFiles}`)); + logger.info('Summary:'); + logger.info(' Files: {count}', { count: totalFiles }); if (diff) { - console.log(chalk.gray(` Changed: ${changedFiles}`)); - console.log(chalk.gray(` New: ${newFiles}`)); - console.log(chalk.gray(` Unchanged: ${totalFiles - changedFiles - newFiles}`)); + logger.info(' Changed: {count}', { count: changedFiles }); + logger.info(' New: {count}', { count: newFiles }); + logger.info(' Unchanged: {count}', { count: totalFiles - changedFiles - newFiles }); } - console.log(); + logger.info(''); // Diff output if (diff) { @@ -106,9 +111,9 @@ export async function preview(options: PreviewOptions): Promise { } if (anyDiff) { - console.log(chalk.cyan('Changes detected. Run `agentplugins build` to write output.\n')); + logger.info('Changes detected. Run `agentplugins build` to write output.\n'); } else { - console.log(chalk.gray('No changes — dist/ is up to date.\n')); + logger.info('No changes — dist/ is up to date.\n'); } } } diff --git a/packages/cli/src/commands/remove.ts b/packages/cli/src/commands/remove.ts index 5d2b67b..87194f6 100644 --- a/packages/cli/src/commands/remove.ts +++ b/packages/cli/src/commands/remove.ts @@ -4,8 +4,10 @@ * Removes a plugin from the store and unlinks all symlinks. */ -import chalk from 'chalk'; import { removePlugin, getPluginInfo } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface RemoveOptions { name: string; @@ -17,26 +19,26 @@ export async function remove(options: RemoveOptions): Promise { const info = getPluginInfo(name); if (!info) { - console.error(chalk.red(`Plugin "${name}" is not installed.`)); + logger.error('Plugin "{name}" is not installed.', { name }); process.exit(1); } - console.log(chalk.bold('\n🗑 AgentPlugins Remove\n')); - console.log(chalk.gray(`Plugin: ${name} v${info.meta.version}`)); - console.log(chalk.gray(`Source: ${info.meta.source}`)); + logger.info('\n🗑 AgentPlugins Remove\n'); + logger.info('Plugin: {name} v{version}', { name, version: info.meta.version }); + logger.info('Source: {source}', { source: info.meta.source }); if (info.symlinks.length > 0) { - console.log(chalk.gray(`Symlinks: ${info.symlinks.length}`)); + logger.info('Symlinks: {count}', { count: info.symlinks.length }); } removePlugin(name); - console.log(chalk.green(`\n✅ Removed ${name}`)); + logger.info('\n✅ Removed {name}', { name }); if (info.symlinks.length > 0) { - console.log(chalk.gray('Unlinked from:')); + logger.info('Unlinked from:'); for (const s of info.symlinks) { - console.log(chalk.gray(` ${s.agentDisplayName}: ${s.linkPath}`)); + logger.info(' {agent}: {path}', { agent: s.agentDisplayName, path: s.linkPath }); } } - console.log(); + logger.info(''); } diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts index 5b85492..b98be27 100644 --- a/packages/cli/src/commands/setup.ts +++ b/packages/cli/src/commands/setup.ts @@ -4,7 +4,6 @@ * Re-runs an installed plugin's setup script with trust-on-first-use gating. */ -import chalk from 'chalk'; import * as p from '@clack/prompts'; import { getStorePath, @@ -19,6 +18,9 @@ import { type SetupSource, } from '@agentplugins/core'; import { join } from 'node:path'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface SetupFlowOptions { name: string; @@ -37,7 +39,7 @@ export interface SetupFlowResult { export async function runSetupFlow(opts: SetupFlowOptions): Promise { if (process.env.AGENTPLUGINS_SETUP_SCRIPTS === '0') { - console.log(chalk.gray('Setup scripts disabled (AGENTPLUGINS_SETUP_SCRIPTS=0).')); + logger.info('Setup scripts disabled (AGENTPLUGINS_SETUP_SCRIPTS=0).'); return { ran: false, skipped: 'kill-switch' }; } @@ -52,9 +54,9 @@ export async function runSetupFlow(opts: SetupFlowOptions): Promise { const meta = readMeta(options.name); if (!meta) { - console.error(chalk.red(`Plugin "${options.name}" is not installed.`)); + logger.error('Plugin "{name}" is not installed.', { name: options.name }); process.exit(1); } const pluginDir = join(getStorePath(), options.name); const manifestResult = findManifestInDir(pluginDir); if (!manifestResult) { - console.error(chalk.red(`No manifest found for installed plugin "${options.name}".`)); + logger.error('No manifest found for installed plugin "{name}".', { name: options.name }); process.exit(1); } await runSetupFlow({ diff --git a/packages/cli/src/commands/update.ts b/packages/cli/src/commands/update.ts index e23764c..30524bc 100644 --- a/packages/cli/src/commands/update.ts +++ b/packages/cli/src/commands/update.ts @@ -4,8 +4,10 @@ * Pulls latest changes for a plugin (or all plugins). */ -import chalk from 'chalk'; import { updatePlugin, listPlugins, getPluginInfo } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; + +const logger = getCliLogger(); export interface UpdateOptions { name?: string; @@ -17,27 +19,30 @@ export async function update(options: UpdateOptions): Promise { if (options.all || !options.name) { const plugins = listPlugins(); if (plugins.length === 0) { - console.log(chalk.gray('\nNo plugins installed.\n')); + logger.info('\nNo plugins installed.\n'); return; } - console.log(chalk.bold(`\n🔄 Updating ${plugins.length} plugin${plugins.length > 1 ? 's' : ''}\n`)); + logger.info('\n🔄 Updating {count} plugin{plural}\n', { + count: plugins.length, + plural: plugins.length > 1 ? 's' : '', + }); let success = 0; let failed = 0; for (const plugin of plugins) { try { - process.stdout.write(chalk.gray(` ${plugin.meta.name}... `)); + process.stdout.write(` ${plugin.meta.name}... `); const meta = updatePlugin(plugin.meta.name); - console.log(chalk.green(`✓ ${meta.version} (${meta.commit.slice(0, 7)})`)); + logger.info('✓ {version} ({commit})', { version: meta.version, commit: meta.commit.slice(0, 7) }); success++; } catch (err) { const msg = err instanceof Error ? err.message : String(err); - console.log(chalk.red(`✗ ${msg}`)); + logger.error('✗ {msg}', { msg }); failed++; } } - console.log(chalk.gray(`\n${success} updated, ${failed} failed.\n`)); + logger.info('\n{success} updated, {failed} failed.\n', { success, failed }); return; } @@ -45,23 +50,26 @@ export async function update(options: UpdateOptions): Promise { const name = options.name; const info = getPluginInfo(name); if (!info) { - console.error(chalk.red(`Plugin "${name}" is not installed.`)); + logger.error('Plugin "{name}" is not installed.', { name }); process.exit(1); } - console.log(chalk.bold('\n🔄 AgentPlugins Update\n')); - console.log(chalk.gray(`Plugin: ${name}`)); - console.log(chalk.gray(`Current: v${info.meta.version} (${info.meta.commit.slice(0, 7)})\n`)); + logger.info('\n🔄 AgentPlugins Update\n'); + logger.info('Plugin: {name}', { name }); + logger.info('Current: v{version} ({commit})\n', { + version: info.meta.version, + commit: info.meta.commit.slice(0, 7), + }); try { const meta = updatePlugin(name); - console.log(chalk.green(`\n✅ Updated ${name}`)); - console.log(chalk.gray(` Version: ${meta.version}`)); - console.log(chalk.gray(` Commit: ${meta.commit.slice(0, 7)}`)); - console.log(chalk.gray(` Updated: ${meta.updatedAt}\n`)); + logger.info('\n✅ Updated {name}', { name }); + logger.info(' Version: {version}', { version: meta.version }); + logger.info(' Commit: {commit}', { commit: meta.commit.slice(0, 7) }); + logger.info(' Updated: {updated}\n', { updated: meta.updatedAt }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); - console.error(chalk.red(`\nFailed to update: ${msg}\n`)); + logger.error('\nFailed to update: {msg}\n', { msg }); process.exit(1); } } diff --git a/packages/cli/src/commands/validate.ts b/packages/cli/src/commands/validate.ts index 10a6d76..52fe541 100644 --- a/packages/cli/src/commands/validate.ts +++ b/packages/cli/src/commands/validate.ts @@ -4,14 +4,16 @@ * Validates plugin configuration without building. */ -import chalk from 'chalk'; import { validateUniversal, validateForPlatform, ALL_TARGETS, } from '@agentplugins/core'; +import { getCliLogger } from '../logger.js'; import type { LoadedConfig } from '../config.js'; +const logger = getCliLogger(); + export interface ValidateOptions { config: LoadedConfig; targets?: string[]; @@ -22,15 +24,15 @@ export async function validate(options: ValidateOptions): Promise { const manifest = config.manifest; const targets = options.targets || manifest.targets || ALL_TARGETS; - console.log(chalk.bold('\n🔍 AgentPlugins Validation\n')); - console.log(chalk.gray(`Plugin: ${manifest.name} v${manifest.version}`)); - console.log(chalk.gray(`Targets: ${targets.join(', ')}\n`)); + logger.info('\n🔍 AgentPlugins Validation\n'); + logger.info('Plugin: {name} v{version}', { name: manifest.name, version: manifest.version }); + logger.info('Targets: {targets}\n', { targets: targets.join(', ') }); // Universal validation - console.log(chalk.blue('Universal Rules:')); + logger.info('Universal Rules:'); const universalIssues = validateUniversal(manifest); if (universalIssues.length === 0) { - console.log(chalk.green(' ✓ No issues found')); + logger.info(' ✓ No issues found'); } else { for (const issue of universalIssues) { printIssue(issue); @@ -39,10 +41,10 @@ export async function validate(options: ValidateOptions): Promise { // Per-platform validation for (const target of targets) { - console.log(chalk.blue(`\n${target}:`)); + logger.info('\n{target}:', { target }); const issues = validateForPlatform(manifest, target as any); if (issues.length === 0) { - console.log(chalk.green(' ✓ No issues found')); + logger.info(' ✓ No issues found'); } else { for (const issue of issues) { printIssue(issue); @@ -54,19 +56,25 @@ export async function validate(options: ValidateOptions): Promise { targets.reduce((sum, t) => sum + validateForPlatform(manifest, t as any).filter(i => i.severity === 'error').length, 0); if (totalErrors === 0) { - console.log(chalk.bold('\n✅ All checks passed!\n')); + logger.info('\n✅ All checks passed!\n'); } else { - console.log(chalk.bold(`\n❌ Found ${totalErrors} error(s)\n`)); + logger.error('\n❌ Found {count} error(s)\n', { count: totalErrors }); process.exit(1); } } function printIssue(issue: { severity: string; field?: string; message: string; suggestion?: string }): void { - const color = issue.severity === 'error' ? chalk.red : issue.severity === 'warning' ? chalk.yellow : chalk.gray; const icon = issue.severity === 'error' ? '✗' : issue.severity === 'warning' ? '⚠' : 'ℹ'; - const field = issue.field ? chalk.gray(`[${issue.field}] `) : ''; - console.log(color(` ${icon} ${field}${issue.message}`)); + const field = issue.field ? `[${issue.field}] ` : ''; + const message = ` ${icon} ${field}${issue.message}`; + if (issue.severity === 'error') { + logger.error(message); + } else if (issue.severity === 'warning') { + logger.warn(message); + } else { + logger.info(message); + } if (issue.suggestion) { - console.log(chalk.cyan(` → ${issue.suggestion}`)); + logger.info(' → {suggestion}', { suggestion: issue.suggestion }); } } diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index 951534e..d9ad998 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -3,17 +3,18 @@ * * Loads plugin configuration from agentplugins.config.ts/js/mjs/json files. * Uses jiti for TypeScript support without pre-compilation. + * + * Supports three export shapes: + * 1. defineConfig({ manifest, plugins, targets }) — power user + * 2. definePlugin(manifest) — bare manifest object + * 3. () => PluginManifest — factory function */ import { existsSync } from 'node:fs'; import { resolve, extname } from 'node:path'; -import jiti from 'jiti'; - -// jiti's CJS default export is a function - cast to proper type -const createJITI = jiti as unknown as (filename: string, opts?: Record) => { - import: (id: string, opts?: Record) => Promise; -}; -import type { PluginManifest } from '@agentplugins/core'; +import { createJiti } from 'jiti'; +import type { PluginManifest, AgentPluginsConfig } from '@agentplugins/core'; +import type { Plugin } from '@agentplugins/pipeline'; export interface LoadedConfig { /** Resolved manifest */ @@ -22,6 +23,19 @@ export interface LoadedConfig { root: string; /** Path to the config file */ configPath: string; + /** User-provided pipeline plugins from defineConfig (empty if bare manifest). */ + plugins: Plugin[]; + /** User-provided target override from defineConfig (undefined if not set). */ + configTargets?: string[]; +} + +function isDefineConfig(obj: unknown): obj is AgentPluginsConfig { + return ( + typeof obj === 'object' && + obj !== null && + 'manifest' in obj && + typeof (obj as Record).manifest === 'object' + ); } /** @@ -37,24 +51,26 @@ export async function loadConfig(configPath: string): Promise { const ext = extname(resolvedPath); let manifest: PluginManifest; + let plugins: Plugin[] = []; + let configTargets: string[] | undefined; if (ext === '.json') { - // JSON config + // JSON config — always a bare manifest const { readFile } = await import('node:fs/promises'); const content = await readFile(resolvedPath, 'utf-8'); manifest = JSON.parse(content) as PluginManifest; } else { // TypeScript/JavaScript config — use jiti - const jiti = createJITI(resolvedPath, { - interopDefault: true, - esmResolve: true, - }); - - const mod = await jiti.import(resolvedPath, { default: true }); - const exported = (mod as any)?.default ?? mod; + const jiti = createJiti(resolvedPath, { interopDefault: true }); + const mod = await jiti.import(resolvedPath); + const exported = (mod as Record)?.default ?? mod; if (typeof exported === 'function') { - manifest = await exported(); + manifest = await (exported as () => Promise)(); + } else if (isDefineConfig(exported)) { + manifest = exported.manifest; + plugins = exported.plugins ?? []; + configTargets = exported.targets; } else { manifest = exported as PluginManifest; } @@ -71,10 +87,9 @@ export async function loadConfig(configPath: string): Promise { throw new Error('Plugin manifest must have a "description" field'); } - // Resolve root directory const root = resolve(resolvedPath, '..'); - return { manifest, root, configPath: resolvedPath }; + return { manifest, root, configPath: resolvedPath, plugins, configTargets }; } /** diff --git a/packages/cli/src/logger.ts b/packages/cli/src/logger.ts new file mode 100644 index 0000000..18abb61 --- /dev/null +++ b/packages/cli/src/logger.ts @@ -0,0 +1,20 @@ +import { configure, getConsoleSink, getLogger } from '@logtape/logtape'; + +let configured = false; + +export async function setupLogger() { + if (configured) return; + await configure({ + sinks: { console: getConsoleSink() }, + filters: {}, + loggers: [ + { category: ['agentplugins', 'cli'], sinks: ['console'], lowestLevel: 'info' }, + { category: ['logtape', 'meta'], sinks: ['console'], lowestLevel: 'warning' }, + ], + }); + configured = true; +} + +export function getCliLogger() { + return getLogger(['agentplugins', 'cli']); +} diff --git a/packages/compile/package.json b/packages/compile/package.json index 67ab94e..ea853e0 100644 --- a/packages/compile/package.json +++ b/packages/compile/package.json @@ -24,7 +24,12 @@ "publishConfig": { "access": "public" }, - "keywords": ["agentplugins", "compile", "codegen", "kernel"], + "keywords": [ + "agentplugins", + "compile", + "codegen", + "kernel" + ], "license": "Apache-2.0", "repository": { "type": "git", @@ -33,11 +38,12 @@ }, "homepage": "https://agentplugins.pages.dev/", "dependencies": { - "@agentplugins/contract": "workspace:*" + "@agentplugins/contract": "workspace:*", + "@agentplugins/pipeline": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/compile/src/codegen.ts b/packages/compile/src/codegen.ts index 8b03d79..4ed5476 100644 --- a/packages/compile/src/codegen.ts +++ b/packages/compile/src/codegen.ts @@ -28,8 +28,9 @@ export function extractCompiledHooks(manifest: PluginManifest): CompiledHook[] { if (!hooks) return []; const compiled: CompiledHook[] = []; + const hooksRecord = hooks as Record; for (const name of Object.keys(hooks) as UniversalHookName[]) { - const def: HookDefinition | undefined = hooks[name]; + const def = hooksRecord[name as string]; if (!def) continue; const handler = def.handler; @@ -43,9 +44,9 @@ export function extractCompiledHooks(manifest: PluginManifest): CompiledHook[] { entry.command = handler.command; } else if (handler.type === 'http') { entry.url = handler.url; - if (handler.headers) entry.headers = { ...handler.headers }; + if (handler.headers) entry.headers = { ...(handler.headers as Record) }; } else { - entry.inlineSource = `// inline handler for ${name} — emit at build time`; + entry.inlineSource = `// inline handler for ${String(name)} — emit at build time`; } compiled.push(entry); @@ -83,7 +84,7 @@ function hookEntry(hook: CompiledHook): string { function buildHooksMap(hooks: CompiledHook[]): string { const indent = ' '; - const lines = hooks.map((h) => `${indent}${h.name}: ${hookEntry(h)},`); + const lines = hooks.map((h) => `${indent}${String(h.name)}: ${hookEntry(h)},`); return `{\n${lines.join('\n')}\n}`; } @@ -130,7 +131,7 @@ export default plugin; function buildHooksDTS(hooks: CompiledHook[]): string { const indent = ' '; - const lines = hooks.map((h) => `${indent}${h.name}: Record;`); + const lines = hooks.map((h) => `${indent}${String(h.name)}: Record;`); return `{\n${lines.join('\n')}\n}`; } @@ -180,7 +181,7 @@ export const GoEmitter: CodeEmitter = { const imports = hooks.length > 0 ? `\nimport "context"\n` : ''; const funcs = hooks.map((h) => { - const fnName = `On${pascalCase(h.name)}`; + const fnName = `On${pascalCase(String(h.name))}`; return [ `func ${fnName}(ctx context.Context) error {`, `\t// TODO: implement ${h.handlerType} handler`, @@ -189,7 +190,7 @@ export const GoEmitter: CodeEmitter = { ].join('\n'); }); - const registrations = hooks.map((h) => `\t// register On${pascalCase(h.name)}`).join('\n'); + const registrations = hooks.map((h) => `\t// register On${pascalCase(String(h.name))}`).join('\n'); const initFn = hooks.length > 0 ? `\nfunc init() {\n${registrations}\n}\n` @@ -212,16 +213,24 @@ export const GoEmitter: CodeEmitter = { }, }; -const EMITTERS: Record = { - typescript: TypeScriptEmitter, - javascript: JavaScriptEmitter, - go: GoEmitter, -}; +const EMITTERS: Map = new Map([ + ['typescript', TypeScriptEmitter], + ['javascript', JavaScriptEmitter], + ['go', GoEmitter], +]); + +export function registerEmitter(emitter: CodeEmitter): void { + EMITTERS.set(emitter.language, emitter); +} -export function getEmitter(language: EmitLanguage): CodeEmitter { - const emitter = EMITTERS[language]; +export function getEmitter(language: EmitLanguage | string): CodeEmitter { + const emitter = EMITTERS.get(language); if (!emitter) { throw new Error(`Unknown emit language: ${String(language)}`); } return emitter; } + +export function getRegisteredEmitters(): ReadonlyMap { + return EMITTERS; +} diff --git a/packages/compile/src/lint.ts b/packages/compile/src/lint.ts index 672cdde..cb3e6c2 100644 --- a/packages/compile/src/lint.ts +++ b/packages/compile/src/lint.ts @@ -5,7 +5,7 @@ * catching quality and safety issues beyond structural validation. */ -import type { PluginManifest } from '@agentplugins/contract'; +import type { PluginManifest, HookDefinition, HookHandler } from '@agentplugins/contract'; export interface LintIssue { rule: string; @@ -18,6 +18,8 @@ export interface LintIssue { export interface LintContext { manifest: PluginManifest; inlineHandlerSource?: string[]; + /** Extra rules appended to the global registry for this lint run only. */ + extraRules?: LintRule[]; } export interface LintRule { @@ -227,7 +229,8 @@ const handlerSafetyRule: LintRule = { // Scan command-handler command strings if (manifest.hooks) { - for (const [name, def] of Object.entries(manifest.hooks)) { + const hooks = manifest.hooks as Record; + for (const [name, def] of Object.entries(hooks)) { if (def && def.handler.type === 'command') { for (const pattern of activePatterns) { const match = pattern.exec(def.handler.command); @@ -262,7 +265,8 @@ const secretsRule: LintRule = { } } if (manifest.hooks) { - for (const [name, def] of Object.entries(manifest.hooks)) { + const hooks = manifest.hooks as Record; + for (const [name, def] of Object.entries(hooks)) { if (def && def.handler.type === 'command') { haystacks.push({ field: `hooks.${name}`, text: def.handler.command }); } @@ -294,7 +298,7 @@ const continueWithSafetyRule: LintRule = { const hasStopHook = !!manifest.hooks?.stop; if (!hasStopHook) return []; - const handler = manifest.hooks?.stop?.handler; + const handler = manifest.hooks?.stop?.handler as HookHandler | undefined; // Detect continueWith usage: in command handler text OR in inline handler source const usesContinueWith = (handler?.type === 'command' && handler.command.includes('continueWith')) || @@ -340,7 +344,8 @@ export function getLintRules(): LintRule[] { export function lint(ctx: LintContext): LintIssue[] { const issues: LintIssue[] = []; - for (const rule of registry) { + const rules = ctx.extraRules ? [...registry, ...ctx.extraRules] : registry; + for (const rule of rules) { issues.push(...rule.run(ctx)); } return issues; diff --git a/packages/compile/src/validation.ts b/packages/compile/src/validation.ts index f2eb1fb..519c421 100644 --- a/packages/compile/src/validation.ts +++ b/packages/compile/src/validation.ts @@ -10,10 +10,12 @@ import { type ValidationIssue, type UniversalHookName, type HookHandler, + type HookDefinition, UNIVERSAL_HOOK_NAMES, ALL_TARGETS, type TargetPlatform, Severity, + type Dependency, } from '@agentplugins/contract'; // ─── Universal Validators ───────────────────────────────────────────────────── @@ -21,7 +23,11 @@ import { const KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/; const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; -export function validateUniversal(plugin: PluginManifest): ValidationIssue[] { +export function validateUniversal( + plugin: PluginManifest, + opts?: { knownTargets?: readonly string[] } +): ValidationIssue[] { + const knownTargets = opts?.knownTargets ?? (ALL_TARGETS as string[]); const issues: ValidationIssue[] = []; // ─── name ───────────────────────────────────────────────────────────────── @@ -71,12 +77,12 @@ export function validateUniversal(plugin: PluginManifest): ValidationIssue[] { // ─── targets ────────────────────────────────────────────────────────────── if (plugin.targets) { for (const target of plugin.targets) { - if (!ALL_TARGETS.includes(target)) { + if (!(knownTargets as string[]).includes(target)) { issues.push({ - severity: Severity.ERROR, + severity: Severity.WARNING, field: 'targets', - message: `Unknown target platform: "${target}"`, - suggestion: `Supported: ${ALL_TARGETS.join(', ')}`, + message: `"${target}" is not a built-in target — ensure a matching adapter is registered or compilation will be skipped`, + suggestion: `Built-in targets: ${ALL_TARGETS.join(', ')}`, }); } } @@ -84,7 +90,8 @@ export function validateUniversal(plugin: PluginManifest): ValidationIssue[] { // ─── hooks ──────────────────────────────────────────────────────────────── if (plugin.hooks) { - for (const [name, def] of Object.entries(plugin.hooks)) { + const hooksRecord = plugin.hooks as Record; + for (const [name, def] of Object.entries(hooksRecord)) { const hookName = name as UniversalHookName; if (!UNIVERSAL_HOOK_NAMES.includes(hookName)) { issues.push({ @@ -130,7 +137,7 @@ export function validateUniversal(plugin: PluginManifest): ValidationIssue[] { // ─── dependencies ──────────────────────────────────────────────────────── if (plugin.dependencies) { for (let i = 0; i < plugin.dependencies.length; i++) { - const dep = plugin.dependencies[i]; + const dep = plugin.dependencies[i] as Dependency; if (dep.type === 'npm') { if (!dep.name) { issues.push({ severity: Severity.ERROR, field: `dependencies[${i}].name`, message: 'npm dependency must have a "name"' }); @@ -257,8 +264,23 @@ export interface PlatformConstraints { notes: string[]; } +const PERMISSIVE_CONSTRAINTS: PlatformConstraints = { + maxNameLength: 256, + supportsHttpHandler: true, + supportsInlineHandler: true, + supportsPromptHandler: true, + supportsMCPToolHandler: true, + supportsNativeTools: true, + supportsMcpServersAsTools: true, + maxDescriptionLength: 4096, + supportedHooks: UNIVERSAL_HOOK_NAMES, + manifestPath: 'plugin.json', + requiresStrictValidation: false, + notes: ['Custom platform — all hooks and handlers assumed supported'], +}; + export function getPlatformConstraints(platform: TargetPlatform): PlatformConstraints { - const base: Record = { + const base: Partial> = { claude: { maxNameLength: 64, supportsHttpHandler: true, @@ -388,7 +410,7 @@ export function getPlatformConstraints(platform: TargetPlatform): PlatformConstr }, }; - return base[platform]; + return base[platform] ?? PERMISSIVE_CONSTRAINTS; } export function validateForPlatform( @@ -400,7 +422,8 @@ export function validateForPlatform( // Check unsupported hooks if (plugin.hooks) { - for (const [name, def] of Object.entries(plugin.hooks)) { + const hooksRecord = plugin.hooks as Record; + for (const [name, def] of Object.entries(hooksRecord)) { const hookName = name as UniversalHookName; if (!def) continue; diff --git a/packages/contract/__tests__/schema.test.ts b/packages/contract/__tests__/schema.test.ts index 6e1e6a0..15a13ed 100644 --- a/packages/contract/__tests__/schema.test.ts +++ b/packages/contract/__tests__/schema.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { PluginManifestSchema, TargetPlatformSchema, ALL_TARGETS } from '../src/index.js'; +import { PluginManifestSchema, TargetPlatformSchema, ALL_TARGETS, AgentDefinitionSchema } from '../src/index.js'; describe('PluginManifestSchema (single source of truth)', () => { it('accepts a minimal valid manifest', () => { @@ -15,14 +15,23 @@ describe('PluginManifestSchema (single source of truth)', () => { expect(() => PluginManifestSchema.parse({})).toThrow(); }); - it('rejects an invalid target platform', () => { - expect(() => TargetPlatformSchema.parse('not-a-platform')).toThrow(); + it('rejects an empty target platform string', () => { + // TargetPlatform is now an open string — any non-empty value is valid + // so custom harnesses can register their own targets. + expect(() => TargetPlatformSchema.parse('')).toThrow(); }); - it('ALL_TARGETS matches the zod enum set', () => { - // Catches drift between the const array and the zod enum. - const enumValues = TargetPlatformSchema.options; - expect(new Set(enumValues)).toEqual(new Set(ALL_TARGETS)); + it('accepts a custom (non-builtin) target platform', () => { + // Open string type — custom adapters may register any non-empty target id. + expect(() => TargetPlatformSchema.parse('my-harness')).not.toThrow(); + }); + + it('ALL_TARGETS contains the canonical builtin platform ids', () => { + // Verify the builtin list is stable and non-empty. + expect(ALL_TARGETS.length).toBeGreaterThan(0); + expect(ALL_TARGETS).toContain('claude'); + expect(ALL_TARGETS).toContain('codex'); + expect(ALL_TARGETS).toContain('copilot'); }); it('accepts sidecar (currently documentation-only, see B21)', () => { @@ -35,3 +44,39 @@ describe('PluginManifestSchema (single source of truth)', () => { expect(parsed.sidecar?.command).toBe('node server.js'); }); }); + +describe('AgentDefinitionSchema — model fields', () => { + it('accepts agent with no model (optional)', () => { + const parsed = AgentDefinitionSchema.parse({ name: 'explorer' }); + expect(parsed.model).toBeUndefined(); + expect(parsed.fallbackModels).toBeUndefined(); + }); + + it('accepts agent with model set', () => { + const parsed = AgentDefinitionSchema.parse({ name: 'oracle', model: 'claude-opus-4-8' }); + expect(parsed.model).toBe('claude-opus-4-8'); + }); + + it('accepts agent with fallbackModels', () => { + const parsed = AgentDefinitionSchema.parse({ + name: 'fixer', + model: 'claude-sonnet-4-6', + fallbackModels: ['glm-5.2', 'kimi-k2'], + }); + expect(parsed.fallbackModels).toEqual(['glm-5.2', 'kimi-k2']); + }); + + it('accepts agents[] on PluginManifest with model', () => { + const parsed = PluginManifestSchema.parse({ + name: 'teams-roster', + version: '0.1.0', + description: 'test', + agents: [ + { name: 'orchestrator', model: 'claude-opus-4-8', fallbackModels: ['glm-5.2'] }, + { name: 'explorer' }, + ], + }); + expect(parsed.agents?.[0].model).toBe('claude-opus-4-8'); + expect(parsed.agents?.[1].model).toBeUndefined(); + }); +}); diff --git a/packages/contract/manifest.schema.json b/packages/contract/manifest.schema.json index 11d42fb..6f838ae 100644 --- a/packages/contract/manifest.schema.json +++ b/packages/contract/manifest.schema.json @@ -32,10 +32,13 @@ "type": "string" }, "email": { - "type": "string" + "type": "string", + "format": "email", + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "url": { - "type": "string" + "type": "string", + "format": "uri" } }, "required": [ @@ -98,7 +101,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -139,6 +142,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -185,7 +191,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -226,6 +232,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -272,7 +281,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -313,6 +322,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -359,7 +371,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -400,6 +412,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -446,7 +461,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -487,6 +502,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -533,7 +551,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -574,6 +592,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -620,7 +641,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -661,6 +682,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -707,7 +731,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -748,6 +772,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -794,7 +821,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -835,6 +862,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -881,7 +911,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -922,6 +952,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -968,7 +1001,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1009,6 +1042,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1055,7 +1091,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1096,6 +1132,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1142,7 +1181,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1183,6 +1222,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1229,7 +1271,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1270,6 +1312,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1316,7 +1361,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1357,6 +1402,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1403,7 +1451,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1444,6 +1492,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1490,7 +1541,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1531,6 +1582,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1577,7 +1631,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1618,6 +1672,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1664,7 +1721,7 @@ "type": "string" }, "handler": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1705,6 +1762,9 @@ }, "headers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1793,6 +1853,15 @@ }, "filePath": { "type": "string" + }, + "model": { + "type": "string" + }, + "fallbackModels": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ @@ -1803,6 +1872,9 @@ }, "mcpServers": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "object", "properties": { @@ -1817,6 +1889,9 @@ }, "env": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } @@ -1837,6 +1912,9 @@ }, "userConfig": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "object", "properties": { @@ -1877,6 +1955,9 @@ }, "metadata": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} }, "capabilities": { @@ -1889,15 +1970,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "claude", - "codex", - "copilot", - "gemini", - "kimi", - "opencode", - "pimono" - ] + "minLength": 1 } }, "nativeEntry": { @@ -1914,25 +1987,18 @@ }, "adapterOverrides": { "type": "object", + "propertyNames": { + "type": "string", + "minLength": 1 + }, "additionalProperties": { "type": "string" - }, - "propertyNames": { - "enum": [ - "claude", - "codex", - "copilot", - "gemini", - "kimi", - "opencode", - "pimono" - ] } }, "dependencies": { "type": "array", "items": { - "anyOf": [ + "oneOf": [ { "type": "object", "properties": { @@ -1990,6 +2056,9 @@ }, "env": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string" } diff --git a/packages/contract/package.json b/packages/contract/package.json index f005886..d2049ec 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -34,12 +34,11 @@ }, "homepage": "https://agentplugins.pages.dev/", "dependencies": { - "zod": "^3.25.76", - "zod-to-json-schema": "^3.24.5" + "zod": "catalog:" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/contract/src/generate-schema.ts b/packages/contract/src/generate-schema.ts index 19227f4..308d854 100644 --- a/packages/contract/src/generate-schema.ts +++ b/packages/contract/src/generate-schema.ts @@ -6,26 +6,28 @@ import { writeFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { zodToJsonSchema } from 'zod-to-json-schema'; +import { z } from 'zod'; import { PluginManifestSchema } from './schema.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); const outPath = join(__dirname, '..', 'manifest.schema.json'); -const schema = zodToJsonSchema(PluginManifestSchema, { - name: 'PluginManifest', - $refStrategy: 'none', - definitionPath: '$defs', - markdownDescription: true, - errorMessages: false, -}); +const schema = z.toJSONSchema(PluginManifestSchema, { + target: 'draft-07', +}) as Record; + +// z.toJSONSchema injects its own $schema; the wrapper below owns it. +delete schema.$schema; const output = { $schema: 'http://json-schema.org/draft-07/schema#', $id: 'https://agentplugins.pages.dev/schema/v1.json', title: 'AgentPlugins Manifest', description: 'Universal plugin manifest for AI agent platforms. https://agentplugins.pages.dev/', - ...schema, + $ref: '#/$defs/PluginManifest', + $defs: { + PluginManifest: schema, + }, }; writeFileSync(outPath, JSON.stringify(output, null, 2) + '\n'); diff --git a/packages/contract/src/schema.ts b/packages/contract/src/schema.ts index a5aa7e7..792f7f4 100644 --- a/packages/contract/src/schema.ts +++ b/packages/contract/src/schema.ts @@ -12,7 +12,8 @@ import { z } from 'zod'; // ─── Target platform ────────────────────────────────────────────────────────── -export const TargetPlatformSchema = z.enum([ +/** Known built-in target ids — custom adapters may use any non-empty string. */ +export const BUILTIN_TARGETS = [ 'claude', 'codex', 'copilot', @@ -20,12 +21,20 @@ export const TargetPlatformSchema = z.enum([ 'kimi', 'opencode', 'pimono', -]); -export type TargetPlatform = z.infer; +] as const; + +export type BuiltinTarget = (typeof BUILTIN_TARGETS)[number]; -export const ALL_TARGETS: TargetPlatform[] = [ - 'claude', 'codex', 'copilot', 'gemini', 'kimi', 'opencode', 'pimono', -]; +/** + * Accepts any non-empty string so custom adapters can declare private targets. + * Known built-in ids have autocomplete via the `BuiltinTarget` union. + */ +export const TargetPlatformSchema = z.string().min(1); + +/** Union preserving autocomplete for built-in ids while allowing custom ones. */ +export type TargetPlatform = BuiltinTarget | (string & {}); + +export const ALL_TARGETS: BuiltinTarget[] = [...BUILTIN_TARGETS]; // ─── Dependency & Sidecar ──────────────────────────────────────────────────── @@ -38,7 +47,7 @@ export type Dependency = z.infer; export const SidecarSchema = z.object({ command: z.string(), args: z.array(z.string()).optional(), - env: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), port: z.number().optional(), health: z.string().optional(), restart: z.enum(['always', 'on-failure', 'no']).optional(), @@ -73,6 +82,8 @@ export const AgentDefinitionSchema = z.object({ prompt: z.string().optional(), tools: z.array(z.string()).optional(), filePath: z.string().optional(), + model: z.string().optional(), + fallbackModels: z.array(z.string()).optional(), }); export type AgentDefinition = z.infer; @@ -81,7 +92,7 @@ export type AgentDefinition = z.infer; export const MCPServerConfigSchema = z.object({ command: z.string(), args: z.array(z.string()).optional(), - env: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), transport: z.enum(['stdio', 'http']).optional(), }); export type MCPServerConfig = z.infer; @@ -103,14 +114,14 @@ export const ToolParameterSchema: z.ZodType = z.lazy(() => description: z.string().optional(), enum: z.array(z.string()).optional(), items: ToolParameterSchema.optional(), - properties: z.record(ToolParameterSchema).optional(), + properties: z.record(z.string(), ToolParameterSchema).optional(), required: z.array(z.string()).optional(), }) ); export const ToolParameterSchemaMapSchema = z.object({ type: z.literal('object'), - properties: z.record(ToolParameterSchema), + properties: z.record(z.string(), ToolParameterSchema), required: z.array(z.string()).optional(), }); @@ -204,7 +215,7 @@ export type CommandHookHandler = z.infer; export const HttpHandlerSchema = z.object({ type: z.literal('http'), url: z.string(), - headers: z.record(z.string()).optional(), + headers: z.record(z.string(), z.string()).optional(), }); export type HttpHookHandler = z.infer; @@ -296,7 +307,7 @@ export const PluginManifestSchema = z.object({ displayName: z.string().optional(), author: z.union([ z.string(), - z.object({ name: z.string(), email: z.string().optional(), url: z.string().optional() }), + z.object({ name: z.string(), email: z.email().optional(), url: z.url().optional() }), ]).optional(), homepage: z.string().optional(), repository: z.string().optional(), @@ -308,9 +319,9 @@ export const PluginManifestSchema = z.object({ hooks: UniversalHooksSchema.optional(), commands: z.array(CommandSchema).optional(), agents: z.array(AgentDefinitionSchema).optional(), - mcpServers: z.record(MCPServerConfigSchema).optional(), - userConfig: z.record(UserConfigOptionSchema).optional(), - metadata: z.record(z.unknown()).optional(), + mcpServers: z.record(z.string(), MCPServerConfigSchema).optional(), + userConfig: z.record(z.string(), UserConfigOptionSchema).optional(), + metadata: z.record(z.string(), z.unknown()).optional(), capabilities: z.array(z.string()).optional(), targets: z.array(TargetPlatformSchema).optional(), diff --git a/packages/core/package.json b/packages/core/package.json index a357cb9..1bf5b1d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -45,12 +45,13 @@ "dependencies": { "@agentplugins/contract": "workspace:*", "@agentplugins/compile": "workspace:*", + "@agentplugins/pipeline": "workspace:*", "@agentplugins/store": "workspace:*", - "zod": "^3.25.76" + "zod": "catalog:" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/core/src/adapter.ts b/packages/core/src/adapter.ts index 2ac9f1d..89fef42 100644 --- a/packages/core/src/adapter.ts +++ b/packages/core/src/adapter.ts @@ -34,6 +34,7 @@ export { loadAllAdapters, getRegisteredPlatforms, registerBuiltinAdapters, + getRegistryApp, } from './registry.js'; // Hook Wrapper Generation @@ -48,6 +49,8 @@ export type { WrapperOptions } from './hook-wrapper.js'; export { extractCompiledHooks, getEmitter, + registerEmitter, + getRegisteredEmitters, TypeScriptEmitter, JavaScriptEmitter, GoEmitter, diff --git a/packages/core/src/codegen.ts b/packages/core/src/codegen.ts index 6ce3bf7..1759938 100644 --- a/packages/core/src/codegen.ts +++ b/packages/core/src/codegen.ts @@ -212,16 +212,24 @@ export const GoEmitter: CodeEmitter = { }, }; -const EMITTERS: Record = { - typescript: TypeScriptEmitter, - javascript: JavaScriptEmitter, - go: GoEmitter, -}; +const EMITTERS: Map = new Map([ + ['typescript', TypeScriptEmitter], + ['javascript', JavaScriptEmitter], + ['go', GoEmitter], +]); + +export function registerEmitter(emitter: CodeEmitter): void { + EMITTERS.set(emitter.language, emitter); +} -export function getEmitter(language: EmitLanguage): CodeEmitter { - const emitter = EMITTERS[language]; +export function getEmitter(language: EmitLanguage | string): CodeEmitter { + const emitter = EMITTERS.get(language); if (!emitter) { throw new Error(`Unknown emit language: ${String(language)}`); } return emitter; } + +export function getRegisteredEmitters(): ReadonlyMap { + return EMITTERS; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 382d6c0..d218129 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -71,6 +71,8 @@ export { getDetectedAgents, normalizeSource, extractRepoName, + parseSubdir, + parseBranch, initStore, cloneRepo, pullRepo, @@ -103,6 +105,8 @@ export { runSetupCommand, readSetupRecord, writeSetupRecord, + securityPlugin, + SECURITY_META_KEYS, } from '@agentplugins/store'; export type { AgentPathEntry, @@ -122,10 +126,35 @@ export type { RunSetupResult, } from '@agentplugins/store'; -// ─── definePlugin helper ────────────────────────────────────────────────────── +// ─── definePlugin / defineConfig ───────────────────────────────────────────── import type { PluginManifest } from '@agentplugins/contract'; +import type { Plugin } from '@agentplugins/pipeline'; +export type { Plugin } from '@agentplugins/pipeline'; + +export interface AgentPluginsConfig { + /** The universal plugin manifest. */ + manifest: PluginManifest; + /** + * Pipeline plugins that contribute adapters, lint rules, emitters, + * and lifecycle middleware. Registered before the builtin adapters so + * they can override or extend any stage. + */ + plugins?: Plugin[]; + /** + * Override the target list for this build. Falls back to + * `manifest.targets` and then to all built-in targets. + */ + targets?: string[]; +} + +/** Identity helper — provides TypeScript inference for the manifest. */ export function definePlugin(manifest: PluginManifest): PluginManifest { return manifest; } + +/** Power-user config with plugins and target overrides. Backward-compatible. */ +export function defineConfig(config: AgentPluginsConfig): AgentPluginsConfig { + return config; +} diff --git a/packages/core/src/registry.ts b/packages/core/src/registry.ts index b2b724d..0d3219a 100644 --- a/packages/core/src/registry.ts +++ b/packages/core/src/registry.ts @@ -1,89 +1,100 @@ /** * AgentPlugins Adapter Registry * - * Central registry for all platform adapters. - * Adapters register themselves; the CLI and build system look them up here. + * Backed by the pipeline App. Each registered adapter becomes a Plugin + * on the shared App instance, fixing the stale `createAdapter`-only bug + * in the previous implementation. */ +import { createApp } from '@agentplugins/pipeline'; +import type { App } from '@agentplugins/pipeline'; import type { PlatformAdapter, TargetPlatform } from './types.js'; -const adapterMap = new Map Promise>(); +const _app = createApp(); + +const _asyncFactories = new Map Promise>(); + +/** Access the shared pipeline App that backs this registry. */ +export function getRegistryApp(): App { + return _app; +} + +const BUILTIN_ADAPTER_SPECS: Array<{ platform: TargetPlatform; pkg: string; exportName: string }> = [ + { platform: 'claude', pkg: '@agentplugins/adapter-claude', exportName: 'createClaudeAdapter' }, + { platform: 'codex', pkg: '@agentplugins/adapter-codex', exportName: 'createCodexAdapter' }, + { platform: 'copilot', pkg: '@agentplugins/adapter-copilot', exportName: 'createCopilotAdapter' }, + { platform: 'gemini', pkg: '@agentplugins/adapter-gemini', exportName: 'createGeminiAdapter' }, + { platform: 'kimi', pkg: '@agentplugins/adapter-kimi', exportName: 'createKimiAdapter' }, + { platform: 'opencode', pkg: '@agentplugins/adapter-opencode', exportName: 'createOpenCodeAdapter' }, + { platform: 'pimono', pkg: '@agentplugins/adapter-pimono', exportName: 'createPiMonoAdapter' }, +]; /** * Register a platform adapter factory. - * Called by each adapter package during initialization. + * The factory is called immediately and the adapter is registered synchronously. */ export function registerAdapter( platform: TargetPlatform, factory: () => Promise ): void { - adapterMap.set(platform, factory); + _asyncFactories.set(platform, factory); + factory() + .then(adapter => _app.use({ name: platform, adapter })) + .catch(() => { + // adapter init failed — do not register + }); } -/** - * Check if an adapter is registered for a platform. - */ export function hasAdapter(platform: TargetPlatform): boolean { - return adapterMap.has(platform); + return _app.adapters.has(platform) || _asyncFactories.has(platform); } -/** - * Load a platform adapter asynchronously. - */ export async function loadAdapter(platform: TargetPlatform): Promise { - const factory = adapterMap.get(platform); - if (!factory) { - throw new Error( - `No adapter registered for platform "${platform}". ` + - `Install the corresponding adapter package: npm install @agentplugins/adapter-${platform}` - ); + const adapter = _app.adapters.get(platform); + if (adapter) return adapter; + + const factory = _asyncFactories.get(platform); + if (factory) { + const resolved = await factory(); + _app.use({ name: platform, adapter: resolved }); + return resolved; } - return factory(); + + throw new Error( + `No adapter registered for platform "${platform}". ` + + `Install the corresponding adapter package: npm install @agentplugins/adapter-${platform}` + ); } -/** - * Load all registered adapters. - */ export async function loadAllAdapters(): Promise> { - const result = new Map(); - for (const [platform, factory] of adapterMap) { - result.set(platform, await factory()); + for (const [platform, factory] of _asyncFactories) { + if (!_app.adapters.has(platform)) { + try { + const adapter = await factory(); + _app.use({ name: platform, adapter }); + } catch { + // skip failed adapters + } + } } - return result; + return new Map(_app.adapters) as Map; } -/** - * Get list of registered platforms. - */ export function getRegisteredPlatforms(): TargetPlatform[] { - return Array.from(adapterMap.keys()); + return Array.from(_app.adapters.keys()) as TargetPlatform[]; } -/** - * Import and register all built-in adapters. - * This is a convenience function that auto-registers all official adapters. - */ export async function registerBuiltinAdapters(): Promise { - // Dynamic imports to avoid loading adapters that aren't installed - const builtinAdapters: { platform: TargetPlatform; pkg: string }[] = [ - { platform: 'claude', pkg: '@agentplugins/adapter-claude' }, - { platform: 'codex', pkg: '@agentplugins/adapter-codex' }, - { platform: 'copilot', pkg: '@agentplugins/adapter-copilot' }, - { platform: 'gemini', pkg: '@agentplugins/adapter-gemini' }, - { platform: 'kimi', pkg: '@agentplugins/adapter-kimi' }, - { platform: 'opencode', pkg: '@agentplugins/adapter-opencode' }, - { platform: 'pimono', pkg: '@agentplugins/adapter-pimono' }, - ]; - - for (const { platform, pkg } of builtinAdapters) { + for (const { platform, pkg, exportName } of BUILTIN_ADAPTER_SPECS) { try { - // Use dynamic import that works in both ESM and bundled contexts - const mod = await import(/* @vite-ignore */ pkg); - if (mod.createAdapter) { - registerAdapter(platform, mod.createAdapter); + // @ts-ignore — dynamic import; package may not be installed + const mod = await import(pkg); + const factory = mod[exportName] as (() => PlatformAdapter) | undefined; + if (typeof factory === 'function') { + _app.use({ name: platform, adapter: factory() }); } } catch { - // Adapter package not installed — that's fine, skip it + // Adapter package not installed — skip silently } } } diff --git a/packages/core/src/validation.ts b/packages/core/src/validation.ts index 737ad29..a2083fb 100644 --- a/packages/core/src/validation.ts +++ b/packages/core/src/validation.ts @@ -1,503 +1,6 @@ -/** - * AgentPlugins Validation Layer - * - * Catches cross-platform issues before compilation. - * Provides platform-specific and universal validators. - */ - -import { - type PluginManifest, - type ValidationIssue, - type UniversalHookName, - type HookHandler, - UNIVERSAL_HOOK_NAMES, - ALL_TARGETS, - type TargetPlatform, - Severity, -} from './types.js'; - -// ─── Universal Validators ───────────────────────────────────────────────────── - -const KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/; -const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; - -export function validateUniversal(plugin: PluginManifest): ValidationIssue[] { - const issues: ValidationIssue[] = []; - - // ─── name ───────────────────────────────────────────────────────────────── - if (!plugin.name) { - issues.push({ severity: Severity.ERROR, field: 'name', message: 'Plugin name is required' }); - } else { - if (!KEBAB_CASE_RE.test(plugin.name)) { - issues.push({ - severity: Severity.ERROR, - field: 'name', - message: `Plugin name "${plugin.name}" must be kebab-case (lowercase letters, numbers, hyphens only)`, - suggestion: `Rename to: ${toKebabCase(plugin.name)}`, - }); - } - if (plugin.name.length > 64) { - issues.push({ - severity: Severity.ERROR, - field: 'name', - message: `Plugin name "${plugin.name}" is ${plugin.name.length} chars (max 64)`, - }); - } - } - - // ─── version ────────────────────────────────────────────────────────────── - if (!plugin.version) { - issues.push({ severity: Severity.ERROR, field: 'version', message: 'Plugin version is required' }); - } else if (!SEMVER_RE.test(plugin.version)) { - issues.push({ - severity: Severity.ERROR, - field: 'version', - message: `Version "${plugin.version}" is not valid semantic versioning`, - suggestion: 'Use format like "1.0.0"', - }); - } - - // ─── description ────────────────────────────────────────────────────────── - if (!plugin.description) { - issues.push({ severity: Severity.WARNING, field: 'description', message: 'Description is recommended for discovery' }); - } else if (plugin.description.length > 1024) { - issues.push({ - severity: Severity.WARNING, - field: 'description', - message: `Description is ${plugin.description.length} chars (some platforms truncate at 1024)`, - }); - } - - // ─── targets ────────────────────────────────────────────────────────────── - if (plugin.targets) { - for (const target of plugin.targets) { - if (!ALL_TARGETS.includes(target)) { - issues.push({ - severity: Severity.ERROR, - field: 'targets', - message: `Unknown target platform: "${target}"`, - suggestion: `Supported: ${ALL_TARGETS.join(', ')}`, - }); - } - } - } - - // ─── hooks ──────────────────────────────────────────────────────────────── - if (plugin.hooks) { - for (const [name, def] of Object.entries(plugin.hooks)) { - const hookName = name as UniversalHookName; - if (!UNIVERSAL_HOOK_NAMES.includes(hookName)) { - issues.push({ - severity: Severity.WARNING, - field: `hooks.${name}`, - message: `Hook "${name}" is not a universal hook — it will be ignored by some platforms`, - }); - continue; - } - if (def) { - issues.push(...validateHandler(hookName, def.handler)); - } - } - } - - // ─── skills ─────────────────────────────────────────────────────────────── - if (plugin.skills) { - for (let i = 0; i < plugin.skills.length; i++) { - const skill = plugin.skills[i]; - if (!skill.name) { - issues.push({ severity: Severity.ERROR, field: `skills[${i}].name`, message: 'Skill name is required' }); - } else if (!KEBAB_CASE_RE.test(skill.name)) { - issues.push({ - severity: Severity.WARNING, - field: `skills[${i}].name`, - message: `Skill name "${skill.name}" should be kebab-case`, - suggestion: `Rename to: ${toKebabCase(skill.name)}`, - }); - } - if (!skill.description) { - issues.push({ severity: Severity.WARNING, field: `skills[${i}].description`, message: 'Skill description is recommended' }); - } - if (!skill.content && !skill.filePath) { - issues.push({ - severity: Severity.ERROR, - field: `skills[${i}]`, - message: 'Skill must have either "content" or "filePath"', - }); - } - } - } - - // ─── dependencies ──────────────────────────────────────────────────────── - if (plugin.dependencies) { - for (let i = 0; i < plugin.dependencies.length; i++) { - const dep = plugin.dependencies[i]; - if (dep.type === 'npm') { - if (!dep.name) { - issues.push({ severity: Severity.ERROR, field: `dependencies[${i}].name`, message: 'npm dependency must have a "name"' }); - } - if (!dep.version) { - issues.push({ severity: Severity.WARNING, field: `dependencies[${i}].version`, message: `npm dependency "${dep.name ?? i}" has no version pinned — resolution is implicit` }); - } - } else if (dep.type === 'binary') { - if (!dep.name) { - issues.push({ severity: Severity.ERROR, field: `dependencies[${i}].name`, message: 'binary dependency must have a "name"' }); - } - } - } - } - - // ─── sidecar ────────────────────────────────────────────────────────────── - if (plugin.sidecar) { - if (!plugin.sidecar.command) { - issues.push({ severity: Severity.WARNING, field: 'sidecar.command', message: 'sidecar command is empty' }); - } else if (plugin.sidecar.port !== undefined) { - if (plugin.sidecar.port < 0 || plugin.sidecar.port > 65535) { - issues.push({ - severity: Severity.ERROR, - field: 'sidecar.port', - message: `sidecar port ${plugin.sidecar.port} is out of range (0-65535)`, - }); - } - } - } - - // ─── integrity ──────────────────────────────────────────────────────────── - if (plugin.integrity !== undefined) { - if (!/^sha256:[a-f0-9]{64}$/i.test(plugin.integrity)) { - issues.push({ - severity: Severity.ERROR, - field: 'integrity', - message: 'integrity must be in the form "sha256:<64 hex chars>"', - suggestion: 'Compute with: shasum -a 256 | awk \'{print "sha256:" $1}\'', - }); - } - } - - // ─── tools ──────────────────────────────────────────────────────────────── - if (plugin.tools) { - for (let i = 0; i < plugin.tools.length; i++) { - const tool = plugin.tools[i]; - if (!tool.name) { - issues.push({ severity: Severity.ERROR, field: `tools[${i}].name`, message: 'Tool name is required' }); - } - if (!tool.description) { - issues.push({ severity: Severity.WARNING, field: `tools[${i}].description`, message: 'Tool description is recommended' }); - } - // Tool handlers are runtime functions, not HookHandler objects - // This check only applies to hooks, not tools - if ((tool as any).handlerType === 'inline' && (tool as any).handlerImpl === undefined) { - issues.push({ - severity: Severity.WARNING, - field: `tools[${i}].handler`, - message: 'Tool has no handler — will be a no-op on platforms without native tool support', - }); - } - } - } - - return issues; -} - -function validateHandler(hookName: string, handler: HookHandler): ValidationIssue[] { - const issues: ValidationIssue[] = []; - - switch (handler.type) { - case 'command': { - if (!handler.command) { - issues.push({ severity: Severity.ERROR, field: `hooks.${hookName}.command`, message: 'Command string is required' }); - } - // Check for dangerous patterns - if (handler.command?.includes('rm -rf /')) { - issues.push({ severity: Severity.WARNING, field: `hooks.${hookName}.command`, message: 'Command contains dangerous pattern "rm -rf /"' }); - } - break; - } - case 'http': { - if (!handler.url) { - issues.push({ severity: Severity.ERROR, field: `hooks.${hookName}.url`, message: 'HTTP URL is required' }); - } else { - // Basic URL validation using pattern check - const urlPattern = /^https?:\/\/.+/; - if (!urlPattern.test(handler.url)) { - issues.push({ severity: Severity.ERROR, field: `hooks.${hookName}.url`, message: `Invalid URL: ${handler.url}` }); - } - } - break; - } - case 'inline': { - if (typeof handler.handler !== 'function') { - issues.push({ severity: Severity.ERROR, field: `hooks.${hookName}.handler`, message: 'Inline handler must be a function' }); - } - break; - } - default: { - issues.push({ severity: Severity.ERROR, field: `hooks.${hookName}.type`, message: `Unknown handler type` }); - } - } - - return issues; -} - -// ─── Platform-Specific Validation Helpers ──────────────────────────────────── - -export interface PlatformConstraints { - maxNameLength: number; - supportsHttpHandler: boolean; - supportsInlineHandler: boolean; - supportsPromptHandler: boolean; - supportsMCPToolHandler: boolean; - /** True when the adapter emits native tool definitions from plugin.tools[]. */ - supportsNativeTools: boolean; - /** True when the platform can consume mcpServers as a tool delivery mechanism. */ - supportsMcpServersAsTools: boolean; - maxDescriptionLength: number; - supportedHooks: readonly UniversalHookName[]; - manifestPath: string; - requiresStrictValidation: boolean; - notes: string[]; -} - -export function getPlatformConstraints(platform: TargetPlatform): PlatformConstraints { - const base: Record = { - claude: { - maxNameLength: 64, - supportsHttpHandler: true, - supportsInlineHandler: false, - supportsPromptHandler: true, - supportsMCPToolHandler: true, - supportsNativeTools: false, - supportsMcpServersAsTools: true, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'sessionEnd', 'userPromptSubmit', 'userPromptExpansion', - 'preToolUse', 'postToolUse', 'postToolUseFailure', 'permissionRequest', - 'permissionDenied', 'subagentStart', 'subagentStop', 'preCompact', - 'postCompact', 'stop', 'stopFailure', 'notification', 'fileChanged', - 'cwdChanged', 'setup', - ], - manifestPath: '.claude-plugin/plugin.json', - requiresStrictValidation: false, - notes: ['5 handler types: command, http, mcp_tool, prompt, agent', 'Use mcpServers for tools — plugin.tools[] is not natively emitted'], - }, - codex: { - maxNameLength: 64, - supportsHttpHandler: false, - supportsInlineHandler: true, - supportsPromptHandler: false, - supportsMCPToolHandler: false, - supportsNativeTools: false, - supportsMcpServersAsTools: true, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'subagentStart', 'preToolUse', 'permissionRequest', - 'postToolUse', 'preCompact', 'postCompact', 'userPromptSubmit', - 'subagentStop', 'stop', - ], - manifestPath: '.codex-plugin/plugin.json', - requiresStrictValidation: false, - notes: ['Inline handlers auto-wrapped as Node.js command scripts (v0.4.0)', 'Use mcpServers for tools — plugin.tools[] is not natively emitted'], - }, - copilot: { - maxNameLength: 64, - supportsHttpHandler: true, - supportsInlineHandler: false, - supportsPromptHandler: true, - supportsMCPToolHandler: false, - supportsNativeTools: false, - supportsMcpServersAsTools: false, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'sessionEnd', 'userPromptSubmit', 'preToolUse', - 'postToolUse', 'postToolUseFailure', 'subagentStart', 'subagentStop', - 'stop', 'preCompact', 'permissionRequest', 'notification', - ], - manifestPath: 'plugin.json', - requiresStrictValidation: true, - notes: ['preToolUse is fail-closed: errors/timeouts deny tool calls', 'No native tool or MCP support'], - }, - gemini: { - maxNameLength: 64, - supportsHttpHandler: false, - supportsInlineHandler: false, - supportsPromptHandler: false, - supportsMCPToolHandler: false, - supportsNativeTools: false, - supportsMcpServersAsTools: false, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'preToolUse', 'postToolUse', 'preCompact', - 'notification', 'userPromptSubmit', 'subagentStart', 'subagentStop', - 'stop', - ], - manifestPath: 'gemini-extension.json', - requiresStrictValidation: true, - notes: ['Uses exit codes: 0=success, 2=block, other=warning', 'No native tool or MCP support'], - }, - kimi: { - maxNameLength: 64, - supportsHttpHandler: true, - supportsInlineHandler: true, - supportsPromptHandler: false, - supportsMCPToolHandler: false, - supportsNativeTools: false, - supportsMcpServersAsTools: false, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'userPromptSubmit', 'preToolUse', 'permissionRequest', - 'notification', 'stop', - ], - manifestPath: 'kimi.plugin.json', - requiresStrictValidation: true, - notes: ['Inline handlers auto-wrapped as Node.js command scripts (v0.4.0)', 'Hooks are fail-open (not fail-closed like Copilot)', 'No native tool or MCP support'], - }, - opencode: { - maxNameLength: 64, - supportsHttpHandler: false, - supportsInlineHandler: true, - supportsPromptHandler: false, - supportsMCPToolHandler: false, - supportsNativeTools: true, - supportsMcpServersAsTools: true, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'sessionEnd', 'preToolUse', 'postToolUse', - 'permissionRequest', 'notification', 'preCompact', 'stop', - ], - manifestPath: 'package.json', // Uses npm package.json + opencode.json - requiresStrictValidation: false, - notes: ['TypeScript-native, inline handlers only. Bun runtime.', 'Supports both plugin.tools[] and mcpServers'], - }, - pimono: { - maxNameLength: 64, - supportsHttpHandler: false, - supportsInlineHandler: true, - supportsPromptHandler: false, - supportsMCPToolHandler: false, - supportsNativeTools: true, - supportsMcpServersAsTools: true, - maxDescriptionLength: 1024, - supportedHooks: [ - 'sessionStart', 'sessionEnd', 'userPromptSubmit', 'preToolUse', - 'postToolUse', 'permissionRequest', 'subagentStart', 'subagentStop', - 'preCompact', 'postCompact', 'stop', 'stopFailure', 'notification', - 'setup', - ], - manifestPath: 'package.json', // Uses package.json with "pi" key - requiresStrictValidation: false, - notes: ['TypeScript-native, inline handlers only. Uses ExtensionAPI pattern.', 'Supports both plugin.tools[] and mcpServers'], - }, - }; - - return base[platform]; -} - -export function validateForPlatform( - plugin: PluginManifest, - platform: TargetPlatform -): ValidationIssue[] { - const constraints = getPlatformConstraints(platform); - const issues: ValidationIssue[] = []; - - // Check unsupported hooks - if (plugin.hooks) { - for (const [name, def] of Object.entries(plugin.hooks)) { - const hookName = name as UniversalHookName; - if (!def) continue; - - if (!constraints.supportedHooks.includes(hookName)) { - issues.push({ - severity: Severity.WARNING, - field: `hooks.${hookName}`, - message: `"${hookName}" is not supported by ${platform} — this hook will be ignored`, - suggestion: findNearestEquivalent(hookName, constraints.supportedHooks), - }); - } - - // Check handler type compatibility - const handler = def.handler; - if (handler.type === 'http' && !constraints.supportsHttpHandler) { - issues.push({ - severity: Severity.ERROR, - field: `hooks.${hookName}.handler`, - message: `HTTP handlers are not supported by ${platform}`, - suggestion: 'Use "command" or "inline" handler type instead', - }); - } - if (handler.type === 'inline' && !constraints.supportsInlineHandler) { - issues.push({ - severity: Severity.INFO, - field: `hooks.${hookName}.handler`, - message: `Inline handlers on ${platform} will be auto-wrapped as command scripts`, - }); - } - } - } - - // ─── tools[] cross-harness compatibility ──────────────────────────────────── - if (plugin.tools && plugin.tools.length > 0 && !constraints.supportsNativeTools) { - const suggestion = constraints.supportsMcpServersAsTools - ? `Use "mcpServers" instead — it is the recommended universal tool mechanism for ${platform}. See /guide/mcp-servers.` - : `${platform} does not support native tools or mcpServers. Tools in this manifest will be ignored on ${platform}.`; - issues.push({ - severity: Severity.WARNING, - field: 'tools', - message: - `"tools[]" is not natively emitted by the ${platform} adapter. ` + suggestion, - suggestion, - }); - } - - // Check name length - if (plugin.name && plugin.name.length > constraints.maxNameLength) { - issues.push({ - severity: Severity.ERROR, - field: 'name', - message: `Name "${plugin.name}" (${plugin.name.length} chars) exceeds ${platform} limit of ${constraints.maxNameLength}`, - }); - } - - // Check description length - if (plugin.description && plugin.description.length > constraints.maxDescriptionLength) { - issues.push({ - severity: Severity.WARNING, - field: 'description', - message: `Description may be truncated on ${platform} (max ${constraints.maxDescriptionLength} chars)`, - }); - } - - return issues; -} - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -function toKebabCase(str: string): string { - return str - .replace(/([a-z])([A-Z])/g, '$1-$2') - .replace(/[\s_]+/g, '-') - .replace(/[^a-zA-Z0-9-]/g, '') - .toLowerCase() - .replace(/^-+|-+$/g, '') - .replace(/-+/g, '-'); -} - -function findNearestEquivalent( - hook: UniversalHookName, - supported: readonly UniversalHookName[] -): string | undefined { - // Map of known equivalents - const equivalents: Partial> = { - userPromptExpansion: 'userPromptSubmit', - permissionDenied: 'permissionRequest', - postToolUseFailure: 'postToolUse', - setup: 'sessionStart', - cwdChanged: 'fileChanged', - stopFailure: 'stop', - }; - - const equiv = equivalents[hook]; - if (equiv && supported.includes(equiv)) { - return `Use "${equiv}" instead, which is supported by this platform`; - } - - return undefined; -} +export { + validateUniversal, + validateForPlatform, + getPlatformConstraints, +} from '@agentplugins/compile'; +export type { PlatformConstraints } from '@agentplugins/compile'; diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 62433ee..6a36b72 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ - include: ['__tests__/**/*.test.ts'], - exclude: ['__tests__/adapter-contract.test.ts'], - environment: 'node', + test: { + include: ['__tests__/**/*.test.ts'], + exclude: ['__tests__/adapter-contract.test.ts'], + environment: 'node', + }, }); diff --git a/packages/ingest/package.json b/packages/ingest/package.json index 92f468b..2d2d124 100644 --- a/packages/ingest/package.json +++ b/packages/ingest/package.json @@ -44,8 +44,8 @@ "gray-matter": "^4.0.3" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 7ad81c9..4b55525 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -47,12 +47,11 @@ "@agentplugins/ingest": "workspace:*", "@agentplugins/schema": "workspace:*", "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.2" + "zod": "catalog:" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/migrate/src/tools/_helpers.ts b/packages/migrate/src/tools/_helpers.ts index 082cf4b..9461269 100644 --- a/packages/migrate/src/tools/_helpers.ts +++ b/packages/migrate/src/tools/_helpers.ts @@ -3,7 +3,6 @@ */ import { z, type ZodRawShape } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; import type { ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'; export interface McpTool { @@ -25,7 +24,7 @@ export function defineTool( * The MCP SDK accepts zod shapes directly so this is mostly used in tests. */ export function describeSchema(shape: ZodRawShape): Record { - return zodToJsonSchema(z.object(shape)) as Record; + return z.toJSONSchema(z.object(shape), { target: 'draft-07' }) as Record; } export { z }; diff --git a/packages/migrate/src/tools/diff-manifest.ts b/packages/migrate/src/tools/diff-manifest.ts index 8e60e65..3f1ff3f 100644 --- a/packages/migrate/src/tools/diff-manifest.ts +++ b/packages/migrate/src/tools/diff-manifest.ts @@ -10,8 +10,8 @@ import { defineTool, z } from './_helpers.js'; export const diffManifestTool = defineTool( { - before: z.record(z.unknown()).describe('The original manifest (or any baseline)'), - after: z.record(z.unknown()).describe('The generated manifest'), + before: z.record(z.string(), z.unknown()).describe('The original manifest (or any baseline)'), + after: z.record(z.string(), z.unknown()).describe('The generated manifest'), }, async ({ before, after }) => { const keys = new Set([...Object.keys(before), ...Object.keys(after)]); diff --git a/packages/migrate/src/tools/write-manifest.ts b/packages/migrate/src/tools/write-manifest.ts index 9f6c609..da50e58 100644 --- a/packages/migrate/src/tools/write-manifest.ts +++ b/packages/migrate/src/tools/write-manifest.ts @@ -14,7 +14,7 @@ import { validateManifest } from '@agentplugins/schema'; export const writeManifestTool = defineTool( { destination: z.string().describe('Absolute path to write the manifest to'), - manifest: z.record(z.unknown()).describe('The manifest object to write'), + manifest: z.record(z.string(), z.unknown()).describe('The manifest object to write'), overwrite: z.boolean().default(false).describe('Overwrite an existing file'), }, async ({ destination, manifest, overwrite }) => { diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json new file mode 100644 index 0000000..6496cc4 --- /dev/null +++ b/packages/pipeline/package.json @@ -0,0 +1,43 @@ +{ + "name": "@agentplugins/pipeline", + "version": "0.5.0", + "description": "AgentPlugins middleware kernel — composable plugin bus for the build and install pipelines", + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, + "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } + } + }, + "scripts": { + "build": "tsdown src/index.ts --format cjs,esm --dts", + "dev": "tsdown src/index.ts --format cjs,esm --dts --watch", + "typecheck": "tsc --noEmit", + "test": "vitest run --passWithNoTests" + }, + "dependencies": { + "@agentplugins/contract": "workspace:*" + }, + "devDependencies": { + "@types/node": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "files": [ + "dist", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/sigilco/agentplugins.git", + "directory": "packages/pipeline" + }, + "homepage": "https://agentplugins.pages.dev/" +} diff --git a/packages/pipeline/src/app.ts b/packages/pipeline/src/app.ts new file mode 100644 index 0000000..a0cc10c --- /dev/null +++ b/packages/pipeline/src/app.ts @@ -0,0 +1,231 @@ +import type { + Plugin, + App, + Middleware, + BuildCtx, + TargetCtx, + InstallCtx, + LintRule, + CodeEmitter, +} from './types.js'; +import type { PlatformAdapter } from '@agentplugins/contract'; + +export class AbortError extends Error { + constructor(reason?: string) { + super(reason ?? 'Pipeline aborted'); + this.name = 'AbortError'; + } +} + +async function runChain( + chain: Middleware[], + ctx: Ctx +): Promise { + let i = 0; + const next = async (): Promise => { + if (i < chain.length) { + await chain[i++](ctx, next); + } + }; + await next(); +} + +class PipelineApp implements App { + private readonly plugins: Plugin[] = []; + private readonly _adapters = new Map(); + private readonly _lintRules: LintRule[] = []; + private readonly _emitters = new Map(); + + get adapters(): ReadonlyMap { + return this._adapters; + } + + get lintRules(): readonly LintRule[] { + return this._lintRules; + } + + get emitters(): ReadonlyMap { + return this._emitters; + } + + use(plugin: Plugin): App { + this.plugins.push(plugin); + + if (plugin.adapter) { + this._adapters.set(plugin.adapter.name, plugin.adapter); + } + + if (plugin.lintRules) { + this._lintRules.push(...plugin.lintRules); + } + + if (plugin.emitters) { + for (const [lang, emitter] of Object.entries(plugin.emitters)) { + this._emitters.set(lang, emitter); + } + } + + return this; + } + + async runBuild(ctx: BuildCtx): Promise { + const preValidateChain = this.plugins + .map(p => p.preValidate) + .filter((m): m is Middleware => m != null); + + const transformIRChain = this.plugins + .map(p => p.transformIR) + .filter((m): m is Middleware => m != null); + + try { + await runChain(preValidateChain, ctx); + await runChain(transformIRChain, ctx); + } catch (err) { + if (err instanceof AbortError) throw err; + throw err; + } + } + + async runTarget(ctx: TargetCtx): Promise { + const postEmitChain = this.plugins + .map(p => p.postEmit) + .filter((m): m is Middleware => m != null); + + try { + await runChain(postEmitChain, ctx); + } catch (err) { + if (err instanceof AbortError) throw err; + throw err; + } + } + + async runInstall(ctx: InstallCtx): Promise { + const onInstallChain = this.plugins + .map(p => p.onInstall) + .filter((m): m is Middleware => m != null); + + try { + await runChain(onInstallChain, ctx); + } catch (err) { + if (err instanceof AbortError) throw err; + throw err; + } + } + + async runAudit(ctx: InstallCtx): Promise { + const onAuditChain = this.plugins + .map(p => p.onAudit) + .filter((m): m is Middleware => m != null); + + await runChain(onAuditChain, ctx); + } +} + +export function createApp(): PipelineApp { + return new PipelineApp(); +} + +// ─── Context factories ──────────────────────────────────────────────────────── + +import type { + PluginManifest, + FileOutput, + NativeCopy, + ValidationIssue, +} from '@agentplugins/contract'; + +export function createBuildCtx(options: { + manifest: PluginManifest; + targets: string[]; + outDir?: string; + pluginRoot?: string; +}): BuildCtx { + const issues: ValidationIssue[] = []; + const warnings: string[] = []; + const files = new Map(); + const nativeCopies = new Map(); + const postInstall = new Map(); + + const abort = (reason?: string): never => { + throw new AbortError(reason); + }; + + return { + manifest: options.manifest, + targets: options.targets, + outDir: options.outDir, + pluginRoot: options.pluginRoot, + issues, + warnings, + files, + nativeCopies, + postInstall, + abort, + addIssue(issue) { issues.push(issue); }, + addWarning(msg) { warnings.push(msg); }, + addFile(target, file) { + const list = files.get(target) ?? []; + list.push(file); + files.set(target, list); + }, + addNativeCopy(target, copy) { + const list = nativeCopies.get(target) ?? []; + list.push(copy); + nativeCopies.set(target, list); + }, + addPostInstall(target, step) { + const list = postInstall.get(target) ?? []; + list.push(step); + postInstall.set(target, list); + }, + }; +} + +export function createTargetCtx(options: { + manifest: PluginManifest; + target: string; + pluginRoot?: string; +}): TargetCtx { + const files: FileOutput[] = []; + const warnings: string[] = []; + const nativeCopies: NativeCopy[] = []; + const postInstall: string[] = []; + + const abort = (reason?: string): never => { + throw new AbortError(reason); + }; + + return { + manifest: options.manifest, + target: options.target, + pluginRoot: options.pluginRoot, + files, + warnings, + nativeCopies, + postInstall, + abort, + addFile(file) { files.push(file); }, + addWarning(msg) { warnings.push(msg); }, + addNativeCopy(copy) { nativeCopies.push(copy); }, + addPostInstall(step) { postInstall.push(step); }, + }; +} + +export function createInstallCtx(options: { + pluginName: string; + installDir: string; + manifest: PluginManifest; + meta?: Record; +}): InstallCtx { + const abort = (reason?: string): never => { + throw new AbortError(reason); + }; + + return { + pluginName: options.pluginName, + installDir: options.installDir, + manifest: options.manifest, + meta: options.meta ?? {}, + abort, + }; +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts new file mode 100644 index 0000000..8345b54 --- /dev/null +++ b/packages/pipeline/src/index.ts @@ -0,0 +1,21 @@ +export type { + LintIssue, + LintContext, + LintRule, + CodeEmitter, + Next, + Middleware, + BuildCtx, + TargetCtx, + InstallCtx, + Plugin, + App, +} from './types.js'; + +export { + AbortError, + createApp, + createBuildCtx, + createTargetCtx, + createInstallCtx, +} from './app.js'; diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts new file mode 100644 index 0000000..cb9dbf0 --- /dev/null +++ b/packages/pipeline/src/types.ts @@ -0,0 +1,117 @@ +import type { + PluginManifest, + PlatformAdapter, + FileOutput, + NativeCopy, + ValidationIssue, +} from '@agentplugins/contract'; + +// ─── Opaque extension interfaces ───────────────────────────────────────────── +// Kept structurally compatible with @agentplugins/compile exports. + +export interface LintIssue { + rule: string; + severity: 'error' | 'warning'; + field?: string; + message: string; + suggestion?: string; +} + +export interface LintContext { + manifest: PluginManifest; + inlineHandlerSource?: string[]; +} + +export interface LintRule { + id: string; + description: string; + run: (ctx: LintContext) => LintIssue[]; +} + +export interface CodeEmitter { + readonly language: string; + emit(manifest: PluginManifest, hooks: unknown[]): FileOutput[]; +} + +// ─── Middleware ─────────────────────────────────────────────────────────────── + +export type Next = () => Promise; +export type Middleware = (ctx: Ctx, next: Next) => Promise; + +// ─── Contexts ───────────────────────────────────────────────────────────────── + +export interface BuildCtx { + manifest: PluginManifest; + targets: string[]; + outDir?: string; + pluginRoot?: string; + issues: ValidationIssue[]; + warnings: string[]; + /** Accumulated output files per target. */ + files: Map; + nativeCopies: Map; + postInstall: Map; + abort(reason?: string): never; + addIssue(issue: ValidationIssue): void; + addWarning(message: string): void; + addFile(target: string, file: FileOutput): void; + addNativeCopy(target: string, copy: NativeCopy): void; + addPostInstall(target: string, step: string): void; +} + +export interface TargetCtx { + manifest: PluginManifest; + target: string; + pluginRoot?: string; + files: FileOutput[]; + warnings: string[]; + nativeCopies: NativeCopy[]; + postInstall: string[]; + abort(reason?: string): never; + addFile(file: FileOutput): void; + addWarning(message: string): void; + addNativeCopy(copy: NativeCopy): void; + addPostInstall(step: string): void; +} + +export interface InstallCtx { + pluginName: string; + installDir: string; + manifest: PluginManifest; + meta: Record; + abort(reason?: string): never; +} + +// ─── Plugin ─────────────────────────────────────────────────────────────────── + +export interface Plugin { + readonly name: string; + /** Contributes a compile target. */ + adapter?: PlatformAdapter; + /** Contributes build-time checks. */ + lintRules?: LintRule[]; + /** Contributes code emitters. */ + emitters?: Record; + // Build-side lifecycle + preValidate?: Middleware; + transformIR?: Middleware; + postEmit?: Middleware; + // Install-side lifecycle + onAudit?: Middleware; + onInstall?: Middleware; +} + +// ─── App ───────────────────────────────────────────────────────────────────── + +export interface App { + use(plugin: Plugin): App; + /** Registered adapters, keyed by target name. */ + readonly adapters: ReadonlyMap; + /** All registered lint rules from plugins. */ + readonly lintRules: readonly LintRule[]; + /** All registered emitters from plugins. */ + readonly emitters: ReadonlyMap; + runBuild(ctx: BuildCtx): Promise; + runTarget(ctx: TargetCtx): Promise; + runInstall(ctx: InstallCtx): Promise; +} diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json new file mode 100644 index 0000000..b813d42 --- /dev/null +++ b/packages/pipeline/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/schema/__tests__/schema.test.ts b/packages/schema/__tests__/schema.test.ts index e52a0f1..03b17d3 100644 --- a/packages/schema/__tests__/schema.test.ts +++ b/packages/schema/__tests__/schema.test.ts @@ -14,7 +14,7 @@ describe("@agentplugins/schema", () => { describe("exports", () => { it("exports manifest schema object", () => { expect(manifestSchema).toBeDefined(); - expect(manifestSchema.$id).toContain("agentplugins.dev"); + expect(manifestSchema.$id).toContain("agentplugins.pages.dev"); expect(manifestSchema.type).toBe("object"); }); diff --git a/packages/schema/package.json b/packages/schema/package.json index 5351f65..f315ffd 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -41,11 +41,11 @@ }, "homepage": "https://agentplugins.pages.dev/", "dependencies": { - "ajv": "^8.17.1" + "ajv": "^8.20.0" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/schema/schemas/adapter.schema.json b/packages/schema/schemas/adapter.schema.json index 9e5ebfc..6d7465f 100644 --- a/packages/schema/schemas/adapter.schema.json +++ b/packages/schema/schemas/adapter.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://agentplugins.dev/schema/v1/adapter.json", + "$id": "https://agentplugins.pages.dev/schema/v1/adapter.json", "title": "AgentPlugins Adapter ABI v1", "description": "Contract for the JSON process ABI. An adapter is any executable (binary, script) that reads a JSON manifest on stdin (or via --manifest ), compiles platform-specific output, writes files, and exits 0 (success) or non-zero (failure). Enables any-language adapters without SDK lock-in.", "type": "object", diff --git a/packages/schema/schemas/agent-paths.json b/packages/schema/schemas/agent-paths.json index a158807..d7bb49e 100644 --- a/packages/schema/schemas/agent-paths.json +++ b/packages/schema/schemas/agent-paths.json @@ -1,5 +1,5 @@ { - "$schema": "https://agentplugins.dev/schema/v1/agent-paths.json", + "$schema": "https://agentplugins.pages.dev/schema/v1/agent-paths.json", "version": 1, "description": "Community-maintained registry of well-known agent skill/plugin paths. AgentPlugins scans these to detect installed agents and fan out symlinks from the universal store (~/.agents/plugins/).", "store": { diff --git a/packages/schema/schemas/manifest.schema.json b/packages/schema/schemas/manifest.schema.json index 64b6e8b..93240a0 100644 --- a/packages/schema/schemas/manifest.schema.json +++ b/packages/schema/schemas/manifest.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://agentplugins.dev/schema/v1.json", + "$id": "https://agentplugins.pages.dev/schema/v1.json", "title": "AgentPlugins Manifest v1", - "description": "The public contract for an AgentPlugins plugin. One manifest compiles to any supported agent harness. Include \"$schema\": \"https://agentplugins.dev/schema/v1.json\" in your manifest for editor autocomplete.", + "description": "The public contract for an AgentPlugins plugin. One manifest compiles to any supported agent harness. Include \"$schema\": \"https://agentplugins.pages.dev/schema/v1.json\" in your manifest for editor autocomplete.", "type": "object", "required": ["name", "version", "description"], "additionalProperties": false, diff --git a/packages/security/package.json b/packages/security/package.json index 9a92712..7ece984 100644 --- a/packages/security/package.json +++ b/packages/security/package.json @@ -44,8 +44,8 @@ "@agentplugins/schema": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/store/__tests__/normalizeSource.test.ts b/packages/store/__tests__/normalizeSource.test.ts new file mode 100644 index 0000000..06146d7 --- /dev/null +++ b/packages/store/__tests__/normalizeSource.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect } from 'vitest'; +import { normalizeSource, parseSubdir, parseBranch } from '../src/store.js'; + +describe('normalizeSource', () => { + it('expands shorthand user/repo', () => { + expect(normalizeSource('sigilco/agentplugins-roster')).toBe('https://github.com/sigilco/agentplugins-roster'); + }); + + it('strips /tree/branch/path', () => { + expect(normalizeSource('https://github.com/sigilco/agentplugins-roster/tree/main/plugins/roster')) + .toBe('https://github.com/sigilco/agentplugins-roster'); + }); + + it('strips /blob/branch/path', () => { + expect(normalizeSource('https://github.com/u/r/blob/main/some/file.ts')) + .toBe('https://github.com/u/r'); + }); + + it('converts SSH to HTTPS', () => { + expect(normalizeSource('git@github.com:u/r.git')).toBe('https://github.com/u/r'); + }); + + it('handles deep branch/subdir', () => { + expect(normalizeSource('https://github.com/u/r/tree/feat/a/sub/dir')) + .toBe('https://github.com/u/r'); + }); + + it('strips trailing .git', () => { + expect(normalizeSource('https://github.com/u/r.git')).toBe('https://github.com/u/r'); + }); + + it('strips trailing slash', () => { + expect(normalizeSource('https://github.com/u/r/')).toBe('https://github.com/u/r'); + }); +}); + +describe('parseSubdir', () => { + it('returns undefined for plain repo URL', () => { + expect(parseSubdir('https://github.com/sigilco/agentplugins-roster')).toBeUndefined(); + }); + + it('returns undefined for shorthand', () => { + expect(parseSubdir('sigilco/agentplugins-roster')).toBeUndefined(); + }); + + it('returns undefined for SSH URL', () => { + expect(parseSubdir('git@github.com:u/r.git')).toBeUndefined(); + }); + + it('extracts single-segment subdir', () => { + expect(parseSubdir('https://github.com/sigilco/agentplugins-roster/tree/main/plugins/roster')) + .toBe('plugins/roster'); + }); + + it('extracts subdir after first-segment branch (slash-in-branch is ambiguous; first segment wins)', () => { + expect(parseSubdir('https://github.com/u/r/tree/feat/a/sub/dir')) + .toBe('a/sub/dir'); + }); +}); + +describe('parseBranch', () => { + it('returns undefined for plain repo URL', () => { + expect(parseBranch('https://github.com/sigilco/agentplugins-roster')).toBeUndefined(); + }); + + it('extracts main branch', () => { + expect(parseBranch('https://github.com/sigilco/agentplugins-roster/tree/main/plugins/roster')) + .toBe('main'); + }); + + it('extracts simple branch (no slash in name)', () => { + expect(parseBranch('https://github.com/u/r/tree/develop')) + .toBe('develop'); + }); +}); diff --git a/packages/store/package.json b/packages/store/package.json index b01e1cf..f5e8590 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -40,11 +40,12 @@ "homepage": "https://agentplugins.pages.dev/", "dependencies": { "@agentplugins/compile": "workspace:*", + "@agentplugins/pipeline": "workspace:*", "@agentplugins/security": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.9.3", - "vitest": "^3.1.4" + "@types/node": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 930ef04..8723188 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -9,3 +9,4 @@ export * from './store.js'; export * from './setup.js'; +export * from './plugin.js'; diff --git a/packages/store/src/plugin.ts b/packages/store/src/plugin.ts new file mode 100644 index 0000000..d08140a --- /dev/null +++ b/packages/store/src/plugin.ts @@ -0,0 +1,124 @@ +/** + * Built-in security Plugin for the install pipeline. + * + * Wraps evaluateScriptPolicy (via gateSetupCommand) and integrity checking + * as explicit, reorderable middleware. Registered by default in the install + * pipeline — power users may remove or reorder it via defineConfig. + */ + +import { existsSync } from 'node:fs'; +import type { Plugin, InstallCtx } from '@agentplugins/pipeline'; +import { hashDirectory, verifyIntegrity, evaluateScriptPolicy, DEFAULT_POLICY } from '@agentplugins/security'; +import { resolveSetupCommand, gateSetupCommand } from './setup.js'; + +/** Abort keys written to InstallCtx.meta by securityPlugin middleware. */ +export const SECURITY_META_KEYS = { + integrity: 'security:integrity', + scriptDecision: 'security:scriptDecision', +} as const; + +/** + * Middleware: verifies the install directory against the manifest's pinned + * integrity field. Aborts if the hash does not match. + */ +async function pinnedIntegrityMiddleware(ctx: InstallCtx, next: () => Promise): Promise { + const manifest = ctx.manifest as unknown as Record; + const integrity = manifest['integrity'] as string | undefined; + + if (integrity && integrity.length > 0) { + const { match, reason } = verifyIntegrity(ctx.installDir, integrity); + if (!match) { + ctx.abort(`Integrity check failed: ${reason ?? 'hash mismatch'}`); + } + } + + await next(); +} + +/** + * Middleware: hashes the install directory and stores the result in ctx.meta. + * Does not abort — callers may inspect ctx.meta[SECURITY_META_KEYS.integrity] + * to decide whether to block. + */ +async function integrityMiddleware(ctx: InstallCtx, next: () => Promise): Promise { + if (ctx.installDir && existsSync(ctx.installDir)) { + const hash = hashDirectory(ctx.installDir); + ctx.meta[SECURITY_META_KEYS.integrity] = hash; + } + await next(); +} + +/** + * Middleware: evaluates the setup-script policy for the plugin's setup command. + * Aborts the install if the policy returns 'deny'. + */ +async function scriptPolicyMiddleware(ctx: InstallCtx, next: () => Promise): Promise { + const manifest = ctx.manifest as unknown as Record; + const resolved = resolveSetupCommand(ctx.installDir, manifest); + + if (resolved) { + const gate = gateSetupCommand(resolved.command, ctx.pluginName); + ctx.meta[SECURITY_META_KEYS.scriptDecision] = gate; + + if (gate.decision === 'deny') { + ctx.abort( + `Setup command blocked by policy for "${ctx.pluginName}": ${gate.reasons.join('; ')}` + ); + } + } + + await next(); +} + +/** + * Middleware: evaluates script policy for all dependency lifecycle commands + * declared in the manifest. Aborts if any are denied. + */ +async function dependencyScriptMiddleware(ctx: InstallCtx, next: () => Promise): Promise { + const manifest = ctx.manifest as unknown as Record; + const deps = Array.isArray(manifest.dependencies) ? manifest.dependencies as Array> : []; + + for (const dep of deps) { + if (typeof dep.lifecycle === 'string' && typeof dep.command === 'string') { + const result = evaluateScriptPolicy( + { + dependency: String(dep.name ?? '?'), + phase: dep.lifecycle as 'preinstall' | 'install' | 'postinstall', + command: dep.command, + pluginName: ctx.pluginName, + }, + DEFAULT_POLICY, + ); + + if (result.decision === 'deny') { + ctx.abort( + `Dependency lifecycle script blocked by policy: ${dep.name} (${dep.lifecycle}): ${result.reasons.join('; ')}` + ); + } + } + } + + await next(); +} + +/** + * Built-in security plugin. Provides three onInstall middleware stages: + * 1. integrityMiddleware — hashes the install dir + * 2. scriptPolicyMiddleware — gates the plugin's own setup command + * 3. dependencyScriptMiddleware — gates dependency lifecycle scripts + * + * All three abort (throw AbortError) on a denied decision. + */ +export const securityPlugin: Plugin = { + name: '@agentplugins/security-gate', + + onInstall: async (ctx, next) => { + await pinnedIntegrityMiddleware(ctx, async () => { + await integrityMiddleware(ctx, async () => { + await scriptPolicyMiddleware(ctx, async () => { + await dependencyScriptMiddleware(ctx, next); + }); + }); + }); + }, +}; diff --git a/packages/store/src/store.ts b/packages/store/src/store.ts index 9b7077c..1acc62d 100644 --- a/packages/store/src/store.ts +++ b/packages/store/src/store.ts @@ -73,6 +73,8 @@ export interface PluginMeta { version: string; /** Trust-on-first-use record for the plugin's `setup` command (v0.5.0+). */ setup?: SetupRecord; + /** Relative subdirectory within the store dir where the manifest lives (monorepo installs). */ + subdir?: string; } export interface DetectedAgent { @@ -279,7 +281,8 @@ export function getDetectedAgents(): DetectedAgent[] { /** * Normalize a plugin source URL. - * Accepts: https://github.com/u/r, git@github.com:u/r.git, u/r + * Accepts: https://github.com/u/r, git@github.com:u/r.git, u/r, + * https://github.com/u/r/tree/main/sub/dir */ export function normalizeSource(source: string): string { let url = source.trim(); @@ -295,6 +298,9 @@ export function normalizeSource(source: string): string { url = `https://github.com/${path}`; } + // Strip /tree/{branch}[/{path}] or /blob/{branch}[/{path}] + url = url.replace(/\/(tree|blob)\/[^?#]*/, ''); + // Strip trailing .git if (url.endsWith('.git')) { url = url.slice(0, -4); @@ -313,6 +319,24 @@ export function extractRepoName(source: string): string { return parts[parts.length - 1] || 'unknown-plugin'; } +/** + * Extract the subdirectory path from a GitHub tree URL. + * e.g. "https://github.com/u/r/tree/main/plugins/roster" → "plugins/roster" + */ +export function parseSubdir(source: string): string | undefined { + const m = source.match(/github\.com\/[^/]+\/[^/]+\/tree\/[^/]+\/(.+)/); + return m?.[1]; +} + +/** + * Extract the branch name from a GitHub tree URL. + * e.g. "https://github.com/u/r/tree/main/plugins/roster" → "main" + */ +export function parseBranch(source: string): string | undefined { + const m = source.match(/github\.com\/[^/]+\/[^/]+\/tree\/([^/]+)/); + return m?.[1]; +} + // ─── Store Operations ──────────────────────────────────────────────────────── /** Ensure the store and skills-compat directories exist */ @@ -336,10 +360,11 @@ export function validateCloneUrl(url: string): void { * Returns the commit hash. * Throws on failure. */ -export function cloneRepo(source: string, dest: string): string { +export function cloneRepo(source: string, dest: string, branch?: string): string { const url = normalizeSource(source); validateCloneUrl(url); - execSync(`git clone --depth 1 "${url}" "${dest}"`, { stdio: 'pipe' }); + const branchFlag = branch ? `--branch "${branch}"` : ''; + execSync(`git clone --depth 1 ${branchFlag} "${url}" "${dest}"`, { stdio: 'pipe' }); return execSync('git rev-parse HEAD', { cwd: dest, encoding: 'utf-8' }).trim(); } @@ -463,6 +488,8 @@ export interface InstallOptions { version: string; /** Whether to symlink into detected agents (default: true) */ symlink?: boolean; + /** Subdirectory within the cloned repo where the plugin lives (monorepo installs). */ + subdir?: string; } export interface InstallResult { @@ -501,6 +528,7 @@ export function installPlugin( updatedAt: now, manifestPath: opts.manifestPath, version: opts.version, + ...(opts.subdir ? { subdir: opts.subdir } : {}), }; writeFileSync(getMetaPath(opts.name), JSON.stringify(meta, null, 2), 'utf-8'); @@ -710,8 +738,9 @@ export function updatePlugin(name: string): PluginMeta { // Pull latest const newCommit = pullRepo(storePath); - // Re-find manifest (in case it changed) - const manifestResult = findManifestInDir(storePath); + // Re-find manifest (in case it changed); respect subdir for monorepo installs + const manifestSearchDir = oldMeta.subdir ? join(storePath, oldMeta.subdir) : storePath; + const manifestResult = findManifestInDir(manifestSearchDir); const version = manifestResult?.manifest['version'] as string || oldMeta.version; const manifestPath = manifestResult?.path || oldMeta.manifestPath; diff --git a/plugins/example-custom-adapter/agentplugins.config.ts b/plugins/example-custom-adapter/agentplugins.config.ts new file mode 100644 index 0000000..08105dc --- /dev/null +++ b/plugins/example-custom-adapter/agentplugins.config.ts @@ -0,0 +1,39 @@ +/** + * Example: plugin with a custom adapter for a private harness. + * + * Uses defineConfig instead of definePlugin to wire in the custom adapter + * without publishing to npm. The adapter lives next to this config file. + * + * Run: agentplugins build + * Output: dist/claude/ and dist/my-harness/ + */ + +import { defineConfig } from '@agentplugins/core'; +import { myHarnessAdapter } from './src/my-harness-adapter.js'; + +export default defineConfig({ + manifest: { + name: 'my-custom-plugin', + version: '0.1.0', + description: 'Plugin demonstrating a private custom adapter alongside built-in targets', + + hooks: { + sessionStart: { + handler: { + type: 'command', + command: 'echo "Session started"', + }, + }, + }, + }, + + plugins: [ + { + name: 'my-harness-adapter', + adapter: myHarnessAdapter, + }, + ], + + // Build for Claude (built-in) + my-harness (custom adapter above) + targets: ['claude', 'my-harness'], +}); diff --git a/plugins/example-custom-adapter/package.json b/plugins/example-custom-adapter/package.json new file mode 100644 index 0000000..ab6c54b --- /dev/null +++ b/plugins/example-custom-adapter/package.json @@ -0,0 +1,14 @@ +{ + "name": "agentplugins-example-custom-adapter", + "description": "Example: defineConfig with a private custom adapter", + "type": "module", + "private": true, + "scripts": { + "build": "agentplugins build" + }, + "devDependencies": { + "@agentplugins/cli": "workspace:*", + "@agentplugins/core": "workspace:*", + "typescript": "catalog:" + } +} diff --git a/plugins/example-custom-adapter/src/my-harness-adapter.ts b/plugins/example-custom-adapter/src/my-harness-adapter.ts new file mode 100644 index 0000000..000786b --- /dev/null +++ b/plugins/example-custom-adapter/src/my-harness-adapter.ts @@ -0,0 +1,52 @@ +/** + * Example custom adapter for a fictional "my-harness" platform. + * + * This shows the minimum surface a PlatformAdapter must implement. + * Copy this file into your project and modify it for your target platform. + */ + +import { Severity, type PlatformAdapter, type PluginManifest, type AdapterOutput } from '@agentplugins/core'; + +const MY_HARNESS_TARGET = 'my-harness' as const; + +export const myHarnessAdapter: PlatformAdapter = { + name: MY_HARNESS_TARGET, + displayName: 'My Harness', + supportedHooks: ['sessionStart', 'preToolUse', 'postToolUse'], + supportedHandlers: ['command', 'inline'], + manifestPath: 'my-harness.json', + manifestFormat: 'json', + + validate(plugin: PluginManifest) { + const issues = []; + if (!plugin.name) { + issues.push({ severity: Severity.ERROR, field: 'name', message: 'name is required' }); + } + return issues; + }, + + compile(plugin: PluginManifest): AdapterOutput { + const manifest = { + name: plugin.name, + version: plugin.version, + description: plugin.description, + runtime: 'node', + }; + + return { + files: [ + { + path: 'my-harness.json', + content: JSON.stringify(manifest, null, 2), + }, + { + path: 'index.js', + content: `// ${plugin.name} v${plugin.version} — compiled by AgentPlugins\nexport const name = '${plugin.name}';\n`, + }, + ], + manifest, + warnings: [], + issues: [], + }; + }, +}; diff --git a/plugins/example-custom-adapter/tsconfig.json b/plugins/example-custom-adapter/tsconfig.json new file mode 100644 index 0000000..d085def --- /dev/null +++ b/plugins/example-custom-adapter/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": ".", + "noEmit": true + }, + "include": ["*.ts", "src/**/*.ts"] +} diff --git a/plugins/example-logger/package.json b/plugins/example-logger/package.json index be0d180..5c85867 100644 --- a/plugins/example-logger/package.json +++ b/plugins/example-logger/package.json @@ -9,8 +9,8 @@ "build:core": "cd ../../packages/core && pnpm build" }, "devDependencies": { - "@agentplugins/core": "workspace:*", "@agentplugins/cli": "workspace:*", - "typescript": "^5.5.0" + "@agentplugins/core": "workspace:*", + "typescript": "catalog:" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef69bf4..7f11fc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,19 +4,55 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@clack/prompts': + specifier: ^1.0.0 + version: 1.6.0 + '@logtape/logtape': + specifier: ^2.0.0 + version: 2.2.1 + '@types/node': + specifier: ^22.20.0 + version: 22.20.0 + cleye: + specifier: ^2.0.0 + version: 2.6.0 + jiti: + specifier: ^2.7.0 + version: 2.7.0 + tsdown: + specifier: ^0.22.3 + version: 0.22.3 + typescript: + specifier: ^6.0.0 + version: 6.0.3 + vitest: + specifier: ^4.0.0 + version: 4.1.9 + zod: + specifier: ^4.4.3 + version: 4.4.3 + importers: .: devDependencies: '@changesets/cli': specifier: ^2.31.0 - version: 2.31.0(@types/node@20.19.41) + version: 2.31.0(@types/node@22.20.0) + vite: + specifier: ^6.0.0 + version: 6.4.3(@types/node@22.20.0)(jiti@2.7.0) docs: devDependencies: '@braintree/sanitize-url': specifier: ^7.1.2 version: 7.1.2 + '@types/node': + specifier: 'catalog:' + version: 22.20.0 cytoscape: specifier: ^3.34.0 version: 3.34.0 @@ -31,19 +67,19 @@ importers: version: 4.4.3 mermaid: specifier: ^11.15.0 - version: 11.15.0 + version: 11.16.0 vitepress: specifier: ^1.5.0 - version: 1.6.4(@algolia/client-search@5.55.0)(@types/node@20.19.41)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3) + version: 1.6.4(@algolia/client-search@5.55.0)(@types/node@22.20.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@6.0.3) vitepress-plugin-group-icons: specifier: ^1.7.5 - version: 1.7.5(vite@5.4.21(@types/node@20.19.41)) + version: 1.7.5(vite@5.4.21(@types/node@22.20.0)) vitepress-plugin-llms: specifier: ^1.13.2 version: 1.13.2 vitepress-plugin-mermaid: specifier: ^2.0.17 - version: 2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@20.19.41)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3)) + version: 2.0.17(mermaid@11.16.0)(vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@22.20.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@6.0.3)) packages/adapter-claude: dependencies: @@ -52,14 +88,17 @@ importers: version: link:../core devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + specifier: 'catalog:' + version: 22.20.0 + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.5.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 + vitest: + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/adapter-codex: dependencies: @@ -68,17 +107,17 @@ importers: version: link:../core devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + specifier: 'catalog:' + version: 22.20.0 + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.3.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/adapter-copilot: dependencies: @@ -86,15 +125,15 @@ importers: specifier: workspace:* version: link:../core devDependencies: - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.3.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/adapter-gemini: dependencies: @@ -102,12 +141,12 @@ importers: specifier: workspace:* version: link:../core devDependencies: - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.3.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 packages/adapter-kimi: dependencies: @@ -115,15 +154,15 @@ importers: specifier: workspace:* version: link:../core devDependencies: - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.3.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/adapter-opencode: dependencies: @@ -134,18 +173,18 @@ importers: specifier: workspace:* version: link:../core zod: - specifier: ^3.22.0 - version: 3.25.76 + specifier: 'catalog:' + version: 4.4.3 devDependencies: tsup: specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + version: 8.5.1(jiti@2.7.0)(postcss@8.5.15)(typescript@6.0.3) typescript: - specifier: ^5.3.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/adapter-pimono: dependencies: @@ -156,15 +195,15 @@ importers: specifier: workspace:* version: link:../core devDependencies: - tsup: - specifier: ^8.0.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3) + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) typescript: - specifier: ^5.4.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/cli: dependencies: @@ -201,6 +240,9 @@ importers: '@agentplugins/ingest': specifier: workspace:* version: link:../ingest + '@agentplugins/pipeline': + specifier: workspace:* + version: link:../pipeline '@agentplugins/schema': specifier: workspace:* version: link:../schema @@ -208,62 +250,62 @@ importers: specifier: workspace:* version: link:../security '@clack/prompts': - specifier: ^0.7.0 - version: 0.7.0 - cac: - specifier: ^6.7.14 - version: 6.7.14 - chalk: - specifier: ^5.3.0 - version: 5.6.2 + specifier: 'catalog:' + version: 1.6.0 + '@logtape/logtape': + specifier: 'catalog:' + version: 2.2.1 + cleye: + specifier: 'catalog:' + version: 2.6.0 jiti: - specifier: ^1.21.6 - version: 1.21.7 + specifier: 'catalog:' + version: 2.7.0 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.5.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/compile: dependencies: '@agentplugins/contract': specifier: workspace:* version: link:../contract + '@agentplugins/pipeline': + specifier: workspace:* + version: link:../pipeline devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/contract: dependencies: zod: - specifier: ^3.25.76 - version: 3.25.76 - zod-to-json-schema: - specifier: ^3.24.5 - version: 3.25.2(zod@3.25.76) + specifier: 'catalog:' + version: 4.4.3 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/core: dependencies: @@ -273,22 +315,25 @@ importers: '@agentplugins/contract': specifier: workspace:* version: link:../contract + '@agentplugins/pipeline': + specifier: workspace:* + version: link:../pipeline '@agentplugins/store': specifier: workspace:* version: link:../store zod: - specifier: ^3.25.76 - version: 3.25.76 + specifier: 'catalog:' + version: 4.4.3 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/ingest: dependencies: @@ -300,14 +345,14 @@ importers: version: 4.0.3 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/migrate: dependencies: @@ -322,39 +367,55 @@ importers: version: link:../schema '@modelcontextprotocol/sdk': specifier: ^1.29.0 - version: 1.29.0(zod@3.25.76) + version: 1.29.0(zod@4.4.3) zod: - specifier: ^3.25.76 - version: 3.25.76 - zod-to-json-schema: - specifier: ^3.25.2 - version: 3.25.2(zod@3.25.76) + specifier: 'catalog:' + version: 4.4.3 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) + + packages/pipeline: + dependencies: + '@agentplugins/contract': + specifier: workspace:* + version: link:../contract + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.20.0 + tsdown: + specifier: 'catalog:' + version: 0.22.3(typescript@6.0.3) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vitest: + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/schema: dependencies: ajv: - specifier: ^8.17.1 + specifier: ^8.20.0 version: 8.20.0 devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/security: dependencies: @@ -363,33 +424,48 @@ importers: version: link:../schema devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) packages/store: dependencies: '@agentplugins/compile': specifier: workspace:* version: link:../compile + '@agentplugins/pipeline': + specifier: workspace:* + version: link:../pipeline '@agentplugins/security': specifier: workspace:* version: link:../security devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.41 + specifier: 'catalog:' + version: 22.20.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 vitest: - specifier: ^3.1.4 - version: 3.2.6(@types/debug@4.1.13)(@types/node@20.19.41) + specifier: 'catalog:' + version: 4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) + + plugins/example-custom-adapter: + devDependencies: + '@agentplugins/cli': + specifier: workspace:* + version: link:../../packages/cli + '@agentplugins/core': + specifier: workspace:* + version: link:../../packages/core + typescript: + specifier: 'catalog:' + version: 6.0.3 plugins/example-logger: devDependencies: @@ -400,8 +476,8 @@ importers: specifier: workspace:* version: link:../../packages/core typescript: - specifier: ^5.5.0 - version: 5.9.3 + specifier: 'catalog:' + version: 6.0.3 packages: @@ -484,19 +560,36 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.2': + resolution: {integrity: sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/parser@7.29.7': resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0': + resolution: {integrity: sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} @@ -505,6 +598,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0': + resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@braintree/sanitize-url@6.0.4': resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} @@ -569,13 +666,13 @@ packages: '@chevrotain/types@11.1.2': resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} - '@clack/core@0.3.5': - resolution: {integrity: sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ==} + '@clack/core@1.4.2': + resolution: {integrity: sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==} + engines: {node: '>= 20.12.0'} - '@clack/prompts@0.7.0': - resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==} - bundledDependencies: - - is-unicode-supported + '@clack/prompts@1.6.0': + resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==} + engines: {node: '>= 20.12.0'} '@docsearch/css@3.8.2': resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} @@ -600,12 +697,27 @@ packages: search-insights: optional: true + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -618,6 +730,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.7': resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} @@ -630,6 +748,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.7': resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} @@ -642,6 +766,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.7': resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} @@ -654,6 +784,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.27.7': resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} @@ -666,6 +802,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.7': resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} @@ -678,6 +820,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.7': resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} @@ -690,6 +838,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.7': resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} @@ -702,6 +856,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.7': resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} @@ -714,6 +874,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.7': resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} @@ -726,6 +892,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.7': resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} @@ -738,6 +910,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.7': resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} @@ -750,6 +928,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.7': resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} @@ -762,6 +946,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.7': resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} @@ -774,6 +964,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.7': resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} @@ -786,6 +982,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.7': resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} @@ -798,12 +1000,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.7': resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.27.7': resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} @@ -816,12 +1030,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.7': resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.27.7': resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} @@ -834,12 +1060,24 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.7': resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.27.7': resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} @@ -852,6 +1090,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.7': resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} @@ -864,6 +1108,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.7': resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} @@ -876,6 +1126,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.7': resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} @@ -888,6 +1144,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.7': resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} @@ -924,10 +1186,6 @@ packages: '@types/node': optional: true - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -941,6 +1199,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@logtape/logtape@2.2.1': + resolution: {integrity: sha512-SkRptJUEAbGuf+/blXDxDa8sSq8no+lxJlfwPTMzrHIULOMsNZRaqT2qF3H9tmGOsaLYc+uF3orRCQfoH0qKzA==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -950,8 +1211,8 @@ packages: '@mermaid-js/mermaid-mindmap@9.3.0': resolution: {integrity: sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw==} - '@mermaid-js/parser@1.1.1': - resolution: {integrity: sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==} + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} @@ -963,6 +1224,12 @@ packages: '@cfworker/json-schema': optional: true + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -975,6 +1242,110 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + + '@rolldown/binding-android-arm64@1.1.3': + resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.3': + resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.3': + resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.3': + resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.3': + resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.3': + resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.3': + resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.3': + resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.3': + resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.3': + resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.3': + resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.3': + resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/rollup-android-arm-eabi@4.61.1': resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] @@ -1137,8 +1508,11 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@sinclair/typebox@0.27.10': - resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1251,6 +1625,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -1269,8 +1646,8 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.41': - resolution: {integrity: sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==} + '@types/node@22.20.0': + resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1294,49 +1671,34 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/expect@1.6.1': - resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==} + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} - '@vitest/expect@3.2.6': - resolution: {integrity: sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ==} - - '@vitest/mocker@3.2.6': - resolution: {integrity: sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw==} + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.6': - resolution: {integrity: sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==} - - '@vitest/runner@1.6.1': - resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==} - - '@vitest/runner@3.2.6': - resolution: {integrity: sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q==} + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} - '@vitest/snapshot@1.6.1': - resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==} + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} - '@vitest/snapshot@3.2.6': - resolution: {integrity: sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw==} + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} - '@vitest/spy@1.6.1': - resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==} + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} - '@vitest/spy@3.2.6': - resolution: {integrity: sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg==} - - '@vitest/utils@1.6.1': - resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==} - - '@vitest/utils@3.2.6': - resolution: {integrity: sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg==} + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} '@vue/compiler-core@3.5.38': resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} @@ -1430,10 +1792,6 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -1466,9 +1824,9 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -1483,13 +1841,14 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-kit@3.0.0: + resolution: {integrity: sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==} + engines: {node: ^22.18.0 || >=24.11.0} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -1504,6 +1863,9 @@ packages: birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} + body-parser@2.3.0: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} @@ -1530,6 +1892,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1541,18 +1907,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} - - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - 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==} @@ -1565,17 +1923,13 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + cleye@2.6.0: + resolution: {integrity: sha512-u0SQCsega/ox+2GSuUlG6wvA9c2FtH8sPmv9G9Q3JRTs7FK6+LtaziRAQgx7lrJ1J7bOd3palhwgZKMg8R6JbQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1621,6 +1975,9 @@ packages: resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} engines: {node: '>=18'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -1821,13 +2178,8 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} delaunator@5.1.0: resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} @@ -1847,10 +2199,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1858,6 +2206,15 @@ packages: dompurify@3.4.11: resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1871,6 +2228,10 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -1895,8 +2256,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.2.0: + resolution: {integrity: sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -1910,6 +2271,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.27.7: resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} @@ -1949,10 +2315,6 @@ packages: 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'} @@ -1984,9 +2346,18 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2052,9 +2423,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2063,9 +2431,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2110,6 +2478,9 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -2121,10 +2492,6 @@ packages: resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} hasBin: true - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -2140,6 +2507,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2185,10 +2556,6 @@ packages: 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-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -2204,8 +2571,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true jose@6.2.3: @@ -2215,9 +2582,6 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - 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 @@ -2226,6 +2590,11 @@ packages: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -2266,10 +2635,6 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - local-pkg@0.5.1: - resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} - engines: {node: '>=14'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2283,12 +2648,6 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2341,15 +2700,12 @@ packages: 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.15.0: - resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} + mermaid@11.16.0: + resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2433,10 +2789,6 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -2472,10 +2824,6 @@ packages: non-layered-tidy-tree-layout@2.0.2: resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} - 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'} @@ -2484,6 +2832,10 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -2491,10 +2843,6 @@ packages: 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'} - oniguruma-to-es@3.1.1: resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} @@ -2509,10 +2857,6 @@ packages: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2546,10 +2890,6 @@ packages: 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-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -2560,19 +2900,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - - 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==} @@ -2642,10 +2972,6 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -2664,6 +2990,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2675,9 +3004,6 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -2719,6 +3045,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2729,6 +3058,30 @@ packages: robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rolldown-plugin-dts@0.26.0: + resolution: {integrity: sha512-e+kEPtUiDES0htk5iqkSeF4EzAV7R+vugGB44iPDuw1Kw9E+WyL1VG7PaV0IIjGHLiacztMBcMTyrr8ON9CT1Q==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20260325.1' + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.1.3: + resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.61.1: resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2762,6 +3115,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -2842,8 +3200,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -2864,16 +3222,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-literal@2.1.1: - resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} - - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - stylis@4.4.0: resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} @@ -2893,6 +3241,9 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} + terminal-columns@2.0.0: + resolution: {integrity: sha512-6IByuUjyNZJXUtwDNm+OIe62zgwwaRbH+WMNTcx05O2G5V9WhvluAAHJY8OvUdwmzMPpqAD/7EUpGdI6ae1aiQ==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -2914,24 +3265,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.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@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: @@ -2962,6 +3297,43 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tsdown@0.22.3: + resolution: {integrity: sha512-louqbfA8Qf//B9jTTL0FPtXTNpjCWv1VPkbcmQMph2pTpzs+LnB1tbe4tDDRVpo2BjF5SgUXaTZe45SxB8pWHg==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.3 + '@tsdown/exe': 0.22.3 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.1: resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} engines: {node: '>=18'} @@ -2981,16 +3353,15 @@ packages: typescript: optional: true - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} + type-flag@4.5.0: + resolution: {integrity: sha512-1aLzxcL6u1O9XHieAJBBX9U4QzwzDTWN0ER9M7QQSvS24NBmGM+N8FcghlgHAzOvDlEEpOx4hEml9CVcDnflcw==} type-is@2.1.0: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -3000,6 +3371,9 @@ packages: ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -3046,16 +3420,6 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@1.6.1: - resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - 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} @@ -3087,6 +3451,46 @@ packages: terser: optional: true + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitepress-plugin-group-icons@1.7.5: resolution: {integrity: sha512-QzcroUuIiVKyXpmEiiHVbfRTQIy9Zbwxpk5JC/zavO8mavitwumz2RZWlwTchMCCHducYyPptkYvXvdnNUWkog==} peerDependencies: @@ -3117,51 +3521,39 @@ packages: postcss: optional: true - vitest@1.6.1: - resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.6.1 - '@vitest/ui': 1.6.1 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@opentelemetry/api': + optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': optional: true - '@vitest/ui': + '@vitest/browser-preview': optional: true - happy-dom: + '@vitest/browser-webdriverio': optional: true - jsdom: + '@vitest/coverage-istanbul': optional: true - - vitest@3.2.6: - resolution: {integrity: sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw==} - 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.6 - '@vitest/ui': 3.2.6 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/debug': - optional: true - '@types/node': - optional: true - '@vitest/browser': + '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true @@ -3207,17 +3599,13 @@ packages: resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} - yocto-queue@1.2.2: - resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} - engines: {node: '>=12.20'} - zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: zod: ^3.25.28 || ^4 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -3341,14 +3729,31 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.2.4 + '@babel/generator@8.0.0': + dependencies: + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-identifier@8.0.2': {} + '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 + '@babel/parser@8.0.0': + dependencies: + '@babel/types': 8.0.0 + '@babel/runtime@7.29.7': {} '@babel/types@7.29.7': @@ -3356,6 +3761,11 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@babel/types@8.0.0': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@braintree/sanitize-url@6.0.4': optional: true @@ -3390,7 +3800,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.31.0(@types/node@20.19.41)': + '@changesets/cli@2.31.0(@types/node@22.20.0)': dependencies: '@changesets/apply-release-plan': 7.1.1 '@changesets/assemble-release-plan': 6.0.10 @@ -3406,7 +3816,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@20.19.41) + '@inquirer/external-editor': 1.0.3(@types/node@22.20.0) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -3506,15 +3916,16 @@ snapshots: '@chevrotain/types@11.1.2': {} - '@clack/core@0.3.5': + '@clack/core@1.4.2': dependencies: - picocolors: 1.1.1 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@clack/prompts@0.7.0': + '@clack/prompts@1.6.0': dependencies: - '@clack/core': 0.3.5 - picocolors: 1.1.1 + '@clack/core': 1.4.2 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 '@docsearch/css@3.8.2': {} @@ -3541,67 +3952,116 @@ snapshots: transitivePeerDependencies: - '@algolia/client-search' + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + '@esbuild/aix-ppc64@0.27.7': optional: true '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm64@0.27.7': optional: true '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-arm@0.27.7': optional: true '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/android-x64@0.27.7': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.27.7': optional: true '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.27.7': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.27.7': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.27.7': optional: true '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.27.7': optional: true '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.27.7': @@ -3610,81 +4070,126 @@ snapshots: '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-loong64@0.27.7': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.27.7': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.27.7': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.27.7': optional: true '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-s390x@0.27.7': optional: true '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/linux-x64@0.27.7': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.27.7': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.27.7': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.27.7': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.27.7': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.27.7': optional: true '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.27.7': optional: true '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.27.7': optional: true '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-ia32@0.27.7': optional: true '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.25.12': + optional: true + '@esbuild/win32-x64@0.27.7': optional: true @@ -3712,16 +4217,12 @@ snapshots: '@iconify/types': 2.0.0 import-meta-resolve: 4.2.0 - '@inquirer/external-editor@1.0.3(@types/node@20.19.41)': + '@inquirer/external-editor@1.0.3(@types/node@22.20.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 20.19.41 - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.10 + '@types/node': 22.20.0 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -3737,6 +4238,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@logtape/logtape@2.2.1': {} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -3764,11 +4267,11 @@ snapshots: non-layered-tidy-tree-layout: 2.0.2 optional: true - '@mermaid-js/parser@1.1.1': + '@mermaid-js/parser@1.2.0': dependencies: '@chevrotain/types': 11.1.2 - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.27) ajv: 8.20.0 @@ -3785,11 +4288,18 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - supports-color + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3802,6 +4312,63 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oxc-project/types@0.137.0': {} + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + + '@rolldown/binding-android-arm64@1.1.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.3': + optional: true + + '@rolldown/binding-darwin-x64@1.1.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.3': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true @@ -3917,7 +4484,12 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@sinclair/typebox@0.27.10': {} + '@standard-schema/spec@1.1.0': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true '@types/chai@5.2.3': dependencies: @@ -4055,6 +4627,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/jsesc@2.5.1': {} + '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.2': @@ -4072,7 +4646,7 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@20.19.41': + '@types/node@22.20.0': dependencies: undici-types: 6.21.0 @@ -4090,81 +4664,51 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@20.19.41))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@22.20.0))(vue@3.5.38(typescript@6.0.3))': dependencies: - vite: 5.4.21(@types/node@20.19.41) - vue: 3.5.38(typescript@5.9.3) + vite: 5.4.21(@types/node@22.20.0) + vue: 3.5.38(typescript@6.0.3) - '@vitest/expect@1.6.1': - dependencies: - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - chai: 4.5.0 - - '@vitest/expect@3.2.6': + '@vitest/expect@4.1.9': dependencies: + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 3.2.6 - '@vitest/utils': 3.2.6 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@vitest/mocker@3.2.6(vite@5.4.21(@types/node@20.19.41))': + '@vitest/mocker@4.1.9(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0))': dependencies: - '@vitest/spy': 3.2.6 + '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 5.4.21(@types/node@20.19.41) - - '@vitest/pretty-format@3.2.6': - dependencies: - tinyrainbow: 2.0.0 + vite: 6.4.3(@types/node@22.20.0)(jiti@2.7.0) - '@vitest/runner@1.6.1': + '@vitest/pretty-format@4.1.9': dependencies: - '@vitest/utils': 1.6.1 - p-limit: 5.0.0 - pathe: 1.1.2 + tinyrainbow: 3.1.0 - '@vitest/runner@3.2.6': + '@vitest/runner@4.1.9': dependencies: - '@vitest/utils': 3.2.6 + '@vitest/utils': 4.1.9 pathe: 2.0.3 - strip-literal: 3.1.0 - '@vitest/snapshot@1.6.1': + '@vitest/snapshot@4.1.9': dependencies: - magic-string: 0.30.21 - pathe: 1.1.2 - pretty-format: 29.7.0 - - '@vitest/snapshot@3.2.6': - dependencies: - '@vitest/pretty-format': 3.2.6 + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@1.6.1': - dependencies: - tinyspy: 2.2.1 - - '@vitest/spy@3.2.6': - dependencies: - tinyspy: 4.0.4 - - '@vitest/utils@1.6.1': - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 + '@vitest/spy@4.1.9': {} - '@vitest/utils@3.2.6': + '@vitest/utils@4.1.9': dependencies: - '@vitest/pretty-format': 3.2.6 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 '@vue/compiler-core@3.5.38': dependencies: @@ -4230,28 +4774,28 @@ snapshots: '@vue/shared': 3.5.38 csstype: 3.2.3 - '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))': + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3))': dependencies: '@vue/compiler-ssr': 3.5.38 '@vue/shared': 3.5.38 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.38(typescript@6.0.3) '@vue/shared@3.5.38': {} - '@vueuse/core@12.8.2(typescript@5.9.3)': + '@vueuse/core@12.8.2(typescript@6.0.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.38(typescript@5.9.3) + '@vueuse/shared': 12.8.2(typescript@6.0.3) + vue: 3.5.38(typescript@6.0.3) transitivePeerDependencies: - typescript - '@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@5.9.3)': + '@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@6.0.3)': dependencies: - '@vueuse/core': 12.8.2(typescript@5.9.3) - '@vueuse/shared': 12.8.2(typescript@5.9.3) - vue: 3.5.38(typescript@5.9.3) + '@vueuse/core': 12.8.2(typescript@6.0.3) + '@vueuse/shared': 12.8.2(typescript@6.0.3) + vue: 3.5.38(typescript@6.0.3) optionalDependencies: focus-trap: 7.8.0 transitivePeerDependencies: @@ -4259,9 +4803,9 @@ snapshots: '@vueuse/metadata@12.8.2': {} - '@vueuse/shared@12.8.2(typescript@5.9.3)': + '@vueuse/shared@12.8.2(typescript@6.0.3)': dependencies: - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.38(typescript@6.0.3) transitivePeerDependencies: - typescript @@ -4270,10 +4814,6 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-walk@8.3.5: - dependencies: - acorn: 8.16.0 - acorn@8.16.0: {} ajv-formats@3.0.1(ajv@8.20.0): @@ -4312,7 +4852,7 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} + ansis@4.3.1: {} any-promise@1.3.0: {} @@ -4324,10 +4864,14 @@ snapshots: array-union@2.1.0: {} - assertion-error@1.1.0: {} - assertion-error@2.0.1: {} + ast-kit@3.0.0: + dependencies: + '@babel/parser': 8.0.0 + estree-walker: 3.0.3 + pathe: 2.0.3 + bail@2.0.2: {} balanced-match@4.0.4: {} @@ -4338,6 +4882,8 @@ snapshots: birpc@2.9.0: {} + birpc@4.0.0: {} + body-parser@2.3.0: dependencies: bytes: 3.1.2 @@ -4369,6 +4915,8 @@ snapshots: cac@6.7.14: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4381,25 +4929,7 @@ snapshots: ccount@2.0.1: {} - chai@4.5.0: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.4 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.1.0 - - 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@5.6.2: {} + chai@6.2.2: {} character-entities-html4@2.1.0: {} @@ -4409,16 +4939,15 @@ snapshots: chardet@2.1.1: {} - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - - check-error@2.1.3: {} - chokidar@4.0.3: dependencies: readdirp: 4.1.2 + cleye@2.6.0: + dependencies: + terminal-columns: 2.0.0 + type-flag: 4.5.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -4449,6 +4978,8 @@ snapshots: content-type@2.0.0: {} + convert-source-map@2.0.0: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -4672,11 +5203,7 @@ snapshots: dependencies: character-entities: 2.0.2 - deep-eql@4.1.4: - dependencies: - type-detect: 4.1.0 - - deep-eql@5.0.2: {} + defu@6.1.7: {} delaunator@5.1.0: dependencies: @@ -4692,8 +5219,6 @@ snapshots: dependencies: dequal: 2.0.3 - diff-sequences@29.6.3: {} - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -4702,6 +5227,8 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + dts-resolver@3.0.0: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4714,6 +5241,8 @@ snapshots: emoji-regex@8.0.0: {} + empathic@2.0.1: {} + encodeurl@2.0.0: {} enquirer@2.4.1: @@ -4729,7 +5258,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.2.0: {} es-object-atoms@1.1.2: dependencies: @@ -4763,6 +5292,35 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.7: optionalDependencies: '@esbuild/aix-ppc64': 0.27.7 @@ -4814,18 +5372,6 @@ snapshots: dependencies: eventsource-parser: 3.1.0 - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - expect-type@1.3.0: {} express-rate-limit@8.5.2(express@5.2.1): @@ -4884,8 +5430,18 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.2: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -4953,8 +5509,6 @@ snapshots: get-caller-file@2.0.5: {} - get-func-name@2.0.2: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4973,7 +5527,9 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 - get-stream@8.0.1: {} + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 glob-parent@5.1.2: dependencies: @@ -5029,6 +5585,8 @@ snapshots: hookable@5.5.3: {} + hookable@6.1.1: {} + html-void-elements@3.0.0: {} http-errors@2.0.1: @@ -5041,8 +5599,6 @@ snapshots: human-id@4.2.0: {} - human-signals@5.0.0: {} - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -5055,6 +5611,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.4.0: {} + inherits@2.0.4: {} internmap@1.0.1: {} @@ -5081,8 +5639,6 @@ snapshots: is-promise@4.0.0: {} - is-stream@3.0.0: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -5093,14 +5649,12 @@ snapshots: isexe@2.0.0: {} - jiti@1.21.7: {} + jiti@2.7.0: {} jose@6.2.3: {} joycon@3.1.1: {} - js-tokens@9.0.1: {} - js-yaml@3.14.2: dependencies: argparse: 1.0.10 @@ -5110,6 +5664,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} @@ -5140,11 +5696,6 @@ snapshots: load-tsconfig@0.2.5: {} - local-pkg@0.5.1: - dependencies: - mlly: 1.8.2 - pkg-types: 1.3.1 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -5155,12 +5706,6 @@ snapshots: longest-streak@3.1.0: {} - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 - - loupe@3.2.1: {} - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5249,15 +5794,13 @@ snapshots: merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} - merge2@1.4.1: {} - mermaid@11.15.0: + mermaid@11.16.0: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.3 - '@mermaid-js/parser': 1.1.1 + '@mermaid-js/parser': 1.2.0 '@types/d3': 7.4.3 '@upsetjs/venn.js': 2.0.0 cytoscape: 3.34.0 @@ -5432,8 +5975,6 @@ snapshots: dependencies: mime-db: 1.54.0 - mimic-fn@4.0.0: {} - minimatch@10.2.5: dependencies: brace-expansion: 5.0.6 @@ -5466,14 +6007,12 @@ snapshots: non-layered-tidy-tree-layout@2.0.2: optional: true - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - object-assign@4.1.1: {} object-inspect@1.13.4: {} + obug@2.1.3: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -5482,10 +6021,6 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - oniguruma-to-es@3.1.1: dependencies: emoji-regex-xs: 1.0.0 @@ -5502,10 +6037,6 @@ snapshots: dependencies: p-try: 2.2.0 - p-limit@5.0.0: - dependencies: - yocto-queue: 1.2.2 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -5528,22 +6059,14 @@ snapshots: path-key@3.1.1: {} - path-key@4.0.0: {} - path-to-regexp@6.3.0: {} path-to-regexp@8.4.2: {} path-type@4.0.0: {} - pathe@1.1.2: {} - pathe@2.0.3: {} - pathval@1.1.1: {} - - pathval@2.0.1: {} - perfect-debounce@1.0.0: {} picocolors@1.1.1: {} @@ -5571,11 +6094,11 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.15): dependencies: lilconfig: 3.1.3 optionalDependencies: - jiti: 1.21.7 + jiti: 2.7.0 postcss: 8.5.15 postcss@8.5.15: @@ -5590,12 +6113,6 @@ snapshots: pretty-bytes@7.1.0: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - property-information@7.2.0: {} proxy-addr@2.0.7: @@ -5611,6 +6128,8 @@ snapshots: quansync@0.2.11: {} + quansync@1.0.0: {} + queue-microtask@1.2.3: {} range-parser@1.2.1: {} @@ -5622,8 +6141,6 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 - react-is@18.3.1: {} - read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -5682,12 +6199,51 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + reusify@1.1.0: {} rfdc@1.4.1: {} robust-predicates@3.0.3: {} + rolldown-plugin-dts@0.26.0(rolldown@1.1.3)(typescript@6.0.3): + dependencies: + '@babel/generator': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@babel/parser': 8.0.0 + ast-kit: 3.0.0 + birpc: 4.0.0 + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.3 + rolldown: 1.1.3 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.1.3: + dependencies: + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.3 + '@rolldown/binding-darwin-arm64': 1.1.3 + '@rolldown/binding-darwin-x64': 1.1.3 + '@rolldown/binding-freebsd-x64': 1.1.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.3 + '@rolldown/binding-linux-arm64-gnu': 1.1.3 + '@rolldown/binding-linux-arm64-musl': 1.1.3 + '@rolldown/binding-linux-ppc64-gnu': 1.1.3 + '@rolldown/binding-linux-s390x-gnu': 1.1.3 + '@rolldown/binding-linux-x64-gnu': 1.1.3 + '@rolldown/binding-linux-x64-musl': 1.1.3 + '@rolldown/binding-openharmony-arm64': 1.1.3 + '@rolldown/binding-wasm32-wasi': 1.1.3 + '@rolldown/binding-win32-arm64-msvc': 1.1.3 + '@rolldown/binding-win32-x64-msvc': 1.1.3 + rollup@4.61.1: dependencies: '@types/estree': 1.0.9 @@ -5753,6 +6309,8 @@ snapshots: semver@7.8.2: {} + semver@7.8.5: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -5852,7 +6410,7 @@ snapshots: statuses@2.0.2: {} - std-env@3.10.0: {} + std-env@4.1.0: {} string-width@4.2.3: dependencies: @@ -5873,16 +6431,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - - strip-literal@2.1.1: - dependencies: - js-tokens: 9.0.1 - - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - stylis@4.4.0: {} sucrase@3.35.1: @@ -5903,6 +6451,8 @@ snapshots: term-size@2.2.1: {} + terminal-columns@2.0.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -5922,15 +6472,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@0.8.4: {} - - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@2.2.1: {} - - tinyspy@4.0.4: {} + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: @@ -5950,7 +6492,35 @@ snapshots: ts-interface-checker@0.1.13: {} - tsup@8.5.1(jiti@1.21.7)(postcss@8.5.15)(typescript@5.9.3): + tsdown@0.22.3(typescript@6.0.3): + dependencies: + ansis: 4.3.1 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.3 + picomatch: 4.0.4 + rolldown: 1.1.3 + rolldown-plugin-dts: 0.26.0(rolldown@1.1.3)(typescript@6.0.3) + semver: 7.8.5 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - vue-tsc + + tslib@2.8.1: + optional: true + + tsup@8.5.1(jiti@2.7.0)(postcss@8.5.15)(typescript@6.0.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 @@ -5961,7 +6531,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.15) resolve-from: 5.0.0 rollup: 4.61.1 source-map: 0.7.6 @@ -5971,14 +6541,14 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.15 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - jiti - supports-color - tsx - yaml - type-detect@4.1.0: {} + type-flag@4.5.0: {} type-is@2.1.0: dependencies: @@ -5986,12 +6556,17 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typescript@5.9.3: {} + typescript@6.0.3: {} uc.micro@2.1.0: {} ufo@1.6.4: {} + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + undici-types@6.21.0: {} unified@11.0.5: @@ -6051,58 +6626,35 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@1.6.1(@types/node@20.19.41): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.4.21(@types/node@20.19.41) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite-node@3.2.4(@types/node@20.19.41): + vite@5.4.21(@types/node@22.20.0): dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 5.4.21(@types/node@20.19.41) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser + esbuild: 0.21.5 + postcss: 8.5.15 + rollup: 4.61.1 + optionalDependencies: + '@types/node': 22.20.0 + fsevents: 2.3.3 - vite@5.4.21(@types/node@20.19.41): + vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0): dependencies: - esbuild: 0.21.5 + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.15 rollup: 4.61.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.19.41 + '@types/node': 22.20.0 fsevents: 2.3.3 + jiti: 2.7.0 - vitepress-plugin-group-icons@1.7.5(vite@5.4.21(@types/node@20.19.41)): + vitepress-plugin-group-icons@1.7.5(vite@5.4.21(@types/node@22.20.0)): dependencies: '@iconify-json/logos': 1.2.11 '@iconify-json/vscode-icons': 1.2.59 '@iconify/utils': 3.1.3 optionalDependencies: - vite: 5.4.21(@types/node@20.19.41) + vite: 5.4.21(@types/node@22.20.0) vitepress-plugin-llms@1.13.2: dependencies: @@ -6123,14 +6675,14 @@ snapshots: transitivePeerDependencies: - supports-color - vitepress-plugin-mermaid@2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@20.19.41)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3)): + vitepress-plugin-mermaid@2.0.17(mermaid@11.16.0)(vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@22.20.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@6.0.3)): dependencies: - mermaid: 11.15.0 - vitepress: 1.6.4(@algolia/client-search@5.55.0)(@types/node@20.19.41)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3) + mermaid: 11.16.0 + vitepress: 1.6.4(@algolia/client-search@5.55.0)(@types/node@22.20.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@6.0.3) optionalDependencies: '@mermaid-js/mermaid-mindmap': 9.3.0 - vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@20.19.41)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.55.0)(@types/node@22.20.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@6.0.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.55.0)(search-insights@2.17.3) @@ -6139,17 +6691,17 @@ snapshots: '@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@20.19.41))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@22.20.0))(vue@3.5.38(typescript@6.0.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.38 - '@vueuse/core': 12.8.2(typescript@5.9.3) - '@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@5.9.3) + '@vueuse/core': 12.8.2(typescript@6.0.3) + '@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@6.0.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@20.19.41) - vue: 3.5.38(typescript@5.9.3) + vite: 5.4.21(@types/node@22.20.0) + vue: 3.5.38(typescript@6.0.3) optionalDependencies: postcss: 8.5.15 transitivePeerDependencies: @@ -6179,88 +6731,42 @@ snapshots: - typescript - universal-cookie - vitest@1.6.1(@types/node@20.19.41): - dependencies: - '@vitest/expect': 1.6.1 - '@vitest/runner': 1.6.1 - '@vitest/snapshot': 1.6.1 - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - acorn-walk: 8.3.5 - chai: 4.5.0 - debug: 4.4.3 - execa: 8.0.1 - local-pkg: 0.5.1 - magic-string: 0.30.21 - pathe: 1.1.2 - picocolors: 1.1.1 - std-env: 3.10.0 - strip-literal: 2.1.1 - tinybench: 2.9.0 - tinypool: 0.8.4 - vite: 5.4.21(@types/node@20.19.41) - vite-node: 1.6.1(@types/node@20.19.41) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 20.19.41 - transitivePeerDependencies: - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vitest@3.2.6(@types/debug@4.1.13)(@types/node@20.19.41): + vitest@4.1.9(@types/node@22.20.0)(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)): dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.6 - '@vitest/mocker': 3.2.6(vite@5.4.21(@types/node@20.19.41)) - '@vitest/pretty-format': 3.2.6 - '@vitest/runner': 3.2.6 - '@vitest/snapshot': 3.2.6 - '@vitest/spy': 3.2.6 - '@vitest/utils': 3.2.6 - chai: 5.3.3 - debug: 4.4.3 + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.2.0 expect-type: 1.3.0 magic-string: 0.30.21 + obug: 2.1.3 pathe: 2.0.3 picomatch: 4.0.4 - std-env: 3.10.0 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.2.4 tinyglobby: 0.2.17 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 5.4.21(@types/node@20.19.41) - vite-node: 3.2.4(@types/node@20.19.41) + tinyrainbow: 3.1.0 + vite: 6.4.3(@types/node@22.20.0)(jiti@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/debug': 4.1.13 - '@types/node': 20.19.41 + '@types/node': 22.20.0 transitivePeerDependencies: - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - vue@3.5.38(typescript@5.9.3): + vue@3.5.38(typescript@6.0.3): dependencies: '@vue/compiler-dom': 3.5.38 '@vue/compiler-sfc': 3.5.38 '@vue/runtime-dom': 3.5.38 - '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.9.3)) + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) '@vue/shared': 3.5.38 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 which@2.0.2: dependencies: @@ -6293,12 +6799,10 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@1.2.2: {} - - zod-to-json-schema@3.25.2(zod@3.25.76): + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod@3.25.76: {} + zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7e2ce5d..ad2daef 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,3 +4,15 @@ packages: - 'docs' allowBuilds: esbuild: true + +catalog: + typescript: '^6.0.0' + vitest: '^4.0.0' + tsdown: '^0.22.3' + jiti: '^2.7.0' + tsx: '^4.22.4' + '@logtape/logtape': '^2.0.0' + cleye: '^2.0.0' + zod: '^4.4.3' + '@types/node': '^22.20.0' + '@clack/prompts': '^1.0.0' diff --git a/scripts/install.sh b/scripts/install.sh index b82d152..6ec5219 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,7 +3,7 @@ # install.sh — Install AgentPlugins CLI via curl # # Usage: -# curl -fsSL https://agentplugins.dev/install.sh | bash +# curl -fsSL https://agentplugins.pages.dev/install.sh | bash # curl -fsSL https://raw.githubusercontent.com/sigilco/agentplugins/main/scripts/install.sh | bash # # Flags: diff --git a/scripts/recompile-installed.ts b/scripts/recompile-installed.ts index bc42fbe..33dde30 100644 --- a/scripts/recompile-installed.ts +++ b/scripts/recompile-installed.ts @@ -1,3 +1,4 @@ +// @ts-nocheck /** * One-time recompile script for installed plugins. * Run from workspace: npx tsx scripts/recompile-installed.ts @@ -5,6 +6,7 @@ import { join } from 'node:path'; import { existsSync } from 'node:fs'; import { homedir } from 'node:os'; +import { createJiti } from 'jiti'; import { compile } from '../packages/cli/src/commands/build.js'; import { getDetectedAgents, @@ -25,14 +27,13 @@ async function tryTsConfig(pluginDir: string) { const fp = join(pluginDir, c); if (!existsSync(fp)) continue; try { - const jiti = (await import('jiti')).default as any; // Map @agentplugins/* so jiti can resolve workspace packages from the plugin dir const wsRoot = new URL('../', import.meta.url).pathname; const alias: Record = { '@agentplugins/core': join(wsRoot, 'packages/core/dist/index.js'), }; - const loader = jiti(fp, { interopDefault: true, esmResolve: true, alias }); - const mod = await loader.import(fp, { default: true }); + const loader = createJiti(fp, { interopDefault: true, alias }); + const mod = await loader.import(fp); const exported = (mod as any)?.default ?? mod; const manifest = typeof exported === 'function' ? await (exported as () => Promise)() : exported; if (manifest?.name) return manifest; diff --git a/spec/v1/README.md b/spec/v1/README.md index 9050617..f3bd6a4 100644 --- a/spec/v1/README.md +++ b/spec/v1/README.md @@ -79,10 +79,10 @@ An adapter is any executable that implements the **JSON process ABI**: read a ma ## Schema consumption - npm: `@agentplugins/schema` (JSON Schema + generated TS types + Ajv validators) -- Hosted: `https://agentplugins.dev/schema/v1.json` +- Hosted: `https://agentplugins.pages.dev/schema/v1.json` - Raw: `https://raw.githubusercontent.com/sigilco/agentplugins/main/spec/v1/manifest.schema.json` -Include `"$schema": "https://agentplugins.dev/schema/v1.json"` in your manifest for editor autocomplete in VS Code, JetBrains, and any JSON-Schema-aware editor. +Include `"$schema": "https://agentplugins.pages.dev/schema/v1.json"` in your manifest for editor autocomplete in VS Code, JetBrains, and any JSON-Schema-aware editor. ## Versioning diff --git a/spec/v1/adapter.schema.json b/spec/v1/adapter.schema.json index 9e5ebfc..6d7465f 100644 --- a/spec/v1/adapter.schema.json +++ b/spec/v1/adapter.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://agentplugins.dev/schema/v1/adapter.json", + "$id": "https://agentplugins.pages.dev/schema/v1/adapter.json", "title": "AgentPlugins Adapter ABI v1", "description": "Contract for the JSON process ABI. An adapter is any executable (binary, script) that reads a JSON manifest on stdin (or via --manifest ), compiles platform-specific output, writes files, and exits 0 (success) or non-zero (failure). Enables any-language adapters without SDK lock-in.", "type": "object", diff --git a/spec/v1/agent-paths.json b/spec/v1/agent-paths.json index a158807..d7bb49e 100644 --- a/spec/v1/agent-paths.json +++ b/spec/v1/agent-paths.json @@ -1,5 +1,5 @@ { - "$schema": "https://agentplugins.dev/schema/v1/agent-paths.json", + "$schema": "https://agentplugins.pages.dev/schema/v1/agent-paths.json", "version": 1, "description": "Community-maintained registry of well-known agent skill/plugin paths. AgentPlugins scans these to detect installed agents and fan out symlinks from the universal store (~/.agents/plugins/).", "store": { diff --git a/spec/v1/manifest.schema.json b/spec/v1/manifest.schema.json index 64b6e8b..93240a0 100644 --- a/spec/v1/manifest.schema.json +++ b/spec/v1/manifest.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://agentplugins.dev/schema/v1.json", + "$id": "https://agentplugins.pages.dev/schema/v1.json", "title": "AgentPlugins Manifest v1", - "description": "The public contract for an AgentPlugins plugin. One manifest compiles to any supported agent harness. Include \"$schema\": \"https://agentplugins.dev/schema/v1.json\" in your manifest for editor autocomplete.", + "description": "The public contract for an AgentPlugins plugin. One manifest compiles to any supported agent harness. Include \"$schema\": \"https://agentplugins.pages.dev/schema/v1.json\" in your manifest for editor autocomplete.", "type": "object", "required": ["name", "version", "description"], "additionalProperties": false, diff --git a/tsconfig.json b/tsconfig.json index 18170b3..a65971e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "module": "NodeNext", "moduleResolution": "NodeNext", "lib": ["ES2022"], + "types": ["node"], "declaration": true, "declarationMap": true, "sourceMap": true, @@ -20,5 +21,5 @@ "outDir": "./dist", "rootDir": "." }, - "exclude": ["node_modules", "dist", "*.js"] + "exclude": ["node_modules", "dist", "*.js", "**/__tests__/**", "**/dist/**", "**/node_modules/**"] }