diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c77b3f3..31e8d5f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,3 +45,43 @@ jobs: node bin/agentic-kit.mjs x reference sync node -e "const fs=require('fs'),os=require('os'),p=require('path').join(os.homedir(),'.claude','CLAUDE.md'); const t=fs.readFileSync(p,'utf8'); for (const s of ['ruflo-preamble','ruflo-reference']) if(!t.includes('')) throw new Error('missing block '+s); console.log('blocks OK')" node bin/agentic-kit.mjs uninstall --dry-run + + # Static quality gates. Platform-independent → single OS. This is the only job + # that installs devDependencies (the published package stays zero-runtime-dep). + quality: + name: quality (typecheck, lint, build, audit) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: pnpm + + - name: Install devDependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck (tsc --checkJs) + run: pnpm run typecheck + - name: Lint (eslint) + run: pnpm run lint + - name: Markdown lint + run: pnpm run lint:md + - name: Build (packaging + load validation) + run: pnpm run build + - name: Dependency audit + run: pnpm run audit + + # Internal/relative links only (fast, deterministic). External links run in + # nightly — network + rate-limits make them flaky per-PR. + links: + name: links (internal) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Check internal links (offline) + uses: lycheeverse/lychee-action@v2 + with: + args: "--offline --config lychee.toml README.md CLAUDE.md 'docs/**/*.md'" + fail: true diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 79a4d416..2bbbd941 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -50,3 +50,16 @@ jobs: run: | node bin/agentic-kit.mjs x verify security node bin/agentic-kit.mjs x verify learning + + links-external: + name: links (external) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + # External link validation runs here, not on PRs: network + rate-limits + # (npm/GitHub 403 bots) make it flaky per-PR. Config + excludes: lychee.toml. + - name: Check all links (internal + external) + uses: lycheeverse/lychee-action@v2 + with: + args: "--config lychee.toml README.md CLAUDE.md 'docs/**/*.md'" + fail: true diff --git a/.gitignore b/.gitignore index 4ad46b18..8175a68d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,13 @@ test-*.log # Dogfooding artifacts — running ruflo init / aqe init / ruflo-onboard against THIS # repo writes these. They are NOT source (see CLAUDE.md "Dogfooding artifacts are not -# source"); never commit them. +# source"); never commit them. The authored instruction files (CLAUDE.md for claude, +# AGENTS.md for codex) ARE tracked; only their generated dirs (.claude/, .agents/) and +# local overrides (.codex/) are ignored. .agentic-qe/ .claude/ +.agents/ +.codex/ .mcp.json ruvector.db *.db diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 00000000..32588007 --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,5 @@ +# URL patterns to skip in link checks (one regex per line). +# Example placeholders / non-resolvable references go here. +^https?://localhost +^https?://127\.0\.0\.1 +^https?://example\.(com|org) diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..63abef8a --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,26 @@ +{ + // Lint only OUR authored markdown. Vendored ruflo/aqe docs (.claude, .agentic-qe, + // .agents) and archived docs are excluded — not ours to fix. + "globs": [ + "README.md", + "CLAUDE.md", + "AGENTS.md", + "docs/**/*.md" + ], + "ignores": [ + "node_modules", + ".claude", + ".agentic-qe", + ".agents", + "docs/archive" + ], + "config": { + "default": true, + "MD013": false, // line length — prose/tables wrap naturally + "MD033": false, // inline HTML — used deliberately in docs + "MD041": false, // first line need not be a top-level heading + "MD024": { "siblings_only": true }, // allow repeated headings in different sections + "MD029": { "style": "ordered" }, + "MD060": false // table pipe-alignment — cosmetic, fights hand-maintained wide tables + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..79a0b830 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,40 @@ + + +# Ruflo Machine Ref (Codex / AGENTS.md) + +This repo (`@pacphi/agentic-kit`) is a plain-ESM Node.js CLI — **zero runtime +dependencies**; only `bin/`, `src/`, and `claude/` ship. Tooling (eslint, tsc +`--checkJs`, markdownlint, lychee) is devDependencies only. Codex reads this file +the way Claude Code reads `CLAUDE.md`; keep the two in sync. + +## Swarm Config + +- **Topology**: hierarchical-mesh (anti-drift) +- **Max Agents**: 15 +- **Memory**: hybrid +- **HNSW**: Enabled +- **Neural**: Enabled + +```bash +ruflo swarm init --topology hierarchical --max-agents 15 --strategy specialized +``` + +## Build & Test + +```bash +pnpm test # node --test unit suite + statusline segments +pnpm run check # typecheck + lint + markdown lint + build + test +pnpm run build # packaging + CLI-load validation (no transpile — plain ESM) +``` + +## Operating rules + +- Do what's asked; nothing more. Prefer editing existing files over new ones. +- Never commit secrets or `.env` files. Never auto-commit/push without an explicit ask. +- **No `Co-Authored-By` trailer** unless `.claude/settings.json` sets `attribution.commit`. +- Keep files under ~500 lines; validate input at boundaries. + +## Agentic QE v3 + + diff --git a/README.md b/README.md index 57549f07..ed93a941 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ prints its plan with reasons — you always see the impact before anything chang What the verbs cover: | Verb | What it does | -|------|--------------| +| ------ | -------------- | | **setup** | Installs/updates ruflo + agentic-qe globally (handling npm ≥11.17's `allow-scripts` so natives build), deploys the token-audit skill, merges the managed guidance blocks into `~/.claude/CLAUDE.md`, offers one-time MCP registration (user scope, with a tool-family picker), and — inside a repo — initializes the project: sanitized `ruflo init`, absolute memory-path pin, a **verified** store→disk write, statusline footer, and a background daemon with **local-only ($0) workers** (token-spending AI workers stay opt-in behind upstream's machine-wide budget). Project scope triggers on a `.git` directory in the current folder; without one it's skipped with a note. `--project` forces it anyway (e.g. a not-yet-`git init`-ed folder), `--minimal` skips it, `--yes` accepts all prompts (non-interactive), `--no-aqe` / `--no-security` disable those subsystems, and `--reconfigure` re-offers MCP registration. | | **status** | Per-subsystem ✓/⚠/✗ (versions, the kit's own version, natives, security, learning, aqe/RVF, MCP, **hosts** (claude/codex — version + install method, or "enabled but not installed"), **providers** (host wiring + aqe fallback chain, or "drifted"/claude-only default), daemons, CLAUDE.md blocks, statusline), each drift row naming what `sync` would do about it. | | **sync** | The one convergence verb: upgrades first when a new release exists, then re-heals everything an upgrade wipes, then re-checks and reports. Included in that heal: it **installs any enabled frontier host** (claude/codex) that's entirely absent — never touching an external (mise/brew/native) install — and **re-applies provider wiring** (the `ENABLE_*` host env, the aqe fallback chain, and ruflo API providers) whenever it has drifted. It also **self-updates the kit**: when a newer `@pacphi/agentic-kit` exists it installs it as the *last* step (the new code applies from the next `ak` run, never mid-sync). Prerelease installs (`4.0.0-alpha.*`) track the `next` npm dist-tag as well as `latest`, so alphas see their successors; stable installs only ever follow `latest`. `--no-upgrade` skips the self-update along with the package upgrades. | @@ -91,7 +91,7 @@ you opt in. Two independent axes: install (mise/brew/native) is detected, reused, and never shadowed. - **Providers** — which LLM the routers use, independent of the host: agentic-qe's `AQE_LLM_PROVIDER` (`claude-code`/`claude`/`openai`/`gemini`/`openrouter`/`azure-openai`/ - `bedrock`/`cognitum`/`ollama`) plus an ordered fallback chain, and ruflo's API providers. + `bedrock`/`cognitum`/`ollama`/`onnx`) plus an ordered fallback chain, and ruflo's API providers. API keys stay in the environment — never written to `kit.json`. `ak x provider status` shows what's detected and wired; `ak x provider pick` chooses and diff --git a/bin/agentic-kit.mjs b/bin/agentic-kit.mjs index c28dbb8c..368be09c 100755 --- a/bin/agentic-kit.mjs +++ b/bin/agentic-kit.mjs @@ -70,6 +70,7 @@ async function main() { return 0; } + /** @type {Record Promise>} */ let table = PORCELAIN; if (cmd === 'x') { table = PLUMBING; diff --git a/docs/PROVIDERS.md b/docs/PROVIDERS.md index 9c5d38fe..c9804010 100644 --- a/docs/PROVIDERS.md +++ b/docs/PROVIDERS.md @@ -20,7 +20,7 @@ Install `ak`, run `ak setup`. Claude Code is the host, agentic-qe uses its own d nothing about providers is written anywhere. This is the whole feature for most people: **it already works, and `kit.json` stays at its defaults.** -``` +```bash ak setup # claude just works; codex/other providers are opt-in ak status # shows a "hosts" + "providers" row so you can see what's true ``` @@ -28,23 +28,24 @@ ak status # shows a "hosts" + "providers" row so you can see what's true If you happen to have `codex` installed, `ak` notices and *offers* — it never flips it on for you: -``` +```text ℹ codex CLI detected — run `ak x provider pick` to let ruflo use both claude and codex ``` ## Level 1 — turn on codex (one command) -``` +```bash ak x provider pick ``` An interactive picker (or flags for scripts). Enable `codex` and `ak`: + - installs it if it's missing (`npm i -g @openai/codex`) — but leaves an existing mise/brew/native install alone, - runs `ruflo init --dual` (ruflo's "Claude Code + Codex hybrid" mode), - writes `ENABLE_CLAUDE_CODE` / `ENABLE_CODEX` into `.claude/settings.local.json`. -``` +```bash ak x provider pick --host claude,codex --yes # non-interactive ``` @@ -52,9 +53,17 @@ ak x provider pick --host claude,codex --yes # non-interactive agentic-qe can run its analysis on any of: `claude-code` (your Claude subscription), `claude` / `openai` / `gemini` / `openrouter` / `azure-openai` / `bedrock` / `cognitum` -(metered API key), or `ollama` (local). +(metered API key), or `ollama` / `onnx` (local). -``` +**Billing is the axis that isn't obvious from the names** — three categories: +`claude-code` runs on your Claude plan ($0 metered), `ollama` and `onnx` are local ($0), +and **everything else bills a metered API key**. `claude-code` is the *only* subscription +option here: the codex and gemini CLIs also support OAuth/subscription login, but that +lives on the **host axis** (Level 1, which CLI runs the loop) — not as an aqe provider +*type*. So there's no `openai`-subscription or `gemini`-subscription entry; their OAuth +paths are reached by enabling those *hosts*, while the provider list is API-metered. + +```bash ak x provider pick --aqe-provider claude-code # run QE on your subscription, no API bill ``` @@ -66,7 +75,7 @@ router will **auto-enable** OpenAI as a fallback on its own — you don't have t When you want explicit ordering rather than env auto-enable, `ak` manages agentic-qe's `.agentic-qe/llm-config.json` from `kit.json`: -``` +```bash ak x provider pick \ --aqe-provider claude-code \ --aqe-fallback 'claude-code:claude-opus-4-8; openai:gpt-5.6; gemini:gemini-3.5-flash' @@ -101,7 +110,7 @@ If you hand-edit `.agentic-qe/llm-config.json` yourself and *don't* use `ak`'s ## Undo, always -``` +```bash ak x provider off # reset to the claude-only default, reversibly ``` diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 36135ded..98be891b 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -15,7 +15,7 @@ fix; `sync` applies them in the right order and re-checks afterward. ## Common situations | Symptom | What's happening | Fix | -|---|---|---| +| --- | --- | --- | | Just upgraded ruflo/agentic-qe (`npm i -g …`) and things feel off | Upgrades re-resolve dependencies: native SQLite bindings and the aidefence package get dropped, the statusline is regenerated without the footer | `ak sync` (this is its main job) | | `status` shows `natives … WASM fallback` | agentdb resolved a non-native better-sqlite3 — on this path **memory writes can silently vanish**. Root cause is npm ≥11.17 blocking install scripts during upgrades | `ak sync` installs the native binding | | `status` shows `aidefence missing` | ruflo ≥3.28 stopped shipping `@claude-flow/aidefence` but `ruflo security defend` still imports it — injection defense is silently non-functional ([ruvnet/ruflo#2670](https://github.com/ruvnet/ruflo/issues/2670)) | `ak sync` reinstalls it; `ak x verify security` proves defend works (exit 1=threat / 0=clean) | diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..b86b3222 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,60 @@ +// Flat ESLint config. Scope: OUR code only (src/bin/tests/scripts). Vendored +// ruflo/aqe content (.claude, .agentic-qe, .agents) and historical docs are +// never linted — they aren't ours to fix and would drown real findings. +import js from '@eslint/js'; +import globals from 'globals'; + +export default [ + { + ignores: [ + 'node_modules/**', + '.claude/**', + '.agentic-qe/**', + '.agents/**', + 'docs/archive/**', + 'coverage/**', + ], + }, + js.configs.recommended, + { + files: ['**/*.{mjs,js,cjs}'], + languageOptions: { + ecmaVersion: 2023, + sourceType: 'module', + globals: { ...globals.node }, + }, + rules: { + // High-signal correctness rules; not a style bikeshed. + 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + 'no-undef': 'error', + 'no-constant-condition': ['error', { checkLoops: false }], + 'no-empty': ['error', { allowEmptyCatch: true }], + 'prefer-const': 'error', + 'no-var': 'error', + 'no-eval': 'error', + eqeqeq: ['error', 'smart'], + }, + }, + { + // CommonJS test/statusline files use require/module. + files: ['**/*.cjs'], + languageOptions: { sourceType: 'commonjs' }, + }, + { + // The statusline footer is an EMITTED shell-statusline template + its test: + // deliberately old-school (var, literal ANSI escape bytes, bare catch bindings) + // because the snippet runs embedded in the user's shell, not as normal source. + // Relax the idiom rules here; syntax/undef checks still apply. + files: ['src/templates/statusline-footer.cjs', 'tests/statusline-segments.test.cjs'], + // getStdinData is injected by the host statusline runtime (guarded with typeof). + languageOptions: { globals: { getStdinData: 'readonly' } }, + rules: { + 'no-var': 'off', + 'no-redeclare': 'off', + 'no-control-regex': 'off', + 'no-unused-vars': 'off', + 'no-useless-assignment': 'off', + 'prefer-const': 'off', + }, + }, +]; diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 00000000..d932a7b8 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,29 @@ +# Link checker config (internal + external). Scope is set by the invoking glob +# in CI/scripts (our authored docs), and vendored trees are excluded below so we +# never validate ruflo/aqe's links. +# +# On PRs we check INTERNAL/relative links only (fast, deterministic); external +# links run in nightly (network + rate-limits make them flaky per-PR). + +# Treat these as success (GitHub/CI sometimes rate-limits or 403s bots). +accept = [200, 206, 429] + +max_retries = 2 +retry_wait_time = 2 +timeout = 20 +max_concurrency = 8 + +# Don't descend into vendored or archived trees. +exclude_path = [ + "node_modules", + ".claude", + ".agentic-qe", + ".agents", + "docs/archive", +] + +# Hosts that reliably 403/rate-limit automated requests — noise, not broken links. +exclude = [ + "^https://img\\.shields\\.io", + "^https://www\\.npmjs\\.com", # npm package pages 403 bots/CI +] diff --git a/package.json b/package.json index e8901791..a8daa239 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,28 @@ "docs/TROUBLESHOOTING.md" ], "scripts": { - "test": "node --test \"tests/kit/*.test.mjs\" && node tests/statusline-segments.test.cjs" + "test": "node --test \"tests/kit/*.test.mjs\" && node tests/statusline-segments.test.cjs", + "typecheck": "tsc -p tsconfig.json", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "lint:md": "markdownlint-cli2", + "lint:md:fix": "markdownlint-cli2 --fix", + "lint:links": "lychee --config lychee.toml README.md CLAUDE.md AGENTS.md 'docs/**/*.md'", + "lint:links:internal": "lychee --offline --config lychee.toml README.md CLAUDE.md AGENTS.md 'docs/**/*.md'", + "build": "node scripts/build-check.mjs", + "audit": "pnpm audit --audit-level=moderate", + "audit:fix": "pnpm audit --fix", + "outdated": "pnpm outdated", + "upgrade": "pnpm up", + "check": "pnpm run typecheck && pnpm run lint && pnpm run lint:md && pnpm run build && pnpm test" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^26.1.1", + "eslint": "^10.7.0", + "globals": "^17.7.0", + "markdownlint-cli2": "^0.23.0", + "typescript": "^7.0.2" }, "repository": { "type": "git", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b60ae17..f98f1655 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,4 +6,1466 @@ settings: importers: - .: {} + .: + devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.7.0) + '@types/node': + specifier: ^26.1.1 + version: 26.1.1 + eslint: + specifier: ^10.7.0 + version: 10.7.0 + globals: + specifier: ^17.7.0 + version: 17.7.0 + markdownlint-cli2: + specifier: ^0.23.0 + version: 0.23.0 + typescript: + specifier: ^7.0.2 + version: 7.0.2 + +packages: + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.2': + resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/katex@0.16.8': + resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.7.0: + resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + engines: {node: '>=18'} + + globby@16.2.0: + resolution: {integrity: sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==} + engines: {node: '>=20'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@5.2.0: + resolution: {integrity: sha512-YeLUMlvR4Ou1B119LIaM0r65JvbOBooJDc9yEu0dClb/uSC5P4FrLU8OCCz/HXWvtPoIrR0dRzABTjo1sTN9Bw==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + markdown-it@14.2.0: + resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} + hasBin: true + + markdownlint-cli2-formatter-default@0.0.6: + resolution: {integrity: sha512-VVDGKsq9sgzu378swJ0fcHfSicUnMxnL8gnLm/Q4J/xsNJ4e5bA6lvAz7PCzIl0/No0lHyaWdqVD2jotxOSFMQ==} + peerDependencies: + markdownlint-cli2: '>=0.0.4' + + markdownlint-cli2@0.23.0: + resolution: {integrity: sha512-1nmgQmU/ZTMRVwYCDs7i1HI3zfBISnT2NNRv+9V01oOLZbAtqL+a7tldpPhBWBVBten3FqhMCGV6EUh9McqutQ==} + engines: {node: '>=22'} + hasBin: true + + markdownlint@0.41.0: + resolution: {integrity: sha512-xMUI3ChBuRuxuLF4ENvCZyS8z/+Jly1coUcZwErKLIB3sDj7ojpaTBa1e9YVPhSN4jGEIjYGQCldbTJS/hqS+A==} + engines: {node: '>=22'} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@4.0.0: + resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + smol-toml@1.7.0: + resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==} + engines: {node: '>= 18'} + + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + engines: {node: '>=20'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0)': + dependencies: + eslint: 10.7.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.6.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.7.0)': + optionalDependencies: + eslint: 10.7.0 + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.2': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@sindresorhus/merge-streams@4.0.0': {} + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/katex@0.16.8': {} + + '@types/ms@2.1.0': {} + + '@types/node@26.1.1': + dependencies: + undici-types: 8.3.0 + + '@types/unist@2.0.11': {} + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@6.2.2: {} + + argparse@2.0.1: {} + + balanced-match@4.0.4: {} + + brace-expansion@5.0.7: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + commander@8.3.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + deep-is@0.1.4: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + entities@4.5.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.7.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.6.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + 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.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + get-east-asian-width@1.6.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@17.7.0: {} + + globby@16.2.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.6 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + imurmurhash@0.1.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-number@7.0.0: {} + + is-path-inside@4.0.0: {} + + isexe@2.0.0: {} + + js-yaml@5.2.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonc-parser@3.3.1: {} + + jsonpointer@5.0.1: {} + + katex@0.16.47: + dependencies: + commander: 8.3.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + linkify-it@5.0.2: + dependencies: + uc.micro: 2.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + markdown-it@14.2.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.2 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdownlint-cli2-formatter-default@0.0.6(markdownlint-cli2@0.23.0): + dependencies: + markdownlint-cli2: 0.23.0 + + markdownlint-cli2@0.23.0: + dependencies: + globby: 16.2.0 + js-yaml: 5.2.0 + jsonc-parser: 3.3.1 + jsonpointer: 5.0.1 + markdown-it: 14.2.0 + markdownlint: 0.41.0 + markdownlint-cli2-formatter-default: 0.0.6(markdownlint-cli2@0.23.0) + micromatch: 4.0.8 + smol-toml: 1.7.0 + transitivePeerDependencies: + - supports-color + + markdownlint@0.41.0: + dependencies: + micromark: 4.0.2 + micromark-core-commonmark: 2.0.3 + micromark-extension-directive: 4.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-math: 3.1.0 + micromark-util-types: 2.0.2 + string-width: 8.2.1 + transitivePeerDependencies: + - supports-color + + mdurl@2.0.0: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@4.0.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.8 + devlop: 1.1.0 + katex: 0.16.47 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.7 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picomatch@2.3.2: {} + + prelude-ls@1.2.1: {} + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + slash@5.1.0: {} + + smol-toml@1.7.0: {} + + string-width@8.2.1: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + uc.micro@2.1.0: {} + + undici-types@8.3.0: {} + + unicorn-magic@0.4.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/scripts/build-check.mjs b/scripts/build-check.mjs new file mode 100644 index 00000000..cd2deee1 --- /dev/null +++ b/scripts/build-check.mjs @@ -0,0 +1,57 @@ +#!/usr/bin/env node +// "build" for a no-transpile ESM package = packaging + load validation: +// 1. every published file parses (node --check) +// 2. the CLI entrypoint loads and responds to --version +// 3. `npm pack --dry-run` resolves the `files` allowlist without error +// Catches the failure modes a transpile step would otherwise surface: a syntax +// error in a shipped file, a broken bin, or a `files` glob that drops a module. +import { spawnSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import path from 'node:path'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const node = process.execPath; +let failures = 0; + +const step = (name, ok, detail = '') => { + console.log(`${ok ? '✓' : '✗'} ${name}${detail ? ` — ${detail}` : ''}`); + if (!ok) failures++; +}; + +// 1. Syntax-check every shipped .mjs/.cjs under bin/ and src/. +const listFiles = (dir) => { + const out = spawnSync('git', ['ls-files', dir], { cwd: root, encoding: 'utf8' }); + return (out.stdout || '').split('\n').filter((f) => /\.(mjs|cjs|js)$/.test(f)); +}; +const shipped = [...listFiles('bin'), ...listFiles('src')]; +let syntaxOk = true; +for (const f of shipped) { + const r = spawnSync(node, ['--check', path.join(root, f)], { encoding: 'utf8' }); + if (r.status !== 0) { syntaxOk = false; console.error(` syntax: ${f}\n${r.stderr}`); } +} +step(`syntax-check ${shipped.length} shipped files`, syntaxOk); + +// 2. CLI entrypoint loads and prints a version. +const bin = path.join(root, 'bin', 'agentic-kit.mjs'); +const v = spawnSync(node, [bin, '--version'], { encoding: 'utf8' }); +step('CLI loads + --version', v.status === 0 && /\d+\.\d+\.\d+/.test(v.stdout), v.stdout.trim()); + +// 3. Packaging: `npm pack --dry-run` must resolve the files allowlist. +const pack = spawnSync('npm', ['pack', '--dry-run', '--json'], { cwd: root, encoding: 'utf8' }); +let packOk = pack.status === 0; +let fileCount = 0; +try { + const meta = JSON.parse(pack.stdout); + fileCount = meta[0]?.files?.length ?? 0; + const bundlesBin = meta[0]?.files?.some((f) => f.path.includes('agentic-kit.mjs')); + packOk = packOk && fileCount > 0 && !!bundlesBin; +} catch { packOk = false; } +step('npm pack --dry-run resolves files', packOk, `${fileCount} files`); + +// Sanity: published version is readable. +const pkgVersion = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8')).version; +step('package.json version present', !!pkgVersion, pkgVersion); + +if (failures) { console.error(`\nbuild-check: ${failures} failure(s)`); process.exit(1); } +console.log('\nbuild-check: OK'); diff --git a/src/commands/setup.mjs b/src/commands/setup.mjs index 932e46c9..39c9d6e1 100644 --- a/src/commands/setup.mjs +++ b/src/commands/setup.mjs @@ -208,7 +208,7 @@ export async function run_project({ flags, cfg }) { // 9. agentic-qe in this repo (sentinel first so aqe init skips duplicate guidance) if (cfg.aqe && !flags['no-aqe'] && await have('aqe')) { - let md = fs.existsSync(projectMd) ? fs.readFileSync(projectMd, 'utf8') : ''; + const md = fs.existsSync(projectMd) ? fs.readFileSync(projectMd, 'utf8') : ''; if (!md.includes('## Agentic QE v3')) { fs.appendFileSync(projectMd, '\n## Agentic QE v3\n\n'); } diff --git a/src/commands/x/provider.mjs b/src/commands/x/provider.mjs index c8bff8f9..e60ce9f5 100644 --- a/src/commands/x/provider.mjs +++ b/src/commands/x/provider.mjs @@ -22,6 +22,11 @@ export const options = { json: { type: 'boolean', default: false }, }; +/** Billing is the non-obvious axis of the aqe provider list. Three categories, + * and claude-code is the ONLY same-vendor subscription alternative to a metered + * key (codex/gemini OAuth live on the host axis, not as aqe provider types). */ +export const AQE_BILLING_HINT = 'billing: claude-code = your Claude subscription ($0), ollama/onnx = local ($0), all others = metered API key'; + export const help = `ak x provider — frontier-host + LLM-provider detection and wiring Two independent axes: which host CLI runs the ruflo loop (claude/codex, can be @@ -36,6 +41,8 @@ Subcommands: Options (pick, all optional — omit for interactive): --host claude,codex enable these ruflo host CLIs --aqe-provider set aqe's primary LLM (or 'none' to unset) + billing: claude-code = Claude sub ($0), + ollama/onnx = local ($0), all others = metered key --aqe-fallback '' ordered aqe chain, e.g. 'claude-code:claude-opus-4-8; openai:gpt-5.6' --provider register ruflo API providers (e.g. openai:gpt-5.6) @@ -92,6 +99,7 @@ async function status({ flags, cwd }) { const ap = cfg.providers.aqeProvider; console.log(bold('\nagentic-qe LLM provider') + dim(' (AQE_LLM_PROVIDER)')); console.log(` ${(ap ?? dim('aqe default (unset)')).padEnd(24)} ${dim(`supported: ${AQE_PROVIDER_TYPES.join(', ')}`)}`); + console.log(` ${dim(AQE_BILLING_HINT)}`); const chain = cfg.providers.aqeFallback ?? []; if (chain.length) { const rendered = chain.map((e) => `${e.provider}${e.models?.length ? `(${e.models.join(',')})` : dim('(no models)')}`).join(' → '); @@ -161,6 +169,7 @@ async function pick({ flags, cwd }) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const hAns = (await rl.question(`Enable which ruflo host(s)? (comma-separated) [${installed.join(',')}]: `)).trim(); enabled = (hAns || installed.join(',')).split(',').map((s) => s.trim()).filter(Boolean); + console.log(dim(` ${AQE_BILLING_HINT}`)); const aAns = (await rl.question(`agentic-qe primary LLM provider — ${AQE_PROVIDER_TYPES.join('/')} (blank = leave aqe default): `)).trim().toLowerCase(); aqeProvider = aAns ? aAns : null; const fAns = (await rl.question('aqe fallback chain, ordered (e.g. "claude-code:claude-opus-4-8; openai:gpt-5.6", blank = none): ')).trim().toLowerCase(); diff --git a/src/lib/heal.mjs b/src/lib/heal.mjs index 7d86e16f..d9670f72 100644 --- a/src/lib/heal.mjs +++ b/src/lib/heal.mjs @@ -43,10 +43,10 @@ const failTail = (r) => * 3. run the package's own install script directly — explicit `npm run` * is user-invoked and never gated by allow-scripts. */ export async function ensureNativeBsq3(dir) { - let r = await npmInstallInto(dir, 'better-sqlite3@^12'); + await npmInstallInto(dir, 'better-sqlite3@^12'); if (bsq3IsNative(dir)) return { ok: true, how: 'native installed' }; await run('npm', ['approve-scripts', 'better-sqlite3'], { cwd: dir, timeout: 60_000 }); - r = await run('npm', ['rebuild', 'better-sqlite3'], { cwd: dir, timeout: 300_000 }); + let r = await run('npm', ['rebuild', 'better-sqlite3'], { cwd: dir, timeout: 300_000 }); if (bsq3IsNative(dir)) return { ok: true, how: 'native rebuilt (scripts approved)' }; const pkgRoot = bsq3Root(dir); if (pkgRoot) { diff --git a/src/lib/providers.mjs b/src/lib/providers.mjs index 6ea1c663..78bfff2e 100644 --- a/src/lib/providers.mjs +++ b/src/lib/providers.mjs @@ -33,9 +33,15 @@ import * as paths from './paths.mjs'; /** Frontier agent-CLI hosts. `pkg` is the npm global package; `enableEnv` is * ruflo's ADR-034 backend flag; `aqe` is the AQE_LLM_PROVIDER value (null when * aqe can't host it). */ +/** ruflo's dual-mode adapter — a SEPARATE npm global from the codex CLI. Without + * it, `ruflo init --dual` aborts with "The @claude-flow/codex package is not + * installed". ak treats it as a managed prerequisite of dual-mode (installed + * only when codex is opted-in AND the codex CLI is detected). */ +export const CODEX_ADAPTER_PKG = '@claude-flow/codex'; + export const HOSTS = [ { id: 'claude', bin: 'claude', pkg: '@anthropic-ai/claude-code', enableEnv: 'ENABLE_CLAUDE_CODE', aqe: 'claude-code' }, - { id: 'codex', bin: 'codex', pkg: '@openai/codex', enableEnv: 'ENABLE_CODEX', aqe: null }, + { id: 'codex', bin: 'codex', pkg: '@openai/codex', enableEnv: 'ENABLE_CODEX', aqe: null, adapterPkg: CODEX_ADAPTER_PKG }, ]; /** API-key LLM providers ruflo's router understands (`ruflo providers`). */ @@ -46,12 +52,13 @@ export const API_PROVIDERS = [ { id: 'ollama', keyEnv: [] }, // local; presence = reachable daemon (not checked here) ]; -/** Valid `AQE_LLM_PROVIDER` values (grounded: aqe billing-modes + router types). - * aqe force-selects any of these for its QE analysis, independent of ruflo's - * host. `claude-code` = Claude subscription; ollama = local; the rest metered. */ +/** Valid `AQE_LLM_PROVIDER` values (grounded: aqe ALL_PROVIDER_TYPES in + * dist/shared/llm/router/types.js). aqe force-selects any of these for its QE + * analysis, independent of ruflo's host. `claude-code` = Claude subscription; + * `ollama`/`onnx` = local ($0); the rest metered. Keep in sync with aqe's list. */ export const AQE_PROVIDER_TYPES = [ 'claude-code', 'claude', 'openai', 'gemini', 'openrouter', - 'azure-openai', 'bedrock', 'cognitum', 'ollama', + 'azure-openai', 'bedrock', 'cognitum', 'ollama', 'onnx', ]; /** Every env key this module owns — the reversible surface for `off`/undo. */ @@ -295,12 +302,40 @@ export async function applyProviders(cfg, cwd = process.cwd()) { return { ok: done.every((d) => !d.includes('failed')), changed: true, detail: `configured: ${done.join(', ')}` }; } +/** Pure decision for the codex dual-mode adapter, factored out for tests: + * install ONLY when codex is opted-in, the codex CLI is present, and the adapter + * is not already installed. Never install the adapter for an absent CLI. */ +export function codexAdapterAction({ opted, cliPresent, adapterInstalled }) { + if (!opted) return 'skip-not-opted'; + if (!cliPresent) return 'skip-no-cli'; + if (adapterInstalled) return 'already-installed'; + return 'install'; +} + +/** Ensure ruflo's dual-mode adapter (@claude-flow/codex) is installed before we + * run `ruflo init --dual`. Guarded: opted-in codex host + detected codex CLI. */ +export async function ensureCodexAdapter(cfg, cwd = process.cwd()) { + const opted = !!cfg.providers?.hosts?.codex; + if (!opted) return { ok: true, changed: false, detail: 'codex disabled — adapter not needed' }; + const cliPresent = await have('codex'); + const adapterInstalled = !!installedVersion(CODEX_ADAPTER_PKG); + const action = codexAdapterAction({ opted, cliPresent, adapterInstalled }); + if (action === 'skip-no-cli') return { ok: true, changed: false, detail: 'codex CLI not detected — adapter install skipped' }; + if (action === 'already-installed') return { ok: true, changed: false, detail: `${CODEX_ADAPTER_PKG} already installed` }; + const r = await run('npm', ['install', '-g', `${CODEX_ADAPTER_PKG}@latest`], { cwd, timeout: 600_000 }); + return { ok: r.code === 0, changed: r.code === 0, detail: r.code === 0 ? `installed ${CODEX_ADAPTER_PKG}` : r.stderr.split('\n').slice(-2).join(' ').slice(0, 200) }; +} + /** Heavy, pick/setup-time only: regenerate dual-mode agents when codex is on. * Kept OUT of the sync hot path (it force-regenerates project files). */ export async function ensureDualAgents(cfg, cwd = process.cwd()) { if (!cfg.providers?.hosts?.codex) return { ok: true, changed: false, detail: 'codex disabled — no dual agents' }; if (!fs.existsSync(path.join(cwd, '.git'))) return { ok: true, changed: false, detail: 'not a project — skipped `ruflo init --dual`' }; if (!(await have('ruflo'))) return { ok: false, detail: 'ruflo not on PATH' }; + // Prerequisite: dual-init aborts unless @claude-flow/codex is present. Install + // it first (guarded on opted-in codex + detected CLI) so a fresh machine just works. + const adapter = await ensureCodexAdapter(cfg, cwd); + if (!adapter.ok) return { ok: false, changed: adapter.changed, detail: `adapter prerequisite failed: ${adapter.detail}` }; const r = await run('ruflo', ['init', '--dual', '--force'], { cwd, timeout: 300_000 }); return { ok: r.code === 0, changed: r.code === 0, detail: r.code === 0 ? 'ruflo init --dual applied' : 'ruflo init --dual failed' }; } diff --git a/src/lib/versions.mjs b/src/lib/versions.mjs index 76176d75..f3817489 100644 --- a/src/lib/versions.mjs +++ b/src/lib/versions.mjs @@ -91,7 +91,8 @@ export const KIT_PKG = '@pacphi/agentic-kit'; /** Drift for the kit itself. Installed = the running copy's package.json * (pkgRoot). Prerelease installs also consult the `next` dist-tag — * prereleases publish there, so `latest` alone would never see them; the - * higher of latest/next wins. Cached in kit.json alongside versionCheck. */ + * higher of latest/next wins. Cached in kit.json alongside versionCheck. + * @param {{ pkgRoot?: string, force?: boolean }} [opts] */ export async function selfDrift({ pkgRoot, force = false } = {}) { let installed = null; try { diff --git a/tests/kit/providers.test.mjs b/tests/kit/providers.test.mjs index 43677f38..50027923 100644 --- a/tests/kit/providers.test.mjs +++ b/tests/kit/providers.test.mjs @@ -8,6 +8,7 @@ import { loadKitConfig } from '../../src/lib/config.mjs'; import { isDefault, managedEnv, applyHosts, undoProviders, MANAGED_ENV_KEYS, HOSTS, installHost, updateHost, applyAqeRouter, undoAqeRouter, aqeRouterFile, + CODEX_ADAPTER_PKG, codexAdapterAction, ensureCodexAdapter, AQE_PROVIDER_TYPES, } from '../../src/lib/providers.mjs'; // A tmp dir with a .git marker → settingsTarget() writes the ISOLATED @@ -222,3 +223,48 @@ test('isDefault is false once an aqe provider is pinned (independent of hosts)', cfg.providers.aqeProvider = 'openai'; assert.equal(isDefault(cfg), false); }); + +test('AQE_PROVIDER_TYPES mirrors aqe ALL_PROVIDER_TYPES (incl. local onnx)', () => { + // Guards against drift from aqe's dist/shared/llm/router/types.js. Order- + // independent set comparison; the point is coverage, not sequence. + const expected = [ + 'claude', 'claude-code', 'openai', 'ollama', 'openrouter', + 'gemini', 'azure-openai', 'bedrock', 'cognitum', 'onnx', + ]; + assert.deepEqual([...AQE_PROVIDER_TYPES].sort(), [...expected].sort()); + assert.equal(AQE_PROVIDER_TYPES.includes('onnx'), true, 'onnx (local) is a valid aqe provider'); +}); + +test('the codex host descriptor carries the dual-mode adapter package name', () => { + const codex = HOSTS.find((h) => h.id === 'codex'); + assert.equal(codex.adapterPkg, CODEX_ADAPTER_PKG); + assert.equal(CODEX_ADAPTER_PKG, '@claude-flow/codex'); +}); + +test('codexAdapterAction skips when codex host is not opted in', () => { + const action = codexAdapterAction({ opted: false, cliPresent: true, adapterInstalled: false }); + assert.equal(action, 'skip-not-opted'); +}); + +test('codexAdapterAction skips when opted in but the codex CLI is not detected', () => { + const action = codexAdapterAction({ opted: true, cliPresent: false, adapterInstalled: false }); + assert.equal(action, 'skip-no-cli'); +}); + +test('codexAdapterAction is a no-op when the adapter is already installed', () => { + const action = codexAdapterAction({ opted: true, cliPresent: true, adapterInstalled: true }); + assert.equal(action, 'already-installed'); +}); + +test('codexAdapterAction installs only when opted in, CLI present, adapter absent', () => { + const action = codexAdapterAction({ opted: true, cliPresent: true, adapterInstalled: false }); + assert.equal(action, 'install'); +}); + +test('ensureCodexAdapter is a no-op without shelling out when codex is disabled', async () => { + const cfg = defaultCfg(); // claude-only default → hosts.codex false + const res = await ensureCodexAdapter(cfg); + assert.equal(res.ok, true); + assert.equal(res.changed, false); + assert.match(res.detail, /codex disabled/); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..87a730ec --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "// note": "Type-CHECK only (noEmit). This is a plain-ESM JS package — no transpile. checkJs + @types/node catches real bugs via inference without requiring the source to be TypeScript. Kept non-strict on purpose: the value is catching genuine mistakes (wrong property, bad arg count), not annotating every value.", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "noEmit": true, + "module": "esnext", + "target": "es2022", + "moduleResolution": "bundler", + "skipLibCheck": true, + "types": ["node"], + "strict": false, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true + }, + "// scope": "Shipped code + scripts. Tests are fixture-heavy (dynamic shapes) — typechecking them yields inference noise, not bugs; they're covered by `pnpm test`.", + "include": ["src/**/*.mjs", "bin/**/*.mjs", "scripts/**/*.mjs"] +}