diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 58ee42b..5e44eb0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -16,3 +16,7 @@ contact_links: - name: πŸ€– Codex Plugin Guide url: https://learn.chatgpt.com/docs/build-plugins about: "Learn how to install and manage Codex plugins" + + - name: πŸ“¦ pi Package Guide + url: https://pi.dev/packages + about: "Learn how to install and manage pi packages" diff --git a/.github/ISSUE_TEMPLATE/hook_issue.yml b/.github/ISSUE_TEMPLATE/hook_issue.yml index b2167e7..c2f7a49 100644 --- a/.github/ISSUE_TEMPLATE/hook_issue.yml +++ b/.github/ISSUE_TEMPLATE/hook_issue.yml @@ -11,7 +11,7 @@ body: **Before submitting:** - Search existing issues to avoid duplicates - Make sure you're using the latest plugin version - - Confirm you restarted Claude Code or started a new Codex task after installing the plugin + - Confirm you restarted Claude Code, started a new Codex task, or started a new pi session after installing the plugin - Have clear examples of the hook's behavior - type: checkboxes @@ -23,7 +23,7 @@ body: required: true - label: "I am using the latest plugin version" required: true - - label: "I restarted Claude Code or started a new Codex task after installing/updating the plugin" + - label: "I restarted Claude Code, started a new Codex task, or started a new pi session after installing/updating the plugin" required: true - label: "I can provide clear examples showing the problem" required: true @@ -47,6 +47,7 @@ body: options: - Claude Code - Codex + - pi validations: required: true @@ -244,8 +245,8 @@ body: label: "Steps to Reproduce" description: "Provide step-by-step instructions to reproduce the issue" placeholder: | - 1. Install the plugin in Claude Code or Codex - 2. Restart Claude Code or start a new Codex task + 1. Install the plugin in Claude Code, Codex, or pi + 2. Restart Claude Code, start a new Codex task, or start a new pi session 3. In Codex, review and trust the plugin hooks in `/hooks` 4. Ask the coding agent to run [specific command] 5. Observe [incorrect hook behavior] @@ -325,7 +326,7 @@ body: id: plugin-version attributes: label: "Plugin Version" - description: "Run `/plugin list` in Claude Code or `codex plugin list` in Codex and paste the version shown" + description: "Run `/plugin list` in Claude Code, `codex plugin list` in Codex, or `pi list` in pi and paste the entry shown" placeholder: "e.g., github-mcp@3.5.0" validations: required: true @@ -334,7 +335,7 @@ body: id: host-version attributes: label: "Host Version" - description: "Run `claude --version` or `codex --version` and paste the output" + description: "Run `claude --version`, `codex --version`, or `pi --version` and paste the output" placeholder: "e.g., 0.1.23" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/mcp_issue.yml b/.github/ISSUE_TEMPLATE/mcp_issue.yml index 16a3b27..32a1f2c 100644 --- a/.github/ISSUE_TEMPLATE/mcp_issue.yml +++ b/.github/ISSUE_TEMPLATE/mcp_issue.yml @@ -11,7 +11,7 @@ body: **Before submitting:** - Search existing issues to avoid duplicates - Make sure you're using the latest plugin version - - Confirm you restarted Claude Code or started a new Codex task after installing the plugin + - Confirm you restarted Claude Code, started a new Codex task, or started a new pi session after installing the plugin - Check that dependencies (`gh`, `jq`) are installed and `gh` is authenticated - type: checkboxes @@ -23,7 +23,7 @@ body: required: true - label: "I am using the latest plugin version" required: true - - label: "I restarted Claude Code or started a new Codex task after installing/updating the plugin" + - label: "I restarted Claude Code, started a new Codex task, or started a new pi session after installing/updating the plugin" required: true - label: "I have verified `gh` is installed and authenticated (`gh auth status`) and `jq` is installed" required: true @@ -47,6 +47,7 @@ body: options: - Claude Code - Codex + - pi validations: required: true @@ -182,9 +183,9 @@ body: label: "Steps to Reproduce" description: "Provide step-by-step instructions to reproduce the issue" placeholder: | - 1. Install the plugin in Claude Code or Codex + 1. Install the plugin in Claude Code, Codex, or pi 2. (Optional) Create `.mcp-gh-tooling.json` with a default `repo` - 3. Restart Claude Code or start a new Codex task + 3. Restart Claude Code, start a new Codex task, or start a new pi session 4. Ask the coding agent to view a PR / list issues / fetch CI logs 5. Observe [incorrect behavior] validations: @@ -223,6 +224,7 @@ body: - .mcp-gh-tooling.json (project root) - .claude/.mcp-gh-tooling.json - .codex/.mcp-gh-tooling.json + - .pi/.mcp-gh-tooling.json - MCP_GH_TOOLING_CONFIG env var - etc. @@ -270,7 +272,7 @@ body: id: plugin-version attributes: label: "Plugin Version" - description: "Run `/plugin list` in Claude Code or `codex plugin list` in Codex and paste the version shown" + description: "Run `/plugin list` in Claude Code, `codex plugin list` in Codex, or `pi list` in pi and paste the entry shown" placeholder: "e.g., github-mcp@3.5.0" validations: required: true @@ -279,7 +281,7 @@ body: id: host-version attributes: label: "Host Version" - description: "Run `claude --version` or `codex --version` and paste the output" + description: "Run `claude --version`, `codex --version`, or `pi --version` and paste the output" placeholder: "e.g., 0.1.23" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/plugin_component_other.yml b/.github/ISSUE_TEMPLATE/plugin_component_other.yml index 92a8187..802c2d5 100644 --- a/.github/ISSUE_TEMPLATE/plugin_component_other.yml +++ b/.github/ISSUE_TEMPLATE/plugin_component_other.yml @@ -47,6 +47,7 @@ body: options: - Claude Code - Codex + - pi validations: required: true @@ -148,8 +149,8 @@ body: label: "Steps to Reproduce" description: "Provide step-by-step instructions to reproduce the issue" placeholder: | - 1. Install the plugin in Claude Code or Codex - 2. Restart Claude Code or start a new Codex task (if required) + 1. Install the plugin in Claude Code, Codex, or pi + 2. Restart Claude Code, start a new Codex task, or start a new pi session (if required) 3. Observe [error/unexpected behavior] 4. Expected [correct behavior] validations: @@ -208,7 +209,7 @@ body: id: plugin-version attributes: label: "Plugin Version" - description: "Run `/plugin list` in Claude Code or `codex plugin list` in Codex and paste the version shown" + description: "Run `/plugin list` in Claude Code, `codex plugin list` in Codex, or `pi list` in pi and paste the entry shown" placeholder: "e.g., github-mcp@3.5.0" validations: required: true @@ -217,7 +218,7 @@ body: id: host-version attributes: label: "Host Version" - description: "Run `claude --version` or `codex --version` and paste the output" + description: "Run `claude --version`, `codex --version`, or `pi --version` and paste the output" placeholder: "e.g., 0.1.23" validations: required: true diff --git a/.github/scripts/setup-bats.sh b/.github/scripts/setup-bats.sh index 3fe8c32..73da544 100755 --- a/.github/scripts/setup-bats.sh +++ b/.github/scripts/setup-bats.sh @@ -10,7 +10,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" BATS_DIR="${REPO_ROOT}/.bats" -BATS_VERSION="1.13.0" +BATS_VERSION="1.14.0" BATS_SUPPORT_VERSION="0.3.0" BATS_ASSERT_VERSION="2.2.4" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ad77b6..296d261 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,16 +8,28 @@ on: - 'plugin-tests/**' - '.mcp-sdk.lock' - '.shellcheckrc' + - '.gitignore' - '.github/workflows/ci.yml' - '.github/scripts/**' + - 'package.json' + - 'package-lock.json' + - 'tsconfig.json' + - 'eslint.config.mjs' + - '.github/workflows/npm-publish.yml' pull_request: paths: - 'plugins/**' - 'plugin-tests/**' - '.mcp-sdk.lock' - '.shellcheckrc' + - '.gitignore' - '.github/workflows/ci.yml' - '.github/scripts/**' + - 'package.json' + - 'package-lock.json' + - 'tsconfig.json' + - 'eslint.config.mjs' + - '.github/workflows/npm-publish.yml' jobs: bash: @@ -26,7 +38,24 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + + - name: Install npm dependencies + run: npm ci + + - name: Type-check + run: npx tsc --noEmit -p . + + - name: Lint + run: npx eslint . --max-warnings 0 + + - name: Node unit tests + run: node --test 'plugin-tests/github-mcp/pi/*.test.ts' - name: Install jq run: sudo apt-get update && sudo apt-get install -y jq @@ -52,7 +81,7 @@ jobs: - name: Setup BATS uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0 with: - bats-version: 1.13.0 + bats-version: 1.14.0 support-install: true assert-install: true assert-version: 2.2.4 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..81e33b7 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,74 @@ +name: npm Publish + +on: + push: + tags: + - 'v*' + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + + # Trusted publishing needs npm 11.5.1 or later. + - name: Update npm + run: npm install -g npm@11.21.0 + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Setup BATS + uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0 + with: + bats-version: 1.14.0 + support-install: true + assert-install: true + assert-version: 2.2.4 + detik-install: false + file-install: false + support-path: ${{ github.workspace }}/.bats/bats-support + assert-path: ${{ github.workspace }}/.bats/bats-assert + + - name: Install npm dependencies + run: npm ci + + - name: Type-check + run: npx tsc --noEmit -p . + + - name: Lint + run: npx eslint . --max-warnings 0 + + - name: Node unit tests + run: node --test 'plugin-tests/github-mcp/pi/*.test.ts' + + - name: BATS + run: | + echo "Running BATS tests with timing..." + bats --timing -r plugin-tests/ + + - name: Verify tag matches package and plugin manifest versions + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + PACKAGE_VERSION=$(jq -r '.version' package.json) + CLAUDE_VERSION=$(jq -r '.version' plugins/github-mcp/.claude-plugin/plugin.json) + CODEX_VERSION=$(jq -r '.version' plugins/github-mcp/.codex-plugin/plugin.json) + if ! { [[ "${TAG_VERSION}" == "${PACKAGE_VERSION}" ]] && [[ "${TAG_VERSION}" == "${CLAUDE_VERSION}" ]] && [[ "${TAG_VERSION}" == "${CODEX_VERSION}" ]]; }; then + echo "Version mismatch: tag=${TAG_VERSION} package.json=${PACKAGE_VERSION} claude-plugin=${CLAUDE_VERSION} codex-plugin=${CODEX_VERSION}" >&2 + exit 1 + fi + + - name: Publish to npm + run: npm publish diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a571240..b1b366a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y jq diff --git a/.gitignore b/.gitignore index 7e1fef0..b136b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ AGENTS.local.md # BATS testing framework (installed locally via .github/scripts/setup-bats.sh) .bats/ + +# Node (pi package development install) +node_modules/ diff --git a/AGENTS.md b/AGENTS.md index f5b809c..b3b9097 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,16 +9,19 @@ from [shopwareLabs/ai-coding-tools](https://github.com/shopwareLabs/ai-coding-to > **Plugin name vs. server names.** The installable plugin is `github-mcp`, but its two MCP > servers keep their original IDs `gh-tooling` (read) and `gh-tooling-write` (write). So you > install `github-mcp@github-agent-tools`, while `/mcp` and `.mcp-gh-tooling.json` use the raw -> server IDs. Claude Code exposes names such as `mcp__plugin_github-mcp_gh-tooling__…`; Codex -> exposes the same tools as `mcp__gh_tooling__…` after sanitizing the server ID. +> server IDs. Claude Code exposes names such as `mcp__plugin_github-mcp_gh-tooling__…`; Codex and +> pi expose the same tools as `mcp__gh_tooling__…` after sanitizing the server ID. -## Dual-target: Claude Code and Codex +## Hosts The plugin's core is assistant-neutral: `plugins/github-mcp/mcp-server-gh/server-{read,write}.sh` -are plain stdio [MCP](https://modelcontextprotocol.io/) servers that both hosts spawn. Claude Code -uses `.claude-plugin/marketplace.json`, `.claude-plugin/plugin.json`, and `.mcp.json`; Codex uses -`.agents/plugins/marketplace.json` and `.codex-plugin/plugin.json`. The hook definition and scripts -are shared. Keep the MCP server scripts and hook behavior as the single core across both hosts. +are plain stdio [MCP](https://modelcontextprotocol.io/) servers that every host spawns. Claude +Code uses `.claude-plugin/marketplace.json`, `.claude-plugin/plugin.json`, and `.mcp.json`; Codex +uses `.agents/plugins/marketplace.json` and `.codex-plugin/plugin.json`; pi installs the repository +root as an npm package and loads its extension from the package entry point +`plugins/github-mcp/pi/index.ts`, declared in root `package.json`'s `pi.extensions`. The hook +definition and scripts are shared. Keep the MCP server scripts and hook behavior as the single core +for every host. The separate `plugin-setup` plugin remains Claude Code-only because its skill uses Claude Code interaction and permission-setting features. Do not list it in the Codex marketplace unless that @@ -31,17 +34,21 @@ directly β€” treat them as you would any source file. For `github-mcp` these are - `.mcp.json` β€” Claude Code MCP server registration - `.codex-plugin/plugin.json` β€” Codex manifest and MCP server registration +- `plugins/github-mcp/pi/` β€” pi extension (server registration, directive, and gates) +- root `package.json` β€” pi npm package manifest, including `pi.extensions` - `mcp-server-gh/` β€” the read/write servers, tool definitions (`tools-*.json`), and `lib/*.sh` - `shared/mcpserver_core.sh` β€” JSON-RPC protocol handler +- `shared/config-dirs.sh` β€” host config directory order, used by the hooks and the servers (not vendored) - `hooks/` β€” `hooks.json` plus the SessionStart/PreToolUse scripts -The `github-mcp` plugin ships only MCP servers and hooks β€” no skills, slash commands, or agents. +The `github-mcp` plugin ships MCP servers, hooks, and (for pi) the extension that adapts them β€” +no skills, slash commands, or agents. The separate `plugin-setup` plugin ships one skill (`github-mcp-setting-up`); its `skills/*/SKILL.md` is a runtime file. If `github-mcp` gains its own `skills/*/SKILL.md`, `commands/*.md`, or `agents/*.md` later, those are runtime files too. **Developer documentation is not runtime.** Repository and plugin `README.md`, `AGENTS.md`, -`CLAUDE.md` (where present), and `CHANGELOG.md` files are read by maintainers; neither host loads +`CLAUDE.md` (where present), and `CHANGELOG.md` files are read by maintainers; no host loads them as installed plugin runtime. When changing runtime behavior, edit runtime files; when updating guides or architecture notes, edit the docs. @@ -130,8 +137,8 @@ convention. Update `plugins/github-mcp/README.md` and `REFERENCE.md` when tool b ### Version bumps -Edit the version in both `plugins/github-mcp/.claude-plugin/plugin.json` and -`plugins/github-mcp/.codex-plugin/plugin.json`, then add a matching +Edit the version in `plugins/github-mcp/.claude-plugin/plugin.json`, +`plugins/github-mcp/.codex-plugin/plugin.json`, and root `package.json`, then add a matching `plugins/github-mcp/CHANGELOG.md` entry. `plugin-setup` is versioned independently in its own Claude Code manifest and `CHANGELOG.md`; keep the `version` in each of its skills' SKILL.md frontmatter equal to that manifest version. @@ -153,8 +160,10 @@ fails the build if any dropdown is out of date. Choose the supported hosts first. Add the corresponding `.claude-plugin/plugin.json` and/or `.codex-plugin/plugin.json`, then register the plugin only in each compatible host marketplace. -Keep shared runtime files host-neutral and keep host-specific launch wiring in the manifests. Run -the relevant host validation and `.github/scripts/update-issue-templates.sh`. +Keep shared runtime files host-neutral and keep host-specific launch wiring in the manifests. pi has +no per-plugin manifest: root `package.json` is the one pi package, and its `files` and +`pi.extensions` ship only `github-mcp`, so bringing another plugin to pi means extending that +package. Run the relevant host validation and `.github/scripts/update-issue-templates.sh`. ## Testing & Validation @@ -171,6 +180,20 @@ codex plugin list --available --json # inspect the resolved Codex marketplac codex plugin add github-mcp@github-agent-tools ``` +### Node and pi + +```bash +npm ci # installs pi, TypeScript, and ESLint +npx tsc --noEmit -p . # type-checks the extension and its tests +npx eslint . --max-warnings 0 # lints the same files (eslint.config.mjs) +node --test 'plugin-tests/github-mcp/pi/*.test.ts' # pi extension unit tests +``` + +`plugin-tests/github-mcp/pi_e2e.bats` drives the real `pi` binary against a scripted model and a +stubbed `gh`, in both the git-clone and npm-tarball install layouts. It runs as part of the BATS +suite below. `pi_e2e.bats` needs GNU coreutils' +`timeout` (or `gtimeout`) on PATH; on macOS, install it with `brew install coreutils`. + ### BATS ```bash @@ -180,16 +203,20 @@ codex plugin add github-mcp@github-agent-tools Tests live in `plugin-tests//` mirroring the plugin structure and load the shared helper at `plugin-tests/test_helper/common_setup.bash` (it resolves the repo root by walking up to `.bats/`). -CI (`.github/workflows/ci.yml`) runs ShellCheck over `plugins plugin-tests .github/scripts`, -`vendor-mcp-sdk.sh --check` for the vendored SDK copy, and BATS over `plugin-tests/`; a separate -`validate.yml` checks the issue-template dropdowns. +CI (`.github/workflows/ci.yml`) runs `npm ci`, the type-check, ESLint, and the Node unit tests, then +ShellCheck over `plugins plugin-tests .github/scripts`, `vendor-mcp-sdk.sh --check` for the +vendored SDK copy, and BATS over `plugin-tests/` (including `pi_e2e.bats`); a separate `validate.yml` checks the issue-template dropdowns. ### Pre-release checklist - [ ] `claude plugin validate .` passes - [ ] Codex marketplace add, list, and plugin install smoke test passes -- [ ] Plugin version bumped in both host manifests with a CHANGELOG entry -- [ ] BATS green (`.bats/bats-core/bin/bats -r plugin-tests/`) +- [ ] Plugin version bumped in the Claude Code and Codex manifests and root `package.json`, with a + CHANGELOG entry +- [ ] `npx tsc --noEmit -p .` passes +- [ ] `npx eslint . --max-warnings 0` passes +- [ ] `node --test 'plugin-tests/github-mcp/pi/*.test.ts'` passes +- [ ] BATS green (`.bats/bats-core/bin/bats -r plugin-tests/`), including `pi_e2e.bats` - [ ] ShellCheck clean - [ ] Issue-template dropdowns up to date (`.github/scripts/validate-issue-templates.sh`) - [ ] Vendored SDK matches its lock (`.github/scripts/vendor-mcp-sdk.sh --check`) @@ -203,6 +230,19 @@ then `/plugin install github-mcp@github-agent-tools`. Codex installs with `codex plugin marketplace add shopwareLabs/github-agent-tools` then `codex plugin add github-mcp@github-agent-tools`. +pi installs the repository as the npm package `@shopware-ag/github-agent-tools`, with +`pi install npm:@shopware-ag/github-agent-tools` or `pi install git:github.com/shopwareLabs/github-agent-tools`. +`.github/workflows/npm-publish.yml` publishes on a pushed tag matching `v*`: it type-checks, lints, runs +the Node unit tests and the full BATS suite, checks the tag against `package.json` and both plugin +manifests' versions, then runs `npm publish` using npm trusted publishing (no stored npm token). +Releasing a version bumps `package.json`, both plugin manifests, and +`plugins/github-mcp/CHANGELOG.md` in one commit, then tags `v` on that commit and pushes +the tag. Bootstrap, once: a maintainer with publish rights in the `@shopware-ag` npm organization +publishes the first version by hand from a clean checkout of its release tag with `npm publish`, +then runs +`npm trust github @shopware-ag/github-agent-tools --file npm-publish.yml --repository shopwareLabs/github-agent-tools --allow-publish` +(npm 11.15.0+). Every later version publishes from the workflow. + ## Using Anthropic dev plugins When developing here with Anthropic's official `plugin-dev`/`feature-dev` plugins, invoke their diff --git a/README.md b/README.md index ce25a41..42f5016 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ GitHub CLI tools for AI coding agents. Wraps the GitHub CLI (`gh`) behind MCP servers β€” pull requests, issues, CI runs, jobs, commits, search, labels, projects, and repository file browsing β€” as first-class MCP tools, with hook-based enforcement that keeps the agent on the tools instead of raw `gh` bash calls. -The MCP servers use the assistant-neutral [Model Context Protocol](https://modelcontextprotocol.io/) and can be integrated with MCP-capable coding agents. This repository provides tested `github-mcp` plugin packaging for both [Claude Code](https://docs.claude.com/en/docs/claude-code/plugins) and [Codex](https://learn.chatgpt.com/docs/build-plugins). +The MCP servers use the assistant-neutral [Model Context Protocol](https://modelcontextprotocol.io/) and can be integrated with MCP-capable coding agents. This repository provides tested `github-mcp` plugin packaging for [Claude Code](https://docs.claude.com/en/docs/claude-code/plugins), [Codex](https://learn.chatgpt.com/docs/build-plugins), and [pi](https://pi.dev). > **Origin**: Extracted from its sibling project [shopwareLabs/ai-coding-tools](https://github.com/shopwareLabs/ai-coding-tools) β€” a broader AI-coding-tools marketplace β€” into this standalone repository. The GitHub tooling was split out so it can evolve and be installed independently. ## ⚑ Quick Start -**Packaged plugin requirements:** `gh` CLI authenticated (`gh auth login`), `jq`, and either [Claude Code](https://docs.claude.com/en/docs/claude-code) or [Codex](https://developers.openai.com/codex/cli/). +**Packaged plugin requirements:** `gh` CLI authenticated (`gh auth login`), `jq`, and [Claude Code](https://docs.claude.com/en/docs/claude-code), [Codex](https://developers.openai.com/codex/cli/), or [pi](https://pi.dev). ### Claude Code @@ -30,9 +30,27 @@ codex plugin add github-mcp@github-agent-tools Start a new Codex task after installation. Open `/hooks` to review and trust the bundled enforcement hooks; Codex skips untrusted plugin hooks. +### pi + +Install from npm: + +```bash +pi install npm:@shopware-ag/github-agent-tools +``` + +Or install from git: + +```bash +pi install git:github.com/shopwareLabs/github-agent-tools +``` + +Run one of the two, never both: installing from both sources loads the extension twice. + +A new pi session or `/reload` loads the package. + The read server (`gh-tooling`) is always active. The write server (`gh-tooling-write`) is opt-in via `enable_write_server: true` in a `.mcp-gh-tooling.json` config file. Configuration is optional β€” the read server works out of the box when `gh` is authenticated. -**Optional Claude Code setup:** the companion `plugin-setup` plugin remains Claude Code-only because it uses Claude Code interaction and permission settings. Install it with `/plugin install plugin-setup@github-agent-tools`, ask Claude to *"set up github-mcp"*, then uninstall it when setup is complete. Codex users configure `.mcp-gh-tooling.json` manually as described in the plugin guide. +**Optional Claude Code setup:** the companion `plugin-setup` plugin remains Claude Code-only because it uses Claude Code interaction and permission settings. Install it with `/plugin install plugin-setup@github-agent-tools`, ask Claude to *"set up github-mcp"*, then uninstall it when setup is complete. Codex and pi users configure `.mcp-gh-tooling.json` manually as described in the plugin guide. ## 🧩 What's Inside diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..34da442 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,242 @@ +// @ts-check +import eslint from '@eslint/js'; +import nodePlugin from 'eslint-plugin-n'; +import perfectionist from 'eslint-plugin-perfectionist'; +import promisePlugin from 'eslint-plugin-promise'; +import regexpPlugin from 'eslint-plugin-regexp'; +import sonarjs from 'eslint-plugin-sonarjs'; +import unicorn from 'eslint-plugin-unicorn'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + // Global ignores + { + ignores: ['**/node_modules/**', '.bats/**', 'eslint.config.mjs'], + }, + + // Base JavaScript and TypeScript strict configuration + eslint.configs.recommended, + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + + // Core configuration for all TypeScript files. Everything here runs on Node: the pi extension, + // its unit tests, and the e2e driver. + { + files: ['**/*.ts'], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + n: nodePlugin, + perfectionist, + promise: promisePlugin, + regexp: regexpPlugin, + sonarjs, + unicorn, + }, + rules: { + // TypeScript-ESLint strict rules + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowExpressions: true, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: true, + }, + ], + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'default', + format: ['camelCase'], + leadingUnderscore: 'allow', + trailingUnderscore: 'forbid', + }, + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE'], + leadingUnderscore: 'allow', + }, + { + selector: 'typeLike', + format: ['PascalCase'], + }, + { + selector: 'enumMember', + format: ['PascalCase', 'UPPER_CASE'], + }, + { + selector: 'property', + format: null, // Allow any format for object properties + }, + { + selector: 'objectLiteralProperty', + format: null, + }, + ], + '@typescript-eslint/consistent-type-exports': 'error', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + fixStyle: 'inline-type-imports', + }, + ], + + // Unicorn recommended rules + ...unicorn.configs.recommended.rules, + 'unicorn/name-replacements': [ + 'error', + { + allowList: { + args: true, + ctx: true, + dir: true, + env: true, + err: true, + fn: true, + params: true, + }, + }, + ], + 'unicorn/no-null': 'off', // child_process reports a signal exit as a null code + 'unicorn/filename-case': [ + 'error', + { + case: 'kebabCase', + }, + ], + + // SonarJS recommended rules + ...sonarjs.configs.recommended.rules, + + // Perfectionist sorting rules + 'perfectionist/sort-imports': [ + 'error', + { + type: 'natural', + order: 'asc', + groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index'], 'type', 'unknown'], + newlinesBetween: 0, + }, + ], + 'perfectionist/sort-named-imports': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-exports': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-interfaces': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-object-types': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-objects': [ + 'error', + { + type: 'natural', + order: 'asc', + partitionByComment: true, + }, + ], + 'perfectionist/sort-enums': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + + // Promise plugin rules + ...promisePlugin.configs.recommended.rules, + 'promise/always-return': 'error', + 'promise/no-return-wrap': 'error', + 'promise/param-names': 'error', + 'promise/catch-or-return': 'error', + 'promise/no-new-statics': 'error', + + // Regexp plugin rules + ...regexpPlugin.configs.recommended.rules, + + // Node.js rules + ...nodePlugin.configs['flat/recommended-module'].rules, + 'n/no-missing-import': 'off', // TypeScript resolves imports, including the .ts extensions + + // General strict rules + 'no-console': 'error', + 'no-debugger': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + eqeqeq: ['error', 'always'], + curly: ['error', 'all'], + }, + }, + + // The pi extension: the package has no runtime dependencies, so pi packages are imported only as + // types, which Node's type stripping erases. + { + files: ['plugins/github-mcp/pi/**/*.ts'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + patterns: [ + { + allowTypeImports: true, + group: ['@earendil-works/*'], + message: 'The pi package has no runtime dependencies; import pi packages only as types.', + }, + ], + }, + ], + }, + }, + + // Test files and fixtures + { + files: ['plugin-tests/**/*.ts'], + rules: { + '@typescript-eslint/no-floating-promises': [ + 'error', + { + allowForKnownSafeCalls: [{ from: 'package', name: ['test', 'describe', 'it', 'suite'], package: 'node:test' }], + }, + ], + '@typescript-eslint/no-non-null-assertion': 'off', + 'n/no-unpublished-import': 'off', // Tests and fixtures import devDependencies + 'sonarjs/no-duplicate-string': 'off', + 'unicorn/import-style': 'off', // Allow namespace imports for Node.js built-ins + }, + }, +); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..14abf68 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5487 @@ +{ + "name": "@shopware-ag/github-agent-tools", + "version": "4.3.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@shopware-ag/github-agent-tools", + "version": "4.3.0", + "license": "MIT", + "devDependencies": { + "@earendil-works/pi-ai": "1.0.0", + "@earendil-works/pi-coding-agent": "1.0.0", + "@eslint/js": "10.0.1", + "@types/node": "24.19.1", + "eslint": "10.11.0", + "eslint-plugin-n": "18.4.0", + "eslint-plugin-perfectionist": "5.12.1", + "eslint-plugin-promise": "7.3.0", + "eslint-plugin-regexp": "3.3.1", + "eslint-plugin-sonarjs": "4.2.2", + "eslint-plugin-unicorn": "76.0.0", + "typescript": "6.0.3", + "typescript-eslint": "8.71.0" + }, + "engines": { + "node": ">=22.19.0" + }, + "peerDependencies": { + "@earendil-works/pi-coding-agent": "*" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.124.0.tgz", + "integrity": "sha512-cN5O8i9UVxHeOQAzj/XjshWXG8KiibJDw9OGpH2Z/eR3n/RBxdoLxDJOcfqAJWvjaMDFfHTBADU04hWRJVkDyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1127.0.tgz", + "integrity": "sha512-IDl/lrPb90aH+pZFHGNDmgH9nAUQj5PlZH1sJ3w7RikctyjHSnY3oNjZhrLoaBoQn/rNK0zsP6OHEqEhj2tdLA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-node": "^3.972.82", + "@aws-sdk/eventstream-handler-node": "^3.972.34", + "@aws-sdk/middleware-eventstream": "^3.972.29", + "@aws-sdk/middleware-websocket": "^3.972.52", + "@aws-sdk/token-providers": "3.1127.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.978.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.1.tgz", + "integrity": "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@aws-sdk/xml-builder": "^3.972.41", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.35.0", + "@smithy/signature-v4": "^5.7.3", + "@smithy/types": "^4.19.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.72.tgz", + "integrity": "sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.74", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.74.tgz", + "integrity": "sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.17.tgz", + "integrity": "sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/credential-provider-env": "^3.972.72", + "@aws-sdk/credential-provider-http": "^3.972.74", + "@aws-sdk/credential-provider-login": "^3.972.79", + "@aws-sdk/credential-provider-process": "^3.972.72", + "@aws-sdk/credential-provider-sso": "^3.973.16", + "@aws-sdk/credential-provider-web-identity": "^3.972.78", + "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.79", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.79.tgz", + "integrity": "sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.84", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.84.tgz", + "integrity": "sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.72", + "@aws-sdk/credential-provider-http": "^3.972.74", + "@aws-sdk/credential-provider-ini": "^3.973.17", + "@aws-sdk/credential-provider-process": "^3.972.72", + "@aws-sdk/credential-provider-sso": "^3.973.16", + "@aws-sdk/credential-provider-web-identity": "^3.972.78", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.72.tgz", + "integrity": "sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.16.tgz", + "integrity": "sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/token-providers": "3.1138.0", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.1138.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1138.0.tgz", + "integrity": "sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.78", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.78.tgz", + "integrity": "sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.35.tgz", + "integrity": "sha512-a8xilRoRaalvSPZdfrs0VY3/BPc0uYhXW56Z/k6nmZ5Vk430LHeowf9APIPq8utM3tMJSNGSSuJMlB9YrPsN+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.30", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.30.tgz", + "integrity": "sha512-B6gvZlcnRBNWraKgyEjKsbhv+VjtbyHmpU7hmtTHzXpDSxHPpCJnYLpglEOOsF+SFne20DbZC5IVC1aNr2Pb4A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.54.tgz", + "integrity": "sha512-bhESdru8u8KosziH8jcVta/NM/dNreeRz2+2aU86si5bfBYOiJ6IiqD+9U4URUcU5WJO3Yw+jO3GLDIHlscXMQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/signature-v4": "^5.7.3", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.46", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.46.tgz", + "integrity": "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.47.tgz", + "integrity": "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/signature-v4": "^5.7.3", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1127.0.tgz", + "integrity": "sha512-Dv2TMWBshJ+tF6ahs2Sy5bh4Iabsd4GAQqVvE9XZmYmnoaVbpS2QKIKE/HRacc7bTtbjEEvP+laGzHvHlf1CiQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz", + "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", + "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@earendil-works/pi-ai": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-1.0.0.tgz", + "integrity": "sha512-3/W1vdDaVtpeMd23ElvJC12HLA5yS/BGqqcXF+0SK082dN7cbgNcCwguTBRBC258Ke8SzSvUW1B75iAf8w8IxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.124.0", + "@aws-sdk/client-bedrock-runtime": "3.1127.0", + "@earendil-works/pi-telemetry": "^1.0.0", + "@google/genai": "2.21.0", + "@smithy/node-http-handler": "4.12.1", + "http-proxy-agent": "9.1.0", + "https-proxy-agent": "9.1.0", + "openai": "7.19.0", + "partial-json": "0.1.7", + "typebox": "1.3.27" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-1.0.0.tgz", + "integrity": "sha512-/FtbxoSQU/mEv1QnichJjRjqteqaIaMWxmhB4G367+MwZfX7/DI5B9YAg5lqbN7nztFskBEtUSZ+FlmMBECtMw==", + "dev": true, + "hasShrinkwrap": true, + "license": "MIT", + "dependencies": { + "@earendil-works/chord": "^1.0.0", + "@earendil-works/pi-agent-core": "^1.0.0", + "@earendil-works/pi-ai": "^1.0.0", + "@earendil-works/pi-codemode": "^1.0.0", + "@earendil-works/pi-mcp": "^1.0.0", + "@earendil-works/pi-tui": "^1.0.0", + "@silvia-odwyer/photon-node": "0.3.4", + "chalk": "6.0.0", + "cross-spawn": "7.0.6", + "diff": "8.0.4", + "grok-mermaid": "0.2.3", + "highlight.js": "10.7.3", + "hosted-git-info": "9.0.3", + "ignore": "7.0.8", + "jiti": "2.7.0", + "minimatch": "10.2.6", + "proper-lockfile": "4.1.2", + "quickjs-wasi": "3.6.2", + "semver": "7.8.5", + "typebox": "1.3.27", + "undici": "8.10.2", + "yaml": "2.9.0" + }, + "bin": { + "pi": "dist/bundle/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@anthropic-ai/sdk": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.124.0.tgz", + "integrity": "sha512-cN5O8i9UVxHeOQAzj/XjshWXG8KiibJDw9OGpH2Z/eR3n/RBxdoLxDJOcfqAJWvjaMDFfHTBADU04hWRJVkDyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1127.0.tgz", + "integrity": "sha512-IDl/lrPb90aH+pZFHGNDmgH9nAUQj5PlZH1sJ3w7RikctyjHSnY3oNjZhrLoaBoQn/rNK0zsP6OHEqEhj2tdLA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-node": "^3.972.82", + "@aws-sdk/eventstream-handler-node": "^3.972.34", + "@aws-sdk/middleware-eventstream": "^3.972.29", + "@aws-sdk/middleware-websocket": "^3.972.52", + "@aws-sdk/token-providers": "3.1127.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/core": { + "version": "3.977.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.9.tgz", + "integrity": "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.5", + "@aws-sdk/xml-builder": "^3.972.40", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.33.3", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.17.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.70.tgz", + "integrity": "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.72.tgz", + "integrity": "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.15.tgz", + "integrity": "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-env": "^3.972.70", + "@aws-sdk/credential-provider-http": "^3.972.72", + "@aws-sdk/credential-provider-login": "^3.972.77", + "@aws-sdk/credential-provider-process": "^3.972.70", + "@aws-sdk/credential-provider-sso": "^3.973.14", + "@aws-sdk/credential-provider-web-identity": "^3.972.76", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.77", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.77.tgz", + "integrity": "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.82", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.82.tgz", + "integrity": "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.70", + "@aws-sdk/credential-provider-http": "^3.972.72", + "@aws-sdk/credential-provider-ini": "^3.973.15", + "@aws-sdk/credential-provider-process": "^3.972.70", + "@aws-sdk/credential-provider-sso": "^3.973.14", + "@aws-sdk/credential-provider-web-identity": "^3.972.76", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.70.tgz", + "integrity": "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.14.tgz", + "integrity": "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/token-providers": "3.1116.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.1116.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1116.0.tgz", + "integrity": "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.76", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.76.tgz", + "integrity": "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.34", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.34.tgz", + "integrity": "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.29.tgz", + "integrity": "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.52", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.52.tgz", + "integrity": "sha512-vsPPM+nMbKJlUCFU+eoGZbdxdxDIAX9LbpjSXaR5Ufpmqgp8TdYQnoExhLu4T3umW/JIIPny1ydbhWidZZYokQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/nested-clients": { + "version": "3.997.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.44.tgz", + "integrity": "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.46", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz", + "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.5", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/token-providers": { + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1127.0.tgz", + "integrity": "sha512-Dv2TMWBshJ+tF6ahs2Sy5bh4Iabsd4GAQqVvE9XZmYmnoaVbpS2QKIKE/HRacc7bTtbjEEvP+laGzHvHlf1CiQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/types": { + "version": "3.974.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz", + "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/xml-builder": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz", + "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/chord": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/chord/-/chord-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "0.28.2" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "@earendil-works/pi-ai": "^1.0.0", + "typebox": "1.3.27" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.124.0", + "@aws-sdk/client-bedrock-runtime": "3.1127.0", + "@earendil-works/pi-telemetry": "^1.0.0", + "@google/genai": "2.21.0", + "@smithy/node-http-handler": "4.12.1", + "http-proxy-agent": "9.1.0", + "https-proxy-agent": "9.1.0", + "openai": "7.19.0", + "partial-json": "0.1.7", + "typebox": "1.3.27" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai/node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai/node_modules/openai": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-7.19.0.tgz", + "integrity": "sha512-MX2s3u2L5racTO0CC/SWpCOasJQBCJrqLKXK+l82cAhdeF8mPMBEe/gxMm0ZFa2xpKpOFLRjxv5afYEZbBXmbQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", + "undici": ">=5 <9", + "ws": "^8.21.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, + "undici": { + "optional": true + }, + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-codemode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-codemode/-/pi-codemode-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "quickjs-wasi": "3.6.2" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-mcp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-mcp/-/pi-mcp-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "7.0.6" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-telemetry": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-1.0.0.tgz", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-1.0.0.tgz", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "1.6.0", + "marked": "18.0.11" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@google/genai": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.21.0.tgz", + "integrity": "sha512-+PDtco2/Z0ONdzCGekCoCT+O1VJS9xJQNN4XzQpXG/t3El/SWWMkCWlFRO1KmivOHPa4Q0VjUYu1HBKCZ/v33Q==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/core": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz", + "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/credential-provider-imds": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/fetch-http-handler": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/node-http-handler": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/signature-v4": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz", + "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/types": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.18.0.tgz", + "integrity": "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/@types/node": { + "version": "22.19.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", + "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/chalk": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.0.tgz", + "integrity": "sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/grok-mermaid": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/grok-mermaid/-/grok-mermaid-0.2.3.tgz", + "integrity": "sha512-/4KopAbsjvuRP9MdPtlDjOHUmUVEohOX73JNcsWpzAtFxh+bq5+Dhb6gzvRieLDwIPQIR3/vy8V1NNTuz4Zsmg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/ignore": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/lru-cache": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", + "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/marked": { + "version": "18.0.11", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz", + "integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/proxy-agent-negotiate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/quickjs-wasi": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-3.6.2.tgz", + "integrity": "sha512-FCqGtGOrMgzUiIrMNMA2YnsOxCNwo31dzqXvclXUC6xeT35NJLKXQJsvbeCTjvoFAwZgEAPg8U6+KAPDGXn8Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/standardwebhooks": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", + "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/typebox": { + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz", + "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/undici": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz", + "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/@earendil-works/pi-telemetry": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-1.0.0.tgz", + "integrity": "sha512-WjNBj5TYIiPZFQEz2WlULcDwPLaKwIlmsKjVeYM+LJSbnSp38kWsHUJysIDGnu23IcLbKoPtywsvYfUJCeZePA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css-tree": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-4.1.1.tgz", + "integrity": "sha512-A+s89eP+yDAPkKjnIOfjlkGifg5J7GPn01nWx14UjBVWktfkxOU4975F9ZZ13xNxkGiblNcBaAqgKhnQrW6SWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.35.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@google/genai": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.21.0.tgz", + "integrity": "sha512-+PDtco2/Z0ONdzCGekCoCT+O1VJS9xJQNN4XzQpXG/t3El/SWWMkCWlFRO1KmivOHPa4Q0VjUYu1HBKCZ/v33Q==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@smithy/core": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.1.tgz", + "integrity": "sha512-i4YPS4B6ts7bjn7UwLnGjiZdprOvHvgGobFZsYK3GIY3E5hIqtj0rReU69BcTpGp+fvtraSNXeG1l+jtJvF55w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", + "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz", + "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@sonarsource/analyzer-commons-configurations": { + "version": "2.31.0-5284", + "resolved": "https://registry.npmjs.org/@sonarsource/analyzer-commons-configurations/-/analyzer-commons-configurations-2.31.0-5284.tgz", + "integrity": "sha512-ruk3blMs9BE9RNSp9LPpWZAIjxJmq4Cp00E3cELIHz7Kym3WxdWGRlKOMYLRQ5tJg/de7jN5AtEElY3bGIfTcw==", + "dev": true, + "license": "LGPL-3.0-only" + }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz", + "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.0.tgz", + "integrity": "sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.71.0", + "@typescript-eslint/type-utils": "8.71.0", + "@typescript-eslint/utils": "8.71.0", + "@typescript-eslint/visitor-keys": "8.71.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.71.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.11.tgz", + "integrity": "sha512-YChdK5txDjwGUvgR7oCJcLGkwi3LDh8Zx8tS7ndYciLEg/oOCL26VUBSUEtgS7EjiybcBqFxh5j0rAdDX7/bbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.0.tgz", + "integrity": "sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.71.0", + "@typescript-eslint/types": "8.71.0", + "@typescript-eslint/typescript-estree": "8.71.0", + "@typescript-eslint/visitor-keys": "8.71.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.0.tgz", + "integrity": "sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.71.0", + "@typescript-eslint/types": "^8.71.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.0.tgz", + "integrity": "sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.0", + "@typescript-eslint/visitor-keys": "8.71.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz", + "integrity": "sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.0.tgz", + "integrity": "sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.0", + "@typescript-eslint/typescript-estree": "8.71.0", + "@typescript-eslint/utils": "8.71.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.0.tgz", + "integrity": "sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.0.tgz", + "integrity": "sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.71.0", + "@typescript-eslint/tsconfig-utils": "8.71.0", + "@typescript-eslint/types": "8.71.0", + "@typescript-eslint/visitor-keys": "8.71.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.0.tgz", + "integrity": "sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.71.0", + "@typescript-eslint/types": "8.71.0", + "@typescript-eslint/typescript-estree": "8.71.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.0.tgz", + "integrity": "sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", + "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001814", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", + "integrity": "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/comment-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.9.tgz", + "integrity": "sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz", + "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.7" + }, + "engines": { + "node": ">=6.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.444", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", + "integrity": "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz", + "integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "11.1.5 || >11.1.6 <12", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-n": { + "version": "18.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-18.4.0.tgz", + "integrity": "sha512-TEm6Nqn9+l7EWfJXOL6pmrEdpiM2ZTgo4fNMRDvBTM70jeQ7x3X3Akcx+sRCIRfpcSVwMypH3ZAA33PLyNKmYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.0", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "globrex": "^0.1.2", + "ignore": "^5.3.2", + "js-yaml": "^5.4.1", + "semver": "^7.6.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.57.1", + "ts-declaration-location": "^1.0.6", + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "ts-declaration-location": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-perfectionist": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.12.1.tgz", + "integrity": "sha512-krdSE7Dz+TCtcVr2+rfNhlOIFFBJcUdoRvjaN7JQnsaVnxJw+BZh9wMkY3MSLCJat+yW+UPTQLKS82Epc7MGZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "^8.70.0", + "@typescript-eslint/utils": "^8.70.0", + "natural-orderby": "^5.0.0" + }, + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-promise": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.3.0.tgz", + "integrity": "sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-regexp": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-3.3.1.tgz", + "integrity": "sha512-58bGUci/AaWAo1ETj6Lsnmy6QXwnSyVONRA805828nnqp7vrTwdaQMc02vtowmYB3A5OB3SLr3r5HgmfxX4dLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "comment-parser": "^1.4.0", + "jsdoc-type-pratt-parser": "^9.0.0", + "refa": "^0.12.1", + "regexp-ast-analysis": "^0.7.1", + "scslre": "^0.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": ">=9.38.0" + } + }, + "node_modules/eslint-plugin-sonarjs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-4.2.2.tgz", + "integrity": "sha512-usNF9GfP89ibwhfxpZaCMW40sC7UEOusDgDCk7lyqAhh4AYdbBaAgLVL5Y/QS0jSed8eJ2ACGH6zzE8ezd/yZQ==", + "dev": true, + "license": "LGPL-3.0-only", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@sonarsource/analyzer-commons-configurations": "^2.31.0-5284", + "builtin-modules": "^3.3.0", + "bytes": "^3.1.2", + "functional-red-black-tree": "^1.0.1", + "globals": "^17.12.0", + "jsx-ast-utils-x": "^0.1.0", + "lodash.merge": "^4.6.2", + "minimatch": "^10.2.6", + "scslre": "^0.3.0", + "semver": "^7.8.5", + "ts-api-utils": "^2.5.0", + "typescript": ">=5 <6.1.0", + "yaml": "^2.9.1" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/globals": { + "version": "17.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", + "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "76.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-76.0.0.tgz", + "integrity": "sha512-3ywJrCMHKYhcBP5yFzGaOOX3dY7+iR3hvpuoS/0EnhRFgVWLejly+t4fOdJvWQxJdHZl1C+QBW7hybXPDld4Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.10.1", + "@eslint/css-tree": "^4.0.5", + "browserslist": "^4.28.8", + "change-case": "^5.4.4", + "ci-info": "^4.4.0", + "core-js-compat": "^3.50.0", + "detect-indent": "^7.0.2", + "entities": "^8.0.0", + "find-up-simple": "^1.0.1", + "globals": "^17.11.0", + "identifier-regex": "^1.1.0", + "indent-string": "^5.0.0", + "is-builtin-module": "^5.0.0", + "pluralize": "^8.0.0", + "quote-js-string": "^0.1.0", + "regjsparser": "^0.13.2", + "reserved-identifiers": "^1.2.0", + "semver": "^7.8.5", + "strip-indent": "^4.1.1", + "yaml": "^2.9.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=10.4" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "17.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", + "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.23" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/gaxios": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.1.tgz", + "integrity": "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/google-auth-library": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.1.tgz", + "integrity": "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/identifier-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/identifier-regex/-/identifier-regex-1.1.0.tgz", + "integrity": "sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==", + "dev": true, + "license": "MIT", + "dependencies": { + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-builtin-module": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", + "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^5.0.0" + }, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-builtin-module/node_modules/builtin-modules": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.4.0.tgz", + "integrity": "sha512-JCWSCdun+4Ovd9BhM/Vtlmwb7oD6Wl4YiPRXXwhAuwlPhW1un/MKgXn7GZoFm53ginnoNzus69V8JWx0K393jg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz", + "integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.2.2.tgz", + "integrity": "sha512-eFY7Rlscvv/BpY6UsVsjKwMhu1tJO/F4U/vC9g3yeRy/JqszuO1UP6Vdq+85G+3FP6fEw/sWR8a5ftPFY86csw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9", + "@types/node": "^26.4.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, + "node_modules/jsdoc-type-pratt-parser/node_modules/@types/node": { + "version": "26.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", + "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/jsdoc-type-pratt-parser/node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsx-ast-utils-x": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", + "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mdn-data": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.35.0.tgz", + "integrity": "sha512-k+1+dEIm2z/BEfdvYzT/fIyAGeDBo/1AJNJI4ilFXJRYoxP/3Ds+GP8shAOIq8jYXY9N5nGB3Ag104NsagmLAA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-orderby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.57", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", + "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/openai": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-7.19.0.tgz", + "integrity": "sha512-MX2s3u2L5racTO0CC/SWpCOasJQBCJrqLKXK+l82cAhdeF8mPMBEe/gxMm0ZFa2xpKpOFLRjxv5afYEZbBXmbQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", + "undici": ">=5 <9", + "ws": "^8.21.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, + "undici": { + "optional": true + }, + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-agent-negotiate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/quote-js-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/quote-js-string/-/quote-js-string-0.1.0.tgz", + "integrity": "sha512-Y3NoRtprEEZQD8RfxMCfS0ZTqc4e+i18OrXEXAvpM6TfC/3y+0L5rNbZiSnbBBEkDfFzbpd8o+cE8q3/anjMGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/quote-js-string?sponsor=1" + } + }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regjsparser": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.3.tgz", + "integrity": "sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standardwebhooks": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", + "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, + "node_modules/strip-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typebox": { + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz", + "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.71.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.0.tgz", + "integrity": "sha512-fBdHYiqQ14RW6mOMXD14Svn82ZsCYAoQSzGRzyEjR59S5A2Krh/l7fGTOQ7iCr8gGy/mHVXtEF7s5fgjEdV0Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.71.0", + "@typescript-eslint/parser": "8.71.0", + "@typescript-eslint/typescript-estree": "8.71.0", + "@typescript-eslint/utils": "8.71.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7a6c5ed --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "@shopware-ag/github-agent-tools", + "version": "4.3.0", + "description": "GitHub CLI MCP servers for pull requests, issues, CI, and repositories, with enforcement that keeps the agent on the MCP tools.", + "type": "module", + "license": "MIT", + "author": "Shopware Labs", + "homepage": "https://github.com/shopwareLabs/github-agent-tools/tree/main/plugins/github-mcp", + "repository": { "type": "git", "url": "git+https://github.com/shopwareLabs/github-agent-tools.git" }, + "bugs": "https://github.com/shopwareLabs/github-agent-tools/issues", + "keywords": ["pi-package", "github", "gh", "mcp"], + "pi": { "extensions": ["./plugins/github-mcp/pi/index.ts"] }, + "files": [ + "plugins/github-mcp/pi/", + "plugins/github-mcp/hooks/", + "plugins/github-mcp/mcp-server-gh/", + "plugins/github-mcp/shared/", + "plugins/github-mcp/README.md", + "plugins/github-mcp/REFERENCE.md", + "plugins/github-mcp/CHANGELOG.md", + "!**/*.log" + ], + "engines": { "node": ">=22.19.0" }, + "publishConfig": { "access": "public" }, + "peerDependencies": { "@earendil-works/pi-coding-agent": "*" }, + "devDependencies": { + "@earendil-works/pi-ai": "1.0.0", + "@earendil-works/pi-coding-agent": "1.0.0", + "@eslint/js": "10.0.1", + "@types/node": "24.19.1", + "eslint": "10.11.0", + "eslint-plugin-n": "18.4.0", + "eslint-plugin-perfectionist": "5.12.1", + "eslint-plugin-promise": "7.3.0", + "eslint-plugin-regexp": "3.3.1", + "eslint-plugin-sonarjs": "4.2.2", + "eslint-plugin-unicorn": "76.0.0", + "typescript": "6.0.3", + "typescript-eslint": "8.71.0" + } +} diff --git a/plugin-tests/github-mcp/check_api_tools.bats b/plugin-tests/github-mcp/check_api_tools.bats index d39e4de..dcf5062 100644 --- a/plugin-tests/github-mcp/check_api_tools.bats +++ b/plugin-tests/github-mcp/check_api_tools.bats @@ -105,6 +105,12 @@ setup_read_blocking() { assert_success } +@test "read api: block_api_tool_write alone leaves the read tool allowed" { + setup_config "gh-tooling" '{"block_api_tool_write": true}' + run_api_hook "$READ_TOOL" "repos/shopware/shopware/pulls/123/comments" + assert_success +} + @test "read api: enforce_mcp_tools false overrides API blocking" { setup_config "gh-tooling" '{"enforce_mcp_tools": false, "block_api_tool_read": true}' run_api_hook "$READ_TOOL" "repos/shopware/shopware/pulls/123/comments" @@ -216,6 +222,12 @@ setup_write_blocking() { assert_success } +@test "write api: block_api_tool_read alone leaves the write tool allowed" { + setup_config "gh-tooling" '{"block_api_tool_read": true}' + run_api_hook "$WRITE_TOOL" "repos/shopware/shopware/pulls/123/comments" "GET" + assert_success +} + @test "write api: also blocks read endpoints" { setup_write_blocking run_api_hook "$WRITE_TOOL" "repos/shopware/shopware/pulls/123/comments" "GET" diff --git a/plugin-tests/github-mcp/fixtures/pi/driver.ts b/plugin-tests/github-mcp/fixtures/pi/driver.ts new file mode 100644 index 0000000..61f0200 --- /dev/null +++ b/plugin-tests/github-mcp/fixtures/pi/driver.ts @@ -0,0 +1,108 @@ +// Scripted model for pi_e2e.bats. Registers the faux provider (model faux/faux-1), answers each +// model request with the next turn of the $SCENARIO script, and records what the package +// extension does: every request's messages go to $TRACE, and every tool call's view of the +// gh-tooling tools goes to $PROBE. Loaded with -e, so it sees each call before the package's gate. +import { appendFileSync } from "node:fs"; +import { + fauxAssistantMessage, + fauxProvider, + type FauxResponseFactory, + fauxText, + fauxToolCall, + type ToolCall, +} from "@earendil-works/pi-ai"; +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +function requiredEnvironmentVariable(name: string): string { + const value = process.env[name]; + if (value === undefined || value === "") { + throw new Error(`driver.ts: ${name} must be set`); + } + return value; +} + +const SCENARIO = requiredEnvironmentVariable("SCENARIO"); +const TRACE = requiredEnvironmentVariable("TRACE"); +const PROBE = requiredEnvironmentVariable("PROBE"); + +const REPO = "shopwareLabs/github-agent-tools"; +const FILES_ENDPOINT = `repos/${REPO}/pulls/8/files`; + +function prView(number: number): ToolCall { + return fauxToolCall( + "mcp__gh_tooling__pr_view", + { fields: "number,title", number, repo: REPO }, + { id: `pr-view-${String(number)}` }, + ); +} + +const ghPrViewInBash = fauxToolCall("bash", { command: "gh pr view 8" }, { id: "bash-gh-pr-view" }); + +/** + * One assistant turn per model request; the last turn ends the run with plain text. + */ +const SCENARIOS: Record = { + codemode: [ + fauxToolCall( + "codemode", + { + code: ` +const results = await Promise.all([11, 12, 13].map((number) => + tools.mcp__gh_tooling__pr_view({ repo: "${REPO}", number, fields: "number,title" }))); +let caught = null; +try { + await tools.mcp__gh_tooling__api_read({ endpoint: "${FILES_ENDPOINT}" }); +} catch (error) { + caught = error.message; +} +return JSON.stringify({ results, caught }); +`, + }, + { id: "codemode-script" }, + ), + "done", + ], + deferred: [ + fauxToolCall("tool_search", { query: "pr_view" }, { id: "search-pr-view" }), + prView(8), + ghPrViewInBash, + fauxToolCall("tool_search", { query: "api_read" }, { id: "search-api-read" }), + fauxToolCall("mcp__gh_tooling__api_read", { endpoint: FILES_ENDPOINT }, { id: "api-read-files" }), + [prView(11), prView(12), prView(13)], + "done", + ], + "environment-leak": [ghPrViewInBash, "done"], + "mcp-override": [prView(8), ghPrViewInBash, "done"], +}; + +const turns = SCENARIOS[SCENARIO]; +if (turns === undefined) { + throw new Error(`driver.ts: unknown SCENARIO ${SCENARIO}`); +} + +const responses: FauxResponseFactory[] = turns.map((turn) => (context) => { + appendFileSync(TRACE, `${JSON.stringify(context.messages)}\n`); + return fauxAssistantMessage(typeof turn === "string" ? fauxText(turn) : turn); +}); + +const isGhTooling = (name: string): boolean => name.startsWith("mcp__gh_tooling"); + +export default function driver(pi: ExtensionAPI): void { + const faux = fauxProvider(); + faux.setResponses(responses); + pi.registerProvider(faux.provider); + + pi.on("tool_call", (event) => { + const ghTools = pi.getAllTools().filter((tool) => isGhTooling(tool.name)); + const exposures = [...new Set(ghTools.map((tool) => tool.exposure))]; + appendFileSync( + PROBE, + `${JSON.stringify({ + active: pi.getActiveTools().filter((name) => isGhTooling(name)).length, + all: ghTools.length, + exposures, + tool: event.toolName, + })}\n`, + ); + }); +} diff --git a/plugin-tests/github-mcp/fixtures/pi/gh-stub.sh b/plugin-tests/github-mcp/fixtures/pi/gh-stub.sh new file mode 100755 index 0000000..3fd07b5 --- /dev/null +++ b/plugin-tests/github-mcp/fixtures/pi/gh-stub.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Stand-in for the gh CLI in pi_e2e.bats, linked as `gh` first on PATH so no +# call reaches GitHub. Logs every invocation to $GH_LOG. Answers only +# `pr view --repo --json `, with {"number":,"title":"stub"} +# restricted to the requested fields; anything else fails. + +set -euo pipefail + +printf '%s\n' "$*" >> "${GH_LOG:?GH_LOG must point at the invocation log}" + +if [[ $# -eq 7 && "$1" == "pr" && "$2" == "view" && "$4" == "--repo" && "$6" == "--json" ]]; then + jq -cn --argjson number "$3" --arg fields "$7" \ + '{number: $number, title: "stub"} | with_entries(select(.key as $key | $fields | split(",") | index($key)))' + exit 0 +fi + +printf 'gh-stub: unsupported invocation: %s\n' "$*" >&2 +exit 1 diff --git a/plugin-tests/github-mcp/gh_tools.bats b/plugin-tests/github-mcp/gh_tools.bats index 2644cd0..9bd189b 100644 --- a/plugin-tests/github-mcp/gh_tools.bats +++ b/plugin-tests/github-mcp/gh_tools.bats @@ -152,6 +152,74 @@ bats_test_function --description "blocks gh run view in ; chain β†’ suggests run assert_output --partial "mcp__gh_tooling__pr_view" } +# Set the hook environment. Empty arguments leave the variable unset. +# Args: $1=GITHUB_MCP_HOST, $2=payload cwd, $3=CLAUDE_PROJECT_DIR +set_hook_env() { + if [[ -n "$1" ]]; then export GITHUB_MCP_HOST="$1"; else unset GITHUB_MCP_HOST; fi + if [[ -n "$2" ]]; then export HOOK_CWD="$2"; mkdir -p "$2"; else unset HOOK_CWD; fi + if [[ -n "$3" ]]; then export CLAUDE_PROJECT_DIR="$3"; mkdir -p "$3"; else unset CLAUDE_PROJECT_DIR; fi +} + +# bats test_tags=host,pi +@test "pi suggestions use the sanitized tool namespace" { + set_hook_env "pi" "${BATS_TEST_TMPDIR}/pi-project" "" + + run_hook "check-gh-tools.sh" "gh pr view 14642" + + assert_failure 2 + assert_output --partial "mcp__gh_tooling__pr_view" +} + +# ============================================================================ +# Host resolution and config directory order +# ============================================================================ + +@test "pi prefers a .pi config over .claude and .codex configs" { + local project="${BATS_TEST_TMPDIR}/pi-project" + write_project_config "$project" ".pi" '{"enforce_mcp_tools": false}' + write_project_config "$project" ".claude" '{"enforce_mcp_tools": true}' + write_project_config "$project" ".codex" '{"enforce_mcp_tools": true}' + set_hook_env "pi" "$project" "" + + run_hook "check-gh-tools.sh" "gh pr view 14642" + + assert_success +} + +# A GITHUB_MCP_HOST exported in the shell does not override Claude Code: the +# payload cwd disables enforcement, CLAUDE_PROJECT_DIR opts into API blocking. +# Args: $1=GITHUB_MCP_HOST +claude_project_dir_wins_over_host() { + local claude_project="${BATS_TEST_TMPDIR}/claude-project" + local payload_project="${BATS_TEST_TMPDIR}/payload-project" + write_project_config "$claude_project" ".claude" '{"enforce_mcp_tools": true, "block_api_commands": true}' + write_project_config "$payload_project" ".${1}" '{"enforce_mcp_tools": false}' + set_hook_env "$1" "$payload_project" "$claude_project" + + run_hook "check-gh-tools.sh" "gh api repos/shopware/shopware/pulls/14642/files" + + assert_failure 2 + assert_output --partial "mcp__plugin_github-mcp_gh-tooling__pr_files" +} + +bats_test_function \ + --description "CLAUDE_PROJECT_DIR resolves as Claude Code even with GITHUB_MCP_HOST=pi" \ + -- claude_project_dir_wins_over_host "pi" +bats_test_function \ + --description "CLAUDE_PROJECT_DIR resolves as Claude Code even with GITHUB_MCP_HOST=codex" \ + -- claude_project_dir_wins_over_host "codex" + +@test "Claude prefers a .pi config over the project-root config" { + local project="${BATS_TEST_TMPDIR}/claude-project" + write_project_config "$project" ".pi" '{"enforce_mcp_tools": false}' + write_project_config "$project" "" '{"enforce_mcp_tools": true}' + set_hook_env "" "" "$project" + + run_hook "check-gh-tools.sh" "gh pr view 14642" + + assert_success +} + # ============================================================================ # block_api_commands: true β€” blocks gh api endpoints with dedicated MCP tools # ============================================================================ diff --git a/plugin-tests/github-mcp/pi/gate.test.ts b/plugin-tests/github-mcp/pi/gate.test.ts new file mode 100644 index 0000000..1fa4922 --- /dev/null +++ b/plugin-tests/github-mcp/pi/gate.test.ts @@ -0,0 +1,114 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test, { type TestContext } from "node:test"; +import { setTimeout as delay } from "node:timers/promises"; +import { runGate, runScript } from "../../../plugins/github-mcp/pi/gate.ts"; + +function makeTemporaryDirectory(t: TestContext): string { + const dir = mkdtempSync(join(tmpdir(), "github-mcp-pi-gate-")); + t.after(() => { + rmSync(dir, { force: true, recursive: true }); + }); + return dir; +} + +function writeScript(dir: string, body: string): string { + const script = join(dir, "script.sh"); + writeFileSync(script, body); + return script; +} + +function isProcessAlive(pid: number): boolean { + try { + process.kill(pid, 0); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ESRCH") { + return false; + } + throw error; + } +} + +test("runScript returns the script's stdout and exit code 0 unchanged", async (t) => { + const script = writeScript(makeTemporaryDirectory(t), "printf ' {\"ok\": true}\\n'\nexit 0\n"); + + const result = await runScript(script, {}, 5000); + + assert.deepEqual(result, { code: 0, stderr: "", stdout: ' {"ok": true}\n' }); +}); + +test("a gate that exits 2 blocks with its trimmed stderr as the reason", async (t) => { + const script = writeScript(makeTemporaryDirectory(t), "printf '\\n Use mcp__gh_tooling__pr_view instead. \\n' >&2\nexit 2\n"); + + const result = await runGate(script, { tool_input: { command: "gh pr view 8" } }, 5000); + + assert.deepEqual(result, { block: true, reason: "Use mcp__gh_tooling__pr_view instead." }); +}); + +test("the script runs with GITHUB_MCP_HOST=pi and without the caller's CLAUDE_PROJECT_DIR", async (t) => { + const previous = process.env.CLAUDE_PROJECT_DIR; + process.env.CLAUDE_PROJECT_DIR = "/claude/project"; + t.after(() => { + if (previous === undefined) { + delete process.env.CLAUDE_PROJECT_DIR; + } else { + process.env.CLAUDE_PROJECT_DIR = previous; + } + }); + const script = writeScript( + makeTemporaryDirectory(t), + 'printf "host=%s claude=%s" "${GITHUB_MCP_HOST-unset}" "${CLAUDE_PROJECT_DIR-unset}"\n', + ); + + const { stdout } = await runScript(script, {}, 5000); + + assert.equal(stdout, "host=pi claude=unset"); +}); + +test("a nonexistent gate script does not block", async (t) => { + const script = join(makeTemporaryDirectory(t), "missing.sh"); + + const result = await runGate(script, {}, 5000); + + assert.deepEqual(result, { block: false }); +}); + +test("a gate that exits 2 without reading its 1 MB input still blocks and raises no unhandled error", async (t) => { + const unhandled: unknown[] = []; + const record = (error: unknown): void => { + unhandled.push(error); + }; + process.on("uncaughtException", record); + process.on("unhandledRejection", record); + t.after(() => { + process.off("uncaughtException", record); + process.off("unhandledRejection", record); + }); + const script = writeScript(makeTemporaryDirectory(t), "exec 0<&-\nprintf 'Use pr_view.' >&2\nexit 2\n"); + + const result = await runGate(script, { payload: "x".repeat(1024 * 1024) }, 5000); + // A late EPIPE would surface after settling; give it a few event-loop turns to arrive. + await delay(100); + + assert.deepEqual(result, { block: true, reason: "Use pr_view." }); + assert.deepEqual(unhandled, []); +}); + +test("a gate that outlives its timeout does not block and its background children are killed", { timeout: 10_000 }, async (t) => { + const dir = makeTemporaryDirectory(t); + const pidFile = join(dir, "sleep.pid"); + const script = writeScript(dir, `sleep 37 &\nprintf '%s' "$!" > '${pidFile}'\nwait\n`); + + const result = await runGate(script, {}, 500); + + assert.deepEqual(result, { block: false }); + const sleepPid = Number(readFileSync(pidFile, "utf8")); + // SIGKILL delivery and reaping of the orphaned sleep are asynchronous; poll briefly. + for (let attempt = 0; attempt < 40 && isProcessAlive(sleepPid); attempt++) { + await delay(50); + } + assert.equal(isProcessAlive(sleepPid), false); +}); diff --git a/plugin-tests/github-mcp/pi/index.test.ts b/plugin-tests/github-mcp/pi/index.test.ts new file mode 100644 index 0000000..02c86af --- /dev/null +++ b/plugin-tests/github-mcp/pi/index.test.ts @@ -0,0 +1,128 @@ +import assert from "node:assert/strict"; +import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import test, { type TestContext } from "node:test"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import type githubMcp from "../../../plugins/github-mcp/pi/index.ts"; +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +type Handler = (event: unknown, ctx: unknown) => unknown; + +const PLUGIN_DIR = fileURLToPath(new URL("../../../plugins/github-mcp", import.meta.url)); + +function makeTemporaryDirectory(t: TestContext): string { + const dir = mkdtempSync(path.join(tmpdir(), "github-mcp-pi-index-")); + t.after(() => { + rmSync(dir, { force: true, recursive: true }); + }); + return dir; +} + +function noop(): void { + // The fake pi has nothing to unsubscribe and no server to start. +} + +/** + * Registers the extension on a fake pi that only records the event handlers. + */ +function register(extension: typeof githubMcp): Map { + const handlers = new Map(); + const pi = { + on: (name: string, handler: Handler) => { + handlers.set(name, handler); + return noop; + }, + registerMcpServer: noop, + }; + extension(pi as unknown as ExtensionAPI); + return handlers; +} + +function handler(handlers: Map, name: string): Handler { + const found = handlers.get(name); + if (found === undefined) { + throw new Error(`the extension registered no ${name} handler`); + } + return found; +} + +/** + * Loads a copy of the extension whose session-start.sh is replaced by `scriptBody`, starts a + * session, and returns the system prompt sections the next agent start receives. + */ +async function sectionsAfterSessionStart(t: TestContext, scriptBody: string): Promise> { + const dir = makeTemporaryDirectory(t); + for (const part of ["pi", "hooks", "shared"]) { + cpSync(path.join(PLUGIN_DIR, part), path.join(dir, "plugin", part), { recursive: true }); + } + writeFileSync(path.join(dir, "plugin", "hooks", "scripts", "session-start.sh"), scriptBody); + const indexUrl = pathToFileURL(path.join(dir, "plugin", "pi", "index.ts")).href; + const { default: extension } = (await import(indexUrl)) as { default: typeof githubMcp }; + const handlers = register(extension); + + await handler(handlers, "session_start")({}, { cwd: dir }); + const sections: Record = {}; + await handler(handlers, "before_agent_start")({ systemPromptOptions: { sections } }, { cwd: dir }); + return sections; +} + +async function loadRealExtension(): Promise> { + const { default: extension } = await import("../../../plugins/github-mcp/pi/index.ts"); + return register(extension); +} + +test("the session-start directive becomes the github_mcp system prompt section", async (t) => { + const sections = await sectionsAfterSessionStart( + t, + `printf '%s' '{"hookSpecificOutput":{"additionalContext":"Use the gh-tooling tools."}}'\n`, + ); + + assert.deepEqual(sections, { github_mcp: "Use the gh-tooling tools." }); +}); + +for (const { output, problem } of [ + { output: "Use the gh-tooling tools.", problem: "is not JSON" }, + { output: '{"additionalContext":"Use the gh-tooling tools."}', problem: "has no hookSpecificOutput" }, + { output: '{"hookSpecificOutput":{"context":"Use the gh-tooling tools."}}', problem: "has no additionalContext" }, + { + output: '{"hookSpecificOutput":{"additionalContext":["Use the gh-tooling tools."]}}', + problem: "has a non-string additionalContext", + }, +]) { + test(`no github_mcp section is added when the session-start output ${problem}`, async (t) => { + const sections = await sectionsAfterSessionStart(t, `printf '%s' '${output}'\n`); + + assert.deepEqual(sections, {}); + }); +} + +test("no github_mcp section is added when session-start exits non-zero", async (t) => { + const sections = await sectionsAfterSessionStart( + t, + `printf '%s' '{"hookSpecificOutput":{"additionalContext":"Use the gh-tooling tools."}}'\nexit 1\n`, + ); + + assert.deepEqual(sections, {}); +}); + +test("a bash command without gh runs unblocked", async (t) => { + const toolCall = handler(await loadRealExtension(), "tool_call"); + + const result = await toolCall({ input: { command: "ls -la" }, toolName: "bash" }, { cwd: makeTemporaryDirectory(t) }); + + assert.equal(result, undefined); +}); + +test("api_read on an endpoint without a dedicated tool runs unblocked when block_api_tool_read is set", async (t) => { + const dir = makeTemporaryDirectory(t); + writeFileSync(path.join(dir, ".mcp-gh-tooling.json"), '{"block_api_tool_read": true}\n'); + const toolCall = handler(await loadRealExtension(), "tool_call"); + + const result = await toolCall( + { input: { endpoint: "repos/octo/demo/branches" }, toolName: "mcp__gh_tooling__api_read" }, + { cwd: dir }, + ); + + assert.equal(result, undefined); +}); diff --git a/plugin-tests/github-mcp/pi_e2e.bats b/plugin-tests/github-mcp/pi_e2e.bats new file mode 100644 index 0000000..52ffe0a --- /dev/null +++ b/plugin-tests/github-mcp/pi_e2e.bats @@ -0,0 +1,331 @@ +#!/usr/bin/env bats +# bats file_tags=github-mcp,pi,e2e +# Drives the real pi binary against the installed package with a scripted model +# (fixtures/pi/driver.ts) and a stubbed gh (fixtures/pi/gh-stub.sh), offline. +# setup_file installs each package variant, runs pi once per scenario, and keeps +# the outputs; every test asserts over those outputs. +bats_require_minimum_version 1.11.0 + +load 'test_helper/common_setup' + +PI_BIN="${REPO_ROOT}/node_modules/.bin/pi" +FIXTURES_DIR="${BATS_TEST_DIRNAME}/fixtures/pi" +REPO_SLUG="shopwareLabs/github-agent-tools" +FILES_ENDPOINT="repos/${REPO_SLUG}/pulls/8/files" + +# Resolves the GNU `timeout` binary this suite bounds each pi run with. +# Stock macOS ships neither name under PATH's `timeout`; Homebrew's coreutils +# provides it as `gtimeout`. Fails the suite outright rather than skipping. +resolve_timeout_bin() { + if command -v timeout >/dev/null 2>&1; then + command -v timeout + elif command -v gtimeout >/dev/null 2>&1; then + command -v gtimeout + else + printf 'pi_e2e.bats: no "timeout" or "gtimeout" on PATH. Install GNU coreutils (e.g. "brew install coreutils").\n' >&2 + return 1 + fi +} + +# Reproduces offline what `pi install git:…` runs: a clone, then npm install in it. +# The cloned repository is a one-commit snapshot of the working tree (tracked and +# non-ignored untracked files that exist on disk, modes and symlinks kept), so the +# layout ships uncommitted changes exactly as the next commit would. +install_git_layout() { + local source="${BATS_FILE_TMPDIR}/git-source" layout="${BATS_FILE_TMPDIR}/git-layout" + local listed="${BATS_FILE_TMPDIR}/git-source-listed" present="${BATS_FILE_TMPDIR}/git-source-present" + local path + git -C "${REPO_ROOT}" ls-files -z --cached --others --exclude-standard >"${listed}" + while IFS= read -r -d '' path; do + if [[ -e "${REPO_ROOT}/${path}" || -L "${REPO_ROOT}/${path}" ]]; then + printf '%s\0' "${path}" + fi + done <"${listed}" >"${present}" + mkdir -p "${source}" + tar -C "${REPO_ROOT}" --null -T "${present}" -cf "${BATS_FILE_TMPDIR}/git-source.tar" + tar -C "${source}" -xf "${BATS_FILE_TMPDIR}/git-source.tar" + git -C "${source}" init --quiet + git -C "${source}" add --all --force + git -C "${source}" -c user.name=pi-e2e -c user.email=pi-e2e@localhost -c commit.gpgsign=false \ + commit --quiet --no-verify -m "working tree snapshot" + git clone --quiet "${source}" "${layout}" + (cd "${layout}" && npm install --omit=dev --legacy-peer-deps --offline --no-audit --no-fund) + git -C "${layout}" status --porcelain >"${BATS_FILE_TMPDIR}/git-layout-status" +} + +# Reproduces offline what `pi install npm:…` runs: npm install of the tarball under a prefix. +install_npm_layout() { + local pack_dir="${BATS_FILE_TMPDIR}/npm-pack" prefix="${BATS_FILE_TMPDIR}/npm-prefix" tarball + mkdir -p "${pack_dir}" "${prefix}" + tarball=$(cd "${REPO_ROOT}" && npm pack --json --pack-destination "${pack_dir}" | jq -er '.[0].filename') + npm install "${pack_dir}/${tarball}" --prefix "${prefix}" --legacy-peer-deps --offline --no-audit --no-fund +} + +# Runs pi once for a scenario against a package variant; outputs land in +# ${BATS_FILE_TMPDIR}/-/. +# Args: $1=variant (git|npm), $2=scenario (a driver.ts SCENARIO) +run_pi_scenario() { + local variant="$1" scenario="$2" + local run_dir="${BATS_FILE_TMPDIR}/${variant}-${scenario}" package_dir + case "${variant}" in + git) package_dir="${BATS_FILE_TMPDIR}/git-layout" ;; + npm) package_dir="${BATS_FILE_TMPDIR}/npm-prefix/node_modules/@shopware-ag/github-agent-tools" ;; + esac + + mkdir -p "${run_dir}/project/.pi" "${run_dir}/bin" "${run_dir}/agent" + git -C "${run_dir}/project" init --quiet + git -C "${run_dir}/project" remote add origin "https://github.com/${REPO_SLUG}.git" + printf '%s\n' '{"block_api_tool_read": true}' >"${run_dir}/project/.pi/.mcp-gh-tooling.json" + ln -s "${FIXTURES_DIR}/gh-stub.sh" "${run_dir}/bin/gh" + : >"${run_dir}/trace.jsonl" + : >"${run_dir}/probe.jsonl" + : >"${run_dir}/gh.log" + + PI_CODING_AGENT_DIR="${run_dir}/agent" "${PI_BIN}" install "${package_dir}" "${run_dir}/settings.json" + mv "${run_dir}/settings.json" "${run_dir}/agent/settings.json" + ;; + environment-leak) + mkdir -p "${run_dir}/leak/.claude" + printf '%s\n' '{"enforce_mcp_tools": false}' >"${run_dir}/leak/.claude/.mcp-gh-tooling.json" + scenario_env=("CLAUDE_PROJECT_DIR=${run_dir}/leak") + ;; + mcp-override) + printf '%s\n' '{"mcpServers": {"gh-tooling": {"command": "false", "enabled": false}}}' \ + >"${run_dir}/agent/mcp.json" + ;; + esac + + local status=0 + ( + cd "${run_dir}/project" + env -u MCP_GH_TOOLING_CONFIG -u PROJECT_ROOT "${scenario_env[@]}" \ + PATH="${run_dir}/bin:${PATH}" \ + PI_CODING_AGENT_DIR="${run_dir}/agent" \ + SCENARIO="${scenario}" \ + TRACE="${run_dir}/trace.jsonl" \ + PROBE="${run_dir}/probe.jsonl" \ + GH_LOG="${run_dir}/gh.log" \ + "${TIMEOUT_BIN}" 60 "${PI_BIN}" --offline --no-session -e "${FIXTURES_DIR}/driver.ts" \ + --model faux/faux-1 --mode json -p go \ + "${run_dir}/stdout.jsonl" 2>"${run_dir}/stderr.log" + ) || status=$? + printf '%s\n' "${status}" >"${run_dir}/exit-code" +} + +setup_file() { + if [[ ! -x "${PI_BIN}" ]]; then + printf 'pi_e2e.bats: %s not found. Run "npm ci" in %s first.\n' "${PI_BIN}" "${REPO_ROOT}" >&2 + return 1 + fi + TIMEOUT_BIN=$(resolve_timeout_bin) || return 1 + # The environment-leak scenario sets it deliberately; no other run may inherit it. + unset CLAUDE_PROJECT_DIR + + install_git_layout + install_npm_layout + run_pi_scenario git deferred + run_pi_scenario npm deferred + run_pi_scenario git codemode + run_pi_scenario git environment-leak + run_pi_scenario git mcp-override +} + +# Prints {"isError": …, "text": …} for one finished tool call, text being the +# concatenated text parts of its result. +# Args: $1=run (-), $2=toolCallId +tool_result() { + jq -c --arg id "$2" ' + select(.type == "tool_execution_end" and .toolCallId == $id) + | {isError, text: ([.result.content[] | select(.type == "text") | .text] | join(""))} + ' "${BATS_FILE_TMPDIR}/$1/stdout.jsonl" +} + +# Prints {"isError": …, "json": …} for a tool call whose result text is JSON. +# Args: $1=run, $2=toolCallId +tool_result_json() { + tool_result "$1" "$2" | jq -c '{isError, json: (.text | fromjson)}' +} + +# Fails unless the tool call ended as an error whose text contains the fragment. +# Args: $1=run, $2=toolCallId, $3=fragment +assert_tool_error_contains() { + local result + result=$(tool_result "$1" "$2") + run jq -r '.isError' <<<"${result}" + assert_output "true" + run jq -r '.text' <<<"${result}" + assert_output --partial "$3" +} + +# Fails if the gh stub logged an invocation matching the regex. +# Args: $1=run, $2=regex over one logged argv line +refute_gh_invoked() { + run grep -E "$2" "${BATS_FILE_TMPDIR}/$1/gh.log" + assert_failure 1 +} + +# ============================================================================ +# Scenario `deferred` β€” asserted for both package variants +# ============================================================================ + +assert_tools_deferred_until_searched() { + local probe="${BATS_FILE_TMPDIR}/$1/probe.jsonl" expected_count + expected_count=$(jq '.tools | length' "${GH_SERVER_DIR}/tools-read.json") + + run jq -nc 'first(inputs | select(.tool == "tool_search")) | .active' "${probe}" + assert_output "0" + run jq -nc 'first(inputs | select(.tool == "mcp__gh_tooling__pr_view")) | {all, exposures}' "${probe}" + assert_output "{\"all\":${expected_count},\"exposures\":[\"deferred\"]}" +} + +assert_tool_search_loads_pr_view() { + local result + result=$(tool_result "$1" search-pr-view) + run jq -r '.isError' <<<"${result}" + assert_output "false" + run jq -r '.text' <<<"${result}" + assert_output --partial "mcp__gh_tooling__pr_view" +} + +assert_pr_view_returns_stub() { + run tool_result_json "$1" pr-view-8 + assert_output '{"isError":false,"json":{"number":8,"title":"stub"}}' +} + +assert_bash_gh_blocked() { + assert_tool_error_contains "$1" bash-gh-pr-view "mcp__gh_tooling__pr_view" + refute_gh_invoked "$1" '^pr view 8$' +} + +assert_api_read_blocked() { + assert_tool_error_contains "$1" api-read-files "pr_files" + refute_gh_invoked "$1" '^api( |$)' +} + +assert_parallel_pr_views_succeed() { + local number + for number in 11 12 13; do + run tool_result_json "$1" "pr-view-${number}" + assert_output "{\"isError\":false,\"json\":{\"number\":${number},\"title\":\"stub\"}}" + done +} + +assert_first_request_carries_sections() { + local host_pi_text + host_pi_text=$(<"${PLUGIN_DIR}/hooks/prompts/host-pi.md") + local first_system + first_system=$(jq -nc 'first(inputs) | .[0]' "${BATS_FILE_TMPDIR}/$1/trace.jsonl") + + run jq -r '.role' <<<"${first_system}" + assert_output "system" + run jq -r '.sections.github_mcp' <<<"${first_system}" + assert_output --partial "${host_pi_text}" + run jq -r '.sections.mcp_servers' <<<"${first_system}" + assert_output --partial "mcp__gh_tooling (tool_search): " +} + +assert_pi_exits_cleanly() { + run cat "${BATS_FILE_TMPDIR}/$1/exit-code" + assert_output "0" +} + +@test "git layout: npm install in the clone leaves the working tree clean" { + run cat "${BATS_FILE_TMPDIR}/git-layout-status" + assert_output "" +} + +@test "git layout: gh-tooling tools stay inactive until tool_search, and all are deferred" { + assert_tools_deferred_until_searched git-deferred +} + +@test "git layout: tool_search for pr_view loads mcp__gh_tooling__pr_view" { + assert_tool_search_loads_pr_view git-deferred +} + +@test "git layout: pr_view returns the stub's JSON" { + assert_pr_view_returns_stub git-deferred +} + +@test "git layout: bash gh pr view is blocked before gh runs" { + assert_bash_gh_blocked git-deferred +} + +@test "git layout: api_read on the PR files endpoint is blocked in favor of pr_files" { + assert_api_read_blocked git-deferred +} + +@test "git layout: three pr_view calls in one turn all succeed" { + assert_parallel_pr_views_succeed git-deferred +} + +@test "git layout: the first request carries the github_mcp and mcp_servers sections" { + assert_first_request_carries_sections git-deferred +} + +@test "git layout: pi exits 0" { + assert_pi_exits_cleanly git-deferred +} + +@test "npm layout: pr_view returns the stub's JSON" { + assert_pr_view_returns_stub npm-deferred +} + +@test "npm layout: bash gh pr view is blocked before gh runs" { + assert_bash_gh_blocked npm-deferred +} + +@test "npm layout: api_read on the PR files endpoint is blocked in favor of pr_files" { + assert_api_read_blocked npm-deferred +} + +@test "npm layout: the first request carries the github_mcp and mcp_servers sections" { + assert_first_request_carries_sections npm-deferred +} + +# ============================================================================ +# Additional scenarios β€” git layout only +# ============================================================================ + +# Prints the value the codemode script returned: the JSON after its "Output:" line. +codemode_output() { + tool_result git-codemode codemode-script | jq -c '.text | split("\nOutput:\n")[1] | fromjson' +} + +@test "codemode: pr_view calls through Promise.all return the stub's JSON" { + run jq -c '[.results[] | .isError, (.content[0].text | fromjson)]' < <(codemode_output) + assert_output '[false,{"number":11,"title":"stub"},false,{"number":12,"title":"stub"},false,{"number":13,"title":"stub"}]' +} + +@test "codemode: a blocked api_read rejects with the gate's message" { + local payload gate_message + payload=$(jq -cn --arg endpoint "${FILES_ENDPOINT}" --arg cwd "${BATS_FILE_TMPDIR}/git-codemode/project" \ + '{tool_name: "mcp__gh_tooling__api_read", tool_input: {endpoint: $endpoint}, cwd: $cwd}') + # The environment pi's extension gives a gate: setup() exports CLAUDE_PROJECT_DIR, + # which would otherwise resolve the hook as Claude Code. + run --separate-stderr env -u CLAUDE_PROJECT_DIR GITHUB_MCP_HOST=pi bash "${SCRIPTS_DIR}/check-api-tools.sh" <<<"${payload}" + assert_failure 2 + gate_message="${stderr}" + + run jq -r '.caught' < <(codemode_output) + assert_output "${gate_message}" +} + +@test "environment leak: CLAUDE_PROJECT_DIR disabling enforcement does not unblock bash gh" { + assert_tool_error_contains git-environment-leak bash-gh-pr-view "mcp__gh_tooling__pr_view" + refute_gh_invoked git-environment-leak '^pr view 8$' +} + +@test "mcp.json override: pr_view of the disabled server is not found" { + run tool_result git-mcp-override pr-view-8 + assert_output '{"isError":true,"text":"Tool mcp__gh_tooling__pr_view not found"}' +} + +@test "mcp.json override: bash gh pr view is still blocked" { + assert_tool_error_contains git-mcp-override bash-gh-pr-view "mcp__gh_tooling__pr_view" + refute_gh_invoked git-mcp-override '^pr view 8$' +} diff --git a/plugin-tests/github-mcp/session_start.bats b/plugin-tests/github-mcp/session_start.bats index 2cd6f2f..8f3ef1f 100644 --- a/plugin-tests/github-mcp/session_start.bats +++ b/plugin-tests/github-mcp/session_start.bats @@ -58,3 +58,74 @@ run_session_start() { assert_success assert_output "" } + +# ============================================================================ +# Host notes and config directory order +# ============================================================================ + +PI_HOST_NOTE=$(<"${PLUGIN_DIR}/hooks/prompts/host-pi.md") + +# Run session-start.sh in a fresh hook environment; $output is the parsed +# additionalContext. Empty host or Claude project dir leaves that variable unset. +# Args: $1=GITHUB_MCP_HOST, $2=CLAUDE_PROJECT_DIR, $3=payload cwd +run_session_start_context() { + local -a hook_env=(-u GITHUB_MCP_HOST -u CLAUDE_PROJECT_DIR) + [[ -n "$1" ]] && hook_env+=("GITHUB_MCP_HOST=$1") + [[ -n "$2" ]] && hook_env+=("CLAUDE_PROJECT_DIR=$2") + local payload + mkdir -p "$3" + payload=$(jq -cn --arg cwd "$3" '{cwd: $cwd}') + run bash -c \ + 'set -o pipefail; printf "%s" "$1" | env "${@:3}" bash "$2" | jq -r ".hookSpecificOutput.additionalContext"' \ + _ "$payload" "$SESSION_SCRIPT" "${hook_env[@]}" +} + +# bats test_tags=host,pi +@test "pi directive ends with the pi tool-naming note after a blank line" { + run_session_start_context "pi" "" "${BATS_TEST_TMPDIR}/pi-project" + + assert_success + [[ "$output" == *$'\n\n'"${PI_HOST_NOTE}" ]] +} + +@test "Claude directive omits the pi tool-naming note" { + local project="${BATS_TEST_TMPDIR}/claude-project" + + run_session_start_context "" "$project" "$project" + + assert_success + refute_output --partial "${PI_HOST_NOTE}" +} + +@test "Codex directive omits the pi tool-naming note" { + run_session_start_context "" "" "${BATS_TEST_TMPDIR}/codex-project" + + assert_success + refute_output --partial "${PI_HOST_NOTE}" +} + +@test "pi takes enable_write_server from .pi over .claude and .codex" { + local project="${BATS_TEST_TMPDIR}/pi-project" + write_project_config "$project" ".pi" '{"enable_write_server": true}' + write_project_config "$project" ".claude" '{"enable_write_server": false}' + write_project_config "$project" ".codex" '{"enable_write_server": false}' + + run_session_start_context "pi" "" "$project" + + assert_success + assert_output --partial "## Write (gh-tooling-write)" +} + +@test "pi takes labels from .pi over .claude and .codex" { + local project="${BATS_TEST_TMPDIR}/pi-project" + write_project_config "$project" ".pi" '{"labels": {"needs-triage": "New and not yet reviewed"}}' + write_project_config "$project" ".claude" '{"labels": {"claude-label": "From .claude"}}' + write_project_config "$project" ".codex" '{"labels": {"codex-label": "From .codex"}}' + + run_session_start_context "pi" "" "$project" + + assert_success + assert_output --partial "- needs-triage: New and not yet reviewed" + refute_output --partial "claude-label" + refute_output --partial "codex-label" +} diff --git a/plugin-tests/github-mcp/tool_dispatch_declaration.bats b/plugin-tests/github-mcp/tool_dispatch_declaration.bats index 57e852b..6829cc7 100644 --- a/plugin-tests/github-mcp/tool_dispatch_declaration.bats +++ b/plugin-tests/github-mcp/tool_dispatch_declaration.bats @@ -13,6 +13,12 @@ bats_require_minimum_version 1.11.0 load 'test_helper/common_setup' setup() { + # A private copy: these tests start the server directly, and the real + # plugin directory is shared with every other suite in this run β€” + # running it in place would append to its log on every test. + PLUGIN_COPY="${BATS_TEST_TMPDIR}/plugin" + cp -R "${PLUGIN_DIR}" "${PLUGIN_COPY}" + GH_SERVER_COPY_DIR="${PLUGIN_COPY}/mcp-server-gh" PROJECT_DIR="${BATS_TEST_TMPDIR}/project" mkdir -p "${PROJECT_DIR}" } @@ -22,7 +28,7 @@ setup() { call_tool() { local server="$1" tool="$2" project_root="$3" printf '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"%s","arguments":{}}}\n' "$tool" \ - | env PROJECT_ROOT="$project_root" bash "${GH_SERVER_DIR}/${server}" 2>/dev/null \ + | env PROJECT_ROOT="$project_root" bash "${GH_SERVER_COPY_DIR}/${server}" 2>/dev/null \ | tail -1 \ | jq -r '.result.content[0].text // .error.message // "no answer"' } @@ -101,12 +107,12 @@ undeclared_tools() { # startup-written list behind. What matters is that this run writes none. # The shipped tools-empty.json does not match the pattern. local before after - before=$(find "${GH_SERVER_DIR}" -maxdepth 1 -name 'tools-empty.*.json' | sort) + before=$(find "${GH_SERVER_COPY_DIR}" -maxdepth 1 -name 'tools-empty.*.json' | sort) printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \ - | env PROJECT_ROOT="${PROJECT_DIR}" bash "${GH_SERVER_DIR}/server-write.sh" >/dev/null 2>&1 + | env PROJECT_ROOT="${PROJECT_DIR}" bash "${GH_SERVER_COPY_DIR}/server-write.sh" >/dev/null 2>&1 - after=$(find "${GH_SERVER_DIR}" -maxdepth 1 -name 'tools-empty.*.json' | sort) + after=$(find "${GH_SERVER_COPY_DIR}" -maxdepth 1 -name 'tools-empty.*.json' | sort) [[ "$before" == "$after" ]] } diff --git a/plugin-tests/github-mcp/write_server_gating.bats b/plugin-tests/github-mcp/write_server_gating.bats index fb68a1f..7cd788c 100644 --- a/plugin-tests/github-mcp/write_server_gating.bats +++ b/plugin-tests/github-mcp/write_server_gating.bats @@ -5,7 +5,14 @@ bats_require_minimum_version 1.11.0 load 'test_helper/common_setup' -SERVER_SCRIPT="${GH_SERVER_DIR}/server-write.sh" +# A private copy: these tests start the server directly, and the real plugin +# directory is shared with every other suite in this run β€” running it in +# place would append to its server-write.log on every test. +setup() { + PLUGIN_COPY="${BATS_TEST_TMPDIR}/plugin" + cp -R "${PLUGIN_DIR}" "${PLUGIN_COPY}" + SERVER_SCRIPT="${PLUGIN_COPY}/mcp-server-gh/server-write.sh" +} # Helper: send a JSON-RPC request send_jsonrpc() { @@ -34,14 +41,6 @@ run_server_request() { run bash -c 'echo "$1" | bash "$2" 2>/dev/null | tail -1' _ "${requests}" "${SERVER_SCRIPT}" } -run_codex_server_request() { - local project_root="$1" - local requests="$2" - - printf '%s' "$requests" | env PROJECT_ROOT="$project_root" GITHUB_MCP_HOST=codex \ - bash "$SERVER_SCRIPT" 2>/dev/null | tail -1 -} - @test "write server returns empty tools list when enable_write_server is false" { run_server_request '{"enable_write_server": false}' "tools/list" assert_success @@ -81,20 +80,67 @@ run_codex_server_request() { [[ "$tool_count" -eq 0 ]] } -@test "Codex server prefers .codex config over .claude config" { - local project_root="${BATS_TEST_TMPDIR}/codex-priority" - mkdir -p "${project_root}/.claude" "${project_root}/.codex" - printf '%s\n' '{"enable_write_server": false}' > "${project_root}/.claude/.mcp-gh-tooling.json" - printf '%s\n' '{"enable_write_server": true}' > "${project_root}/.codex/.mcp-gh-tooling.json" +# ============================================================================ +# Config directory order +# ============================================================================ +# Run the write server for a host and print the tools/list response. +# Args: $1=GITHUB_MCP_HOST value ("" for none), $2=project root +run_host_server_tools_list() { + local host="$1" + local project_root="$2" local requests requests=$(send_jsonrpc "initialize" 1) requests+=$'\n' requests+=$(send_jsonrpc "tools/list" 2) - run run_codex_server_request "$project_root" "$requests" + + local -a host_env=(-u GITHUB_MCP_HOST) + [[ -n "$host" ]] && host_env=("GITHUB_MCP_HOST=${host}") + printf '%s' "$requests" | env "${host_env[@]}" PROJECT_ROOT="$project_root" \ + bash "$SERVER_SCRIPT" 2>/dev/null | tail -1 +} + +@test "Codex server prefers .codex config over .claude config" { + local project_root="${BATS_TEST_TMPDIR}/codex-priority" + write_project_config "$project_root" ".codex" '{"enable_write_server": true}' + write_project_config "$project_root" ".claude" '{"enable_write_server": false}' + + run run_host_server_tools_list "codex" "$project_root" assert_success - local tool_count - tool_count=$(echo "$output" | jq '.result.tools | length') - [[ "$tool_count" -gt 0 ]] + [[ "$(jq '.result.tools | length' <<<"$output")" -gt 0 ]] +} + +@test "pi server prefers .pi config over .claude and .codex configs" { + local project_root="${BATS_TEST_TMPDIR}/pi-priority" + write_project_config "$project_root" ".pi" '{"enable_write_server": true}' + write_project_config "$project_root" ".claude" '{"enable_write_server": false}' + write_project_config "$project_root" ".codex" '{"enable_write_server": false}' + + run run_host_server_tools_list "pi" "$project_root" + + assert_success + [[ "$(jq '.result.tools | length' <<<"$output")" -gt 0 ]] +} + +@test "Claude server prefers .claude config over .pi config" { + local project_root="${BATS_TEST_TMPDIR}/claude-over-pi" + write_project_config "$project_root" ".claude" '{"enable_write_server": true}' + write_project_config "$project_root" ".pi" '{"enable_write_server": false}' + + run run_host_server_tools_list "" "$project_root" + + assert_success + [[ "$(jq '.result.tools | length' <<<"$output")" -gt 0 ]] +} + +@test "Claude server prefers .pi config over the project-root config" { + local project_root="${BATS_TEST_TMPDIR}/claude-pi-over-root" + write_project_config "$project_root" ".pi" '{"enable_write_server": true}' + write_project_config "$project_root" "" '{"enable_write_server": false}' + + run run_host_server_tools_list "" "$project_root" + + assert_success + [[ "$(jq '.result.tools | length' <<<"$output")" -gt 0 ]] } diff --git a/plugin-tests/test_helper/common_setup.bash b/plugin-tests/test_helper/common_setup.bash index 2bcd07f..ce96aa0 100644 --- a/plugin-tests/test_helper/common_setup.bash +++ b/plugin-tests/test_helper/common_setup.bash @@ -12,6 +12,16 @@ _get_repo_root() { REPO_ROOT="$(_get_repo_root)" +# The hooks select their host from GITHUB_MCP_HOST; a value from the developer's +# shell would change their behavior. Tests that need a host set it explicitly. +unset GITHUB_MCP_HOST + +# The servers honor MCP_GH_TOOLING_CONFIG and PROJECT_ROOT ahead of project +# config locations; values from the developer's shell would redirect config +# lookup. Tests that need either set them explicitly. +unset MCP_GH_TOOLING_CONFIG +unset PROJECT_ROOT + # Load BATS helper libraries load "${REPO_ROOT}/.bats/bats-support/load" load "${REPO_ROOT}/.bats/bats-assert/load" @@ -67,6 +77,13 @@ setup_codex_config() { printf '%s\n' "$content" > "${CODEX_PROJECT_DIR}/.codex/.mcp-${prefix}.json" } +# Write a config file into a project directory, creating the directory. +# Args: $1=project dir, $2=config dir relative to it ("" for the root), $3=JSON +write_project_config() { + mkdir -p "${1}/${2}" + printf '%s\n' "$3" > "${1}/${2}/.mcp-gh-tooling.json" +} + # Default teardown for suites using setup_config; test files may override. teardown() { unset CLAUDE_PROJECT_DIR diff --git a/plugins/github-mcp/AGENTS.md b/plugins/github-mcp/AGENTS.md index a4fe6ab..98d103a 100644 --- a/plugins/github-mcp/AGENTS.md +++ b/plugins/github-mcp/AGENTS.md @@ -8,24 +8,34 @@ plugins/github-mcp/ β”œβ”€β”€ REFERENCE.md # Full tool parameter docs and examples (31 read + 25 write tools) β”œβ”€β”€ AGENTS.md # LLM navigation guide (this file) β”œβ”€β”€ CHANGELOG.md # Version history +β”œβ”€β”€ SETUP.md # Interactive setup procedure (kept byte-identical to plugin-setup's copy) β”‚ β”œβ”€β”€ .claude-plugin/plugin.json # Claude Code plugin manifest β”œβ”€β”€ .codex-plugin/plugin.json # Codex plugin manifest + inline MCP registrations β”œβ”€β”€ .mcp.json # Claude Code MCP registrations β”‚ +β”œβ”€β”€ pi/ # PI EXTENSION +β”‚ β”œβ”€β”€ index.ts # Extension factory: server registration, directive, gates +β”‚ └── gate.ts # runScript()/runGate(): spawns a hook script, parses its result +β”‚ β”œβ”€β”€ hooks/ # HOOKS (MCP tool enforcement) β”‚ β”œβ”€β”€ hooks.json # Hook configuration (SessionStart + PreToolUse x3) β”‚ β”œβ”€β”€ prompts/ -β”‚ β”‚ └── mcp-tool-directives.md # SessionStart prompt template: MCP tool listing and usage rules +β”‚ β”‚ β”œβ”€β”€ mcp-tool-directives.md # SessionStart prompt template: MCP tool listing and usage rules +β”‚ β”‚ β”œβ”€β”€ write-operations-enabled.md # {{WRITE_SECTION}} filler when enable_write_server is true +β”‚ β”‚ β”œβ”€β”€ write-operations-disabled.md # {{WRITE_SECTION}} filler when enable_write_server is false/absent +β”‚ β”‚ β”œβ”€β”€ label-definitions-header.md # {{LABEL_SECTION}} header, followed by one generated line per label +β”‚ β”‚ └── host-pi.md # host-.md: appended after the assembled template for that host β”‚ └── scripts/ β”‚ β”œβ”€β”€ session-start.sh # SessionStart hook: assembles prompt from template + conditional sections β”‚ β”œβ”€β”€ check-gh-tools.sh # Blocks common gh CLI bash commands (read + write) β”‚ β”œβ”€β”€ check-api-tools.sh # Blocks MCP api_read/api tool bypass of dedicated tools β”‚ └── lib/ -β”‚ └── common.sh # Shared: parse_hook_input(), load_mcp_config(), block_tool() +β”‚ └── common.sh # Shared: resolve_hook_context(), find_mcp_config(), parse_hook_input(), load_mcp_config(), block_tool() β”‚ β”œβ”€β”€ shared/ # SHARED FRAMEWORK (language-agnostic) -β”‚ └── mcpserver_core.sh # JSON-RPC 2.0 protocol handler +β”‚ β”œβ”€β”€ mcpserver_core.sh # JSON-RPC 2.0 protocol handler (vendored) +β”‚ └── config-dirs.sh # github_mcp_config_dirs(): host config directory order for hooks and servers β”‚ └── mcp-server-gh/ # GITHUB CLI MCP SERVERS β”œβ”€β”€ server-read.sh # Read server entry point - loads optional .mcp-gh-tooling.json @@ -34,9 +44,10 @@ plugins/github-mcp/ β”œβ”€β”€ config-write.json # Write server metadata (name="gh-tooling-write") β”œβ”€β”€ tools-read.json # 31 read tools (PR, issue, CI, commit, search, repo, release, label, project, api_read) β”œβ”€β”€ tools-write.json # 25 write tools (PR lifecycle, reviews, issues, issue types/fields, labels, assignees, sub-issues, projects, api) + β”œβ”€β”€ tools-empty.json # Tools list the write server reports while enable_write_server is false β”œβ”€β”€ mcp-gh-tooling.schema.json # JSON Schema for .mcp-gh-tooling.json └── lib/ - β”œβ”€β”€ common.sh # _gh_validate_number/repo/sha(), _gh_resolve_repo(), _gh_validate_jq_filter(), _gh_post_process(), _gh_parse_github_url(), _gh_validate_path(), _gh_download_file(), _gh_resolve_owner_repo() + β”œβ”€β”€ common.sh # _load_gh_config(), _gh_validate_number/repo/sha(), _gh_resolve_repo(), _gh_validate_jq_filter(), _gh_post_process(), _gh_parse_github_url(), _gh_validate_path(), _gh_download_file(), _gh_resolve_owner_repo() β”œβ”€β”€ pr.sh # tool_pr_view/diff/list/checks/comments/reviews/files/commits() β”œβ”€β”€ pr_write.sh # tool_pr_create/edit/ready/merge/close/reopen() β”œβ”€β”€ issue.sh # tool_issue_view(), tool_issue_list() @@ -72,17 +83,29 @@ This plugin provides: - `check-gh-tools.sh` - Blocks bash commands that should use MCP tools instead (both read and write commands) - `check-api-tools.sh` - Blocks `api_read` and `api` MCP tools when targeting endpoints with dedicated tools (opt-in via `block_api_tool_read`/`block_api_tool_write`) - All hook types configurable via `enforce_mcp_tools: false` in `.mcp-gh-tooling.json` +- **pi Extension** via `pi/index.ts` (the package entry point declared in root `package.json`'s + `pi.extensions`), loaded only by pi: + - Registers both MCP servers with `exposure: "deferred"`, same command scripts as Claude Code and + Codex + - Runs `session-start.sh` on `session_start` and stores its directive for `before_agent_start` + - Runs `check-gh-tools.sh`/`check-api-tools.sh` on `tool_call` through `pi/gate.ts`'s + `runGate()`/`runScript()` in place of hooks.json's PreToolUse matchers ## Architecture ### Config Loading -The gh-tooling servers have their own config loading logic independent of any shared config framework: +The gh-tooling servers load their config with `_load_gh_config()` in `mcp-server-gh/lib/common.sh`: - Config is **optional** (works without any config file if `gh` is authenticated) - Provides a default repo so `repo` doesn't need to be passed to every tool call -- Config discovery checks standard locations, including the project root, `.claude/`, and `.codex/` -- When both host-specific files exist, the active host's file has the highest priority and the - other host's file remains a fallback +- Config discovery checks standard locations, including the project root, `.claude/`, `.codex/`, + and `.pi/` +- The host directory order comes from `github_mcp_config_dirs()` in `shared/config-dirs.sh`, which + the hooks' `find_mcp_config()` also uses: the active host's directory first, then the others in the + fixed order `.claude/`, `.codex/`, `.pi/`. The servers take the host from `GITHUB_MCP_HOST`; any + value other than `pi` or `codex` uses the Claude Code order +- The hooks' `resolve_hook_context()` selects Claude Code whenever `CLAUDE_PROJECT_DIR` is set, then + `GITHUB_MCP_HOST` (`pi` or `codex`), and falls back to Codex - Write server checks `enable_write_server` flag and returns empty tools list when disabled ### Protocol Flow @@ -120,6 +143,7 @@ Captures `__raw` and `__exit` separately; branches on `suppress_errors` for `2>/ | Add blocked gh command | `hooks/scripts/check-gh-tools.sh` | - | `block_tool()`, grep pattern | | Add blocked API endpoint | `hooks/scripts/check-api-tools.sh` | - | Endpoint pattern matching | | Modify shared hook logic | `hooks/scripts/lib/common.sh` | - | `parse_hook_input()`, `load_mcp_config()`, `block_tool()` | +| Change the host config directory order | `shared/config-dirs.sh` | - | `github_mcp_config_dirs()`, used by hooks and servers | | Modify Claude Code registration | `.mcp.json` | `.claude-plugin/plugin.json` | `${CLAUDE_PLUGIN_ROOT}` | | Modify Codex registration | `.codex-plugin/plugin.json` | - | Inline `mcpServers`, inherited project cwd | | Disable hook enforcement | `.mcp-gh-tooling.json` | - | `enforce_mcp_tools: false` | @@ -149,12 +173,21 @@ Captures `__raw` and `__exit` separately; branches on `suppress_errors` for `2>/ 5. Add bash command blocking in `hooks/scripts/check-gh-tools.sh` 6. Update README.md and REFERENCE.md +**Modifying the pi extension:** +1. `pi/index.ts` registers the servers, runs the SessionStart directive, and wires the two gates β€” + it calls the same scripts as `hooks/hooks.json`, so a new blocked command or a directive change + needs no change here +2. `pi/gate.ts` only spawns a script and interprets its exit code; change it only to change how a + gate script's result is turned into a block/allow decision +3. Both files import pi's own packages with `import type` only, so the extension keeps no runtime + dependency on them + **Key design decisions:** - No environment wrapping (gh always runs natively on host) - Config is optional (no config = works with no default repo) - Uses bash arrays instead of string eval for injection safety - Read/write separation: read server always active, write server gated by config flag -- Claude Code and Codex launch the same server scripts; do not fork the MCP implementation by host +- Every host launches the same server scripts; do not fork the MCP implementation by host - The Codex launcher locates the installed plugin but leaves `cwd` unset so the server inherits the active project directory used for GitHub repository inference and project config discovery - Hook has three enforcement layers: `enforce_mcp_tools` (default `true`) blocks high-level subcommands; `block_api_commands` (default `false`, opt-in) blocks `gh api` bash calls; `block_api_tool_read`/`block_api_tool_write` (default `false`, opt-in) blocks MCP API tool bypass @@ -168,6 +201,7 @@ the host: |------|------------|-------------| | Claude Code | `mcp__plugin_github-mcp_gh-tooling__` | `mcp__plugin_github-mcp_gh-tooling-write__` | | Codex | `mcp__gh_tooling__` | `mcp__gh_tooling_write__` | +| pi | `mcp__gh_tooling__` | `mcp__gh_tooling_write__` | ```yaml # Codex read tools @@ -179,26 +213,45 @@ tools: mcp__gh_tooling_write__pr_create, mcp__gh_tooling_write__pr_comment, mcp_ ## Testing -BATS tests for hook scripts and MCP tool functions are in `plugin-tests/github-mcp/`: +BATS tests for hook scripts and MCP tool functions are in `plugin-tests/github-mcp/`; the pi extension's +own unit tests are Node tests under `plugin-tests/github-mcp/pi/`: | Test File | Coverage | |-----------|----------| -| `gh_tools.bats` | GitHub CLI tool blocking (gh pr, gh issue, gh run, gh search, gh label, gh project, gh api) | -| `check_api_tools.bats` | Dedicated API-tool enforcement for Claude Code and Codex tool namespaces | +| `api_read_restriction.bats` | `api_read`'s GET-only method allow-list versus `api`'s full method access | +| `check_api_tools.bats` | Dedicated API-tool enforcement for the Claude Code tool namespace and the sanitized one Codex and pi share | +| `download_cancel_cleanup.bats` | Partial-file cleanup when a `repo_file` or `search_code` download is cancelled mid-write | +| `gh_tools.bats` | GitHub CLI read-command blocking (gh pr, gh issue, gh run, gh search, gh api) and host and config resolution | +| `gh_tools_write.bats` | GitHub CLI blocking for the write-server commands (gh pr/issue create/edit/close/reopen/review/comment, gh project item-add/item-edit) and for `gh label list` and `gh project list`/`view` | +| `mcp_tool_gh.bats` | MCP tool shared parameters (`_gh_validate_jq_filter`, `_gh_post_process`, `suppress_errors`, `fallback`) and core read tool behavior (`pr_view`/`diff`/`list`/`checks`/`comments`/`reviews`/`files`/`commits`, `issue_view`/`list`, `run_view`/`list`/`logs`, `workflow_jobs`, `commit_pulls`, `search`/`search_code`/`search_repos`/`search_commits`/`search_discussions`, `repo_tree`/`repo_file`, `job_view`/`logs`/`annotations`) | +| `pi_e2e.bats` | End-to-end `pi` binary runs against a scripted model and stubbed `gh`, in both git-clone and npm-install layouts, plus codemode and config-override cases | +| `read_tools_escape_sequences.bats` | ANSI escape-sequence stripping and byte-for-byte downloads across `job_logs`, `api`, `repo_file` | +| `read_tools_issue_schema.bats` | `issue_schema` org resolution, name filters, and merge output | +| `read_tools_issue_view_fields.bats` | `issue_view`'s `with_field_values` REST merge, single-select option-name resolution, and validation | +| `read_tools_jq_filter_fields.bats` | `jq_filter`/`fields` interaction across the pr, run, issue, and search read tools | +| `read_tools_new.bats` | `label_list`, `project_list`, `project_view`, including their `jq_filter` handling | +| `release_tools.bats` | `release_list` semver ranking, prereleases, major/minor constraints, batch mode, `fields`, and `resolve_sha` | +| `server_startup_tools_list.bats` | `server-read.sh` startup validation of `tools-read.json` | | `session_start.bats` | Shared SessionStart context and host-specific config discovery | +| `session_start_write.bats` | SessionStart write-section and label-section rendering | +| `tool_dispatch_declaration.bats` | Read and write server dispatch limited to the tools each declares | +| `tool_schemas.bats` | Shipped tool schemas against the vendored validator: identifier unions, required fields, defaults, and validation round-trips | | `write_server_gating.bats` | Write-server gating and active-host config priority | -| `read_tools_issue_schema.bats` | `issue_schema` org resolution, name filters, and merge output | +| `write_tools_edit_params.bats` | `label_add`/`label_remove` and `assignee_add`/`assignee_remove` routing through `gh pr edit`/`gh issue edit` | +| `write_tools_graphql.bats` | `sub_issue_add`/`sub_issue_remove` GraphQL mutations and node-ID resolution | +| `write_tools_issue.bats` | `issue_create`/`edit`/`close`/`reopen`/`comment` parameter handling | | `write_tools_issue_schema.bats` | `issue_type_set` and `issue_field_set` name resolution and value checks | -| `mcp_tool_gh.bats` | MCP tool shared parameters (_gh_validate_jq_filter, _gh_post_process, suppress_errors, fallback) | -| `tool_schemas.bats` | Shipped tool schemas against the vendored validator: identifier unions, required fields, defaults, and validation round-trips | +| `write_tools_pr.bats` | `pr_create`/`edit`/`ready`/`merge`/`close`/`reopen` parameter handling | +| `write_tools_project.bats` | `project_item_add` and `project_status_set` name-to-ID resolution | +| `write_tools_review.bats` | `pr_review_submit`/`pr_comment`/`pr_review_reply` parameter handling and REST payloads | +| `pi/gate.test.ts` | `runGate()`/`runScript()`: exit codes, stderr capture, stdin handling, and the timeout that kills a gate's background children | +| `pi/index.test.ts` | The extension's event handlers on a fake pi: the `github_mcp` section from session-start output (malformed output and non-zero exits add none), and which `bash` and `api_read` calls the gates block or let through | The vendored SDK's own surface β€” argument validation and logging β€” is tested upstream in `shopwareLabs/bash-mcp-sdk`, not here. -Run tests: -```bash -.bats/bats-core/bin/bats plugin-tests/github-mcp/*.bats -``` +Run tests: see root `AGENTS.md` Β§BATS and Β§Node and pi for the exact commands, the one-time BATS +setup, and the `pi_e2e.bats` prerequisites (`npm ci`, GNU `timeout`/`gtimeout`). ## External References diff --git a/plugins/github-mcp/CHANGELOG.md b/plugins/github-mcp/CHANGELOG.md index 8f2e4e8..7d2be1e 100644 --- a/plugins/github-mcp/CHANGELOG.md +++ b/plugins/github-mcp/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- github-mcp runs in [pi](https://pi.dev) as a pi package. `plugins/github-mcp/pi/index.ts` is a pi + extension that registers the same two MCP servers, runs the same SessionStart directive script, + and runs the same `check-gh-tools.sh`/`check-api-tools.sh` gates through `pi/gate.ts` in place of + Claude Code's and Codex's PreToolUse hooks. The repository root is now also the npm package + `@shopware-ag/github-agent-tools`, installable with `pi install npm:@shopware-ag/github-agent-tools` + or `pi install git:github.com/shopwareLabs/github-agent-tools`, and published to the npm registry + by `.github/workflows/npm-publish.yml` on a pushed release tag. + +### Changed + +- `.pi/.mcp-gh-tooling.json` is a new config location, checked by the MCP servers and the + enforcement hooks on all three hosts, ahead of the project root. Claude Code and Codex now also + consult it: when it exists alongside a `.claude/` or `.codex/` file, the active host's own + directory still wins, but `.pi/` now ranks ahead of the project-root fallback for every host. + ## [4.3.0] - 2026-10-02 ### Fixed diff --git a/plugins/github-mcp/README.md b/plugins/github-mcp/README.md index 529dc51..b67d5a0 100644 --- a/plugins/github-mcp/README.md +++ b/plugins/github-mcp/README.md @@ -48,6 +48,11 @@ codex plugin add github-mcp@github-agent-tools Start a new Codex task after installation. Open `/hooks` to review and trust the bundled hooks; Codex skips plugin hooks until they are trusted. +### pi + +See the root [README](../../README.md#pi) for the install commands. A new pi session or `/reload` +loads the package. + ### Interactive Setup (Claude Code only) Install the `plugin-setup` plugin, then ask Claude to help you set up github-mcp: @@ -60,7 +65,7 @@ Install the `plugin-setup` plugin, then ask Claude to help you set up github-mcp Help me set up github-mcp ``` -The `github-mcp-setting-up` skill verifies prerequisites (`gh`, `jq`) and optionally creates a config file with a default repository. It uses Claude Code-specific interaction and permission settings, so it is not listed in the Codex marketplace. Codex users configure the plugin manually as described below. +The `github-mcp-setting-up` skill verifies prerequisites (`gh`, `jq`) and optionally creates a config file with a default repository. It uses Claude Code-specific interaction and permission settings, so it is not listed in the Codex marketplace. Codex and pi users configure the plugin manually as described below. ### Verification @@ -143,7 +148,7 @@ Configuration is loaded in the following priority order: - `.kiro/.mcp-gh-tooling.json` (Kiro) - `.windsurf/.mcp-gh-tooling.json` (Windsurf/Codeium) - `.zed/.mcp-gh-tooling.json` (Zed editor) - - Host override directories: `.claude/.mcp-gh-tooling.json` and `.codex/.mcp-gh-tooling.json`. When both exist, the active host's directory has highest priority; the other host's file remains a fallback. + - Host override directories: `.claude/.mcp-gh-tooling.json`, `.codex/.mcp-gh-tooling.json`, and `.pi/.mcp-gh-tooling.json`. The active host's directory has the highest priority, followed by the others in the fixed order `.claude/`, `.codex/`, `.pi/`. The servers take the host from `GITHUB_MCP_HOST` (`pi` or `codex`; any other value or none means Claude Code). The enforcement hooks use the same order, then the project root, and take the first match. **Prerequisites:** - `gh` CLI installed: `brew install gh` (macOS) or see [GitHub CLI installation](https://cli.github.com/) @@ -235,6 +240,12 @@ Blocks bash commands that match known `gh` subcommands and redirects to the corr Optionally blocks the `api_read` and `api` tools when they target endpoints that have dedicated MCP tools. Configured separately via `block_api_tool_read` and `block_api_tool_write`. Implemented in `hooks/scripts/check-api-tools.sh`. +On pi, the extension's `tool_call` handler runs `check-gh-tools.sh` and `check-api-tools.sh` and +blocks the call when a script exits `2`. + +A gate script blocks only by exiting `2`. Any other outcome β€” a crash, a timeout, a missing script β€” +lets the call through, on every host. + All hook layers respect the `enforce_mcp_tools` setting and are disabled when set to `false`. ### Disabling Enforcement @@ -327,15 +338,34 @@ The raw server IDs stay `gh-tooling` and `gh-tooling-write`, but each host rende |---|---|---| | Claude Code | `mcp__plugin_github-mcp_gh-tooling__pr_view` | `mcp__plugin_github-mcp_gh-tooling-write__pr_create` | | Codex | `mcp__gh_tooling__pr_view` | `mcp__gh_tooling_write__pr_create` | +| pi | `mcp__gh_tooling__pr_view` | `mcp__gh_tooling_write__pr_create` | Use the identifiers exposed by the active host rather than copying the other host's spelling. +## pi + +- Both servers are registered with `exposure: "deferred"`: pi lists `gh-tooling` and + `gh-tooling-write` in its system prompt's `mcp_servers` section, and a tool is undeclared β€” so + the model does not see it as a direct tool call option β€” until `tool_search` loads it. A codemode + script can call a deferred tool whether or not it has been declared. +- An MCP result over 20 KB reaches the model with the middle cut out. Pass `max_lines`, `fields`, + or `jq_filter` to keep a result under that limit. +- A `gh-tooling` entry in pi's `mcp.json` (`~/.pi/agent/mcp.json` or a trusted project's + `.pi/mcp.json`) overrides the extension's registration β€” this is how to change the exposure pi + gives the server. The entry needs its own `command`, since it does not inherit the extension's, + and must set `env: {"GITHUB_MCP_HOST": "pi"}`; without it the server uses the Claude Code config + order while the hooks use pi's. +- The servers need bash 4.1+ and jq 1.7+ on the `PATH` pi runs with. +- pi support covers macOS and Linux. + ## Troubleshooting ### MCP Server Not Starting -1. Restart Claude Code or start a new Codex task after plugin installation -2. Check `/mcp` for connection status +1. Restart Claude Code or start a new Codex task, or start a new pi session or run `/reload`, after + plugin installation +2. Check `/mcp` for connection status; under pi, `gh-tooling` and `gh-tooling-write` show the + extension as their source 3. In Codex, open `/hooks` and confirm the plugin hooks are trusted if enforcement is missing 4. Verify `jq` is installed: `which jq` 5. Verify `gh` is installed and authenticated: `gh auth status` diff --git a/plugins/github-mcp/hooks/prompts/host-pi.md b/plugins/github-mcp/hooks/prompts/host-pi.md new file mode 100644 index 0000000..dc6ebf2 --- /dev/null +++ b/plugins/github-mcp/hooks/prompts/host-pi.md @@ -0,0 +1 @@ +In pi, these tools are named `mcp__gh_tooling__` (read) and `mcp__gh_tooling_write__` (write), for example `mcp__gh_tooling__pr_view`. A tool that `tool_search` loads is callable from your next call. diff --git a/plugins/github-mcp/hooks/scripts/check-api-tools.sh b/plugins/github-mcp/hooks/scripts/check-api-tools.sh index ca3830d..5e73cc4 100755 --- a/plugins/github-mcp/hooks/scripts/check-api-tools.sh +++ b/plugins/github-mcp/hooks/scripts/check-api-tools.sh @@ -1,5 +1,5 @@ #!/bin/bash -# MCP API Tool Enforcer (Claude Code and Codex) +# MCP API Tool Enforcer # ========================================================= # Blocks MCP api/api_read tool calls when a dedicated tool exists. # Controlled by block_api_tool_read and block_api_tool_write in .mcp-gh-tooling.json. diff --git a/plugins/github-mcp/hooks/scripts/check-gh-tools.sh b/plugins/github-mcp/hooks/scripts/check-gh-tools.sh index 1b6bbb4..0fa0ab1 100644 --- a/plugins/github-mcp/hooks/scripts/check-gh-tools.sh +++ b/plugins/github-mcp/hooks/scripts/check-gh-tools.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Dev Tooling MCP Enforcer (GitHub CLI, Claude Code and Codex) +# Dev Tooling MCP Enforcer (GitHub CLI) # ========================================================= # Blocks common gh CLI bash commands in favor of gh-tooling MCP tools. # Controlled by two fields in .mcp-gh-tooling.json: diff --git a/plugins/github-mcp/hooks/scripts/lib/common.sh b/plugins/github-mcp/hooks/scripts/lib/common.sh index a4bdc01..d3b7ed4 100644 --- a/plugins/github-mcp/hooks/scripts/lib/common.sh +++ b/plugins/github-mcp/hooks/scripts/lib/common.sh @@ -1,8 +1,8 @@ #!/bin/bash # Shared functions for MCP tool enforcement hooks # ================================================ -# This library provides common functionality for Claude Code and Codex hooks -# that block bash commands in favor of MCP tools. +# This library provides common functionality for the hooks that block bash +# commands in favor of MCP tools, on every host. # # Usage: # source "${SCRIPT_DIR}/lib/common.sh" @@ -15,21 +15,32 @@ # HOOK_INPUT - Raw hook input read from stdin # COMMAND - The bash command being checked # PROJECT_DIR - Project directory reported by the active host -# HOOK_HOST - Host inferred from the hook environment (claude/codex) +# HOOK_HOST - Host inferred from the hook environment (claude/codex/pi) # CONFIG_FILE - Path to loaded config file (or empty) # ENVIRONMENT - Environment from config (native/docker/vagrant/ddev) # ENFORCE_MCP_TOOLS - Whether to enforce MCP tools (true/false) +source "$(dirname "${BASH_SOURCE[0]}")/../../../shared/config-dirs.sh" + # Resolve the active host and project directory from hook input. -# Claude Code provides CLAUDE_PROJECT_DIR; Codex provides cwd in the JSON payload. +# Claude Code provides CLAUDE_PROJECT_DIR, which selects claude even when +# GITHUB_MCP_HOST is exported in the shell. Otherwise GITHUB_MCP_HOST=pi or +# codex names the host, and codex is assumed. Codex and pi provide cwd in the +# JSON payload. resolve_hook_context() { local input="${1:-}" if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then HOOK_HOST="claude" - PROJECT_DIR="${CLAUDE_PROJECT_DIR}" + elif [[ "${GITHUB_MCP_HOST:-}" == "pi" || "${GITHUB_MCP_HOST:-}" == "codex" ]]; then + HOOK_HOST="${GITHUB_MCP_HOST}" else HOOK_HOST="codex" + fi + + if [[ "$HOOK_HOST" == "claude" ]]; then + PROJECT_DIR="${CLAUDE_PROJECT_DIR}" + else PROJECT_DIR="" if command -v jq &>/dev/null; then PROJECT_DIR=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true) @@ -37,38 +48,28 @@ resolve_hook_context() { fi } -# Find a project config, preferring the active host's directory and then the -# other supported host's directory before the project-root fallback. +# Find a project config in the host config directories, in the order +# github_mcp_config_dirs gives for the active host, then in the project root. +# First match wins. # Args: $1 = config prefix # Sets: CONFIG_FILE (global) find_mcp_config() { local config_prefix="$1" - local -a locations CONFIG_FILE="" [[ -z "${PROJECT_DIR:-}" ]] && return 0 - if [[ "${HOOK_HOST:-claude}" == "codex" ]]; then - locations=( - ".codex/.mcp-${config_prefix}.json" - ".claude/.mcp-${config_prefix}.json" - ".mcp-${config_prefix}.json" - ) - else - locations=( - ".claude/.mcp-${config_prefix}.json" - ".codex/.mcp-${config_prefix}.json" - ".mcp-${config_prefix}.json" - ) - fi - - local location - for location in "${locations[@]}"; do - if [[ -f "${PROJECT_DIR}/${location}" ]]; then - CONFIG_FILE="${PROJECT_DIR}/${location}" - break + local dir + while IFS= read -r dir; do + if [[ -f "${PROJECT_DIR}/${dir}/.mcp-${config_prefix}.json" ]]; then + CONFIG_FILE="${PROJECT_DIR}/${dir}/.mcp-${config_prefix}.json" + return 0 fi - done + done < <(github_mcp_config_dirs "${HOOK_HOST:-claude}") + + if [[ -f "${PROJECT_DIR}/.mcp-${config_prefix}.json" ]]; then + CONFIG_FILE="${PROJECT_DIR}/.mcp-${config_prefix}.json" + fi } # Parse hook input from stdin @@ -119,7 +120,8 @@ block_tool() { local description="$2" local display_tool="$tool" - if [[ "${HOOK_HOST:-claude}" == "codex" ]]; then + # Claude Code is the only host that qualifies tool names with the plugin name. + if [[ "${HOOK_HOST:-claude}" != "claude" ]]; then display_tool="${display_tool//gh-tooling-write/gh_tooling_write}" display_tool="${display_tool//gh-tooling/gh_tooling}" else diff --git a/plugins/github-mcp/hooks/scripts/session-start.sh b/plugins/github-mcp/hooks/scripts/session-start.sh index 900ff9c..e35c8f9 100755 --- a/plugins/github-mcp/hooks/scripts/session-start.sh +++ b/plugins/github-mcp/hooks/scripts/session-start.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # SessionStart hook: inject gh-tooling MCP tool usage directives into conversation context. # Reads template from hooks/prompts/mcp-tool-directives.md, assembles dynamic sections -# from .mcp-gh-tooling.json config, and outputs to stdout as JSON additionalContext. +# from .mcp-gh-tooling.json config, appends the active host's hooks/prompts/host-.md +# when one exists, and outputs to stdout as JSON additionalContext. set -euo pipefail HOOK_DIR="$(cd "$(dirname "$0")/.." && pwd)" @@ -49,6 +50,12 @@ fi assembled="${template/\{\{WRITE_SECTION\}\}/$write_section}" assembled="${assembled/\{\{LABEL_SECTION\}\}/$label_section}" +# Append the active host's note, if it has one, after one blank line +host_note="${PROMPTS_DIR}/host-${HOOK_HOST}.md" +if [[ -f "$host_note" ]]; then + assembled="${assembled%$'\n'}"$'\n\n'"$(<"$host_note")" +fi + # Output as JSON additionalContext context=$(printf '%s' "$assembled" | jq -Rs '.') cat <= 0; i-- )); do + locations+=("${project_root}/${host_dirs[i]}/${config_name}") + done + + local loc + for loc in "${locations[@]}"; do + if [[ -f "${loc}" ]]; then + GH_TOOLING_CONFIG_FILE="${loc}" + log "INFO" "Found config: ${loc}" + fi + done + + if [[ -z "${GH_TOOLING_CONFIG_FILE}" ]]; then + log "INFO" "${no_config_message}" + fi +} + # Validate a GitHub number (PR, issue, run, job ID - positive integer) # Args: $1 = value, $2 = field name for error message # Outputs error message to stdout and returns 1 on failure diff --git a/plugins/github-mcp/mcp-server-gh/server-read.sh b/plugins/github-mcp/mcp-server-gh/server-read.sh index 073f527..43ab989 100755 --- a/plugins/github-mcp/mcp-server-gh/server-read.sh +++ b/plugins/github-mcp/mcp-server-gh/server-read.sh @@ -23,57 +23,6 @@ source "${SHARED_DIR}/mcpserver_core.sh" GH_TOOLING_CONFIG_FILE="" GH_DEFAULT_REPO="" -_load_gh_config() { - local project_root="$1" - local config_name=".mcp-gh-tooling.json" - - if [[ -n "${MCP_GH_TOOLING_CONFIG:-}" ]]; then - if [[ -f "${MCP_GH_TOOLING_CONFIG}" ]]; then - GH_TOOLING_CONFIG_FILE="${MCP_GH_TOOLING_CONFIG}" - log "INFO" "Config from MCP_GH_TOOLING_CONFIG: ${GH_TOOLING_CONFIG_FILE}" - else - log "WARN" "MCP_GH_TOOLING_CONFIG set but file not found: ${MCP_GH_TOOLING_CONFIG}" - fi - return 0 - fi - - # Check standard config locations (last found wins). If both supported-host - # overrides exist, prefer the active host's directory. - local -a locations=( - "${project_root}/${config_name}" - "${project_root}/.aiassistant/${config_name}" - "${project_root}/.amazonq/${config_name}" - "${project_root}/.cline/${config_name}" - "${project_root}/.cursor/${config_name}" - "${project_root}/.kiro/${config_name}" - "${project_root}/.windsurf/${config_name}" - "${project_root}/.zed/${config_name}" - ) - - if [[ "${GITHUB_MCP_HOST:-claude}" == "codex" ]]; then - locations+=( - "${project_root}/.claude/${config_name}" - "${project_root}/.codex/${config_name}" - ) - else - locations+=( - "${project_root}/.codex/${config_name}" - "${project_root}/.claude/${config_name}" - ) - fi - - for loc in "${locations[@]}"; do - if [[ -f "${loc}" ]]; then - GH_TOOLING_CONFIG_FILE="${loc}" - log "INFO" "Found config: ${loc}" - fi - done - - if [[ -z "${GH_TOOLING_CONFIG_FILE}" ]]; then - log "INFO" "No .mcp-gh-tooling.json found - using defaults (no default repo)" - fi -} - _read_gh_config() { if [[ -z "${GH_TOOLING_CONFIG_FILE}" ]] || [[ ! -f "${GH_TOOLING_CONFIG_FILE}" ]]; then return 0 @@ -104,7 +53,7 @@ source "${SCRIPT_DIR}/lib/project.sh" trap 'log "ERROR" "Unexpected error on line ${LINENO}"' ERR -_load_gh_config "${PROJECT_ROOT}" +_load_gh_config "${PROJECT_ROOT}" "No .mcp-gh-tooling.json found - using defaults (no default repo)" _read_gh_config _gh_require_tools_list diff --git a/plugins/github-mcp/mcp-server-gh/server-write.sh b/plugins/github-mcp/mcp-server-gh/server-write.sh index 98c9644..fcc0907 100755 --- a/plugins/github-mcp/mcp-server-gh/server-write.sh +++ b/plugins/github-mcp/mcp-server-gh/server-write.sh @@ -22,55 +22,6 @@ source "${SHARED_DIR}/mcpserver_core.sh" GH_TOOLING_CONFIG_FILE="" GH_DEFAULT_REPO="" -_load_gh_config() { - local project_root="$1" - local config_name=".mcp-gh-tooling.json" - - if [[ -n "${MCP_GH_TOOLING_CONFIG:-}" ]]; then - if [[ -f "${MCP_GH_TOOLING_CONFIG}" ]]; then - GH_TOOLING_CONFIG_FILE="${MCP_GH_TOOLING_CONFIG}" - log "INFO" "Config from MCP_GH_TOOLING_CONFIG: ${GH_TOOLING_CONFIG_FILE}" - else - log "WARN" "MCP_GH_TOOLING_CONFIG set but file not found: ${MCP_GH_TOOLING_CONFIG}" - fi - return 0 - fi - - local -a locations=( - "${project_root}/${config_name}" - "${project_root}/.aiassistant/${config_name}" - "${project_root}/.amazonq/${config_name}" - "${project_root}/.cline/${config_name}" - "${project_root}/.cursor/${config_name}" - "${project_root}/.kiro/${config_name}" - "${project_root}/.windsurf/${config_name}" - "${project_root}/.zed/${config_name}" - ) - - if [[ "${GITHUB_MCP_HOST:-claude}" == "codex" ]]; then - locations+=( - "${project_root}/.claude/${config_name}" - "${project_root}/.codex/${config_name}" - ) - else - locations+=( - "${project_root}/.codex/${config_name}" - "${project_root}/.claude/${config_name}" - ) - fi - - for loc in "${locations[@]}"; do - if [[ -f "${loc}" ]]; then - GH_TOOLING_CONFIG_FILE="${loc}" - log "INFO" "Found config: ${loc}" - fi - done - - if [[ -z "${GH_TOOLING_CONFIG_FILE}" ]]; then - log "INFO" "No .mcp-gh-tooling.json found - write server disabled (no config)" - fi -} - _read_gh_config() { if [[ -z "${GH_TOOLING_CONFIG_FILE}" ]] || [[ ! -f "${GH_TOOLING_CONFIG_FILE}" ]]; then return 0 @@ -117,7 +68,7 @@ source "${SCRIPT_DIR}/lib/project.sh" trap 'log "ERROR" "Unexpected error on line ${LINENO}"' ERR -_load_gh_config "${PROJECT_ROOT}" +_load_gh_config "${PROJECT_ROOT}" "No .mcp-gh-tooling.json found - write server disabled (no config)" _read_gh_config _check_write_enabled _gh_require_tools_list diff --git a/plugins/github-mcp/pi/gate.ts b/plugins/github-mcp/pi/gate.ts new file mode 100644 index 0000000..a97258c --- /dev/null +++ b/plugins/github-mcp/pi/gate.ts @@ -0,0 +1,80 @@ +import { spawn } from "node:child_process"; + +export interface ScriptResult { + code: number | null; + stderr: string; + stdout: string; +} + +export type GateResult = { block: true; reason: string } | { block: false }; + +/** + * Runs `bash