From e76bc592ae5887255e9e61b63a0d79a31a57b701 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Tue, 5 May 2026 21:41:28 +0000 Subject: [PATCH 01/27] feat(container): add Claude Code Karma feature with session tracking hooks Adds the claude-code-karma devcontainer feature with read-only session tracking hooks in settings.base.json. Updates check-setup.sh with Karma health checks, adds Karma tools to cc-tools listing, and fixes GH_CONFIG_DIR default path in setup-aliases.sh. --- .../config/settings-opus-45-200k.json | 107 +++++++- .../config/settings-opus-46-1m-400k.json | 107 +++++++- .../config/settings-opus-46-200k.json | 107 +++++++- .../config/settings-opus-47-1m-400k.json | 107 +++++++- .../codeforge/config/settings.base.json | 102 ++++++- .../defaults/codeforge/config/settings.json | 107 +++++++- .../features/claude-code-karma/README.md | 71 +++++ .../devcontainer-feature.json | 39 +++ .../features/claude-code-karma/install.sh | 251 ++++++++++++++++++ .../.devcontainer/scripts/check-setup.sh | 6 + .../.devcontainer/scripts/setup-aliases.sh | 4 +- container/.devcontainer/scripts/setup.sh | 2 +- container/README.md | 6 +- container/test.js | 3 + 14 files changed, 996 insertions(+), 23 deletions(-) mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings.base.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/config/settings.json create mode 100755 container/.devcontainer/features/claude-code-karma/README.md create mode 100755 container/.devcontainer/features/claude-code-karma/devcontainer-feature.json create mode 100755 container/.devcontainer/features/claude-code-karma/install.sh mode change 100644 => 100755 container/README.md mode change 100644 => 100755 container/test.js diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json old mode 100644 new mode 100755 index 9bc705a..14c64cb --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json +++ b/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -90,7 +90,10 @@ ] }, "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], + "allow": [ + "Read(/workspaces/*)", + "WebFetch(domain:*)" + ], "deny": [], "ask": [], "defaultMode": "plan", @@ -98,7 +101,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json old mode 100644 new mode 100755 index 42392b1..5530618 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json +++ b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -90,7 +90,10 @@ ] }, "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], + "allow": [ + "Read(/workspaces/*)", + "WebFetch(domain:*)" + ], "deny": [], "ask": [], "defaultMode": "plan", @@ -98,7 +101,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json old mode 100644 new mode 100755 index 40b5059..4ddb363 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json +++ b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -90,7 +90,10 @@ ] }, "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], + "allow": [ + "Read(/workspaces/*)", + "WebFetch(domain:*)" + ], "deny": [], "ask": [], "defaultMode": "plan", @@ -98,7 +101,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json old mode 100644 new mode 100755 index 8d1b8b9..d903ee4 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json +++ b/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -89,7 +89,10 @@ ] }, "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], + "allow": [ + "Read(/workspaces/*)", + "WebFetch(domain:*)" + ], "deny": [], "ask": [], "defaultMode": "plan", @@ -97,7 +100,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/defaults/codeforge/config/settings.base.json b/container/.devcontainer/defaults/codeforge/config/settings.base.json old mode 100644 new mode 100755 index 4050a76..fe45380 --- a/container/.devcontainer/defaults/codeforge/config/settings.base.json +++ b/container/.devcontainer/defaults/codeforge/config/settings.base.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -94,7 +94,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/defaults/codeforge/config/settings.json b/container/.devcontainer/defaults/codeforge/config/settings.json old mode 100644 new mode 100755 index f5259f9..a1cdfff --- a/container/.devcontainer/defaults/codeforge/config/settings.json +++ b/container/.devcontainer/defaults/codeforge/config/settings.json @@ -1,5 +1,5 @@ { - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "skipDangerousModePermissionPrompt": true, @@ -89,7 +89,10 @@ ] }, "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], + "allow": [ + "Read(/workspaces/*)", + "WebFetch(domain:*)" + ], "deny": [], "ask": [], "defaultMode": "plan", @@ -97,7 +100,105 @@ }, "enabledMcpjsonServers": [], "disabledMcpjsonServers": [], - "hooks": {}, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "karma-live-session-tracker", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "karma-title-generator", + "timeout": 15 + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "/usr/local/bin/ccstatusline-wrapper" diff --git a/container/.devcontainer/features/claude-code-karma/README.md b/container/.devcontainer/features/claude-code-karma/README.md new file mode 100755 index 0000000..7003e3b --- /dev/null +++ b/container/.devcontainer/features/claude-code-karma/README.md @@ -0,0 +1,71 @@ +# claude-code-karma + +Installs [Claude Code Karma](https://github.com/JayantDevkar/claude-code-karma), a local-first web dashboard for Claude Code sessions, analytics, hooks, plugins, tools, live sessions, and generated titles. + +## CodeForge Boundaries + +CodeForge owns `~/.claude/settings.json`. This feature patches Karma so its Settings API is read-only and cannot write Claude settings. + +The only Karma-related Claude settings are baked into CodeForge's generated settings profiles: + +- live session tracking hooks +- session title generation hook on `SessionEnd` +- `cleanupPeriodDays: 90` +- `alwaysThinkingEnabled: true` + +## Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `version` | string | `4067d87ee5c85eb7d2877890ba6174115f0bee2e` | Upstream git ref to install, or `none` to skip | +| `apiPort` | string | `7848` | FastAPI backend port | +| `frontendPort` | string | `7847` | Dashboard frontend port | +| `autostart` | string | `true` | Start API and frontend on container start | +| `username` | string | `automatic` | Container user for runtime ownership | + +## Usage + +After container start, open the dashboard at: + +```text +http://localhost:7847 +``` + +The API runs at: + +```text +http://localhost:7848 +``` + +Useful checks: + +```bash +karma-status +curl http://localhost:${CODEFORGE_KARMA_API_PORT:-7848}/health +``` + +## Commands + +| Command | Purpose | +|---------|---------| +| `karma-status` | Show API/frontend process status and recent logs | +| `karma-live-session-tracker` | Hook wrapper for live session tracking | +| `karma-title-generator` | Hook wrapper for session title generation | + +## Runtime Environment Overrides + +The post-start service reads these environment variables if set: + +| Variable | Default | Description | +|----------|---------|-------------| +| `CODEFORGE_KARMA_API_PORT` | `7848` | API service port | +| `CODEFORGE_KARMA_FRONTEND_PORT` | `7847` | Frontend service port | +| `CLAUDE_KARMA_API` | `http://localhost:$CODEFORGE_KARMA_API_PORT` | Title hook API URL | + +If you change ports after build, rebuild the container so the frontend bundle uses the same API port. + +## Data + +Karma reads Claude Code data from `~/.claude/` and writes its own metadata under `~/.claude_karma/`. + +Karma must not write `~/.claude/settings.json`; CodeForge's installer patches both backend and UI to keep settings read-only. diff --git a/container/.devcontainer/features/claude-code-karma/devcontainer-feature.json b/container/.devcontainer/features/claude-code-karma/devcontainer-feature.json new file mode 100755 index 0000000..cad10a2 --- /dev/null +++ b/container/.devcontainer/features/claude-code-karma/devcontainer-feature.json @@ -0,0 +1,39 @@ +{ + "id": "claude-code-karma", + "version": "1.0.0", + "name": "Claude Code Karma Dashboard", + "description": "Installs Claude Code Karma, a local-first Claude Code session dashboard with live tracking and generated session titles", + "documentationURL": "https://github.com/JayantDevkar/claude-code-karma", + "options": { + "version": { + "type": "string", + "description": "Git ref to install. Use 'none' to skip.", + "default": "4067d87ee5c85eb7d2877890ba6174115f0bee2e" + }, + "apiPort": { + "type": "string", + "description": "Karma FastAPI port. Also used by the title-generator hook wrapper.", + "default": "7848" + }, + "frontendPort": { + "type": "string", + "description": "Karma dashboard frontend port.", + "default": "7847" + }, + "autostart": { + "type": "string", + "description": "Auto-start Karma API and frontend on container start", + "default": "true", + "enum": ["true", "false"] + }, + "username": { + "type": "string", + "description": "Container user to own runtime data", + "default": "automatic" + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/node", + "ghcr.io/devcontainers-extra/features/uv" + ] +} diff --git a/container/.devcontainer/features/claude-code-karma/install.sh b/container/.devcontainer/features/claude-code-karma/install.sh new file mode 100755 index 0000000..6ad2cbc --- /dev/null +++ b/container/.devcontainer/features/claude-code-karma/install.sh @@ -0,0 +1,251 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +KARMA_REF="${VERSION:-4067d87ee5c85eb7d2877890ba6174115f0bee2e}" +API_PORT="${APIPORT:-7848}" +FRONTEND_PORT="${FRONTENDPORT:-7847}" +AUTOSTART="${AUTOSTART:-true}" +USERNAME="${USERNAME:-automatic}" +INSTALL_DIR="/opt/claude-code-karma" + +if [ "${KARMA_REF}" = "none" ]; then + echo "[claude-code-karma] Skipping installation (version=none)" + exit 0 +fi + +echo "[claude-code-karma] Starting installation..." +echo "[claude-code-karma] Ref: ${KARMA_REF}" +echo "[claude-code-karma] API port: ${API_PORT}" +echo "[claude-code-karma] Frontend port: ${FRONTEND_PORT}" + +if [ -f /usr/local/share/nvm/nvm.sh ]; then + # shellcheck disable=SC1091 + source /usr/local/share/nvm/nvm.sh +fi + +for cmd in git python3 npm; do + if ! command -v "${cmd}" >/dev/null 2>&1; then + echo "[claude-code-karma] ERROR: ${cmd} is required but not found" + exit 1 + fi +done + +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + for current_user in vscode node codespace; do + if id -u "${current_user}" >/dev/null 2>&1; then + USERNAME="${current_user}" + break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root +fi + +USER_HOME="$(getent passwd "${USERNAME}" | cut -d: -f6)" +if [ -z "${USER_HOME}" ] || [ ! -d "${USER_HOME}" ]; then + echo "[claude-code-karma] ERROR: Home directory not found for user ${USERNAME}" + exit 1 +fi + +if [[ ! "${API_PORT}" =~ ^[0-9]+$ ]] || [[ ! "${FRONTEND_PORT}" =~ ^[0-9]+$ ]]; then + echo "[claude-code-karma] ERROR: apiPort and frontendPort must be numeric" + exit 1 +fi + +if [ "${API_PORT}" = "8000" ] || [ "${FRONTEND_PORT}" = "5173" ]; then + echo "[claude-code-karma] ERROR: upstream default ports 8000/5173 are not allowed in CodeForge" + exit 1 +fi + +if [ "${AUTOSTART}" != "true" ] && [ "${AUTOSTART}" != "false" ]; then + echo "[claude-code-karma] ERROR: autostart must be true or false" + exit 1 +fi + +echo "[claude-code-karma] Installing for user: ${USERNAME} (${USER_HOME})" + +rm -rf "${INSTALL_DIR}" +git clone https://github.com/JayantDevkar/claude-code-karma.git "${INSTALL_DIR}" +git -C "${INSTALL_DIR}" checkout --detach "${KARMA_REF}" + +echo "[claude-code-karma] Applying CodeForge read-only settings patch..." +python3 - "${INSTALL_DIR}" "${API_PORT}" <<'PY' +import pathlib +import re +import sys + +root = pathlib.Path(sys.argv[1]) +api_port = sys.argv[2] + +settings_py = root / "api" / "routers" / "settings.py" +text = settings_py.read_text() +pattern = re.compile( + r'@router\.put\("/", response_model=Dict\[str, Any\]\)\n' + r'async def update_settings\(updates: ClaudeSettingsUpdate\):\n' + r'.*?(?=\n\n@router\.|\Z)', + re.S, +) +replacement = '''@router.put("/", response_model=Dict[str, Any]) +async def update_settings(updates: ClaudeSettingsUpdate): + """Settings are managed by CodeForge and are intentionally read-only.""" + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Claude settings are managed by CodeForge and cannot be modified from Karma.", + ) +''' +new_text, count = pattern.subn(replacement, text, count=1) +if count != 1: + raise SystemExit("failed to patch settings.py update_settings") +settings_py.write_text(new_text) + +page = root / "frontend" / "src" / "routes" / "settings" / "+page.svelte" +text = page.read_text() +text = text.replace( + "subtitle=\"Manage your Claude Code configuration\"", + "subtitle=\"View your CodeForge-managed Claude Code configuration\"", +) +text = text.replace( + "// State\n\tlet isLoading", + "// State\n\tconst SETTINGS_READ_ONLY = true;\n\tlet isLoading", +) +text = text.replace( + "async function updateSetting(field: string, value: unknown) {\n\t\tsavingField = field;", + "async function updateSetting(field: string, value: unknown) {\n\t\tif (SETTINGS_READ_ONLY) {\n\t\t\terror = 'Claude settings are managed by CodeForge and are read-only in Karma.';\n\t\t\treturn;\n\t\t}\n\t\tsavingField = field;", +) +text = text.replace("disabled={savingField", "disabled={SETTINGS_READ_ONLY || savingField") +text = text.replace("disabled={savingField", "disabled={SETTINGS_READ_ONLY || savingField") +text = text.replace("disabled={savingField", "disabled={SETTINGS_READ_ONLY || savingField") +page.write_text(text) + +config = root / "frontend" / "src" / "lib" / "config.ts" +text = config.read_text() +text = text.replace( + "export const API_BASE = import.meta.env.PUBLIC_API_URL || 'http://localhost:8000';", + "export const API_BASE = import.meta.env.PUBLIC_API_URL || `http://localhost:${import.meta.env.PUBLIC_KARMA_API_PORT || '" + api_port + "'}`;", +) +config.write_text(text) +PY + +echo "[claude-code-karma] Installing API dependencies..." +python3 -m venv "${INSTALL_DIR}/api/.venv" +"${INSTALL_DIR}/api/.venv/bin/pip" install --upgrade pip +"${INSTALL_DIR}/api/.venv/bin/pip" install -e "${INSTALL_DIR}/api[dev]" +"${INSTALL_DIR}/api/.venv/bin/pip" install -r "${INSTALL_DIR}/api/requirements.txt" + +echo "[claude-code-karma] Installing and building frontend..." +( + cd "${INSTALL_DIR}/frontend" + npm install + PUBLIC_KARMA_API_PORT="${API_PORT}" PUBLIC_API_URL="http://localhost:${API_PORT}" npm run build +) +npm cache clean --force >/dev/null 2>&1 || true + +chown -R "${USERNAME}:" "${INSTALL_DIR}" 2>/dev/null || true +mkdir -p "${USER_HOME}/.claude_karma" +chown -R "${USERNAME}:" "${USER_HOME}/.claude_karma" 2>/dev/null || true + +cat > /usr/local/bin/karma-live-session-tracker < /usr/local/bin/karma-title-generator < /usr/local/bin/karma-status <<'EOF' +#!/bin/bash +echo "Claude Code Karma" +echo "API port: ${CODEFORGE_KARMA_API_PORT:-7848}" +echo "Frontend port: ${CODEFORGE_KARMA_FRONTEND_PORT:-7847}" +echo "" +for name in api frontend; do + pid_file="/tmp/claude-code-karma-${name}.pid" + if [ -f "${pid_file}" ] && kill -0 "$(cat "${pid_file}")" 2>/dev/null; then + echo "${name}: running (PID $(cat "${pid_file}"))" + else + echo "${name}: stopped" + fi +done +echo "" +echo "Logs: /tmp/claude-code-karma-api.log, /tmp/claude-code-karma-frontend.log" +EOF +chmod +x /usr/local/bin/karma-status + +if [ "${AUTOSTART}" = "true" ]; then + mkdir -p /usr/local/devcontainer-poststart.d + cat > /usr/local/devcontainer-poststart.d/44-claude-code-karma.sh < "/tmp/claude-code-karma-\${name}.pid" +} + +if [ -f /usr/local/share/nvm/nvm.sh ]; then + # shellcheck disable=SC1091 + source /usr/local/share/nvm/nvm.sh +fi + +mkdir -p "\${KARMA_USER_HOME}/.claude_karma" +chown -R "\${KARMA_USER}:" "\${KARMA_USER_HOME}/.claude_karma" 2>/dev/null || true + +if [ ! -f /tmp/claude-code-karma-api.pid ] || ! kill -0 "\$(cat /tmp/claude-code-karma-api.pid)" 2>/dev/null; then + ( + cd "\${KARMA_HOME}/api" + export CLAUDE_KARMA_CLAUDE_BASE="\${CLAUDE_CONFIG_DIR:-\${KARMA_USER_HOME}/.claude}" + export CLAUDE_KARMA_CORS_ORIGINS="[\"http://localhost:\${FRONTEND_PORT}\",\"http://127.0.0.1:\${FRONTEND_PORT}\"]" + exec "\${KARMA_HOME}/api/.venv/bin/uvicorn" main:app --host 0.0.0.0 --port "\${API_PORT}" + ) >>"\${API_LOG}" 2>&1 & + echo \$! > /tmp/claude-code-karma-api.pid + echo "[claude-code-karma] API started on port \${API_PORT}" +else + echo "[claude-code-karma] API already running" +fi + +if [ ! -f /tmp/claude-code-karma-frontend.pid ] || ! kill -0 "\$(cat /tmp/claude-code-karma-frontend.pid)" 2>/dev/null; then + ( + cd "\${KARMA_HOME}/frontend" + export HOST=0.0.0.0 + export PORT="\${FRONTEND_PORT}" + exec node build/index.js + ) >>"\${FRONTEND_LOG}" 2>&1 & + echo \$! > /tmp/claude-code-karma-frontend.pid + echo "[claude-code-karma] Frontend started on port \${FRONTEND_PORT}" +else + echo "[claude-code-karma] Frontend already running" +fi +EOF + chmod +x /usr/local/devcontainer-poststart.d/44-claude-code-karma.sh + echo "[claude-code-karma] Post-start hook installed" +else + echo "[claude-code-karma] Autostart disabled" +fi + +echo "[claude-code-karma] Installation complete" +echo " Dashboard: http://localhost:${FRONTEND_PORT}" +echo " API: http://localhost:${API_PORT}" +echo " Settings: read-only; CodeForge owns ~/.claude/settings.json" diff --git a/container/.devcontainer/scripts/check-setup.sh b/container/.devcontainer/scripts/check-setup.sh index 4e5b70e..c3688c7 100755 --- a/container/.devcontainer/scripts/check-setup.sh +++ b/container/.devcontainer/scripts/check-setup.sh @@ -71,6 +71,12 @@ echo "Claude Code Router:" warn_check "ccr installed" "command -v ccr" warn_check "CCR config exists" "test -f ${HOME}/.claude-code-router/config.json" +echo "" +echo "Claude Code Karma:" +warn_check "Karma hook wrappers installed" "command -v karma-live-session-tracker && command -v karma-title-generator" +warn_check "Karma status command installed" "command -v karma-status" +warn_check "Karma API responding" "curl -fsS http://localhost:${CODEFORGE_KARMA_API_PORT:-7848}/health" + echo "" echo "━━━━━━━━━━━━━━━━━━━━" echo " $PASS passed, $FAIL failed, $WARN warnings" diff --git a/container/.devcontainer/scripts/setup-aliases.sh b/container/.devcontainer/scripts/setup-aliases.sh index f71eb74..339832c 100755 --- a/container/.devcontainer/scripts/setup-aliases.sh +++ b/container/.devcontainer/scripts/setup-aliases.sh @@ -98,7 +98,7 @@ for rc in ~/.bashrc ~/.zshrc; do ${BLOCK_START} export CLAUDE_CONFIG_DIR="${CLAUDE_CONFIG_DIR}" -export GH_CONFIG_DIR="${GH_CONFIG_DIR:-/workspaces/.gh}" +export GH_CONFIG_DIR="${GH_CONFIG_DIR:-/home/vscode/.config/gh}" export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 @@ -190,7 +190,7 @@ cc-tools() { echo "━━━━━━━━━━━━━━━━━━━━━━━━" printf " %-20s %s\n" "COMMAND" "STATUS" echo " ────────────────────────────────────" - for cmd in claude cc cc5 cc6 cc61 cc7 cc71 ccw ccw5 ccw6 ccw61 ccw7 ccw71 ccraw cc-orc cc-orc5 cc-orc6 cc-orc61 cc-orc7 cc-orc71 codeforge ccr omc omc-cc ccusage ccburn claude-monitor codex ccusage-codex \\ + for cmd in claude cc cc5 cc6 cc61 cc7 cc71 ccw ccw5 ccw6 ccw61 ccw7 ccw71 ccraw cc-orc cc-orc5 cc-orc6 cc-orc61 cc-orc7 cc-orc71 codeforge ccr omc omc-cc ccusage ccburn claude-monitor karma-status karma-live-session-tracker karma-title-generator codex ccusage-codex \\ ct cargo ruff biome dprint shfmt shellcheck hadolint \\ ast-grep tree-sitter pyright typescript-language-server \\ agent-browser gh docker git jq tmux bun go infocmp; do diff --git a/container/.devcontainer/scripts/setup.sh b/container/.devcontainer/scripts/setup.sh index c4977ad..d666b84 100755 --- a/container/.devcontainer/scripts/setup.sh +++ b/container/.devcontainer/scripts/setup.sh @@ -155,7 +155,7 @@ run_script "$SCRIPT_DIR/setup-terminal.sh" "$SETUP_TERMINAL" # Background the update to avoid blocking container start if [ "$SETUP_UPDATE_CLAUDE" = "true" ] && [ -f "$SCRIPT_DIR/setup-update-claude.sh" ]; then - CLAUDE_UPDATE_LOG="${CLAUDE_UPDATE_LOG:-/workspaces/.tmp/claude-update.log}" + CLAUDE_UPDATE_LOG="${CLAUDE_UPDATE_LOG:-/tmp/claude-update.log}" mkdir -p "$(dirname "$CLAUDE_UPDATE_LOG")" bash "$SCRIPT_DIR/setup-update-claude.sh" >>"$CLAUDE_UPDATE_LOG" 2>&1 & disown diff --git a/container/README.md b/container/README.md old mode 100644 new mode 100755 index 0159731..e1feb6b --- a/container/README.md +++ b/container/README.md @@ -77,11 +77,11 @@ tree-sitter (JS/TS/Python), ast-grep, Pyright, TypeScript LSP ### Claude Code Tools -`claude`, `cc` (wrapper), `ccw` (writing mode wrapper), `ccusage`, `ccburn`, `ccstatusline`, `claude-monitor` +`claude`, `cc` (wrapper), `ccw` (writing mode wrapper), `ccusage`, `ccburn`, `ccstatusline`, `claude-monitor`, `karma-status` -### Custom Features (21) +### Custom Features (26) -tmux, agent-browser, claude-monitor, ccusage, ccburn, ccstatusline, ast-grep, tree-sitter, lsp-servers, biome, ruff, shfmt, shellcheck, hadolint, dprint, ccms, notify-hook, mcp-qdrant, chromaterm, kitty-terminfo +tmux, agent-browser, claude-monitor, claude-code-karma, ccusage, ccburn, ccstatusline, ast-grep, tree-sitter, lsp-servers, biome, ruff, shfmt, shellcheck, hadolint, dprint, ccms, notify-hook, mcp-qdrant, chromaterm, kitty-terminfo, codex-cli, hermes-agent, claude-code-router, codeforge-cli, oh-my-claude ### Agents (19) & Skills (34) diff --git a/container/test.js b/container/test.js old mode 100644 new mode 100755 index 0faf24f..c244e47 --- a/container/test.js +++ b/container/test.js @@ -39,6 +39,9 @@ function runTests() { ".devcontainer/features/oh-my-claude/devcontainer-feature.json", ".devcontainer/features/oh-my-claude/install.sh", ".devcontainer/features/oh-my-claude/README.md", + ".devcontainer/features/claude-code-karma/devcontainer-feature.json", + ".devcontainer/features/claude-code-karma/install.sh", + ".devcontainer/features/claude-code-karma/README.md", ]; let allFilesExist = true; From b6f73bbda559904d3fef828ca662579cdeaf80a8 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Tue, 5 May 2026 21:41:43 +0000 Subject: [PATCH 02/27] docs: add Karma hooks, settings boundary, and environment reference updates Documents Karma's read-only tracking hooks in the hooks page, adds the Karma settings boundary section to settings-and-permissions, and updates environment variables, whats-included, and accessing-services references. --- docs/src/content/docs/customize/hooks.md | 2 ++ .../content/docs/customize/settings-and-permissions.md | 8 +++++++- docs/src/content/docs/reference/environment-variables.md | 3 +-- docs/src/content/docs/reference/whats-included.md | 5 +++-- docs/src/content/docs/use/accessing-services.md | 5 +++++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/src/content/docs/customize/hooks.md b/docs/src/content/docs/customize/hooks.md index 272bb93..bf255b7 100755 --- a/docs/src/content/docs/customize/hooks.md +++ b/docs/src/content/docs/customize/hooks.md @@ -11,6 +11,8 @@ Hooks are scripts that execute at specific points during a Claude Code session. CodeForge uses eight hook points, each serving a different purpose in the session lifecycle: +Claude Code Karma also registers read-only tracking hooks in CodeForge's generated `settings.json` profiles for `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `Notification`, `Stop`, `SubagentStart`, `SubagentStop`, and `SessionEnd`. These hooks call CodeForge wrapper commands and are managed from `settings.base.json`, not from Karma's Settings UI. + ### PreToolUse Fires **before** a tool executes. Used for validation and gating. diff --git a/docs/src/content/docs/customize/settings-and-permissions.md b/docs/src/content/docs/customize/settings-and-permissions.md index 2ee5740..f896a63 100755 --- a/docs/src/content/docs/customize/settings-and-permissions.md +++ b/docs/src/content/docs/customize/settings-and-permissions.md @@ -17,7 +17,7 @@ Common fields include: { "model": "opus[1m]", "effortLevel": "high", - "cleanupPeriodDays": 60, + "cleanupPeriodDays": 90, "autoCompact": true, "alwaysThinkingEnabled": true, "teammateMode": "auto", @@ -93,6 +93,12 @@ Status line behavior is also configured in `settings.json`: } ``` +## Karma Settings Boundary + +Claude Code Karma is installed by default and can display the active Claude settings. CodeForge patches Karma so the Settings page and Settings API are read-only. + +Persistent setting changes belong in `.codeforge/config/settings.json` or, for shipped defaults, `defaults/codeforge/config/settings.base.json`. Do not rely on Karma to modify `~/.claude/settings.json`. + ## Configuration Precedence When the same setting exists in more than one place, precedence is: diff --git a/docs/src/content/docs/reference/environment-variables.md b/docs/src/content/docs/reference/environment-variables.md index 7e0f4c5..ada540d 100755 --- a/docs/src/content/docs/reference/environment-variables.md +++ b/docs/src/content/docs/reference/environment-variables.md @@ -72,8 +72,7 @@ Variables set by the DevContainer environment that define workspace paths. |----------|-------------|---------|--------| | `WORKSPACE_ROOT` | Workspace root directory | `/workspaces` | devcontainer.json | | `CLAUDE_CONFIG_DIR` | Claude configuration directory | `/home/vscode/.claude` | devcontainer.json | -| `GH_CONFIG_DIR` | GitHub CLI configuration directory | `/workspaces/.gh` | devcontainer.json | -| `TMPDIR` | Temporary files directory | `/workspaces/.tmp` | devcontainer.json | +| `GH_CONFIG_DIR` | GitHub CLI configuration directory | `/home/vscode/.config/gh` | devcontainer.json | | `HERMES_CDP_ENDPOINT` | Chrome CDP endpoint for Hermes/browser tooling inside the container | `http://192.168.65.254:9223` | devcontainer.json | | `CLAUDECODE` | Set to `null` to unset the detection flag, enabling nested Claude Code sessions | `null` | devcontainer.json | diff --git a/docs/src/content/docs/reference/whats-included.md b/docs/src/content/docs/reference/whats-included.md index 37e83c2..41a975c 100755 --- a/docs/src/content/docs/reference/whats-included.md +++ b/docs/src/content/docs/reference/whats-included.md @@ -26,8 +26,8 @@ Other pages should link here instead of repeating counts. | Plugins | 17 | | Custom agents | 19 | | Skills | 34 | -| CLI tools and utilities | 25 | -| DevContainer features | 23 | +| CLI tools and utilities | 26 | +| DevContainer features | 26 | These counts are the canonical inventory numbers. Some deeper reference pages intentionally include adjacent items such as runtimes or language servers for convenience, but they should link back here for the official counts. @@ -50,6 +50,7 @@ These counts are the canonical inventory numbers. Some deeper reference pages in |------|--------|-------| | `cc`, `claude`, `ccw`, `ccraw`, `cc-orc` | Default | Session launch commands | | `check-setup`, `cc-tools`, `ccusage`, `claude-monitor` | Default | Everyday operational tools | +| `claude-code-karma`, `karma-status` | Default | Local Claude Code session dashboard, live tracking, and generated titles | | LSP servers | Disabled by default | Require feature enablement and plugin toggle | | `ccms` | Deprecated | Replaced by `codeforge session search` | | `ccburn` | Disabled by default | Older burn-rate view, largely superseded by `ccstatusline` | diff --git a/docs/src/content/docs/use/accessing-services.md b/docs/src/content/docs/use/accessing-services.md index 822c9bf..e9cd031 100755 --- a/docs/src/content/docs/use/accessing-services.md +++ b/docs/src/content/docs/use/accessing-services.md @@ -19,6 +19,8 @@ If you use VS Code, you usually only need this page when automatic forwarding is | devcontainer-bridge (`dbr`) | Any terminal client | Dynamic — polls `/proc/net/tcp` | Host daemon required | | SSH tunneling | Any SSH client | Manual | Per-port command | +Default CodeForge services include Claude Code Karma on port `7847` and the Karma API on port `7848`. + ## Windows: Mirrored Networking (Recommended) If you're on Windows with WSL 2, **mirrored networking** is the recommended approach. It makes `localhost` work bidirectionally between your host and the container — no forwarding tools needed. @@ -103,6 +105,9 @@ For one-off port forwarding or environments where `dbr` isn't available, use SSH # Forward a single port ssh -L 3000:localhost:3000 @ +# Forward Claude Code Karma and its API +ssh -L 7847:localhost:7847 -L 7848:localhost:7848 @ + # Forward multiple ports ssh -L 3000:localhost:3000 -L 8080:localhost:8080 @ ``` From 2cf208b0de02bd9ba17ac38a866ed7bf9e87d391 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Tue, 5 May 2026 21:42:05 +0000 Subject: [PATCH 03/27] feat(cli): restructure doctor as module with --fix TUI and new checks Refactors the monolithic doctor.ts into a module directory with separate files for types, checks, formatting, and fix orchestration. Adds three new check categories: - Volume candidates: scans for high-churn dirs (node_modules, .next, .venv, target, etc.) on slow filesystems, recommends Docker volumes - WSL .wslconfig: detects host RAM, generates optimized config with memory limits, autoMemoryReclaim, and sparseVhd - Windows Defender: provides PowerShell exclusion commands for Docker and WSL processes Introduces --fix mode with @clack/prompts TUI multi-select grouped by category, plus --yes (non-interactive), --dry-run (preview), and --only (filter) flags. Fix actions write .codeforge/mounts.json for compose integration. WSL checks are hidden on non-WSL systems. --- cli/bun.lock | 13 + cli/package.json | 5 +- cli/src/commands/doctor/checks/auth.ts | 50 ++ cli/src/commands/doctor/checks/environment.ts | 172 +++++ cli/src/commands/doctor/checks/volumes.ts | 245 +++++++ cli/src/commands/doctor/checks/wsl.ts | 114 +++ cli/src/commands/doctor/fix.ts | 165 +++++ cli/src/commands/doctor/format.ts | 74 ++ cli/src/commands/doctor/index.ts | 96 +++ cli/src/commands/doctor/types.ts | 31 + cli/src/commands/doctor/util.ts | 26 + cli/src/index.ts | 2 + cli/tests/doctor.test.ts | 651 ++++++++++++++++++ 13 files changed, 1642 insertions(+), 2 deletions(-) create mode 100644 cli/src/commands/doctor/checks/auth.ts create mode 100644 cli/src/commands/doctor/checks/environment.ts create mode 100644 cli/src/commands/doctor/checks/volumes.ts create mode 100644 cli/src/commands/doctor/checks/wsl.ts create mode 100644 cli/src/commands/doctor/fix.ts create mode 100644 cli/src/commands/doctor/format.ts create mode 100644 cli/src/commands/doctor/index.ts create mode 100644 cli/src/commands/doctor/types.ts create mode 100644 cli/src/commands/doctor/util.ts create mode 100644 cli/tests/doctor.test.ts diff --git a/cli/bun.lock b/cli/bun.lock index 13d6fd1..4f7c638 100644 --- a/cli/bun.lock +++ b/cli/bun.lock @@ -5,6 +5,7 @@ "": { "name": "codeforge-cli", "dependencies": { + "@clack/prompts": "^1.3.0", "@devcontainers/cli": "^0.71.0", "chalk": "^5.4.0", "commander": "^13.0.0", @@ -17,6 +18,10 @@ }, }, "packages": { + "@clack/core": ["@clack/core@1.3.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA=="], + + "@clack/prompts": ["@clack/prompts@1.3.0", "", { "dependencies": { "@clack/core": "1.3.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw=="], + "@devcontainers/cli": ["@devcontainers/cli@0.71.0", "", { "bin": { "devcontainer": "devcontainer.js" } }, "sha512-My13mDQCZy4zFsIoU2LVdnm3g2oSvxAkp+Z1gO/cYBYG3YUdteIEGm43igq67WPEpdJqE3LjKkIKp4I6fJBzEQ=="], "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], @@ -29,6 +34,14 @@ "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], + + "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], + + "fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], diff --git a/cli/package.json b/cli/package.json index c13d4b0..980cde5 100644 --- a/cli/package.json +++ b/cli/package.json @@ -28,9 +28,10 @@ "prepublishOnly": "bun run build && bun test" }, "dependencies": { + "@clack/prompts": "^1.3.0", "@devcontainers/cli": "^0.71.0", - "commander": "^13.0.0", - "chalk": "^5.4.0" + "chalk": "^5.4.0", + "commander": "^13.0.0" }, "devDependencies": { "@types/bun": "^1.3.10", diff --git a/cli/src/commands/doctor/checks/auth.ts b/cli/src/commands/doctor/checks/auth.ts new file mode 100644 index 0000000..71e6882 --- /dev/null +++ b/cli/src/commands/doctor/checks/auth.ts @@ -0,0 +1,50 @@ +import type { CheckResult } from "../types.js"; +import { spawn } from "../util.js"; + +export async function checkGhAuth(): Promise { + const { stdout, exitCode } = await spawn("gh", ["auth", "status"]); + if (exitCode === 0) { + const userMatch = stdout.match(/Logged in to [^ ]+ as ([^\s(]+)/); + const user = userMatch?.[1] ?? "unknown"; + return { + name: "GitHub CLI authenticated", + category: "auth", + status: "pass", + message: `user: ${user}`, + }; + } + return { + name: "GitHub CLI authenticated", + category: "auth", + status: "warn", + message: "not authenticated", + hint: "Run: gh auth login", + }; +} + +export async function checkGitUser(): Promise { + const [nameResult, emailResult] = await Promise.all([ + spawn("git", ["config", "user.name"]), + spawn("git", ["config", "user.email"]), + ]); + const name = nameResult.stdout; + const email = emailResult.stdout; + if (name && email) { + return { + name: "Git user configured", + category: "auth", + status: "pass", + message: `${name} <${email}>`, + }; + } + const missing = [!name && "user.name", !email && "user.email"] + .filter(Boolean) + .join(", "); + return { + name: "Git user configured", + category: "auth", + status: "warn", + message: `missing: ${missing}`, + hint: "Run: git config --global user.name 'Name' && git config --global user.email 'email'", + }; +} diff --git a/cli/src/commands/doctor/checks/environment.ts b/cli/src/commands/doctor/checks/environment.ts new file mode 100644 index 0000000..1745f75 --- /dev/null +++ b/cli/src/commands/doctor/checks/environment.ts @@ -0,0 +1,172 @@ +import { homedir } from "node:os"; +import { join } from "node:path"; +import type { CheckResult } from "../types.js"; +import { SLOW_FS_TYPES, spawn } from "../util.js"; + +export async function checkWorkspaceFs(): Promise { + const target = process.env.WORKSPACE_ROOT || "/workspaces"; + const { stdout, exitCode } = await spawn("stat", ["-f", "-c", "%T", target]); + if (exitCode !== 0) { + return { + name: "Workspace filesystem", + category: "environment", + status: "info", + message: "unable to detect (stat -f unavailable)", + }; + } + const fsType = stdout.toLowerCase(); + const isSlow = SLOW_FS_TYPES.some((s) => fsType.includes(s)); + if (isSlow) { + return { + name: "Workspace filesystem", + category: "environment", + status: "warn", + message: `${fsType} (slow)`, + hint: "Move project to WSL filesystem for faster I/O: \\\\wsl$\\\\home\\...", + }; + } + return { + name: "Workspace filesystem", + category: "environment", + status: "pass", + message: `${fsType} (fast)`, + }; +} + +export async function checkTmpdir(): Promise { + const tmpdir = process.env.TMPDIR; + if (!tmpdir) { + return { + name: "TMPDIR location", + category: "environment", + status: "pass", + message: "unset (using container /tmp)", + }; + } + if (tmpdir === "/tmp" || tmpdir.startsWith("/tmp/")) { + return { + name: "TMPDIR location", + category: "environment", + status: "pass", + message: `${tmpdir} (fast)`, + }; + } + // Check if TMPDIR is on a slow filesystem + const { stdout, exitCode } = await spawn("stat", ["-f", "-c", "%T", tmpdir]); + if (exitCode === 0) { + const fsType = stdout.toLowerCase(); + const isSlow = SLOW_FS_TYPES.some((s) => fsType.includes(s)); + if (isSlow) { + return { + name: "TMPDIR location", + category: "environment", + status: "warn", + message: `${tmpdir} on ${fsType} (slow)`, + hint: "Unset TMPDIR to use container /tmp", + fix: { + label: "Move TMPDIR to /tmp", + detail: + "Sets TMPDIR=/tmp so temporary files use the container's fast tmpfs instead of the slow bind mount.", + impact: "1 env var change, no rebuild needed", + requiresRebuild: false, + apply: async () => { + // Write to shell profile to persist + const profilePath = join(homedir(), ".bashrc"); + const line = "\nexport TMPDIR=/tmp\n"; + await Bun.write( + profilePath, + (await Bun.file(profilePath).text()) + line, + ); + process.env.TMPDIR = "/tmp"; + return { applied: true, message: "Added TMPDIR=/tmp to ~/.bashrc" }; + }, + }, + }; + } + } + return { + name: "TMPDIR location", + category: "environment", + status: "info", + message: `${tmpdir}`, + }; +} + +export async function checkCacheDirs(): Promise { + const home = homedir(); + const dirs = [ + { path: join(home, ".cache"), label: "~/.cache" }, + { path: join(home, ".npm"), label: "~/.npm" }, + { path: join(home, ".bun/install/cache"), label: "~/.bun/install/cache" }, + ]; + + const results = await Promise.all( + dirs.map(async ({ path, label }) => { + const { stdout, exitCode } = await spawn("mountpoint", ["-q", path]); + return { label, isMount: exitCode === 0 }; + }), + ); + + const onVolume = results.filter((r) => r.isMount).map((r) => r.label); + const notOnVolume = results.filter((r) => !r.isMount).map((r) => r.label); + + if (onVolume.length === dirs.length) { + return { + name: "Cache directories on volumes", + category: "environment", + status: "pass", + message: "all volume-backed", + }; + } + if (onVolume.length > 0) { + return { + name: "Cache directories on volumes", + category: "environment", + status: "info", + message: `volume: ${onVolume.join(", ")}; local: ${notOnVolume.join(", ")}`, + }; + } + return { + name: "Cache directories on volumes", + category: "environment", + status: "info", + message: "none volume-backed", + }; +} + +export async function checkDockerMemory(): Promise { + // Try cgroup v2 first, then v1 + for (const path of [ + "/sys/fs/cgroup/memory.max", + "/sys/fs/cgroup/memory/memory.limit_in_bytes", + ]) { + try { + const file = Bun.file(path); + const text = (await file.text()).trim(); + if (text === "max" || text === "9223372036854771712") { + return { + name: "Container memory", + category: "environment", + status: "info", + message: "unlimited", + }; + } + const bytes = parseInt(text, 10); + if (!isNaN(bytes)) { + const gb = (bytes / (1024 * 1024 * 1024)).toFixed(1); + return { + name: "Container memory", + category: "environment", + status: "info", + message: `${gb}GB`, + }; + } + } catch {} + } + return { + name: "Container memory", + category: "environment", + status: "info", + message: "unable to detect", + }; +} diff --git a/cli/src/commands/doctor/checks/volumes.ts b/cli/src/commands/doctor/checks/volumes.ts new file mode 100644 index 0000000..01721e3 --- /dev/null +++ b/cli/src/commands/doctor/checks/volumes.ts @@ -0,0 +1,245 @@ +import { existsSync } from "node:fs"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { basename, dirname, join, relative } from "node:path"; +import type { CheckResult } from "../types.js"; +import { SLOW_FS_TYPES, spawn } from "../util.js"; + +interface VolumeCandidate { + pattern: string; + contextFiles: string[]; +} + +const VOLUME_CANDIDATES: VolumeCandidate[] = [ + { pattern: "node_modules", contextFiles: ["package.json"] }, + { + pattern: ".next", + contextFiles: ["next.config.js", "next.config.ts", "next.config.mjs"], + }, + { pattern: ".nuxt", contextFiles: ["nuxt.config.ts", "nuxt.config.js"] }, + { + pattern: ".svelte-kit", + contextFiles: ["svelte.config.js", "svelte.config.ts"], + }, + { + pattern: ".venv", + contextFiles: ["pyproject.toml", "requirements.txt"], + }, + { + pattern: "venv", + contextFiles: ["pyproject.toml", "requirements.txt"], + }, + { pattern: "target", contextFiles: ["Cargo.toml"] }, + { pattern: ".turbo", contextFiles: ["turbo.json"] }, +]; + +const CANDIDATE_NAMES = VOLUME_CANDIDATES.map((c) => c.pattern); + +interface MountsJson { + version: number; + volumes: MountEntry[]; +} + +interface MountEntry { + path: string; + source: "auto" | "user"; + signal: string; + added: string; +} + +function hasContextFile( + dirPath: string, + contextFiles: string[], +): string | null { + const parent = dirname(dirPath); + for (const cf of contextFiles) { + if (existsSync(join(dirname(dirPath), cf))) { + return cf; + } + if (existsSync(join(parent, "..", cf))) { + return cf; + } + } + return null; +} + +async function isMountpoint(dirPath: string): Promise { + const { exitCode } = await spawn("mountpoint", ["-q", dirPath]); + return exitCode === 0; +} + +async function getFsType(dirPath: string): Promise { + const { stdout, exitCode } = await spawn("stat", ["-f", "-c", "%T", dirPath]); + if (exitCode !== 0) return "unknown"; + return stdout; +} + +function todayIso(): string { + return new Date().toISOString().slice(0, 10); +} + +async function readMountsJson(filePath: string): Promise { + try { + const raw = await readFile(filePath, "utf-8"); + const parsed = JSON.parse(raw) as MountsJson; + if (parsed.version === 1 && Array.isArray(parsed.volumes)) { + return parsed; + } + } catch { + // file doesn't exist or is invalid + } + return { version: 1, volumes: [] }; +} + +function sanitizeName(name: string): string { + return name.replace(/[^a-zA-Z0-9-]/g, "-").replace(/^-+|-+$/g, ""); +} + +export async function checkVolumeCandidates( + workspaceRoot: string, +): Promise { + const nameArgs = CANDIDATE_NAMES.flatMap((n) => ["-name", n, "-o"]); + // Remove trailing -o + nameArgs.pop(); + + const { stdout, exitCode } = await spawn("find", [ + workspaceRoot, + "-maxdepth", + "4", + "-type", + "d", + "(", + ...nameArgs, + ")", + "-not", + "-path", + "*/node_modules/*/node_modules", + ]); + + if (exitCode !== 0 || !stdout) { + return [ + { + name: "Volume candidates", + category: "volumes", + status: "pass", + message: "no slow bind-mounted directories detected", + }, + ]; + } + + const dirs = stdout.split("\n").filter(Boolean); + const results: CheckResult[] = []; + + for (const dirPath of dirs) { + const dirName = basename(dirPath); + const candidate = VOLUME_CANDIDATES.find((c) => c.pattern === dirName); + if (!candidate) continue; + + const contextFile = hasContextFile(dirPath, candidate.contextFiles); + if (!contextFile) continue; + + const [mounted, fsType] = await Promise.all([ + isMountpoint(dirPath), + getFsType(dirPath), + ]); + + if (mounted) continue; + const isSlow = SLOW_FS_TYPES.some((s) => fsType.includes(s)); + if (!isSlow) continue; + + const relPath = relative(workspaceRoot, dirPath); + const sanitized = sanitizeName(relPath); + const mountsPath = join(workspaceRoot, ".codeforge", "mounts.json"); + + results.push({ + name: `Volume candidate: ${relPath}`, + category: "volumes", + status: "warn", + message: `${dirName} on ${fsType} (slow bind mount)`, + hint: "Add as Docker volume for faster I/O", + fix: { + label: `Add ${relPath} as Docker volume`, + detail: `Registers ${relPath} in .codeforge/mounts.json so CodeForge can mount it as a Docker volume instead of a bind mount, significantly improving I/O performance.`, + impact: "requires rebuild", + requiresRebuild: true, + apply: async (): Promise<{ + applied: boolean; + message: string; + }> => { + const codeforgeDir = join(workspaceRoot, ".codeforge"); + if (!existsSync(codeforgeDir)) { + await mkdir(codeforgeDir, { recursive: true }); + } + + const mounts = await readMountsJson(mountsPath); + + const existing = mounts.volumes.findIndex((v) => v.path === relPath); + const entry: MountEntry = { + path: relPath, + source: "auto", + signal: contextFile, + added: todayIso(), + }; + + if (existing >= 0) { + // Preserve user entries, update auto entries + if (mounts.volumes[existing].source === "user") { + return { + applied: false, + message: `${relPath} already has a user-defined mount entry`, + }; + } + mounts.volumes[existing] = entry; + } else { + mounts.volumes.push(entry); + } + + await writeFile( + mountsPath, + JSON.stringify(mounts, null, "\t") + "\n", + "utf-8", + ); + + // Check if Docker Compose infrastructure exists + const composeExists = existsSync( + join(workspaceRoot, ".devcontainer", "docker-compose.yml"), + ); + + if (composeExists) { + return { + applied: true, + message: `Added ${relPath} to .codeforge/mounts.json. Volumes will auto-apply on next container rebuild.`, + }; + } + + const volumeSnippet = JSON.stringify( + { + source: `codeforge-vol-${sanitized}`, + target: `/workspaces/${relPath}`, + type: "volume", + }, + null, + " ", + ); + + return { + applied: true, + message: `Added ${relPath} to .codeforge/mounts.json.\nTo apply manually in devcontainer.json, add to "mounts":\n${volumeSnippet}`, + }; + }, + }, + }); + } + + if (results.length === 0) { + return [ + { + name: "Volume candidates", + category: "volumes", + status: "pass", + message: "no slow bind-mounted directories detected", + }, + ]; + } + + return results; +} diff --git a/cli/src/commands/doctor/checks/wsl.ts b/cli/src/commands/doctor/checks/wsl.ts new file mode 100644 index 0000000..feb4a4d --- /dev/null +++ b/cli/src/commands/doctor/checks/wsl.ts @@ -0,0 +1,114 @@ +import type { CheckResult } from "../types.js"; + +async function getHostRamGB(): Promise { + try { + const content = await Bun.file("/proc/meminfo").text(); + const match = content.match(/MemTotal:\s+(\d+)\s+kB/); + if (!match) return 0; + return Math.round(Number(match[1]) / 1024 / 1024); + } catch { + return 0; + } +} + +interface WslConfigTier { + memory: string; + swap: string; +} + +function getWslConfigTier(ramGB: number): WslConfigTier { + if (ramGB <= 16) return { memory: "8GB", swap: "1GB" }; + if (ramGB <= 32) return { memory: "12GB", swap: "2GB" }; + if (ramGB <= 64) return { memory: "16GB", swap: "2GB" }; + return { memory: "24GB", swap: "2GB" }; +} + +function generateWslConfig(tier: WslConfigTier): string { + return [ + "[wsl2]", + `memory=${tier.memory}`, + `swap=${tier.swap}`, + "autoMemoryReclaim=gradual", + "sparseVhd=true", + "", + ].join("\n"); +} + +export async function checkWslConfig( + isWsl: boolean, +): Promise { + if (!isWsl) return null; + + const totalRamGB = await getHostRamGB(); + if (totalRamGB === 0) { + return { + name: "WSL memory configuration", + category: "wsl", + status: "info", + message: "could not detect host RAM", + hint: "Check /proc/meminfo is readable", + }; + } + + const tier = getWslConfigTier(totalRamGB); + const config = generateWslConfig(tier); + + return { + name: "WSL memory configuration", + category: "wsl", + status: "info", + message: `${totalRamGB}GB host RAM detected`, + hint: "Optimize WSL with a .wslconfig file", + fix: { + label: "Generate optimized .wslconfig", + detail: `A .wslconfig file controls WSL2 resource allocation. Based on ${totalRamGB}GB host RAM, the recommended settings limit WSL to ${tier.memory} memory and ${tier.swap} swap, enable gradual memory reclaim to return unused pages to the host, and enable sparse VHD to reclaim disk space automatically.`, + impact: "advisory only — must save manually on Windows host", + requiresRebuild: false, + apply: async () => ({ + applied: false, + message: + "Cannot auto-apply from inside the container. Save the .wslconfig content shown below to your Windows host, then restart WSL with: wsl --shutdown", + advisory: `Save this to %USERPROFILE%\\.wslconfig on your Windows host:\n\n${config}\nThen restart WSL:\n wsl --shutdown`, + }), + }, + }; +} + +export async function checkDefenderExclusions( + isWsl: boolean, +): Promise { + if (!isWsl) return null; + + const powershellCommands = [ + "# Run as Administrator in PowerShell", + 'Add-MpPreference -ExclusionPath "$env:LOCALAPPDATA\\Docker"', + 'Add-MpPreference -ExclusionPath "$env:PROGRAMFILES\\Docker"', + 'Add-MpPreference -ExclusionProcess "com.docker.backend.exe"', + 'Add-MpPreference -ExclusionProcess "vpnkit.exe"', + 'Add-MpPreference -ExclusionProcess "wsl.exe"', + 'Add-MpPreference -ExclusionProcess "wslhost.exe"', + "# Add your project path:", + '# Add-MpPreference -ExclusionPath "\\\\wsl$\\Ubuntu\\home\\\\projects"', + ].join("\n"); + + return { + name: "Windows Defender exclusions", + category: "wsl", + status: "info", + message: "Defender real-time scanning may slow file operations", + hint: "Exclude WSL and project paths from Defender", + fix: { + label: "Generate Defender exclusion commands", + detail: + "Windows Defender real-time protection scans every file operation that crosses the WSL/Windows boundary. Excluding Docker and WSL processes from scanning can significantly reduce I/O latency for builds, installs, and file watches.", + impact: "advisory only — requires admin PowerShell on Windows host", + requiresRebuild: false, + apply: async () => ({ + applied: false, + message: + "Cannot auto-apply from inside the container. Run the PowerShell commands shown below as Administrator on your Windows host.", + advisory: `Run these commands in an elevated PowerShell session on your Windows host:\n\n${powershellCommands}`, + }), + }, + }; +} diff --git a/cli/src/commands/doctor/fix.ts b/cli/src/commands/doctor/fix.ts new file mode 100644 index 0000000..606f097 --- /dev/null +++ b/cli/src/commands/doctor/fix.ts @@ -0,0 +1,165 @@ +import { + cancel, + intro, + isCancel, + log, + multiselect, + note, + outro, +} from "@clack/prompts"; +import type { CheckCategory, CheckResult, FixResult } from "./types.js"; + +interface FixOptions { + yes: boolean; + dryRun: boolean; + only?: string; +} + +const CATEGORY_LABELS: Record = { + auth: "Authentication", + environment: "Environment", + volumes: "Volumes", + wsl: "WSL", +}; + +const categoryMap: Record = { + auth: "auth", + env: "environment", + environment: "environment", + volumes: "volumes", + wsl: "wsl", +}; + +function categoryLabel(cat: CheckCategory): string { + return CATEGORY_LABELS[cat]; +} + +function groupByCategory( + checks: CheckResult[], +): Map { + const map = new Map(); + for (const check of checks) { + const existing = map.get(check.category) ?? []; + existing.push(check); + map.set(check.category, existing); + } + return map; +} + +async function applyFixes(fixes: CheckResult[]): Promise { + const results: { check: CheckResult; result: FixResult }[] = []; + const advisories: { check: CheckResult; result: FixResult }[] = []; + + for (const check of fixes) { + const result = await check.fix!.apply(); + if (result.advisory) { + advisories.push({ check, result }); + } else { + results.push({ check, result }); + } + } + + const applied = results.filter((r) => r.result.applied); + if (applied.length > 0) { + log.success( + `Applied ${applied.length} fix${applied.length !== 1 ? "es" : ""}:`, + ); + for (const { result } of applied) { + log.message(` ${result.message}`); + } + } + + if (advisories.length > 0) { + const needsManual = advisories.length; + log.info( + `${needsManual} fix${needsManual !== 1 ? "es" : ""} require${needsManual === 1 ? "s" : ""} manual action:`, + ); + for (const { check, result } of advisories) { + note(result.advisory!, check.fix!.label); + } + } + + const needsRebuild = fixes.some((f) => f.fix!.requiresRebuild); + if (needsRebuild) { + log.warn("Some changes require a container rebuild to take effect."); + } +} + +export async function runFixMode( + checks: CheckResult[], + options: FixOptions, +): Promise { + const fixable = checks.filter((c) => c.fix != null); + if (fixable.length === 0) { + log.info("No fixable issues found."); + return; + } + + if (!options.yes && !process.stdout.isTTY) { + console.error( + "Error: --fix requires an interactive terminal. Use --fix --yes for non-interactive mode.", + ); + process.exit(1); + } + + let candidates = fixable; + if (options.only) { + const cat = categoryMap[options.only]; + if (!cat) { + console.error( + `Unknown category: ${options.only}. Valid: auth, env, volumes, wsl`, + ); + process.exit(1); + } + candidates = fixable.filter((c) => c.category === cat); + } + + if (options.dryRun) { + log.info( + `Found ${candidates.length} fixable issue${candidates.length !== 1 ? "s" : ""}:`, + ); + for (const check of candidates) { + log.message(` ${check.fix!.label} (${check.fix!.impact})`); + } + return; + } + + if (options.yes) { + await applyFixes(candidates); + return; + } + + intro("CodeForge Doctor — Fix Mode"); + + const selectedFixes: CheckResult[] = []; + const categories = groupByCategory(candidates); + + for (const [category, fixes] of Array.from(categories)) { + const result = await multiselect({ + message: `${categoryLabel(category)} (${fixes.length} fix${fixes.length !== 1 ? "es" : ""})`, + options: fixes.map((f) => ({ + value: f.name, + label: f.fix!.label, + hint: f.fix!.impact, + })), + initialValues: fixes.map((f) => f.name), + required: false, + }); + + if (isCancel(result)) { + cancel("Fix mode cancelled."); + process.exit(0); + } + + const selected = fixes.filter((f) => (result as string[]).includes(f.name)); + selectedFixes.push(...selected); + } + + if (selectedFixes.length === 0) { + outro("No fixes selected."); + return; + } + + await applyFixes(selectedFixes); + outro("Done!"); +} diff --git a/cli/src/commands/doctor/format.ts b/cli/src/commands/doctor/format.ts new file mode 100644 index 0000000..0c79c37 --- /dev/null +++ b/cli/src/commands/doctor/format.ts @@ -0,0 +1,74 @@ +import chalk from "chalk"; +import type { CheckCategory, CheckResult, DoctorReport } from "./types.js"; + +export function formatText(report: DoctorReport, useColor: boolean): string { + const c = { + bold: useColor ? chalk.bold : (s: string) => s, + green: useColor ? chalk.green : (s: string) => s, + yellow: useColor ? chalk.yellow : (s: string) => s, + red: useColor ? chalk.red : (s: string) => s, + blue: useColor ? chalk.blue : (s: string) => s, + }; + + function icon(status: CheckResult["status"]): string { + switch (status) { + case "pass": + return c.green("✓"); + case "warn": + return c.yellow("⚠"); + case "fail": + return c.red("✗"); + case "info": + return c.blue("ℹ"); + } + } + + const lines: string[] = []; + lines.push(c.bold("CodeForge Doctor")); + lines.push("━━━━━━━━━━━━━━━━"); + lines.push(""); + + const categoryConfig: { key: CheckCategory; label: string }[] = [ + { key: "auth", label: "Authentication" }, + { key: "environment", label: "Environment" }, + { key: "volumes", label: "Volumes" }, + { key: "wsl", label: "WSL" }, + ]; + + for (const { key, label } of categoryConfig) { + const checks = report.checks.filter((ch) => ch.category === key); + if (checks.length === 0) continue; + lines.push(`${c.bold(label)}:`); + for (const check of checks) { + lines.push(` ${icon(check.status)} ${check.name} (${check.message})`); + if (check.hint) { + lines.push(` → ${check.hint}`); + } + } + lines.push(""); + } + + lines.push("━━━━━━━━━━━━━━━━"); + const parts = [`${report.passed} passed`]; + if (report.failed > 0) parts.push(`${report.failed} failed`); + if (report.warnings > 0) + parts.push(`${report.warnings} warning${report.warnings !== 1 ? "s" : ""}`); + lines.push(` ${parts.join(", ")}`); + + return lines.join("\n"); +} + +export function formatJson(report: DoctorReport): string { + return JSON.stringify( + { + checks: report.checks.map(({ fix, ...rest }) => rest), + summary: { + passed: report.passed, + failed: report.failed, + warnings: report.warnings, + }, + }, + null, + 2, + ); +} diff --git a/cli/src/commands/doctor/index.ts b/cli/src/commands/doctor/index.ts new file mode 100644 index 0000000..8f8c140 --- /dev/null +++ b/cli/src/commands/doctor/index.ts @@ -0,0 +1,96 @@ +import type { Command } from "commander"; +import { checkGhAuth, checkGitUser } from "./checks/auth.js"; +import { + checkCacheDirs, + checkDockerMemory, + checkTmpdir, + checkWorkspaceFs, +} from "./checks/environment.js"; +import { checkVolumeCandidates } from "./checks/volumes.js"; +import { checkDefenderExclusions, checkWslConfig } from "./checks/wsl.js"; +import { runFixMode } from "./fix.js"; +import { formatJson, formatText } from "./format.js"; +import type { CheckResult, DoctorReport } from "./types.js"; + +export function registerDoctorCommand(parent: Command): void { + parent + .command("doctor") + .description("Check CodeForge environment health (WSL, auth, caches)") + .option("--format ", "Output format: text or json", "text") + .option("--no-color", "Disable colored output") + .option("--fix", "Enter interactive fix mode") + .option("-y, --yes", "Skip TUI prompts, apply all fixes") + .option("--dry-run", "Show what --fix would change without applying") + .option("--only ", "Filter fixes: auth, env, volumes, wsl") + .action(async (options) => { + const workspaceRoot = process.env.WORKSPACE_ROOT || "/workspaces"; + + // Run auth + environment checks in parallel + const [authChecks, envChecks] = await Promise.all([ + Promise.all([checkGhAuth(), checkGitUser()]), + Promise.all([ + checkWorkspaceFs(), + checkTmpdir(), + checkCacheDirs(), + checkDockerMemory(), + ]), + ]); + + // Determine WSL status from workspace FS check + const isWsl = envChecks.some( + (c) => c.name === "Workspace filesystem" && c.status === "warn", + ); + + // Run volume + WSL checks (WSL checks return null when not WSL) + const [volumeChecks, wslConfigCheck, defenderCheck] = await Promise.all([ + checkVolumeCandidates(workspaceRoot), + checkWslConfig(isWsl), + checkDefenderExclusions(isWsl), + ]); + + // Assemble all checks, filtering out nulls from WSL checks + const wslChecks = [wslConfigCheck, defenderCheck].filter( + (c): c is CheckResult => c !== null, + ); + + const checks: CheckResult[] = [ + ...authChecks, + ...envChecks, + ...volumeChecks, + ...wslChecks, + ]; + + const report: DoctorReport = { + checks, + passed: checks.filter((c) => c.status === "pass").length, + failed: checks.filter((c) => c.status === "fail").length, + warnings: checks.filter((c) => c.status === "warn").length, + }; + + // Display report + if (options.format === "json") { + console.log(formatJson(report)); + } else { + const useColor = options.color !== false; + console.log(formatText(report, useColor)); + } + + // Fix mode + if (options.fix) { + console.log(""); // spacing between report and fix mode + await runFixMode(checks, { + yes: options.yes ?? false, + dryRun: options.dryRun ?? false, + only: options.only, + }); + } + + if (report.failed > 0) { + process.exit(1); + } + }); +} + +// Exported for testing +export { formatJson, formatText } from "./format.js"; +export type { CheckResult, DoctorReport } from "./types.js"; diff --git a/cli/src/commands/doctor/types.ts b/cli/src/commands/doctor/types.ts new file mode 100644 index 0000000..58492be --- /dev/null +++ b/cli/src/commands/doctor/types.ts @@ -0,0 +1,31 @@ +export type CheckCategory = "auth" | "environment" | "volumes" | "wsl"; + +export interface FixResult { + applied: boolean; + message: string; + advisory?: string; // for fixes that can't auto-apply (Defender, .wslconfig) +} + +export interface FixAction { + label: string; // shown in TUI: "Move TMPDIR to /tmp" + detail: string; // full explanation + impact: string; // "1 env var change" or "requires rebuild" + requiresRebuild: boolean; + apply: () => Promise; +} + +export interface CheckResult { + name: string; + category: CheckCategory; + status: "pass" | "warn" | "fail" | "info"; + message: string; + hint?: string; + fix?: FixAction; +} + +export interface DoctorReport { + checks: CheckResult[]; + passed: number; + failed: number; + warnings: number; +} diff --git a/cli/src/commands/doctor/util.ts b/cli/src/commands/doctor/util.ts new file mode 100644 index 0000000..5c37a0e --- /dev/null +++ b/cli/src/commands/doctor/util.ts @@ -0,0 +1,26 @@ +export const SLOW_FS_TYPES = [ + "smb", + "smb2", + "9p", + "drvfs", + "cifs", + "nfs", + "fuse.drvfs", +]; + +export async function spawn( + cmd: string, + args: string[], +): Promise<{ stdout: string; exitCode: number }> { + try { + const proc = Bun.spawn([cmd, ...args], { + stdout: "pipe", + stderr: "pipe", + }); + const stdout = await new Response(proc.stdout).text(); + const exitCode = await proc.exited; + return { stdout: stdout.trim(), exitCode }; + } catch { + return { stdout: "", exitCode: 1 }; + } +} diff --git a/cli/src/index.ts b/cli/src/index.ts index cc7add9..35bb91c 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -9,6 +9,7 @@ import { registerContainerLsCommand } from "./commands/container/ls.js"; import { registerContainerRebuildCommand } from "./commands/container/rebuild.js"; import { registerContainerShellCommand } from "./commands/container/shell.js"; import { registerContainerUpCommand } from "./commands/container/up.js"; +import { registerDoctorCommand } from "./commands/doctor/index.js"; import { registerIndexBuildCommand } from "./commands/index/build.js"; import { registerIndexCleanCommand } from "./commands/index/clean.js"; import { registerIndexSearchCommand } from "./commands/index/search.js"; @@ -104,6 +105,7 @@ registerContainerLsCommand(container); registerContainerShellCommand(container); registerProxyCommand(program); +registerDoctorCommand(program); // Proxy middleware: when outside container and not --local, proxy existing commands into container program.hook("preAction", async (_thisCommand, actionCommand) => { diff --git a/cli/tests/doctor.test.ts b/cli/tests/doctor.test.ts new file mode 100644 index 0000000..ccc77a8 --- /dev/null +++ b/cli/tests/doctor.test.ts @@ -0,0 +1,651 @@ +import { + afterEach, + beforeEach, + describe, + expect, + mock, + spyOn, + test, +} from "bun:test"; +import { Command } from "commander"; +import { runFixMode } from "../src/commands/doctor/fix.js"; +import { + type CheckResult, + type DoctorReport, + formatJson, + formatText, + registerDoctorCommand, +} from "../src/commands/doctor/index.js"; +import type { FixAction } from "../src/commands/doctor/types.js"; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function makeReport(checks: CheckResult[]): DoctorReport { + return { + checks, + passed: checks.filter((c) => c.status === "pass").length, + failed: checks.filter((c) => c.status === "fail").length, + warnings: checks.filter((c) => c.status === "warn").length, + }; +} + +function makeCheck(overrides: Partial = {}): CheckResult { + return { + name: "Test check", + category: "environment", + status: "pass", + message: "all good", + ...overrides, + }; +} + +function makeFix(overrides: Partial = {}): FixAction { + return { + label: "Fix something", + detail: "Detailed explanation", + impact: "minor change", + requiresRebuild: false, + apply: mock(() => Promise.resolve({ applied: true, message: "Fixed it" })), + ...overrides, + }; +} + +// --------------------------------------------------------------------------- +// 1. Registration tests +// --------------------------------------------------------------------------- + +describe("registerDoctorCommand", () => { + test("registers doctor as a subcommand", () => { + const program = new Command(); + registerDoctorCommand(program); + const cmd = program.commands.find((c) => c.name() === "doctor"); + expect(cmd).toBeDefined(); + expect(cmd!.description()).toContain("health"); + }); + + test("has --format and --no-color options", () => { + const program = new Command(); + registerDoctorCommand(program); + const cmd = program.commands.find((c) => c.name() === "doctor")!; + const opts = cmd.options.map((o) => o.long); + expect(opts).toContain("--format"); + expect(opts).toContain("--no-color"); + }); + + test("has --fix, --yes, --dry-run, --only options", () => { + const program = new Command(); + registerDoctorCommand(program); + const cmd = program.commands.find((c) => c.name() === "doctor")!; + const opts = cmd.options.map((o) => o.long); + expect(opts).toContain("--fix"); + expect(opts).toContain("--yes"); + expect(opts).toContain("--dry-run"); + expect(opts).toContain("--only"); + }); +}); + +// --------------------------------------------------------------------------- +// 2. Format tests — formatText +// --------------------------------------------------------------------------- + +describe("formatText", () => { + test("renders pass/warn/fail summary line", () => { + const report = makeReport([ + makeCheck({ + name: "GitHub CLI authenticated", + category: "auth", + status: "pass", + message: "user: test", + }), + makeCheck({ + name: "Git user configured", + category: "auth", + status: "pass", + message: "Test ", + }), + makeCheck({ + name: "Workspace filesystem", + category: "environment", + status: "warn", + message: "drvfs (slow)", + hint: "Move to WSL", + }), + makeCheck({ + name: "TMPDIR location", + category: "environment", + status: "pass", + message: "unset (using container /tmp)", + }), + makeCheck({ + name: "Cache directories on volumes", + category: "environment", + status: "pass", + message: "all volume-backed", + }), + makeCheck({ + name: "Container memory", + category: "environment", + status: "info", + message: "6.0GB", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("4 passed"); + expect(output).toContain("1 warning"); + expect(output).not.toContain("failed"); + }); + + test("renders section headers for auth and environment categories", () => { + const report = makeReport([ + makeCheck({ + name: "GitHub CLI authenticated", + category: "auth", + status: "pass", + message: "user: test", + }), + makeCheck({ + name: "Git user configured", + category: "auth", + status: "pass", + message: "Test ", + }), + makeCheck({ + name: "Workspace filesystem", + category: "environment", + status: "pass", + message: "ext4 (fast)", + }), + makeCheck({ + name: "TMPDIR location", + category: "environment", + status: "pass", + message: "unset (using container /tmp)", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("Authentication:"); + expect(output).toContain("Environment:"); + }); + + test("renders Volumes category when volume checks present", () => { + const report = makeReport([ + makeCheck({ + name: "Volume candidate: project/node_modules", + category: "volumes", + status: "warn", + message: "node_modules on drvfs (slow bind mount)", + hint: "Add as Docker volume for faster I/O", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("Volumes:"); + }); + + test("hides WSL category when no WSL checks present", () => { + const report = makeReport([ + makeCheck({ + name: "GitHub CLI authenticated", + category: "auth", + status: "pass", + message: "user: test", + }), + makeCheck({ + name: "Workspace filesystem", + category: "environment", + status: "pass", + message: "ext4 (fast)", + }), + ]); + const output = formatText(report, false); + expect(output).not.toContain("WSL:"); + }); + + test("renders WSL category when WSL checks present", () => { + const report = makeReport([ + makeCheck({ + name: "WSL memory configuration", + category: "wsl", + status: "info", + message: "32GB host RAM detected", + hint: "Optimize WSL with a .wslconfig file", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("WSL:"); + }); + + test("renders hints for warnings", () => { + const report = makeReport([ + makeCheck({ + name: "GitHub CLI authenticated", + category: "auth", + status: "warn", + message: "not authenticated", + hint: "Run: gh auth login", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("Run: gh auth login"); + }); + + test("includes title", () => { + const report = makeReport([]); + const output = formatText(report, false); + expect(output).toContain("CodeForge Doctor"); + }); +}); + +// --------------------------------------------------------------------------- +// 2. Format tests — formatJson +// --------------------------------------------------------------------------- + +describe("formatJson", () => { + test("returns valid JSON with checks and summary", () => { + const report = makeReport([ + makeCheck({ + name: "GitHub CLI authenticated", + category: "auth", + status: "pass", + message: "user: test", + }), + makeCheck({ + name: "Workspace filesystem", + category: "environment", + status: "warn", + message: "drvfs", + hint: "Move to WSL", + }), + ]); + const parsed = JSON.parse(formatJson(report)); + expect(parsed.checks).toHaveLength(2); + expect(parsed.summary.passed).toBe(1); + expect(parsed.summary.warnings).toBe(1); + expect(parsed.summary.failed).toBe(0); + }); + + test("preserves hints in JSON output", () => { + const report = makeReport([ + makeCheck({ + status: "warn", + message: "msg", + hint: "do this", + }), + ]); + const parsed = JSON.parse(formatJson(report)); + expect(parsed.checks[0].hint).toBe("do this"); + }); + + test("strips fix field from JSON output", () => { + const report = makeReport([ + makeCheck({ + name: "TMPDIR location", + category: "environment", + status: "warn", + message: "not ideal", + hint: "Move it", + fix: makeFix(), + }), + ]); + const parsed = JSON.parse(formatJson(report)); + expect(parsed.checks[0].fix).toBeUndefined(); + }); +}); + +// --------------------------------------------------------------------------- +// 3. Type/structure tests +// --------------------------------------------------------------------------- + +describe("type/structure", () => { + test("CheckResult with fix action has correct shape", () => { + const fix = makeFix({ + label: "Move TMPDIR", + detail: "Moves TMPDIR to /tmp", + impact: "1 env var change", + requiresRebuild: false, + }); + const check = makeCheck({ + name: "TMPDIR location", + category: "environment", + status: "warn", + message: "on slow fs", + hint: "Move to /tmp", + fix, + }); + + expect(check.fix).toBeDefined(); + expect(check.fix!.label).toBe("Move TMPDIR"); + expect(check.fix!.detail).toBe("Moves TMPDIR to /tmp"); + expect(check.fix!.impact).toBe("1 env var change"); + expect(check.fix!.requiresRebuild).toBe(false); + expect(typeof check.fix!.apply).toBe("function"); + }); + + test("DoctorReport computes passed/failed/warnings correctly", () => { + const report = makeReport([ + makeCheck({ status: "pass" }), + makeCheck({ status: "pass" }), + makeCheck({ status: "fail" }), + makeCheck({ status: "warn" }), + makeCheck({ status: "warn" }), + makeCheck({ status: "info" }), + ]); + expect(report.passed).toBe(2); + expect(report.failed).toBe(1); + expect(report.warnings).toBe(2); + }); + + test("info status does not count as passed, failed, or warning", () => { + const report = makeReport([ + makeCheck({ status: "info" }), + makeCheck({ status: "info" }), + ]); + expect(report.passed).toBe(0); + expect(report.failed).toBe(0); + expect(report.warnings).toBe(0); + }); +}); + +// --------------------------------------------------------------------------- +// 4. Volume detection tests (checks/volumes.ts) +// --------------------------------------------------------------------------- + +describe("checkVolumeCandidates", () => { + // These tests import checkVolumeCandidates which shells out to find/stat/mountpoint. + // We test at a higher level by mocking the spawn utility. + + test("returns pass when no candidates found (find returns empty)", async () => { + // Mock the module-level spawn so find returns nothing + const volumesMod = await import("../src/commands/doctor/checks/volumes.js"); + + // Mock the util module's spawn to simulate find returning empty + const utilMod = await import("../src/commands/doctor/util.js"); + const originalSpawn = utilMod.spawn; + + // We can't easily mock the spawn inside volumes.ts since it's already + // imported, so instead we call checkVolumeCandidates with a path that + // won't have any matching directories. + const results = await volumesMod.checkVolumeCandidates( + "/nonexistent-test-path-that-will-not-exist", + ); + + // When find fails or returns empty, should return a single pass result + expect(results.length).toBe(1); + expect(results[0].status).toBe("pass"); + expect(results[0].category).toBe("volumes"); + expect(results[0].message).toContain("no slow bind-mounted"); + }); +}); + +// --------------------------------------------------------------------------- +// 5. WSL checks tests (checks/wsl.ts) +// --------------------------------------------------------------------------- + +describe("checkWslConfig", () => { + const { checkWslConfig } = require("../src/commands/doctor/checks/wsl.js"); + + test("returns null when isWsl is false", async () => { + const result = await checkWslConfig(false); + expect(result).toBeNull(); + }); + + test("returns CheckResult with correct category when isWsl is true", async () => { + const result = await checkWslConfig(true); + // When isWsl is true, it always returns a CheckResult (never null) + expect(result).not.toBeNull(); + expect(result!.category).toBe("wsl"); + expect(result!.name).toBe("WSL memory configuration"); + }); + + test("returns CheckResult with RAM tier info when isWsl is true", async () => { + const result = await checkWslConfig(true); + expect(result).not.toBeNull(); + // Message should reference host RAM (either detected amount or info about detection) + expect(result!.message).toMatch(/RAM|meminfo/i); + }); + + test("includes fix action with .wslconfig generation", async () => { + const result = await checkWslConfig(true); + expect(result).not.toBeNull(); + expect(result!.fix).toBeDefined(); + expect(result!.fix!.label).toContain(".wslconfig"); + + // Apply should return advisory (cannot auto-apply from container) + const fixResult = await result!.fix!.apply(); + expect(fixResult.applied).toBe(false); + expect(fixResult.advisory).toBeDefined(); + expect(fixResult.advisory).toContain(".wslconfig"); + }); +}); + +describe("checkDefenderExclusions", () => { + const { + checkDefenderExclusions, + } = require("../src/commands/doctor/checks/wsl.js"); + + test("returns null when isWsl is false", async () => { + const result = await checkDefenderExclusions(false); + expect(result).toBeNull(); + }); + + test("returns CheckResult with correct category when isWsl is true", async () => { + const result = await checkDefenderExclusions(true); + expect(result).not.toBeNull(); + expect(result!.category).toBe("wsl"); + expect(result!.name).toBe("Windows Defender exclusions"); + }); + + test("returns CheckResult with PowerShell commands when isWsl is true", async () => { + const result = await checkDefenderExclusions(true); + expect(result).not.toBeNull(); + expect(result!.fix).toBeDefined(); + + const fixResult = await result!.fix!.apply(); + expect(fixResult.applied).toBe(false); + expect(fixResult.advisory).toBeDefined(); + expect(fixResult.advisory).toContain("PowerShell"); + expect(fixResult.advisory).toContain("Add-MpPreference"); + }); +}); + +// --------------------------------------------------------------------------- +// 6. Fix mode tests (fix.ts) +// --------------------------------------------------------------------------- + +describe("runFixMode", () => { + let logSpy: ReturnType; + let errorSpy: ReturnType; + let exitSpy: ReturnType; + let originalIsTTY: boolean | undefined; + + beforeEach(() => { + logSpy = spyOn(console, "log").mockImplementation(() => {}); + errorSpy = spyOn(console, "error").mockImplementation(() => {}); + exitSpy = spyOn(process, "exit").mockImplementation((() => { + throw new Error("process.exit called"); + }) as any); + originalIsTTY = process.stdout.isTTY; + }); + + afterEach(() => { + logSpy.mockRestore(); + errorSpy.mockRestore(); + exitSpy.mockRestore(); + Object.defineProperty(process.stdout, "isTTY", { + value: originalIsTTY, + writable: true, + configurable: true, + }); + }); + + test("reports 'No fixable issues found' when no fixes exist", async () => { + const checks: CheckResult[] = [ + makeCheck({ status: "pass" }), + makeCheck({ status: "warn" }), + // Neither has a fix property + ]; + + await runFixMode(checks, { yes: false, dryRun: false }); + // @clack/prompts log.info is used internally; the function should return + // without error. We verify it didn't call process.exit. + expect(exitSpy).not.toHaveBeenCalled(); + }); + + test("exits with error in non-TTY without --yes", async () => { + Object.defineProperty(process.stdout, "isTTY", { + value: false, + writable: true, + configurable: true, + }); + + const checks: CheckResult[] = [ + makeCheck({ + status: "warn", + fix: makeFix(), + }), + ]; + + expect(runFixMode(checks, { yes: false, dryRun: false })).rejects.toThrow(); + expect(exitSpy).toHaveBeenCalledWith(1); + }); + + test("dry run mode lists fixes without applying", async () => { + // dry-run still goes through the TTY guard when yes=false, + // so simulate an interactive terminal + Object.defineProperty(process.stdout, "isTTY", { + value: true, + writable: true, + configurable: true, + }); + + const applyFn = mock(() => + Promise.resolve({ applied: true, message: "Applied" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + status: "warn", + fix: makeFix({ + label: "Fix the thing", + impact: "minor", + apply: applyFn, + }), + }), + ]; + + await runFixMode(checks, { yes: false, dryRun: true }); + + // apply should NOT have been called + expect(applyFn).not.toHaveBeenCalled(); + // Should not exit with error + expect(exitSpy).not.toHaveBeenCalled(); + }); + + test("auto-apply mode (--yes) applies all fixes", async () => { + const applyFn1 = mock(() => + Promise.resolve({ applied: true, message: "Fixed A" }), + ); + const applyFn2 = mock(() => + Promise.resolve({ applied: true, message: "Fixed B" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Check A", + category: "environment", + status: "warn", + fix: makeFix({ apply: applyFn1 }), + }), + makeCheck({ + name: "Check B", + category: "auth", + status: "warn", + fix: makeFix({ apply: applyFn2 }), + }), + // This one has no fix — should be skipped + makeCheck({ name: "Check C", status: "pass" }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false }); + + expect(applyFn1).toHaveBeenCalledTimes(1); + expect(applyFn2).toHaveBeenCalledTimes(1); + }); + + test("category filter (--only) limits which fixes run", async () => { + const authApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed auth" }), + ); + const envApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed env" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Auth check", + category: "auth", + status: "warn", + fix: makeFix({ apply: authApply }), + }), + makeCheck({ + name: "Env check", + category: "environment", + status: "warn", + fix: makeFix({ apply: envApply }), + }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false, only: "auth" }); + + expect(authApply).toHaveBeenCalledTimes(1); + expect(envApply).not.toHaveBeenCalled(); + }); + + test("category filter accepts 'env' alias for 'environment'", async () => { + const envApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed env" }), + ); + const authApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed auth" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Env check", + category: "environment", + status: "warn", + fix: makeFix({ apply: envApply }), + }), + makeCheck({ + name: "Auth check", + category: "auth", + status: "warn", + fix: makeFix({ apply: authApply }), + }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false, only: "env" }); + + expect(envApply).toHaveBeenCalledTimes(1); + expect(authApply).not.toHaveBeenCalled(); + }); + + test("unknown --only category exits with error", async () => { + const checks: CheckResult[] = [ + makeCheck({ + status: "warn", + fix: makeFix(), + }), + ]; + + expect( + runFixMode(checks, { + yes: true, + dryRun: false, + only: "bogus", + }), + ).rejects.toThrow(); + expect(exitSpy).toHaveBeenCalledWith(1); + }); +}); From 69043ec52fe22f86decf7089b9e2f89c2e78c617 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Tue, 5 May 2026 21:42:32 +0000 Subject: [PATCH 04/27] feat(container): migrate to Docker Compose for dynamic volume management Replaces inline image/mounts/runArgs in devcontainer.json with a docker-compose.yml base file and generated compose override. Volume names are now fixed (no ${devcontainerId} suffix) for simpler management. Adds generate-mounts.mjs as an initializeCommand that reads .codeforge/mounts.json and produces docker-compose.codeforge.yml with project-specific volume mounts. The script runs on the host (Node.js, cross-platform) and gracefully falls back to an empty override when no mounts config exists. --- container/.devcontainer/.gitignore | 5 +- container/.devcontainer/devcontainer.json | 45 +++++----- container/.devcontainer/docker-compose.yml | 25 ++++++ .../.devcontainer/scripts/generate-mounts.mjs | 88 +++++++++++++++++++ 4 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 container/.devcontainer/docker-compose.yml create mode 100755 container/.devcontainer/scripts/generate-mounts.mjs diff --git a/container/.devcontainer/.gitignore b/container/.devcontainer/.gitignore index f751f19..8c4a32e 100644 --- a/container/.devcontainer/.gitignore +++ b/container/.devcontainer/.gitignore @@ -6,4 +6,7 @@ # Un-ignore dotfiles that should be tracked (overrides root .* pattern) !.env.example !.secrets.example -!.gitignore \ No newline at end of file +!.gitignore + +# Generated by initializeCommand (codeforge doctor --fix) +docker-compose.codeforge.yml \ No newline at end of file diff --git a/container/.devcontainer/devcontainer.json b/container/.devcontainer/devcontainer.json index 231691c..8c11bd9 100755 --- a/container/.devcontainer/devcontainer.json +++ b/container/.devcontainer/devcontainer.json @@ -1,33 +1,16 @@ { "name": "CodeForge - ${localWorkspaceFolderBasename}", - "image": "mcr.microsoft.com/devcontainers/python:3.14", + "dockerComposeFile": ["docker-compose.yml", "docker-compose.codeforge.yml"], + "service": "codeforge", + "initializeCommand": "node .devcontainer/scripts/generate-mounts.mjs", "workspaceFolder": "/workspaces", "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces,type=bind", - "mounts": [ - { - "source": "codeforge-claude-config-${devcontainerId}", - "target": "/home/vscode/.claude", - "type": "volume" - }, - { - "source": "codeforge-codex-config-${devcontainerId}", - "target": "/home/vscode/.codex", - "type": "volume" - }, - { - "source": "codeforge-hermes-config-${devcontainerId}", - "target": "/home/vscode/.hermes", - "type": "volume" - } - ], - "remoteEnv": { "WORKSPACE_ROOT": "/workspaces", "CLAUDE_CONFIG_DIR": "/home/vscode/.claude", - "GH_CONFIG_DIR": "/workspaces/.gh", - "TMPDIR": "/workspaces/.tmp", + "GH_CONFIG_DIR": "/home/vscode/.config/gh", "HERMES_CDP_ENDPOINT": "http://host.docker.internal:9223", "TERM": "${localEnv:TERM:xterm-256color}", "COLORTERM": "truecolor", @@ -119,6 +102,7 @@ "./features/tmux", "./features/agent-browser", "./features/claude-monitor", + "./features/claude-code-karma", "./features/ccusage", "./features/claude-code-router", "./features/oh-my-claude", @@ -185,6 +169,13 @@ "installer": "uv", "username": "automatic" }, + "./features/claude-code-karma": { + "version": "4067d87ee5c85eb7d2877890ba6174115f0bee2e", + "apiPort": "7848", + "frontendPort": "7847", + "autostart": "true", + "username": "automatic" + }, // ccburn — disabled; replaced by native ccstatusline session-usage/weekly-usage widgets // "./features/ccburn": { // "version": "latest", @@ -226,6 +217,14 @@ }, "portsAttributes": { + "7847": { + "label": "Claude Code Karma", + "onAutoForward": "notify" + }, + "7848": { + "label": "Claude Code Karma API", + "onAutoForward": "silent" + }, "*": { "onAutoForward": "notify" } @@ -277,7 +276,5 @@ "alefragnani.project-manager" ] } - }, - - "runArgs": ["--memory=6g", "--memory-swap=12g", "--name=codeforge"] + } } diff --git a/container/.devcontainer/docker-compose.yml b/container/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..8b0b61e --- /dev/null +++ b/container/.devcontainer/docker-compose.yml @@ -0,0 +1,25 @@ +services: + codeforge: + image: mcr.microsoft.com/devcontainers/python:3.14 + volumes: + # Persistent config volumes + - codeforge-claude-config:/home/vscode/.claude + - codeforge-codex-config:/home/vscode/.codex + - codeforge-hermes-config:/home/vscode/.hermes + - codeforge-gh-config:/home/vscode/.config/gh + # Cache volumes + - codeforge-cache:/home/vscode/.cache + - codeforge-npm-cache:/home/vscode/.npm + - codeforge-bun-cache:/home/vscode/.bun/install/cache + mem_limit: 6g + memswap_limit: 6g + container_name: codeforge + +volumes: + codeforge-claude-config: + codeforge-codex-config: + codeforge-hermes-config: + codeforge-gh-config: + codeforge-cache: + codeforge-npm-cache: + codeforge-bun-cache: diff --git a/container/.devcontainer/scripts/generate-mounts.mjs b/container/.devcontainer/scripts/generate-mounts.mjs new file mode 100755 index 0000000..04da727 --- /dev/null +++ b/container/.devcontainer/scripts/generate-mounts.mjs @@ -0,0 +1,88 @@ +#!/usr/bin/env node + +// generate-mounts.mjs — Reads .codeforge/mounts.json, generates docker-compose.codeforge.yml +// Runs on the HOST via initializeCommand. Must work on Windows, macOS, Linux. +// Node.js is always available (VS Code requires it). + +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; + +const MOUNTS_FILE = ".codeforge/mounts.json"; +const OUTPUT_FILE = ".devcontainer/docker-compose.codeforge.yml"; + +// When no mounts.json exists, generate an empty override so dockerComposeFile doesn't error +function generateEmptyOverride() { + return [ + '# No custom volumes configured. Run "codeforge doctor --fix" inside the container.', + "services:", + " codeforge: {}", + "", + ].join("\n"); +} + +function sanitizeName(path) { + return path.replace(/[^a-zA-Z0-9-]/g, "-").replace(/^-+|-+$/g, ""); +} + +function generateOverride(mounts) { + const lines = [ + "# Auto-generated by CodeForge. Do not edit.", + "# Source: .codeforge/mounts.json", + "# Regenerate: codeforge doctor --fix --yes --only volumes", + "services:", + " codeforge:", + " volumes:", + ]; + + const volumeNames = []; + + for (const vol of mounts.volumes) { + const volName = `codeforge-vol-${sanitizeName(vol.path)}`; + // Use /workspaces/ prefix since that's the workspace mount target + lines.push(` - ${volName}:/workspaces/${vol.path}`); + volumeNames.push(volName); + } + + lines.push(""); + lines.push("volumes:"); + for (const name of volumeNames) { + lines.push(` ${name}:`); + } + lines.push(""); + + return lines.join("\n"); +} + +// Main +try { + let content; + + if (!existsSync(MOUNTS_FILE)) { + content = generateEmptyOverride(); + } else { + const raw = readFileSync(MOUNTS_FILE, "utf-8"); + const mounts = JSON.parse(raw); + + if (!mounts.volumes || mounts.volumes.length === 0) { + content = generateEmptyOverride(); + } else { + content = generateOverride(mounts); + } + } + + // Ensure output directory exists + const outputDir = dirname(OUTPUT_FILE); + if (!existsSync(outputDir)) { + mkdirSync(outputDir, { recursive: true }); + } + + writeFileSync(OUTPUT_FILE, content, "utf-8"); + console.log(`[CodeForge] Generated ${OUTPUT_FILE}`); +} catch (err) { + // Don't fail the container build if mount generation fails + console.warn(`[CodeForge] Warning: mount generation failed: ${err.message}`); + // Write empty override as fallback + try { + writeFileSync(OUTPUT_FILE, generateEmptyOverride(), "utf-8"); + } catch {} +} From c2f0ec592dc0a06b447ef3e897481ec6410eb495 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Tue, 5 May 2026 21:42:47 +0000 Subject: [PATCH 05/27] docs: document doctor --fix, volume mounts, and compose migration Updates CHANGELOG, AGENTS, and README with doctor --fix, volume candidate detection, WSL .wslconfig generator, Defender exclusion guide, and Docker Compose migration entries. Adds volume mounts section to container-configuration docs and doctor --fix row to cli-tools reference. --- container/.devcontainer/AGENTS.md | 9 +++- container/.devcontainer/CHANGELOG.md | 23 ++++++++++ container/.devcontainer/README.md | 16 +++++-- .../docs/customize/container-configuration.md | 46 +++++++++++++++++-- docs/src/content/docs/reference/changelog.md | 23 ++++++++++ docs/src/content/docs/reference/cli-tools.md | 22 ++++++++- 6 files changed, 129 insertions(+), 10 deletions(-) mode change 100644 => 100755 container/.devcontainer/AGENTS.md mode change 100644 => 100755 container/.devcontainer/CHANGELOG.md mode change 100644 => 100755 container/.devcontainer/README.md diff --git a/container/.devcontainer/AGENTS.md b/container/.devcontainer/AGENTS.md old mode 100644 new mode 100755 index b732fef..91f9afe --- a/container/.devcontainer/AGENTS.md +++ b/container/.devcontainer/AGENTS.md @@ -15,7 +15,9 @@ CodeForge devcontainer for AI-assisted development with Claude Code. | `defaults/codeforge/config/disabled-hooks.json` | Disable individual plugin hooks by script name | | `defaults/codeforge/config/claude-code-router.json` | LLM provider routing config (deployed to ~/.claude-code-router/) | | `defaults/codeforge/file-manifest.json` | Controls which config files deploy and when | -| `devcontainer.json` | Container definition: image, features, mounts | +| `devcontainer.json` | Container definition: features, compose config, mounts | +| `docker-compose.yml` | Base Docker Compose file: image, volumes, resource limits | +| `.codeforge/mounts.json` | User/auto-detected volume mount configuration | | `.env` | Boolean flags controlling setup steps | Config files deploy via `defaults/codeforge/file-manifest.json` on every container start. Most deploy to `~/.claude/`; ccstatusline config deploys to `~/.config/ccstatusline/`. Each entry supports `overwrite`: `"if-changed"` (default, sha256), `"always"`, or `"never"`. Supported variables: `${CLAUDE_CONFIG_DIR}`, `${WORKSPACE_ROOT}`, `${HOME}`. @@ -37,8 +39,11 @@ Config files deploy via `defaults/codeforge/file-manifest.json` on every contain | `ccr start` / `ccr stop` | Claude Code Router daemon control | | `ccr-apply` | Redeploy router config + restart daemon | | `ccusage` / `ccburn` | Token usage analysis / burn rate | +| `karma-status` | Claude Code Karma dashboard process status and logs | | `agent-browser` | Headless Chromium (Playwright-based) | | `check-setup` | Verify CodeForge setup health | +| `codeforge doctor` | Environment health check (WSL, auth, caches, memory, volumes) | +| `codeforge doctor --fix` | Interactive fix mode — apply fixes for detected issues | | `dbr` | Dynamic port forwarding ([devcontainer-bridge](https://github.com/bradleybeddoes/devcontainer-bridge)) | | `cc-tools` | List all installed tools with versions | @@ -89,6 +94,8 @@ Codex CLI credentials (`~/.codex/`) are backed by a separate Docker named volume **oh-my-claude:** The local `features/oh-my-claude` feature is opt-in. It installs the OMC CLI and generated agents, skips OMC hooks/MCP/statusline, and preserves CodeForge-managed `~/.claude/settings.json`. OMC proxy sessions are launched per session with `omc cc` or the `omc-cc` helper; do not add a post-start OMC daemon. +**Claude Code Karma:** The local `features/claude-code-karma` feature is default-on. It installs Karma from a pinned git ref, starts the UI on port `7847` and API on port `7848`, and patches Karma so its Settings API/UI are read-only. CodeForge owns `~/.claude/settings.json`; add Karma hooks in `defaults/codeforge/config/settings.base.json`, not from Karma. + ## Modifying Behavior 1. **Change shared Claude settings**: Edit `defaults/codeforge/config/settings.base.json`, then run `node scripts/generate-settings-profiles.js` diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md old mode 100644 new mode 100755 index 313949d..eb0a367 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -2,6 +2,22 @@ ## Unreleased +### Performance + +- **TMPDIR removed from remoteEnv** — previously set to `/workspaces/.tmp` (bind mount, slow on NTFS/WSL). Now unset, defaulting to the container's `/tmp` (overlay/tmpfs). Scripts using `${TMPDIR:-/tmp}` already handle this correctly. Any user scripts depending on `$TMPDIR` being a persistent location should use an explicit path instead. +- **GH_CONFIG_DIR moved to Docker named volume** — changed from `/workspaces/.gh` (bind mount) to `/home/vscode/.config/gh` backed by a `codeforge-gh-config` named volume. Faster I/O and no gitignore hazard. One-time `gh auth login` required after first rebuild. +- **Cache volume mounts added** — `~/.cache`, `~/.npm`, and `~/.bun/install/cache` are now backed by Docker named volumes (`codeforge-cache`, `codeforge-npm-cache`, `codeforge-bun-cache`), keeping high-churn package manager caches off the bind mount. +- **Docker Compose migration** — devcontainer now uses `docker-compose.yml` for volume management instead of inline `mounts` in devcontainer.json. Volume names are fixed (no `${devcontainerId}` suffix), simplifying volume management. Existing `${devcontainerId}`-suffixed volumes are orphaned — run `docker volume prune` to clean up, and re-authenticate `gh auth login` on first rebuild. +- **Dynamic volume mounts** — `initializeCommand` runs `generate-mounts.mjs` on the host before container build, reading `.codeforge/mounts.json` to generate a `docker-compose.codeforge.yml` override with project-specific volume mounts for high-churn directories. + +### CLI + +- **`codeforge doctor` command** — new top-level command that checks environment health: GitHub CLI auth, Git user config, workspace filesystem type (detects slow WSL/NTFS mounts), TMPDIR location, cache directory volume backing, and container memory limits. Supports `--format json` and `--no-color`. +- **`codeforge doctor --fix`** — interactive fix mode with TUI multi-select (via @clack/prompts). Groups fixable issues by category (auth, environment, volumes, WSL), pre-selects all fixes, and applies selected fixes. Supports `--yes` for non-interactive apply, `--dry-run` for preview, and `--only ` for filtering. +- **Volume candidate detection** — `codeforge doctor` now scans for high-churn directories (node_modules, .next, .nuxt, .venv, target, .turbo, etc.) on slow filesystems and recommends Docker volume mounts. Fix action writes `.codeforge/mounts.json` for automatic compose integration. +- **WSL .wslconfig generator** — on WSL environments, `codeforge doctor` detects host RAM and generates an optimized `.wslconfig` with memory limits, swap, `autoMemoryReclaim=gradual`, and `sparseVhd=true`. Advisory-only — prints content for manual save to `%USERPROFILE%\.wslconfig`. +- **Windows Defender exclusion guide** — on WSL environments, `codeforge doctor` provides PowerShell commands to exclude Docker and WSL processes from Windows Defender real-time scanning. + ### Terminal - **Alt+Enter newline keybinding** — added `alt+enter` → `chat:newline` to the default Claude Code keybindings. Windows Terminal doesn't support the Kitty keyboard protocol, so Shift+Enter and Ctrl+Enter send identical bytes to plain Enter. Alt+Enter sends ESC+CR, which is universally distinct and works reliably as a newline key. @@ -21,6 +37,7 @@ ### Configuration +- **Claude session retention increased** — default `cleanupPeriodDays` is now `90` across all generated Claude settings profiles. Extended thinking remains enabled by default. - **Dangerous-mode permission prompt skipped by default** — `skipDangerousModePermissionPrompt: true` is now set in `settings.base.json` and propagates to all five generated profiles. Suppresses the one-time bypass-permissions confirmation on new devcontainers. - **Effort level bumped to `max` on opus-4-7** — both opus-4-7 overlays (200k and 1M-400k) now set `effortLevel: "max"` and `CLAUDE_CODE_EFFORT_LEVEL: "max"`. Opus-4-5 and opus-4-6 profiles no longer carry any effort-level setting; they use `MAX_THINKING_TOKENS: 31999` with adaptive thinking disabled (token budgets, not effort levels). `CLAUDE_CODE_EFFORT_LEVEL` was removed from base settings so it no longer leaks into non-4.7 profiles. - **Claude settings profiles** — replaced the single hand-edited default with `settings.base.json` plus model overlays that generate five deployed settings files: opus-4-7 200k default, opus-4-7 1M bounded to 400k, opus-4-6 200k, opus-4-6 1M bounded to 400k, and opus-4-5 200k. @@ -46,6 +63,12 @@ - **Version bumped to latest** — updated from pinned 0.11.1; picks up `--auto-connect` fixes and CDP improvements through v0.26.0 - **Host Chrome CDP documentation overhauled** — corrected Chrome version requirements (136+ needs `--user-data-dir`, 144+ has `chrome://inspect` checkbox), documented `host.docker.internal` for container-to-host networking, fixed flag naming (`--auto-connect` not `--autoConnect`), added Windows PowerShell launch commands +### Claude Code Karma + +- **New default dashboard: `claude-code-karma`** — installs Claude Code Karma from pinned upstream commit `4067d87ee5c85eb7d2877890ba6174115f0bee2e`, starts the dashboard on port `7847`, and starts the API on port `7848`. +- **Live tracking and titles enabled** — CodeForge now registers Karma live-session tracking hooks and the SessionEnd title generator hook in the generated Claude settings profiles. +- **Settings protected** — Karma is patched to keep Claude settings read-only. CodeForge owns `~/.claude/settings.json`; Karma can read settings but cannot write them from its API or Settings UI. + ### Documentation - **New guide: Windows Networking** — comprehensive WSL 2 mirrored networking setup for Windows users; recommended approach for port forwarding, replacing `dbr` on Windows diff --git a/container/.devcontainer/README.md b/container/.devcontainer/README.md old mode 100644 new mode 100755 index d419a91..2370b55 --- a/container/.devcontainer/README.md +++ b/container/.devcontainer/README.md @@ -155,10 +155,12 @@ Expected output shows your authenticated account and token scopes. ### Credential Persistence -GitHub CLI credentials are automatically persisted across container rebuilds. The container is configured to store credentials in `/workspaces/.gh/` (via `GH_CONFIG_DIR`), which is part of the bind-mounted workspace. Claude Code credentials persist via a Docker named volume mounted at `~/.claude/`. +GitHub CLI credentials are persisted across container rebuilds via a Docker named volume mounted at `~/.config/gh/` (via `GH_CONFIG_DIR`). Claude Code credentials persist via a separate Docker named volume mounted at `~/.claude/`. **You only need to authenticate once.** After running `gh auth login` or configuring `.secrets`, your credentials will survive container rebuilds and be available in future sessions. +> **Note:** If upgrading from a previous CodeForge version, you'll need to run `gh auth login` once after your first rebuild. Previous credentials in `/workspaces/.gh/` are not migrated to the new volume. + ## Using Claude Code ### The `cc` Command @@ -251,7 +253,8 @@ curl http://$CDP_HOST:9223/json/version | `ccburn` | Visual token burn rate tracker with pace indicators | | `ccstatusline` | Status bar display (integrated into Claude Code, not standalone CLI) | | `claude-monitor` | Real-time usage tracking | -| `codeforge-dashboard` | Session analytics dashboard — auto-launches on start (port 7847) | +| `karma-status` | Claude Code Karma dashboard status and logs | +| `claude-code-karma` | Session analytics dashboard — auto-launches on start (UI port 7847, API port 7848) | | `ccr` | Claude Code Router — routes API calls to alternate LLM providers (auto-starts on port 3456) | ## Configuration @@ -287,9 +290,12 @@ To add a custom config file, append an entry to `file-manifest.json`: ``` Key defaults: -- **Model**: Claude Opus 4-6 +- **Model**: Claude Opus 4-7 - **Default mode**: Plan (prompts before executing) - **Max output tokens**: 64,000 +- **Session retention**: 90 days + +Claude Code Karma can read these settings in its dashboard, but CodeForge patches Karma so it cannot write `~/.claude/settings.json`. ### Keybindings @@ -321,6 +327,7 @@ CodeForge includes custom devcontainer features. Any feature can be disabled by | `tmux` | Terminal multiplexer with Catppuccin theme for Agent Teams | | `agent-browser` | Headless browser automation for AI agents | | `claude-monitor` | Real-time token usage monitoring with ML predictions | +| `claude-code-karma` | Local Claude Code session analytics dashboard with live tracking and generated titles | | `ccusage` | Usage analytics CLI | | `ccburn` | Visual token burn rate tracker with pace indicators | | `ccstatusline` | Status bar display (integrated into Claude Code, not standalone CLI) | @@ -334,7 +341,6 @@ CodeForge includes custom devcontainer features. Any feature can be disabled by | `hadolint` | Dockerfile linter (disabled by default) | | `dprint` | Pluggable formatter for Markdown/YAML/TOML (disabled by default) | | `ccms` | Claude Code session history search | -| `claude-session-dashboard` | Local session analytics dashboard with web UI | | `codex-cli` | OpenAI Codex CLI terminal coding agent | | `hermes-agent` | Nous Research Hermes Agent CLI (interactive `hermes setup` on first use) | | `notify-hook` | Desktop notifications on Claude completion | @@ -364,7 +370,7 @@ Three methods for providing GitHub/NPM credentials, in order of precedence: 2. **`.secrets` file** — Create `.devcontainer/.secrets` with token values (see template at `.secrets.example`). Auto-configured by `setup-auth.sh` on container start 3. **Interactive login** — Run `gh auth login` for GitHub CLI, then set git identity manually -All methods persist across container rebuilds via the bind-mounted `/workspaces/.gh/` directory. +All methods persist across container rebuilds via a Docker named volume at `~/.config/gh/`. 4. **`.secrets` file with `CLAUDE_AUTH_TOKEN`** — Long-lived Claude auth token from `claude setup-token`. Auto-creates `~/.claude/.credentials.json` on container start. 5. **`.secrets` file with `OPENAI_API_KEY`** — OpenAI API key for Codex CLI. Auto-creates `~/.codex/auth.json` on container start. diff --git a/docs/src/content/docs/customize/container-configuration.md b/docs/src/content/docs/customize/container-configuration.md index 81ab480..090a075 100755 --- a/docs/src/content/docs/customize/container-configuration.md +++ b/docs/src/content/docs/customize/container-configuration.md @@ -15,14 +15,20 @@ Use it when you want to change: - forwarded ports - optional runtimes and tools +CodeForge's default service ports include Claude Code Karma on `7847` and its API on `7848`. + ## Base Image and Resources -Example: +The base image, memory limits, and named volumes are defined in `docker-compose.yml`. The `initializeCommand` generates a compose override from `.codeforge/mounts.json` for project-specific volume mounts. ```json { - "image": "mcr.microsoft.com/devcontainers/python:3.14", - "runArgs": ["--memory=6g", "--memory-swap=12g"], + "dockerComposeFile": [ + "docker-compose.yml", + "docker-compose.codeforge.yml" + ], + "service": "codeforge", + "initializeCommand": "node .devcontainer/scripts/generate-mounts.mjs", "remoteUser": "vscode", "containerUser": "vscode" } @@ -57,6 +63,40 @@ Changing `devcontainer.json` usually requires a rebuild. - use a normal rebuild for routine changes - use a no-cache rebuild when the first build was interrupted or a feature install is corrupted +## Volume Mounts + +CodeForge uses Docker Compose for volume management. Base volumes (config, caches) are defined in `docker-compose.yml`. Project-specific volumes (e.g., `node_modules`, `.next`) are configured via `.codeforge/mounts.json`. + +### Detecting volume candidates + +Run `codeforge doctor` inside the container to scan for high-churn directories on slow filesystems: + +```bash +codeforge doctor --fix +``` + +The fix mode writes `.codeforge/mounts.json` and volumes auto-apply on the next container rebuild. + +### Manual volume configuration + +Add entries to `.codeforge/mounts.json` directly: + +```json +{ + "version": 1, + "volumes": [ + { + "path": "node_modules", + "source": "user", + "signal": "package.json", + "added": "2026-05-05" + } + ] +} +``` + +Rebuild the container to apply. The `initializeCommand` reads this file and generates `docker-compose.codeforge.yml` with the corresponding Docker volumes. + ## What This Page Does Not Cover This page focuses on the container itself. For runtime Claude behavior, use [Settings and Permissions](./settings-and-permissions/). diff --git a/docs/src/content/docs/reference/changelog.md b/docs/src/content/docs/reference/changelog.md index f4dbcc2..145e5e7 100755 --- a/docs/src/content/docs/reference/changelog.md +++ b/docs/src/content/docs/reference/changelog.md @@ -49,6 +49,22 @@ For minor and patch updates, you can usually just rebuild the container. Check t ## Unreleased +### Performance + +- **TMPDIR removed from remoteEnv** — previously set to `/workspaces/.tmp` (bind mount, slow on NTFS/WSL). Now unset, defaulting to the container's `/tmp` (overlay/tmpfs). Scripts using `${TMPDIR:-/tmp}` already handle this correctly. Any user scripts depending on `$TMPDIR` being a persistent location should use an explicit path instead. +- **GH_CONFIG_DIR moved to Docker named volume** — changed from `/workspaces/.gh` (bind mount) to `/home/vscode/.config/gh` backed by a `codeforge-gh-config` named volume. Faster I/O and no gitignore hazard. One-time `gh auth login` required after first rebuild. +- **Cache volume mounts added** — `~/.cache`, `~/.npm`, and `~/.bun/install/cache` are now backed by Docker named volumes (`codeforge-cache`, `codeforge-npm-cache`, `codeforge-bun-cache`), keeping high-churn package manager caches off the bind mount. +- **Docker Compose migration** — devcontainer now uses `docker-compose.yml` for volume management instead of inline `mounts` in devcontainer.json. Volume names are fixed (no `${devcontainerId}` suffix), simplifying volume management. Existing `${devcontainerId}`-suffixed volumes are orphaned — run `docker volume prune` to clean up, and re-authenticate `gh auth login` on first rebuild. +- **Dynamic volume mounts** — `initializeCommand` runs `generate-mounts.mjs` on the host before container build, reading `.codeforge/mounts.json` to generate a `docker-compose.codeforge.yml` override with project-specific volume mounts for high-churn directories. + +### CLI + +- **`codeforge doctor` command** — new top-level command that checks environment health: GitHub CLI auth, Git user config, workspace filesystem type (detects slow WSL/NTFS mounts), TMPDIR location, cache directory volume backing, and container memory limits. Supports `--format json` and `--no-color`. +- **`codeforge doctor --fix`** — interactive fix mode with TUI multi-select (via @clack/prompts). Groups fixable issues by category (auth, environment, volumes, WSL), pre-selects all fixes, and applies selected fixes. Supports `--yes` for non-interactive apply, `--dry-run` for preview, and `--only ` for filtering. +- **Volume candidate detection** — `codeforge doctor` now scans for high-churn directories (node_modules, .next, .nuxt, .venv, target, .turbo, etc.) on slow filesystems and recommends Docker volume mounts. Fix action writes `.codeforge/mounts.json` for automatic compose integration. +- **WSL .wslconfig generator** — on WSL environments, `codeforge doctor` detects host RAM and generates an optimized `.wslconfig` with memory limits, swap, `autoMemoryReclaim=gradual`, and `sparseVhd=true`. Advisory-only — prints content for manual save to `%USERPROFILE%\.wslconfig`. +- **Windows Defender exclusion guide** — on WSL environments, `codeforge doctor` provides PowerShell commands to exclude Docker and WSL processes from Windows Defender real-time scanning. + ### Terminal - **Alt+Enter newline keybinding** — added `alt+enter` → `chat:newline` to the default Claude Code keybindings. Windows Terminal doesn't support the Kitty keyboard protocol, so Shift+Enter and Ctrl+Enter send identical bytes to plain Enter. Alt+Enter sends ESC+CR, which is universally distinct and works reliably as a newline key. @@ -68,6 +84,7 @@ For minor and patch updates, you can usually just rebuild the container. Check t ### Configuration +- **Claude session retention increased** — default `cleanupPeriodDays` is now `90` across all generated Claude settings profiles. Extended thinking remains enabled by default. - **Dangerous-mode permission prompt skipped by default** — `skipDangerousModePermissionPrompt: true` is now set in `settings.base.json` and propagates to all five generated profiles. Suppresses the one-time bypass-permissions confirmation on new devcontainers. - **Effort level bumped to `max` on opus-4-7** — both opus-4-7 overlays (200k and 1M-400k) now set `effortLevel: "max"` and `CLAUDE_CODE_EFFORT_LEVEL: "max"`. Opus-4-5 and opus-4-6 profiles no longer carry any effort-level setting; they use `MAX_THINKING_TOKENS: 31999` with adaptive thinking disabled (token budgets, not effort levels). `CLAUDE_CODE_EFFORT_LEVEL` was removed from base settings so it no longer leaks into non-4.7 profiles. - **Claude settings profiles** — replaced the single hand-edited default with `settings.base.json` plus model overlays that generate five deployed settings files: opus-4-7 200k default, opus-4-7 1M bounded to 400k, opus-4-6 200k, opus-4-6 1M bounded to 400k, and opus-4-5 200k. @@ -93,6 +110,12 @@ For minor and patch updates, you can usually just rebuild the container. Check t - **Version bumped to latest** — updated from pinned 0.11.1; picks up `--auto-connect` fixes and CDP improvements through v0.26.0 - **Host Chrome CDP documentation overhauled** — corrected Chrome version requirements (136+ needs `--user-data-dir`, 144+ has `chrome://inspect` checkbox), documented `host.docker.internal` for container-to-host networking, fixed flag naming (`--auto-connect` not `--autoConnect`), added Windows PowerShell launch commands +### Claude Code Karma + +- **New default dashboard: `claude-code-karma`** — installs Claude Code Karma from pinned upstream commit `4067d87ee5c85eb7d2877890ba6174115f0bee2e`, starts the dashboard on port `7847`, and starts the API on port `7848`. +- **Live tracking and titles enabled** — CodeForge now registers Karma live-session tracking hooks and the SessionEnd title generator hook in the generated Claude settings profiles. +- **Settings protected** — Karma is patched to keep Claude settings read-only. CodeForge owns `~/.claude/settings.json`; Karma can read settings but cannot write them from its API or Settings UI. + ### Documentation - **New guide: Windows Networking** — comprehensive WSL 2 mirrored networking setup for Windows users; recommended approach for port forwarding, replacing `dbr` on Windows diff --git a/docs/src/content/docs/reference/cli-tools.md b/docs/src/content/docs/reference/cli-tools.md index e29f830..f1ac7c1 100755 --- a/docs/src/content/docs/reference/cli-tools.md +++ b/docs/src/content/docs/reference/cli-tools.md @@ -5,7 +5,7 @@ sidebar: order: 3 --- -CodeForge includes 25 core CLI tools and utilities in the default inventory. +CodeForge includes 26 core CLI tools and utilities in the default inventory. This page also includes closely related runtimes, package managers, and language servers so you can see the full command-line surface in one place. @@ -126,6 +126,24 @@ Real-time terminal-based Claude session monitor. Shows active sessions and their claude-monitor ``` +### claude-code-karma / karma-status + +Claude Code Karma is the default local web dashboard for Claude Code session history, live sessions, analytics, hooks, plugins, tools, and generated session titles. CodeForge starts the dashboard automatically. + +```bash +# Show dashboard process status and logs +karma-status + +# Dashboard and API defaults +curl http://localhost:7848/health +``` + +Open the dashboard at `http://localhost:7847`. The API runs on `http://localhost:7848`. + +:::caution[Settings are read-only] +CodeForge owns `~/.claude/settings.json`. Karma is patched in CodeForge so its Settings page can read settings but cannot modify them. +::: + ### agent-browser A headless Chromium browser (Playwright-based) available for agents that need to inspect web content or take screenshots. @@ -318,6 +336,8 @@ The table below is broader than the canonical 25-tool inventory because it also | 12 | `cc-tools` | Session | List all available tools | | 13 | `codeforge` | Session | CodeForge CLI — session search, plugins, indexing _(experimental)_ | | 14 | `codeforge proxy` | Session | Launch Claude Code through mitmproxy for API traffic inspection | +| 14a | `codeforge doctor` | Session | Environment health check — WSL filesystem, auth, caches, memory, volumes | +| 14b | `codeforge doctor --fix` | Session | Interactive fix mode — TUI multi-select to apply fixes for detected issues | | 15 | `codex` | Session | OpenAI Codex CLI terminal coding agent | | 16 | `ccusage-codex` | Session | Codex token usage statistics | | 17 | `hermes` | Session | Nous Research Hermes Agent CLI (run `hermes setup` first) | From 8d618a3cd0dbb551cf67ce8a77c589e89960ae1d Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:34:51 +0000 Subject: [PATCH 06/27] ci: add npm caching and expand CodeQL to staging branch --- .github/workflows/ci.yml | 6 ++++++ .github/workflows/codeql.yml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d682e8d..944756e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,8 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 18 + cache: npm + cache-dependency-path: container/package-lock.json - run: npm test working-directory: container @@ -26,6 +28,10 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 18 + cache: npm + cache-dependency-path: container/package-lock.json + - run: npm ci + working-directory: container - run: npx @biomejs/biome check setup.js test.js working-directory: container diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2bf94d7..b2f054f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,9 @@ name: "CodeQL" on: push: - branches: [main] + branches: [main, staging] pull_request: - branches: [main] + branches: [main, staging] schedule: - cron: "0 6 * * 1" From e83996542c30f82b009878f47d8ab01d749d4eb3 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:34:59 +0000 Subject: [PATCH 07/27] chore: update contributing guidelines and project configuration --- .gitignore | 13 +++++++++++-- CONTRIBUTING.md | 6 +++--- pyproject.toml | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) mode change 100644 => 100755 .gitignore diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 9753a7a..6c040da --- a/.gitignore +++ b/.gitignore @@ -46,8 +46,8 @@ container/.claude/ container/.tmp/ container/.codeforge/.checksums/ container/.codeforge/.markers/ -container/.devcontainer/.env -container/.devcontainer/.secrets +container/.codeforge/secrets/ +container/.devcontainer/.generated/ container/.devcontainer/state/ container/.devcontainer/config/backups/ container/.devcontainer/.dockercache/ @@ -64,11 +64,20 @@ container/.devcontainer/.codeforge-preserve # CLI-specific cli/.pytest_cache/ cli/.ruff_cache/ +.codeforge/.checksums/ +.codeforge/.markers/ .codeforge/data/ +.codeforge/secrets/ # Dashboard (deprecated — migrating to CodeDirective, a separate repo) dashboard/ +# Abandoned packages +proxy/ + +# Stale analysis artifacts +DELETEME/ + # Docs-specific docs/.astro/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2660764..3d7baac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,9 @@ guidelines for contributing to the project. ## How to Contribute 1. **Fork** the repository -2. **Create a branch** from `main` for your change +2. **Create a branch** from `staging` for your change 3. **Make your changes** following the guidelines below -4. **Submit a pull request** using the PR template +4. **Submit a pull request** to `staging` using the PR template ## Development Setup @@ -25,7 +25,7 @@ To test the devcontainer itself, open the project in VS Code and select - Fill out the [pull request template](.github/pull_request_template.md) - Ensure `npm test` passes -- Add an entry to `.devcontainer/CHANGELOG.md` describing your change +- Add an entry to the appropriate changelog: `.devcontainer/CHANGELOG.md` for container changes, `cli/CHANGELOG.md` for CLI changes - Update documentation if your change affects user-facing behavior - PRs require one approving review before merge diff --git a/pyproject.toml b/pyproject.toml index 794bca9..3010d68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,3 @@ +# Used for running container plugin tests via pytest. See container/CLAUDE.md. [tool.pytest.ini_options] testpaths = ["container/tests"] From c57b27d319e0d60976246eb8df85002de3d2e445 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:35:37 +0000 Subject: [PATCH 08/27] refactor(container): replace flat config defaults with three-tier layout Move packaged defaults from defaults/codeforge/config/ into organized subdirectories (claude/, codex/, rtk/) with a new container.json for setup identity. Settings profiles are now minimal overlays instead of full copies. Remove legacy .env.example and .secrets.example files. --- container/.devcontainer/.env.example | 46 ---- container/.devcontainer/.gitignore | 10 +- container/.devcontainer/.secrets.example | 40 --- .../defaults/codeforge/README.md | 13 + .../defaults/codeforge/claude/claude.json | 4 + .../{config => claude}/disabled-hooks.json | 5 +- .../codeforge/claude/hooks/rtk-rewrite.sh | 93 +++++++ .../{config => claude}/keybindings.json | 0 .../router/config.json} | 0 .../{config => claude}/rules/auto-memory.md | 0 .../rules/explicit-start.md | 0 .../rules/plan-presentation.md | 0 .../codeforge/claude/rules/rtk-awareness.md | 25 ++ .../rules/scope-discipline.md | 0 .../rules/session-search.md | 0 .../{config => claude}/rules/spec-workflow.md | 0 .../rules/surface-decisions.md | 0 .../rules/workspace-scope.md | 0 .../rules/zero-tolerance-bugs.md | 0 .../settings/base.json} | 75 +++++- .../settings/profiles/opus-45-200k.json | 3 + .../settings/profiles/opus-46-1m-400k.json | 3 + .../settings/profiles/opus-46-200k.json | 3 + .../settings/profiles/opus-47-1m-400k.json | 7 + .../settings/profiles/opus-47-200k.json | 7 + .../statusline/settings.json} | 30 ++- .../system-prompts/main.md} | 0 .../system-prompts/orchestrator.md} | 0 .../system-prompts/writing.md} | 0 .../defaults/codeforge/codex/AGENTS.md | 37 +++ .../codex-config.toml => codex/config.toml} | 0 .../config/settings-opus-46-1m-400k.json | 229 ------------------ .../config/settings-opus-46-200k.json | 229 ------------------ .../config/settings-opus-47-1m-400k.json | 229 ------------------ .../settings-profiles/opus-45-200k.json | 12 - .../settings-profiles/opus-46-1m-400k.json | 12 - .../settings-profiles/opus-46-200k.json | 12 - .../settings-profiles/opus-47-1m-400k.json | 12 - .../settings-profiles/opus-47-200k.json | 12 - .../codeforge/config/settings.base.json | 220 ----------------- .../defaults/codeforge/config/settings.json | 229 ------------------ .../defaults/codeforge/container.json | 24 ++ .../defaults/codeforge/file-manifest.json | 170 +++++++++---- .../defaults/codeforge/rtk/config.toml | 14 ++ container/.gitignore | 1 + container/.npmignore | 5 +- 46 files changed, 458 insertions(+), 1353 deletions(-) delete mode 100644 container/.devcontainer/.env.example mode change 100644 => 100755 container/.devcontainer/.gitignore delete mode 100644 container/.devcontainer/.secrets.example create mode 100755 container/.devcontainer/defaults/codeforge/README.md create mode 100755 container/.devcontainer/defaults/codeforge/claude/claude.json rename container/.devcontainer/defaults/codeforge/{config => claude}/disabled-hooks.json (56%) create mode 100755 container/.devcontainer/defaults/codeforge/claude/hooks/rtk-rewrite.sh rename container/.devcontainer/defaults/codeforge/{config => claude}/keybindings.json (100%) rename container/.devcontainer/defaults/codeforge/{config/claude-code-router.json => claude/router/config.json} (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/auto-memory.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/explicit-start.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/plan-presentation.md (100%) create mode 100644 container/.devcontainer/defaults/codeforge/claude/rules/rtk-awareness.md rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/scope-discipline.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/session-search.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/spec-workflow.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/surface-decisions.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/workspace-scope.md (100%) rename container/.devcontainer/defaults/codeforge/{config => claude}/rules/zero-tolerance-bugs.md (100%) rename container/.devcontainer/defaults/codeforge/{config/settings-opus-45-200k.json => claude/settings/base.json} (82%) create mode 100755 container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-45-200k.json create mode 100755 container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-1m-400k.json create mode 100755 container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-200k.json create mode 100755 container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-1m-400k.json create mode 100755 container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-200k.json rename container/.devcontainer/defaults/codeforge/{config/ccstatusline-settings.json => claude/statusline/settings.json} (87%) rename container/.devcontainer/defaults/codeforge/{config/main-system-prompt.md => claude/system-prompts/main.md} (100%) rename container/.devcontainer/defaults/codeforge/{config/orchestrator-system-prompt.md => claude/system-prompts/orchestrator.md} (100%) rename container/.devcontainer/defaults/codeforge/{config/writing-system-prompt.md => claude/system-prompts/writing.md} (100%) create mode 100644 container/.devcontainer/defaults/codeforge/codex/AGENTS.md rename container/.devcontainer/defaults/codeforge/{config/codex-config.toml => codex/config.toml} (100%) delete mode 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json delete mode 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json delete mode 100755 container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json delete mode 100644 container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-45-200k.json delete mode 100644 container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-1m-400k.json delete mode 100644 container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-200k.json delete mode 100644 container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-1m-400k.json delete mode 100644 container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-200k.json delete mode 100755 container/.devcontainer/defaults/codeforge/config/settings.base.json delete mode 100755 container/.devcontainer/defaults/codeforge/config/settings.json create mode 100755 container/.devcontainer/defaults/codeforge/container.json mode change 100644 => 100755 container/.devcontainer/defaults/codeforge/file-manifest.json create mode 100644 container/.devcontainer/defaults/codeforge/rtk/config.toml diff --git a/container/.devcontainer/.env.example b/container/.devcontainer/.env.example deleted file mode 100644 index 5ff23a2..0000000 --- a/container/.devcontainer/.env.example +++ /dev/null @@ -1,46 +0,0 @@ -# CodeForge Environment Configuration -# Copy to .env and customize. .env is gitignored. - -# Paths (defaults shown — uncomment to override) -# CLAUDE_CONFIG_DIR=$HOME/.claude -# CODEFORGE_DIR=/workspaces/.codeforge -# CONFIG_SOURCE_DIR is deprecated — use CODEFORGE_DIR instead - -# Setup: copy config files to CLAUDE_CONFIG_DIR (per .codeforge/file-manifest.json) -SETUP_CONFIG=true - -# Setup: add cc/claude/ccraw aliases to shell rc files -SETUP_ALIASES=true - -# Setup: configure Git and NPM auth from .secrets file -SETUP_AUTH=true - -# Setup: install official Anthropic plugins + register local marketplace -SETUP_PLUGINS=true - -# Setup: auto-update Claude Code CLI to latest on container start (runs in background) -SETUP_UPDATE_CLAUDE=true - -# Setup: auto-detect projects for VS Code Project Manager extension -# Setup: configure VS Code Shift+Enter keybinding for Claude Code terminal -SETUP_TERMINAL=true - -# Setup: run post-start hooks from /usr/local/devcontainer-poststart.d/ -SETUP_POSTSTART=true - -SETUP_PROJECTS=true - -# Version lock: set to a specific semver (e.g., 2.1.80) to pin Claude Code to that version. -# When set, the update script installs this exact version instead of updating to latest. -# Unset or empty = update to latest as normal. -# CLAUDE_VERSION_LOCK=2.1.80 - -# Plugin blacklist: comma-separated plugin names to skip during installation -# Example: PLUGIN_BLACKLIST="ticket-workflow,auto-linter" -PLUGIN_BLACKLIST="" - -# Project detection: directories to exclude from auto-detection (space-separated) -# PROJECT_EXCLUDE_DIRS=".claude .gh .tmp .devcontainer .config node_modules .git" - -# Official plugins: space-separated list installed from Anthropic/third-party marketplaces -# OFFICIAL_PLUGINS="frontend-design@claude-plugins-official svelte@sveltejs/mcp" diff --git a/container/.devcontainer/.gitignore b/container/.devcontainer/.gitignore old mode 100644 new mode 100755 index 8c4a32e..ab935de --- a/container/.devcontainer/.gitignore +++ b/container/.devcontainer/.gitignore @@ -1,12 +1,10 @@ -# Explicitly ignored (safety net on top of root .* rule) +# Explicitly ignored .review -.secrets .build-cache/ +.generated/ -# Un-ignore dotfiles that should be tracked (overrides root .* pattern) -!.env.example -!.secrets.example +# Un-ignore dotfiles that should be tracked !.gitignore # Generated by initializeCommand (codeforge doctor --fix) -docker-compose.codeforge.yml \ No newline at end of file +docker-compose.codeforge.yml diff --git a/container/.devcontainer/.secrets.example b/container/.devcontainer/.secrets.example deleted file mode 100644 index ec2c108..0000000 --- a/container/.devcontainer/.secrets.example +++ /dev/null @@ -1,40 +0,0 @@ -# Copy this file to .secrets and fill in your tokens. -# .secrets is gitignored and will never be committed. - -# GitHub Personal Access Token (classic or fine-grained) -GH_TOKEN= -# GitHub username for git config -GH_USERNAME= -# GitHub email for git config (use noreply for privacy) -GH_EMAIL= - -# NPM auth token for registry.npmjs.org -NPM_TOKEN= - -# Claude long-lived auth token (from 'claude setup-token') -CLAUDE_AUTH_TOKEN= - -# OpenAI API key for Codex CLI -OPENAI_API_KEY= - -# Claude Code Router — provider API keys (set the ones you use) -# See https://github.com/musistudio/claude-code-router for provider docs -ANTHROPIC_API_KEY= -DEEPSEEK_API_KEY= -GEMINI_API_KEY= -OPENROUTER_API_KEY= - -# oh-my-claude — Chinese LLM provider API keys -KIMI_API_KEY= -ZHIPU_API_KEY= -ZAI_API_KEY= -ALIYUN_API_KEY= -MINIMAX_API_KEY= -MINIMAX_CN_API_KEY= - -# oh-my-claude — optional local/embedding providers -OLLAMA_HOST= -EMBEDDING_API_BASE= -EMBEDDING_MODEL= -EMBEDDING_API_KEY= -EMBEDDING_DIMENSIONS= diff --git a/container/.devcontainer/defaults/codeforge/README.md b/container/.devcontainer/defaults/codeforge/README.md new file mode 100755 index 0000000..cfc447b --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/README.md @@ -0,0 +1,13 @@ +# CodeForge Packaged Defaults + +This directory contains shipped defaults. Do not copy it wholesale into +`.codeforge/`. + +Project overrides use the same logical paths under `.codeforge/`. For example, +override `claude/system-prompts/main.md` by creating +`.codeforge/claude/system-prompts/main.md`. + +Claude settings are generated from `claude/settings/base.json` plus one of the +profile overlays in `claude/settings/profiles/`. Generated files are written to +`.devcontainer/.generated/codeforge/claude/settings/` and deployed to +`~/.claude/settings*.json`. diff --git a/container/.devcontainer/defaults/codeforge/claude/claude.json b/container/.devcontainer/defaults/codeforge/claude/claude.json new file mode 100755 index 0000000..2a70a45 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/claude.json @@ -0,0 +1,4 @@ +{ + "hasCompletedOnboarding": true, + "bypassPermissionsModeAccepted": true +} diff --git a/container/.devcontainer/defaults/codeforge/config/disabled-hooks.json b/container/.devcontainer/defaults/codeforge/claude/disabled-hooks.json similarity index 56% rename from container/.devcontainer/defaults/codeforge/config/disabled-hooks.json rename to container/.devcontainer/defaults/codeforge/claude/disabled-hooks.json index 824a20b..866f212 100644 --- a/container/.devcontainer/defaults/codeforge/config/disabled-hooks.json +++ b/container/.devcontainer/defaults/codeforge/claude/disabled-hooks.json @@ -4,9 +4,6 @@ "ticket-linker", "spec-reminder", "commit-reminder", - "memory-awareness", - "preference-awareness", - "context-memory", - "post-tool" + "preference-awareness" ] } diff --git a/container/.devcontainer/defaults/codeforge/claude/hooks/rtk-rewrite.sh b/container/.devcontainer/defaults/codeforge/claude/hooks/rtk-rewrite.sh new file mode 100755 index 0000000..5790be7 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/hooks/rtk-rewrite.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# RTK PreToolUse hook for Claude Code +# Rewrites supported Bash commands to use RTK compression proxy. +# +# Behavior: +# - Intercepts Bash tool calls +# - Rewrites supported commands by prepending "rtk" +# - By default, does NOT auto-allow (lets normal permission flow handle it) +# - Set RTK_AUTO_ALLOW=1 to restore upstream auto-allow behavior +# +# Requires: jq + +set -euo pipefail + +# Read hook input from stdin +INPUT=$(cat) + +# Extract tool name and command +TOOL_NAME=$(echo "${INPUT}" | jq -r '.tool_name // empty') +COMMAND=$(echo "${INPUT}" | jq -r '.tool_input.command // empty') + +# Only process Bash tool calls +if [ "${TOOL_NAME}" != "Bash" ] || [ -z "${COMMAND}" ]; then + echo '{}' + exit 0 +fi + +# Skip if rtk is not installed +if ! command -v rtk >/dev/null 2>&1; then + echo '{}' + exit 0 +fi + +# Skip if command already uses rtk +if echo "${COMMAND}" | grep -qE '^\s*rtk\s'; then + echo '{}' + exit 0 +fi + +# RTK-supported command prefixes +# Reference: https://github.com/rtk-ai/rtk#supported-commands +RTK_COMMANDS=( + git npm npx yarn pnpm bun bunx cargo rustup pip pip3 uv uvx + python python3 node deno go docker kubectl helm terraform + aws gcloud az make cmake gradle mvn ant + cat ls find grep rg fd tree file wc du df + curl wget http httpie + jq yq sed awk cut sort uniq head tail + ps top htop free uptime who w id env printenv + ping traceroute dig nslookup host ss netstat + tar zip unzip gzip gunzip + diff patch + pytest jest vitest mocha + eslint prettier biome ruff mypy pyright tsc + gh +) + +# Extract the base command (first word, ignoring env vars and leading whitespace) +BASE_CMD=$(echo "${COMMAND}" | sed -E 's/^[[:space:]]*//' | sed -E 's/^([A-Z_]+=[^ ]+ )*//' | awk '{print $1}') + +# Check if it's a supported command +SUPPORTED=false +for cmd in "${RTK_COMMANDS[@]}"; do + if [ "${BASE_CMD}" = "${cmd}" ]; then + SUPPORTED=true + break + fi +done + +if [ "${SUPPORTED}" != "true" ]; then + echo '{}' + exit 0 +fi + +# Rewrite the command by prepending rtk +REWRITTEN="rtk ${COMMAND}" + +# Build response +if [ "${RTK_AUTO_ALLOW:-0}" = "1" ]; then + # Auto-allow mode: skip permission prompt for rewritten commands + jq -n \ + --arg cmd "${REWRITTEN}" \ + '{ + "updatedInput": {"command": $cmd}, + "permissionDecision": "allow", + "permissionDecisionReason": "RTK auto-rewrite (RTK_AUTO_ALLOW=1)" + }' +else + # Default: rewrite only, let normal permission flow handle approval + jq -n \ + --arg cmd "${REWRITTEN}" \ + '{"updatedInput": {"command": $cmd}}' +fi diff --git a/container/.devcontainer/defaults/codeforge/config/keybindings.json b/container/.devcontainer/defaults/codeforge/claude/keybindings.json similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/keybindings.json rename to container/.devcontainer/defaults/codeforge/claude/keybindings.json diff --git a/container/.devcontainer/defaults/codeforge/config/claude-code-router.json b/container/.devcontainer/defaults/codeforge/claude/router/config.json similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/claude-code-router.json rename to container/.devcontainer/defaults/codeforge/claude/router/config.json diff --git a/container/.devcontainer/defaults/codeforge/config/rules/auto-memory.md b/container/.devcontainer/defaults/codeforge/claude/rules/auto-memory.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/auto-memory.md rename to container/.devcontainer/defaults/codeforge/claude/rules/auto-memory.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/explicit-start.md b/container/.devcontainer/defaults/codeforge/claude/rules/explicit-start.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/explicit-start.md rename to container/.devcontainer/defaults/codeforge/claude/rules/explicit-start.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/plan-presentation.md b/container/.devcontainer/defaults/codeforge/claude/rules/plan-presentation.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/plan-presentation.md rename to container/.devcontainer/defaults/codeforge/claude/rules/plan-presentation.md diff --git a/container/.devcontainer/defaults/codeforge/claude/rules/rtk-awareness.md b/container/.devcontainer/defaults/codeforge/claude/rules/rtk-awareness.md new file mode 100644 index 0000000..de2c507 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/rules/rtk-awareness.md @@ -0,0 +1,25 @@ +# RTK (Rust Token Killer) + +RTK is a transparent CLI proxy that compresses command output before it reaches your context window. It is active in this environment — Bash commands are automatically rewritten via a PreToolUse hook. + +## What You Need to Know + +- **You don't need to prefix commands with `rtk`** — the hook does this automatically +- Output you receive from Bash is already compressed (60-90% token savings) +- The original semantics are preserved; only verbose/redundant output is stripped + +## Meta-Commands + +These RTK-specific commands provide insight into compression behavior: + +| Command | Purpose | +|---------|---------| +| `rtk gain` | Show token savings statistics for the current session | +| `rtk discover` | List all commands RTK can compress | +| `rtk status` | Show RTK version and configuration | +| `rtk telemetry status` | Verify telemetry is disabled | + +## Important + +- Do NOT confuse `rtk` (Rust Token Killer, rtk-ai/rtk) with the unrelated Rust Type Kit package +- If you need raw uncompressed output, use `command ` instead of `` — the hook only rewrites the base command form diff --git a/container/.devcontainer/defaults/codeforge/config/rules/scope-discipline.md b/container/.devcontainer/defaults/codeforge/claude/rules/scope-discipline.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/scope-discipline.md rename to container/.devcontainer/defaults/codeforge/claude/rules/scope-discipline.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/session-search.md b/container/.devcontainer/defaults/codeforge/claude/rules/session-search.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/session-search.md rename to container/.devcontainer/defaults/codeforge/claude/rules/session-search.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/spec-workflow.md b/container/.devcontainer/defaults/codeforge/claude/rules/spec-workflow.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/spec-workflow.md rename to container/.devcontainer/defaults/codeforge/claude/rules/spec-workflow.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/surface-decisions.md b/container/.devcontainer/defaults/codeforge/claude/rules/surface-decisions.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/surface-decisions.md rename to container/.devcontainer/defaults/codeforge/claude/rules/surface-decisions.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/workspace-scope.md b/container/.devcontainer/defaults/codeforge/claude/rules/workspace-scope.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/workspace-scope.md rename to container/.devcontainer/defaults/codeforge/claude/rules/workspace-scope.md diff --git a/container/.devcontainer/defaults/codeforge/config/rules/zero-tolerance-bugs.md b/container/.devcontainer/defaults/codeforge/claude/rules/zero-tolerance-bugs.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/rules/zero-tolerance-bugs.md rename to container/.devcontainer/defaults/codeforge/claude/rules/zero-tolerance-bugs.md diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json b/container/.devcontainer/defaults/codeforge/claude/settings/base.json similarity index 82% rename from container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json rename to container/.devcontainer/defaults/codeforge/claude/settings/base.json index 14c64cb..960baf7 100755 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-45-200k.json +++ b/container/.devcontainer/defaults/codeforge/claude/settings/base.json @@ -16,6 +16,7 @@ "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", "FORCE_AUTOUPDATE_PLUGINS": "1", "CLAUDE_CODE_SCROLL_SPEED": "3", + "ENABLE_TOOL_SEARCH": "auto:5", "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", "CLAUDE_CODE_ENABLE_TASKS": "1", @@ -29,15 +30,13 @@ "CLAUDE_AUTO_BACKGROUND_TASKS": "1", "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", "CLAUDE_CODE_NO_FLICKER": "1", + "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", "CLAUDE_CODE_MAX_RETRIES": "1", "BASH_MAX_OUTPUT_LENGTH": "15000", "TASK_MAX_OUTPUT_LENGTH": "64000", "DISABLE_AUTOUPDATER": "1", - "ANTHROPIC_MODEL": "claude-opus-4-5", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-5", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", + "MAX_THINKING_TOKENS": "31999", "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" }, @@ -90,10 +89,7 @@ ] }, "permissions": { - "allow": [ - "Read(/workspaces/*)", - "WebFetch(domain:*)" - ], + "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], "deny": [], "ask": [], "defaultMode": "plan", @@ -111,6 +107,15 @@ "timeout": 5 } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "claude-mem-hook context", + "timeout": 15 + } + ] } ], "UserPromptSubmit": [ @@ -122,6 +127,37 @@ "timeout": 5 } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "claude-mem-hook session-init", + "timeout": 10 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Read", + "hooks": [ + { + "type": "command", + "command": "claude-mem-hook file-context", + "timeout": 10 + } + ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash $HOME/.claude/hooks/rtk-rewrite.sh", + "timeout": 5 + } + ] } ], "PostToolUse": [ @@ -133,6 +169,16 @@ "timeout": 5 } ] + }, + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "claude-mem-hook observation", + "timeout": 15 + } + ] } ], "Notification": [ @@ -155,6 +201,15 @@ "timeout": 5 } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "claude-mem-hook summarize", + "timeout": 30 + } + ] } ], "SubagentStart": [ @@ -223,7 +278,5 @@ "prompt-snippets@devs-marketplace": false, "git-workflow@devs-marketplace": false }, - "autoUpdatesChannel": "latest", - "model": "claude-opus-4-5", - "autoCompactWindow": 200000 + "autoUpdatesChannel": "latest" } diff --git a/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-45-200k.json b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-45-200k.json new file mode 100755 index 0000000..56e8c90 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-45-200k.json @@ -0,0 +1,3 @@ +{ + "_meta": { "model": "claude-opus-4-5", "contextWindow": 200000 } +} diff --git a/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-1m-400k.json b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-1m-400k.json new file mode 100755 index 0000000..b3018be --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-1m-400k.json @@ -0,0 +1,3 @@ +{ + "_meta": { "model": "claude-opus-4-6[1m]", "contextWindow": 400000 } +} diff --git a/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-200k.json b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-200k.json new file mode 100755 index 0000000..9caf925 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-200k.json @@ -0,0 +1,3 @@ +{ + "_meta": { "model": "claude-opus-4-6", "contextWindow": 200000 } +} diff --git a/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-1m-400k.json b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-1m-400k.json new file mode 100755 index 0000000..5199bea --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-1m-400k.json @@ -0,0 +1,7 @@ +{ + "_meta": { "model": "claude-opus-4-7[1m]", "contextWindow": 400000 }, + "effortLevel": "max", + "env": { + "CLAUDE_CODE_EFFORT_LEVEL": "max" + } +} diff --git a/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-200k.json b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-200k.json new file mode 100755 index 0000000..e37ce92 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-200k.json @@ -0,0 +1,7 @@ +{ + "_meta": { "model": "claude-opus-4-7", "contextWindow": 200000 }, + "effortLevel": "max", + "env": { + "CLAUDE_CODE_EFFORT_LEVEL": "max" + } +} diff --git a/container/.devcontainer/defaults/codeforge/config/ccstatusline-settings.json b/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json similarity index 87% rename from container/.devcontainer/defaults/codeforge/config/ccstatusline-settings.json rename to container/.devcontainer/defaults/codeforge/claude/statusline/settings.json index 528e4f5..47d8f60 100644 --- a/container/.devcontainer/defaults/codeforge/config/ccstatusline-settings.json +++ b/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json @@ -153,16 +153,38 @@ { "id": "session-usage", "type": "session-usage", + "rawValue": true, "color": "brightWhite", - "backgroundColor": "bgBlue", - "rawValue": true + "backgroundColor": "bgBlue" + }, + { + "id": "session-reset", + "type": "reset-timer", + "rawValue": true, + "metadata": { + "absolute": "true", + "compact": "true" + }, + "color": "black", + "backgroundColor": "bgYellow" }, { "id": "weekly-usage", "type": "weekly-usage", + "rawValue": true, "color": "brightWhite", - "backgroundColor": "bgMagenta", - "rawValue": true + "backgroundColor": "bgMagenta" + }, + { + "id": "weekly-reset", + "type": "weekly-reset-timer", + "rawValue": true, + "metadata": { + "absolute": "true", + "compact": "true" + }, + "color": "brightWhite", + "backgroundColor": "bgBrightBlack" } ] ], diff --git a/container/.devcontainer/defaults/codeforge/config/main-system-prompt.md b/container/.devcontainer/defaults/codeforge/claude/system-prompts/main.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/main-system-prompt.md rename to container/.devcontainer/defaults/codeforge/claude/system-prompts/main.md diff --git a/container/.devcontainer/defaults/codeforge/config/orchestrator-system-prompt.md b/container/.devcontainer/defaults/codeforge/claude/system-prompts/orchestrator.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/orchestrator-system-prompt.md rename to container/.devcontainer/defaults/codeforge/claude/system-prompts/orchestrator.md diff --git a/container/.devcontainer/defaults/codeforge/config/writing-system-prompt.md b/container/.devcontainer/defaults/codeforge/claude/system-prompts/writing.md similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/writing-system-prompt.md rename to container/.devcontainer/defaults/codeforge/claude/system-prompts/writing.md diff --git a/container/.devcontainer/defaults/codeforge/codex/AGENTS.md b/container/.devcontainer/defaults/codeforge/codex/AGENTS.md new file mode 100644 index 0000000..1a523b5 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/codex/AGENTS.md @@ -0,0 +1,37 @@ +# RTK — Token Compression + +RTK (`rtk`) is installed in this environment. It compresses command output to save context window tokens. + +## Instruction + +**Always prefix shell commands with `rtk` when running supported commands.** + +Examples: +- `rtk git status` instead of `git status` +- `rtk npm test` instead of `npm test` +- `rtk cargo build` instead of `cargo build` +- `rtk ls -la` instead of `ls -la` + +## Supported Commands + +git, npm, npx, yarn, pnpm, bun, bunx, cargo, rustup, pip, uv, uvx, +python, node, deno, go, docker, kubectl, helm, terraform, +aws, gcloud, az, make, cmake, gradle, mvn, +cat, ls, find, grep, rg, fd, tree, file, wc, du, df, +curl, wget, jq, yq, sed, awk, cut, sort, uniq, head, tail, +ps, top, free, uptime, env, printenv, +tar, zip, unzip, diff, patch, +pytest, jest, vitest, mocha, +eslint, prettier, biome, ruff, mypy, pyright, tsc, gh + +## When NOT to Use RTK + +- Interactive commands (vim, nano, less) +- Commands where you need exact byte-level output +- Piped commands where RTK would interfere with downstream parsing + +## Meta-Commands + +- `rtk gain` — show token savings +- `rtk discover` — list supported commands +- `rtk status` — version and config diff --git a/container/.devcontainer/defaults/codeforge/config/codex-config.toml b/container/.devcontainer/defaults/codeforge/codex/config.toml similarity index 100% rename from container/.devcontainer/defaults/codeforge/config/codex-config.toml rename to container/.devcontainer/defaults/codeforge/codex/config.toml diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json deleted file mode 100755 index 5530618..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "cleanupPeriodDays": 90, - "autoCompact": true, - "alwaysThinkingEnabled": true, - "skipDangerousModePermissionPrompt": true, - "disableAutoMode": "disable", - "env": { - "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - "BASH_DEFAULT_TIMEOUT_MS": "120000", - "BASH_MAX_TIMEOUT_MS": "300000", - "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", - "MAX_MCP_OUTPUT_TOKENS": "10000", - "MCP_TIMEOUT": "120000", - "MCP_TOOL_TIMEOUT": "30000", - "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", - "FORCE_AUTOUPDATE_PLUGINS": "1", - "CLAUDE_CODE_SCROLL_SPEED": "3", - "ENABLE_TOOL_SEARCH": "auto:5", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", - "CLAUDE_CODE_ENABLE_TASKS": "1", - "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0", - "ENABLE_CLAUDE_CODE_SM_COMPACT": "1", - "CLAUDE_CODE_FORCE_GLOBAL_CACHE": "1", - "CLAUDE_CODE_PLAN_MODE_INTERVIEW_PHASE": "true", - "CLAUDE_CODE_PLAN_V2_AGENT_COUNT": "5", - "CLAUDE_CODE_PLAN_MODE_REQUIRED": "true", - "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "true", - "CLAUDE_AUTO_BACKGROUND_TASKS": "1", - "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", - "CLAUDE_CODE_NO_FLICKER": "1", - "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", - "CLAUDE_CODE_MAX_RETRIES": "1", - "BASH_MAX_OUTPUT_LENGTH": "15000", - "TASK_MAX_OUTPUT_LENGTH": "64000", - "DISABLE_AUTOUPDATER": "1", - "ANTHROPIC_MODEL": "claude-opus-4-6[1m]", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-6[1m]", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "400000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", - "MAX_THINKING_TOKENS": "31999", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" - }, - "teammateMode": "auto", - "showClearContextOnPlanAccept": true, - "showThinkingSummaries": true, - "attribution": { - "commit": "", - "pr": "" - }, - "autoMemoryDirectory": "./.claude/memory", - "plansDirectory": "./.claude/plans", - "viewMode": "focus", - "spinnerVerbs": { - "mode": "replace", - "verbs": [ - "Butchering", - "Mangling", - "Wrecking", - "Botching", - "Misreading", - "Derailing", - "Overcomplicating", - "Hallucinating", - "Breaking", - "Fumbling", - "Sabotaging", - "Shredding", - "Confusing", - "Corrupting", - "Ruining", - "Winging", - "Guessing", - "Misinterpreting", - "Overengineering", - "Improvising Poorly", - "Making It Worse", - "Massacring", - "Mutilating", - "Annihilating", - "Trashing", - "Destroying", - "Misfiring", - "Ignoring", - "Unthinking", - "Wondering", - "Draining", - "Exhausting", - "Petering Out" - ] - }, - "permissions": { - "allow": [ - "Read(/workspaces/*)", - "WebFetch(domain:*)" - ], - "deny": [], - "ask": [], - "defaultMode": "plan", - "additionalDirectories": [] - }, - "enabledMcpjsonServers": [], - "disabledMcpjsonServers": [], - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "karma-title-generator", - "timeout": 15 - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/usr/local/bin/ccstatusline-wrapper" - }, - "enabledPlugins": { - "frontend-design@anthropics/claude-code": true, - "code-review@anthropics/claude-code": true, - "feature-dev@anthropics/claude-code": true, - "pr-review-toolkit@anthropics/claude-code": true, - "codeforge-lsp@devs-marketplace": false, - "ticket-workflow@devs-marketplace": false, - "notify-hook@devs-marketplace": false, - "dangerous-command-blocker@devs-marketplace": false, - "protected-files-guard@devs-marketplace": true, - "agent-system@devs-marketplace": true, - "skill-engine@devs-marketplace": false, - "spec-workflow@devs-marketplace": false, - "session-context@devs-marketplace": false, - "auto-code-quality@devs-marketplace": true, - "workspace-scope-guard@devs-marketplace": true, - "prompt-snippets@devs-marketplace": false, - "git-workflow@devs-marketplace": false - }, - "autoUpdatesChannel": "latest", - "model": "claude-opus-4-6[1m]", - "autoCompactWindow": 400000 -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json deleted file mode 100755 index 4ddb363..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-46-200k.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "cleanupPeriodDays": 90, - "autoCompact": true, - "alwaysThinkingEnabled": true, - "skipDangerousModePermissionPrompt": true, - "disableAutoMode": "disable", - "env": { - "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - "BASH_DEFAULT_TIMEOUT_MS": "120000", - "BASH_MAX_TIMEOUT_MS": "300000", - "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", - "MAX_MCP_OUTPUT_TOKENS": "10000", - "MCP_TIMEOUT": "120000", - "MCP_TOOL_TIMEOUT": "30000", - "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", - "FORCE_AUTOUPDATE_PLUGINS": "1", - "CLAUDE_CODE_SCROLL_SPEED": "3", - "ENABLE_TOOL_SEARCH": "auto:5", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", - "CLAUDE_CODE_ENABLE_TASKS": "1", - "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0", - "ENABLE_CLAUDE_CODE_SM_COMPACT": "1", - "CLAUDE_CODE_FORCE_GLOBAL_CACHE": "1", - "CLAUDE_CODE_PLAN_MODE_INTERVIEW_PHASE": "true", - "CLAUDE_CODE_PLAN_V2_AGENT_COUNT": "5", - "CLAUDE_CODE_PLAN_MODE_REQUIRED": "true", - "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "true", - "CLAUDE_AUTO_BACKGROUND_TASKS": "1", - "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", - "CLAUDE_CODE_NO_FLICKER": "1", - "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", - "CLAUDE_CODE_MAX_RETRIES": "1", - "BASH_MAX_OUTPUT_LENGTH": "15000", - "TASK_MAX_OUTPUT_LENGTH": "64000", - "DISABLE_AUTOUPDATER": "1", - "ANTHROPIC_MODEL": "claude-opus-4-6", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-6", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", - "MAX_THINKING_TOKENS": "31999", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" - }, - "teammateMode": "auto", - "showClearContextOnPlanAccept": true, - "showThinkingSummaries": true, - "attribution": { - "commit": "", - "pr": "" - }, - "autoMemoryDirectory": "./.claude/memory", - "plansDirectory": "./.claude/plans", - "viewMode": "focus", - "spinnerVerbs": { - "mode": "replace", - "verbs": [ - "Butchering", - "Mangling", - "Wrecking", - "Botching", - "Misreading", - "Derailing", - "Overcomplicating", - "Hallucinating", - "Breaking", - "Fumbling", - "Sabotaging", - "Shredding", - "Confusing", - "Corrupting", - "Ruining", - "Winging", - "Guessing", - "Misinterpreting", - "Overengineering", - "Improvising Poorly", - "Making It Worse", - "Massacring", - "Mutilating", - "Annihilating", - "Trashing", - "Destroying", - "Misfiring", - "Ignoring", - "Unthinking", - "Wondering", - "Draining", - "Exhausting", - "Petering Out" - ] - }, - "permissions": { - "allow": [ - "Read(/workspaces/*)", - "WebFetch(domain:*)" - ], - "deny": [], - "ask": [], - "defaultMode": "plan", - "additionalDirectories": [] - }, - "enabledMcpjsonServers": [], - "disabledMcpjsonServers": [], - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "karma-title-generator", - "timeout": 15 - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/usr/local/bin/ccstatusline-wrapper" - }, - "enabledPlugins": { - "frontend-design@anthropics/claude-code": true, - "code-review@anthropics/claude-code": true, - "feature-dev@anthropics/claude-code": true, - "pr-review-toolkit@anthropics/claude-code": true, - "codeforge-lsp@devs-marketplace": false, - "ticket-workflow@devs-marketplace": false, - "notify-hook@devs-marketplace": false, - "dangerous-command-blocker@devs-marketplace": false, - "protected-files-guard@devs-marketplace": true, - "agent-system@devs-marketplace": true, - "skill-engine@devs-marketplace": false, - "spec-workflow@devs-marketplace": false, - "session-context@devs-marketplace": false, - "auto-code-quality@devs-marketplace": true, - "workspace-scope-guard@devs-marketplace": true, - "prompt-snippets@devs-marketplace": false, - "git-workflow@devs-marketplace": false - }, - "autoUpdatesChannel": "latest", - "model": "claude-opus-4-6", - "autoCompactWindow": 200000 -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json deleted file mode 100755 index d903ee4..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "cleanupPeriodDays": 90, - "autoCompact": true, - "alwaysThinkingEnabled": true, - "skipDangerousModePermissionPrompt": true, - "disableAutoMode": "disable", - "env": { - "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - "BASH_DEFAULT_TIMEOUT_MS": "120000", - "BASH_MAX_TIMEOUT_MS": "300000", - "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", - "MAX_MCP_OUTPUT_TOKENS": "10000", - "MCP_TIMEOUT": "120000", - "MCP_TOOL_TIMEOUT": "30000", - "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", - "FORCE_AUTOUPDATE_PLUGINS": "1", - "CLAUDE_CODE_SCROLL_SPEED": "3", - "ENABLE_TOOL_SEARCH": "auto:5", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", - "CLAUDE_CODE_ENABLE_TASKS": "1", - "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0", - "ENABLE_CLAUDE_CODE_SM_COMPACT": "1", - "CLAUDE_CODE_FORCE_GLOBAL_CACHE": "1", - "CLAUDE_CODE_PLAN_MODE_INTERVIEW_PHASE": "true", - "CLAUDE_CODE_PLAN_V2_AGENT_COUNT": "5", - "CLAUDE_CODE_PLAN_MODE_REQUIRED": "true", - "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "true", - "CLAUDE_AUTO_BACKGROUND_TASKS": "1", - "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", - "CLAUDE_CODE_NO_FLICKER": "1", - "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", - "CLAUDE_CODE_MAX_RETRIES": "1", - "BASH_MAX_OUTPUT_LENGTH": "15000", - "TASK_MAX_OUTPUT_LENGTH": "64000", - "DISABLE_AUTOUPDATER": "1", - "ANTHROPIC_MODEL": "claude-opus-4-7[1m]", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7[1m]", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "400000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", - "CLAUDE_CODE_EFFORT_LEVEL": "max" - }, - "teammateMode": "auto", - "showClearContextOnPlanAccept": true, - "showThinkingSummaries": true, - "attribution": { - "commit": "", - "pr": "" - }, - "autoMemoryDirectory": "./.claude/memory", - "plansDirectory": "./.claude/plans", - "viewMode": "focus", - "spinnerVerbs": { - "mode": "replace", - "verbs": [ - "Butchering", - "Mangling", - "Wrecking", - "Botching", - "Misreading", - "Derailing", - "Overcomplicating", - "Hallucinating", - "Breaking", - "Fumbling", - "Sabotaging", - "Shredding", - "Confusing", - "Corrupting", - "Ruining", - "Winging", - "Guessing", - "Misinterpreting", - "Overengineering", - "Improvising Poorly", - "Making It Worse", - "Massacring", - "Mutilating", - "Annihilating", - "Trashing", - "Destroying", - "Misfiring", - "Ignoring", - "Unthinking", - "Wondering", - "Draining", - "Exhausting", - "Petering Out" - ] - }, - "permissions": { - "allow": [ - "Read(/workspaces/*)", - "WebFetch(domain:*)" - ], - "deny": [], - "ask": [], - "defaultMode": "plan", - "additionalDirectories": [] - }, - "enabledMcpjsonServers": [], - "disabledMcpjsonServers": [], - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "karma-title-generator", - "timeout": 15 - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/usr/local/bin/ccstatusline-wrapper" - }, - "enabledPlugins": { - "frontend-design@anthropics/claude-code": true, - "code-review@anthropics/claude-code": true, - "feature-dev@anthropics/claude-code": true, - "pr-review-toolkit@anthropics/claude-code": true, - "codeforge-lsp@devs-marketplace": false, - "ticket-workflow@devs-marketplace": false, - "notify-hook@devs-marketplace": false, - "dangerous-command-blocker@devs-marketplace": false, - "protected-files-guard@devs-marketplace": true, - "agent-system@devs-marketplace": true, - "skill-engine@devs-marketplace": false, - "spec-workflow@devs-marketplace": false, - "session-context@devs-marketplace": false, - "auto-code-quality@devs-marketplace": true, - "workspace-scope-guard@devs-marketplace": true, - "prompt-snippets@devs-marketplace": false, - "git-workflow@devs-marketplace": false - }, - "autoUpdatesChannel": "latest", - "model": "claude-opus-4-7[1m]", - "autoCompactWindow": 400000, - "effortLevel": "max" -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-45-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-45-200k.json deleted file mode 100644 index 654661e..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-45-200k.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "claude-opus-4-5", - "autoCompactWindow": 200000, - "env": { - "ANTHROPIC_MODEL": "claude-opus-4-5", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-5", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", - "MAX_THINKING_TOKENS": "31999", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" - } -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-1m-400k.json deleted file mode 100644 index 0e4b80d..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-1m-400k.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "claude-opus-4-6[1m]", - "autoCompactWindow": 400000, - "env": { - "ANTHROPIC_MODEL": "claude-opus-4-6[1m]", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-6[1m]", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "400000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", - "MAX_THINKING_TOKENS": "31999", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" - } -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-200k.json deleted file mode 100644 index 669e475..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-46-200k.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "claude-opus-4-6", - "autoCompactWindow": 200000, - "env": { - "ANTHROPIC_MODEL": "claude-opus-4-6", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-6", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", - "MAX_THINKING_TOKENS": "31999", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1" - } -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-1m-400k.json b/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-1m-400k.json deleted file mode 100644 index 19a12ef..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-1m-400k.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "claude-opus-4-7[1m]", - "autoCompactWindow": 400000, - "effortLevel": "max", - "env": { - "ANTHROPIC_MODEL": "claude-opus-4-7[1m]", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7[1m]", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "400000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", - "CLAUDE_CODE_EFFORT_LEVEL": "max" - } -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-200k.json b/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-200k.json deleted file mode 100644 index 9c3d827..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings-profiles/opus-47-200k.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "model": "claude-opus-4-7", - "autoCompactWindow": 200000, - "effortLevel": "max", - "env": { - "ANTHROPIC_MODEL": "claude-opus-4-7", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", - "CLAUDE_CODE_EFFORT_LEVEL": "max" - } -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings.base.json b/container/.devcontainer/defaults/codeforge/config/settings.base.json deleted file mode 100755 index fe45380..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings.base.json +++ /dev/null @@ -1,220 +0,0 @@ -{ - "cleanupPeriodDays": 90, - "autoCompact": true, - "alwaysThinkingEnabled": true, - "skipDangerousModePermissionPrompt": true, - "disableAutoMode": "disable", - "env": { - "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - "BASH_DEFAULT_TIMEOUT_MS": "120000", - "BASH_MAX_TIMEOUT_MS": "300000", - "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", - "MAX_MCP_OUTPUT_TOKENS": "10000", - "MCP_TIMEOUT": "120000", - "MCP_TOOL_TIMEOUT": "30000", - "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", - "FORCE_AUTOUPDATE_PLUGINS": "1", - "CLAUDE_CODE_SCROLL_SPEED": "3", - - "ENABLE_TOOL_SEARCH": "auto:5", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", - "CLAUDE_CODE_ENABLE_TASKS": "1", - "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0", - "ENABLE_CLAUDE_CODE_SM_COMPACT": "1", - "CLAUDE_CODE_FORCE_GLOBAL_CACHE": "1", - "CLAUDE_CODE_PLAN_MODE_INTERVIEW_PHASE": "true", - "CLAUDE_CODE_PLAN_V2_AGENT_COUNT": "5", - "CLAUDE_CODE_PLAN_MODE_REQUIRED": "true", - "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "true", - "CLAUDE_AUTO_BACKGROUND_TASKS": "1", - "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", - "CLAUDE_CODE_NO_FLICKER": "1", - - "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", - "CLAUDE_CODE_MAX_RETRIES": "1", - "BASH_MAX_OUTPUT_LENGTH": "15000", - "TASK_MAX_OUTPUT_LENGTH": "64000", - "DISABLE_AUTOUPDATER": "1" - }, - "teammateMode": "auto", - "showClearContextOnPlanAccept": true, - "showThinkingSummaries": true, - "attribution": { - "commit": "", - "pr": "" - }, - "autoMemoryDirectory": "./.claude/memory", - "plansDirectory": "./.claude/plans", - "viewMode": "focus", - "spinnerVerbs": { - "mode": "replace", - "verbs": [ - "Butchering", - "Mangling", - "Wrecking", - "Botching", - "Misreading", - "Derailing", - "Overcomplicating", - "Hallucinating", - "Breaking", - "Fumbling", - "Sabotaging", - "Shredding", - "Confusing", - "Corrupting", - "Ruining", - "Winging", - "Guessing", - "Misinterpreting", - "Overengineering", - "Improvising Poorly", - "Making It Worse", - "Massacring", - "Mutilating", - "Annihilating", - "Trashing", - "Destroying", - "Misfiring", - "Ignoring", - "Unthinking", - "Wondering", - "Draining", - "Exhausting", - "Petering Out" - ] - }, - "permissions": { - "allow": ["Read(/workspaces/*)", "WebFetch(domain:*)"], - "deny": [], - "ask": [], - "defaultMode": "plan", - "additionalDirectories": [] - }, - "enabledMcpjsonServers": [], - "disabledMcpjsonServers": [], - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "karma-title-generator", - "timeout": 15 - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/usr/local/bin/ccstatusline-wrapper" - }, - "enabledPlugins": { - "frontend-design@anthropics/claude-code": true, - "code-review@anthropics/claude-code": true, - "feature-dev@anthropics/claude-code": true, - "pr-review-toolkit@anthropics/claude-code": true, - "codeforge-lsp@devs-marketplace": false, - "ticket-workflow@devs-marketplace": false, - "notify-hook@devs-marketplace": false, - "dangerous-command-blocker@devs-marketplace": false, - "protected-files-guard@devs-marketplace": true, - "agent-system@devs-marketplace": true, - "skill-engine@devs-marketplace": false, - "spec-workflow@devs-marketplace": false, - "session-context@devs-marketplace": false, - "auto-code-quality@devs-marketplace": true, - "workspace-scope-guard@devs-marketplace": true, - "prompt-snippets@devs-marketplace": false, - "git-workflow@devs-marketplace": false - }, - "autoUpdatesChannel": "latest" -} diff --git a/container/.devcontainer/defaults/codeforge/config/settings.json b/container/.devcontainer/defaults/codeforge/config/settings.json deleted file mode 100755 index a1cdfff..0000000 --- a/container/.devcontainer/defaults/codeforge/config/settings.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "cleanupPeriodDays": 90, - "autoCompact": true, - "alwaysThinkingEnabled": true, - "skipDangerousModePermissionPrompt": true, - "disableAutoMode": "disable", - "env": { - "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - "BASH_DEFAULT_TIMEOUT_MS": "120000", - "BASH_MAX_TIMEOUT_MS": "300000", - "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", - "MAX_MCP_OUTPUT_TOKENS": "10000", - "MCP_TIMEOUT": "120000", - "MCP_TOOL_TIMEOUT": "30000", - "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80", - "FORCE_AUTOUPDATE_PLUGINS": "1", - "CLAUDE_CODE_SCROLL_SPEED": "3", - "ENABLE_TOOL_SEARCH": "auto:5", - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", - "CLAUDE_CODE_ENABLE_TASKS": "1", - "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0", - "ENABLE_CLAUDE_CODE_SM_COMPACT": "1", - "CLAUDE_CODE_FORCE_GLOBAL_CACHE": "1", - "CLAUDE_CODE_PLAN_MODE_INTERVIEW_PHASE": "true", - "CLAUDE_CODE_PLAN_V2_AGENT_COUNT": "5", - "CLAUDE_CODE_PLAN_MODE_REQUIRED": "true", - "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "true", - "CLAUDE_AUTO_BACKGROUND_TASKS": "1", - "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false", - "CLAUDE_CODE_NO_FLICKER": "1", - "CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "10", - "CLAUDE_CODE_MAX_RETRIES": "1", - "BASH_MAX_OUTPUT_LENGTH": "15000", - "TASK_MAX_OUTPUT_LENGTH": "64000", - "DISABLE_AUTOUPDATER": "1", - "ANTHROPIC_MODEL": "claude-opus-4-7", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "200000", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "200000", - "CLAUDE_CODE_EFFORT_LEVEL": "max" - }, - "teammateMode": "auto", - "showClearContextOnPlanAccept": true, - "showThinkingSummaries": true, - "attribution": { - "commit": "", - "pr": "" - }, - "autoMemoryDirectory": "./.claude/memory", - "plansDirectory": "./.claude/plans", - "viewMode": "focus", - "spinnerVerbs": { - "mode": "replace", - "verbs": [ - "Butchering", - "Mangling", - "Wrecking", - "Botching", - "Misreading", - "Derailing", - "Overcomplicating", - "Hallucinating", - "Breaking", - "Fumbling", - "Sabotaging", - "Shredding", - "Confusing", - "Corrupting", - "Ruining", - "Winging", - "Guessing", - "Misinterpreting", - "Overengineering", - "Improvising Poorly", - "Making It Worse", - "Massacring", - "Mutilating", - "Annihilating", - "Trashing", - "Destroying", - "Misfiring", - "Ignoring", - "Unthinking", - "Wondering", - "Draining", - "Exhausting", - "Petering Out" - ] - }, - "permissions": { - "allow": [ - "Read(/workspaces/*)", - "WebFetch(domain:*)" - ], - "deny": [], - "ask": [], - "defaultMode": "plan", - "additionalDirectories": [] - }, - "enabledMcpjsonServers": [], - "disabledMcpjsonServers": [], - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "karma-live-session-tracker", - "timeout": 5 - } - ] - }, - { - "hooks": [ - { - "type": "command", - "command": "karma-title-generator", - "timeout": 15 - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/usr/local/bin/ccstatusline-wrapper" - }, - "enabledPlugins": { - "frontend-design@anthropics/claude-code": true, - "code-review@anthropics/claude-code": true, - "feature-dev@anthropics/claude-code": true, - "pr-review-toolkit@anthropics/claude-code": true, - "codeforge-lsp@devs-marketplace": false, - "ticket-workflow@devs-marketplace": false, - "notify-hook@devs-marketplace": false, - "dangerous-command-blocker@devs-marketplace": false, - "protected-files-guard@devs-marketplace": true, - "agent-system@devs-marketplace": true, - "skill-engine@devs-marketplace": false, - "spec-workflow@devs-marketplace": false, - "session-context@devs-marketplace": false, - "auto-code-quality@devs-marketplace": true, - "workspace-scope-guard@devs-marketplace": true, - "prompt-snippets@devs-marketplace": false, - "git-workflow@devs-marketplace": false - }, - "autoUpdatesChannel": "latest", - "model": "claude-opus-4-7", - "autoCompactWindow": 200000, - "effortLevel": "max" -} diff --git a/container/.devcontainer/defaults/codeforge/container.json b/container/.devcontainer/defaults/codeforge/container.json new file mode 100755 index 0000000..9a8ea78 --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/container.json @@ -0,0 +1,24 @@ +{ + "setup": { + "config": true, + "aliases": true, + "auth": true, + "plugins": true, + "updateClaude": true, + "terminal": true, + "poststart": true, + "projects": true + }, + "identity": { + "name": null, + "email": null + }, + "timezone": "America/Chicago", + "claude": { + "versionLock": null + }, + "plugins": { + "blacklist": [], + "official": ["frontend-design@claude-plugins-official"] + } +} diff --git a/container/.devcontainer/defaults/codeforge/file-manifest.json b/container/.devcontainer/defaults/codeforge/file-manifest.json old mode 100644 new mode 100755 index 8b0b23d..48283aa --- a/container/.devcontainer/defaults/codeforge/file-manifest.json +++ b/container/.devcontainer/defaults/codeforge/file-manifest.json @@ -1,143 +1,223 @@ [ { - "src": "config/settings.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.state", + "src": "claude/claude.json", + "dest": "${HOME}", + "destFilename": ".claude.json", + "enabled": true, + "overwrite": "never" + }, + { + "id": "codeforge.container-config", + "src": "container.json", + "dest": "${CODEFORGE_DIR}", + "destFilename": "container.json", + "enabled": true, + "overwrite": "never" + }, + { + "id": "claude.settings.default", + "src": "claude/settings/settings.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/settings-opus-47-1m-400k.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.settings.opus-46-200k", + "src": "claude/settings/settings-opus-46-200k.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/settings-opus-46-200k.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.settings.opus-46-1m-400k", + "src": "claude/settings/settings-opus-46-1m-400k.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/settings-opus-46-1m-400k.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.settings.opus-47-200k", + "src": "claude/settings/settings-opus-47-200k.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/settings-opus-45-200k.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.settings.opus-47-1m-400k", + "src": "claude/settings/settings-opus-47-1m-400k.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/keybindings.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.settings.opus-45-200k", + "src": "claude/settings/settings-opus-45-200k.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/main-system-prompt.md", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.keybindings", + "src": "claude/keybindings.json", + "dest": "${HOME}/.claude", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/spec-workflow.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", - "enabled": false, + "id": "claude.system-prompts.main", + "src": "claude/system-prompts/main.md", + "dest": "${HOME}/.claude", + "destFilename": "main-system-prompt.md", + "enabled": true, + "overwrite": "if-changed" + }, + { + "id": "claude.system-prompts.writing", + "src": "claude/system-prompts/writing.md", + "dest": "${HOME}/.claude", + "destFilename": "writing-system-prompt.md", + "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/workspace-scope.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.system-prompts.orchestrator", + "src": "claude/system-prompts/orchestrator.md", + "dest": "${HOME}/.claude", + "destFilename": "orchestrator-system-prompt.md", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/session-search.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.spec-workflow", + "src": "claude/rules/spec-workflow.md", + "dest": "${HOME}/.claude/rules", + "enabled": false, + "overwrite": "if-changed" + }, + { + "id": "claude.rule.workspace-scope", + "src": "claude/rules/workspace-scope.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/auto-memory.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.session-search", + "src": "claude/rules/session-search.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/explicit-start.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.auto-memory", + "src": "claude/rules/auto-memory.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/plan-presentation.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.explicit-start", + "src": "claude/rules/explicit-start.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/scope-discipline.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.plan-presentation", + "src": "claude/rules/plan-presentation.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/surface-decisions.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.scope-discipline", + "src": "claude/rules/scope-discipline.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/rules/zero-tolerance-bugs.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.surface-decisions", + "src": "claude/rules/surface-decisions.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/writing-system-prompt.md", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.rule.zero-tolerance-bugs", + "src": "claude/rules/zero-tolerance-bugs.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/orchestrator-system-prompt.md", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.rule.rtk-awareness", + "src": "claude/rules/rtk-awareness.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/ccstatusline-settings.json", + "id": "claude.statusline.user", + "src": "claude/statusline/settings.json", "dest": "${HOME}/.config/ccstatusline", "destFilename": "settings.json", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/ccstatusline-settings.json", + "id": "claude.statusline.template", + "src": "claude/statusline/settings.json", "dest": "/usr/local/share/ccstatusline", "destFilename": "settings.template.json", "enabled": true, "overwrite": "always" }, { - "src": "config/disabled-hooks.json", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.disabled-hooks", + "src": "claude/disabled-hooks.json", + "dest": "${HOME}/.claude", + "destFilename": "disabled-hooks.json", "enabled": true, "overwrite": "never" }, { - "src": "config/codex-config.toml", + "id": "claude.router.config", + "src": "claude/router/config.json", + "dest": "${HOME}/.claude-code-router", + "destFilename": "config.json", + "enabled": true, + "overwrite": "if-changed" + }, + { + "id": "claude.hook.rtk-rewrite", + "src": "claude/hooks/rtk-rewrite.sh", + "dest": "${HOME}/.claude/hooks", + "enabled": true, + "overwrite": "if-changed" + }, + { + "id": "rtk.config", + "src": "rtk/config.toml", + "dest": "${HOME}/.config/rtk", + "destFilename": "config.toml", + "enabled": true, + "overwrite": "if-changed" + }, + { + "id": "codex.config", + "src": "codex/config.toml", "dest": "${HOME}/.codex", "destFilename": "config.toml", "enabled": true, "overwrite": "if-changed" }, { - "src": "config/claude-code-router.json", - "dest": "${HOME}/.claude-code-router", - "destFilename": "config.json", + "id": "codex.agents", + "src": "codex/AGENTS.md", + "dest": "${HOME}/.codex", + "destFilename": "AGENTS.md", "enabled": true, "overwrite": "if-changed" } diff --git a/container/.devcontainer/defaults/codeforge/rtk/config.toml b/container/.devcontainer/defaults/codeforge/rtk/config.toml new file mode 100644 index 0000000..33394be --- /dev/null +++ b/container/.devcontainer/defaults/codeforge/rtk/config.toml @@ -0,0 +1,14 @@ +# RTK Configuration +# https://github.com/rtk-ai/rtk + +[telemetry] +enabled = false + +[hooks] +# Commands to exclude from RTK rewriting (empty = rewrite all supported) +exclude_commands = [] + +[tee] +# Save raw output for debugging failed commands +enabled = true +mode = "failures" diff --git a/container/.gitignore b/container/.gitignore index f8cc3de..45ce14a 100644 --- a/container/.gitignore +++ b/container/.gitignore @@ -18,3 +18,4 @@ # DevContainer build cache .devcontainer/.dockercache/ +.devcontainer/.generated/ diff --git a/container/.npmignore b/container/.npmignore index d144079..df6e9c9 100644 --- a/container/.npmignore +++ b/container/.npmignore @@ -3,6 +3,7 @@ # Development and testing test.js .devcontainer/state/ +.devcontainer/.generated/ .devcontainer/config/backups/ # Documentation that's not needed in package @@ -51,4 +52,6 @@ Thumbs.db .devcontainer/.secrets # Per-installation state (not tracked) -.codeforge/.markers/ \ No newline at end of file +.codeforge/.checksums/ +.codeforge/.markers/ +.codeforge/data/ From 2c973bb0d39c7f18e13ad07e1f750659501e7ce7 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:35:50 +0000 Subject: [PATCH 09/27] feat(container): update devcontainer.json and Docker Compose for v3 Add new features (rtk, claude-mem, lamarck, ccdiag, sandcastle, claude-session-analyzer), update formatter versions to latest, add VS Code settings for terminal/editor UX. Rename generate-mounts.mjs to generate-compose.mjs with expanded compose generation. Simplify secrets model and remove deprecated env vars (CLAUDE_CONFIG_DIR). --- container/.devcontainer/devcontainer.json | 203 +++++++++++++----- container/.devcontainer/docker-compose.yml | 5 +- .../scripts/generate-compose.mjs | 149 +++++++++++++ .../.devcontainer/scripts/generate-mounts.mjs | 88 -------- 4 files changed, 306 insertions(+), 139 deletions(-) create mode 100755 container/.devcontainer/scripts/generate-compose.mjs delete mode 100755 container/.devcontainer/scripts/generate-mounts.mjs diff --git a/container/.devcontainer/devcontainer.json b/container/.devcontainer/devcontainer.json index 8c11bd9..53f29c0 100755 --- a/container/.devcontainer/devcontainer.json +++ b/container/.devcontainer/devcontainer.json @@ -2,38 +2,32 @@ "name": "CodeForge - ${localWorkspaceFolderBasename}", "dockerComposeFile": ["docker-compose.yml", "docker-compose.codeforge.yml"], "service": "codeforge", - "initializeCommand": "node .devcontainer/scripts/generate-mounts.mjs", + "initializeCommand": "node .devcontainer/scripts/generate-compose.mjs", "workspaceFolder": "/workspaces", "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces,type=bind", "remoteEnv": { "WORKSPACE_ROOT": "/workspaces", - "CLAUDE_CONFIG_DIR": "/home/vscode/.claude", "GH_CONFIG_DIR": "/home/vscode/.config/gh", "HERMES_CDP_ENDPOINT": "http://host.docker.internal:9223", "TERM": "${localEnv:TERM:xterm-256color}", "COLORTERM": "truecolor", - "CLAUDECODE": null + "RTK_AUTO_ALLOW": "${localEnv:RTK_AUTO_ALLOW}", + "TZ": "${localEnv:TZ:America/Chicago}" }, "secrets": { "GH_TOKEN": { - "description": "GitHub Personal Access Token (optional - for git auth)", + "description": "GitHub Personal Access Token (optional - for git auth + identity)", "documentationUrl": "https://github.com/settings/tokens" }, "NPM_TOKEN": { "description": "NPM auth token (optional - for npm registry)", "documentationUrl": "https://www.npmjs.com/settings/~/tokens" }, - "GH_USERNAME": { - "description": "GitHub username for git config (optional)" - }, - "GH_EMAIL": { - "description": "GitHub email for git config (optional)" - }, - "CLAUDE_AUTH_TOKEN": { - "description": "Claude long-lived auth token from 'claude setup-token' (optional - sk-ant-oat01-*)", + "CLAUDE_CODE_OAUTH_TOKEN": { + "description": "Claude Code OAuth token from 'claude setup-token' (optional - sk-ant-*)", "documentationUrl": "https://docs.anthropic.com/en/docs/claude-code/cli-reference#claude-setup-token" }, "OPENAI_API_KEY": { @@ -41,7 +35,7 @@ "documentationUrl": "https://platform.openai.com/api-keys" }, "ANTHROPIC_API_KEY": { - "description": "Anthropic API key for claude-code-router (optional - sk-ant-api03-*)", + "description": "Anthropic API key for claude-code-router (optional - conflicts with CLAUDE_CODE_OAUTH_TOKEN)", "documentationUrl": "https://console.anthropic.com/settings/keys" }, "DEEPSEEK_API_KEY": { @@ -55,30 +49,6 @@ "OPENROUTER_API_KEY": { "description": "OpenRouter API key for claude-code-router (optional)", "documentationUrl": "https://openrouter.ai/keys" - }, - "KIMI_API_KEY": { - "description": "Moonshot Kimi API key for oh-my-claude (optional)", - "documentationUrl": "https://platform.moonshot.cn/" - }, - "ZHIPU_API_KEY": { - "description": "ZhiPu GLM API key for oh-my-claude (optional)", - "documentationUrl": "https://open.bigmodel.cn/" - }, - "ALIYUN_API_KEY": { - "description": "Aliyun DashScope API key for oh-my-claude (optional)", - "documentationUrl": "https://dashscope.console.aliyun.com/" - }, - "MINIMAX_API_KEY": { - "description": "MiniMax API key for oh-my-claude (optional)", - "documentationUrl": "https://platform.minimaxi.com/" - }, - "ZAI_API_KEY": { - "description": "Z.AI / ZhiPu global API key for oh-my-claude (optional)", - "documentationUrl": "https://z.ai/" - }, - "MINIMAX_CN_API_KEY": { - "description": "MiniMax CN API key for oh-my-claude (optional)", - "documentationUrl": "https://platform.minimaxi.com/" } }, @@ -95,8 +65,10 @@ "ghcr.io/devcontainers/features/docker-outside-of-docker", "ghcr.io/devcontainers-extra/features/uv", "ghcr.io/rails/devcontainer/features/bun", + "ghcr.io/devcontainers/features/go", "./features/codeforge-cli", "./features/claude-code-native", + "./features/rtk", "./features/codex-cli", "./features/hermes-agent", "./features/tmux", @@ -106,6 +78,11 @@ "./features/ccusage", "./features/claude-code-router", "./features/oh-my-claude", + "./features/claude-mem", + "./features/lamarck", + "./features/ccdiag", + "./features/claude-session-analyzer", + "./features/sandcastle", // "./features/ccburn", "./features/ccstatusline", "./features/ast-grep", @@ -140,9 +117,9 @@ // "ghcr.io/devcontainers/features/rust:1.5.0": { // "version": "latest" // }, - // Uncomment to add Go runtime (not installed by default): - // "ghcr.io/devcontainers/features/go:1": {}, + "ghcr.io/devcontainers/features/go:1": {}, "./features/claude-code-native": {}, + "./features/rtk": {}, "./features/codex-cli": {}, "./features/hermes-agent": {}, "./features/tmux": {}, @@ -164,6 +141,30 @@ "username": "automatic", "providerAgentsOnly": true }, + "./features/claude-mem": { + "version": "v12.6.2", + "workerPort": "37777", + "autostart": "true", + "chromaMode": "local", + "username": "automatic" + }, + "./features/lamarck": { + "version": "latest", + "cronInterval": "4h", + "username": "automatic" + }, + "./features/ccdiag": { + "version": "latest", + "username": "automatic" + }, + "./features/claude-session-analyzer": { + "version": "latest", + "username": "automatic" + }, + "./features/sandcastle": { + "version": "latest", + "username": "automatic" + }, "./features/claude-monitor": { "version": "latest", "installer": "uv", @@ -200,13 +201,13 @@ "version": "latest", "username": "automatic" }, - "./features/shfmt": { "version": "none" }, + "./features/shfmt": { "version": "latest" }, "./features/dprint": { - "version": "none", + "version": "latest", "username": "automatic" }, - "./features/shellcheck": { "version": "none" }, - "./features/hadolint": { "version": "none" }, + "./features/shellcheck": { "version": "latest" }, + "./features/hadolint": { "version": "latest" }, "./features/biome": {}, "./features/codeforge-cli": {}, "./features/notify-hook": { @@ -225,6 +226,14 @@ "label": "Claude Code Karma API", "onAutoForward": "silent" }, + "37777": { + "label": "Claude-Mem Worker/UI", + "onAutoForward": "silent" + }, + "9119": { + "label": "ccdiag API Proxy", + "onAutoForward": "silent" + }, "*": { "onAutoForward": "notify" } @@ -238,6 +247,7 @@ "customizations": { "vscode": { "settings": { + // --- Terminal --- "terminal.integrated.profiles.linux": { "zsh": { "path": "zsh" @@ -259,21 +269,114 @@ "-workbench.action.quickOpen", "-workbench.action.terminal.focusFind" ], + "terminal.integrated.fontFamily": "CaskaydiaCove Nerd Font, JetBrains Mono, Cascadia Code, Consolas, monospace", + "terminal.integrated.fontSize": 14, + "terminal.integrated.lineHeight": 1.2, + "terminal.integrated.enableVisualBell": true, + "terminal.integrated.initialHint": false, + + // --- Remote --- "remote.extensionKind": { "wenbopan.vscode-terminal-osc-notifier": ["ui"] }, - "projectManager.git.baseFolders": ["/workspaces"], - "projectManager.git.maxDepthRecursion": 2, - "projectManager.showProjectNameInStatusBar": true, - "projectManager.openInNewWindowWhenClickingInStatusBar": false, - "projectManager.projectsLocation": "/workspaces/.config/project-manager", + "remote.defaultExtensionsIfInstalledLocally": [], + "remote.autoForwardPortsSource": "hybrid", + + // --- Git --- "git.autofetch": false, - "git.autorefresh": false + "git.autorefresh": false, + "git.addAICoAuthor": "off", + + // --- Theme / Appearance --- + "workbench.colorTheme": "Monokai Pro (Filter Spectrum)", + "workbench.iconTheme": "material-icon-theme", + "workbench.productIconTheme": "fluent-icons", + + // --- Font --- + "editor.fontFamily": "JetBrains Mono, Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", + "editor.fontLigatures": true, + "editor.lineHeight": 24, + "editor.fontWeight": "450", + "editor.letterSpacing": 0.2, + "editor.fontSize": 12, + + // --- Editor Behavior --- + "editor.semanticHighlighting.enabled": true, + "editor.bracketPairColorization.enabled": true, + "editor.guides.bracketPairs": "active", + "editor.guides.indentation": true, + "editor.guides.highlightActiveIndentation": true, + "editor.cursorBlinking": "smooth", + "editor.cursorSmoothCaretAnimation": "on", + "editor.cursorStyle": "line", + "editor.smoothScrolling": true, + "editor.minimap.enabled": true, + "editor.minimap.renderCharacters": false, + "editor.minimap.scale": 1, + "editor.stickyScroll.enabled": true, + "editor.renderWhitespace": "selection", + "editor.renderLineHighlight": "all", + "editor.wordWrap": "off", + + // --- Workbench Layout --- + "workbench.tree.indent": 18, + "workbench.tree.renderIndentGuides": "always", + "workbench.editor.showTabs": "multiple", + "workbench.editor.tabSizing": "shrink", + "workbench.editor.highlightModifiedTabs": true, + "workbench.activityBar.location": "top", + "workbench.activityBar.compact": true, + "workbench.sideBar.location": "right", + + // --- Window --- + "window.commandCenter": false, + "window.titleBarStyle": "custom", + "window.title": "${dirty}${rootName}${separator}${activeRepositoryBranchName}", + "window.confirmBeforeClose": "keyboardOnly", + "window.restoreWindows": "preserve", + "window.newWindowDimensions": "maximized", + + // --- Extension: Error Lens --- + "errorLens.enabled": true, + "errorLens.messageEnabled": true, + "errorLens.gutterIconsEnabled": true, + + // --- Extension: Material Icon Theme --- + "material-icon-theme.folders.theme": "specific", + "material-icon-theme.hidesExplorerArrows": false, + "material-icon-theme.opacity": 1, + "material-icon-theme.saturation": 1, + + // --- Extension: Workspace Wiki --- + "workspaceWiki.showHiddenFiles": true, + "workspaceWiki.showIgnoredFiles": true, + + // --- Misc --- + "accessibility.signals.terminalBell": { "sound": "on" }, + "chat.agent.enabled": false }, "extensions": [ "wenbopan.vscode-terminal-osc-notifier", "GitHub.vscode-github-actions", - "alefragnani.project-manager" + "ms-azuretools.vscode-docker", + "biomejs.biome", + "vitest.explorer", + "usernamehw.errorlens", + "eamodio.gitlens", + "Gruntfuggly.todo-tree", + "johnpapa.vscode-peacock", + "miguelsolorio.fluent-icons", + "oderwat.indent-rainbow", + "NarasimaPandiyan.jetbrainsmono", + "sugatoray.vscode-markdown-extension-pack", + "AlanWalk.markdown-navigation", + "ms-vscode.Theme-MarkdownKit", + "PKief.material-icon-theme", + "monokai.theme-monokai-pro-vscode", + "johannes-exe-or-something.org-viewer", + "mrmryb.project-hub", + "tomoki1207.pdf", + "alexjsully.workspace-wiki" ] } } diff --git a/container/.devcontainer/docker-compose.yml b/container/.devcontainer/docker-compose.yml index 8b0b61e..fe7d418 100644 --- a/container/.devcontainer/docker-compose.yml +++ b/container/.devcontainer/docker-compose.yml @@ -11,9 +11,12 @@ services: - codeforge-cache:/home/vscode/.cache - codeforge-npm-cache:/home/vscode/.npm - codeforge-bun-cache:/home/vscode/.bun/install/cache + # Workspace + - ..:/workspaces mem_limit: 6g - memswap_limit: 6g + memswap_limit: 6g # Equal to mem_limit — swap disabled intentionally container_name: codeforge + command: sleep infinity volumes: codeforge-claude-config: diff --git a/container/.devcontainer/scripts/generate-compose.mjs b/container/.devcontainer/scripts/generate-compose.mjs new file mode 100755 index 0000000..a98e829 --- /dev/null +++ b/container/.devcontainer/scripts/generate-compose.mjs @@ -0,0 +1,149 @@ +#!/usr/bin/env node + +// generate-compose.mjs — Reads .codeforge/mounts.json and .codeforge/secrets/, +// generates docker-compose.codeforge.yml with volumes and secrets. +// Runs on the HOST via initializeCommand. Must work on Windows, macOS, Linux. +// Node.js is always available (VS Code requires it). + +import { + existsSync, + mkdirSync, + readdirSync, + readFileSync, + statSync, + writeFileSync, +} from "node:fs"; +import { dirname, join } from "node:path"; + +const MOUNTS_FILE = ".codeforge/mounts.json"; +const SECRETS_DIR = ".codeforge/secrets"; +const OUTPUT_FILE = ".devcontainer/docker-compose.codeforge.yml"; + +function sanitizeName(path) { + return path.replace(/[^a-zA-Z0-9-]/g, "-").replace(/^-+|-+$/g, ""); +} + +function discoverSecrets() { + if (!existsSync(SECRETS_DIR)) { + return []; + } + try { + return readdirSync(SECRETS_DIR) + .filter((name) => { + const fullPath = join(SECRETS_DIR, name); + return statSync(fullPath).isFile() && !name.startsWith("."); + }) + .sort(); + } catch { + return []; + } +} + +function loadMounts() { + if (!existsSync(MOUNTS_FILE)) { + return []; + } + try { + const raw = readFileSync(MOUNTS_FILE, "utf-8"); + const mounts = JSON.parse(raw); + return mounts.volumes || []; + } catch { + return []; + } +} + +function generateCompose(mounts, secrets) { + const lines = [ + "# Auto-generated by CodeForge. Do not edit.", + "# Sources: .codeforge/mounts.json, .codeforge/secrets/", + "# Regenerate: codeforge doctor --fix --yes --only volumes", + ]; + + const hasVolumes = mounts.length > 0; + const hasSecrets = secrets.length > 0; + + lines.push("services:"); + lines.push(" codeforge:"); + + if (hasVolumes) { + lines.push(" volumes:"); + for (const vol of mounts) { + const volName = `codeforge-vol-${sanitizeName(vol.path)}`; + lines.push(` - ${volName}:/workspaces/${vol.path}`); + } + } + + if (hasSecrets) { + lines.push(" secrets:"); + for (const name of secrets) { + lines.push(` - ${name}`); + } + } + + if (!hasVolumes && !hasSecrets) { + lines[lines.length - 1] = " codeforge: {}"; + } + + if (hasVolumes) { + lines.push(""); + lines.push("volumes:"); + for (const vol of mounts) { + const volName = `codeforge-vol-${sanitizeName(vol.path)}`; + lines.push(` ${volName}:`); + } + } + + if (hasSecrets) { + lines.push(""); + lines.push("secrets:"); + for (const name of secrets) { + if (!/^[a-zA-Z0-9_-]+$/.test(name)) { + console.error(`[CodeForge] Invalid secret name: ${name}`); + process.exit(1); + } + lines.push(` ${name}:`); + lines.push(` file: ../.codeforge/secrets/${name}`); + } + } + + lines.push(""); + return lines.join("\n"); +} + +// Main +try { + const mounts = loadMounts(); + const secrets = discoverSecrets(); + const content = generateCompose(mounts, secrets); + + const outputDir = dirname(OUTPUT_FILE); + if (!existsSync(outputDir)) { + mkdirSync(outputDir, { recursive: true }); + } + + writeFileSync(OUTPUT_FILE, content, "utf-8"); + + const parts = []; + if (mounts.length > 0) parts.push(`${mounts.length} volume(s)`); + if (secrets.length > 0) parts.push(`${secrets.length} secret(s)`); + const summary = parts.length > 0 ? parts.join(", ") : "empty override"; + console.log(`[CodeForge] Generated ${OUTPUT_FILE} (${summary})`); +} catch (err) { + console.warn( + `[CodeForge] Warning: compose generation failed: ${err.message}`, + ); + try { + const fallback = [ + "# Compose generation failed. Empty override.", + "services:", + " codeforge: {}", + "", + ].join("\n"); + writeFileSync(OUTPUT_FILE, fallback, "utf-8"); + } catch (innerErr) { + console.error( + `[CodeForge] Fallback compose generation failed: ${innerErr.message}`, + ); + process.exit(1); + } +} diff --git a/container/.devcontainer/scripts/generate-mounts.mjs b/container/.devcontainer/scripts/generate-mounts.mjs deleted file mode 100755 index 04da727..0000000 --- a/container/.devcontainer/scripts/generate-mounts.mjs +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env node - -// generate-mounts.mjs — Reads .codeforge/mounts.json, generates docker-compose.codeforge.yml -// Runs on the HOST via initializeCommand. Must work on Windows, macOS, Linux. -// Node.js is always available (VS Code requires it). - -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; -import { dirname, join } from "node:path"; - -const MOUNTS_FILE = ".codeforge/mounts.json"; -const OUTPUT_FILE = ".devcontainer/docker-compose.codeforge.yml"; - -// When no mounts.json exists, generate an empty override so dockerComposeFile doesn't error -function generateEmptyOverride() { - return [ - '# No custom volumes configured. Run "codeforge doctor --fix" inside the container.', - "services:", - " codeforge: {}", - "", - ].join("\n"); -} - -function sanitizeName(path) { - return path.replace(/[^a-zA-Z0-9-]/g, "-").replace(/^-+|-+$/g, ""); -} - -function generateOverride(mounts) { - const lines = [ - "# Auto-generated by CodeForge. Do not edit.", - "# Source: .codeforge/mounts.json", - "# Regenerate: codeforge doctor --fix --yes --only volumes", - "services:", - " codeforge:", - " volumes:", - ]; - - const volumeNames = []; - - for (const vol of mounts.volumes) { - const volName = `codeforge-vol-${sanitizeName(vol.path)}`; - // Use /workspaces/ prefix since that's the workspace mount target - lines.push(` - ${volName}:/workspaces/${vol.path}`); - volumeNames.push(volName); - } - - lines.push(""); - lines.push("volumes:"); - for (const name of volumeNames) { - lines.push(` ${name}:`); - } - lines.push(""); - - return lines.join("\n"); -} - -// Main -try { - let content; - - if (!existsSync(MOUNTS_FILE)) { - content = generateEmptyOverride(); - } else { - const raw = readFileSync(MOUNTS_FILE, "utf-8"); - const mounts = JSON.parse(raw); - - if (!mounts.volumes || mounts.volumes.length === 0) { - content = generateEmptyOverride(); - } else { - content = generateOverride(mounts); - } - } - - // Ensure output directory exists - const outputDir = dirname(OUTPUT_FILE); - if (!existsSync(outputDir)) { - mkdirSync(outputDir, { recursive: true }); - } - - writeFileSync(OUTPUT_FILE, content, "utf-8"); - console.log(`[CodeForge] Generated ${OUTPUT_FILE}`); -} catch (err) { - // Don't fail the container build if mount generation fails - console.warn(`[CodeForge] Warning: mount generation failed: ${err.message}`); - // Write empty override as fallback - try { - writeFileSync(OUTPUT_FILE, generateEmptyOverride(), "utf-8"); - } catch {} -} From 7c2ba507427645c25a1e1afc404ca70bd7023278 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:36:06 +0000 Subject: [PATCH 10/27] refactor(container): rewrite setup scripts and test suite for v3 config Rewrite setup.js to support three-tier manifest merging (packaged defaults + generated output + user overrides). Refactor setup-config.sh and setup-auth.sh for file-per-secret model. Add v3 migration script. Update generate-settings-profiles.js for base + overlay architecture. Rewrite test.js in TAP format to validate new config pipeline. Bump to v3.0.0, require Node >= 18. --- container/.codeforge/container.json | 24 + .../.devcontainer/scripts/check-setup.sh | 19 +- .../scripts/ensure-settings-generated.sh | 87 +++ .../scripts/generate-settings-profiles.js | 343 ++++++++++- .../.devcontainer/scripts/setup-aliases.sh | 66 +- container/.devcontainer/scripts/setup-auth.sh | 249 +++++--- .../.devcontainer/scripts/setup-config.sh | 278 ++++++--- .../scripts/setup-migrate-claude.sh | 2 +- .../scripts/setup-migrate-codeforge-v3.sh | 159 +++++ .../scripts/setup-migrate-codeforge.sh | 78 +-- .../.devcontainer/scripts/setup-plugins.sh | 2 +- container/.devcontainer/scripts/setup.sh | 135 ++-- container/CLAUDE.md | 2 +- container/README.md | 19 +- container/package.json | 6 +- container/setup.js | 237 ++++--- container/test.js | 582 ++++++++---------- 17 files changed, 1508 insertions(+), 780 deletions(-) create mode 100755 container/.codeforge/container.json create mode 100755 container/.devcontainer/scripts/ensure-settings-generated.sh create mode 100755 container/.devcontainer/scripts/setup-migrate-codeforge-v3.sh diff --git a/container/.codeforge/container.json b/container/.codeforge/container.json new file mode 100755 index 0000000..9a8ea78 --- /dev/null +++ b/container/.codeforge/container.json @@ -0,0 +1,24 @@ +{ + "setup": { + "config": true, + "aliases": true, + "auth": true, + "plugins": true, + "updateClaude": true, + "terminal": true, + "poststart": true, + "projects": true + }, + "identity": { + "name": null, + "email": null + }, + "timezone": "America/Chicago", + "claude": { + "versionLock": null + }, + "plugins": { + "blacklist": [], + "official": ["frontend-design@claude-plugins-official"] + } +} diff --git a/container/.devcontainer/scripts/check-setup.sh b/container/.devcontainer/scripts/check-setup.sh index c3688c7..3c9d600 100755 --- a/container/.devcontainer/scripts/check-setup.sh +++ b/container/.devcontainer/scripts/check-setup.sh @@ -10,6 +10,11 @@ echo "━━━━━━━━━━━━━━━━━━━━" PASS=0 FAIL=0 WARN=0 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")" +WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +CLAUDE_DIR="$HOME/.claude" check() { local label="$1" cmd="$2" @@ -38,11 +43,15 @@ echo "Core:" check "Claude Code installed" "command -v claude" warn_check "Claude native binary" "[ -x ~/.local/bin/claude ]" check "cc launcher configured" "type cc" -check "Config directory exists" "[ -d '${CLAUDE_CONFIG_DIR:-$HOME/.claude}' ]" -check "Settings file exists" "[ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json' ]" -check "Opus 4.7 1M settings profile exists" "[ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings-opus-47-1m-400k.json' ]" -check "Opus 4.6 settings profiles exist" "[ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings-opus-46-200k.json' ] && [ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings-opus-46-1m-400k.json' ]" -check "Opus 4.5 settings profile exists" "[ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings-opus-45-200k.json' ]" +check "Config directory exists" "[ -d '$CLAUDE_DIR' ]" +check ".codeforge overrides/state exists" "[ -d '$CODEFORGE_DIR/.markers' ]" +check "Settings generation marker exists" "[ -f '$CODEFORGE_DIR/.markers/settings-generated-v3' ]" +check "Generated settings are current" "node '$SCRIPT_DIR/generate-settings-profiles.js' --check" +check "Settings file exists" "[ -f '$CLAUDE_DIR/settings.json' ]" +check "Default settings matches Opus 4.6 200k" "cmp -s '$CLAUDE_DIR/settings.json' '$CLAUDE_DIR/settings-opus-46-200k.json'" +check "Opus 4.7 settings profiles exist" "[ -f '$CLAUDE_DIR/settings-opus-47-200k.json' ] && [ -f '$CLAUDE_DIR/settings-opus-47-1m-400k.json' ]" +check "Opus 4.6 settings profiles exist" "[ -f '$CLAUDE_DIR/settings-opus-46-200k.json' ] && [ -f '$CLAUDE_DIR/settings-opus-46-1m-400k.json' ]" +check "Opus 4.5 settings profile exists" "[ -f '$CLAUDE_DIR/settings-opus-45-200k.json' ]" echo "" echo "Authentication:" diff --git a/container/.devcontainer/scripts/ensure-settings-generated.sh b/container/.devcontainer/scripts/ensure-settings-generated.sh new file mode 100755 index 0000000..4c445c7 --- /dev/null +++ b/container/.devcontainer/scripts/ensure-settings-generated.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +# Fast stale check for generated Claude settings. Intended for setup and aliases. + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")" +WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +DEFAULT_SETTINGS_DIR="${DEVCONTAINER_DIR}/defaults/codeforge/claude/settings" +OVERRIDE_SETTINGS_DIR="${CODEFORGE_DIR}/claude/settings" +GENERATED_SETTINGS_DIR="${DEVCONTAINER_DIR}/.generated/codeforge/claude/settings" +MARKER="${CODEFORGE_DIR}/.markers/settings-generated-v3" + +FORCE=false +QUIET=false +for arg in "$@"; do + case "$arg" in + --force) FORCE=true ;; + --quiet) QUIET=true ;; + esac +done + +log() { + if [ "$QUIET" != "true" ]; then + echo "[ensure-settings-generated] $*" + fi +} + +is_stale() { + [ "$FORCE" = "true" ] && return 0 + [ ! -f "$MARKER" ] && return 0 + + local output + for output in \ + settings.json \ + settings-opus-46-200k.json \ + settings-opus-46-1m-400k.json \ + settings-opus-47-200k.json \ + settings-opus-47-1m-400k.json \ + settings-opus-45-200k.json; do + [ -f "${GENERATED_SETTINGS_DIR}/${output}" ] || return 0 + done + + if [ -d "$DEFAULT_SETTINGS_DIR" ] && find "$DEFAULT_SETTINGS_DIR" -type f -newer "$MARKER" -print -quit | grep -q .; then + return 0 + fi + + if [ -d "$OVERRIDE_SETTINGS_DIR" ] && find "$OVERRIDE_SETTINGS_DIR" -type f -newer "$MARKER" -print -quit | grep -q .; then + return 0 + fi + + # Fallback: check if generator script itself is newer than marker + if [ -f "$SCRIPT_DIR/generate-settings-profiles.js" ] && [ "$SCRIPT_DIR/generate-settings-profiles.js" -nt "$MARKER" ]; then + return 0 + fi + + return 1 +} + +mkdir -p "$CODEFORGE_DIR/.markers" "$GENERATED_SETTINGS_DIR" "$HOME/.claude" + +if ! is_stale; then + log "Generated Claude settings are current." + exit 0 +fi + +log "Generating Claude settings..." +if [ "$FORCE" = "true" ]; then + if ! node "$SCRIPT_DIR/generate-settings-profiles.js"; then + echo "[ensure-settings-generated] ERROR: Claude settings generation failed." >&2 + exit 1 + fi +else + if ! node "$SCRIPT_DIR/generate-settings-profiles.js" --if-stale; then + echo "[ensure-settings-generated] ERROR: Claude settings generation failed." >&2 + exit 1 + fi +fi + +log "Deploying generated Claude settings..." +if ! bash "$SCRIPT_DIR/setup-config.sh" --only-settings; then + echo "[ensure-settings-generated] ERROR: Claude settings deployment failed." >&2 + exit 1 +fi diff --git a/container/.devcontainer/scripts/generate-settings-profiles.js b/container/.devcontainer/scripts/generate-settings-profiles.js index b18489a..ad72b42 100755 --- a/container/.devcontainer/scripts/generate-settings-profiles.js +++ b/container/.devcontainer/scripts/generate-settings-profiles.js @@ -4,52 +4,53 @@ const fs = require("node:fs"); const path = require("node:path"); +const crypto = require("node:crypto"); -const repoRoot = path.resolve(__dirname, ".."); -const configDir = path.join(repoRoot, "defaults", "codeforge", "config"); +const devcontainerDir = path.resolve(__dirname, ".."); +const workspaceRoot = path.resolve( + process.env.WORKSPACE_ROOT || path.resolve(devcontainerDir, ".."), +); +const defaultsRoot = path.join(devcontainerDir, "defaults", "codeforge"); +const overrideRoot = path.resolve( + process.env.CODEFORGE_DIR || path.join(workspaceRoot, ".codeforge"), +); +const generatedRoot = path.join(devcontainerDir, ".generated", "codeforge"); +const generatedSettingsDir = path.join(generatedRoot, "claude", "settings"); +const markerFile = path.join(overrideRoot, ".markers", "settings-generated-v3"); +// isDefault marks which profile settings.json symlinks to. const profiles = [ - { - overlay: "opus-47-200k.json", - output: "settings.json", - }, - { - overlay: "opus-47-1m-400k.json", - output: "settings-opus-47-1m-400k.json", - }, { overlay: "opus-46-200k.json", output: "settings-opus-46-200k.json", + isDefault: true, }, { overlay: "opus-46-1m-400k.json", output: "settings-opus-46-1m-400k.json", }, + { + overlay: "opus-47-200k.json", + output: "settings-opus-47-200k.json", + }, + { + overlay: "opus-47-1m-400k.json", + output: "settings-opus-47-1m-400k.json", + }, { overlay: "opus-45-200k.json", output: "settings-opus-45-200k.json", }, ]; -function readJson(file) { - return JSON.parse(fs.readFileSync(file, "utf8")); -} - function isPlainObject(value) { - return ( - value !== null && - typeof value === "object" && - !Array.isArray(value) - ); + return value !== null && typeof value === "object" && !Array.isArray(value); } function merge(base, overlay) { const result = { ...base }; for (const [key, value] of Object.entries(overlay)) { - if ( - isPlainObject(value) && - isPlainObject(result[key]) - ) { + if (isPlainObject(value) && isPlainObject(result[key])) { result[key] = merge(result[key], value); } else { result[key] = value; @@ -58,20 +59,302 @@ function merge(base, overlay) { return result; } +// Expand _meta shorthand into model, env context/window fields, and 1M flag. +// Keeps overlay env on top of meta-derived env, so explicit env always wins. +function expandMeta(overlay) { + if (!overlay._meta) return overlay; + const { _meta, env: overlayEnv, ...rest } = overlay; + const { model, contextWindow } = _meta; + const isOneMillion = model.includes("[1m]"); + + const metaEnv = { + CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(contextWindow), + CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(contextWindow), + }; + if (!isOneMillion) { + metaEnv.CLAUDE_CODE_DISABLE_1M_CONTEXT = "1"; + } + + return { + model, + env: { ...metaEnv, ...(overlayEnv || {}) }, + ...rest, + }; +} + +function normalizeLegacyProfile(overlay) { + if (overlay._meta) return overlay; + const model = overlay.model || ""; + const env = overlay.env || {}; + if (!model.includes("[1m]") && !("CLAUDE_CODE_DISABLE_1M_CONTEXT" in env)) { + return { + ...overlay, + env: { + ...env, + CLAUDE_CODE_DISABLE_1M_CONTEXT: "1", + }, + }; + } + return overlay; +} + +function readJson(file) { + return JSON.parse(fs.readFileSync(file, "utf8")); +} + function writeJson(file, data) { + fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, `${JSON.stringify(data, null, "\t")}\n`); } -function main() { - const base = readJson(path.join(configDir, "settings.base.json")); - const profileDir = path.join(configDir, "settings-profiles"); +function sha256(file) { + return crypto + .createHash("sha256") + .update(fs.readFileSync(file)) + .digest("hex"); +} + +function statMtime(file) { + try { + return fs.statSync(file).mtimeMs; + } catch { + return 0; + } +} + +function resolveInput(relativePath) { + const overridePath = path.join(overrideRoot, relativePath); + if (fs.existsSync(overridePath)) { + return { + path: overridePath, + source: "override", + relativePath, + }; + } + return { + path: path.join(defaultsRoot, relativePath), + source: "default", + relativePath, + }; +} + +function getInputs() { + const inputs = [resolveInput("claude/settings/base.json")]; + for (const profile of profiles) { + inputs.push( + resolveInput( + path.join("claude", "settings", "profiles", profile.overlay), + ), + ); + } + const seen = new Map(); + for (const input of inputs) { + seen.set(input.path, input); + } + return [...seen.values()]; +} + +function formatInput(input) { + return `${input.source} ${input.path}`; +} + +function validateProfile(profile, expanded, overlayInput) { + const env = expanded.env || {}; + const isOneMillion = (expanded.model || "").includes("[1m]"); + const hasDisable = env.CLAUDE_CODE_DISABLE_1M_CONTEXT === "1"; + if (!isOneMillion && !hasDisable) { + throw new Error( + `Profile ${profile.output} (overlay: ${profile.overlay}, source: ${formatInput(overlayInput)}): must set CLAUDE_CODE_DISABLE_1M_CONTEXT=1`, + ); + } + if (isOneMillion && "CLAUDE_CODE_DISABLE_1M_CONTEXT" in env) { + throw new Error( + `Profile ${profile.output} (overlay: ${profile.overlay}, source: ${formatInput(overlayInput)}): must not set CLAUDE_CODE_DISABLE_1M_CONTEXT`, + ); + } +} + +function buildGenerated() { + const baseInput = resolveInput("claude/settings/base.json"); + const base = readJson(baseInput.path); + const outputs = []; for (const profile of profiles) { - const overlay = readJson(path.join(profileDir, profile.overlay)); - const settings = merge(base, overlay); - writeJson(path.join(configDir, profile.output), settings); - console.log(`generated ${profile.output}`); + const overlayInput = resolveInput( + path.join("claude", "settings", "profiles", profile.overlay), + ); + const rawOverlay = readJson(overlayInput.path); + const expanded = normalizeLegacyProfile(expandMeta(rawOverlay)); + validateProfile(profile, expanded, overlayInput); + const settings = merge(base, expanded); + outputs.push({ + file: path.join(generatedSettingsDir, profile.output), + profile, + settings, + }); + } + + return outputs; +} + +// Write settings.json as a symlink to the default profile. +// Falls back to a file copy on platforms that don't support symlinks. +function writeDefaultLink(defaultProfile) { + const symlinkPath = path.join(generatedSettingsDir, "settings.json"); + try { + fs.unlinkSync(symlinkPath); + } catch { + /* not present */ + } + try { + fs.symlinkSync(defaultProfile.output, symlinkPath); + console.log(`symlinked settings.json -> ${defaultProfile.output}`); + } catch { + fs.copyFileSync( + path.join(generatedSettingsDir, defaultProfile.output), + symlinkPath, + ); + console.log( + `copied settings.json from ${defaultProfile.output} (symlink unavailable)`, + ); + } +} + +// Returns true if settings.json is the correct symlink or an identical copy. +function isDefaultLinkValid(defaultProfile) { + const symlinkPath = path.join(generatedSettingsDir, "settings.json"); + if (!fs.existsSync(symlinkPath)) return false; + try { + return fs.readlinkSync(symlinkPath) === defaultProfile.output; + } catch { + const a = fs.readFileSync(symlinkPath, "utf8"); + const b = fs.readFileSync( + path.join(generatedSettingsDir, defaultProfile.output), + "utf8", + ); + return a === b; + } +} + +function readMarker() { + try { + return JSON.parse(fs.readFileSync(markerFile, "utf8")); + } catch { + return null; + } +} + +function isStale() { + const marker = readMarker(); + if (!marker) return true; + + const markerTime = statMtime(markerFile); + for (const input of getInputs()) { + if (!fs.existsSync(input.path)) return true; + if (statMtime(input.path) > markerTime) return true; + } + + for (const profile of profiles) { + if (!fs.existsSync(path.join(generatedSettingsDir, profile.output))) { + return true; + } + } + + const defaultProfile = profiles.find((p) => p.isDefault); + if (defaultProfile && !isDefaultLinkValid(defaultProfile)) return true; + + return false; +} + +function writeMarker() { + fs.mkdirSync(path.dirname(markerFile), { recursive: true }); + const inputs = {}; + for (const input of getInputs()) { + inputs[input.relativePath] = { + path: input.path, + source: input.source, + sha256: fs.existsSync(input.path) ? sha256(input.path) : null, + }; + } + const outputs = {}; + for (const profile of profiles) { + const file = path.join(generatedSettingsDir, profile.output); + outputs[`claude/settings/${profile.output}`] = { + path: file, + sha256: fs.existsSync(file) ? sha256(file) : null, + }; + } + const defaultProfile = profiles.find((p) => p.isDefault); + if (defaultProfile) { + outputs["claude/settings/settings.json"] = { + path: path.join(generatedSettingsDir, "settings.json"), + default: defaultProfile.output, + }; + } + writeJson(markerFile, { + version: 3, + generatedAt: new Date().toISOString(), + defaultProfile: defaultProfile?.output ?? null, + inputs, + outputs, + }); +} + +function checkGenerated() { + const outputs = buildGenerated(); + const diffs = []; + for (const output of outputs) { + if (!fs.existsSync(output.file)) { + diffs.push(`${output.profile.output}: missing`); + continue; + } + const actual = readJson(output.file); + if (JSON.stringify(actual) !== JSON.stringify(output.settings)) { + diffs.push(`${path.basename(output.file)} is stale`); + } + } + + const defaultProfile = profiles.find((p) => p.isDefault); + if (defaultProfile && !isDefaultLinkValid(defaultProfile)) { + diffs.push( + `settings.json is missing or does not match ${defaultProfile.output}`, + ); + } + + if (diffs.length > 0) { + for (const diff of diffs) { + console.error(diff); + } + process.exit(1); + } + console.log("settings profiles are current"); +} + +function writeGenerated() { + const outputs = buildGenerated(); + for (const output of outputs) { + writeJson(output.file, output.settings); + console.log(`generated ${path.relative(devcontainerDir, output.file)}`); + } + + const defaultProfile = profiles.find((p) => p.isDefault); + if (defaultProfile) { + writeDefaultLink(defaultProfile); + } + + writeMarker(); +} + +function main() { + const args = new Set(process.argv.slice(2)); + if (args.has("--check")) { + checkGenerated(); + return; + } + if (args.has("--if-stale") && !isStale()) { + return; } + writeGenerated(); } if (require.main === module) { diff --git a/container/.devcontainer/scripts/setup-aliases.sh b/container/.devcontainer/scripts/setup-aliases.sh index 339832c..9e1fe5a 100755 --- a/container/.devcontainer/scripts/setup-aliases.sh +++ b/container/.devcontainer/scripts/setup-aliases.sh @@ -6,8 +6,6 @@ # Idempotent: removes the entire managed block then re-writes it fresh. # Safe to run on every container start via postStartCommand. -CLAUDE_DIR="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}" - echo "[setup-aliases] Configuring Claude aliases..." # Resolve check-setup path once (used inside the block we write) @@ -24,7 +22,8 @@ for rc in ~/.bashrc ~/.zshrc; do ls -t "${rc}.bak."* 2>/dev/null | tail -n +4 | xargs rm -f 2>/dev/null || true # --- 2. Remove existing managed block (if present) --- - sed -i '/# === CodeForge Claude aliases START/,/# === CodeForge Claude aliases END/d' "$rc" + tmp="$(mktemp)" + sed '/# === CodeForge Claude aliases START/,/# === CodeForge Claude aliases END/d' "$rc" > "$tmp" && mv "$tmp" "$rc" # --- 3. Legacy cleanup (pre-marker formats only) --- # These remove remnants from versions that predated the block-marker system. @@ -97,8 +96,11 @@ for rc in ~/.bashrc ~/.zshrc; do cat >>"$rc" <&2 + return 1 + } +} + _codeforge_claude_profile() { local settings_file="\$1" local prompt_file="\$2" shift 2 + _codeforge_ensure_settings || return \$? CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 "\$_CLAUDE_WRAP" "\$_CLAUDE_BIN" \\ --settings "\$settings_file" \\ --system-prompt-file "\$prompt_file" \\ @@ -149,27 +159,27 @@ _codeforge_claude_profile() { "\$@" } -cc() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } +cc() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } claude() { cc "\$@"; } -cc5() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-45-200k.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } -cc6() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-200k.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } -cc61() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-1m-400k.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } -cc7() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } -cc71() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-47-1m-400k.json" "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" "\$@"; } - -ccw() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } -ccw5() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-45-200k.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } -ccw6() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-200k.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } -ccw61() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-1m-400k.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } -ccw7() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } -ccw71() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-47-1m-400k.json" "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" "\$@"; } - -cc-orc() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } -cc-orc5() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-45-200k.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } -cc-orc6() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-200k.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } -cc-orc61() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-46-1m-400k.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } -cc-orc7() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } -cc-orc71() { _codeforge_claude_profile "\$CLAUDE_CONFIG_DIR/settings-opus-47-1m-400k.json" "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" "\$@"; } +cc5() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-45-200k.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } +cc6() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-200k.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } +cc61() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-1m-400k.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } +cc7() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-200k.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } +cc71() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-1m-400k.json" "\$_CLAUDE_DIR/main-system-prompt.md" "\$@"; } + +ccw() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } +ccw5() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-45-200k.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } +ccw6() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-200k.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } +ccw61() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-1m-400k.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } +ccw7() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-200k.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } +ccw71() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-1m-400k.json" "\$_CLAUDE_DIR/writing-system-prompt.md" "\$@"; } + +cc-orc() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } +cc-orc5() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-45-200k.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } +cc-orc6() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-200k.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } +cc-orc61() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-46-1m-400k.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } +cc-orc7() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-200k.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } +cc-orc71() { _codeforge_claude_profile "\$_CLAUDE_DIR/settings-opus-47-1m-400k.json" "\$_CLAUDE_DIR/orchestrator-system-prompt.md" "\$@"; } alias ccr-apply='codeforge config apply && (ccr restart 2>/dev/null || ccr start) && echo "CCR config applied and restarted"' alias omc-doctor='omc doctor --detail' omc-cc() { @@ -191,7 +201,7 @@ cc-tools() { printf " %-20s %s\n" "COMMAND" "STATUS" echo " ────────────────────────────────────" for cmd in claude cc cc5 cc6 cc61 cc7 cc71 ccw ccw5 ccw6 ccw61 ccw7 ccw71 ccraw cc-orc cc-orc5 cc-orc6 cc-orc61 cc-orc7 cc-orc71 codeforge ccr omc omc-cc ccusage ccburn claude-monitor karma-status karma-live-session-tracker karma-title-generator codex ccusage-codex \\ - ct cargo ruff biome dprint shfmt shellcheck hadolint \\ + rtk ct cargo ruff biome dprint shfmt shellcheck hadolint \\ ast-grep tree-sitter pyright typescript-language-server \\ agent-browser gh docker git jq tmux bun go infocmp; do if command -v "\$cmd" >/dev/null 2>&1; then @@ -212,13 +222,13 @@ BLOCK_EOF done echo "[setup-aliases] Aliases configured:" -echo " cc/claude -> claude (opus-4-7, 200k ctx) with \$CLAUDE_CONFIG_DIR/main-system-prompt.md" +echo " cc/claude -> claude (opus-4-6, 200k ctx) with \$_CLAUDE_DIR/main-system-prompt.md" echo " ccraw -> vanilla claude without any config" echo " cc5 -> claude (opus-4-5, 200k ctx)" echo " cc6/cc61 -> claude (opus-4-6, 200k ctx / 1m bounded to 400k)" echo " cc7/cc71 -> claude (opus-4-7, 200k ctx / 1m bounded to 400k)" -echo " ccw* -> same model profiles with \$CLAUDE_CONFIG_DIR/writing-system-prompt.md" -echo " cc-orc* -> same model profiles with \$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" +echo " ccw* -> same model profiles with \$_CLAUDE_DIR/writing-system-prompt.md" +echo " cc-orc* -> same model profiles with \$_CLAUDE_DIR/orchestrator-system-prompt.md" echo " ccr-apply -> redeploy claude-code-router config + restart daemon" echo " omc-cc -> launch Claude Code through oh-my-claude when installed" echo " omc-doctor -> diagnose oh-my-claude when installed" diff --git a/container/.devcontainer/scripts/setup-auth.sh b/container/.devcontainer/scripts/setup-auth.sh index 8e937c6..60fab14 100755 --- a/container/.devcontainer/scripts/setup-auth.sh +++ b/container/.devcontainer/scripts/setup-auth.sh @@ -1,128 +1,177 @@ #!/bin/bash # SPDX-License-Identifier: GPL-3.0-only # Copyright (c) 2026 Marcus Krueger -# Configure Git (GitHub CLI) and NPM authentication from .secrets file or environment variables. -# Environment variables override .secrets values, supporting Codespaces secrets and localEnv. +# Configure authentication from Docker Compose secrets or environment variables. +# +# Secret resolution: env var (Codespaces) → /run/secrets/ (Docker Compose) → skip. # Auth failure should not block other setup steps, so set -e is intentionally omitted. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")" -SECRETS_FILE="$DEVCONTAINER_DIR/.secrets" - -# Source .secrets file if it exists (env vars take precedence via :- defaults below) -if [ -f "$SECRETS_FILE" ]; then - echo "[setup-auth] Loading tokens from .secrets file" - set -a - source "$SECRETS_FILE" - set +a -else - echo "[setup-auth] No .secrets file found, using environment variables only" -fi +WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +CONFIG_FILE="${CODEFORGE_DIR}/container.json" + +_USERNAME="${SUDO_USER:-${USER:-vscode}}" +_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) +_USER_HOME="${_USER_HOME:-/home/$_USERNAME}" + +# --- Secret reader --- +# Reads a secret from env var (Codespaces) or /run/secrets/ (Docker Compose). +# Usage: value=$(read_secret ) +read_secret() { + local name="$1" env_name="$2" + if [ -n "${!env_name:-}" ]; then + printf '%s' "${!env_name}" + return 0 + fi + local f="/run/secrets/$name" + if [ -f "$f" ]; then + tr -d '\n' < "$f" + return 0 + fi + return 1 +} + +# --- Identity override from container.json --- +jq_val() { + [ -f "$CONFIG_FILE" ] && jq -r "$1" "$CONFIG_FILE" 2>/dev/null || echo "" +} AUTH_CONFIGURED=false # --- GitHub CLI auth --- -if [ -n "$GH_TOKEN" ]; then +if _gh_token=$(read_secret gh_token GH_TOKEN); then echo "[setup-auth] Authenticating GitHub CLI..." - # Capture token value then unset env var — gh refuses --with-token when - # GH_TOKEN is already exported (it says "use the env var instead"). - _gh_token="$GH_TOKEN" + # Unset GH_TOKEN before login — gh refuses --with-token when GH_TOKEN is exported unset GH_TOKEN if gh auth login --with-token <<< "$_gh_token" 2>/dev/null; then echo "[setup-auth] GitHub CLI authenticated" gh auth setup-git 2>/dev/null && echo "[setup-auth] Git credential helper configured" AUTH_CONFIGURED=true + + # Derive git identity from GitHub API + _identity_name=$(jq_val '.identity.name // empty') + _identity_email=$(jq_val '.identity.email // empty') + + if [ -z "$_identity_name" ]; then + _identity_name=$(gh api user -q .login 2>/dev/null || true) + fi + if [ -z "$_identity_email" ]; then + _identity_email=$(gh api user/emails -q '.[] | select(.primary) | .email' 2>/dev/null || true) + if [ -z "$_identity_email" ]; then + _gh_id=$(gh api user -q .id 2>/dev/null || true) + if [ -n "$_gh_id" ] && [ -n "$_identity_name" ]; then + _identity_email="${_gh_id}+${_identity_name}@users.noreply.github.com" + fi + fi + fi + + if [ -n "$_identity_name" ]; then + git config --global user.name "$_identity_name" + echo "[setup-auth] Git user.name set to $_identity_name" + fi + if [ -n "$_identity_email" ]; then + git config --global user.email "$_identity_email" + echo "[setup-auth] Git user.email set to $_identity_email" + fi else echo "[setup-auth] WARNING: GitHub CLI authentication failed" fi - unset _gh_token + unset _gh_token _identity_name _identity_email _gh_id else echo "[setup-auth] GH_TOKEN not set, skipping GitHub CLI auth" fi -# --- Git user config --- -if [ -n "$GH_USERNAME" ]; then - git config --global user.name "$GH_USERNAME" - echo "[setup-auth] Git user.name set to $GH_USERNAME" - unset GH_USERNAME -fi - -if [ -n "$GH_EMAIL" ]; then - git config --global user.email "$GH_EMAIL" - echo "[setup-auth] Git user.email set to $GH_EMAIL" - unset GH_EMAIL -fi - # --- NPM auth --- -if [ -n "$NPM_TOKEN" ]; then +if _npm_token=$(read_secret npm_token NPM_TOKEN); then echo "[setup-auth] Configuring NPM registry auth..." - if npm config set "//registry.npmjs.org/:_authToken=$NPM_TOKEN" 2>/dev/null; then + if npm config set "//registry.npmjs.org/:_authToken=$_npm_token" 2>/dev/null; then echo "[setup-auth] NPM auth token configured" AUTH_CONFIGURED=true else echo "[setup-auth] WARNING: NPM auth configuration failed" fi - unset NPM_TOKEN + unset _npm_token else echo "[setup-auth] NPM_TOKEN not set, skipping NPM auth" fi -# --- Claude auth token (from 'claude setup-token') --- -# Long-lived tokens only — generated via: claude setup-token -# Note: After unset, the token remains visible in /proc//environ for the -# lifetime of this process. This is a platform limitation of environment variables. -_USERNAME="${SUDO_USER:-${USER:-vscode}}" -_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) -_USER_HOME="${_USER_HOME:-/home/$_USERNAME}" -CLAUDE_CRED_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}" -CLAUDE_CRED_FILE="$CLAUDE_CRED_DIR/.credentials.json" -if [ -n "$CLAUDE_AUTH_TOKEN" ]; then - # Validate token format (claude setup-token produces sk-ant-* tokens) - if [[ ! "$CLAUDE_AUTH_TOKEN" =~ ^sk-ant- ]]; then - echo "[setup-auth] WARNING: CLAUDE_AUTH_TOKEN doesn't match expected format (sk-ant-*), skipping" - elif [ -f "$CLAUDE_CRED_FILE" ]; then - echo "[setup-auth] .credentials.json already exists, skipping token injection" - # Verify permissions haven't been tampered with - perms=$(stat -c %a "$CLAUDE_CRED_FILE" 2>/dev/null) - if [ -n "$perms" ] && [ "$perms" != "600" ]; then - echo "[setup-auth] WARNING: .credentials.json has permissions $perms (expected 600), fixing" - chmod 600 "$CLAUDE_CRED_FILE" - fi - AUTH_CONFIGURED=true - else - echo "[setup-auth] Creating .credentials.json from CLAUDE_AUTH_TOKEN..." - # Create directory with restrictive permissions (matches credential file at 600) - ( umask 077; mkdir -p "$CLAUDE_CRED_DIR" ) - # Escape JSON-special characters in token value (defense against malformed JSON - # if a token ever contains " or \ — unlikely with sk-ant-* but closes the gap) - ESCAPED_TOKEN=$(printf '%s' "$CLAUDE_AUTH_TOKEN" | sed 's/\\/\\\\/g; s/"/\\"/g') - # Write credentials with restrictive permissions from the start (no race window). - # Uses printf '%s' to avoid shell expansion of token value (defense against - # metacharacters in the token string — backticks, $(), quotes). - if ( umask 077; printf '{\n "claudeAiOauth": {\n "accessToken": "%s",\n "refreshToken": "%s",\n "expiresAt": 9999999999999,\n "scopes": ["user:inference", "user:profile"]\n }\n}\n' "$ESCAPED_TOKEN" "$ESCAPED_TOKEN" > "$CLAUDE_CRED_FILE" ); then - echo "[setup-auth] Claude auth token configured" - AUTH_CONFIGURED=true +# --- Claude Code OAuth token --- +# CLAUDE_CODE_OAUTH_TOKEN is Claude Code's native env var for headless/CI auth. +# WARNING: CLAUDE_CODE_OAUTH_TOKEN does not work when ANTHROPIC_API_KEY is set. +if _claude_token=$(read_secret claude_code_oauth_token CLAUDE_CODE_OAUTH_TOKEN); then + echo "[setup-auth] Configuring Claude Code OAuth token..." + + for rc in "${_USER_HOME}/.bashrc" "${_USER_HOME}/.zshrc"; do + [ -f "$rc" ] || continue + grep -q "export CLAUDE_CODE_OAUTH_TOKEN=" "$rc" 2>/dev/null || \ + printf 'export CLAUDE_CODE_OAUTH_TOKEN=%q\n' "$_claude_token" >> "$rc" + done + + # Claude Code documents CLAUDE_CODE_OAUTH_TOKEN for setup-token auth, but + # some Linux/container first-run paths do not honor it reliably. Also write + # the same token to the native Linux credential file shape. + _CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}" + _CLAUDE_CREDENTIALS_FILE="${_CLAUDE_DIR}/.credentials.json" + _CLAUDE_EXPIRES_AT="$(($(date +%s) * 1000 + 365 * 24 * 60 * 60 * 1000))" + if command -v jq >/dev/null 2>&1; then + ( umask 077; mkdir -p "$_CLAUDE_DIR" ) + _CLAUDE_CREDENTIALS_TMP="$(mktemp)" + if [ -f "$_CLAUDE_CREDENTIALS_FILE" ] && jq empty "$_CLAUDE_CREDENTIALS_FILE" 2>/dev/null; then + jq \ + --arg token "$_claude_token" \ + --argjson expiresAt "$_CLAUDE_EXPIRES_AT" \ + '.claudeAiOauth = ((.claudeAiOauth // {}) + { + accessToken: $token, + refreshToken: (.claudeAiOauth.refreshToken // ""), + expiresAt: $expiresAt, + scopes: (.claudeAiOauth.scopes // ["user:inference", "user:profile", "user:sessions:claude_code"]) + })' \ + "$_CLAUDE_CREDENTIALS_FILE" > "$_CLAUDE_CREDENTIALS_TMP" else - echo "[setup-auth] WARNING: Failed to write .credentials.json — check permissions on $CLAUDE_CRED_DIR" + jq -n \ + --arg token "$_claude_token" \ + --argjson expiresAt "$_CLAUDE_EXPIRES_AT" \ + '{ + claudeAiOauth: { + accessToken: $token, + refreshToken: "", + expiresAt: $expiresAt, + scopes: ["user:inference", "user:profile", "user:sessions:claude_code"] + } + }' > "$_CLAUDE_CREDENTIALS_TMP" fi + install -m 600 "$_CLAUDE_CREDENTIALS_TMP" "$_CLAUDE_CREDENTIALS_FILE" + rm -f "$_CLAUDE_CREDENTIALS_TMP" + chown "$_USERNAME:$_USERNAME" "$_CLAUDE_CREDENTIALS_FILE" 2>/dev/null || true + echo "[setup-auth] Claude Code credentials file configured" + else + echo "[setup-auth] WARNING: jq not found; skipped Claude Code credentials file" fi - unset CLAUDE_AUTH_TOKEN + unset _CLAUDE_DIR _CLAUDE_CREDENTIALS_FILE _CLAUDE_CREDENTIALS_TMP _CLAUDE_EXPIRES_AT + + echo "[setup-auth] CLAUDE_CODE_OAUTH_TOKEN configured" + AUTH_CONFIGURED=true + + # Check for ANTHROPIC_API_KEY conflict + if _anthropic_key=$(read_secret anthropic_api_key ANTHROPIC_API_KEY 2>/dev/null); then + echo "[setup-auth] WARNING: Both CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY are set." + echo "[setup-auth] CLAUDE_CODE_OAUTH_TOKEN will not work when ANTHROPIC_API_KEY is present." + echo "[setup-auth] Remove the anthropic_api_key secret if you want OAuth token auth." + fi + unset _claude_token else - echo "[setup-auth] CLAUDE_AUTH_TOKEN not set, skipping Claude auth" + echo "[setup-auth] CLAUDE_CODE_OAUTH_TOKEN not set, skipping Claude auth" fi +unset _anthropic_key # --- OpenAI Codex auth (API key) --- -# For Codex CLI: set OPENAI_API_KEY in .secrets or as an environment variable. -# Browser-based ChatGPT OAuth is also supported — run `codex` interactively. _CODEX_DIR="${CODEX_HOME:-${_USER_HOME}/.codex}" _CODEX_AUTH_FILE="$_CODEX_DIR/auth.json" -if [ -n "$OPENAI_API_KEY" ]; then - # Validate token format (OpenAI API keys start with sk-) - if [[ ! "$OPENAI_API_KEY" =~ ^sk- ]]; then - echo "[setup-auth] WARNING: OPENAI_API_KEY doesn't match expected format (sk-*), skipping" - elif [ -f "$_CODEX_AUTH_FILE" ]; then +if _openai_key=$(read_secret openai_api_key OPENAI_API_KEY); then + if [ -f "$_CODEX_AUTH_FILE" ]; then echo "[setup-auth] Codex auth.json already exists, skipping token injection" - # Verify permissions perms=$(stat -c %a "$_CODEX_AUTH_FILE" 2>/dev/null) if [ -n "$perms" ] && [ "$perms" != "600" ]; then echo "[setup-auth] WARNING: Codex auth.json has permissions $perms (expected 600), fixing" @@ -132,46 +181,52 @@ if [ -n "$OPENAI_API_KEY" ]; then else echo "[setup-auth] Creating Codex auth.json from OPENAI_API_KEY..." ( umask 077; mkdir -p "$_CODEX_DIR" ) - ESCAPED_KEY=$(printf '%s' "$OPENAI_API_KEY" | sed 's/\\/\\\\/g; s/"/\\"/g') - if ( umask 077; printf '{\n "auth_mode": "apikey",\n "OPENAI_API_KEY": "%s"\n}\n' "$ESCAPED_KEY" > "$_CODEX_AUTH_FILE" ); then + if command -v jq >/dev/null 2>&1; then + ( umask 077; jq -n --arg key "$_openai_key" '{auth_mode: "apikey", OPENAI_API_KEY: $key}' > "$_CODEX_AUTH_FILE" ) + else + ESCAPED_KEY=$(printf '%s' "$_openai_key" | sed 's/\\/\\\\/g; s/"/\\"/g') + ( umask 077; printf '{\n "auth_mode": "apikey",\n "OPENAI_API_KEY": "%s"\n}\n' "$ESCAPED_KEY" > "$_CODEX_AUTH_FILE" ) + fi + if [ -f "$_CODEX_AUTH_FILE" ]; then echo "[setup-auth] Codex API key configured" AUTH_CONFIGURED=true else echo "[setup-auth] WARNING: Failed to write Codex auth.json — check permissions on $_CODEX_DIR" fi fi - unset OPENAI_API_KEY + unset _openai_key ESCAPED_KEY else echo "[setup-auth] OPENAI_API_KEY not set, skipping Codex auth" fi # --- Claude Code Router provider keys --- -# Export provider API keys for CCR's $ENV_VAR interpolation in config.json. -# Unlike Claude/Codex tokens which write to credential files then unset, -# CCR keys must persist as env vars because CCR reads them at runtime. +# CCR reads env vars at runtime via $ENV_VAR interpolation in config.json. +# Keys must persist as env vars in shell rc files. _CCR_KEY_CONFIGURED=false for _CCR_VAR in ANTHROPIC_API_KEY DEEPSEEK_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY; do - if [ -n "${!_CCR_VAR:-}" ]; then - # Write to shell rc files so they're available in interactive shells - grep -q "export ${_CCR_VAR}=" "${_USER_HOME}/.bashrc" 2>/dev/null || \ - echo "export ${_CCR_VAR}=\"${!_CCR_VAR}\"" >> "${_USER_HOME}/.bashrc" - grep -q "export ${_CCR_VAR}=" "${_USER_HOME}/.zshrc" 2>/dev/null || \ - echo "export ${_CCR_VAR}=\"${!_CCR_VAR}\"" >> "${_USER_HOME}/.zshrc" - echo "[setup-auth] ✓ ${_CCR_VAR} configured for claude-code-router" + _secret_name=$(echo "$_CCR_VAR" | tr '[:upper:]' '[:lower:]') + if _ccr_val=$(read_secret "$_secret_name" "$_CCR_VAR"); then + for rc in "${_USER_HOME}/.bashrc" "${_USER_HOME}/.zshrc"; do + [ -f "$rc" ] || continue + grep -q "export ${_CCR_VAR}=" "$rc" 2>/dev/null || \ + echo "export ${_CCR_VAR}=\"${_ccr_val}\"" >> "$rc" + done + echo "[setup-auth] ${_CCR_VAR} configured for claude-code-router" _CCR_KEY_CONFIGURED=true + unset _ccr_val fi done if [ "$_CCR_KEY_CONFIGURED" = true ]; then AUTH_CONFIGURED=true else - echo "[setup-auth] No claude-code-router provider keys set (ANTHROPIC_API_KEY, DEEPSEEK_API_KEY, etc.)" + echo "[setup-auth] No claude-code-router provider keys set" fi -unset _CCR_VAR _CCR_KEY_CONFIGURED +unset _CCR_VAR _CCR_KEY_CONFIGURED _secret_name # --- Summary --- if [ "$AUTH_CONFIGURED" = true ]; then echo "[setup-auth] Auth configuration complete" else - echo "[setup-auth] No tokens provided — auth configuration skipped" - echo "[setup-auth] To configure, copy .secrets.example to .secrets and fill in your tokens" + echo "[setup-auth] No secrets provided — auth configuration skipped" + echo "[setup-auth] To configure, add secret files to .codeforge/secrets/ (one file per secret)" fi diff --git a/container/.devcontainer/scripts/setup-config.sh b/container/.devcontainer/scripts/setup-config.sh index c4c9cbe..e587a88 100755 --- a/container/.devcontainer/scripts/setup-config.sh +++ b/container/.devcontainer/scripts/setup-config.sh @@ -1,142 +1,250 @@ #!/bin/bash # SPDX-License-Identifier: GPL-3.0-only # Copyright (c) 2026 Marcus Krueger -# Copy configuration files to workspace based on file-manifest.json +# Deploy effective CodeForge configuration from packaged defaults plus +# optional .codeforge/ overrides. + +set -uo pipefail log() { echo "[setup-config] $*"; } warn() { echo "[setup-config] WARNING: $*"; } err() { echo "[setup-config] ERROR: $*" >&2; } -CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT:?}/.codeforge}" -CONFIG_DIR="$CODEFORGE_DIR" -MANIFEST="$CODEFORGE_DIR/file-manifest.json" - -# Legacy fallback: if .codeforge/ doesn't exist but old config dir does, warn and use old path -if [ ! -d "$CODEFORGE_DIR" ] && [ -d "${WORKSPACE_ROOT}/.devcontainer/config/defaults" ]; then - warn ".codeforge/ not found — falling back to .devcontainer/config/defaults (deprecated)" - CONFIG_DIR="${WORKSPACE_ROOT}/.devcontainer/config" - MANIFEST="$CONFIG_DIR/file-manifest.json" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")" +WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +DEFAULTS_DIR="${DEVCONTAINER_DIR}/defaults/codeforge" +GENERATED_DIR="${DEVCONTAINER_DIR}/.generated/codeforge" +DEFAULT_MANIFEST="${DEFAULTS_DIR}/file-manifest.json" +USER_MANIFEST="${CODEFORGE_DIR}/file-manifest.json" +ONLY_ID_PREFIX="" + +if [ "${1:-}" = "--only-settings" ]; then + ONLY_ID_PREFIX="claude.settings." fi -# Deprecation notice if legacy OVERWRITE_CONFIG is still set if [ -n "${OVERWRITE_CONFIG+x}" ]; then - warn "OVERWRITE_CONFIG is deprecated. Use per-file 'overwrite' in config/file-manifest.json instead." + warn "OVERWRITE_CONFIG is deprecated. Use per-file 'overwrite' in file-manifest.json instead." fi -# ── Legacy fallback ────────────────────────────────────────────── -legacy_copy() { - local target_dir="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}" - warn "file-manifest.json not found, falling back to legacy copy" - mkdir -p "$target_dir" - for file in "$CONFIG_DIR"/config/settings*.json "$CONFIG_DIR"/config/keybindings.json "$CONFIG_DIR"/config/main-system-prompt.md; do - [ -f "$file" ] || continue - local basename="${file##*/}" - cp "$file" "$target_dir/$basename" - chown "$(id -un):$(id -gn)" "$target_dir/$basename" 2>/dev/null || true - log "Copied $basename (legacy)" - done - log "Configuration complete (legacy)" -} +if [ ! -f "$DEFAULT_MANIFEST" ]; then + err "Default file-manifest.json not found at $DEFAULT_MANIFEST" + exit 1 +fi -if [ ! -f "$MANIFEST" ]; then - legacy_copy - exit 0 +if ! jq empty "$DEFAULT_MANIFEST" 2>/dev/null; then + err "Invalid JSON in $DEFAULT_MANIFEST" + exit 1 fi -# ── Validate manifest JSON ────────────────────────────────────── -if ! jq empty "$MANIFEST" 2>/dev/null; then - err "Invalid JSON in file-manifest.json" +if [ -f "$USER_MANIFEST" ] && ! jq empty "$USER_MANIFEST" 2>/dev/null; then + err "Invalid JSON in $USER_MANIFEST" exit 1 fi -# ── Variable expansion ─────────────────────────────────────────── +if [ "$(jq 'all(.[]; has("id"))' "$DEFAULT_MANIFEST")" != "true" ]; then + err "Default manifest entries must include stable id fields" + exit 1 +fi + +effective_manifest="$(mktemp)" +trap 'rm -f "$effective_manifest"' EXIT + +if [ -f "$USER_MANIFEST" ]; then + jq -s ' + def entry_id: .id // .src; + reduce (.[0] + .[1])[] as $entry ({}; + ($entry | entry_id) as $id + | .[$id] = ((.[$id] // {}) + $entry) + | if ($entry.disabled == true) then .[$id].enabled = false else . end + ) + | [ .[] ] + ' "$DEFAULT_MANIFEST" "$USER_MANIFEST" > "$effective_manifest" +else + cp "$DEFAULT_MANIFEST" "$effective_manifest" +fi + +[ "${DEBUG:-}" = "1" ] && log "Effective manifest: $(cat "$effective_manifest")" + expand_vars() { local val="$1" - val="${val//\$\{CLAUDE_CONFIG_DIR\}/$CLAUDE_CONFIG_DIR}" - val="${val//\$\{WORKSPACE_ROOT\}/$WORKSPACE_ROOT}" - val="${val//\$\{HOME\}/$HOME}" - # Warn on any remaining unresolved ${...} tokens + # Escape replacement values to prevent bash pattern interpretation + local safe_workspace + safe_workspace="$(printf '%s' "$WORKSPACE_ROOT" | sed 's/[\/&]/\\&/g')" + local safe_home + safe_home="$(printf '%s' "${HOME:-/home/vscode}" | sed 's/[\/&]/\\&/g')" + local safe_codeforge + safe_codeforge="$(printf '%s' "$CODEFORGE_DIR" | sed 's/[\/&]/\\&/g')" + val="$(printf '%s' "$val" | sed "s|\${WORKSPACE_ROOT}|${safe_workspace}|g")" + val="$(printf '%s' "$val" | sed "s|\${CODEFORGE_DIR}|${safe_codeforge}|g")" + val="$(printf '%s' "$val" | sed "s|\${HOME}|${safe_home}|g")" if [[ "$val" =~ \$\{[^}]+\} ]]; then warn "Unresolved variable in: $val" fi echo "$val" } -# ── Change detection ───────────────────────────────────────────── +resolve_source() { + local src="$1" + local candidate + for root in "$CODEFORGE_DIR" "$GENERATED_DIR" "$DEFAULTS_DIR"; do + candidate="${root}/${src}" + if [ -f "$candidate" ]; then + echo "$candidate" + return 0 + fi + done + return 1 +} + should_copy() { local src="$1" dest="$2" [ ! -f "$dest" ] && return 0 local src_hash dest_hash - src_hash=$(sha256sum "$src" | cut -d' ' -f1) - dest_hash=$(sha256sum "$dest" | cut -d' ' -f1) + src_hash="$(sha256sum "$src" | cut -d' ' -f1)" + dest_hash="$(sha256sum "$dest" | cut -d' ' -f1)" [ "$src_hash" != "$dest_hash" ] } -# ── Process manifest ───────────────────────────────────────────── -log "Copying configuration files..." +repair_claude_state() { + local target="${HOME:-/home/vscode}/.claude.json" + local legacy="${HOME:-/home/vscode}/.claude/.claude.json" + local defaults="${DEFAULTS_DIR}/claude/claude.json" + local legacy_input defaults_input tmp + + [ -f "$defaults" ] || return 0 + mkdir -p "$(dirname "$target")" + if [ ! -f "$target" ]; then + if cp "$defaults" "$target" 2>/dev/null; then + chmod 600 "$target" 2>/dev/null || true + chown "$(id -un):$(id -gn)" "$target" 2>/dev/null || true + log "Copied claude.state -> $target" + fi + return 0 + fi + + command -v jq >/dev/null 2>&1 || return 0 + jq empty "$target" 2>/dev/null || return 0 + + legacy_input="$legacy" + if [ ! -f "$legacy_input" ] || ! jq empty "$legacy_input" 2>/dev/null; then + legacy_input="$(mktemp)" + printf '{}\n' > "$legacy_input" + fi + defaults_input="$defaults" + tmp="$(mktemp)" + if jq -s ' + .[0] as $current + | .[1] as $legacy + | .[2] as $defaults + | $current + | .hasCompletedOnboarding = (.hasCompletedOnboarding // $legacy.hasCompletedOnboarding // $defaults.hasCompletedOnboarding // true) + | .bypassPermissionsModeAccepted = (.bypassPermissionsModeAccepted // $legacy.bypassPermissionsModeAccepted // $defaults.bypassPermissionsModeAccepted // true) + | .hasTrustDialogAccepted = (.hasTrustDialogAccepted // $legacy.hasTrustDialogAccepted // true) + ' "$target" "$legacy_input" "$defaults_input" > "$tmp"; then + if ! cmp -s "$target" "$tmp"; then + install -m 600 "$tmp" "$target" + chown "$(id -un):$(id -gn)" "$target" 2>/dev/null || true + log "Repaired Claude state flags in $target" + fi + fi + rm -f "$tmp" + [ "$legacy_input" = "$legacy" ] || rm -f "$legacy_input" +} + +processed=0 +copied=0 +skipped=0 + +if [ -n "$ONLY_ID_PREFIX" ]; then + log "Deploying generated Claude settings..." +else + log "Deploying effective configuration files..." +fi + +jq -r '.[] | [ + (.id // ""), + (.src // ""), + (.dest // ""), + (.destFilename // "__NONE__"), + (.enabled // true | tostring), + (.overwrite // "if-changed") +] | @tsv' "$effective_manifest" | + while IFS=$'\t' read -r id src dest dest_filename enabled overwrite; do + if [ -n "$ONLY_ID_PREFIX" ] && [[ "$id" != "$ONLY_ID_PREFIX"* ]]; then + continue + fi + + processed=$((processed + 1)) -# Single jq invocation to extract all fields (reduces N×5 subprocess calls to 1) -# Note: empty destFilename uses "__NONE__" sentinel because bash read collapses -# consecutive tab delimiters, which shifts fields when destFilename is empty. -jq -r '.[] | [.src, .dest, (.destFilename // "__NONE__"), (.enabled // true | tostring), (.overwrite // "if-changed")] | @tsv' "$MANIFEST" | - while IFS=$'\t' read -r src dest dest_filename enabled overwrite; do - # Skip disabled entries if [ "$enabled" = "false" ]; then - log "Skipping $src (disabled)" + log "Skipping $id (disabled)" + skipped=$((skipped + 1)) continue fi - # Resolve paths - src_path="$CONFIG_DIR/$src" - dest_dir=$(expand_vars "$dest") - [ "$dest_filename" = "__NONE__" ] && dest_filename="" - filename="${dest_filename:-${src##*/}}" - dest_path="$dest_dir/$filename" + if [ -z "$id" ] || [ -z "$src" ] || [ -z "$dest" ]; then + warn "Skipping malformed manifest entry: id=$id src=$src dest=$dest" + skipped=$((skipped + 1)) + continue + fi - # Validate source exists - if [ ! -f "$src_path" ]; then - warn "$src not found in config dir, skipping" + if ! src_path="$(resolve_source "$src")"; then + warn "$src not found in overrides, generated output, or defaults; skipping" + skipped=$((skipped + 1)) continue fi - # Ensure destination directory exists + dest_dir="$(expand_vars "$dest")" + [ "$dest_filename" = "__NONE__" ] && dest_filename="" + filename="${dest_filename:-${src##*/}}" + dest_path="${dest_dir}/${filename}" + mkdir -p "$dest_dir" - # Apply overwrite strategy case "$overwrite" in - always) - if cp "$src_path" "$dest_path" 2>/dev/null; then - log "Copied $src → $dest_path (always)" - else - warn "Failed to copy $src → $dest_path (permission denied?)" - fi - ;; - never) - if [ ! -f "$dest_path" ]; then + always) if cp "$src_path" "$dest_path" 2>/dev/null; then - log "Copied $src → $dest_path (new)" + copied=$((copied + 1)) + log "Copied $id -> $dest_path" else - warn "Failed to copy $src → $dest_path (permission denied?)" + warn "Failed to copy $id -> $dest_path" + skipped=$((skipped + 1)) fi - else - log "Skipping $src (exists, overwrite=never)" - fi - ;; - if-changed | *) - if should_copy "$src_path" "$dest_path"; then - if cp "$src_path" "$dest_path" 2>/dev/null; then - log "Copied $src → $dest_path (changed)" + ;; + never) + if [ ! -f "$dest_path" ]; then + if cp "$src_path" "$dest_path" 2>/dev/null; then + copied=$((copied + 1)) + log "Copied $id -> $dest_path" + else + warn "Failed to copy $id -> $dest_path" + skipped=$((skipped + 1)) + fi + else + log "Skipping $id (exists, overwrite=never)" + skipped=$((skipped + 1)) + fi + ;; + if-changed | *) + if should_copy "$src_path" "$dest_path"; then + if cp "$src_path" "$dest_path" 2>/dev/null; then + copied=$((copied + 1)) + log "Copied $id -> $dest_path" + else + warn "Failed to copy $id -> $dest_path" + skipped=$((skipped + 1)) + fi else - warn "Failed to copy $src → $dest_path (permission denied?)" + log "Skipping $id (unchanged)" + skipped=$((skipped + 1)) fi - else - log "Skipping $src (unchanged)" - fi - ;; + ;; esac - # Fix ownership chown "$(id -un):$(id -gn)" "$dest_path" 2>/dev/null || true done +repair_claude_state log "Configuration complete" diff --git a/container/.devcontainer/scripts/setup-migrate-claude.sh b/container/.devcontainer/scripts/setup-migrate-claude.sh index 6ad2198..9a88cd6 100755 --- a/container/.devcontainer/scripts/setup-migrate-claude.sh +++ b/container/.devcontainer/scripts/setup-migrate-claude.sh @@ -13,7 +13,7 @@ OLD_DIR="/workspaces/.claude" _USERNAME="${SUDO_USER:-${USER:-vscode}}" _USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) _USER_HOME="${_USER_HOME:-/home/$_USERNAME}" -NEW_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}" +NEW_DIR="${_USER_HOME}/.claude" MARKER="$NEW_DIR/.migrated-from-workspaces" CODEFORGE_MARKER="${CODEFORGE_DIR:-${WORKSPACE_ROOT:-/workspaces}/.codeforge}/.markers/v2-migrated" diff --git a/container/.devcontainer/scripts/setup-migrate-codeforge-v3.sh b/container/.devcontainer/scripts/setup-migrate-codeforge-v3.sh new file mode 100755 index 0000000..f3d9c5e --- /dev/null +++ b/container/.devcontainer/scripts/setup-migrate-codeforge-v3.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +# One-time migration from copied .codeforge/config defaults to v3 overrides/state. + +set -uo pipefail + +WORKSPACE_ROOT="${WORKSPACE_ROOT:?WORKSPACE_ROOT not set}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +MARKERS_DIR="${CODEFORGE_DIR}/.markers" +MARKER="${MARKERS_DIR}/config-layout-v3" +REPORT="${MARKERS_DIR}/config-layout-v3-report.md" +BACKUP_ROOT="${CODEFORGE_DIR}/backups" +TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)" +BACKUP_DIR="${BACKUP_ROOT}/config-layout-v3-${TIMESTAMP}" +OLD_CONFIG_DIR="${CODEFORGE_DIR}/config" + +log() { echo "[setup-migrate-codeforge-v3] $*"; } +report() { printf '%s\n' "$*" >>"$REPORT"; } + +mkdir -p "$MARKERS_DIR" "$CODEFORGE_DIR/data" "$CODEFORGE_DIR/.checksums" + +if [ -f "$MARKER" ]; then + log "v3 config layout marker exists; skipping" + exit 0 +fi + +cat >"$REPORT" <&2 + exit 1 + fi + report "- Backed up old config directory to \`${BACKUP_DIR}/config\`." + fi + if [ -f "${CODEFORGE_DIR}/file-manifest.json" ]; then + if ! cp -a "${CODEFORGE_DIR}/file-manifest.json" "$BACKUP_DIR/file-manifest.json"; then + echo "[migration] ERROR: Could not back up old user manifest" >&2 + exit 1 + fi + report "- Backed up old user manifest to \`${BACKUP_DIR}/file-manifest.json\`." + fi +else + report "- No old \`.codeforge/config\` directory or user manifest was present." +fi + +move_file() { + local old="$1" + local new="$2" + local label="$3" + local src="${OLD_CONFIG_DIR}/${old}" + local dest="${CODEFORGE_DIR}/${new}" + + if [ ! -f "$src" ]; then + return 0 + fi + mkdir -p "$(dirname "$dest")" + if [ -e "$dest" ]; then + report "- Skipped ${label}: \`${new}\` already exists; old file remains at \`config/${old}\`." + else + if ! mv "$src" "$dest"; then + echo "[migration] ERROR: Could not move ${label}: config/${old} -> ${new}" >&2 + exit 1 + fi + report "- Moved ${label}: \`config/${old}\` -> \`${new}\`." + fi +} + +move_dir() { + local old="$1" + local new="$2" + local label="$3" + local src="${OLD_CONFIG_DIR}/${old}" + local dest="${CODEFORGE_DIR}/${new}" + + if [ ! -d "$src" ]; then + return 0 + fi + mkdir -p "$(dirname "$dest")" + if [ -e "$dest" ]; then + report "- Skipped ${label}: \`${new}\` already exists; old directory remains at \`config/${old}\`." + else + if ! mv "$src" "$dest"; then + echo "[migration] ERROR: Could not move ${label}: config/${old} -> ${new}" >&2 + exit 1 + fi + report "- Moved ${label}: \`config/${old}\` -> \`${new}\`." + fi +} + +move_file "settings.base.json" "claude/settings/base.json" "settings base override" +if [ -d "${OLD_CONFIG_DIR}/settings-profiles" ]; then + mkdir -p "${CODEFORGE_DIR}/claude/settings/profiles" + for profile in "${OLD_CONFIG_DIR}/settings-profiles"/*.json; do + [ -f "$profile" ] || continue + name="$(basename "$profile")" + if [ -e "${CODEFORGE_DIR}/claude/settings/profiles/${name}" ]; then + report "- Skipped settings profile \`${name}\`: new override already exists." + else + if ! mv "$profile" "${CODEFORGE_DIR}/claude/settings/profiles/${name}"; then + echo "[migration] ERROR: Could not move settings profile: ${name}" >&2 + exit 1 + fi + report "- Moved settings profile: \`config/settings-profiles/${name}\` -> \`claude/settings/profiles/${name}\`." + fi + done + rmdir "${OLD_CONFIG_DIR}/settings-profiles" 2>/dev/null || true +fi + +move_file "main-system-prompt.md" "claude/system-prompts/main.md" "main system prompt" +move_file "writing-system-prompt.md" "claude/system-prompts/writing.md" "writing system prompt" +move_file "orchestrator-system-prompt.md" "claude/system-prompts/orchestrator.md" "orchestrator system prompt" +move_dir "rules" "claude/rules" "rules overrides" +move_dir "hooks" "claude/hooks" "hook overrides" +move_file "ccstatusline-settings.json" "claude/statusline/settings.json" "ccstatusline config" +move_file "claude-code-router.json" "claude/router/config.json" "Claude Code Router config" +move_file "disabled-hooks.json" "claude/disabled-hooks.json" "disabled hooks config" +move_file "keybindings.json" "claude/keybindings.json" "Claude keybindings" +move_file "codex-config.toml" "codex/config.toml" "Codex config" +move_file "codex-rtk-awareness.md" "codex/AGENTS.md" "Codex AGENTS.md" +move_file "rtk-config.toml" "rtk/config.toml" "RTK config" + +if [ -d "$OLD_CONFIG_DIR" ]; then + shopt -s nullglob + for generated in \ + "${OLD_CONFIG_DIR}/settings.json" \ + "${OLD_CONFIG_DIR}"/settings-opus-*.json; do + report "- Left generated settings file in place for review, not as source of truth: \`config/$(basename "$generated")\`." + done + shopt -u nullglob + if find "$OLD_CONFIG_DIR" -mindepth 1 -print -quit | grep -q .; then + report "- Remaining old files were left under \`config/\` for manual review." + else + rmdir "$OLD_CONFIG_DIR" 2>/dev/null || true + report "- Removed empty old \`config/\` directory." + fi +fi + +if [ -f "${CODEFORGE_DIR}/file-manifest.json" ]; then + report "- Existing user manifest was preserved. Review it for v3 \`id\` fields and new logical paths." +fi + +{ + echo "migratedAt=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + echo "report=${REPORT}" + echo "backup=${BACKUP_DIR}" +} >"$MARKER" + +log "v3 migration complete; report written to ${REPORT}" diff --git a/container/.devcontainer/scripts/setup-migrate-codeforge.sh b/container/.devcontainer/scripts/setup-migrate-codeforge.sh index ff32ef5..df0835e 100755 --- a/container/.devcontainer/scripts/setup-migrate-codeforge.sh +++ b/container/.devcontainer/scripts/setup-migrate-codeforge.sh @@ -1,70 +1,40 @@ #!/bin/bash # SPDX-License-Identifier: GPL-3.0-only # Copyright (c) 2026 Marcus Krueger -# One-time migration: .devcontainer/config/ → .codeforge/ -# Migrates config files, manifest, and terminal scripts from the legacy -# .devcontainer/config/ layout to the new .codeforge/ directory structure. +# Ensure the minimal .codeforge/ workspace directory exists. + +set -uo pipefail WORKSPACE_ROOT="${WORKSPACE_ROOT:?WORKSPACE_ROOT not set}" CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" -OLD_CONFIG_DIR="${WORKSPACE_ROOT}/.devcontainer/config" -OLD_DEFAULTS_DIR="${OLD_CONFIG_DIR}/defaults" -MARKER="$CODEFORGE_DIR/.markers/v2-migrated" +README="${CODEFORGE_DIR}/README.md" log() { echo "[setup-migrate-codeforge] $*"; } -warn() { echo "[setup-migrate-codeforge] WARNING: $*"; } -# Already migrated — skip -if [ -d "$CODEFORGE_DIR" ]; then - log ".codeforge/ already exists — skipping migration" - exit 0 -fi +mkdir -p \ + "$CODEFORGE_DIR/.markers" \ + "$CODEFORGE_DIR/.checksums" \ + "$CODEFORGE_DIR/data" -# Nothing to migrate — try bootstrapping from bundled defaults instead -if [ ! -d "$OLD_DEFAULTS_DIR" ]; then - BUNDLED_DEFAULTS="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/defaults/codeforge" - if [ -d "$BUNDLED_DEFAULTS" ]; then - log "Bootstrapping .codeforge/ from bundled defaults..." - cp -a "$BUNDLED_DEFAULTS" "$CODEFORGE_DIR" - mkdir -p "$CODEFORGE_DIR/.markers" "$CODEFORGE_DIR/.checksums" - date -Iseconds > "$CODEFORGE_DIR/.markers/v2-bootstrapped" - log "Bootstrap complete — .codeforge/ is ready" - else - warn "No .codeforge/, no legacy layout, and no bundled defaults found" - warn "Run 'npx @coredirective/cf-container --force' to scaffold .codeforge/" - fi - exit 0 -fi +if [ ! -f "$README" ]; then + cat >"$README" <<'EOF' +# CodeForge Project Overrides -log "Migrating .devcontainer/config/ → .codeforge/ ..." +This directory is intentionally small and user-owned. -# Create directory structure -mkdir -p "$CODEFORGE_DIR/config/rules" \ - "$CODEFORGE_DIR/scripts" \ - "$CODEFORGE_DIR/.markers" \ - "$CODEFORGE_DIR/.checksums" +Packaged defaults live in `.devcontainer/defaults/codeforge/`. Put files here +only when you want to override a packaged default, add project-local state, or +store CodeForge marker files. -# Copy config files from .devcontainer/config/defaults/ → .codeforge/config/ -if [ -d "$OLD_DEFAULTS_DIR" ]; then - cp -a "$OLD_DEFAULTS_DIR/." "$CODEFORGE_DIR/config/" - log "Copied config files from defaults/ → .codeforge/config/" -fi - -# Copy file-manifest.json, rewriting src paths from defaults/ to config/ -if [ -f "$OLD_CONFIG_DIR/file-manifest.json" ]; then - sed 's|"defaults/|"config/|g' "$OLD_CONFIG_DIR/file-manifest.json" > "$CODEFORGE_DIR/file-manifest.json" - log "Copied file-manifest.json (rewrote defaults/ → config/)" -fi +Override files use the same logical path as packaged defaults. For example: -# Copy terminal scripts from .devcontainer/ → .codeforge/scripts/ -for script in connect-external-terminal.sh connect-external-terminal.ps1; do - if [ -f "${WORKSPACE_ROOT}/.devcontainer/${script}" ]; then - cp "${WORKSPACE_ROOT}/.devcontainer/${script}" "$CODEFORGE_DIR/scripts/${script}" - log "Copied ${script} → .codeforge/scripts/" - fi -done +- `.codeforge/claude/system-prompts/main.md` +- `.codeforge/claude/settings/base.json` +- `.codeforge/file-manifest.json` -# Write migration marker -date -Iseconds > "$MARKER" +CodeForge may also create marker and audit files under `.codeforge/.markers/`. +EOF + log "Created .codeforge/README.md" +fi -log "Migration complete — .codeforge/ is ready" +log ".codeforge/ scaffold is ready" diff --git a/container/.devcontainer/scripts/setup-plugins.sh b/container/.devcontainer/scripts/setup-plugins.sh index e2513a9..501b8f6 100755 --- a/container/.devcontainer/scripts/setup-plugins.sh +++ b/container/.devcontainer/scripts/setup-plugins.sh @@ -30,7 +30,7 @@ for plugin in "${OFFICIAL_PLUGINS[@]}"; do if claude plugin install "$plugin" 2>/dev/null; then echo "[setup-plugins] Installed: $plugin" else - echo "[setup-plugins] Warning: Failed to install $plugin (may already exist)" + echo "[setup-plugins] Warning: Failed to install $plugin (may already exist)" >&2 fi done diff --git a/container/.devcontainer/scripts/setup.sh b/container/.devcontainer/scripts/setup.sh index d666b84..39ba042 100755 --- a/container/.devcontainer/scripts/setup.sh +++ b/container/.devcontainer/scripts/setup.sh @@ -5,75 +5,83 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")" -ENV_FILE="$DEVCONTAINER_DIR/.env" - -# Load configuration -if [ -f "$ENV_FILE" ]; then - set -a - source "$ENV_FILE" - set +a +WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}" +CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}" +CONFIG_FILE="${CODEFORGE_DIR}/container.json" + +# --- Migration warnings for removed config files --- +if [ -f "$DEVCONTAINER_DIR/.env" ]; then + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " MIGRATION: .devcontainer/.env is no longer used" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " Setup config has moved to .codeforge/container.json" + echo " Delete .devcontainer/.env after migrating your settings." + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" fi - -# Deprecation guard: .env may still set CLAUDE_CONFIG_DIR=/workspaces/.claude -# (pre-v2.0 default). Since .env is gitignored, PR updates can't fix it. -# Override with warning so all child scripts use the correct home location. -if [ "$CLAUDE_CONFIG_DIR" = "/workspaces/.claude" ]; then - echo "[setup] WARNING: CLAUDE_CONFIG_DIR=/workspaces/.claude is deprecated (moved to home dir in v2.0)" - echo "[setup] Updating .devcontainer/.env automatically." - CLAUDE_CONFIG_DIR="$HOME/.claude" - # Fix the file on disk so subsequent restarts don't trigger this guard - if [ -f "$ENV_FILE" ]; then - sed -i 's|^CLAUDE_CONFIG_DIR=.*/workspaces/\.claude.*|# CLAUDE_CONFIG_DIR removed (v2.0: now uses $HOME/.claude)|' "$ENV_FILE" - echo "[setup] .env updated — CLAUDE_CONFIG_DIR line commented out." - fi +if [ -f "$DEVCONTAINER_DIR/.secrets" ]; then + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " MIGRATION: .devcontainer/.secrets is no longer used" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " Secrets have moved to .codeforge/secrets/ (one file per secret)." + echo " See AGENTS.md for the new secret names and setup instructions." + echo " Delete .devcontainer/.secrets after migrating your tokens." + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" fi -# Deprecation guard: CONFIG_SOURCE_DIR may still point to /workspaces/.claude -# (pre-v2.0 default was to keep config source in workspace .claude dir). -# Override with correct path. -if [ "$CONFIG_SOURCE_DIR" = "/workspaces/.claude" ]; then - echo "[setup] WARNING: CONFIG_SOURCE_DIR=/workspaces/.claude is deprecated (moved to .devcontainer/config in v2.0)" - echo "[setup] Updating .devcontainer/.env automatically." - CONFIG_SOURCE_DIR="$DEVCONTAINER_DIR/config" - if [ -f "$ENV_FILE" ]; then - sed -i 's|^CONFIG_SOURCE_DIR=.*/workspaces/\.claude.*|# CONFIG_SOURCE_DIR removed (v2.0: now uses .devcontainer/config)|' "$ENV_FILE" - echo "[setup] .env updated — CONFIG_SOURCE_DIR line commented out." - fi -fi +# --- Load configuration from .codeforge/container.json --- +jq_val() { + [ -f "$CONFIG_FILE" ] && jq -r "$1" "$CONFIG_FILE" 2>/dev/null || echo "$2" +} -# Deprecation guard: CONFIG_SOURCE_DIR may still point to .devcontainer/config -# (pre-v2.0 default). Redirect to .codeforge. -if [ "$CONFIG_SOURCE_DIR" = "$DEVCONTAINER_DIR/config" ] || [ "$CONFIG_SOURCE_DIR" = "/workspaces/.devcontainer/config" ]; then - echo "[setup] WARNING: CONFIG_SOURCE_DIR pointing to .devcontainer/config is deprecated (moved to .codeforge in v2.0)" - echo "[setup] Redirecting to .codeforge." - : "${CODEFORGE_DIR:=${WORKSPACE_ROOT:?}/.codeforge}" - unset CONFIG_SOURCE_DIR - if [ -f "$ENV_FILE" ]; then - sed -i 's|^CONFIG_SOURCE_DIR=.*\.devcontainer/config.*|# CONFIG_SOURCE_DIR removed (v2.0: now uses .codeforge)|' "$ENV_FILE" - echo "[setup] .env updated — CONFIG_SOURCE_DIR line commented out." +SETUP_CONFIG=$(jq_val '.setup.config // true' true) +SETUP_ALIASES=$(jq_val '.setup.aliases // true' true) +SETUP_AUTH=$(jq_val '.setup.auth // true' true) +SETUP_PLUGINS=$(jq_val '.setup.plugins // true' true) +SETUP_UPDATE_CLAUDE=$(jq_val '.setup.updateClaude // true' true) +SETUP_PROJECTS=$(jq_val '.setup.projects // true' true) +SETUP_TERMINAL=$(jq_val '.setup.terminal // true' true) +SETUP_POSTSTART=$(jq_val '.setup.poststart // true' true) +CLAUDE_VERSION_LOCK=$(jq_val '.claude.versionLock // empty' "") +CODEFORGE_TIMEZONE=$(jq_val '.timezone // "America/Chicago"' "America/Chicago") + +export CODEFORGE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE CLAUDE_VERSION_LOCK SETUP_PROJECTS SETUP_TERMINAL SETUP_POSTSTART + +# --- Configure timezone --- +if [ -n "$CODEFORGE_TIMEZONE" ]; then + export TZ="$CODEFORGE_TIMEZONE" + # Persist for all shells via profile.d + if [ ! -f /etc/profile.d/codeforge-tz.sh ] || ! grep -q "TZ=\"$CODEFORGE_TIMEZONE\"" /etc/profile.d/codeforge-tz.sh 2>/dev/null; then + sudo tee /etc/profile.d/codeforge-tz.sh > /dev/null </dev/null; then - echo "[setup] WARNING: Could not fix volume ownership on $HOME/.claude — subsequent scripts may fail" -fi +# regardless of remoteUser. Every mount point from docker-compose.yml must +# be listed here. This is the only setup script requiring sudo. +_VOLUME_MOUNTS=( + "$HOME/.claude" + "$HOME/.codex" + "$HOME/.hermes" + "$HOME/.config/gh" + "$HOME/.cache" + "$HOME/.npm" + "$HOME/.bun/install/cache" +) +_OWNER="$(id -un):$(id -gn)" +for _vol in "${_VOLUME_MOUNTS[@]}"; do + [ -d "$_vol" ] || continue + if ! sudo chown "$_OWNER" "$_vol" 2>/dev/null; then + echo "[setup] WARNING: Could not fix volume ownership on $_vol" + fi +done +unset _VOLUME_MOUNTS _OWNER _vol # Mark workspace as safe for Git — bind-mounted workspace may have # different uid than container user, causing "dubious ownership" @@ -107,7 +115,6 @@ run_script() { local exit_code=$? echo "FAILED (exit $exit_code)" SETUP_RESULTS+=("$name:failed") - # Show output on failure for diagnostics echo "$output" | sed 's/^/ /' fi else @@ -146,7 +153,9 @@ run_poststart_hooks() { run_script "$SCRIPT_DIR/setup-migrate-claude.sh" "true" run_script "$SCRIPT_DIR/setup-migrate-codeforge.sh" "true" +run_script "$SCRIPT_DIR/setup-migrate-codeforge-v3.sh" "true" run_script "$SCRIPT_DIR/setup-auth.sh" "$SETUP_AUTH" +run_script "$SCRIPT_DIR/ensure-settings-generated.sh" "$SETUP_CONFIG" run_script "$SCRIPT_DIR/setup-config.sh" "$SETUP_CONFIG" run_script "$SCRIPT_DIR/setup-aliases.sh" "$SETUP_ALIASES" run_script "$SCRIPT_DIR/setup-plugins.sh" "$SETUP_PLUGINS" @@ -157,7 +166,7 @@ run_script "$SCRIPT_DIR/setup-terminal.sh" "$SETUP_TERMINAL" if [ "$SETUP_UPDATE_CLAUDE" = "true" ] && [ -f "$SCRIPT_DIR/setup-update-claude.sh" ]; then CLAUDE_UPDATE_LOG="${CLAUDE_UPDATE_LOG:-/tmp/claude-update.log}" mkdir -p "$(dirname "$CLAUDE_UPDATE_LOG")" - bash "$SCRIPT_DIR/setup-update-claude.sh" >>"$CLAUDE_UPDATE_LOG" 2>&1 & + ( bash "$SCRIPT_DIR/setup-update-claude.sh" >>"$CLAUDE_UPDATE_LOG" 2>&1 && touch /tmp/.claude-update-ok || touch /tmp/.claude-update-failed ) & disown SETUP_RESULTS+=("setup-update-claude:background") else diff --git a/container/CLAUDE.md b/container/CLAUDE.md index 7e87c83..f555dd1 100644 --- a/container/CLAUDE.md +++ b/container/CLAUDE.md @@ -28,4 +28,4 @@ All user-facing changes MUST be reflected in documentation: ### User Configuration -All user-customizable configuration files belong in `.codeforge/`. New config files go in `.codeforge/config/`, with a corresponding entry in `.codeforge/file-manifest.json`. +Packaged defaults belong in `.devcontainer/defaults/codeforge/`. `.codeforge/` is only for project overrides and state. New config defaults should go under the relevant `claude/`, `codex/`, or `rtk/` subfolder and get a stable `id` in `.devcontainer/defaults/codeforge/file-manifest.json`; project overrides use the same logical path under `.codeforge/`. diff --git a/container/README.md b/container/README.md index e1feb6b..dd6ceed 100755 --- a/container/README.md +++ b/container/README.md @@ -114,19 +114,20 @@ For the full architecture breakdown — hook pipeline, agent routing, skill load ## Configuration -All configuration lives in `.devcontainer/` and deploys automatically on container start. Key files: +Packaged defaults live in `.devcontainer/defaults/codeforge/`, generated Claude settings live in `.devcontainer/.generated/`, and project overrides/state live in `.codeforge/`. Key files: | File | What It Configures | User-Modifiable? | |------|--------------------|------------------| -| `.codeforge/config/settings.json` | Model, plugins, permissions, environment variables | Yes | -| `.codeforge/config/main-system-prompt.md` | Claude's behavioral guidelines and directives | Yes | -| `.codeforge/config/keybindings.json` | Keyboard shortcuts | Yes | -| `.codeforge/config/ccstatusline-settings.json` | Terminal status bar widgets and layout | Yes | -| `.codeforge/file-manifest.json` | Which config files deploy and how they update | Yes | +| `.devcontainer/defaults/codeforge/claude/settings/base.json` | Shared Claude settings source | Override via `.codeforge/claude/settings/base.json` | +| `.devcontainer/defaults/codeforge/claude/settings/profiles/*.json` | Model-specific settings overlays | Override via `.codeforge/claude/settings/profiles/*.json` | +| `.devcontainer/defaults/codeforge/claude/system-prompts/main.md` | Claude's behavioral guidelines and directives | Override via `.codeforge/claude/system-prompts/main.md` | +| `.devcontainer/defaults/codeforge/claude/keybindings.json` | Keyboard shortcuts | Override via `.codeforge/claude/keybindings.json` | +| `.devcontainer/defaults/codeforge/claude/statusline/settings.json` | Terminal status bar widgets and layout | Override via `.codeforge/claude/statusline/settings.json` | +| `.devcontainer/defaults/codeforge/file-manifest.json` | Which config files deploy and how they update | Override entries via `.codeforge/file-manifest.json` | | `devcontainer.json` | Container image, features, runtimes, ports | Yes | -| `.env` | Setup phase toggles (auth, plugins, aliases, etc.) | Yes | +| `.codeforge/container.json` | Setup phase toggles (auth, plugins, aliases, etc.), Claude version lock, identity overrides | Yes | -Config files use SHA-256 change detection — your edits persist across container rebuilds unless the source changes. Set a file's overwrite mode to `"never"` in `file-manifest.json` to permanently preserve your customizations. +Config files use effective source resolution: `.codeforge/` first, then generated output, then packaged defaults. Set a file's overwrite mode to `"never"` in `.codeforge/file-manifest.json` to preserve deployed customizations. For the complete configuration guide, see the [documentation site](https://codeforge.core-directive.com/customization/configuration/). @@ -177,7 +178,7 @@ npm publish ## Changelog -See [CHANGELOG.md](.devcontainer/CHANGELOG.md) for release history. Current version: **2.1.0**. +See [CHANGELOG.md](.devcontainer/CHANGELOG.md) for release history. Current version: **3.0.0**. ## Further Reading diff --git a/container/package.json b/container/package.json index 66c551b..ccde2b8 100644 --- a/container/package.json +++ b/container/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "2.2.1", + "version": "3.0.0", "description": "Complete development container that sets up Claude Code with modular devcontainer features, modern dev tools, and persistent configurations. Drop it into any project and get a production-ready AI development environment in minutes.", "main": "setup.js", "bin": { @@ -38,13 +38,11 @@ "!.devcontainer/.env", "!.devcontainer/.env.*", "!.devcontainer/.secrets", - ".codeforge/**/*", - "!.codeforge/.markers/**", "setup.js", "README.md" ], "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" }, "repository": { "type": "git", diff --git a/container/setup.js b/container/setup.js index 39be9a6..e6d6cb0 100755 --- a/container/setup.js +++ b/container/setup.js @@ -5,6 +5,7 @@ const fs = require("node:fs"); const path = require("node:path"); const crypto = require("node:crypto"); +const { execFileSync } = require("node:child_process"); // ── Default preserve list ──────────────────────────────────────── // Files in .devcontainer that should NOT overwrite user customizations. @@ -54,6 +55,9 @@ function loadPreserveList(devcontainerDest) { // ── computeChecksum ────────────────────────────────────────────── // Returns SHA-256 hex digest of a file's contents. function computeChecksum(filePath) { + if (!fs.existsSync(filePath)) { + throw new Error(`File not found for checksum: ${filePath}`); + } return crypto .createHash("sha256") .update(fs.readFileSync(filePath)) @@ -106,6 +110,35 @@ function writeChecksums(codeforgeDir, version, checksums) { ); } +function ensureCodeforgeScaffold(codeforgeDir) { + fs.mkdirSync(path.join(codeforgeDir, ".markers"), { recursive: true }); + fs.mkdirSync(path.join(codeforgeDir, ".checksums"), { recursive: true }); + fs.mkdirSync(path.join(codeforgeDir, "data"), { recursive: true }); + + const readme = path.join(codeforgeDir, "README.md"); + if (!fs.existsSync(readme)) { + fs.writeFileSync( + readme, + [ + "# CodeForge Project Overrides", + "", + "This directory is intentionally small and user-owned.", + "", + "Packaged defaults live in `.devcontainer/defaults/codeforge/`. Put files here only when you want to override a packaged default, add project-local state, or store CodeForge marker files.", + "", + "Override files use the same logical path as packaged defaults. For example:", + "", + "- `.codeforge/claude/system-prompts/main.md`", + "- `.codeforge/claude/settings/base.json`", + "- `.codeforge/file-manifest.json`", + "", + "CodeForge may also create marker and audit files under `.codeforge/.markers/`.", + "", + ].join("\n"), + ); + } +} + // ── readChecksums ──────────────────────────────────────────────── // Reads latest version's checksums from .checksums/ dir. // Returns { files: {} } if none found. @@ -119,10 +152,14 @@ function readChecksums(codeforgeDir) { .readdirSync(checksumsDir) .filter((f) => f.endsWith(".json")) .sort((a, b) => { - const pa = a.replace(".json", "").split(".").map(Number); - const pb = b.replace(".json", "").split(".").map(Number); - for (let i = 0; i < Math.max(pa.length, pb.length); i++) { - const diff = (pa[i] || 0) - (pb[i] || 0); + const parse = (v) => { + const m = v.replace(".json", "").match(/^(\d+)\.(\d+)\.(\d+)/); + return m ? [parseInt(m[1]), parseInt(m[2]), parseInt(m[3])] : [0, 0, 0]; + }; + const pa = parse(a); + const pb = parse(b); + for (let i = 0; i < 3; i++) { + const diff = pa[i] - pb[i]; if (diff !== 0) return diff; } return 0; @@ -262,10 +299,16 @@ function syncDirectory(src, dest, preserveSet) { } // Preserved files: skip overwrite, save package version as .codeforge-new - if (preserveSet.has(relativePath) && fs.existsSync(destPath)) { - fs.copyFileSync(srcPath, `${destPath}.codeforge-new`); - stats.preserved++; - stats.preservedFiles.push(relativePath); + if (preserveSet.has(relativePath)) { + if (fs.existsSync(destPath)) { + fs.copyFileSync(srcPath, `${destPath}.codeforge-new`); + stats.preserved++; + stats.preservedFiles.push(relativePath); + continue; + } + // Preserve-listed but missing at dest — copy and count as new + fs.copyFileSync(srcPath, destPath); + stats.added++; continue; } @@ -312,7 +355,7 @@ function main() { console.log(""); console.log("Subcommands:"); console.log( - " config apply Deploy .codeforge/config/ files to ~/.claude/", + " config apply Deploy effective CodeForge defaults plus overrides", ); console.log(""); console.log( @@ -325,11 +368,7 @@ function main() { const packageDir = __dirname; const devcontainerSrc = path.join(packageDir, ".devcontainer"); const devcontainerDest = path.join(currentDir, ".devcontainer"); - const codeforgeSrc = path.join(packageDir, ".codeforge"); const codeforgeDest = path.join(currentDir, ".codeforge"); - const packageVersion = JSON.parse( - fs.readFileSync(path.join(packageDir, "package.json"), "utf8"), - ).version; console.log(""); @@ -352,34 +391,8 @@ function main() { " Reset complete. All .devcontainer customizations removed.", ); - // .codeforge uses checksum-based preservation (not wipe) - if (fs.existsSync(codeforgeSrc)) { - if (fs.existsSync(codeforgeDest)) { - const codeforgeStats = syncCodeforgeDirectory( - codeforgeSrc, - codeforgeDest, - ); - console.log(" .codeforge/ user modifications preserved."); - console.log(` Updated: ${codeforgeStats.updated} files`); - console.log(` Added: ${codeforgeStats.added} new files`); - console.log( - ` Preserved: ${codeforgeStats.preserved} user config files`, - ); - if (codeforgeStats.defaultFiles.length > 0) { - console.log(""); - console.log( - " Review .default files for new defaults you may want to merge:", - ); - for (const f of codeforgeStats.defaultFiles) { - console.log(` ${f}.default`); - } - } - } else { - copyDirectory(codeforgeSrc, codeforgeDest); - } - const newChecksums = generateChecksums(codeforgeSrc); - writeChecksums(codeforgeDest, packageVersion, newChecksums); - } + ensureCodeforgeScaffold(codeforgeDest); + console.log(" .codeforge/ overrides/state scaffold ensured."); console.log(""); printNextSteps(); @@ -418,33 +431,9 @@ function main() { console.log(""); } - // .codeforge sync with checksum-based preservation - if (fs.existsSync(codeforgeSrc)) { - const codeforgeStats = syncCodeforgeDirectory( - codeforgeSrc, - codeforgeDest, - ); - const newChecksums = generateChecksums(codeforgeSrc); - writeChecksums(codeforgeDest, packageVersion, newChecksums); - - console.log(" .codeforge/ update:"); - console.log(` Updated: ${codeforgeStats.updated} files`); - console.log(` Added: ${codeforgeStats.added} new files`); - console.log( - ` Preserved: ${codeforgeStats.preserved} user config files`, - ); - - if (codeforgeStats.defaultFiles.length > 0) { - console.log(""); - console.log( - " Review .default files for new defaults you may want to merge:", - ); - for (const f of codeforgeStats.defaultFiles) { - console.log(` ${f}.default`); - } - } - console.log(""); - } + ensureCodeforgeScaffold(codeforgeDest); + console.log(" .codeforge/ overrides/state scaffold ensured."); + console.log(""); printNextSteps(); } else { @@ -463,12 +452,7 @@ function main() { try { copyDirectory(devcontainerSrc, devcontainerDest); - - if (fs.existsSync(codeforgeSrc)) { - copyDirectory(codeforgeSrc, codeforgeDest); - const checksums = generateChecksums(codeforgeSrc); - writeChecksums(codeforgeDest, packageVersion, checksums); - } + ensureCodeforgeScaffold(codeforgeDest); console.log(" CodeForge DevContainer configuration installed!"); console.log(""); @@ -482,33 +466,83 @@ function main() { } // ── configApply ────────────────────────────────────────────────── -// Deploys .codeforge/config/ files to ~/.claude/ using file-manifest.json. +// Deploys effective CodeForge defaults plus optional .codeforge/ overrides. function configApply() { const codeforgeDir = process.env.CODEFORGE_DIR || path.join(process.cwd(), ".codeforge"); - const manifest = path.join(codeforgeDir, "file-manifest.json"); + const workspaceRoot = process.env.WORKSPACE_ROOT || process.cwd(); + const devcontainerDir = path.join(workspaceRoot, ".devcontainer"); + const defaultsRoot = path.join(devcontainerDir, "defaults", "codeforge"); + const generatedRoot = path.join(devcontainerDir, ".generated", "codeforge"); + const defaultManifest = path.join(defaultsRoot, "file-manifest.json"); + const userManifest = path.join(codeforgeDir, "file-manifest.json"); - if (!fs.existsSync(manifest)) { - console.error("Error: file-manifest.json not found at " + manifest); + if (!fs.existsSync(defaultManifest)) { + console.error( + "Error: default file-manifest.json not found at " + defaultManifest, + ); console.error("Are you in a CodeForge project directory?"); process.exit(1); } - const entries = JSON.parse(fs.readFileSync(manifest, "utf-8")); + ensureCodeforgeScaffold(codeforgeDir); + + const generator = path.join( + devcontainerDir, + "scripts", + "generate-settings-profiles.js", + ); + if (fs.existsSync(generator)) { + execFileSync(process.execPath, [generator, "--if-stale"], { + stdio: "inherit", + env: { + ...process.env, + WORKSPACE_ROOT: workspaceRoot, + CODEFORGE_DIR: codeforgeDir, + }, + }); + } + + const defaultEntries = JSON.parse(fs.readFileSync(defaultManifest, "utf-8")); + const userEntries = fs.existsSync(userManifest) + ? JSON.parse(fs.readFileSync(userManifest, "utf-8")) + : []; + const entries = mergeManifestEntries(defaultEntries, userEntries); const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(process.env.HOME || "/home/vscode", ".claude"); - const workspaceRoot = process.env.WORKSPACE_ROOT || process.cwd(); function expandVars(val) { - return val + const expanded = val .replace(/\$\{CLAUDE_CONFIG_DIR\}/g, claudeConfigDir) .replace(/\$\{WORKSPACE_ROOT\}/g, workspaceRoot) .replace(/\$\{HOME\}/g, process.env.HOME || "/home/vscode"); + return path.resolve(expanded); + } + + function isPathWithin(root, candidate) { + const relative = path.relative(path.resolve(root), path.resolve(candidate)); + return ( + relative === "" || + (!relative.startsWith("..") && !path.isAbsolute(relative)) + ); + } + + function resolveSource(src) { + for (const root of [codeforgeDir, generatedRoot, defaultsRoot]) { + const candidate = path.resolve(root, src); + if (!isPathWithin(root, candidate)) { + continue; + } + if (fs.existsSync(candidate)) { + return candidate; + } + } + return null; } console.log(""); - console.log("Applying .codeforge/config/ to Claude configuration..."); + console.log("Applying effective CodeForge configuration..."); console.log(""); let deployed = 0; @@ -532,20 +566,16 @@ function configApply() { ); } - const codeforgeRoot = path.resolve(codeforgeDir); - const srcPath = path.resolve(codeforgeRoot, entry.src); - if (!srcPath.startsWith(codeforgeRoot + path.sep)) { + const srcPath = resolveSource(entry.src); + if (!srcPath) { console.log( - " Skip: " + entry.src + " (source path escapes .codeforge/)", + " Skip: " + + entry.src + + " (not found in overrides, generated output, or defaults)", ); skipped++; continue; } - if (!fs.existsSync(srcPath)) { - console.log(" Skip: " + entry.src + " (not found)"); - skipped++; - continue; - } const homeDir = path.resolve(process.env.HOME || "/home/vscode"); const allowedDestRoots = [ @@ -595,6 +625,29 @@ function configApply() { ); } +function mergeManifestEntries(defaultEntries, userEntries) { + const byId = new Map(); + for (const entry of [...defaultEntries, ...userEntries]) { + const id = entry.id || entry.src; + if (!id) { + console.warn("Manifest entry missing id/src — skipping"); + continue; + } + const previous = byId.get(id) || {}; + const merged = { + ...previous, + ...entry, + id, + overwrite: entry.overwrite || previous.overwrite || "if-changed", + }; + if (entry.disabled === true) { + merged.enabled = false; + } + byId.set(id, merged); + } + return [...byId.values()]; +} + function printNextSteps() { console.log("Next steps:"); console.log(" 1. Open this folder in VS Code"); @@ -625,5 +678,7 @@ module.exports = { loadPreserveList, computeChecksum, generateChecksums, + ensureCodeforgeScaffold, + mergeManifestEntries, main, }; diff --git a/container/test.js b/container/test.js index c244e47..881b79c 100755 --- a/container/test.js +++ b/container/test.js @@ -3,341 +3,301 @@ // Copyright (c) 2026 Marcus Krueger const fs = require("node:fs"); +const os = require("node:os"); const path = require("node:path"); +const { execFileSync } = require("node:child_process"); const { copyDirectory, computeChecksum, + ensureCodeforgeScaffold, generateChecksums, - syncCodeforgeDirectory, + mergeManifestEntries, main, } = require("./setup.js"); -function runTests() { - console.log("🧪 Running CodeForge package tests...\n"); +const root = __dirname; +const defaultsDir = path.join(root, ".devcontainer", "defaults", "codeforge"); +const generatedSettingsDir = path.join( + root, + ".devcontainer", + ".generated", + "codeforge", + "claude", + "settings", +); - // Test 1: copyDirectory function exists - console.log("✓ Test 1: copyDirectory function exists"); +let failed = false; - // Test 2: main function exists - console.log("✓ Test 2: main function exists"); +function pass(message) { + console.log(`ok - ${message}`); +} - // Test 3: Check required files exist - const requiredFiles = [ - "package.json", - "setup.js", - "README.md", - ".devcontainer/devcontainer.json", - ".devcontainer/scripts/setup.sh", - ".devcontainer/scripts/generate-settings-profiles.js", - ".devcontainer/defaults/codeforge/config/settings.base.json", - ".devcontainer/defaults/codeforge/config/settings.json", - ".devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json", - ".devcontainer/defaults/codeforge/config/settings-opus-46-200k.json", - ".devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json", - ".devcontainer/defaults/codeforge/config/settings-opus-45-200k.json", - ".devcontainer/defaults/codeforge/file-manifest.json", - ".devcontainer/features/oh-my-claude/devcontainer-feature.json", - ".devcontainer/features/oh-my-claude/install.sh", - ".devcontainer/features/oh-my-claude/README.md", - ".devcontainer/features/claude-code-karma/devcontainer-feature.json", - ".devcontainer/features/claude-code-karma/install.sh", - ".devcontainer/features/claude-code-karma/README.md", - ]; +function fail(message) { + console.log(`not ok - ${message}`); + failed = true; +} - let allFilesExist = true; - requiredFiles.forEach((file) => { - if (fs.existsSync(path.join(__dirname, file))) { - console.log( - `✓ Test 3.${requiredFiles.indexOf(file) + 1}: ${file} exists`, - ); - } else { - console.log( - `❌ Test 3.${requiredFiles.indexOf(file) + 1}: ${file} missing`, - ); - allFilesExist = false; - } - }); +function assert(condition, message) { + if (condition) { + pass(message); + } else { + fail(message); + } +} - // Test 4: Package.json has correct structure - const packageJson = JSON.parse( - fs.readFileSync(path.join(__dirname, "package.json"), "utf8"), - ); - const requiredFields = ["name", "version", "bin", "files"]; - let packageValid = true; +function readJson(file) { + return JSON.parse(fs.readFileSync(file, "utf8")); +} - requiredFields.forEach((field) => { - if (packageJson[field]) { - console.log( - `✓ Test 4.${requiredFields.indexOf(field) + 1}: package.json has ${field}`, - ); - } else { - console.log( - `❌ Test 4.${requiredFields.indexOf(field) + 1}: package.json missing ${field}`, - ); - packageValid = false; - } - }); +function exists(relativePath) { + return fs.existsSync(path.join(root, relativePath)); +} - // Test 5: Setup script is executable - let setupExecutable = true; - const setupStat = fs.statSync(path.join(__dirname, "setup.js")); - if (setupStat.mode & 0o111) { - console.log("✓ Test 5: setup.js is executable"); - } else { - console.log("❌ Test 5: setup.js is not executable"); - setupExecutable = false; - } +console.log("Running CodeForge package tests\n"); - // Test 6: New checksum and sync functions exist - let checksumFunctionsExist = true; - if (typeof computeChecksum === "function") { - console.log("✓ Test 6.1: computeChecksum function exists"); - } else { - console.log("❌ Test 6.1: computeChecksum function missing"); - checksumFunctionsExist = false; - } - if (typeof generateChecksums === "function") { - console.log("✓ Test 6.2: generateChecksums function exists"); - } else { - console.log("❌ Test 6.2: generateChecksums function missing"); - checksumFunctionsExist = false; - } - if (typeof syncCodeforgeDirectory === "function") { - console.log("✓ Test 6.3: syncCodeforgeDirectory function exists"); - } else { - console.log("❌ Test 6.3: syncCodeforgeDirectory function missing"); - checksumFunctionsExist = false; - } +assert(typeof copyDirectory === "function", "copyDirectory export exists"); +assert(typeof main === "function", "main export exists"); +assert(typeof computeChecksum === "function", "computeChecksum export exists"); +assert( + typeof generateChecksums === "function", + "generateChecksums export exists", +); +assert( + typeof ensureCodeforgeScaffold === "function", + "ensureCodeforgeScaffold export exists", +); +assert( + typeof mergeManifestEntries === "function", + "mergeManifestEntries export exists", +); - // Test 7: generateChecksums produces expected structure - let checksumStructureValid = true; - const defaultsDir = path.join( - __dirname, - ".devcontainer", - "defaults", - "codeforge", - ); - if (fs.existsSync(defaultsDir)) { - const checksums = generateChecksums(defaultsDir); - if (typeof checksums === "object" && checksums !== null) { - const keys = Object.keys(checksums); - if (keys.length > 0) { - const firstValue = checksums[keys[0]]; - if (typeof firstValue === "string" && firstValue.length === 64) { - console.log( - "✓ Test 7: generateChecksums returns valid SHA-256 hex map", - ); - } else { - console.log( - "❌ Test 7: generateChecksums values are not SHA-256 hex strings", - ); - checksumStructureValid = false; - } - } else { - console.log("❌ Test 7: generateChecksums returned empty map"); - checksumStructureValid = false; - } - } else { - console.log("❌ Test 7: generateChecksums did not return an object"); - checksumStructureValid = false; - } - } else { - console.log( - "❌ Test 7: .devcontainer/defaults/codeforge/ not found, skipping", - ); - checksumStructureValid = false; - } +const requiredFiles = [ + "package.json", + "setup.js", + "README.md", + ".devcontainer/devcontainer.json", + ".devcontainer/scripts/setup.sh", + ".devcontainer/scripts/setup-config.sh", + ".devcontainer/scripts/setup-migrate-codeforge.sh", + ".devcontainer/scripts/setup-migrate-codeforge-v3.sh", + ".devcontainer/scripts/ensure-settings-generated.sh", + ".devcontainer/scripts/generate-settings-profiles.js", + ".devcontainer/defaults/codeforge/file-manifest.json", + ".devcontainer/defaults/codeforge/claude/settings/base.json", + ".devcontainer/defaults/codeforge/claude/settings/profiles/opus-45-200k.json", + ".devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-200k.json", + ".devcontainer/defaults/codeforge/claude/settings/profiles/opus-46-1m-400k.json", + ".devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-200k.json", + ".devcontainer/defaults/codeforge/claude/settings/profiles/opus-47-1m-400k.json", + ".devcontainer/defaults/codeforge/claude/system-prompts/main.md", + ".devcontainer/defaults/codeforge/claude/system-prompts/writing.md", + ".devcontainer/defaults/codeforge/claude/system-prompts/orchestrator.md", + ".devcontainer/defaults/codeforge/claude/statusline/settings.json", + ".devcontainer/defaults/codeforge/claude/router/config.json", + ".devcontainer/defaults/codeforge/codex/config.toml", + ".devcontainer/defaults/codeforge/codex/AGENTS.md", + ".devcontainer/defaults/codeforge/rtk/config.toml", + ".devcontainer/features/oh-my-claude/devcontainer-feature.json", + ".devcontainer/features/claude-code-karma/devcontainer-feature.json", +]; +for (const file of requiredFiles) { + assert(exists(file), `${file} exists`); +} - // Test 8: Defaults directory has expected config structure - let defaultsStructureValid = true; - const expectedSubdirs = ["config"]; - const expectedFiles = [ - "file-manifest.json", - "config/settings.base.json", - "config/settings.json", - "config/settings-opus-47-1m-400k.json", - "config/settings-opus-46-200k.json", - "config/settings-opus-46-1m-400k.json", - "config/settings-opus-45-200k.json", - ]; +const removedSourceFiles = [ + ".devcontainer/defaults/codeforge/config/settings.json", + ".devcontainer/defaults/codeforge/config/settings-opus-45-200k.json", + ".devcontainer/defaults/codeforge/config/settings-opus-46-200k.json", + ".devcontainer/defaults/codeforge/config/settings-opus-46-1m-400k.json", + ".devcontainer/defaults/codeforge/config/settings-opus-47-1m-400k.json", +]; +for (const file of removedSourceFiles) { + assert(!exists(file), `${file} is not a packaged source file`); +} - if (fs.existsSync(defaultsDir)) { - for (const subdir of expectedSubdirs) { - const subdirPath = path.join(defaultsDir, subdir); - if ( - !fs.existsSync(subdirPath) || - !fs.statSync(subdirPath).isDirectory() - ) { - console.log(`❌ Test 8: Missing expected subdirectory: ${subdir}`); - defaultsStructureValid = false; - } - } - for (const file of expectedFiles) { - const filePath = path.join(defaultsDir, file); - if (!fs.existsSync(filePath)) { - console.log(`❌ Test 8: Missing expected file: ${file}`); - defaultsStructureValid = false; - } - } - if (defaultsStructureValid) { - console.log("✓ Test 8: Defaults directory has expected structure"); - } - } else { - console.log("❌ Test 8: .devcontainer/defaults/codeforge/ not found"); - defaultsStructureValid = false; - } +const packageJson = readJson(path.join(root, "package.json")); +assert(packageJson.name, "package.json has name"); +assert(packageJson.version, "package.json has version"); +assert(packageJson.bin, "package.json has bin"); +assert(Array.isArray(packageJson.files), "package.json has files list"); +assert( + !packageJson.files.some((entry) => entry.startsWith(".codeforge")), + "package does not publish a .codeforge defaults tree", +); - // Test 9: Settings profiles are generated from base + overlays - let settingsProfilesValid = true; - const configDir = path.join(defaultsDir, "config"); - const profileDir = path.join(configDir, "settings-profiles"); - const profileMatrix = [ - ["opus-47-200k.json", "settings.json"], - ["opus-47-1m-400k.json", "settings-opus-47-1m-400k.json"], - ["opus-46-200k.json", "settings-opus-46-200k.json"], - ["opus-46-1m-400k.json", "settings-opus-46-1m-400k.json"], - ["opus-45-200k.json", "settings-opus-45-200k.json"], - ]; - const profileEnvKeys = [ - "ANTHROPIC_MODEL", - "ANTHROPIC_DEFAULT_OPUS_MODEL", - "CLAUDE_CODE_MAX_CONTEXT_TOKENS", - "CLAUDE_CODE_AUTO_COMPACT_WINDOW", - "CLAUDE_CODE_EFFORT_LEVEL", - "MAX_THINKING_TOKENS", - "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING", - ]; - const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, "utf8")); - const isObject = (value) => - value !== null && typeof value === "object" && !Array.isArray(value); - const merge = (base, overlay) => { - const result = { ...base }; - for (const [key, value] of Object.entries(overlay)) { - if (isObject(value) && isObject(result[key])) { - result[key] = merge(result[key], value); - } else { - result[key] = value; - } - } - return result; - }; - const stripProfileFields = (settings) => { - const stripped = JSON.parse(JSON.stringify(settings)); - delete stripped.model; - delete stripped.autoCompactWindow; - delete stripped.effortLevel; - if (stripped.env) { - for (const key of profileEnvKeys) { - delete stripped.env[key]; - } - } - return stripped; - }; - try { - const base = readJson(path.join(configDir, "settings.base.json")); - for (const [overlayFile, outputFile] of profileMatrix) { - const overlay = readJson(path.join(profileDir, overlayFile)); - const generated = readJson(path.join(configDir, outputFile)); - const expected = merge(base, overlay); - if (JSON.stringify(generated) !== JSON.stringify(expected)) { - console.log(`❌ Test 9: ${outputFile} is stale`); - settingsProfilesValid = false; - } - if ( - JSON.stringify(stripProfileFields(generated)) !== - JSON.stringify(stripProfileFields(base)) - ) { - console.log( - `❌ Test 9: ${outputFile} diverges from base outside profile fields`, - ); - settingsProfilesValid = false; - } - } - if (settingsProfilesValid) { - console.log("✓ Test 9: Settings profiles preserve shared base settings"); - } - } catch (error) { - console.log(`❌ Test 9: Settings profile validation failed: ${error}`); - settingsProfilesValid = false; - } +const scaffoldDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-scaffold-")); +ensureCodeforgeScaffold(scaffoldDir); +assert(fs.existsSync(path.join(scaffoldDir, "README.md")), "scaffold writes README"); +assert( + fs.existsSync(path.join(scaffoldDir, ".markers")), + "scaffold creates marker directory", +); +assert( + fs.existsSync(path.join(scaffoldDir, "data")), + "scaffold creates data directory", +); +fs.rmSync(scaffoldDir, { recursive: true, force: true }); - // Test 10: oh-my-claude feature stays aligned with CodeForge ownership - let omcFeatureValid = true; - const omcInstallPath = path.join( - __dirname, - ".devcontainer", - "features", - "oh-my-claude", - "install.sh", +const checksums = generateChecksums(defaultsDir); +assert(Object.keys(checksums).length > 0, "generateChecksums sees defaults"); +assert( + Object.values(checksums).every( + (value) => typeof value === "string" && value.length === 64, + ), + "generateChecksums returns SHA-256 hex values", +); + +const generatorMarkerDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-marker-")); +execFileSync( + process.execPath, + [path.join(root, ".devcontainer", "scripts", "generate-settings-profiles.js")], + { + cwd: root, + stdio: "inherit", + env: { + ...process.env, + WORKSPACE_ROOT: root, + CODEFORGE_DIR: generatorMarkerDir, + }, + }, +); + +const generatedDefault = readJson(path.join(generatedSettingsDir, "settings.json")); +const generatedOpus46 = readJson( + path.join(generatedSettingsDir, "settings-opus-46-200k.json"), +); +assert( + JSON.stringify(generatedDefault) === JSON.stringify(generatedOpus46), + "settings.json matches opus-46-200k", +); + +const nonOneMillionOutputs = [ + "settings.json", + "settings-opus-46-200k.json", + "settings-opus-47-200k.json", + "settings-opus-45-200k.json", +]; +for (const output of nonOneMillionOutputs) { + const settings = readJson(path.join(generatedSettingsDir, output)); + assert( + settings.env?.CLAUDE_CODE_DISABLE_1M_CONTEXT === "1", + `${output} disables 1M context`, ); - const omcFeatureJsonPath = path.join( - __dirname, - ".devcontainer", - "features", - "oh-my-claude", - "devcontainer-feature.json", +} +for (const output of [ + "settings-opus-46-1m-400k.json", + "settings-opus-47-1m-400k.json", +]) { + const settings = readJson(path.join(generatedSettingsDir, output)); + assert( + !("CLAUDE_CODE_DISABLE_1M_CONTEXT" in (settings.env ?? {})), + `${output} does not disable 1M context`, ); - if (fs.existsSync(omcInstallPath) && fs.existsSync(omcFeatureJsonPath)) { - const omcInstall = fs.readFileSync(omcInstallPath, "utf8"); - const omcFeature = JSON.parse(fs.readFileSync(omcFeatureJsonPath, "utf8")); - if ( - omcInstall.includes("--skip-hooks") && - omcInstall.includes("--skip-mcp") - ) { - console.log("✓ Test 10.1: oh-my-claude install skips hooks and MCP"); - } else { - console.log("❌ Test 10.1: oh-my-claude install must skip hooks and MCP"); - omcFeatureValid = false; - } - if (!/omc proxy (start|stop|restart)/.test(omcInstall)) { - console.log( - "✓ Test 10.2: oh-my-claude install avoids stale daemon commands", - ); - } else { - console.log( - "❌ Test 10.2: oh-my-claude install uses stale daemon commands", - ); - omcFeatureValid = false; - } - if ( - omcFeature.options && - !omcFeature.options.autostart && - !omcFeature.options.installLaunchAliases - ) { - console.log( - "✓ Test 10.3: oh-my-claude feature delegates aliases to setup-aliases.sh and has no autostart", - ); - } else { - console.log( - "❌ Test 10.3: oh-my-claude feature should not own aliases (setup-aliases.sh does) and should not autostart", - ); - omcFeatureValid = false; - } - } else { - console.log("❌ Test 10: oh-my-claude feature files missing"); - omcFeatureValid = false; - } +} +assert( + fs.existsSync(path.join(generatorMarkerDir, ".markers", "settings-generated-v3")), + "settings generator writes v3 marker", +); +fs.rmSync(generatorMarkerDir, { recursive: true, force: true }); - // Summary - console.log("\n📊 Test Results:"); - if ( - allFilesExist && - packageValid && - setupExecutable && - checksumFunctionsExist && - checksumStructureValid && - defaultsStructureValid && - settingsProfilesValid && - omcFeatureValid - ) { - console.log("🎉 All tests passed! Package is ready for distribution."); - process.exit(0); - } else { - console.log("❌ Some tests failed. Check the errors above."); - process.exit(1); - } +const legacyProfileDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-legacy-profile-")); +const legacyOverrideDir = path.join( + legacyProfileDir, + "claude", + "settings", + "profiles", +); +fs.mkdirSync(legacyOverrideDir, { recursive: true }); +fs.writeFileSync( + path.join(legacyOverrideDir, "opus-46-200k.json"), + JSON.stringify( + { + model: "claude-opus-4-6", + autoCompactWindow: 200000, + env: { + CLAUDE_CODE_MAX_CONTEXT_TOKENS: "200000", + CLAUDE_CODE_AUTO_COMPACT_WINDOW: "200000", + }, + }, + null, + "\t", + ) + "\n", +); +execFileSync( + process.execPath, + [path.join(root, ".devcontainer", "scripts", "generate-settings-profiles.js")], + { + cwd: root, + stdio: "inherit", + env: { + ...process.env, + WORKSPACE_ROOT: root, + CODEFORGE_DIR: legacyProfileDir, + }, + }, +); +const generatedLegacyProfile = readJson( + path.join(generatedSettingsDir, "settings-opus-46-200k.json"), +); +assert( + generatedLegacyProfile.env?.CLAUDE_CODE_DISABLE_1M_CONTEXT === "1", + "legacy non-1M profile override disables 1M context", +); +fs.rmSync(legacyProfileDir, { recursive: true, force: true }); + +const manifest = readJson(path.join(defaultsDir, "file-manifest.json")); +assert(manifest.length > 0, "default manifest has entries"); +assert(manifest.every((entry) => entry.id), "default manifest entries have stable ids"); +assert( + manifest.find((entry) => entry.id === "claude.state")?.dest === "${HOME}", + "Claude state deploys to home directory", +); +const merged = mergeManifestEntries(manifest, [ + { id: manifest[0].id, disabled: true, src: manifest[0].src }, + { + id: "custom.example", + src: "claude/system-prompts/main.md", + dest: "${CLAUDE_CONFIG_DIR}", + overwrite: "if-changed", + }, +]); +assert( + merged.find((entry) => entry.id === manifest[0].id)?.enabled === false, + "user manifest can disable a default by id", +); +assert( + merged.some((entry) => entry.id === "custom.example"), + "user manifest can add entries by id", +); +for (const entry of manifest) { + const source = [ + path.join(root, ".devcontainer", ".generated", "codeforge", entry.src), + path.join(defaultsDir, entry.src), + ].find((candidate) => fs.existsSync(candidate)); + assert(Boolean(source), `manifest source exists: ${entry.id}`); } -if (require.main === module) { - runTests(); +const migrationScript = fs.readFileSync( + path.join(root, ".devcontainer", "scripts", "setup-migrate-codeforge-v3.sh"), + "utf8", +); +assert( + migrationScript.includes("config-layout-v3") && + migrationScript.includes("config-layout-v3-report.md"), + "v3 migration script writes marker and report", +); + +const setupScript = fs.readFileSync(path.join(root, ".devcontainer", "scripts", "setup.sh"), "utf8"); +assert( + setupScript.includes("setup-migrate-codeforge-v3.sh") && + setupScript.includes("ensure-settings-generated.sh"), + "setup runs v3 migration and settings generation", +); + +console.log(""); +if (failed) { + console.log("Some tests failed."); + process.exit(1); } + +console.log("All tests passed."); From a73b3a48c0b37f89fd5c88cc4f59c0ab99634155 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:36:20 +0000 Subject: [PATCH 11/27] feat(cli): support v3 config layout with manifest merging Update config discovery to search .devcontainer/defaults/codeforge/ alongside .codeforge/. Add multi-root manifest loading that merges packaged defaults with user overrides by entry id. Add generated settings path resolution and on-demand profile generation. Expand variable substitution to support WORKSPACE_ROOT and CODEFORGE_DIR. Bump to v3.0.0. --- cli/package.json | 2 +- cli/src/commands/config/apply.ts | 48 ++++++++++++++++++-- cli/src/index.ts | 2 +- cli/src/loaders/config-loader.ts | 78 +++++++++++++++++++++++++++++--- cli/src/loaders/plugin-loader.ts | 69 +++++++++++++++++++--------- cli/src/schemas/config.ts | 2 + 6 files changed, 166 insertions(+), 35 deletions(-) mode change 100644 => 100755 cli/src/commands/config/apply.ts mode change 100644 => 100755 cli/src/loaders/plugin-loader.ts diff --git a/cli/package.json b/cli/package.json index 980cde5..2991f17 100644 --- a/cli/package.json +++ b/cli/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.2.1", + "version": "3.0.0", "description": "CLI for CodeForge development workflows", "keywords": [ "codeforge", diff --git a/cli/src/commands/config/apply.ts b/cli/src/commands/config/apply.ts old mode 100644 new mode 100755 index a85959c..a4e423e --- a/cli/src/commands/config/apply.ts +++ b/cli/src/commands/config/apply.ts @@ -1,9 +1,13 @@ import chalk from "chalk"; +import { spawnSync } from "child_process"; import type { Command } from "commander"; import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs"; import { homedir } from "os"; import { basename, dirname, resolve } from "path"; -import { loadFileManifest } from "../../loaders/config-loader.js"; +import { + loadFileManifest, + resolveManifestSource, +} from "../../loaders/config-loader.js"; interface ConfigApplyOptions { dryRun?: boolean; @@ -15,7 +19,10 @@ function findWorkspaceRoot(): string | null { let dir = process.cwd(); while (true) { - if (existsSync(resolve(dir, ".codeforge"))) { + if ( + existsSync(resolve(dir, ".codeforge")) || + existsSync(resolve(dir, ".devcontainer/defaults/codeforge")) + ) { return dir; } const parent = resolve(dir, ".."); @@ -26,7 +33,11 @@ function findWorkspaceRoot(): string | null { function expandVariables(path: string): string { return path - .replace(/\$\{CLAUDE_CONFIG_DIR\}/g, resolve(homedir(), ".claude")) + .replace(/\$\{WORKSPACE_ROOT\}/g, findWorkspaceRoot() ?? process.cwd()) + .replace( + /\$\{CODEFORGE_DIR\}/g, + resolve(findWorkspaceRoot() ?? process.cwd(), ".codeforge"), + ) .replace(/\$\{HOME\}/g, homedir()); } @@ -56,11 +67,29 @@ export function registerConfigApplyCommand(parent: Command): void { const workspaceRoot = findWorkspaceRoot(); if (!workspaceRoot) { console.error( - "Error: Could not find .codeforge/ directory in any parent", + "Error: Could not find CodeForge workspace in any parent", ); process.exit(1); } + const generator = resolve( + workspaceRoot, + ".devcontainer/scripts/generate-settings-profiles.js", + ); + if (existsSync(generator)) { + const result = spawnSync("node", [generator, "--if-stale"], { + stdio: "inherit", + env: { + ...process.env, + WORKSPACE_ROOT: workspaceRoot, + CODEFORGE_DIR: resolve(workspaceRoot, ".codeforge"), + }, + }); + if (result.status !== 0) { + process.exit(result.status ?? 1); + } + } + const manifest = await loadFileManifest(workspaceRoot); if (manifest.length === 0) { console.log("No files in manifest."); @@ -83,7 +112,14 @@ export function registerConfigApplyCommand(parent: Command): void { continue; } - const src = resolve(workspaceRoot, ".codeforge", entry.src); + const src = resolveManifestSource(workspaceRoot, entry.src); + if (!src) { + skipped++; + console.log( + ` ${chalk.yellow("\u2717")} ${entry.src} (source missing)`, + ); + continue; + } const destDir = expandVariables(entry.dest); const destFilename = entry.destFilename ?? basename(entry.src); const dest = resolve(destDir, destFilename); @@ -103,6 +139,8 @@ export function registerConfigApplyCommand(parent: Command): void { continue; } + // Note: TOCTOU race possible if file changes between check and copy. + // Acceptable for config deployment — files are not security-sensitive. if ( entry.overwrite === "if-changed" && destExists && diff --git a/cli/src/index.ts b/cli/src/index.ts index 35bb91c..7dfe79a 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -40,7 +40,7 @@ const program = new Command(); program .name("codeforge") .description("CLI for CodeForge development workflows (experimental)") - .version("0.1.0") + .version("3.0.0") .option("--local", "Run against local host filesystem (skip container proxy)") .option("--container ", "Target a specific container by name"); diff --git a/cli/src/loaders/config-loader.ts b/cli/src/loaders/config-loader.ts index 6a0626b..acb97bb 100644 --- a/cli/src/loaders/config-loader.ts +++ b/cli/src/loaders/config-loader.ts @@ -1,6 +1,6 @@ -import { mkdirSync } from "fs"; +import { existsSync, mkdirSync } from "fs"; import { homedir } from "os"; -import { resolve } from "path"; +import { isAbsolute, relative, resolve } from "path"; import type { ManifestEntry, SettingsJson } from "../schemas/config.js"; export async function loadSettings(path: string): Promise { @@ -27,11 +27,75 @@ export function findDeployedSettings(): string { export async function loadFileManifest( workspaceRoot: string, ): Promise { - try { - return await Bun.file( - resolve(workspaceRoot, ".codeforge/file-manifest.json"), - ).json(); - } catch { + const defaultManifest = resolve( + workspaceRoot, + ".devcontainer/defaults/codeforge/file-manifest.json", + ); + if (!existsSync(defaultManifest)) { return []; } + + const defaultEntries = await readManifest(defaultManifest); + const userEntries = await readManifest( + resolve(workspaceRoot, ".codeforge/file-manifest.json"), + ); + + return mergeManifestEntries(defaultEntries, userEntries); +} + +export function resolveManifestSource( + workspaceRoot: string, + src: string, +): string | null { + for (const root of [ + resolve(workspaceRoot, ".codeforge"), + resolve(workspaceRoot, ".devcontainer/.generated/codeforge"), + resolve(workspaceRoot, ".devcontainer/defaults/codeforge"), + ]) { + const candidate = resolve(root, src); + if (!isPathWithin(root, candidate)) { + continue; + } + if (existsSync(candidate)) { + return candidate; + } + } + return null; +} + +async function readManifest(path: string): Promise { + if (!existsSync(path)) { + return []; + } + return await Bun.file(path).json(); +} + +function mergeManifestEntries( + defaultEntries: ManifestEntry[], + userEntries: ManifestEntry[], +): ManifestEntry[] { + const byId = new Map(); + for (const entry of [...defaultEntries, ...userEntries]) { + const id = entry.id || entry.src; + if (!id) { + continue; + } + const previous = byId.get(id); + const merged = { + ...(previous ?? {}), + ...entry, + id, + overwrite: entry.overwrite ?? previous?.overwrite ?? "if-changed", + }; + if (entry.disabled === true) { + merged.enabled = false; + } + byId.set(id, merged); + } + return [...byId.values()]; +} + +function isPathWithin(root: string, candidate: string): boolean { + const rel = relative(resolve(root), resolve(candidate)); + return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel)); } diff --git a/cli/src/loaders/plugin-loader.ts b/cli/src/loaders/plugin-loader.ts old mode 100644 new mode 100755 index b4a43c7..46efa96 --- a/cli/src/loaders/plugin-loader.ts +++ b/cli/src/loaders/plugin-loader.ts @@ -1,3 +1,4 @@ +import { existsSync } from "fs"; import { homedir } from "os"; import { basename, dirname, resolve } from "path"; import type { SettingsJson } from "../schemas/config.js"; @@ -79,7 +80,9 @@ export async function loadPluginDetail(installPath: string): Promise<{ }); } } - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } // Agents try { @@ -96,9 +99,13 @@ export async function loadPluginDetail(installPath: string): Promise<{ description: fm.description || "", filename: basename(entry), }); - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } } - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } // Skills try { @@ -116,9 +123,13 @@ export async function loadPluginDetail(installPath: string): Promise<{ description: fm.description || "", dirname: dir, }); - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } } - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } // Scripts try { @@ -129,7 +140,9 @@ export async function loadPluginDetail(installPath: string): Promise<{ })) { scripts.push(basename(entry)); } - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } return { hooks, agents, skills, scripts }; } @@ -144,15 +157,13 @@ export function findSettingsPaths(): { let dir = process.cwd(); while (true) { - const candidate = resolve(dir, ".codeforge/config/settings.json"); - try { - const stat = Bun.file(candidate); - // Check if file exists by accessing size — throws if missing - if (stat.size !== undefined) { + for (const candidate of settingsSourceCandidates(dir)) { + if (existsSync(candidate)) { source = candidate; break; } - } catch {} + } + if (source) break; const parent = resolve(dir, ".."); if (parent === dir) break; @@ -163,21 +174,35 @@ export function findSettingsPaths(): { try { const workspaceRoot = findWorkspacePath(); if (workspaceRoot) { - const fallback = resolve( - workspaceRoot, - ".codeforge/config/settings.json", - ); - const stat = Bun.file(fallback); - if (stat.size !== undefined) { - source = fallback; + for (const fallback of settingsSourceCandidates(workspaceRoot)) { + if (existsSync(fallback)) { + source = fallback; + break; + } } } - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } } return { deployed, source }; } +function settingsSourceCandidates(workspaceRoot: string): string[] { + return [ + resolve( + workspaceRoot, + ".devcontainer/.generated/codeforge/claude/settings/settings.json", + ), + resolve(workspaceRoot, ".codeforge/claude/settings/base.json"), + resolve( + workspaceRoot, + ".devcontainer/defaults/codeforge/claude/settings/base.json", + ), + ]; +} + function resolveEnabled( qualifiedName: string, pluginName: string, @@ -248,7 +273,9 @@ export async function loadInstalledPlugins(): Promise { ).json(); description = pluginJson.description || ""; author = pluginJson.author?.name || ""; - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plugin-loader] ${err}`); + } const detail = await loadPluginDetail(entry.installPath); diff --git a/cli/src/schemas/config.ts b/cli/src/schemas/config.ts index 6a9a34c..3f247ff 100644 --- a/cli/src/schemas/config.ts +++ b/cli/src/schemas/config.ts @@ -1,8 +1,10 @@ export interface ManifestEntry { + id: string; src: string; dest: string; destFilename?: string; enabled?: boolean; + disabled?: boolean; overwrite: "if-changed" | "always" | "never"; } From 6caeff9c6e61a0032c936e48cbe0adb92cc74952 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:36:30 +0000 Subject: [PATCH 12/27] fix(cli): improve indexer performance, debug logging, and validation Cache file content before DB mutations in indexer to reduce disk I/O. Add DEBUG env var logging to plan/task/plugin loaders for silent failure diagnostics. Add port range validation to proxy command. Add strict JSON parsing with type guards in search engine. Respect CLAUDE_PROJECTS_DIR in session discovery. Add null check for CA cert generation. --- cli/src/commands/index/build.ts | 30 ++++++++++++++++++++---------- cli/src/commands/proxy.ts | 10 ++++++++++ cli/src/indexer/db.ts | 1 + cli/src/loaders/plan-loader.ts | 4 +++- cli/src/loaders/task-loader.ts | 4 +++- cli/src/search/engine.ts | 19 ++++++++++++++----- cli/src/utils/glob.ts | 2 +- cli/src/utils/mitmproxy.ts | 5 +++++ 8 files changed, 57 insertions(+), 18 deletions(-) diff --git a/cli/src/commands/index/build.ts b/cli/src/commands/index/build.ts index 161d7f8..6951880 100644 --- a/cli/src/commands/index/build.ts +++ b/cli/src/commands/index/build.ts @@ -93,6 +93,20 @@ export function registerIndexBuildCommand(parent: Command): void { } } + // Read all file content BEFORE any DB mutations + const fileData = new Map< + string, + { hash: string; content: string; ext: string; lang: string } + >(); + for (const relPath of filesToProcess) { + const absPath = resolve(workspaceRoot, relPath); + const ext = "." + (relPath.split(".").pop() ?? ""); + const lang = getLanguageForExtension(ext) ?? "unknown"; + const hash = await hashFileContent(absPath); + const content = await Bun.file(absPath).text(); + fileData.set(relPath, { hash, content, ext, lang }); + } + // Delete old data for changed + deleted files for (const file of [ ...scanned.changedFiles, @@ -101,21 +115,17 @@ export function registerIndexBuildCommand(parent: Command): void { deleteFileAndSymbols(db, file); } - // Insert file records first (symbols have FK to files) + // Insert file records using cached data const fileRecords: IndexedFile[] = []; for (const relPath of filesToProcess) { - const absPath = resolve(workspaceRoot, relPath); - const ext = "." + (relPath.split(".").pop() ?? ""); - const lang = getLanguageForExtension(ext) ?? "unknown"; - const hash = await hashFileContent(absPath); - const content = await Bun.file(absPath).text(); - const lineCount = content.split("\n").length; - const size = Buffer.byteLength(content, "utf-8"); + const data = fileData.get(relPath)!; + const lineCount = data.content.split("\n").length; + const size = Buffer.byteLength(data.content, "utf-8"); fileRecords.push({ path: relPath, - hash, + hash: data.hash, size, - language: lang, + language: data.lang, lineCount, lastIndexed: new Date() .toISOString() diff --git a/cli/src/commands/proxy.ts b/cli/src/commands/proxy.ts index 3f1beb1..b29cc4a 100644 --- a/cli/src/commands/proxy.ts +++ b/cli/src/commands/proxy.ts @@ -72,6 +72,16 @@ export function registerProxyCommand(parent: Command): void { process.exit(1); } + if ( + proxyPort < 1 || + proxyPort > 65535 || + webPort < 1 || + webPort > 65535 + ) { + console.error(`${chalk.red("✗")} Port must be between 1 and 65535.`); + process.exit(1); + } + if (await isPortInUse(proxyPort)) { console.error( `${chalk.red("✗")} Port ${proxyPort} is already in use.`, diff --git a/cli/src/indexer/db.ts b/cli/src/indexer/db.ts index 7c908bc..930f9ce 100644 --- a/cli/src/indexer/db.ts +++ b/cli/src/indexer/db.ts @@ -151,6 +151,7 @@ export function searchSymbols( ORDER BY fts.rank LIMIT ?`, ) + // Type cast from SQLite row — schema enforced at table creation (see createTables above) .all(query, limit) as Array<{ id: number; name: string; diff --git a/cli/src/loaders/plan-loader.ts b/cli/src/loaders/plan-loader.ts index 07d99f7..a01adb3 100644 --- a/cli/src/loaders/plan-loader.ts +++ b/cli/src/loaders/plan-loader.ts @@ -26,7 +26,9 @@ export async function loadPlans(): Promise { } results.push({ slug, filePath: entry, title, content }); - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[plan-loader] ${err}`); + } } } catch { // Directory doesn't exist — return empty diff --git a/cli/src/loaders/task-loader.ts b/cli/src/loaders/task-loader.ts index 6ef9a05..bc236a6 100644 --- a/cli/src/loaders/task-loader.ts +++ b/cli/src/loaders/task-loader.ts @@ -28,7 +28,9 @@ export async function loadTasks(options?: { if (options?.status && task.status !== options.status) continue; results.push({ ...task, team }); - } catch {} + } catch (err) { + if (process.env.DEBUG) console.error(`[task-loader] ${err}`); + } } } catch { // Directory doesn't exist — return empty diff --git a/cli/src/search/engine.ts b/cli/src/search/engine.ts index 17518e9..f37af0b 100644 --- a/cli/src/search/engine.ts +++ b/cli/src/search/engine.ts @@ -115,25 +115,34 @@ export async function search(options: SearchOptions): Promise { try { for await (const line of readLines(filePath)) { - let raw: SessionMessage; + let raw: unknown; try { - raw = JSON.parse(line) as SessionMessage; + raw = JSON.parse(line); } catch { // Skip malformed JSON lines continue; } + if (!raw || typeof raw !== "object") continue; + + const msg_raw = raw as SessionMessage; + // Must have required fields - if (!raw.type || !raw.sessionId || !raw.uuid || !raw.timestamp) { + if ( + !msg_raw.type || + !msg_raw.sessionId || + !msg_raw.uuid || + !msg_raw.timestamp + ) { continue; } // Skip non-searchable types (progress, queue-operation, etc.) - if (!isSearchableType(raw.type)) { + if (!isSearchableType(msg_raw.type)) { continue; } - const msg = toSearchableMessage(raw, filePath); + const msg = toSearchableMessage(msg_raw, filePath); // Apply filters if (!filter(msg)) continue; diff --git a/cli/src/utils/glob.ts b/cli/src/utils/glob.ts index 28cb367..ee000e6 100644 --- a/cli/src/utils/glob.ts +++ b/cli/src/utils/glob.ts @@ -2,7 +2,7 @@ import { statSync } from "fs"; import { getHome, resolveNormalized } from "./platform.js"; const DEFAULT_PATTERN = "**/*.jsonl"; -const DEFAULT_BASE_DIR = ".claude/projects"; +const DEFAULT_BASE_DIR = process.env.CLAUDE_PROJECTS_DIR ?? ".claude/projects"; export async function discoverSessionFiles( pattern?: string, diff --git a/cli/src/utils/mitmproxy.ts b/cli/src/utils/mitmproxy.ts index 47d2323..a6e5146 100644 --- a/cli/src/utils/mitmproxy.ts +++ b/cli/src/utils/mitmproxy.ts @@ -61,6 +61,11 @@ export async function ensureCaCert(): Promise { proc.kill(); return certPath; } + if (proc.exitCode !== null) { + throw new Error( + "mitmproxy exited unexpectedly during CA certificate generation", + ); + } await new Promise((resolve) => setTimeout(resolve, 500)); } From 5518df81dde8b3ce5ab6a0a730ac23f87b883a08 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:36:42 +0000 Subject: [PATCH 13/27] feat(container): add 6 new devcontainer features Add RTK (Rust Token Killer) for transparent Bash output compression. Add claude-mem for persistent memory via SQLite + Chroma vectors. Add ccdiag for Go-based session diagnostics (orphans, errors, tokens). Add claude-session-analyzer for session quality metrics. Add lamarck for automated skill analysis from session history. Add sandcastle for multi-agent orchestration via git worktrees. --- .../features/ccdiag/devcontainer-feature.json | 20 ++ .../.devcontainer/features/ccdiag/install.sh | 122 ++++++++ .../claude-mem/devcontainer-feature.json | 41 +++ .../features/claude-mem/install.sh | 265 ++++++++++++++++++ .../devcontainer-feature.json | 20 ++ .../claude-session-analyzer/install.sh | 92 ++++++ .../lamarck/devcontainer-feature.json | 26 ++ .../.devcontainer/features/lamarck/install.sh | 123 ++++++++ .../features/rtk/devcontainer-feature.json | 20 ++ .../.devcontainer/features/rtk/install.sh | 118 ++++++++ .../sandcastle/devcontainer-feature.json | 20 ++ .../features/sandcastle/install.sh | 126 +++++++++ 12 files changed, 993 insertions(+) create mode 100644 container/.devcontainer/features/ccdiag/devcontainer-feature.json create mode 100755 container/.devcontainer/features/ccdiag/install.sh create mode 100644 container/.devcontainer/features/claude-mem/devcontainer-feature.json create mode 100755 container/.devcontainer/features/claude-mem/install.sh create mode 100644 container/.devcontainer/features/claude-session-analyzer/devcontainer-feature.json create mode 100755 container/.devcontainer/features/claude-session-analyzer/install.sh create mode 100644 container/.devcontainer/features/lamarck/devcontainer-feature.json create mode 100755 container/.devcontainer/features/lamarck/install.sh create mode 100644 container/.devcontainer/features/rtk/devcontainer-feature.json create mode 100755 container/.devcontainer/features/rtk/install.sh create mode 100644 container/.devcontainer/features/sandcastle/devcontainer-feature.json create mode 100755 container/.devcontainer/features/sandcastle/install.sh diff --git a/container/.devcontainer/features/ccdiag/devcontainer-feature.json b/container/.devcontainer/features/ccdiag/devcontainer-feature.json new file mode 100644 index 0000000..a41c0f9 --- /dev/null +++ b/container/.devcontainer/features/ccdiag/devcontainer-feature.json @@ -0,0 +1,20 @@ +{ + "id": "ccdiag", + "version": "1.0.0", + "name": "ccdiag - Claude Code Session Diagnostics", + "description": "CLI tool for analyzing Claude Code sessions: orphaned tool calls, errors, token usage, and API proxy", + "documentationURL": "https://github.com/kolkov/ccdiag", + "options": { + "version": { + "type": "string", + "description": "Version to install (use 'none' to skip)", + "default": "latest" + }, + "username": { + "type": "string", + "description": "Container user", + "default": "automatic" + } + }, + "installsAfter": ["ghcr.io/devcontainers/features/go"] +} diff --git a/container/.devcontainer/features/ccdiag/install.sh b/container/.devcontainer/features/ccdiag/install.sh new file mode 100755 index 0000000..d4b7904 --- /dev/null +++ b/container/.devcontainer/features/ccdiag/install.sh @@ -0,0 +1,122 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +# === IMPORT OPTIONS === +CCDIAG_VERSION="${VERSION:-latest}" +USERNAME="${USERNAME:-automatic}" + +# Skip installation if version is "none" +if [ "${CCDIAG_VERSION}" = "none" ]; then + echo "[ccdiag] Skipping installation (version=none)" + exit 0 +fi + +echo "[ccdiag] Starting ccdiag installation..." + +# === SOURCE NVM === +if [ -f /usr/local/share/nvm/nvm.sh ]; then + source /usr/local/share/nvm/nvm.sh +fi + +# === VALIDATE DEPENDENCIES === +if ! go version &>/dev/null; then + echo "[ccdiag] ERROR: Go is not available" + echo " Ensure the Go feature is installed first" + echo " (ghcr.io/devcontainers/features/go)" + exit 1 +fi + +# === DETECT USER === +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + for CURRENT_USER in vscode node codespace; do + if id -u "${CURRENT_USER}" >/dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root +fi + +echo "[ccdiag] Installing for user: ${USERNAME}" + +# === GET USER HOME === +USER_HOME=$(getent passwd "${USERNAME}" | cut -d: -f6) +if [ ! -d "${USER_HOME}" ]; then + echo "[ccdiag] ERROR: Home directory not found for user ${USERNAME}" + exit 1 +fi + +# === INSTALL VIA GO === +export GOPATH=/tmp/go-install + +echo "[ccdiag] Installing ccdiag via go install..." +INSTALL_SUCCESS=false + +# Try @latest first (tagged release) +if [ "${CCDIAG_VERSION}" = "latest" ]; then + if GOPATH=/tmp/go-install go install github.com/kolkov/ccdiag/cmd/ccdiag@latest 2>/dev/null; then + INSTALL_SUCCESS=true + echo "[ccdiag] Installed via @latest" + elif GOPATH=/tmp/go-install go install github.com/kolkov/ccdiag/cmd/ccdiag@main 2>/dev/null; then + INSTALL_SUCCESS=true + echo "[ccdiag] Installed via @main (fallback)" + fi +else + if GOPATH=/tmp/go-install go install "github.com/kolkov/ccdiag/cmd/ccdiag@${CCDIAG_VERSION}" 2>/dev/null; then + INSTALL_SUCCESS=true + echo "[ccdiag] Installed via @${CCDIAG_VERSION}" + elif GOPATH=/tmp/go-install go install github.com/kolkov/ccdiag/cmd/ccdiag@main 2>/dev/null; then + INSTALL_SUCCESS=true + echo "[ccdiag] Installed via @main (fallback)" + fi +fi + +if [ "${INSTALL_SUCCESS}" = "false" ]; then + echo "[ccdiag] WARNING: Could not install ccdiag" + echo " go install failed for both @latest and @main" + echo " The container build will continue without ccdiag" + rm -rf /tmp/go-install + exit 0 +fi + +# === DEPLOY BINARY === +cp /tmp/go-install/bin/ccdiag /usr/local/bin/ccdiag +chmod +x /usr/local/bin/ccdiag +chown "${USERNAME}:${USERNAME}" /usr/local/bin/ccdiag + +# === CLEANUP === +rm -rf /tmp/go-install + +# === VERIFICATION === +echo "[ccdiag] Verifying installation..." +if ccdiag --help &>/dev/null; then + echo "[ccdiag] ✓ ccdiag is available" +else + echo "[ccdiag] WARNING: ccdiag binary installed but --help failed" + echo " The binary may require runtime dependencies" +fi + +# === SUMMARY === +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " ccdiag Installation Complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Configuration:" +echo " • User: ${USERNAME}" +echo " • Version: ${CCDIAG_VERSION}" +echo " • Binary: /usr/local/bin/ccdiag" +echo "" +echo "Usage:" +echo " ccdiag orphans # Find orphaned tool calls" +echo " ccdiag errors # Analyze session errors" +echo " ccdiag tokens # Token usage breakdown" +echo " ccdiag proxy # Launch API proxy" +echo " ccdiag --help # Full options" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container/.devcontainer/features/claude-mem/devcontainer-feature.json b/container/.devcontainer/features/claude-mem/devcontainer-feature.json new file mode 100644 index 0000000..33ae28d --- /dev/null +++ b/container/.devcontainer/features/claude-mem/devcontainer-feature.json @@ -0,0 +1,41 @@ +{ + "id": "claude-mem", + "version": "1.0.0", + "name": "Claude-Mem Memory System", + "description": "Persistent memory across Claude Code sessions with real-time observation capture, hybrid search (SQLite + Chroma vectors), and MCP tools", + "documentationURL": "https://github.com/thedotmack/claude-mem", + "options": { + "version": { + "type": "string", + "description": "npm version or git ref (use 'none' to skip)", + "default": "latest" + }, + "workerPort": { + "type": "string", + "description": "Worker service HTTP port", + "default": "37777" + }, + "autostart": { + "type": "string", + "description": "Auto-start worker on container start", + "default": "true", + "enum": ["true", "false"] + }, + "chromaMode": { + "type": "string", + "description": "Chroma vector store mode", + "default": "local", + "enum": ["local", "disabled"] + }, + "username": { + "type": "string", + "description": "Container user", + "default": "automatic" + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/node", + "ghcr.io/devcontainers-extra/features/uv", + "ghcr.io/rails/devcontainer/features/bun" + ] +} diff --git a/container/.devcontainer/features/claude-mem/install.sh b/container/.devcontainer/features/claude-mem/install.sh new file mode 100755 index 0000000..5b48dec --- /dev/null +++ b/container/.devcontainer/features/claude-mem/install.sh @@ -0,0 +1,265 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +# Import options from devcontainer-feature.json +CLAUDE_MEM_VERSION="${VERSION:-latest}" +WORKER_PORT="${WORKERPORT:-37777}" +AUTOSTART="${AUTOSTART:-true}" +CHROMA_MODE="${CHROMAMODE:-local}" +USERNAME="${USERNAME:-automatic}" + +# Skip installation if version is "none" +if [ "${CLAUDE_MEM_VERSION}" = "none" ]; then + echo "[claude-mem] Skipping installation (version=none)" + exit 0 +fi + +echo "[claude-mem] Starting installation..." +echo "[claude-mem] Version: ${CLAUDE_MEM_VERSION}" +echo "[claude-mem] Worker port: ${WORKER_PORT}" +echo "[claude-mem] Chroma mode: ${CHROMA_MODE}" +echo "[claude-mem] Autostart: ${AUTOSTART}" + +# Source NVM +if [ -f /usr/local/share/nvm/nvm.sh ]; then + # shellcheck disable=SC1091 + source /usr/local/share/nvm/nvm.sh +fi +export PATH="/home/vscode/.bun/bin:/root/.bun/bin:${PATH}" + +# Validate required tools +for cmd in bun node npm git; do + if ! command -v "${cmd}" >/dev/null 2>&1; then + echo "[claude-mem] ERROR: ${cmd} is required but not found" + exit 1 + fi +done + +# Detect user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + for current_user in vscode node codespace; do + if id -u "${current_user}" >/dev/null 2>&1; then + USERNAME="${current_user}" + break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root +fi + +# Get user home directory +USER_HOME="$(getent passwd "${USERNAME}" | cut -d: -f6)" +if [ -z "${USER_HOME}" ] || [ ! -d "${USER_HOME}" ]; then + echo "[claude-mem] ERROR: Home directory not found for user ${USERNAME}" + exit 1 +fi + +# Validate WORKER_PORT is numeric +if [[ ! "${WORKER_PORT}" =~ ^[0-9]+$ ]]; then + echo "[claude-mem] ERROR: workerPort must be numeric" + exit 1 +fi + +# Validate AUTOSTART is true/false +if [ "${AUTOSTART}" != "true" ] && [ "${AUTOSTART}" != "false" ]; then + echo "[claude-mem] ERROR: autostart must be true or false" + exit 1 +fi + +# Validate CHROMA_MODE is local/disabled +if [ "${CHROMA_MODE}" != "local" ] && [ "${CHROMA_MODE}" != "disabled" ]; then + echo "[claude-mem] ERROR: chromaMode must be local or disabled" + exit 1 +fi + +echo "[claude-mem] Installing for user: ${USERNAME} (${USER_HOME})" + +# Clone repository +rm -rf /opt/claude-mem +git clone https://github.com/thedotmack/claude-mem.git /opt/claude-mem/ + +# Checkout specific version if not "latest" +if [ "${CLAUDE_MEM_VERSION}" != "latest" ]; then + git -C /opt/claude-mem checkout --detach "${CLAUDE_MEM_VERSION}" +fi + +# Install dependencies +echo "[claude-mem] Installing npm dependencies..." +cd /opt/claude-mem && npm install --omit=dev --legacy-peer-deps + +# Create data directories +mkdir -p "${USER_HOME}/.claude-mem/chroma" "${USER_HOME}/.claude-mem/logs" + +# Create settings file +cat > "${USER_HOME}/.claude-mem/settings.json" < /usr/local/bin/claude-mem-worker <<'EOF' +#!/bin/bash +# claude-mem worker management +ACTION="${1:-status}" +PORT="${CLAUDE_MEM_WORKER_PORT:-37777}" +PID_FILE="/tmp/claude-mem-worker.pid" +LOG_FILE="/tmp/claude-mem-worker.log" + +if [ -f /usr/local/share/nvm/nvm.sh ]; then + source /usr/local/share/nvm/nvm.sh +fi +export PATH="/home/vscode/.bun/bin:/root/.bun/bin:${PATH}" + +case "${ACTION}" in + start) + if [ -f "${PID_FILE}" ] && kill -0 "$(cat "${PID_FILE}")" 2>/dev/null; then + echo "[claude-mem] Worker already running (PID $(cat "${PID_FILE}"))" + exit 0 + fi + bun /opt/claude-mem/plugin/scripts/worker-service.cjs start >>"${LOG_FILE}" 2>&1 & + echo $! > "${PID_FILE}" + echo "[claude-mem] Worker started (PID $!, port ${PORT})" + ;; + stop) + if [ -f "${PID_FILE}" ] && kill -0 "$(cat "${PID_FILE}")" 2>/dev/null; then + kill "$(cat "${PID_FILE}")" + rm -f "${PID_FILE}" + echo "[claude-mem] Worker stopped" + else + echo "[claude-mem] Worker not running" + fi + ;; + status) + if [ -f "${PID_FILE}" ] && kill -0 "$(cat "${PID_FILE}")" 2>/dev/null; then + echo "[claude-mem] Worker running (PID $(cat "${PID_FILE}"), port ${PORT})" + else + echo "[claude-mem] Worker not running" + fi + ;; + *) + echo "Usage: claude-mem-worker {start|stop|status}" + exit 1 + ;; +esac +EOF + +# Create wrapper script: claude-mem-hook +cat > /usr/local/bin/claude-mem-hook <<'EOF' +#!/bin/bash +# Thin HTTP proxy — sends hook payload to worker, exits 0 always +EVENT="${1:-unknown}" +PORT="${CLAUDE_MEM_WORKER_PORT:-37777}" +PAYLOAD=$(cat) +curl -s -m 10 -X POST "http://localhost:${PORT}/hook/${EVENT}" \ + -H "Content-Type: application/json" \ + -d "${PAYLOAD}" >/dev/null 2>&1 || true +exit 0 +EOF + +# Make wrapper scripts executable +chmod +x /usr/local/bin/claude-mem-worker +chmod +x /usr/local/bin/claude-mem-hook + +# Create poststart script (only if AUTOSTART=true) +if [ "${AUTOSTART}" = "true" ]; then + mkdir -p /usr/local/devcontainer-poststart.d + cat > /usr/local/devcontainer-poststart.d/45-claude-mem.sh </dev/null; then + export CLAUDE_MEM_WORKER_PORT="\${WORKER_PORT}" + bun "\${CLAUDE_MEM_HOME}/plugin/scripts/worker-service.cjs" start >>"\${LOG_FILE}" 2>&1 & + echo \$! > "\${PID_FILE}" + echo "[claude-mem] Worker started (PID \$!, port \${WORKER_PORT})" +else + echo "[claude-mem] Worker already running (PID \$(cat "\${PID_FILE}"))" +fi + +# Register MCP server in Claude Code settings +SETTINGS_FILE="\${CLAUDE_MEM_USER_HOME}/.claude/settings.json" +if [ ! -f "\${SETTINGS_FILE}" ]; then + echo '{}' > "\${SETTINGS_FILE}" +fi + +if ! command -v jq &>/dev/null; then + echo "[claude-mem] WARNING: jq not available, skipping MCP registration" + exit 0 +fi + +SERVER_CONFIG=\$(jq -n '{ + command: "bun", + args: ["/opt/claude-mem/plugin/scripts/mcp-server.cjs"] +}') + +TEMP_FILE=\$(mktemp) +jq --argjson server "\${SERVER_CONFIG}" \ + '.mcpServers["claude-mem"] = \$server' \ + "\${SETTINGS_FILE}" > "\${TEMP_FILE}" + +if jq empty "\${TEMP_FILE}" 2>/dev/null; then + mv "\${TEMP_FILE}" "\${SETTINGS_FILE}" + echo "[claude-mem] MCP server registered in Claude Code settings" +else + echo "[claude-mem] ERROR: Generated invalid JSON" + rm -f "\${TEMP_FILE}" +fi + +chmod 644 "\${SETTINGS_FILE}" +chown "${USERNAME}:${USERNAME}" "\${SETTINGS_FILE}" 2>/dev/null || true +EOF + chmod +x /usr/local/devcontainer-poststart.d/45-claude-mem.sh + echo "[claude-mem] Post-start hook installed" +else + echo "[claude-mem] Autostart disabled" +fi + +# Set ownership +chown -R "${USERNAME}:" /opt/claude-mem +chown -R "${USERNAME}:" "${USER_HOME}/.claude-mem" + +# Verify installation +if [ ! -d /opt/claude-mem/node_modules ]; then + echo "[claude-mem] ERROR: npm install failed (node_modules not found)" + exit 1 +fi + +# Print summary +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Claude-Mem Installation Complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " User: ${USERNAME}" +echo " Version: ${CLAUDE_MEM_VERSION}" +echo " Worker port: ${WORKER_PORT}" +echo " Chroma mode: ${CHROMA_MODE}" +echo " Autostart: ${AUTOSTART}" +echo "" +echo " MCP tools available:" +echo " - memory_store" +echo " - memory_search" +echo " - memory_recall" +echo " - observation_capture" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container/.devcontainer/features/claude-session-analyzer/devcontainer-feature.json b/container/.devcontainer/features/claude-session-analyzer/devcontainer-feature.json new file mode 100644 index 0000000..da69be1 --- /dev/null +++ b/container/.devcontainer/features/claude-session-analyzer/devcontainer-feature.json @@ -0,0 +1,20 @@ +{ + "id": "claude-session-analyzer", + "version": "1.0.0", + "name": "Claude Session Quality Analyzer", + "description": "Python tool for measuring session quality: thinking depth, Read:Edit ratio, frustration indicators", + "documentationURL": "https://github.com/lucemia/claude-session-analyzer", + "options": { + "version": { + "type": "string", + "description": "Git ref to install (use 'none' to skip)", + "default": "latest" + }, + "username": { + "type": "string", + "description": "Container user", + "default": "automatic" + } + }, + "installsAfter": ["ghcr.io/devcontainers-extra/features/uv"] +} diff --git a/container/.devcontainer/features/claude-session-analyzer/install.sh b/container/.devcontainer/features/claude-session-analyzer/install.sh new file mode 100755 index 0000000..79c93d6 --- /dev/null +++ b/container/.devcontainer/features/claude-session-analyzer/install.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +# === IMPORT OPTIONS === +ANALYZER_VERSION="${VERSION:-latest}" +USERNAME="${USERNAME:-automatic}" + +# Skip installation if version is "none" +if [ "${ANALYZER_VERSION}" = "none" ]; then + echo "[claude-session-analyzer] Skipping installation (version=none)" + exit 0 +fi + +echo "[claude-session-analyzer] Starting installation..." + +# === VALIDATE DEPENDENCIES === +for cmd in python3 git; do + if ! command -v "${cmd}" >/dev/null 2>&1; then + echo "[claude-session-analyzer] ERROR: ${cmd} is required but not found" + exit 1 + fi +done + +# === DETECT USER === +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + for current_user in vscode node codespace; do + if id -u "${current_user}" >/dev/null 2>&1; then + USERNAME="${current_user}" + break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root +fi + +echo "[claude-session-analyzer] Installing for user: ${USERNAME}" + +# === GET USER HOME === +USER_HOME="$(getent passwd "${USERNAME}" | cut -d: -f6)" +if [ -z "${USER_HOME}" ] || [ ! -d "${USER_HOME}" ]; then + echo "[claude-session-analyzer] ERROR: Home directory not found for user ${USERNAME}" + exit 1 +fi + +# === CLONE REPO === +rm -rf /opt/claude-session-analyzer +git clone https://github.com/lucemia/claude-session-analyzer.git /opt/claude-session-analyzer/ + +# Checkout specific ref if not "latest" +if [ "${ANALYZER_VERSION}" != "latest" ]; then + git -C /opt/claude-session-analyzer checkout --detach "${ANALYZER_VERSION}" +fi + +# === CREATE WRAPPER SCRIPT === +cat > /usr/local/bin/analyze-sessions <<'EOF' +#!/bin/bash +exec python3 /opt/claude-session-analyzer/analyze_sessions.py "$@" +EOF +chmod +x /usr/local/bin/analyze-sessions + +# === SET OWNERSHIP === +chown -R "${USERNAME}:" /opt/claude-session-analyzer + +# === VERIFICATION === +echo "[claude-session-analyzer] Verifying installation..." +if analyze-sessions --help >/dev/null 2>&1; then + echo "[claude-session-analyzer] ✓ analyze-sessions is accessible" +else + echo "[claude-session-analyzer] WARNING: Could not verify analyze-sessions" + echo " The tool uses Python stdlib only — check python3 availability" + echo " The wrapper will still work once the repo is accessible" +fi + +# === SUMMARY === +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Claude Session Analyzer Installation Complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Configuration:" +echo " • User: ${USERNAME}" +echo " • Version: ${ANALYZER_VERSION}" +echo "" +echo "Usage:" +echo " analyze-sessions ~/.claude/projects/ --start 2026-01-01" +echo " analyze-sessions --help" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container/.devcontainer/features/lamarck/devcontainer-feature.json b/container/.devcontainer/features/lamarck/devcontainer-feature.json new file mode 100644 index 0000000..debcb90 --- /dev/null +++ b/container/.devcontainer/features/lamarck/devcontainer-feature.json @@ -0,0 +1,26 @@ +{ + "id": "lamarck", + "version": "1.0.0", + "name": "Lamarck - Skill Analysis & Learning", + "description": "Analyzes Claude Code sessions to extract learnings into MEMORY.md. Unique skill analysis capability.", + "documentationURL": "https://github.com/johnlindquist/lamarck", + "options": { + "version": { + "type": "string", + "description": "npm version (use 'none' to skip)", + "default": "latest" + }, + "cronInterval": { + "type": "string", + "description": "Cron interval for batch processing", + "default": "4h", + "enum": ["2h", "4h", "6h", "12h", "disabled"] + }, + "username": { + "type": "string", + "description": "Container user", + "default": "automatic" + } + }, + "installsAfter": ["ghcr.io/devcontainers/features/node"] +} diff --git a/container/.devcontainer/features/lamarck/install.sh b/container/.devcontainer/features/lamarck/install.sh new file mode 100755 index 0000000..a2755ad --- /dev/null +++ b/container/.devcontainer/features/lamarck/install.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +# === IMPORT OPTIONS === +# NOTE: DevContainer converts camelCase options to UPPERCASE without underscores +# "version" → VERSION, "cronInterval" → CRONINTERVAL, "username" → USERNAME +LAMARCK_VERSION="${VERSION:-latest}" +CRONINTERVAL="${CRONINTERVAL:-4h}" +USERNAME="${USERNAME:-automatic}" + +# Skip installation if version is "none" +if [ "${LAMARCK_VERSION}" = "none" ]; then + echo "[lamarck] Skipping installation (version=none)" + exit 0 +fi + +echo "[lamarck] Starting lamarck installation..." + +# === SOURCE NVM === +# Node is installed via NVM by the node feature +if [ -f /usr/local/share/nvm/nvm.sh ]; then + source /usr/local/share/nvm/nvm.sh +fi + +# === VALIDATE DEPENDENCIES === +if ! command -v npm &>/dev/null; then + echo "[lamarck] ERROR: npm is not available" + echo " Ensure node feature is installed first" + echo " NVM path: /usr/local/share/nvm/nvm.sh" + exit 1 +fi + +# === DETECT USER === +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + for CURRENT_USER in vscode node codespace; do + if id -u "${CURRENT_USER}" >/dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + [ -z "${USERNAME}" ] && USERNAME=root +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then + USERNAME=root +fi + +echo "[lamarck] Installing for user: ${USERNAME}" + +# === GET USER HOME === +USER_HOME=$(getent passwd "${USERNAME}" | cut -d: -f6) +if [ ! -d "${USER_HOME}" ]; then + echo "[lamarck] ERROR: Home directory not found for user ${USERNAME}" + exit 1 +fi + +# === INSTALL === +echo "[lamarck] Installing lamarck@${LAMARCK_VERSION} globally..." +npm install -g "lamarck@${LAMARCK_VERSION}" + +# === VERIFY === +if lamarck --help &>/dev/null; then + echo "[lamarck] Verified: lamarck is available" +else + echo "[lamarck] WARNING: Could not verify lamarck installation" + echo " 'lamarck --help' did not succeed" + echo " The tool may still work once PATH is configured" +fi + +# === INSTALL CRON === +apt-get update && apt-get install -y --no-install-recommends cron + +# === CREATE STATE DIRECTORY === +mkdir -p "${USER_HOME}/.lamarck" +chown "${USERNAME}:" "${USER_HOME}/.lamarck" + +# === SET UP CRONTAB === +if [ "${CRONINTERVAL}" != "disabled" ]; then + case "${CRONINTERVAL}" in + 2h) CRON_SCHEDULE="0 */2 * * *" ;; + 4h) CRON_SCHEDULE="0 */4 * * *" ;; + 6h) CRON_SCHEDULE="0 */6 * * *" ;; + 12h) CRON_SCHEDULE="0 */12 * * *" ;; + *) + echo "[lamarck] ERROR: Invalid cronInterval: ${CRONINTERVAL}" + exit 1 + ;; + esac + + echo "${CRON_SCHEDULE} ${USERNAME} . /usr/local/share/nvm/nvm.sh && lamarck --project /workspaces >> /tmp/lamarck.log 2>&1" > /etc/cron.d/lamarck + chmod 0644 /etc/cron.d/lamarck + echo "[lamarck] Crontab installed: ${CRON_SCHEDULE}" +else + echo "[lamarck] Cron disabled, skipping crontab setup" +fi + +# === POSTSTART SCRIPT === +mkdir -p /usr/local/devcontainer-poststart.d +cat > /usr/local/devcontainer-poststart.d/46-lamarck-cron.sh <<'EOF' +#!/bin/bash +if ! pgrep -x cron >/dev/null 2>&1; then + cron + echo "[lamarck] cron daemon started" +fi +EOF +chmod +x /usr/local/devcontainer-poststart.d/46-lamarck-cron.sh + +# === SUMMARY === +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Lamarck Installation Complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Configuration:" +echo " • User: ${USERNAME}" +echo " • Version: ${LAMARCK_VERSION}" +echo " • Cron interval: ${CRONINTERVAL}" +echo "" +echo "Usage:" +echo " lamarck skill --mode suggest" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container/.devcontainer/features/rtk/devcontainer-feature.json b/container/.devcontainer/features/rtk/devcontainer-feature.json new file mode 100644 index 0000000..4067cc6 --- /dev/null +++ b/container/.devcontainer/features/rtk/devcontainer-feature.json @@ -0,0 +1,20 @@ +{ + "id": "rtk", + "version": "1.0.0", + "name": "RTK (Rust Token Killer)", + "description": "Installs RTK CLI proxy that compresses command output before it reaches LLM context windows (60-90% token savings)", + "documentationURL": "https://github.com/rtk-ai/rtk", + "options": { + "version": { + "type": "string", + "description": "Version to install: 'latest' or a specific semver. Use 'none' to skip.", + "default": "latest" + }, + "username": { + "type": "string", + "description": "Container user to install for", + "default": "automatic" + } + }, + "installsAfter": ["ghcr.io/devcontainers/features/node"] +} diff --git a/container/.devcontainer/features/rtk/install.sh b/container/.devcontainer/features/rtk/install.sh new file mode 100755 index 0000000..cfaadee --- /dev/null +++ b/container/.devcontainer/features/rtk/install.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +VERSION="${VERSION:-latest}" +USERNAME="${USERNAME:-automatic}" + +# Skip if version is "none" +if [ "${VERSION}" = "none" ]; then + echo "[rtk] Skipping installation (version=none)" + exit 0 +fi + +echo "[rtk] Starting installation..." +echo "[rtk] Version: ${VERSION}" + +# === VALIDATE DEPENDENCIES === +if ! command -v curl >/dev/null 2>&1; then + echo "[rtk] ERROR: curl is required" + exit 1 +fi + +if ! command -v tar >/dev/null 2>&1; then + echo "[rtk] ERROR: tar is required" + exit 1 +fi + +# === DETECT USER === +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + if [ -n "${_REMOTE_USER:-}" ]; then + USERNAME="${_REMOTE_USER}" + elif getent passwd vscode >/dev/null 2>&1; then + USERNAME="vscode" + elif getent passwd node >/dev/null 2>&1; then + USERNAME="node" + elif getent passwd codespace >/dev/null 2>&1; then + USERNAME="codespace" + else + USERNAME="root" + fi +fi + +USER_HOME=$(getent passwd "${USERNAME}" | cut -d: -f6) +if [ -z "${USER_HOME}" ]; then + echo "[rtk] ERROR: Could not determine home directory for ${USERNAME}" + exit 1 +fi + +echo "[rtk] Installing for user: ${USERNAME} (home: ${USER_HOME})" + +# === DETERMINE DOWNLOAD URL === +ARCH="$(uname -m)" +case "${ARCH}" in + x86_64) BINARY_ARCH="x86_64-unknown-linux-musl" ;; + aarch64) BINARY_ARCH="aarch64-unknown-linux-musl" ;; + *) + echo "[rtk] ERROR: Unsupported architecture: ${ARCH}" + exit 1 + ;; +esac + +if [ "${VERSION}" = "latest" ]; then + DOWNLOAD_URL="https://github.com/rtk-ai/rtk/releases/latest/download/rtk-${BINARY_ARCH}.tar.gz" +else + if ! echo "${VERSION}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then + echo "[rtk] ERROR: Invalid version '${VERSION}'" + echo " Use 'latest' or a specific semver (e.g., 0.38.0)" + exit 1 + fi + DOWNLOAD_URL="https://github.com/rtk-ai/rtk/releases/download/v${VERSION}/rtk-${BINARY_ARCH}.tar.gz" +fi + +# === INSTALL === +echo "[rtk] Downloading from: ${DOWNLOAD_URL}" + +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "${TMP_DIR}"' EXIT + +if ! curl -fsSL "${DOWNLOAD_URL}" -o "${TMP_DIR}/rtk.tar.gz"; then + echo "[rtk] ERROR: Failed to download RTK binary" + echo " URL: ${DOWNLOAD_URL}" + exit 1 +fi + +tar -xzf "${TMP_DIR}/rtk.tar.gz" -C "${TMP_DIR}" + +# Find the rtk binary (may be in a subdirectory) +RTK_BIN=$(find "${TMP_DIR}" -name "rtk" -type f -executable | head -1) +if [ -z "${RTK_BIN}" ]; then + # Try without executable flag (might not be preserved in tar) + RTK_BIN=$(find "${TMP_DIR}" -name "rtk" -type f | head -1) +fi + +if [ -z "${RTK_BIN}" ]; then + echo "[rtk] ERROR: Could not find rtk binary in archive" + ls -laR "${TMP_DIR}" + exit 1 +fi + +chmod +x "${RTK_BIN}" +mv "${RTK_BIN}" /usr/local/bin/rtk + +# === CREATE CONFIG DIRECTORY === +mkdir -p "${USER_HOME}/.config/rtk" +chown -R "${USERNAME}:" "${USER_HOME}/.config/rtk" + +# === VERIFICATION === +if command -v rtk >/dev/null 2>&1; then + INSTALLED_VERSION=$(rtk --version 2>/dev/null || echo "unknown") + echo "[rtk] RTK installed: ${INSTALLED_VERSION}" + echo "[rtk] Binary: $(command -v rtk)" +else + echo "[rtk] ERROR: Installation failed -- rtk not found in PATH" + exit 1 +fi + +echo "[rtk] Installation complete" diff --git a/container/.devcontainer/features/sandcastle/devcontainer-feature.json b/container/.devcontainer/features/sandcastle/devcontainer-feature.json new file mode 100644 index 0000000..18adc65 --- /dev/null +++ b/container/.devcontainer/features/sandcastle/devcontainer-feature.json @@ -0,0 +1,20 @@ +{ + "id": "sandcastle", + "version": "1.0.0", + "name": "Sandcastle (AI Agent Orchestration)", + "description": "Installs @ai-hero/sandcastle for orchestrating AI coding agents in isolated environments (worktrees)", + "documentationURL": "https://github.com/mattpocock/sandcastle", + "options": { + "version": { + "type": "string", + "description": "Version to install: 'latest' or a specific semver (>=0.5.4). Use 'none' to skip.", + "default": "latest" + }, + "username": { + "type": "string", + "description": "Container user to install for", + "default": "automatic" + } + }, + "installsAfter": ["ghcr.io/devcontainers/features/node"] +} diff --git a/container/.devcontainer/features/sandcastle/install.sh b/container/.devcontainer/features/sandcastle/install.sh new file mode 100755 index 0000000..0eb92b9 --- /dev/null +++ b/container/.devcontainer/features/sandcastle/install.sh @@ -0,0 +1,126 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +set -euo pipefail + +VERSION="${VERSION:-latest}" +USERNAME="${USERNAME:-automatic}" + +# Skip if version is "none" +if [ "${VERSION}" = "none" ]; then + echo "[sandcastle] Skipping installation (version=none)" + exit 0 +fi + +echo "[sandcastle] Starting installation..." +echo "[sandcastle] Version: ${VERSION}" + +# === VALIDATE DEPENDENCIES === +if ! command -v npm >/dev/null 2>&1; then + echo "[sandcastle] ERROR: npm is required" + echo " Ensure node feature is installed first" + exit 1 +fi + +if ! command -v git >/dev/null 2>&1; then + echo "[sandcastle] ERROR: git is required" + exit 1 +fi + +# === DETECT USER === +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + if [ -n "${_REMOTE_USER:-}" ]; then + USERNAME="${_REMOTE_USER}" + elif getent passwd vscode >/dev/null 2>&1; then + USERNAME="vscode" + elif getent passwd node >/dev/null 2>&1; then + USERNAME="node" + elif getent passwd codespace >/dev/null 2>&1; then + USERNAME="codespace" + else + USERNAME="root" + fi +fi + +USER_HOME=$(getent passwd "${USERNAME}" | cut -d: -f6) +if [ -z "${USER_HOME}" ]; then + echo "[sandcastle] ERROR: Could not determine home directory for ${USERNAME}" + exit 1 +fi + +echo "[sandcastle] Installing for user: ${USERNAME} (home: ${USER_HOME})" + +# === DETERMINE PACKAGE === +if [ "${VERSION}" = "latest" ]; then + PACKAGE="@ai-hero/sandcastle" +else + if ! echo "${VERSION}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then + echo "[sandcastle] ERROR: Invalid version '${VERSION}'" + echo " Use 'latest' or a semver (e.g., 0.5.7)" + exit 1 + fi + # Enforce minimum version for command injection CVE fix + MAJOR=$(echo "${VERSION}" | cut -d. -f1) + MINOR=$(echo "${VERSION}" | cut -d. -f2) + PATCH=$(echo "${VERSION}" | cut -d. -f3) + if [ "${MAJOR}" -eq 0 ] && [ "${MINOR}" -lt 5 ]; then + echo "[sandcastle] ERROR: Version ${VERSION} is below minimum safe version (0.5.4)" + echo " Versions <0.5.4 have a command injection vulnerability" + exit 1 + fi + if [ "${MAJOR}" -eq 0 ] && [ "${MINOR}" -eq 5 ] && [ "${PATCH}" -lt 4 ]; then + echo "[sandcastle] ERROR: Version ${VERSION} is below minimum safe version (0.5.4)" + echo " Versions <0.5.4 have a command injection vulnerability" + exit 1 + fi + PACKAGE="@ai-hero/sandcastle@${VERSION}" +fi + +# === INSTALL === +echo "[sandcastle] Installing ${PACKAGE} globally via npm..." + +if [ "${USERNAME}" = "root" ]; then + npm install -g "${PACKAGE}" +else + if su - "${USERNAME}" -c "npm install -g '${PACKAGE}'" 2>/dev/null; then + : # success + else + echo "[sandcastle] su failed, falling back to direct npm install..." + npm install -g "${PACKAGE}" + fi +fi + +# Clean npm cache to reduce image size +npm cache clean --force 2>/dev/null || true + +# === VERIFICATION === +if command -v sandcastle >/dev/null 2>&1; then + INSTALLED_VERSION=$(sandcastle --version 2>/dev/null || echo "unknown") + echo "[sandcastle] Sandcastle installed: ${INSTALLED_VERSION}" + echo "[sandcastle] Binary: $(command -v sandcastle)" +else + # Check common npm global paths + FOUND="" + for candidate in /usr/local/bin/sandcastle /usr/bin/sandcastle "${USER_HOME}/.npm-global/bin/sandcastle"; do + if [ -x "${candidate}" ]; then + FOUND="${candidate}" + break + fi + done + + if [ -n "${FOUND}" ]; then + INSTALLED_VERSION=$("${FOUND}" --version 2>/dev/null || echo "unknown") + echo "[sandcastle] Sandcastle installed: ${INSTALLED_VERSION}" + echo "[sandcastle] Binary: ${FOUND}" + else + echo "[sandcastle] ERROR: Installation failed -- sandcastle not found" + echo "[sandcastle] Checking npm global bin directory..." + npm bin -g 2>/dev/null || true + ls -la "$(npm bin -g 2>/dev/null)/" 2>/dev/null || true + exit 1 + fi +fi + +echo "[sandcastle] Installation complete" +echo "[sandcastle] Usage: sandcastle init (scaffold .sandcastle/ in a project)" +echo "[sandcastle] sandcastle run (execute a workflow)" From 951af7fc063b9678816c658d615d31dda887b33e Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:37:00 +0000 Subject: [PATCH 14/27] refactor(container): modernize plugins and update existing features Replace task-tracker with activity-tracker to track subagents and background bash commands. Add stale-entry pruning to quality gate. Extend protected-files-guard to block .codeforge/secrets/. Remove CLAUDE_CONFIG_DIR references from all features and agent-system scripts. Add rate limit reset time widgets to ccstatusline. Update AGENTS.md, CHANGELOG.md, and README for v3 architecture. --- container/.devcontainer/AGENTS.md | 150 +++++++++++++++--- container/.devcontainer/CHANGELOG.md | 57 +++++++ container/.devcontainer/README.md | 122 +++++++------- .../.devcontainer/features/ccburn/install.sh | 2 +- .../features/ccstatusline/README.md | 8 +- .../features/ccstatusline/install.sh | 57 ++++++- .../.devcontainer/features/ccusage/README.md | 4 +- .../features/claude-code-karma/install.sh | 2 +- .../features/claude-code-native/install.sh | 37 ----- .../features/claude-code-router/install.sh | 4 +- .../features/codex-cli/README.md | 8 +- .../features/hermes-agent/README.md | 2 +- .../features/mcp-qdrant/README.md | 2 +- .../features/mcp-qdrant/install.sh | 2 +- .../features/mcp-qdrant/poststart-hook.sh | 2 +- .../features/oh-my-claude/README.md | 15 +- .../agent-system/agents/claude-guide.md | 11 +- .../scripts/guard-readonly-bash.py | 2 +- .../scripts/redirect-builtin-agents.py | 2 +- .../scripts/teammate-idle-check.py | 4 +- .../.claude-plugin/plugin.json | 2 +- .../plugins/auto-code-quality/README.md | 43 +++-- .../auto-code-quality/hooks/hooks.json | 37 ++++- .../{task-tracker.py => activity-tracker.py} | 64 +++++--- .../auto-code-quality/scripts/quality-gate.py | 40 ++++- .../plugins/protected-files-guard/README.md | 1 + .../scripts/guard-protected-bash.py | 1 + .../scripts/guard-protected.py | 1 + .../tests/plugins/test_guard_protected.py | 2 + .../plugins/test_guard_protected_bash.py | 2 + 30 files changed, 476 insertions(+), 210 deletions(-) mode change 100644 => 100755 container/.devcontainer/features/ccburn/install.sh mode change 100644 => 100755 container/.devcontainer/features/ccstatusline/README.md mode change 100644 => 100755 container/.devcontainer/features/ccusage/README.md mode change 100644 => 100755 container/.devcontainer/features/codex-cli/README.md mode change 100644 => 100755 container/.devcontainer/features/hermes-agent/README.md mode change 100644 => 100755 container/.devcontainer/features/mcp-qdrant/README.md mode change 100644 => 100755 container/.devcontainer/features/oh-my-claude/README.md mode change 100644 => 100755 container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py rename container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/{task-tracker.py => activity-tracker.py} (54%) mode change 100644 => 100755 container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md mode change 100644 => 100755 container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py mode change 100644 => 100755 container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py mode change 100644 => 100755 container/tests/plugins/test_guard_protected.py mode change 100644 => 100755 container/tests/plugins/test_guard_protected_bash.py diff --git a/container/.devcontainer/AGENTS.md b/container/.devcontainer/AGENTS.md index 91f9afe..22625d7 100755 --- a/container/.devcontainer/AGENTS.md +++ b/container/.devcontainer/AGENTS.md @@ -6,33 +6,37 @@ CodeForge devcontainer for AI-assisted development with Claude Code. | File | Purpose | |------|---------| -| `defaults/codeforge/config/settings.base.json` | Shared Claude Code settings: permissions, plugins, env vars | -| `defaults/codeforge/config/settings-profiles/*.json` | Model/context/thinking overlays used to generate deployed settings profiles | -| `defaults/codeforge/config/settings*.json` | Generated Claude Code settings profiles deployed to `~/.claude/` | -| `defaults/codeforge/config/main-system-prompt.md` | System prompt defining assistant behavior | -| `defaults/codeforge/config/orchestrator-system-prompt.md` | Orchestrator mode prompt (delegation-first) | -| `defaults/codeforge/config/ccstatusline-settings.json` | Status bar widget layout (deployed to ~/.config/ccstatusline/) | -| `defaults/codeforge/config/disabled-hooks.json` | Disable individual plugin hooks by script name | -| `defaults/codeforge/config/claude-code-router.json` | LLM provider routing config (deployed to ~/.claude-code-router/) | +| `defaults/codeforge/claude/settings/base.json` | Shared Claude Code settings: permissions, plugins, env vars | +| `defaults/codeforge/claude/settings/profiles/*.json` | Model/context overlays (use `_meta: { model, contextWindow }` shorthand) used to generate deployed profiles | +| `.generated/codeforge/claude/settings/settings*.json` | Generated profiles deployed to `~/.claude/`; `settings.json` is a symlink to the default profile (`isDefault: true` in the generator) | +| `defaults/codeforge/claude/system-prompts/main.md` | System prompt defining assistant behavior | +| `defaults/codeforge/claude/system-prompts/orchestrator.md` | Orchestrator mode prompt (delegation-first) | +| `defaults/codeforge/claude/statusline/settings.json` | Status bar widget layout (deployed to ~/.config/ccstatusline/) | +| `defaults/codeforge/claude/disabled-hooks.json` | Disable individual plugin hooks by script name | +| `defaults/codeforge/claude/router/config.json` | LLM provider routing config (deployed to ~/.claude-code-router/) | | `defaults/codeforge/file-manifest.json` | Controls which config files deploy and when | | `devcontainer.json` | Container definition: features, compose config, mounts | | `docker-compose.yml` | Base Docker Compose file: image, volumes, resource limits | | `.codeforge/mounts.json` | User/auto-detected volume mount configuration | -| `.env` | Boolean flags controlling setup steps | +| `.codeforge/container.json` | Setup flags, identity, timezone, version lock, plugin config | +| `.codeforge/secrets/` | Docker Compose secrets (one file per secret, gitignored) | -Config files deploy via `defaults/codeforge/file-manifest.json` on every container start. Most deploy to `~/.claude/`; ccstatusline config deploys to `~/.config/ccstatusline/`. Each entry supports `overwrite`: `"if-changed"` (default, sha256), `"always"`, or `"never"`. Supported variables: `${CLAUDE_CONFIG_DIR}`, `${WORKSPACE_ROOT}`, `${HOME}`. +Config files deploy via `defaults/codeforge/file-manifest.json` on every container start. Most deploy to `~/.claude/`; ccstatusline config deploys to `~/.config/ccstatusline/`. Each entry supports `overwrite`: `"if-changed"` (default, sha256), `"always"`, or `"never"`. Supported variables: `${WORKSPACE_ROOT}`, `${CODEFORGE_DIR}`, `${HOME}`. ## Commands | Command | Purpose | |---------|---------| -| `cc` / `claude` / `cc7` | Run Claude Code with auto-configuration (opus-4-7, 200k context) | +| `cc` / `claude` / `cc6` | Run Claude Code with auto-configuration (opus-4-6, 200k context) | | `codeforge config apply` | Deploy config files to `~/.claude/` (same as container start) | | `ccraw` | Vanilla Claude Code (bypasses config) | -| `cc5` / `cc6` / `cc61` / `cc71` | Main prompt profiles for opus-4-5 200k, opus-4-6 200k, opus-4-6 1M bounded to 400k, opus-4-7 1M bounded to 400k | +| `cc5` / `cc6` / `cc61` / `cc7` / `cc71` | Main prompt profiles for opus-4-5 200k, opus-4-6 200k, opus-4-6 1M bounded to 400k, opus-4-7 200k, opus-4-7 1M bounded to 400k | | `ccw*` | Same profile matrix with the writing system prompt | | `cc-orc*` | Same profile matrix in orchestrator mode, delegation-first | | `codex` | OpenAI Codex CLI terminal coding agent | +| `rtk gain` | Show RTK token savings statistics for the session | +| `rtk discover` | List all commands RTK can compress | +| `rtk status` | Show RTK version and configuration | | `hermes` | Nous Research Hermes Agent CLI (run `hermes setup` on first use) | | `ccms` | Session history search _(disabled — requires Rust toolchain; uncomment in devcontainer.json to enable)_ | | `codeforge proxy` | Launch Claude Code through mitmproxy — inspect API traffic in browser (port 8081) | @@ -46,6 +50,11 @@ Config files deploy via `defaults/codeforge/file-manifest.json` on every contain | `codeforge doctor --fix` | Interactive fix mode — apply fixes for detected issues | | `dbr` | Dynamic port forwarding ([devcontainer-bridge](https://github.com/bradleybeddoes/devcontainer-bridge)) | | `cc-tools` | List all installed tools with versions | +| `claude-mem-worker` | Claude-Mem worker management (start/stop/status) | +| `ccdiag` | Session diagnostics: orphaned tool calls, errors, token usage, API proxy | +| `analyze-sessions` | Session quality metrics: thinking depth, Read:Edit ratio, frustration indicators | +| `lamarck skill ` | Skill analysis and improvement suggestions from session history | +| `sandcastle` | AI agent orchestration — parallel workflows, branch strategies, iteration loops | ## Plugins @@ -76,37 +85,126 @@ Declared in `settings.json` under `enabledPlugins`, auto-activated on start: ## Rules System -Rules in `defaults/codeforge/config/rules/` deploy to `.claude/rules/` on every container start. They load into ALL sessions automatically. +Rules in `defaults/codeforge/claude/rules/` deploy to `.claude/rules/` on every container start. They load into ALL sessions automatically. -**Current rules:** `auto-memory.md`, `explicit-start.md`, `plan-presentation.md`, `scope-discipline.md`, `session-search.md`, `spec-workflow.md`, `surface-decisions.md`, `workspace-scope.md`, `zero-tolerance-bugs.md` +**Current rules:** `auto-memory.md`, `explicit-start.md`, `plan-presentation.md`, `rtk-awareness.md`, `scope-discipline.md`, `session-search.md`, `spec-workflow.md`, `surface-decisions.md`, `workspace-scope.md`, `zero-tolerance-bugs.md` -**Adding rules:** Create `.md` in `defaults/codeforge/config/rules/`, add a manifest entry in `defaults/codeforge/file-manifest.json`. +**Adding rules:** Create `.md` in `defaults/codeforge/claude/rules/`, add a manifest entry with a stable `id` in `defaults/codeforge/file-manifest.json`. ## Authentication & Persistence The `~/.claude/` directory is backed by a Docker named volume (`codeforge-claude-config-${devcontainerId}`), persisting config, credentials, and session data across container rebuilds. Each devcontainer instance gets an isolated volume. -**Token authentication:** Set `CLAUDE_AUTH_TOKEN` in `.devcontainer/.secrets` (or as a Codespaces secret) with a long-lived token from `claude setup-token`. On container start, `setup-auth.sh` auto-creates `~/.claude/.credentials.json` with `600` permissions. If `.credentials.json` already exists, token injection is skipped (idempotent). Tokens must match `sk-ant-*` format. +**Secrets** are stored as individual files in `.codeforge/secrets/` (gitignored). Each file contains the raw secret value. On host startup, `generate-compose.mjs` discovers these files and generates Docker Compose secret mounts at `/run/secrets/`. The `setup-auth.sh` script reads secrets with a fallback chain: env var (Codespaces) → `/run/secrets/` (Docker Compose) → skip. -Codex CLI credentials (`~/.codex/`) are backed by a separate Docker named volume (`codeforge-codex-config-${devcontainerId}`). Set `OPENAI_API_KEY` in `.devcontainer/.secrets` (or as a Codespaces secret) for automatic API key injection, or run `codex` interactively for browser-based ChatGPT OAuth. +**Supported secrets:** `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. -**Claude Code Router:** Set provider API keys (`ANTHROPIC_API_KEY`, `DEEPSEEK_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`) in `.devcontainer/.secrets`. Keys are exported as env vars on container start and read at runtime by the router's `$ENV_VAR` interpolation in `~/.claude-code-router/config.json`. Edit routing rules in `defaults/codeforge/config/claude-code-router.json` and run `ccr-apply` to redeploy. +**Claude Code auth:** Set `claude_code_oauth_token` (from `claude setup-token`) in `.codeforge/secrets/`. The token is exported as `CLAUDE_CODE_OAUTH_TOKEN` env var for Claude Code's native headless auth. **WARNING:** `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. + +**GitHub auth + identity:** Set `gh_token` in `.codeforge/secrets/`. On container start, `setup-auth.sh` authenticates via `gh auth login`, configures the git credential helper, and derives `user.name` and `user.email` from the GitHub API. Override identity via `.codeforge/container.json` `identity` section. + +**Codex CLI:** Set `openai_api_key` in `.codeforge/secrets/`. Credentials (`~/.codex/`) are backed by a separate Docker named volume. + +**Claude Code Router:** Set provider keys (`anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`) in `.codeforge/secrets/`. Keys are exported as env vars on container start and read at runtime by the router's `$ENV_VAR` interpolation in `~/.claude-code-router/config.json`. Edit routing rules in `defaults/codeforge/claude/router/config.json` or override `.codeforge/claude/router/config.json`, then run `ccr-apply` to redeploy. **oh-my-claude:** The local `features/oh-my-claude` feature is opt-in. It installs the OMC CLI and generated agents, skips OMC hooks/MCP/statusline, and preserves CodeForge-managed `~/.claude/settings.json`. OMC proxy sessions are launched per session with `omc cc` or the `omc-cc` helper; do not add a post-start OMC daemon. -**Claude Code Karma:** The local `features/claude-code-karma` feature is default-on. It installs Karma from a pinned git ref, starts the UI on port `7847` and API on port `7848`, and patches Karma so its Settings API/UI are read-only. CodeForge owns `~/.claude/settings.json`; add Karma hooks in `defaults/codeforge/config/settings.base.json`, not from Karma. +**Claude Code Karma:** The local `features/claude-code-karma` feature is default-on. It installs Karma from a pinned git ref, starts the UI on port `7847` and API on port `7848`, and patches Karma so its Settings API/UI are read-only. CodeForge owns generated `~/.claude/settings.json`; add Karma hooks in `defaults/codeforge/claude/settings/base.json`, not from Karma. + +## Memory & Analysis Tools + +### claude-mem — Persistent Memory System + +Real-time observation capture with hybrid search (SQLite + Chroma vectors) and MCP tools. Replaces the previously-disabled `memory-awareness`, `context-memory`, and `post-tool` hooks. + +**Architecture:** A background worker service (port 37777) receives hook events via HTTP POST and processes them asynchronously. The `claude-mem-hook` wrapper script is a thin HTTP proxy — it sends the payload and exits 0 immediately regardless of outcome (non-blocking). + +**Hooks registered in `claude/settings/base.json`:** +- `SessionStart` → `claude-mem-hook context` (load relevant memories) +- `UserPromptSubmit` → `claude-mem-hook session-init` (initialize session tracking) +- `PreToolUse[Read]` → `claude-mem-hook file-context` (inject file-level memories) +- `PostToolUse[*]` → `claude-mem-hook observation` (capture observations) +- `Stop` → `claude-mem-hook summarize` (session summary + memory consolidation) + +**MCP tools** (registered via poststart.d, available in Claude Code sessions): +- `search` — hybrid SQLite FTS + Chroma vector search +- `timeline` — chronological observation history +- `get_observations` — retrieve specific observations +- `smart_search` — context-aware search with relevance ranking + +**Data:** `~/.claude-mem/` (settings, SQLite DB, Chroma vectors, logs) + +**Management:** `claude-mem-worker start|stop|status` + +**License:** AGPL-3.0 (development tool only, not distributed) + +### lamarck — Skill Analysis + +Analyzes Claude Code session history to extract learnings and skill patterns. Runs on a cron schedule (default: every 4 hours). + +**Usage:** +- `lamarck skill --mode suggest` — get improvement suggestions for a specific skill +- `lamarck --project /workspaces` — full session analysis (runs automatically via cron) + +**Cron:** `0 */4 * * *` (configurable via `cronInterval` option in devcontainer.json) + +**State:** `~/.lamarck/`, logs at `/tmp/lamarck.log` + +### ccdiag — Session Diagnostics + +Go CLI tool for diagnosing Claude Code session issues. + +**Usage:** +- `ccdiag orphans` — find orphaned tool calls (started but never completed) +- `ccdiag errors` — analyze session errors and failure patterns +- `ccdiag tokens` — token usage breakdown per session/tool +- `ccdiag proxy` — launch API proxy on port 9119 for traffic inspection + +### claude-session-analyzer — Quality Metrics + +Python tool measuring session quality via multiple signals. + +**Usage:** +- `analyze-sessions ~/.claude/projects/ --start 2026-01-01` — analyze sessions from a date +- `analyze-sessions ~/.claude/projects/ --format json` — JSON output for scripting + +**Metrics:** thinking depth, Read:Edit ratio, tool call patterns, frustration indicators (repeated failures, context resets) + +## Agent Orchestration + +### sandcastle — Multi-Agent Workflows + +TypeScript library/CLI for orchestrating AI coding agents in isolated environments. Handles workflow orchestration — branch strategies, prompt templating, iteration loops, completion detection, lifecycle hooks, and merging — while delegating execution to a pluggable sandbox provider. + +**Usage:** +- `sandcastle init` — scaffold `.sandcastle/` workflow directory in a project +- `sandcastle run` — execute a workflow definition + +**Target workflows:** +- Parallel agents on separate branches (fan-out decomposition) +- Implement → review → fix sequential pipelines +- Large plan decomposition → staged units → distribution → review loops → final reports + +**Integration model:** Use a custom local-process provider that creates git worktrees as sandboxes and spawns Claude Code CLI as child processes. Agents inherit all `~/.claude/` config, workspace-scope-guard confinement, and environment variables from the parent process. + +**Docs:** [github.com/mattpocock/sandcastle](https://github.com/mattpocock/sandcastle) + +**Security:** Pinned to ≥0.5.4 (command injection CVE fix in earlier versions). ## Modifying Behavior -1. **Change shared Claude settings**: Edit `defaults/codeforge/config/settings.base.json`, then run `node scripts/generate-settings-profiles.js` -2. **Change system prompt**: Edit `defaults/codeforge/config/main-system-prompt.md` -3. **Add config file**: Place in `defaults/codeforge/config/`, add entry to `defaults/codeforge/file-manifest.json` +1. **Change shared Claude settings**: Edit `defaults/codeforge/claude/settings/base.json`, then run `node scripts/generate-settings-profiles.js` + - **Add a new profile**: Create a `profiles/.json` with `_meta: { model, contextWindow }` and any profile-specific fields, then add an entry to the `profiles` array in `scripts/generate-settings-profiles.js`. Set `isDefault: true` to make it the default `settings.json` symlink target. + - **Change the default profile**: Set `isDefault: true` on the desired profile in the `profiles` array and remove it from the previous default, then regenerate. +2. **Change system prompt**: Edit `defaults/codeforge/claude/system-prompts/main.md` +3. **Add config file**: Place it under the relevant `defaults/codeforge/{claude,codex,rtk}/` area, add an entry with stable `id` to `defaults/codeforge/file-manifest.json` 4. **Add features**: Add to `"features"` in `devcontainer.json` 5. **Disable features**: Set `"version": "none"` in the feature's config -6. **Disable setup steps**: Set flags to `false` in `.env` -7. **Customize status bar**: Edit `defaults/codeforge/config/ccstatusline-settings.json` -8. **Lock Claude Code version**: Set `CLAUDE_VERSION_LOCK=2.1.80` in `.env` — the update script installs that exact version on container start instead of updating to latest. Unset to resume auto-updates. -9. **Disable individual hooks**: Add script name (without `.py`) to `disabled` array in `defaults/codeforge/config/disabled-hooks.json` — takes effect immediately, no restart needed +6. **Disable setup steps**: Set flags to `false` in `.codeforge/container.json` under `setup` +7. **Customize status bar**: Edit `defaults/codeforge/claude/statusline/settings.json` +8. **Lock Claude Code version**: Set `"versionLock": "2.1.80"` under `claude` in `.codeforge/container.json` — the update script installs that exact version on container start instead of updating to latest. Set to `null` to resume auto-updates. +9. **Disable individual hooks**: Add script name (without `.py`) to `disabled` array in `~/.claude/disabled-hooks.json` or override source `.codeforge/claude/disabled-hooks.json` +10. **Change container timezone**: Set `"timezone": "America/New_York"` (or any IANA timezone) in `.codeforge/container.json`. Default is `America/Chicago` (Central Time). Applied on container start. ## Plugin Development Notes diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index eb0a367..1749e47 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -2,6 +2,35 @@ ## Unreleased +### Status Line + +- **Rate limit reset times** — the 5-hour and 7-day rate limit widgets now display when limits reset (e.g., `5h: 42% (14:30)` and `7d: 15% (Mon 09:00)`). Uses custom-command scripts instead of built-in ccstatusline types. + +### Configuration + +- **Container timezone** — new `timezone` field in `.codeforge/container.json` (default: `America/Chicago`). Set to any IANA timezone (e.g., `America/New_York`, `Europe/London`). Applied via `TZ` env var on container start. + +### Bug Fixes + +- **Fix named volume ownership for all mount points** — `setup.sh` only fixed `root:root` ownership on `~/.claude`, leaving 6 other Docker named volumes unfixed. `~/.config/gh` and `~/.bun/install/cache` were actively broken (`gh auth login` would fail with `permission denied`). Now loops over all volume mount points from `docker-compose.yml`. + +### Developer Tooling + +- **Enable shfmt, dprint, shellcheck, hadolint** — previously disabled (`"version": "none"`), now set to `"latest"`. Provides shell formatting, markdown/TOML/Dockerfile formatting, shell linting, and Dockerfile linting out of the box. + +### Secrets & Configuration + +- **Docker Compose secrets** — secrets now use Docker Compose file-based secrets mounted at `/run/secrets/`. Place secret files in `.codeforge/secrets/` (one file per secret, raw value only). The `generate-compose.mjs` init script auto-discovers secrets and generates the compose override. Supported secrets: `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. Env vars (Codespaces) remain supported as a fallback. +- **`.secrets` and `.env` files removed** — replaced by `.codeforge/secrets/` (for sensitive tokens) and `.codeforge/container.json` (for setup flags, identity, plugin config). Migration warnings are shown if old files are detected. +- **`CLAUDE_CODE_OAUTH_TOKEN` replaces `CLAUDE_AUTH_TOKEN`** — Claude Code's native OAuth env var is now used for headless auth instead of manual `.credentials.json` injection. Note: `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. +- **Git identity derived from `gh auth login`** — username and email are automatically set from the GitHub API after authentication. Manual `GH_USERNAME` and `GH_EMAIL` config is no longer needed. Override via `.codeforge/container.json` `identity` section if needed. +- **`.codeforge/container.json`** — new structured JSON config replaces `.env` for setup flags (`setup.config`, `setup.aliases`, etc.), Claude version lock, plugin blacklist, and identity overrides. Deployed from packaged defaults with `overwrite: "never"`. +- **`.claude.json` pre-populated** — `hasCompletedOnboarding` and `bypassPermissionsModeAccepted` are now seeded via the file manifest on first deploy, removing the `99-claude-onboarding.sh` post-start hook. +- **`CLAUDE_CONFIG_DIR` removed** — this env var is no longer set or referenced anywhere. All paths use `$HOME/.claude` directly. +- **`CLAUDECODE` env var removed** — the `CLAUDECODE=null` override in `remoteEnv` has been removed. +- **oh-my-claude provider keys removed from default secrets** — `KIMI_API_KEY`, `ZHIPU_API_KEY`, `ALIYUN_API_KEY`, `MINIMAX_API_KEY`, `ZAI_API_KEY`, `MINIMAX_CN_API_KEY` removed from `devcontainer.json` secrets. Users who enable oh-my-claude can add their own secret files. +- **`generate-mounts.mjs` renamed to `generate-compose.mjs`** — now generates both volume mounts and Docker secrets in the compose override. + ### Performance - **TMPDIR removed from remoteEnv** — previously set to `/workspaces/.tmp` (bind mount, slow on NTFS/WSL). Now unset, defaulting to the container's `/tmp` (overlay/tmpfs). Scripts using `${TMPDIR:-/tmp}` already handle this correctly. Any user scripts depending on `$TMPDIR` being a persistent location should use an explicit path instead. @@ -10,6 +39,14 @@ - **Docker Compose migration** — devcontainer now uses `docker-compose.yml` for volume management instead of inline `mounts` in devcontainer.json. Volume names are fixed (no `${devcontainerId}` suffix), simplifying volume management. Existing `${devcontainerId}`-suffixed volumes are orphaned — run `docker volume prune` to clean up, and re-authenticate `gh auth login` on first rebuild. - **Dynamic volume mounts** — `initializeCommand` runs `generate-mounts.mjs` on the host before container build, reading `.codeforge/mounts.json` to generate a `docker-compose.codeforge.yml` override with project-specific volume mounts for high-churn directories. +### Agent Orchestration + +- **New feature: `sandcastle`** — installs [@ai-hero/sandcastle](https://github.com/mattpocock/sandcastle) globally via npm for multi-agent workflow orchestration. Supports parallel agents on separate branches, implement→review→fix pipelines, and plan decomposition workflows. Uses a local-process provider model — agents run as Claude Code CLI child processes in git worktrees, inheriting all `~/.claude/` config and workspace-scope-guard confinement. Pinned to ≥0.5.4 (command injection CVE fix). Set `"version": "none"` in devcontainer.json to disable. + +### Context Optimization + +- **RTK (Rust Token Killer) integration** — new feature (`features/rtk`) installs the RTK CLI proxy that compresses Bash command output before it reaches the LLM context window (60-90% token savings). A PreToolUse hook transparently rewrites supported commands (`git`, `npm`, `cargo`, `docker`, etc.) to route through RTK. Auto-allow is disabled by default — set `RTK_AUTO_ALLOW=1` to skip permission prompts for rewritten commands. Includes Codex CLI awareness file for instruction-based prefix compliance. + ### CLI - **`codeforge doctor` command** — new top-level command that checks environment health: GitHub CLI auth, Git user config, workspace filesystem type (detects slow WSL/NTFS mounts), TMPDIR location, cache directory volume backing, and container memory limits. Supports `--format json` and `--no-color`. @@ -37,6 +74,17 @@ ### Configuration +- **Profile overlay `_meta` shorthand** — profile overlay files (`defaults/codeforge/claude/settings/profiles/*.json`) now use a `_meta: { model, contextWindow }` field instead of repeating the model string and context window across three separate keys. The generator expands `_meta` into `model`, `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, `CLAUDE_CODE_AUTO_COMPACT_WINDOW`, and `CLAUDE_CODE_DISABLE_1M_CONTEXT`. Profiles are now 3–7 lines each. To add a new profile, specify `_meta` plus any profile-specific fields and add an entry to the `profiles` array in `generate-settings-profiles.js`. +- **Thinking settings moved to base** — `MAX_THINKING_TOKENS: 31999` and `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING: 1` moved from individual profile overlays into `base.json` so all profiles inherit them. Opus 4.7 inherits these settings unchanged. +- **`ANTHROPIC_MODEL` / `ANTHROPIC_DEFAULT_OPUS_MODEL` removed** — these env vars were duplicating the top-level `model` field and are no longer set in generated profiles. Model selection is controlled by `model` in the settings JSON (set by `--settings` flag) plus any explicit `--model` CLI flag. +- **`autoCompactWindow` top-level key removed** — context window is now set only via `CLAUDE_CODE_AUTO_COMPACT_WINDOW` env var (still present). The redundant top-level `autoCompactWindow` field has been removed from all profiles. +- **`settings.json` is now a symlink to `settings-opus-46-200k.json`** — the default settings file is no longer a separate generated copy; it is a symlink (or identical file copy on platforms without symlink support) pointing to the opus-46-200k profile. The active default profile is controlled by the `isDefault: true` flag in the `profiles` array of `generate-settings-profiles.js`. + +- **Config defaults layout v3** - `.codeforge/` is now a minimal overrides/state directory with README, markers, checksums, and data. Packaged defaults stay under `.devcontainer/defaults/codeforge/`, organized by `claude/`, `codex/`, and `rtk/`. +- **Claude settings generation moved to `.generated`** - settings are generated from `claude/settings/base.json` plus profile overlays into `.devcontainer/.generated/codeforge/claude/settings/`, then deployed to `~/.claude/settings*.json`. The default profile is now `opus-46-200k`; `settings.json` matches `settings-opus-46-200k.json`. +- **Manifest override IDs** - default manifest entries now require stable `id` values. Optional `.codeforge/file-manifest.json` entries can override, disable, or add files by `id`; source resolution checks `.codeforge/`, generated output, then packaged defaults. +- **v3 migration and stale marker** - setup writes `.codeforge/.markers/config-layout-v3` and a migration report, and alias launch checks `.codeforge/.markers/settings-generated-v3` so stale Claude settings regenerate before `cc` starts. + - **Claude session retention increased** — default `cleanupPeriodDays` is now `90` across all generated Claude settings profiles. Extended thinking remains enabled by default. - **Dangerous-mode permission prompt skipped by default** — `skipDangerousModePermissionPrompt: true` is now set in `settings.base.json` and propagates to all five generated profiles. Suppresses the one-time bypass-permissions confirmation on new devcontainers. - **Effort level bumped to `max` on opus-4-7** — both opus-4-7 overlays (200k and 1M-400k) now set `effortLevel: "max"` and `CLAUDE_CODE_EFFORT_LEVEL: "max"`. Opus-4-5 and opus-4-6 profiles no longer carry any effort-level setting; they use `MAX_THINKING_TOKENS: 31999` with adaptive thinking disabled (token budgets, not effort levels). `CLAUDE_CODE_EFFORT_LEVEL` was removed from base settings so it no longer leaks into non-4.7 profiles. @@ -46,6 +94,15 @@ - **Router features disabled by default** — `claude-code-router` and `oh-my-claude` are both present but configured with `version: "none"` in `devcontainer.json`; CCR autostart is also false. - **Auto mode disabled by default** — `disableAutoMode: "disable"` added to the base settings profile, removing the `auto` permission mode from the Shift+Tab cycle and rejecting `--permission-mode auto` at startup. Users who want auto mode back can override via `~/.claude/settings.json`. +### Memory & Analysis + +- **claude-mem memory system** — real-time observation capture with hybrid search (SQLite + Chroma vectors). Background worker on port 37777 receives hook events via HTTP POST. MCP tools (search, timeline, get_observations, smart_search) registered via poststart.d. Replaces disabled `memory-awareness`, `context-memory`, and `post-tool` hooks. +- **lamarck skill analyzer** — periodic session analysis via cron (every 4h by default). Use `lamarck skill --mode suggest` for skill-specific improvement suggestions from session history. +- **ccdiag session diagnostics** — Go CLI for session health: `ccdiag orphans` (orphaned tool calls), `ccdiag errors` (failure patterns), `ccdiag tokens` (usage breakdown), `ccdiag proxy` (API inspection on port 9119). +- **claude-session-analyzer** — Python quality metrics tool: thinking depth, Read:Edit ratio, frustration indicators. Usage: `analyze-sessions ~/.claude/projects/ --start 2026-01-01`. +- **Go runtime enabled** — `ghcr.io/devcontainers/features/go:1` uncommented in devcontainer.json (required by ccdiag). +- **Cron daemon installed** — required by lamarck for scheduled batch processing. Started via poststart.d hook. + ### oh-my-claude - **StatusLine protection hardened** — install.sh now explicitly resets `statusLine` to ccstatusline after `omc install` completes, since OMC lacks a `--skip-statusline` flag. This prevents OMC from overwriting CodeForge's statusline config. diff --git a/container/.devcontainer/README.md b/container/.devcontainer/README.md index 2370b55..898042f 100755 --- a/container/.devcontainer/README.md +++ b/container/.devcontainer/README.md @@ -44,16 +44,18 @@ Authentication credentials are stored in `~/.claude/` and persist across contain ### Long-Lived Token Authentication -For headless or automated environments, you can use a long-lived auth token instead of browser login: +For headless or automated environments, use a long-lived auth token: 1. Generate a token: `claude setup-token` -2. Add to `.devcontainer/.secrets`: +2. Create the secret file: ```bash - CLAUDE_AUTH_TOKEN=sk-ant-oat01-your-token-here + echo "sk-ant-oat01-your-token-here" > .codeforge/secrets/claude_code_oauth_token ``` -3. On next container start, `setup-auth.sh` will create `~/.claude/.credentials.json` automatically. +3. On next container start, `setup-auth.sh` exports `CLAUDE_CODE_OAUTH_TOKEN` and writes the token to Claude Code's Linux credential file automatically. -You can also set `CLAUDE_AUTH_TOKEN` as a Codespaces secret for cloud environments. +You can also set `CLAUDE_CODE_OAUTH_TOKEN` as a Codespaces secret for cloud environments. + +> **Note:** `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. For more options, see the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-code). @@ -80,43 +82,38 @@ Get an API key from [platform.openai.com/api-keys](https://platform.openai.com/a ### Automatic Token Setup -Add your API key to `.devcontainer/.secrets`: +Create the secret file: ```bash -OPENAI_API_KEY=sk-your-key-here +echo "sk-your-key-here" > .codeforge/secrets/openai_api_key ``` On next container start, `setup-auth.sh` will create `~/.codex/auth.json` automatically. You can also set `OPENAI_API_KEY` as a Codespaces secret. ## GitHub & NPM Authentication -### Automatic Auth via `.secrets` (Recommended) - -CodeForge can automatically configure GitHub CLI, git identity, and NPM auth on every container start. Copy the template and fill in your tokens: - -```bash -cp .devcontainer/.secrets.example .devcontainer/.secrets -``` +### Automatic Auth via Secrets (Recommended) -Edit `.devcontainer/.secrets`: +CodeForge automatically configures GitHub CLI, git identity, and NPM auth on every container start using Docker Compose secrets. Create secret files in `.codeforge/secrets/`: ```bash -GH_TOKEN=ghp_your_token_here -GH_USERNAME=your-github-username -GH_EMAIL=your-email@example.com -NPM_TOKEN=npm_your_token_here +mkdir -p .codeforge/secrets +echo "ghp_your_token_here" > .codeforge/secrets/gh_token +echo "npm_your_token_here" > .codeforge/secrets/npm_token ``` On the next container start (or rebuild), `setup-auth.sh` will: - Authenticate `gh` CLI and configure git credential helper -- Set `git config --global user.name` and `user.email` +- Derive `git config --global user.name` and `user.email` from the GitHub API - Set NPM registry auth token -The `.secrets` file is gitignored at two levels (root `.*` + `.devcontainer/.gitignore`) and will never be committed. +The `.codeforge/secrets/` directory is gitignored and will never be committed. + +**Identity override**: To use a different name or email than your GitHub profile, set `identity.name` and `identity.email` in `.codeforge/container.json`. -**Environment variable fallback**: For Codespaces or CI, set `GH_TOKEN`, `GH_USERNAME`, `GH_EMAIL`, and/or `NPM_TOKEN` as environment variables (e.g., via Codespaces secrets or `localEnv` in `devcontainer.json`). Environment variables take precedence over `.secrets` file values. +**Environment variable fallback**: For Codespaces or CI, set `GH_TOKEN` and/or `NPM_TOKEN` as environment variables (e.g., via Codespaces secrets). Environment variables take precedence over Docker secrets. -Disable automatic auth by setting `SETUP_AUTH=false` in `.devcontainer/.env`. +Disable automatic auth by setting `setup.auth` to `false` in `.codeforge/container.json`. ### Interactive Login (Alternative) @@ -157,7 +154,7 @@ Expected output shows your authenticated account and token scopes. GitHub CLI credentials are persisted across container rebuilds via a Docker named volume mounted at `~/.config/gh/` (via `GH_CONFIG_DIR`). Claude Code credentials persist via a separate Docker named volume mounted at `~/.claude/`. -**You only need to authenticate once.** After running `gh auth login` or configuring `.secrets`, your credentials will survive container rebuilds and be available in future sessions. +**You only need to authenticate once.** After running `gh auth login` or setting up secrets in `.codeforge/secrets/`, your credentials will survive container rebuilds and be available in future sessions. > **Note:** If upgrading from a previous CodeForge version, you'll need to run `gh auth login` once after your first rebuild. Previous credentials in `/workspaces/.gh/` are not migrated to the new volume. @@ -259,38 +256,54 @@ curl http://$CDP_HOST:9223/json/version ## Configuration -### Environment Variables +### Container Configuration -Copy `.devcontainer/.env.example` to `.devcontainer/.env` and customize: +Setup behavior is configured via `.codeforge/container.json`. A default is deployed on first start. Example: -| Variable | Default | Description | -|----------|---------|-------------| -| `CLAUDE_CONFIG_DIR` | `/home/vscode/.claude` | Claude configuration directory | -| `SETUP_CONFIG` | `true` | Copy config files during setup (per `file-manifest.json`) | -| `SETUP_ALIASES` | `true` | Add `cc`/`claude`/`ccraw` aliases to shell | -| `SETUP_AUTH` | `true` | Configure Git/NPM auth from `.secrets` | -| `SETUP_PLUGINS` | `true` | Install official plugins + register marketplace | -| `SETUP_UPDATE_CLAUDE` | `true` | Auto-update Claude Code on container start | -| `SETUP_TERMINAL` | `true` | Configure VS Code Shift+Enter keybinding for Claude Code terminal | -| `SETUP_PROJECTS` | `true` | Auto-detect projects for VS Code Project Manager | -| `SETUP_POSTSTART` | `true` | Run post-start hooks from `/usr/local/devcontainer-poststart.d/` | -| `PLUGIN_BLACKLIST` | `""` | Comma-separated plugin names to skip | +```json +{ + "setup": { + "config": true, "aliases": true, "auth": true, + "plugins": true, "updateClaude": true, "terminal": true, + "poststart": true, "projects": true + }, + "identity": { "name": null, "email": null }, + "claude": { "versionLock": null }, + "plugins": { "blacklist": [], "official": ["frontend-design@claude-plugins-official"] } +} +``` + +| Field | Default | Description | +|-------|---------|-------------| +| `setup.config` | `true` | Deploy config files (per `file-manifest.json`) | +| `setup.aliases` | `true` | Add `cc`/`claude`/`ccraw` aliases to shell | +| `setup.auth` | `true` | Configure auth from Docker secrets | +| `setup.plugins` | `true` | Install official plugins + register marketplace | +| `setup.updateClaude` | `true` | Auto-update Claude Code on container start | +| `setup.terminal` | `true` | Configure VS Code Shift+Enter keybinding | +| `setup.projects` | `true` | Auto-detect projects for VS Code Project Manager | +| `setup.poststart` | `true` | Run post-start hooks | +| `identity.name` | `null` | Override git user.name (derived from GitHub API by default) | +| `identity.email` | `null` | Override git user.email (derived from GitHub API by default) | +| `claude.versionLock` | `null` | Pin Claude Code to a specific version | +| `plugins.blacklist` | `[]` | Plugin names to skip during installation | ### Claude Code Settings -Default settings are in `.codeforge/config/settings.json`. File copying is controlled by `.codeforge/file-manifest.json`, which specifies per-file overwrite behavior (`"if-changed"`, `"always"`, or `"never"`). +Default settings inputs are in `.devcontainer/defaults/codeforge/claude/settings/`. CodeForge generates final `settings*.json` files into `.devcontainer/.generated/codeforge/claude/settings/` and deploys them to `~/.claude/`. Project overrides use matching paths under `.codeforge/claude/settings/`. To add a custom config file, append an entry to `file-manifest.json`: ```json { - "src": "my-config.json", + "id": "custom.my-config", + "src": "claude/my-config.json", "dest": "${WORKSPACE_ROOT}", "overwrite": "if-changed" } ``` Key defaults: -- **Model**: Claude Opus 4-7 +- **Model**: Claude Opus 4-6 200k by default - **Default mode**: Plan (prompts before executing) - **Max output tokens**: 64,000 - **Session retention**: 90 days @@ -299,7 +312,7 @@ Claude Code Karma can read these settings in its dashboard, but CodeForge patche ### Keybindings -Default keybindings are in `.codeforge/config/keybindings.json` (empty by default — Claude Code defaults apply). Customize by adding entries to the `bindings` array. +Default keybindings are in `.devcontainer/defaults/codeforge/claude/keybindings.json` (empty by default; Claude Code defaults apply). Customize by adding an override at `.codeforge/claude/keybindings.json`. **VS Code Terminal Passthrough**: `Ctrl+P` and `Ctrl+F` are configured to pass through to the terminal (via `terminal.integrated.commandsToSkipShell`) so Claude Code receives them. Other VS Code shortcuts that conflict with Claude Code: @@ -316,7 +329,7 @@ For conflicting shortcuts, use Meta (Alt) variants or add custom keybindings. ### System Prompt -The default system prompt is in `.codeforge/config/main-system-prompt.md`. Override it by creating a `.claude/main-system-prompt.md` in your project directory. +The default system prompt is in `.devcontainer/defaults/codeforge/claude/system-prompts/main.md`. Override it with `.codeforge/claude/system-prompts/main.md`. ## Custom Features @@ -351,7 +364,7 @@ CodeForge includes custom devcontainer features. Any feature can be disabled by | Plugin | Description | |--------|-------------| | `dangerous-command-blocker` | Blocks destructive bash commands (rm -rf, sudo rm, chmod 777, force push) | -| `protected-files-guard` | Blocks modifications to .env, lock files, .git/, and credentials | +| `protected-files-guard` | Blocks modifications to secrets, lock files, .git/, and credentials | | `workspace-scope-guard` | Enforces working directory scope — blocks writes and warns on reads outside the project | ### auto-code-quality @@ -364,16 +377,17 @@ Features create shell aliases during container build (e.g., `ccusage`, `ccburn`) ## Credential Management -Three methods for providing GitHub/NPM credentials, in order of precedence: +Secrets are provided via Docker Compose file-based secrets in `.codeforge/secrets/` (one file per secret, raw value). Resolution order: + +1. **Environment variables** — Set `GH_TOKEN`, `NPM_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, etc. as env vars (e.g., via Codespaces secrets) +2. **Docker secrets** — Place secret files in `.codeforge/secrets/` (auto-mounted at `/run/secrets/` via Docker Compose) +3. **Interactive login** — Run `gh auth login` for GitHub CLI -1. **Environment variables** — Set `GH_TOKEN`, `GH_USERNAME`, `GH_EMAIL`, `NPM_TOKEN` as environment variables (e.g., via Codespaces secrets or `localEnv` in `devcontainer.json`) -2. **`.secrets` file** — Create `.devcontainer/.secrets` with token values (see template at `.secrets.example`). Auto-configured by `setup-auth.sh` on container start -3. **Interactive login** — Run `gh auth login` for GitHub CLI, then set git identity manually +Supported secrets: `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. -All methods persist across container rebuilds via a Docker named volume at `~/.config/gh/`. +All credentials persist across container rebuilds via Docker named volumes. -4. **`.secrets` file with `CLAUDE_AUTH_TOKEN`** — Long-lived Claude auth token from `claude setup-token`. Auto-creates `~/.claude/.credentials.json` on container start. -5. **`.secrets` file with `OPENAI_API_KEY`** — OpenAI API key for Codex CLI. Auto-creates `~/.codex/auth.json` on container start. +> **Note:** `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. ## Agents & Skills @@ -476,8 +490,8 @@ The `setup-projects.sh` script auto-detects projects under `/workspaces/` and ma - **Authentication required**: Run `claude` once to authenticate before using `cc` - **Plan mode default**: The container starts in "plan" mode, which prompts for approval before making changes -- **Config is managed by manifest**: `.codeforge/file-manifest.json` controls which files are copied and when — default `overwrite: "if-changed"` uses sha256 comparison. Persistent changes go in `.codeforge/config/settings.json` -- **GitHub auth persists**: Run `gh auth login` once or configure `.secrets`; credentials survive container rebuilds +- **Config is managed by manifest**: `.devcontainer/defaults/codeforge/file-manifest.json` provides defaults, and optional `.codeforge/file-manifest.json` overrides entries by `id`. Persistent changes go in `.codeforge/` override paths, not in generated `settings.json` +- **GitHub auth persists**: Run `gh auth login` once or set up `.codeforge/secrets/gh_token`; credentials survive container rebuilds - **Agent Teams needs tmux**: Split panes only work inside tmux. Use the "Claude Teams (tmux)" VS Code terminal profile or `.codeforge/scripts/connect-external-terminal.sh` from WezTerm/iTerm2 ## Troubleshooting @@ -489,7 +503,7 @@ Common issues and solutions. For detailed troubleshooting, see the [Troubleshoot | `cc: command not found` | Run `source ~/.bashrc` or open a new terminal | | `claude` fails during startup | Background update may be in progress — wait 10s and retry | | GitHub push fails | Run `gh auth status` to check authentication | -| Plugin not loading | Check `enabledPlugins` in `.codeforge/config/settings.json` | +| Plugin not loading | Check `enabledPlugins` in generated `~/.claude/settings.json` and the source under `claude/settings/base.json` | | Feature not installed | Check `devcontainer.json` for `"version": "none"` | | Tool version/status | Run `cc-tools` to list all tools with version info | | Full health check | Run `check-setup` to verify setup status | diff --git a/container/.devcontainer/features/ccburn/install.sh b/container/.devcontainer/features/ccburn/install.sh old mode 100644 new mode 100755 index 6be1c4c..a71c5a7 --- a/container/.devcontainer/features/ccburn/install.sh +++ b/container/.devcontainer/features/ccburn/install.sh @@ -126,7 +126,7 @@ if ! command -v npx &>/dev/null; then fi # Check if credentials exist (ccburn needs OAuth token) -CRED_FILE="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.credentials.json" +CRED_FILE="$HOME/.claude/.credentials.json" if [ ! -f "$CRED_FILE" ]; then echo "ccburn: awaiting auth" exit 0 diff --git a/container/.devcontainer/features/ccstatusline/README.md b/container/.devcontainer/features/ccstatusline/README.md old mode 100644 new mode 100755 index 22e7677..4c406cd --- a/container/.devcontainer/features/ccstatusline/README.md +++ b/container/.devcontainer/features/ccstatusline/README.md @@ -74,7 +74,7 @@ The feature will validate these are present and exit with an error if missing. - ✅ **Automatic Integration**: Auto-configures `~/.claude/settings.json` - ✅ **Idempotent**: Safe to run multiple times - ✅ **Multi-user**: Automatically detects container user -- ✅ **Config-aware**: Respects `CLAUDE_CONFIG_DIR` environment variable (defaults to `~/.claude`) +- ✅ **Config-aware**: Uses `~/.claude` for Claude Code settings ## Post-Installation Steps @@ -102,7 +102,7 @@ You should see formatted output with powerline styling. **3. Check Claude Code integration:** ```bash -cat "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" | jq '.statusLine' +cat "$HOME/.claude/settings.json" | jq '.statusLine' ``` Should show: @@ -204,7 +204,7 @@ cat ~/.config/ccstatusline/settings.json | jq . echo '{"model":{"display_name":"Test"}}' | npx -y ccstatusline@latest # 3. Check Claude Code settings -cat "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" | jq '.statusLine' +cat "$HOME/.claude/settings.json" | jq '.statusLine' # 4. Manually run auto-config if needed configure-ccstatusline-auto @@ -258,7 +258,7 @@ configure-ccstatusline-auto npm install -g ccstatusline@latest ``` -Then update `${CLAUDE_CONFIG_DIR:-~/.claude}/settings.json`: +Then update `~/.claude/settings.json`: ```json { "statusLine": { diff --git a/container/.devcontainer/features/ccstatusline/install.sh b/container/.devcontainer/features/ccstatusline/install.sh index 866bde0..8c6cb47 100755 --- a/container/.devcontainer/features/ccstatusline/install.sh +++ b/container/.devcontainer/features/ccstatusline/install.sh @@ -69,7 +69,8 @@ else fi # Widget config is managed by file-manifest.json (deployed by setup-config.sh) -# Source: .codeforge/config/ccstatusline-settings.json +# Source: .devcontainer/defaults/codeforge/claude/statusline/settings.json +# Override: .codeforge/claude/statusline/settings.json # Deployed to: ~/.config/ccstatusline/settings.json (if-changed) # Template: /usr/local/share/ccstatusline/settings.template.json (always) echo "[ccstatusline] Widget config managed by file-manifest.json" @@ -175,6 +176,54 @@ GITCHANGES_EOF chmod +x /usr/local/bin/ccstatusline-git-changes echo "[ccstatusline] ✓ Git-changes helper installed at /usr/local/bin/ccstatusline-git-changes" +# Create session-usage helper script (5-hour rate limit with reset time) +echo "[ccstatusline] Creating session-usage helper..." +cat > /usr/local/bin/ccstatusline-session-usage <<'SESSIONUSAGE_EOF' +#!/bin/bash +# Reads Claude Code JSON from stdin, outputs 5-hour rate limit with reset time +# Format: 5h: X% (HH:MM) +INPUT=$(cat) +FIVE=$(echo "$INPUT" | jq -r '.rate_limits.five_hour.used_percentage // empty' 2>/dev/null) +FIVE_RESET=$(echo "$INPUT" | jq -r '.rate_limits.five_hour.resets_at // empty' 2>/dev/null) + +[ -z "$FIVE" ] && exit 0 + +OUT="5h: $(printf '%.0f' "$FIVE")%" +if [ -n "$FIVE_RESET" ]; then + # Handle both Unix timestamp formats (integer and float) + RESET_TIME=$(date -d "@$FIVE_RESET" '+%H:%M' 2>/dev/null || date -r "${FIVE_RESET%.*}" '+%H:%M' 2>/dev/null) + [ -n "$RESET_TIME" ] && OUT="$OUT ($RESET_TIME)" +fi +echo "$OUT" +SESSIONUSAGE_EOF + +chmod +x /usr/local/bin/ccstatusline-session-usage +echo "[ccstatusline] ✓ Session-usage helper installed at /usr/local/bin/ccstatusline-session-usage" + +# Create weekly-usage helper script (7-day rate limit with reset time) +echo "[ccstatusline] Creating weekly-usage helper..." +cat > /usr/local/bin/ccstatusline-weekly-usage <<'WEEKLYUSAGE_EOF' +#!/bin/bash +# Reads Claude Code JSON from stdin, outputs 7-day rate limit with reset time +# Format: 7d: X% (Day HH:MM) +INPUT=$(cat) +WEEK=$(echo "$INPUT" | jq -r '.rate_limits.seven_day.used_percentage // empty' 2>/dev/null) +WEEK_RESET=$(echo "$INPUT" | jq -r '.rate_limits.seven_day.resets_at // empty' 2>/dev/null) + +[ -z "$WEEK" ] && exit 0 + +OUT="7d: $(printf '%.0f' "$WEEK")%" +if [ -n "$WEEK_RESET" ]; then + # Handle both Unix timestamp formats (integer and float) + RESET_TIME=$(date -d "@$WEEK_RESET" '+%a %H:%M' 2>/dev/null || date -r "${WEEK_RESET%.*}" '+%a %H:%M' 2>/dev/null) + [ -n "$RESET_TIME" ] && OUT="$OUT ($RESET_TIME)" +fi +echo "$OUT" +WEEKLYUSAGE_EOF + +chmod +x /usr/local/bin/ccstatusline-weekly-usage +echo "[ccstatusline] ✓ Weekly-usage helper installed at /usr/local/bin/ccstatusline-weekly-usage" + # Create wrapper script to protect configuration echo "[ccstatusline] Creating wrapper script..." cat > /usr/local/bin/ccstatusline-wrapper <<'WRAPPER_EOF' @@ -256,7 +305,7 @@ fi USERNAME="${SUDO_USER:-vscode}" _USER_HOME=$(getent passwd "$USERNAME" 2>/dev/null | cut -d: -f6) _USER_HOME="${_USER_HOME:-/home/$USERNAME}" -SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json" +SETTINGS_FILE="${_USER_HOME}/.claude/settings.json" # Ensure directory exists mkdir -p "$(dirname "${SETTINGS_FILE}")" @@ -296,7 +345,7 @@ echo " ccstatusline Installation Complete" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Configuration:" -echo " • Source: .codeforge/config/ccstatusline-settings.json" +echo " • Source: .devcontainer/defaults/codeforge/claude/statusline/settings.json" echo " • Deployed to: ~/.config/ccstatusline/settings.json (by file-manifest)" echo " • Template: /usr/local/share/ccstatusline/settings.template.json" echo " • User: ${USERNAME}" @@ -308,7 +357,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ echo "" echo "1. Widget config is deployed automatically on container start" echo "" -echo "2. To customize: edit .codeforge/config/ccstatusline-settings.json" +echo "2. To customize: create/edit .codeforge/claude/statusline/settings.json" echo " Changes deploy on next container start (if-changed)" echo "" echo "3. Test manually:" diff --git a/container/.devcontainer/features/ccusage/README.md b/container/.devcontainer/features/ccusage/README.md old mode 100644 new mode 100755 index 01f75dd..d966731 --- a/container/.devcontainer/features/ccusage/README.md +++ b/container/.devcontainer/features/ccusage/README.md @@ -109,9 +109,7 @@ ccusage-codex monthly --json This feature creates a shell alias that points to `npx -y ccusage@`, which automatically downloads and runs the latest version of ccusage without requiring a global installation. -ccusage automatically reads from Claude Code's JSONL usage logs located in: -- `~/.claude/` (default) -- `$CLAUDE_CONFIG_DIR` (if customized) +ccusage automatically reads from Claude Code's JSONL usage logs located in `~/.claude/`. ## Advanced Options diff --git a/container/.devcontainer/features/claude-code-karma/install.sh b/container/.devcontainer/features/claude-code-karma/install.sh index 6ad2cbc..67d3de1 100755 --- a/container/.devcontainer/features/claude-code-karma/install.sh +++ b/container/.devcontainer/features/claude-code-karma/install.sh @@ -216,7 +216,7 @@ chown -R "\${KARMA_USER}:" "\${KARMA_USER_HOME}/.claude_karma" 2>/dev/null || tr if [ ! -f /tmp/claude-code-karma-api.pid ] || ! kill -0 "\$(cat /tmp/claude-code-karma-api.pid)" 2>/dev/null; then ( cd "\${KARMA_HOME}/api" - export CLAUDE_KARMA_CLAUDE_BASE="\${CLAUDE_CONFIG_DIR:-\${KARMA_USER_HOME}/.claude}" + export CLAUDE_KARMA_CLAUDE_BASE="\${KARMA_USER_HOME}/.claude" export CLAUDE_KARMA_CORS_ORIGINS="[\"http://localhost:\${FRONTEND_PORT}\",\"http://127.0.0.1:\${FRONTEND_PORT}\"]" exec "\${KARMA_HOME}/api/.venv/bin/uvicorn" main:app --host 0.0.0.0 --port "\${API_PORT}" ) >>"\${API_LOG}" 2>&1 & diff --git a/container/.devcontainer/features/claude-code-native/install.sh b/container/.devcontainer/features/claude-code-native/install.sh index d34998d..feebe10 100755 --- a/container/.devcontainer/features/claude-code-native/install.sh +++ b/container/.devcontainer/features/claude-code-native/install.sh @@ -99,41 +99,4 @@ else exit 1 fi -# === POST-START HOOK === -# Ensures hasCompletedOnboarding is set when token auth is configured. -# Runs as the LAST post-start hook (99- prefix) to catch overwrites from -# Claude Code CLI/extension that may race with postStartCommand. -HOOK_DIR="/usr/local/devcontainer-poststart.d" -mkdir -p "$HOOK_DIR" -cat > "$HOOK_DIR/99-claude-onboarding.sh" << 'HOOK_EOF' -#!/bin/bash -# Ensure hasCompletedOnboarding: true in .claude.json when token auth exists. -# Runs after all setup scripts to catch any overwrites by Claude Code CLI/extension. -_USERNAME="${SUDO_USER:-${USER:-vscode}}" -_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) -_USER_HOME="${_USER_HOME:-/home/$_USERNAME}" -CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}" -CLAUDE_JSON="$CLAUDE_DIR/.claude.json" -CRED_FILE="$CLAUDE_DIR/.credentials.json" - -# Only act when token auth is configured -[ -f "$CRED_FILE" ] || exit 0 - -if [ -f "$CLAUDE_JSON" ]; then - if ! grep -q '"hasCompletedOnboarding"' "$CLAUDE_JSON" 2>/dev/null; then - if command -v jq >/dev/null 2>&1; then - jq '. + {"hasCompletedOnboarding": true}' "$CLAUDE_JSON" > "${CLAUDE_JSON}.tmp" && \ - mv "${CLAUDE_JSON}.tmp" "$CLAUDE_JSON" - else - sed -i '$ s/}$/,\n "hasCompletedOnboarding": true\n}/' "$CLAUDE_JSON" - fi - echo "[claude-onboarding] Injected hasCompletedOnboarding into .claude.json" - fi -else - printf '{\n "hasCompletedOnboarding": true\n}\n' > "$CLAUDE_JSON" - echo "[claude-onboarding] Created .claude.json with hasCompletedOnboarding" -fi -HOOK_EOF -chmod +x "$HOOK_DIR/99-claude-onboarding.sh" - echo "[claude-code-native] Installation complete" diff --git a/container/.devcontainer/features/claude-code-router/install.sh b/container/.devcontainer/features/claude-code-router/install.sh index 910f98a..5e1edc0 100755 --- a/container/.devcontainer/features/claude-code-router/install.sh +++ b/container/.devcontainer/features/claude-code-router/install.sh @@ -161,7 +161,7 @@ done if [ "$_HAS_KEY" = "false" ]; then echo "[claude-code-router] No provider API keys configured — skipping auto-start" - echo "[claude-code-router] Set provider keys in .devcontainer/.secrets and rebuild" + echo "[claude-code-router] Set provider keys in .codeforge/secrets/ and rebuild" exit 0 fi @@ -213,7 +213,7 @@ echo " ccr restart # Restart with new config" echo " ccr status # Check daemon status" echo " ccr-apply # Redeploy config + restart" echo "" -echo "Config: .codeforge/config/claude-code-router.json" +echo "Config: .devcontainer/defaults/codeforge/claude/router/config.json (override with .codeforge/claude/router/config.json)" echo "Docs: https://github.com/musistudio/claude-code-router" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container/.devcontainer/features/codex-cli/README.md b/container/.devcontainer/features/codex-cli/README.md old mode 100644 new mode 100755 index af646f0..26372e7 --- a/container/.devcontainer/features/codex-cli/README.md +++ b/container/.devcontainer/features/codex-cli/README.md @@ -59,15 +59,15 @@ Set the `OPENAI_API_KEY` environment variable: } ``` -Or add it to `.devcontainer/.secrets`: +Or create the secret file: -``` -OPENAI_API_KEY=sk-... +```bash +echo -n "sk-..." > .codeforge/secrets/openai_api_key ``` ### Credential Storage -The `codex-config.toml` file (deployed to `~/.codex/config.toml`) controls credential storage. The default `"file"` backend stores credentials in `~/.codex/auth.json`, which is recommended for containers. +The CodeForge Codex config source is `.devcontainer/defaults/codeforge/codex/config.toml` and can be overridden with `.codeforge/codex/config.toml`. It deploys to `~/.codex/config.toml` and controls credential storage. The default `"file"` backend stores credentials in `~/.codex/auth.json`, which is recommended for containers. ## Usage diff --git a/container/.devcontainer/features/hermes-agent/README.md b/container/.devcontainer/features/hermes-agent/README.md old mode 100644 new mode 100755 index 2467266..7b33e6b --- a/container/.devcontainer/features/hermes-agent/README.md +++ b/container/.devcontainer/features/hermes-agent/README.md @@ -57,7 +57,7 @@ Both files land inside the `~/.hermes/` Docker named volume (`codeforge-hermes-c ### Using MiniMax -If you already have `MINIMAX_API_KEY` in `.devcontainer/.secrets`, it is exported into the container environment. When `hermes setup` prompts for the API key, paste the value from `echo $MINIMAX_API_KEY`. +If you have `MINIMAX_API_KEY` set as an environment variable (e.g., via `.codeforge/secrets/minimax_api_key`), it is available in the container. When `hermes setup` prompts for the API key, paste the value from `echo $MINIMAX_API_KEY`. ### Why No Auto-Seeding? diff --git a/container/.devcontainer/features/mcp-qdrant/README.md b/container/.devcontainer/features/mcp-qdrant/README.md old mode 100644 new mode 100755 index a56920b..73c2ff4 --- a/container/.devcontainer/features/mcp-qdrant/README.md +++ b/container/.devcontainer/features/mcp-qdrant/README.md @@ -154,7 +154,7 @@ The feature will validate these are present and exit with an error if missing. - ✅ **Cloud or Local**: Supports both Qdrant Cloud and local instances - ✅ **Idempotent**: Safe to run multiple times - ✅ **Multi-user**: Automatically detects container user -- ✅ **Config-aware**: Respects `CLAUDE_CONFIG_DIR` environment variable (defaults to `~/.claude`) +- ✅ **Config-aware**: Uses `~/.claude` for Claude Code settings - ✅ **Native mcpServers**: Uses VS Code's native devcontainer mcpServers support (declarative configuration) - ✅ **Dynamic Configuration**: Environment variables loaded from `/workspaces/.qdrant-mcp.env` file - ✅ **Secure**: API keys protected with 600 permissions on env file diff --git a/container/.devcontainer/features/mcp-qdrant/install.sh b/container/.devcontainer/features/mcp-qdrant/install.sh index 5b381b7..8d5174b 100755 --- a/container/.devcontainer/features/mcp-qdrant/install.sh +++ b/container/.devcontainer/features/mcp-qdrant/install.sh @@ -196,7 +196,7 @@ _USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) _USER_HOME="${_USER_HOME:-/home/$_USERNAME}" # Ensure settings.json exists -SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json" +SETTINGS_FILE="${_USER_HOME}/.claude/settings.json" if [ ! -f "$SETTINGS_FILE" ]; then echo "[mcp-qdrant] ERROR: $SETTINGS_FILE not found" exit 1 diff --git a/container/.devcontainer/features/mcp-qdrant/poststart-hook.sh b/container/.devcontainer/features/mcp-qdrant/poststart-hook.sh index 698aa1a..a44334a 100755 --- a/container/.devcontainer/features/mcp-qdrant/poststart-hook.sh +++ b/container/.devcontainer/features/mcp-qdrant/poststart-hook.sh @@ -64,7 +64,7 @@ _USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6) _USER_HOME="${_USER_HOME:-/home/$_USERNAME}" # Ensure settings.json exists -SETTINGS_FILE="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}/settings.json" +SETTINGS_FILE="${_USER_HOME}/.claude/settings.json" if [ ! -f "$SETTINGS_FILE" ]; then echo "[mcp-qdrant] ERROR: $SETTINGS_FILE not found" exit 1 diff --git a/container/.devcontainer/features/oh-my-claude/README.md b/container/.devcontainer/features/oh-my-claude/README.md old mode 100644 new mode 100755 index 5741b24..ebbef7c --- a/container/.devcontainer/features/oh-my-claude/README.md +++ b/container/.devcontainer/features/oh-my-claude/README.md @@ -34,19 +34,16 @@ There is no CodeForge post-start OMC daemon. The upstream OMC CLI owns the lifec ## Provider Keys -Set provider keys in `.devcontainer/.secrets`: +Set provider keys as Docker Compose secrets in `.codeforge/secrets/` (one file per key, raw value only): ```bash -DEEPSEEK_API_KEY= -KIMI_API_KEY= -ALIYUN_API_KEY= -ZHIPU_API_KEY= -ZAI_API_KEY= -MINIMAX_API_KEY= -MINIMAX_CN_API_KEY= -OPENROUTER_API_KEY= +echo -n "your-key" > .codeforge/secrets/deepseek_api_key +echo -n "your-key" > .codeforge/secrets/openrouter_api_key +# Add other provider keys as needed ``` +OMC provider keys (KIMI, ALIYUN, ZHIPU, ZAI, MINIMAX, MINIMAX_CN) are not managed by CodeForge — set them as environment variables in your shell or via OMC's own auth system. + OMC also supports OAuth for selected providers. Use `omc auth list` and `omc auth login ` after the container starts. ## Configuration diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md index c83798c..3e6d19c 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md @@ -82,9 +82,12 @@ Direct model interaction via the Claude API (formerly Anthropic API). Covers Mes .claude/main-system-prompt.md # Active system prompt CLAUDE.md # Project instructions -# User-customizable configuration -.codeforge/config/settings.json # Default settings -.codeforge/config/main-system-prompt.md # Default system prompt +# Packaged defaults and project overrides +.devcontainer/defaults/codeforge/claude/settings/base.json # Default settings source +.devcontainer/.generated/codeforge/claude/settings/settings.json # Generated default settings +.devcontainer/defaults/codeforge/claude/system-prompts/main.md # Default system prompt +.codeforge/claude/settings/base.json # Optional settings override +.codeforge/claude/system-prompts/main.md # Optional prompt override # Plugin directory .devcontainer/plugins/devs-marketplace/plugins/ # All plugins @@ -152,7 +155,7 @@ If the question involves configuration or SDK usage, provide a complete, runnabl **Agent approach**: 1. WebFetch the Claude Code documentation for environment variable reference -2. Read local `.codeforge/config/settings.json` to show which are currently configured +2. Read generated `.devcontainer/.generated/codeforge/claude/settings/settings.json` or deployed `~/.claude/settings.json` to show which are currently configured 3. Summarize the most important variables with their effects **Output includes**: Answer with a categorized list of environment variables (model selection, behavior, performance, experimental features), Documentation References to the official docs, Related Features noting the `settings.json` `env` field as an alternative to shell environment variables. diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py index e014302..2742c02 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py @@ -22,7 +22,7 @@ import sys import os -# Hook gate — check .codeforge/config/disabled-hooks.json +# Hook gate - check ~/.claude/disabled-hooks.json _dh = os.path.join(os.getcwd(), ".codeforge", "config", "disabled-hooks.json") if os.path.exists(_dh): with open(_dh) as _f: diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py index 2557ad9..14e0132 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py @@ -22,7 +22,7 @@ from datetime import datetime, timezone import os -# Hook gate — check .codeforge/config/disabled-hooks.json +# Hook gate - check ~/.claude/disabled-hooks.json _dh = os.path.join(os.getcwd(), ".codeforge", "config", "disabled-hooks.json") if os.path.exists(_dh): with open(_dh) as _f: diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py old mode 100644 new mode 100755 index fbbd537..87a201a --- a/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py @@ -13,7 +13,7 @@ import os import sys -# Hook gate — check .codeforge/config/disabled-hooks.json +# Hook gate - check ~/.claude/disabled-hooks.json _dh = os.path.join(os.getcwd(), ".codeforge", "config", "disabled-hooks.json") if os.path.exists(_dh): with open(_dh) as _f: @@ -23,7 +23,7 @@ def find_incomplete_tasks(teammate_name: str) -> list[str]: """Scan task directories for incomplete tasks owned by this teammate.""" - config_dir = os.environ.get("CLAUDE_CONFIG_DIR", os.path.expanduser("~/.claude")) + config_dir = os.path.expanduser("~/.claude") tasks_base = os.path.join(config_dir, "tasks") if not os.path.isdir(tasks_base): diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json index 7144c07..08209b8 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "auto-code-quality", - "description": "Code quality plugin: collects edited files on edit, validates JSON/JSONC/YAML/TOML syntax instantly, tracks background tasks, and gates stops with a /cq skill prompt — format, lint, and test on demand instead of automatically", + "description": "Code quality plugin: collects edited files on edit, validates JSON/JSONC/YAML/TOML syntax instantly, tracks background activity (subagents + background bash), and gates stops with a /cq skill prompt — format, lint, and test on demand instead of automatically", "author": { "name": "AnExiledDev" } diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md index eb4f1d4..b0d9956 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md @@ -4,11 +4,12 @@ Claude Code plugin that tracks edited files and runs code quality checks on dema ## What It Does -Two-phase pipeline with an explicit quality gate: +Three-phase pipeline with an explicit quality gate: 1. **Track** (PostToolUse on Edit/Write) — Records which files Claude edits, validates data file syntax instantly -2. **Gate** (Stop hook) — Lightweight check: if files were edited and no background tasks are running, blocks the stop and prompts Claude to run `/cq` -3. **Quality** (`/cq` skill) — Claude formats, lints (with auto-fix), and runs affected tests on all edited files +2. **Activity** (SubagentStart/Stop, PreToolUse/PostToolUse/PostToolUseFailure on Bash) — Tracks active subagents and background bash commands so the gate stays silent during orchestration +3. **Gate** (Stop hook) — Lightweight check: if files were edited and no background work is active, blocks the stop and prompts Claude to run `/cq` +4. **Quality** (`/cq` skill) — Claude formats, lints (with auto-fix), and runs affected tests on all edited files The `/cq` skill can also be invoked manually at any time during a session. @@ -64,12 +65,14 @@ Just work normally. When Claude stops after editing files: Type `/cq` at any point to run quality checks on all files edited so far in the session. -### With background tasks +### With background work -The quality gate is background-task-aware: -- While tasks are running, the gate stays silent (no blocking) -- Once all tasks complete and Claude stops, the gate activates -- This prevents race conditions from formatting files that agents are still writing +The quality gate is background-activity-aware. It tracks two types of background work: + +- **Subagents** — tracked via SubagentStart/SubagentStop hooks +- **Background bash** — tracked via PreToolUse (when `run_in_background: true`) and PostToolUse/PostToolUseFailure + +While any background work is active, the gate stays silent. Once all work completes and Claude stops, the gate activates. Entries older than 30 minutes are automatically pruned as stale (handles crashes/timeouts that prevent cleanup). ## Installation @@ -109,16 +112,22 @@ You edit a file (Edit/Write tool) ├─→ collect-edited-files.py Appends path to temp files └─→ syntax-validator.py Validates JSON/YAML/TOML syntax immediately -Background task spawned (TaskCreated) - └─→ task-tracker.py Records task as active +Subagent starts (SubagentStart) + └─→ activity-tracker.py Records agent as active + +Subagent stops (SubagentStop) + └─→ activity-tracker.py Removes agent from active list + +Background bash starts (PreToolUse[Bash] with run_in_background: true) + └─→ activity-tracker.py Records bash command as active -Background task done (TaskCompleted) - └─→ task-tracker.py Removes task from active list +Background bash ends (PostToolUse[Bash] or PostToolUseFailure[Bash]) + └─→ activity-tracker.py Removes bash command from active list Claude stops responding (Stop event) - └─→ quality-gate.py Checks tasks + edited files + └─→ quality-gate.py Checks active work + edited files │ - ├─ Tasks active? → skip (exit 0) + ├─ Work active? → skip (exit 0) ├─ No edits? → skip (exit 0) └─ Edits found → block stop → Claude runs /cq → /cq formats, lints, tests @@ -134,7 +143,7 @@ Session-scoped temp files in `/tmp/`: |------|---------|------------|---------| | `claude-cq-edited-{session_id}` | Edited file paths (format + test) | collect-edited-files.py | quality-gate.py, /cq skill | | `claude-cq-lint-{session_id}` | Edited file paths (lint) | collect-edited-files.py | /cq skill | -| `claude-active-tasks-{session_id}` | Active background task IDs | task-tracker.py | quality-gate.py | +| `claude-active-work-{session_id}` | Active background work (`type:id:timestamp`) | activity-tracker.py | quality-gate.py | All temp files are cleaned up after processing (by the gate and/or the skill). @@ -169,7 +178,7 @@ Add the script name (without `.py`) to the `disabled` array in `~/.claude/disabl } ``` -Available hook names: `collect-edited-files`, `syntax-validator`, `task-tracker`, `quality-gate` +Available hook names: `collect-edited-files`, `syntax-validator`, `activity-tracker`, `quality-gate` ## Conflict Warning @@ -190,7 +199,7 @@ auto-code-quality/ ├── scripts/ │ ├── collect-edited-files.py # File path collector (PostToolUse) │ ├── syntax-validator.py # JSON/YAML/TOML validator (PostToolUse) -│ ├── task-tracker.py # Background task counter (TaskCreated/Completed) +│ ├── activity-tracker.py # Background work tracker (SubagentStart/Stop, Bash Pre/Post/Failure) │ └── quality-gate.py # Stop gate — prompts /cq if needed (Stop) ├── skills/ │ └── cq/ diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json index aa72e8c..3f64f49 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json @@ -1,5 +1,5 @@ { - "description": "Code quality: file collection on edit, syntax validation, task tracking, and /cq quality gate at stop", + "description": "Code quality: file collection on edit, syntax validation, activity tracking (subagents + background bash), and /cq quality gate on demand", "hooks": { "PostToolUse": [ { @@ -16,36 +16,59 @@ "timeout": 5 } ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/activity-tracker.py", + "timeout": 3 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/activity-tracker.py", + "timeout": 3 + } + ] } ], - "TaskCreated": [ + "PostToolUseFailure": [ { + "matcher": "Bash", "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/task-tracker.py", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/activity-tracker.py", "timeout": 3 } ] } ], - "TaskCompleted": [ + "SubagentStart": [ { "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/task-tracker.py", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/activity-tracker.py", "timeout": 3 } ] } ], - "Stop": [ + "SubagentStop": [ { "hooks": [ { "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/quality-gate.py", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/activity-tracker.py", "timeout": 3 } ] diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/task-tracker.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/activity-tracker.py similarity index 54% rename from container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/task-tracker.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/activity-tracker.py index d130ac0..2356d6c 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/task-tracker.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/activity-tracker.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 """ -Task lifecycle tracker — handles TaskCreated and TaskCompleted hooks. +Activity tracker — handles SubagentStart, SubagentStop, PreToolUse[Bash], +PostToolUse[Bash], and PostToolUseFailure[Bash] hooks. -Maintains a session-scoped file listing active background tasks. -The quality-gate Stop hook reads this file to skip blocking when -tasks are still running. +Maintains a session-scoped file listing active background work items +(subagents and background bash commands). The quality-gate Stop hook +reads this file to skip blocking when background work is in progress. + +Entry format: type:id:timestamp (one per line) + - agent:{agent_id}:{unix_ts} + - bash:{tool_use_id}:{unix_ts} Always exits 0. """ @@ -47,25 +52,24 @@ def _locked_append(path: str, data: str) -> None: return -def _locked_remove(path: str, task_id: str) -> None: - """Remove a task ID from the file under an exclusive lock.""" +def _locked_remove(path: str, prefix: str) -> None: + """Remove entries matching a type:id prefix from the file under an exclusive lock.""" for attempt in range(_MAX_RETRIES): try: with open(path, "r+") as f: fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) lines = [line.strip() for line in f if line.strip()] - try: - lines.remove(task_id) - except ValueError: - return # Task ID not found — may have been cleaned up + remaining = [entry for entry in lines if not entry.startswith(prefix)] + + if remaining == lines: + return # Nothing matched - if lines: + if remaining: f.seek(0) f.truncate() - f.write("\n".join(lines) + "\n") + f.write("\n".join(remaining) + "\n") else: - # No more active tasks — remove file after releasing lock f.close() try: os.unlink(path) @@ -92,16 +96,30 @@ def main(): sys.exit(0) event = input_data.get("hook_event_name", "") - if event not in ("TaskCreated", "TaskCompleted"): - sys.exit(0) - - task_id = input_data.get("task_id", "") or input_data.get("id", "") or "unknown" - tasks_file = f"/tmp/claude-active-tasks-{session_id}" - - if event == "TaskCreated": - _locked_append(tasks_file, task_id + "\n") - elif event == "TaskCompleted": - _locked_remove(tasks_file, task_id) + work_file = f"/tmp/claude-active-work-{session_id}" + now = int(time.time()) + + if event == "SubagentStart": + agent_id = input_data.get("agent_id", "") + if agent_id: + _locked_append(work_file, f"agent:{agent_id}:{now}\n") + + elif event == "SubagentStop": + agent_id = input_data.get("agent_id", "") + if agent_id: + _locked_remove(work_file, f"agent:{agent_id}:") + + elif event == "PreToolUse": + tool_input = input_data.get("tool_input", {}) + if tool_input.get("run_in_background") is True: + tool_use_id = input_data.get("tool_use_id", "") + if tool_use_id: + _locked_append(work_file, f"bash:{tool_use_id}:{now}\n") + + elif event in ("PostToolUse", "PostToolUseFailure"): + tool_use_id = input_data.get("tool_use_id", "") + if tool_use_id: + _locked_remove(work_file, f"bash:{tool_use_id}:") sys.exit(0) diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/quality-gate.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/quality-gate.py index a998f02..b638b35 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/quality-gate.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/quality-gate.py @@ -13,6 +13,7 @@ import json import os import sys +import time # Hook gate — check ~/.claude/disabled-hooks.json _dh = os.path.join(os.path.expanduser("~"), ".claude", "disabled-hooks.json") @@ -39,15 +40,44 @@ def main(): print("quality-gate: session_id missing from hook input", file=sys.stderr) sys.exit(0) - # Skip if background tasks are still running - tasks_file = f"/tmp/claude-active-tasks-{session_id}" + # Skip if background work is still running (subagents, background bash) + work_file = f"/tmp/claude-active-work-{session_id}" try: - with open(tasks_file) as f: + with open(work_file) as f: entries = [line.strip() for line in f if line.strip()] if entries: - sys.exit(0) + # Filter out stale entries (>30 min old) + now = time.time() + fresh = [] + for entry in entries: + parts = entry.rsplit(":", 1) + if len(parts) == 2: + try: + ts = int(parts[1]) + if now - ts <= 1800: + fresh.append(entry) + except ValueError: + fresh.append(entry) # Keep unparseable entries + else: + fresh.append(entry) # Keep malformed entries + + if fresh: + # Rewrite file without stale entries + if len(fresh) < len(entries): + try: + with open(work_file, "w") as wf: + wf.write("\n".join(fresh) + "\n") + except OSError: + pass + sys.exit(0) + else: + # All entries stale — clean up and proceed + try: + os.unlink(work_file) + except OSError: + pass except FileNotFoundError: - pass # No tasks file means no active tasks + pass # No work file means no active background work except OSError: pass diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md old mode 100644 new mode 100755 index c050951..b77a71d --- a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md @@ -15,6 +15,7 @@ Intercepts file operations and checks target paths against a set of protected pa | Lock files | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `Gemfile.lock`, `poetry.lock`, `Cargo.lock`, `composer.lock`, `uv.lock` | Must be modified via package manager | | Certificates & keys | `.pem`, `.key`, `.crt`, `.p12`, `.pfx` | Sensitive cryptographic material | | Credential files | `credentials.json`, `secrets.yaml`, `secrets.yml`, `secrets.json`, `.secrets` | Contains secrets | +| Docker secrets | `.codeforge/secrets/` | Contains Docker Compose secrets | | Auth directories | `.ssh/`, `.aws/` | Contains authentication data | | Auth config files | `.netrc`, `.npmrc`, `.pypirc` | Contains authentication credentials | | SSH private keys | `id_rsa`, `id_ed25519`, `id_ecdsa` | SSH private key files | diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py old mode 100644 new mode 100755 index 064d81d..c9d0516 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py @@ -54,6 +54,7 @@ (r"(^|/)secrets\.yml$", "Blocked: secrets.yml contains secrets"), (r"(^|/)secrets\.json$", "Blocked: secrets.json contains secrets"), (r"(^|/)\.secrets$", "Blocked: .secrets file contains secrets"), + (r"(^|/)\.codeforge/secrets/", "Blocked: .codeforge/secrets/ contains Docker secrets"), (r"(^|/)\.ssh/", "Blocked: .ssh/ contains sensitive authentication data"), (r"(^|/)\.aws/", "Blocked: .aws/ contains AWS credentials"), (r"(^|/)\.netrc$", "Blocked: .netrc contains authentication credentials"), diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py old mode 100644 new mode 100755 index 072dce0..21af59c --- a/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py @@ -53,6 +53,7 @@ (r"(^|/)secrets\.yml$", "Blocked: secrets.yml contains secrets"), (r"(^|/)secrets\.json$", "Blocked: secrets.json contains secrets"), (r"(^|/)\.secrets$", "Blocked: .secrets file contains secrets"), + (r"(^|/)\.codeforge/secrets/", "Blocked: .codeforge/secrets/ contains Docker secrets"), # Auth directories and files (r"(^|/)\.ssh/", "Blocked: .ssh/ contains sensitive authentication data"), (r"(^|/)\.aws/", "Blocked: .aws/ contains AWS credentials"), diff --git a/container/tests/plugins/test_guard_protected.py b/container/tests/plugins/test_guard_protected.py old mode 100644 new mode 100755 index cfab10d..bbaa15d --- a/container/tests/plugins/test_guard_protected.py +++ b/container/tests/plugins/test_guard_protected.py @@ -145,6 +145,8 @@ class TestCredentialFiles: "secrets.yml", "secrets.json", ".secrets", + ".codeforge/secrets/gh_token", + ".codeforge/secrets/claude_code_oauth_token", ], ) def test_credential_files_are_protected(self, path: str) -> None: diff --git a/container/tests/plugins/test_guard_protected_bash.py b/container/tests/plugins/test_guard_protected_bash.py old mode 100644 new mode 100755 index 50f78a4..52baacb --- a/container/tests/plugins/test_guard_protected_bash.py +++ b/container/tests/plugins/test_guard_protected_bash.py @@ -167,6 +167,7 @@ class TestBlockedBashWrites: ("sed -i 's/x/y/' package-lock.json", "package-lock.json"), ("cat > .ssh/config", ".ssh/config"), ("mv old credentials.json", "credentials.json"), + ("cp token .codeforge/secrets/gh_token", ".codeforge/secrets/gh_token"), ], ids=[ "redirect-to-env", @@ -175,6 +176,7 @@ class TestBlockedBashWrites: "sed-to-package-lock", "cat-to-ssh-config", "mv-to-credentials", + "cp-to-codeforge-secrets", ], ) def test_protected_file_write_is_blocked(self, command, blocked_path): From 7103dea42f3f612421ec9f61342f0e28c0971bc2 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:37:13 +0000 Subject: [PATCH 15/27] docs: document v3 config layout, new features, and migration Rewrite secrets-and-auth for file-per-secret model. Update all config path references for three-tier layout. Rewrite migration guide for v2/v3 transition. Add changelog entries for RTK, claude-mem, sandcastle, ccdiag, lamarck, and Docker Compose migration. Update Hero stats (4 agents, 17 plugins, 26 tools, 34 skills). Add OG meta tags. Bump docs to v3.0.0. --- docs/astro.config.mjs | 33 +++- docs/package.json | 2 +- docs/src/components/FeatureCard.astro | 1 + docs/src/components/Header.astro | 2 +- docs/src/components/Hero.astro | 18 +-- docs/src/content/docs/customize/hooks.md | 4 +- .../customize/keybindings-and-terminal.md | 4 +- .../docs/customize/optional-components.md | 8 +- docs/src/content/docs/customize/rules.md | 9 +- .../docs/customize/secrets-and-auth.md | 96 +++++++++--- .../customize/settings-and-permissions.md | 37 ++++- .../content/docs/customize/system-prompts.md | 21 ++- .../extend/plugins/protected-files-guard.md | 1 + docs/src/content/docs/reference/changelog.md | 41 +++++ docs/src/content/docs/reference/cli-tools.md | 2 +- .../docs/reference/environment-variables.md | 21 ++- .../content/docs/reference/troubleshooting.md | 4 +- .../docs/start-here/install-in-vscode.md | 36 +++-- .../content/docs/start-here/migrate-to-v2.md | 147 +++++++----------- .../src/content/docs/use/agents-and-skills.md | 2 +- docs/src/content/docs/use/session-basics.md | 2 +- 21 files changed, 311 insertions(+), 180 deletions(-) diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 580ad7e..ca76322 100755 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -38,7 +38,9 @@ export default defineConfig({ Header: "./src/components/Header.astro", }, editLink: { - baseUrl: "https://github.com/AnExiledDev/CodeForge/edit/main/docs/", + baseUrl: + process.env.GITHUB_EDIT_BASE || + "https://github.com/AnExiledDev/CodeForge/edit/main/docs/", }, head: [ { @@ -48,6 +50,35 @@ export default defineConfig({ content: "/og-image.png", }, }, + { + tag: "meta", + attrs: { + property: "og:title", + content: "CodeForge — Your AI Dev Environment", + }, + }, + { + tag: "meta", + attrs: { + property: "og:description", + content: + "A batteries-included Claude Code setup with plugins, agents, skills, and automation.", + }, + }, + { + tag: "meta", + attrs: { + name: "twitter:card", + content: "summary_large_image", + }, + }, + { + tag: "meta", + attrs: { + name: "twitter:image", + content: "/og-image.png", + }, + }, { tag: "link", attrs: { diff --git a/docs/package.json b/docs/package.json index 7a004ac..d55f9fe 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "codeforge-docs", - "version": "0.1.0", + "version": "3.0.0", "private": true, "type": "module", "scripts": { diff --git a/docs/src/components/FeatureCard.astro b/docs/src/components/FeatureCard.astro index 5f3e392..f3f13bc 100644 --- a/docs/src/components/FeatureCard.astro +++ b/docs/src/components/FeatureCard.astro @@ -9,6 +9,7 @@ const { title, description, icon } = Astro.props; ---
+ {/* SAFETY: icon must be a trusted SVG string literal, never user input */}

{title}

{description}

diff --git a/docs/src/components/Header.astro b/docs/src/components/Header.astro index b10742b..87914cd 100644 --- a/docs/src/components/Header.astro +++ b/docs/src/components/Header.astro @@ -22,7 +22,7 @@ const shouldRenderSearch = {shouldRenderSearch && }
- Docs + Docs diff --git a/docs/src/components/Hero.astro b/docs/src/components/Hero.astro index 31866bb..b74b275 100644 --- a/docs/src/components/Hero.astro +++ b/docs/src/components/Hero.astro @@ -38,13 +38,13 @@ const _props = Astro.props;

- The complete Claude Code setup — 12 plugins, 21 tools, - 17 AI agents. Drop it into any project and start building. + The complete Claude Code setup — 17 plugins, 26 tools, + 4 AI agents. Drop it into any project and start building.

{/* ---- CTA buttons ---- */}
- + Get Started @@ -61,10 +61,10 @@ const _props = Astro.props; {/* ---- Stats bar (pill style) ---- */}
- 12 Plugins - 21 Tools - 17 AI Agents - 22 Skills + 17 Plugins + 26 Tools + 4 AI Agents + 34 Skills
@@ -79,7 +79,7 @@ const _props = Astro.props; />
3

Build

-

Type cc to launch Claude Code with 17 agents, 12 plugins, and full quality automation.

+

Type cc to launch Claude Code with 4 agents, 17 plugins, and full quality automation.

diff --git a/docs/src/content/docs/customize/hooks.md b/docs/src/content/docs/customize/hooks.md index bf255b7..737695c 100755 --- a/docs/src/content/docs/customize/hooks.md +++ b/docs/src/content/docs/customize/hooks.md @@ -11,7 +11,7 @@ Hooks are scripts that execute at specific points during a Claude Code session. CodeForge uses eight hook points, each serving a different purpose in the session lifecycle: -Claude Code Karma also registers read-only tracking hooks in CodeForge's generated `settings.json` profiles for `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `Notification`, `Stop`, `SubagentStart`, `SubagentStop`, and `SessionEnd`. These hooks call CodeForge wrapper commands and are managed from `settings.base.json`, not from Karma's Settings UI. +Claude Code Karma also registers read-only tracking hooks in CodeForge's generated `settings.json` profiles for `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `Notification`, `Stop`, `SubagentStart`, `SubagentStop`, and `SessionEnd`. These hooks call CodeForge wrapper commands and are managed from `claude/settings/base.json`, not from Karma's Settings UI. ### PreToolUse @@ -287,7 +287,7 @@ Here is a quick reference of all hooks registered by CodeForge's default plugins ## Per-Hook Disable -Individual hooks can be disabled without turning off their entire plugin. The file `.codeforge/config/disabled-hooks.json` contains a `"disabled"` array of script names: +Individual hooks can be disabled without turning off their entire plugin. The source override is `.codeforge/claude/disabled-hooks.json`, deployed to `~/.claude/disabled-hooks.json`, and contains a `"disabled"` array of script names: ```json { diff --git a/docs/src/content/docs/customize/keybindings-and-terminal.md b/docs/src/content/docs/customize/keybindings-and-terminal.md index a787968..bf62be5 100755 --- a/docs/src/content/docs/customize/keybindings-and-terminal.md +++ b/docs/src/content/docs/customize/keybindings-and-terminal.md @@ -67,7 +67,7 @@ Common command IDs: ### Option 3: Custom Claude Code Keybindings -Edit `.codeforge/config/keybindings.json` to remap Claude Code actions to non-conflicting shortcuts: +Create or edit `.codeforge/claude/keybindings.json` to remap Claude Code actions to non-conflicting shortcuts: ```json { @@ -89,7 +89,7 @@ Edit `.codeforge/config/keybindings.json` to remap Claude Code actions to non-co The keybindings file is deployed to `~/.claude/keybindings.json` on container start via `file-manifest.json`. :::note[Shipped Default] -CodeForge ships with an empty bindings array (`"bindings": []`) in `.codeforge/config/keybindings.json`. No custom keybindings are active by default — add your own entries to the array using the format above. +CodeForge ships with an empty bindings array (`"bindings": []`) in `.devcontainer/defaults/codeforge/claude/keybindings.json`. No custom keybindings are active by default; add your own override at `.codeforge/claude/keybindings.json` using the format above. ::: ## Claude Code Shortcut Reference diff --git a/docs/src/content/docs/customize/optional-components.md b/docs/src/content/docs/customize/optional-components.md index 787f530..0e5c8ba 100755 --- a/docs/src/content/docs/customize/optional-components.md +++ b/docs/src/content/docs/customize/optional-components.md @@ -13,7 +13,7 @@ This page mostly covers components that are **Optional** or **Disabled by defaul ## Per-Hook Disable -Individual hooks can be disabled without turning off their entire plugin. The file `.codeforge/config/disabled-hooks.json` contains a `"disabled"` array of script names: +Individual hooks can be disabled without turning off their entire plugin. The source override is `.codeforge/claude/disabled-hooks.json`, deployed to `~/.claude/disabled-hooks.json`, and contains a `"disabled"` array of script names: ```json { @@ -192,7 +192,7 @@ OpenAI's open-source terminal coding agent. Enabled by default — set `"version ### Configuration -The feature installs Codex CLI via npm and creates `~/.codex/` for credentials and configuration. A user-editable `config.toml` is deployed from `.codeforge/config/codex-config.toml` via the file manifest. +The feature installs Codex CLI via npm and creates `~/.codex/` for credentials and configuration. The packaged default is `.devcontainer/defaults/codeforge/codex/config.toml`; override it with `.codeforge/codex/config.toml`. ```json "./features/codex-cli": {} @@ -203,7 +203,7 @@ The feature installs Codex CLI via npm and creates `~/.codex/` for credentials a Codex CLI requires an OpenAI account. Two methods: 1. **Browser login** — run `codex` and select "Sign in with ChatGPT" (requires ChatGPT Plus, Pro, Business, Edu, or Enterprise plan) -2. **API key** — set `OPENAI_API_KEY` in `.devcontainer/.secrets` or as a Codespaces secret. On container start, `setup-auth.sh` auto-creates `~/.codex/auth.json`. +2. **API key** — place your key in `.codeforge/secrets/openai_api_key` or set `OPENAI_API_KEY` as a Codespaces secret. On container start, `setup-auth.sh` auto-creates `~/.codex/auth.json`. Credentials persist across container rebuilds via a Docker named volume (`codeforge-codex-config-${devcontainerId}`). @@ -237,7 +237,7 @@ hermes setup Pick a provider (Anthropic, OpenAI, MiniMax, local, etc.), paste an API key, and choose a default model. The wizard writes `~/.hermes/config.yaml` and `~/.hermes/.env`. -If you already have `MINIMAX_API_KEY` in `.devcontainer/.secrets`, paste `echo $MINIMAX_API_KEY` into the wizard when prompted. +If you already have a MiniMax API key, place it in `.codeforge/secrets/` and paste the value into the wizard when prompted. `~/.hermes/` is backed by the `codeforge-hermes-config-${devcontainerId}` Docker named volume, so setup is a one-time cost per devcontainer instance. diff --git a/docs/src/content/docs/customize/rules.md b/docs/src/content/docs/customize/rules.md index 2e0f91f..85c3330 100755 --- a/docs/src/content/docs/customize/rules.md +++ b/docs/src/content/docs/customize/rules.md @@ -11,7 +11,7 @@ Rules are Markdown files that define hard constraints applied to every Claude Co Rule files are Markdown documents placed in `.claude/rules/`. Claude Code loads every `.md` file in this directory at session start and treats their contents as mandatory instructions. The filename is descriptive but does not affect loading -- all files are loaded equally. -Rules are deployed from `.codeforge/config/rules/` to `~/.claude/rules/` via the file manifest on every container start. You can also add rules directly to `.claude/rules/` in your project. +Packaged rules live in `.devcontainer/defaults/codeforge/claude/rules/` and deploy to `~/.claude/rules/` via the file manifest on every container start. To override a packaged rule, use the same logical path under `.codeforge/claude/rules/`. You can also add rules directly to `.claude/rules/` in your project. ### Rule Precedence @@ -109,13 +109,14 @@ Claude treats rule file content as mandatory. If you write "consider using X," C To make a rule deploy automatically to all projects: -1. Create the rule file in `.codeforge/config/rules/` +1. Create the rule file in `.codeforge/claude/rules/` 2. Add an entry to `.codeforge/file-manifest.json`: ```json { - "src": "config/rules/my-rule.md", - "dest": "${CLAUDE_CONFIG_DIR}/rules", + "id": "claude.rule.my-rule", + "src": "claude/rules/my-rule.md", + "dest": "${HOME}/.claude/rules", "enabled": true, "overwrite": "if-changed" } diff --git a/docs/src/content/docs/customize/secrets-and-auth.md b/docs/src/content/docs/customize/secrets-and-auth.md index 8e244cf..40b1868 100755 --- a/docs/src/content/docs/customize/secrets-and-auth.md +++ b/docs/src/content/docs/customize/secrets-and-auth.md @@ -1,45 +1,93 @@ --- title: Secrets and Auth -description: Configure secrets, tokens, and authentication for GitHub, NPM, Claude Code, and Codex inside CodeForge. +description: Configure secrets, tokens, and authentication for GitHub, NPM, Claude Code, and other tools inside CodeForge. sidebar: order: 3 --- -CodeForge supports automatic authentication through `.devcontainer/.secrets` and DevContainer-hosted secrets such as Codespaces secrets. +CodeForge uses file-based secrets in `.codeforge/secrets/` and automatic identity derivation from GitHub CLI. -## `.secrets` File +## Secrets Directory -Create `.devcontainer/.secrets` with `KEY=VALUE` entries: +Create individual files under `.codeforge/secrets/`, one secret per file with no trailing newline: + +```text +.codeforge/ + secrets/ + gh_token + npm_token + claude_code_oauth_token + openai_api_key + anthropic_api_key + deepseek_api_key + gemini_api_key + openrouter_api_key +``` + +Each file contains only the raw token value. For example: ```bash -GH_TOKEN=ghp_your_token_here -GH_USERNAME=your-github-username -GH_EMAIL=your-email@example.com -NPM_TOKEN=npm_your_token_here -CLAUDE_AUTH_TOKEN=sk-ant-oat01-your-token-here -OPENAI_API_KEY=sk-your-openai-key +# Create the secrets directory +mkdir -p .codeforge/secrets + +# Add a GitHub token +echo -n "ghp_your_token_here" > .codeforge/secrets/gh_token + +# Add a Claude Code OAuth token +echo -n "sk-ant-oat01-your-token-here" > .codeforge/secrets/claude_code_oauth_token ``` -## Common Variables +## Supported Secrets + +| File | Purpose | +|------|---------| +| `gh_token` | GitHub CLI and HTTPS git auth | +| `npm_token` | npm registry auth | +| `claude_code_oauth_token` | Claude Code OAuth token (native `CLAUDE_CODE_OAUTH_TOKEN` env var) | +| `openai_api_key` | Codex CLI API-key auth | +| `anthropic_api_key` | Direct Anthropic API key | +| `deepseek_api_key` | DeepSeek API key | +| `gemini_api_key` | Google Gemini API key | +| `openrouter_api_key` | OpenRouter API key | + +## Fallback Chain + +CodeForge resolves each secret using a three-step fallback chain: -| Variable | Purpose | -|----------|---------| -| `GH_TOKEN` | GitHub CLI and HTTPS git auth | -| `GH_USERNAME` | Git user name | -| `GH_EMAIL` | Git user email | -| `NPM_TOKEN` | npm auth | -| `CLAUDE_AUTH_TOKEN` | Claude Code long-lived auth token | -| `OPENAI_API_KEY` | Codex CLI API-key auth | +1. **Environment variable** (e.g., Codespaces secrets) — if the env var is already set, it wins +2. **Docker Compose secret** at `/run/secrets/` — file-based secrets mounted by Docker Compose from `.codeforge/secrets/` +3. **Skip** — if neither source provides the secret, it is silently skipped -## Behavior +This means you can use the same `.codeforge/secrets/` layout locally and in Codespaces. In Codespaces, set the secret as a repository or organization secret and the env var takes precedence automatically. -- CodeForge can inject Claude and Codex credentials on container start. -- Codespaces or other host secrets take precedence over `.secrets`. -- Existing credentials are usually left in place when the setup is idempotent. +## Claude Code Authentication + +Claude Code reads the `CLAUDE_CODE_OAUTH_TOKEN` environment variable natively. Place your OAuth token in `.codeforge/secrets/claude_code_oauth_token` and CodeForge sets the env var on container start. + +:::caution[ANTHROPIC_API_KEY conflict] +`CLAUDE_CODE_OAUTH_TOKEN` does **not** work when `ANTHROPIC_API_KEY` is also set. If you have both, Claude Code uses the API key and ignores the OAuth token. Remove `ANTHROPIC_API_KEY` (or the `.codeforge/secrets/anthropic_api_key` file) if you want OAuth authentication. +::: + +## GitHub Identity + +GitHub identity (name and email for git commits) is automatically derived from `gh api user` after `gh auth login` succeeds. You do not need to configure `GH_USERNAME` or `GH_EMAIL` manually. + +### Overriding Identity + +If you need to override the auto-derived identity, add an `identity` block to `.codeforge/container.json`: + +```json +{ + "identity": { + "name": "Your Name", + "email": "your-email@example.com" + } +} +``` ## Security Note -Never commit `.devcontainer/.secrets`. +Never commit `.codeforge/secrets/`. The installer adds it to `.gitignore` automatically. ## Troubleshooting Auth diff --git a/docs/src/content/docs/customize/settings-and-permissions.md b/docs/src/content/docs/customize/settings-and-permissions.md index f896a63..206769f 100755 --- a/docs/src/content/docs/customize/settings-and-permissions.md +++ b/docs/src/content/docs/customize/settings-and-permissions.md @@ -5,9 +5,16 @@ sidebar: order: 1 --- -The main runtime configuration file is `.codeforge/config/settings.json`. +Claude settings are generated, not hand-authored as a full `settings.json`. -Use it to control how Claude Code behaves inside CodeForge. +Packaged inputs live under `.devcontainer/defaults/codeforge/claude/settings/`: + +- `base.json` for shared settings, permissions, hooks, plugin toggles, and status line wiring +- `profiles/*.json` for model-specific overlays + +Project overrides live under `.codeforge/claude/settings/` using the same logical paths. For example, create `.codeforge/claude/settings/base.json` to replace the packaged base, or `.codeforge/claude/settings/profiles/opus-46-200k.json` to replace one model overlay. + +The generator writes final files to `.devcontainer/.generated/codeforge/claude/settings/`, and setup deploys them to `~/.claude/settings*.json`. ## Core Settings @@ -66,7 +73,7 @@ Set any entry to `false` to disable it without uninstalling it. ## Environment Block -`settings.json` can also set environment variables that influence Claude Code internals: +Generated `settings.json` can also set environment variables that influence Claude Code internals: ```json { @@ -97,15 +104,33 @@ Status line behavior is also configured in `settings.json`: Claude Code Karma is installed by default and can display the active Claude settings. CodeForge patches Karma so the Settings page and Settings API are read-only. -Persistent setting changes belong in `.codeforge/config/settings.json` or, for shipped defaults, `defaults/codeforge/config/settings.base.json`. Do not rely on Karma to modify `~/.claude/settings.json`. +Persistent setting changes belong in `.codeforge/claude/settings/base.json` or a profile override under `.codeforge/claude/settings/profiles/`. Shipped defaults live in `.devcontainer/defaults/codeforge/claude/settings/`. Do not rely on Karma to modify `~/.claude/settings.json`. + +## Model Profiles + +The default profile is `opus-46-200k`. Generated `settings.json` is intentionally identical to `settings-opus-46-200k.json`. + +Aliases map to generated files: + +- `cc`, `ccw`, `cc-orc`: `settings.json` +- `cc6`, `ccw6`, `cc-orc6`: `settings-opus-46-200k.json` +- `cc61`, `ccw61`, `cc-orc61`: `settings-opus-46-1m-400k.json` +- `cc7`, `ccw7`, `cc-orc7`: `settings-opus-47-200k.json` +- `cc71`, `ccw71`, `cc-orc71`: `settings-opus-47-1m-400k.json` +- `cc5`, `ccw5`, `cc-orc5`: `settings-opus-45-200k.json` + +Non-1M profiles set `CLAUDE_CODE_DISABLE_1M_CONTEXT=1`; 1M profiles do not set that flag. ## Configuration Precedence When the same setting exists in more than one place, precedence is: 1. environment variables -2. project config in `.codeforge/config/` -3. shipped defaults +2. project overrides in `.codeforge/claude/settings/` +3. generated settings in `.devcontainer/.generated/codeforge/claude/settings/` +4. shipped defaults in `.devcontainer/defaults/codeforge/claude/settings/` + +CodeForge writes `.codeforge/.markers/settings-generated-v3` after generation. Alias launch performs a fast mtime check against that marker and regenerates settings before launching Claude if sources are stale. ## Related diff --git a/docs/src/content/docs/customize/system-prompts.md b/docs/src/content/docs/customize/system-prompts.md index 61e95c9..1f74fbb 100755 --- a/docs/src/content/docs/customize/system-prompts.md +++ b/docs/src/content/docs/customize/system-prompts.md @@ -11,7 +11,8 @@ System prompts define how Claude Code behaves during your sessions -- its coding The main system prompt is loaded for every `cc` or `claude` session. It is the single most influential file in shaping how Claude works with your code. -**Location:** `.codeforge/config/main-system-prompt.md` +**Packaged default:** `.devcontainer/defaults/codeforge/claude/system-prompts/main.md` +**Project override:** `.codeforge/claude/system-prompts/main.md` **Deployed to:** `~/.claude/main-system-prompt.md` ### What It Controls @@ -52,7 +53,8 @@ Each section is self-contained. You can edit, remove, or add sections independen The writing system prompt is activated when you launch Claude with the `ccw` command. It replaces the development-focused prompt with one tuned for creative fiction writing. -**Location:** `.codeforge/config/writing-system-prompt.md` +**Packaged default:** `.devcontainer/defaults/codeforge/claude/system-prompts/writing.md` +**Project override:** `.codeforge/claude/system-prompts/writing.md` **Deployed to:** `~/.claude/writing-system-prompt.md` ### Key Differences from Main Prompt @@ -70,7 +72,8 @@ Use `cc` for coding sessions and `ccw` for writing sessions. Both are shell alia The orchestrator system prompt is activated when you launch Claude with the `cc-orc` command. It configures a delegation-first orchestrator mode where Claude prioritizes decomposing work into subagent tasks rather than doing everything inline. -**Location:** `.codeforge/config/orchestrator-system-prompt.md` +**Packaged default:** `.devcontainer/defaults/codeforge/claude/system-prompts/orchestrator.md` +**Project override:** `.codeforge/claude/system-prompts/orchestrator.md` **Deployed to:** `~/.claude/orchestrator-system-prompt.md` ### Key Differences from Main Prompt @@ -87,7 +90,7 @@ Use `cc` for standard coding sessions, `ccw` for writing sessions, and `cc-orc` ### Editing the Main Prompt -To change development behavior, edit `.codeforge/config/main-system-prompt.md`. Your changes are deployed to `~/.claude/` on the next container start via the file manifest. +To change development behavior, create or edit `.codeforge/claude/system-prompts/main.md`. Your override is deployed to `~/.claude/` on the next container start via the file manifest. For changes to take effect immediately (without restarting the container), edit the deployed copy at `~/.claude/main-system-prompt.md` directly. Be aware that this copy will be overwritten on the next container rebuild unless you change the overwrite mode in `file-manifest.json`. @@ -182,12 +185,14 @@ Rules override the system prompt when they conflict. CLAUDE.md provides context ## Deployment and File Manifest -Both system prompts are listed in `file-manifest.json` and deployed to `~/.claude/` on every container start: +The system prompts are listed in `.devcontainer/defaults/codeforge/file-manifest.json` and deployed to `~/.claude/` on every container start: ```json { - "src": "config/main-system-prompt.md", - "dest": "${CLAUDE_CONFIG_DIR}", + "id": "claude.system-prompts.main", + "src": "claude/system-prompts/main.md", + "dest": "${HOME}/.claude", + "destFilename": "main-system-prompt.md", "enabled": true, "overwrite": "if-changed" } @@ -196,7 +201,7 @@ Both system prompts are listed in `file-manifest.json` and deployed to `~/.claud The `if-changed` mode means your deployed copy is only overwritten when the source file's SHA-256 hash changes. If you want to make persistent local edits to the deployed prompt, change the overwrite mode to `"never"` so your changes survive container rebuilds. :::note[Two Copies] -The source file at `.codeforge/config/main-system-prompt.md` is the canonical version. The deployed copy at `~/.claude/main-system-prompt.md` is what Claude Code actually loads. Edits to the source are deployed on next container start. Edits to the deployed copy take effect immediately but may be overwritten. +The effective source is resolved from `.codeforge/claude/system-prompts/main.md` first, then `.devcontainer/defaults/codeforge/claude/system-prompts/main.md`. The deployed copy at `~/.claude/main-system-prompt.md` is what Claude Code actually loads. Edits to the deployed copy take effect immediately but may be overwritten. ::: ## Related diff --git a/docs/src/content/docs/extend/plugins/protected-files-guard.md b/docs/src/content/docs/extend/plugins/protected-files-guard.md index 7d108d9..8682af5 100755 --- a/docs/src/content/docs/extend/plugins/protected-files-guard.md +++ b/docs/src/content/docs/extend/plugins/protected-files-guard.md @@ -33,6 +33,7 @@ Files that typically contain API keys, database passwords, and other credentials | `credentials.json` | `credentials.json`, `config/credentials.json` | | `secrets.yaml` / `secrets.yml` / `secrets.json` | Any secrets file | | `.secrets` | `.secrets` | +| `.codeforge/secrets/` | `.codeforge/secrets/*` | ### Lock Files diff --git a/docs/src/content/docs/reference/changelog.md b/docs/src/content/docs/reference/changelog.md index 145e5e7..af28de7 100755 --- a/docs/src/content/docs/reference/changelog.md +++ b/docs/src/content/docs/reference/changelog.md @@ -49,6 +49,19 @@ For minor and patch updates, you can usually just rebuild the container. Check t ## Unreleased +### Secrets & Configuration + +- **Docker Compose secrets** — secrets now use Docker Compose file-based secrets mounted at `/run/secrets/`. Place secret files in `.codeforge/secrets/` (one file per secret, raw value only). The `generate-compose.mjs` init script auto-discovers secrets and generates the compose override. Supported secrets: `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. Env vars (Codespaces) remain supported as a fallback. +- **`.secrets` and `.env` files removed** — replaced by `.codeforge/secrets/` (for sensitive tokens) and `.codeforge/container.json` (for setup flags, identity, plugin config). Migration warnings are shown if old files are detected. +- **`CLAUDE_CODE_OAUTH_TOKEN` replaces `CLAUDE_AUTH_TOKEN`** — Claude Code's native OAuth env var is now used for headless auth instead of manual `.credentials.json` injection. Note: `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. +- **Git identity derived from `gh auth login`** — username and email are automatically set from the GitHub API after authentication. Manual `GH_USERNAME` and `GH_EMAIL` config is no longer needed. Override via `.codeforge/container.json` `identity` section if needed. +- **`.codeforge/container.json`** — new structured JSON config replaces `.env` for setup flags (`setup.config`, `setup.aliases`, etc.), Claude version lock, plugin blacklist, and identity overrides. Deployed from packaged defaults with `overwrite: "never"`. +- **`.claude.json` pre-populated** — `hasCompletedOnboarding` and `bypassPermissionsModeAccepted` are now seeded via the file manifest on first deploy, removing the `99-claude-onboarding.sh` post-start hook. +- **`CLAUDE_CONFIG_DIR` removed** — this env var is no longer set or referenced anywhere. All paths use `$HOME/.claude` directly. +- **`CLAUDECODE` env var removed** — the `CLAUDECODE=null` override in `remoteEnv` has been removed. +- **oh-my-claude provider keys removed from default secrets** — `KIMI_API_KEY`, `ZHIPU_API_KEY`, `ALIYUN_API_KEY`, `MINIMAX_API_KEY`, `ZAI_API_KEY`, `MINIMAX_CN_API_KEY` removed from `devcontainer.json` secrets. Users who enable oh-my-claude can add their own secret files. +- **`generate-mounts.mjs` renamed to `generate-compose.mjs`** — now generates both volume mounts and Docker secrets in the compose override. + ### Performance - **TMPDIR removed from remoteEnv** — previously set to `/workspaces/.tmp` (bind mount, slow on NTFS/WSL). Now unset, defaulting to the container's `/tmp` (overlay/tmpfs). Scripts using `${TMPDIR:-/tmp}` already handle this correctly. Any user scripts depending on `$TMPDIR` being a persistent location should use an explicit path instead. @@ -57,6 +70,14 @@ For minor and patch updates, you can usually just rebuild the container. Check t - **Docker Compose migration** — devcontainer now uses `docker-compose.yml` for volume management instead of inline `mounts` in devcontainer.json. Volume names are fixed (no `${devcontainerId}` suffix), simplifying volume management. Existing `${devcontainerId}`-suffixed volumes are orphaned — run `docker volume prune` to clean up, and re-authenticate `gh auth login` on first rebuild. - **Dynamic volume mounts** — `initializeCommand` runs `generate-mounts.mjs` on the host before container build, reading `.codeforge/mounts.json` to generate a `docker-compose.codeforge.yml` override with project-specific volume mounts for high-churn directories. +### Agent Orchestration + +- **New feature: `sandcastle`** — installs [@ai-hero/sandcastle](https://github.com/mattpocock/sandcastle) globally via npm for multi-agent workflow orchestration. Supports parallel agents on separate branches, implement→review→fix pipelines, and plan decomposition workflows. Uses a local-process provider model — agents run as Claude Code CLI child processes in git worktrees, inheriting all `~/.claude/` config and workspace-scope-guard confinement. Pinned to ≥0.5.4 (command injection CVE fix). Set `"version": "none"` in devcontainer.json to disable. + +### Context Optimization + +- **RTK (Rust Token Killer) integration** — new feature (`features/rtk`) installs the RTK CLI proxy that compresses Bash command output before it reaches the LLM context window (60-90% token savings). A PreToolUse hook transparently rewrites supported commands (`git`, `npm`, `cargo`, `docker`, etc.) to route through RTK. Auto-allow is disabled by default — set `RTK_AUTO_ALLOW=1` to skip permission prompts for rewritten commands. Includes Codex CLI awareness file for instruction-based prefix compliance. + ### CLI - **`codeforge doctor` command** — new top-level command that checks environment health: GitHub CLI auth, Git user config, workspace filesystem type (detects slow WSL/NTFS mounts), TMPDIR location, cache directory volume backing, and container memory limits. Supports `--format json` and `--no-color`. @@ -84,6 +105,17 @@ For minor and patch updates, you can usually just rebuild the container. Check t ### Configuration +- **Profile overlay `_meta` shorthand** — profile overlay files (`defaults/codeforge/claude/settings/profiles/*.json`) now use a `_meta: { model, contextWindow }` field instead of repeating the model string and context window across three separate keys. The generator expands `_meta` into `model`, `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, `CLAUDE_CODE_AUTO_COMPACT_WINDOW`, and `CLAUDE_CODE_DISABLE_1M_CONTEXT`. Profiles are now 3–7 lines each. To add a new profile, specify `_meta` plus any profile-specific fields and add an entry to the `profiles` array in `generate-settings-profiles.js`. +- **Thinking settings moved to base** — `MAX_THINKING_TOKENS: 31999` and `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING: 1` moved from individual profile overlays into `base.json` so all profiles inherit them. Opus 4.7 inherits these settings unchanged. +- **`ANTHROPIC_MODEL` / `ANTHROPIC_DEFAULT_OPUS_MODEL` removed** — these env vars were duplicating the top-level `model` field and are no longer set in generated profiles. Model selection is controlled by `model` in the settings JSON (set by `--settings` flag) plus any explicit `--model` CLI flag. +- **`autoCompactWindow` top-level key removed** — context window is now set only via `CLAUDE_CODE_AUTO_COMPACT_WINDOW` env var (still present). The redundant top-level `autoCompactWindow` field has been removed from all profiles. +- **`settings.json` is now a symlink to `settings-opus-46-200k.json`** — the default settings file is no longer a separate generated copy; it is a symlink (or identical file copy on platforms without symlink support) pointing to the opus-46-200k profile. The active default profile is controlled by the `isDefault: true` flag in the `profiles` array of `generate-settings-profiles.js`. + +- **Config defaults layout v3** - `.codeforge/` is now a minimal overrides/state directory with README, markers, checksums, and data. Packaged defaults stay under `.devcontainer/defaults/codeforge/`, organized by `claude/`, `codex/`, and `rtk/`. +- **Claude settings generation moved to `.generated`** - settings are generated from `claude/settings/base.json` plus profile overlays into `.devcontainer/.generated/codeforge/claude/settings/`, then deployed to `~/.claude/settings*.json`. The default profile is now `opus-46-200k`; `settings.json` matches `settings-opus-46-200k.json`. +- **Manifest override IDs** - default manifest entries now require stable `id` values. Optional `.codeforge/file-manifest.json` entries can override, disable, or add files by `id`; source resolution checks `.codeforge/`, generated output, then packaged defaults. +- **v3 migration and stale marker** - setup writes `.codeforge/.markers/config-layout-v3` and a migration report, and alias launch checks `.codeforge/.markers/settings-generated-v3` so stale Claude settings regenerate before `cc` starts. + - **Claude session retention increased** — default `cleanupPeriodDays` is now `90` across all generated Claude settings profiles. Extended thinking remains enabled by default. - **Dangerous-mode permission prompt skipped by default** — `skipDangerousModePermissionPrompt: true` is now set in `settings.base.json` and propagates to all five generated profiles. Suppresses the one-time bypass-permissions confirmation on new devcontainers. - **Effort level bumped to `max` on opus-4-7** — both opus-4-7 overlays (200k and 1M-400k) now set `effortLevel: "max"` and `CLAUDE_CODE_EFFORT_LEVEL: "max"`. Opus-4-5 and opus-4-6 profiles no longer carry any effort-level setting; they use `MAX_THINKING_TOKENS: 31999` with adaptive thinking disabled (token budgets, not effort levels). `CLAUDE_CODE_EFFORT_LEVEL` was removed from base settings so it no longer leaks into non-4.7 profiles. @@ -93,6 +125,15 @@ For minor and patch updates, you can usually just rebuild the container. Check t - **Router features disabled by default** — `claude-code-router` and `oh-my-claude` are both present but configured with `version: "none"` in `devcontainer.json`; CCR autostart is also false. - **Auto mode disabled by default** — `disableAutoMode: "disable"` added to the base settings profile, removing the `auto` permission mode from the Shift+Tab cycle and rejecting `--permission-mode auto` at startup. Users who want auto mode back can override via `~/.claude/settings.json`. +### Memory & Analysis + +- **claude-mem memory system** — real-time observation capture with hybrid search (SQLite + Chroma vectors). Background worker on port 37777 receives hook events via HTTP POST. MCP tools (search, timeline, get_observations, smart_search) registered via poststart.d. Replaces disabled `memory-awareness`, `context-memory`, and `post-tool` hooks. +- **lamarck skill analyzer** — periodic session analysis via cron (every 4h by default). Use `lamarck skill --mode suggest` for skill-specific improvement suggestions from session history. +- **ccdiag session diagnostics** — Go CLI for session health: `ccdiag orphans` (orphaned tool calls), `ccdiag errors` (failure patterns), `ccdiag tokens` (usage breakdown), `ccdiag proxy` (API inspection on port 9119). +- **claude-session-analyzer** — Python quality metrics tool: thinking depth, Read:Edit ratio, frustration indicators. Usage: `analyze-sessions ~/.claude/projects/ --start 2026-01-01`. +- **Go runtime enabled** — `ghcr.io/devcontainers/features/go:1` uncommented in devcontainer.json (required by ccdiag). +- **Cron daemon installed** — required by lamarck for scheduled batch processing. Started via poststart.d hook. + ### oh-my-claude - **StatusLine protection hardened** — install.sh now explicitly resets `statusLine` to ccstatusline after `omc install` completes, since OMC lacks a `--skip-statusline` flag. This prevents OMC from overwriting CodeForge's statusline config. diff --git a/docs/src/content/docs/reference/cli-tools.md b/docs/src/content/docs/reference/cli-tools.md index f1ac7c1..a007611 100755 --- a/docs/src/content/docs/reference/cli-tools.md +++ b/docs/src/content/docs/reference/cli-tools.md @@ -163,7 +163,7 @@ codex # Codex requires authentication on first run: # Option 1: Browser login — select "Sign in with ChatGPT" -# Option 2: API key — set OPENAI_API_KEY in .devcontainer/.secrets +# Option 2: API key — set OPENAI_API_KEY via .codeforge/secrets/openai_api_key ``` :::note[Separate from Claude Code] diff --git a/docs/src/content/docs/reference/environment-variables.md b/docs/src/content/docs/reference/environment-variables.md index ada540d..12d2393 100755 --- a/docs/src/content/docs/reference/environment-variables.md +++ b/docs/src/content/docs/reference/environment-variables.md @@ -17,8 +17,9 @@ Variables that control Claude Code's core behavior inside the CodeForge containe | `ANTHROPIC_DEFAULT_OPUS_MODEL` | Opus model ID | `claude-opus-4-6` | settings.json | | `ANTHROPIC_DEFAULT_SONNET_MODEL` | Sonnet model ID | `claude-sonnet-4-6` | settings.json | | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Haiku model ID | `claude-haiku-4-5-20251001` | settings.json | -| `CLAUDE_CONFIG_DIR` | Claude Code configuration directory | `/home/vscode/.claude` | devcontainer.json | +| ~~`CLAUDE_CONFIG_DIR`~~ | _Removed — Claude Code uses `$HOME/.claude` directly_ | — | — | | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | Maximum tokens per response | `64000` | settings.json | +| `CLAUDE_CODE_DISABLE_1M_CONTEXT` | Disable 1M context model routing for non-1M profiles | `1` on non-1M profiles; unset on 1M profiles | generated settings.json | | `MAX_THINKING_TOKENS` | Maximum tokens for extended thinking | `63999` | settings.json | | `CLAUDE_CODE_SHELL` | Shell used for Bash tool execution | `zsh` | settings.json | | `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | Context usage percentage that triggers auto-compaction | `95` | settings.json | @@ -71,7 +72,7 @@ Variables set by the DevContainer environment that define workspace paths. | Variable | Description | Default | Set In | |----------|-------------|---------|--------| | `WORKSPACE_ROOT` | Workspace root directory | `/workspaces` | devcontainer.json | -| `CLAUDE_CONFIG_DIR` | Claude configuration directory | `/home/vscode/.claude` | devcontainer.json | +| ~~`CLAUDE_CONFIG_DIR`~~ | _Removed — Claude Code uses `$HOME/.claude` directly_ | — | — | | `GH_CONFIG_DIR` | GitHub CLI configuration directory | `/home/vscode/.config/gh` | devcontainer.json | | `HERMES_CDP_ENDPOINT` | Chrome CDP endpoint for Hermes/browser tooling inside the container | `http://192.168.65.254:9223` | devcontainer.json | | `CLAUDECODE` | Set to `null` to unset the detection flag, enabling nested Claude Code sessions | `null` | devcontainer.json | @@ -84,23 +85,22 @@ Variables that configure individual tools within the container. |----------|------|-------------| | `CCMS_PROJECT` | ccms | Default project scope for session search _(only when ccms is enabled)_ | | `CCMS_FORMAT` | ccms | Default output format (`text`, `json`) _(only when ccms is enabled)_ | -| `OPENAI_API_KEY` | codex | OpenAI API key for Codex CLI authentication | +| `OPENAI_API_KEY` | codex | OpenAI API key for Codex CLI authentication (set via `.codeforge/secrets/openai_api_key`) | | `CODEX_HOME` | codex | Override Codex config directory (default: `~/.codex/`) | | `HERMES_CDP_ENDPOINT` | hermes / browser tooling | Host Chrome CDP endpoint; Windows uses the `.devcontainer\scripts\start-hermes-chrome.ps1` portproxy on port 9223. Use the resolved IPv4 address for `host.docker.internal`; Chrome rejects DNS Host headers. | | `RUFF_CONFIG` | ruff | Path to ruff configuration file | | `BIOME_CONFIG_PATH` | biome | Path to biome configuration file | -## Setup Variables (.env) +## Setup Variables (container.json) -These variables live in `.devcontainer/.env` and control what `setup.sh` does on each container start. Copy `.env.example` to `.env` and customize. +These variables are configured in `.codeforge/container.json` and control what `setup.sh` does on each container start. The old `.devcontainer/.env` file has been replaced by this structured JSON config. | Variable | Default | Description | |----------|---------|-------------| -| `CLAUDE_CONFIG_DIR` | `/home/vscode/.claude` | Where Claude Code config files are stored | -| `CODEFORGE_DIR` | (auto-detected) | Source directory for user-customizable config files (`.codeforge/`). Replaces the deprecated `CONFIG_SOURCE_DIR` from v1.x. | +| `CODEFORGE_DIR` | (auto-detected) | Project overrides/state directory (`.codeforge/`). Packaged defaults remain in `.devcontainer/defaults/codeforge/`; this replaces the deprecated copied-config model. | | `SETUP_CONFIG` | `true` | Copy config files per `file-manifest.json` | | `SETUP_ALIASES` | `true` | Add `cc`/`claude`/`ccraw`/`cc-tools` aliases to shell | -| `SETUP_AUTH` | `true` | Configure Git/NPM auth from `.secrets` file | +| `SETUP_AUTH` | `true` | Configure Git/NPM auth from `.codeforge/secrets/` | | `SETUP_PLUGINS` | `true` | Install Anthropic plugins and register local marketplace | | `SETUP_UPDATE_CLAUDE` | `true` | Background-update Claude Code CLI binary | | `CLAUDE_VERSION_LOCK` | (unset) | Pin Claude Code to a specific semver version (e.g., `1.0.33`). When set, the update script installs the exact version instead of updating to latest. | @@ -110,7 +110,7 @@ These variables live in `.devcontainer/.env` and control what `setup.sh` does on | `PLUGIN_BLACKLIST` | `""` | Comma-separated plugin names to skip during installation | :::tip[Disabling Setup Steps] -Set any flag to `false` to skip that step. For example, `SETUP_PROJECTS=false` disables project auto-detection if you manage the Project Manager list manually. +Set any flag to `false` to skip that step. For example, set `SETUP_PROJECTS` to `false` in `.codeforge/container.json` to disable project auto-detection if you manage the Project Manager list manually. ::: ## Language Runtime Variables @@ -137,8 +137,7 @@ Applied when the container is created. Persists across all sessions. ```json { "remoteEnv": { - "WORKSPACE_ROOT": "/workspaces", - "CLAUDE_CONFIG_DIR": "/home/vscode/.claude" + "WORKSPACE_ROOT": "/workspaces" } } ``` diff --git a/docs/src/content/docs/reference/troubleshooting.md b/docs/src/content/docs/reference/troubleshooting.md index 63169cf..dcbac86 100755 --- a/docs/src/content/docs/reference/troubleshooting.md +++ b/docs/src/content/docs/reference/troubleshooting.md @@ -65,7 +65,7 @@ gh auth status gh auth login ``` -Or configure `GH_TOKEN` in `.devcontainer/.secrets`. +Or place your token in `.codeforge/secrets/gh_token`. ### Codex CLI @@ -191,7 +191,7 @@ Check these in order: Use the smallest reset that solves the problem: 1. delete and redeploy runtime config in `~/.claude/` -2. restore default source config under `.codeforge/config/` +2. remove or fix the matching override under `.codeforge/`, then regenerate/deploy settings 3. reset aliases in your shell config 4. rebuild the container 5. disable and re-enable a single feature diff --git a/docs/src/content/docs/start-here/install-in-vscode.md b/docs/src/content/docs/start-here/install-in-vscode.md index e558525..b9ad865 100755 --- a/docs/src/content/docs/start-here/install-in-vscode.md +++ b/docs/src/content/docs/start-here/install-in-vscode.md @@ -15,7 +15,7 @@ From your project root: npx @coredirective/cf-container ``` -This creates a `.devcontainer/` directory and a `.codeforge/` directory in your project. +This creates `.devcontainer/` and a minimal `.codeforge/` overrides/state directory. Packaged defaults stay under `.devcontainer/defaults/codeforge/`; `.codeforge/` is where you put project overrides and where CodeForge stores marker files. ### If `.devcontainer/` already exists @@ -26,7 +26,7 @@ npx @coredirective/cf-container --force npx @coredirective/cf-container --reset ``` -- `--force` performs a smart update and preserves your customizations +- `--force` updates `.devcontainer/` and preserves `.codeforge/` overrides/state - `--reset` recreates `.devcontainer/` from clean defaults while preserving `.codeforge/` ## Step 2: Open the Project in VS Code @@ -44,7 +44,7 @@ The first build usually takes several minutes. During that time CodeForge is: 1. Pulling the base image 2. Installing features and runtimes -3. Deploying configuration and shell aliases +3. Generating Claude settings and deploying effective config 4. Activating plugins and setup scripts Do not interrupt the first build unless it is clearly stuck. If it fails halfway through, rebuild without cache. @@ -53,19 +53,25 @@ Do not interrupt the first build unless it is clearly stuck. If it fails halfway ```text your-project/ -├── .devcontainer/ -│ ├── devcontainer.json -│ ├── .env -│ ├── features/ -│ ├── plugins/ -│ └── scripts/ -├── .codeforge/ -│ ├── file-manifest.json -│ ├── config/ -│ └── scripts/ -└── ...your existing files +|-- .devcontainer/ +| |-- devcontainer.json +| |-- defaults/codeforge/ +| |-- .generated/ +| |-- features/ +| |-- plugins/ +| `-- scripts/ +|-- .codeforge/ +| |-- README.md +| |-- container.json +| |-- secrets/ +| |-- .markers/ +| |-- .checksums/ +| `-- data/ +`-- ...your existing files ``` +`.codeforge/` is not a copied defaults tree. Add files there only when you want to override a packaged default, such as `.codeforge/claude/system-prompts/main.md` or `.codeforge/claude/settings/base.json`. Place secrets (tokens, API keys) in `.codeforge/secrets/` as individual files -- see [Secrets and Auth](/customize/secrets-and-auth/) for details. + ## What to Do Next Once the container is open and the terminal is available, continue to [Verify Your Install](./verify-install/). @@ -74,4 +80,4 @@ Once the container is open and the terminal is available, continue to [Verify Yo - [Use the DevContainer CLI](./devcontainer-cli/) - [Other Clients](./other-clients/) -- [Migrate to v2](./migrate-to-v2/) +- [Migrate to v2/v3](./migrate-to-v2/) diff --git a/docs/src/content/docs/start-here/migrate-to-v2.md b/docs/src/content/docs/start-here/migrate-to-v2.md index 4973bbf..cda3f60 100755 --- a/docs/src/content/docs/start-here/migrate-to-v2.md +++ b/docs/src/content/docs/start-here/migrate-to-v2.md @@ -1,122 +1,95 @@ --- -title: Migrate to v2 -description: What changed in CodeForge v2 and how to upgrade from v1.x — configuration externalization, new file paths, and automatic migration. +title: Migrate to v2/v3 +description: What changed in CodeForge configuration layout and how to upgrade safely. sidebar: order: 8 --- -CodeForge v2 externalizes user configuration from `.devcontainer/config/defaults/` to a new top-level `.codeforge/` directory. This separates your customizations from the DevContainer infrastructure, making updates cleaner and reducing merge conflicts. +CodeForge now uses a defaults-plus-overrides model. -## What Changed +- Packaged defaults live in `.devcontainer/defaults/codeforge/` +- Generated Claude settings live in `.devcontainer/.generated/codeforge/claude/settings/` +- `.codeforge/` is a minimal user-owned overrides/state directory with `README.md`, `.markers/`, `.checksums/`, and `data/` -In v1.x, user-customizable files lived inside `.devcontainer/config/defaults/`. This meant every CodeForge update touched the same directory as your customizations, making it hard to tell what changed and what you'd modified. - -v2 moves all user config to `.codeforge/` — a directory you own. CodeForge updates modify `.devcontainer/` (infrastructure) while `.codeforge/` (your customizations) stays untouched unless you change it. +`.codeforge/` is no longer a copied defaults tree. ## Key Path Changes -| v1.x Path | v2 Path | -|-----------|---------| -| `.devcontainer/config/defaults/settings.json` | `.codeforge/config/settings.json` | -| `.devcontainer/config/defaults/main-system-prompt.md` | `.codeforge/config/main-system-prompt.md` | -| `.devcontainer/config/defaults/rules/` | `.codeforge/config/rules/` | -| `.devcontainer/config/file-manifest.json` | `.codeforge/file-manifest.json` | -| `.devcontainer/connect-external-terminal.sh` | `.codeforge/scripts/connect-external-terminal.sh` | -| `.devcontainer/connect-external-terminal.ps1` | `.codeforge/scripts/connect-external-terminal.ps1` | -| `CONFIG_SOURCE_DIR` env var | `CODEFORGE_DIR` env var | +| Old Path | New Path | +|----------|----------| +| `.codeforge/config/settings.base.json` | `.codeforge/claude/settings/base.json` | +| `.codeforge/config/settings-profiles/*.json` | `.codeforge/claude/settings/profiles/*.json` | +| `.codeforge/config/main-system-prompt.md` | `.codeforge/claude/system-prompts/main.md` | +| `.codeforge/config/writing-system-prompt.md` | `.codeforge/claude/system-prompts/writing.md` | +| `.codeforge/config/orchestrator-system-prompt.md` | `.codeforge/claude/system-prompts/orchestrator.md` | +| `.codeforge/config/rules/` | `.codeforge/claude/rules/` | +| `.codeforge/config/hooks/` | `.codeforge/claude/hooks/` | +| `.codeforge/config/ccstatusline-settings.json` | `.codeforge/claude/statusline/settings.json` | +| `.codeforge/config/claude-code-router.json` | `.codeforge/claude/router/config.json` | +| `.codeforge/config/codex-config.toml` | `.codeforge/codex/config.toml` | +| `.codeforge/config/codex-rtk-awareness.md` | `.codeforge/codex/AGENTS.md` | +| `.codeforge/config/rtk-config.toml` | `.codeforge/rtk/config.toml` | ## Automatic Migration -On first container start after updating to v2, the migration script (`setup-migrate-codeforge.sh`) runs automatically: - -1. Detects the legacy `.devcontainer/config/defaults/` directory -2. Creates the `.codeforge/` directory structure -3. Copies all config files from `defaults/` to `.codeforge/config/` -4. Copies and rewrites `file-manifest.json` (updates internal paths) -5. Moves terminal scripts to `.codeforge/scripts/` -6. Writes a migration marker at `.codeforge/.markers/v2-migrated` - -The migration is **idempotent** — if `.codeforge/` already exists, the script skips entirely. It never overwrites existing files. - -## Manual Migration - -If you prefer to migrate by hand: - -1. **Update CodeForge:** - ```bash - npx @coredirective/cf-container@latest - ``` - -2. **Review the new `.codeforge/` directory** created by the installer. It contains default versions of all config files. - -3. **Move your customizations** from the old paths to the new paths. Only copy files you've actually modified — the installer provides fresh defaults for everything else. - -4. **Update `.env`** if you set `CONFIG_SOURCE_DIR`: - ```bash - # Old (v1.x) - CONFIG_SOURCE_DIR=/custom/path - - # New (v2) - CODEFORGE_DIR=/custom/path - ``` +On container start, setup runs two migration steps: -5. **Rebuild the container:** - - **VS Code:** `Ctrl+Shift+P` → **Dev Containers: Rebuild Container** - - **CLI:** `devcontainer up --workspace-folder . --remove-existing-container` +1. `setup-migrate-codeforge.sh` ensures the minimal `.codeforge/` scaffold exists. +2. `setup-migrate-codeforge-v3.sh` moves known old override files into the new layout. -6. **Verify the migration:** - ```bash - check-setup - ``` +The v3 migration writes: -## How Updates Work in v2 +- marker: `.codeforge/.markers/config-layout-v3` +- report: `.codeforge/.markers/config-layout-v3-report.md` +- backup: `.codeforge/backups/config-layout-v3-/` -CodeForge v2 uses **checksum-based modification detection** to protect your customizations during updates: +If the marker already exists, the v3 migration skips. Generated full settings files such as old `settings.json` and `settings-opus-*.json` are not treated as source of truth; they are reported for audit instead. -- When you run `npx @coredirective/cf-container@latest`, the installer compares each `.codeforge/` file's SHA-256 checksum against the known default. -- **Unmodified files** are updated in place with the new default. -- **Modified files** are preserved. The new default is written as a `.default` file (e.g., `settings.json.default`) for you to review and merge manually. +## Settings Generation -The `--force` flag triggers this smart sync. The `--reset` flag wipes `.devcontainer/` but preserves `.codeforge/` — your customizations are always safe. +Settings are generated from: -## Breaking Changes +- `.devcontainer/defaults/codeforge/claude/settings/base.json` +- `.devcontainer/defaults/codeforge/claude/settings/profiles/*.json` +- optional matching overrides under `.codeforge/claude/settings/` -### `CONFIG_SOURCE_DIR` Deprecated +Generated files are written to `.devcontainer/.generated/codeforge/claude/settings/` and deployed to `~/.claude/settings*.json`. -The `CONFIG_SOURCE_DIR` environment variable is replaced by `CODEFORGE_DIR`. If your `.env` file still sets `CONFIG_SOURCE_DIR`, the startup script: +The default profile is `opus-46-200k`; generated `settings.json` matches `settings-opus-46-200k.json`. Alias launch checks `.codeforge/.markers/settings-generated-v3` and regenerates settings if sources are stale. -1. Detects the stale variable -2. Overrides it internally to use the correct path -3. Auto-comments the line in `.env` with a deprecation warning +## Manifest Overrides -No action is required — it's handled automatically. But you should update `.env` to use `CODEFORGE_DIR` to avoid the warning. +The default manifest lives at `.devcontainer/defaults/codeforge/file-manifest.json`. A project may add `.codeforge/file-manifest.json` to override manifest entries by stable `id`. -## New Capabilities +Examples: -v2 introduces several features alongside the directory restructure: +```json +[ + { + "id": "claude.system-prompts.main", + "src": "claude/system-prompts/main.md", + "overwrite": "never" + }, + { + "id": "claude.rule.session-search", + "disabled": true + } +] +``` -- **`codeforge config apply`** — CLI command to deploy config files to `~/.claude/` on demand (same operation that runs on container start) -- **`.codeforge-preserve`** — list additional files in `.codeforge/` that should be preserved during updates, beyond the defaults -- **Checksum tracking** — `.codeforge/.checksums/` stores SHA-256 hashes so the system knows which files you've modified +Source resolution checks `.codeforge/` first, then generated output, then packaged defaults. ## Troubleshooting -**Migration didn't run automatically:** -- Check if `.codeforge/` already exists (the script skips if it does) -- Verify `WORKSPACE_ROOT` is set correctly (defaults to `/workspaces`) -- Run the migration script manually: `bash .devcontainer/scripts/setup-migrate-codeforge.sh` +**Settings are stale:** run `bash .devcontainer/scripts/ensure-settings-generated.sh --force`. -**Config files not deploying after migration:** -- Run `codeforge config apply` to trigger a manual deployment -- Check `.codeforge/file-manifest.json` — paths should reference `config/` (not `defaults/`) +**Config did not deploy:** run `codeforge config apply` or `bash .devcontainer/scripts/setup-config.sh`. -**Old `CONFIG_SOURCE_DIR` in `.env` causing issues:** -- Remove or comment the line — `CODEFORGE_DIR` is the v2 equivalent -- The startup script handles this automatically, but manual cleanup avoids the deprecation warning +**Migration did not run:** check for `.codeforge/.markers/config-layout-v3`. Remove only if you intentionally want to re-run migration after restoring the old layout from backup. ## Related -- [Settings and Permissions](/customize/settings-and-permissions/) — full settings reference -- [Environment Variables](/reference/environment-variables/) — all environment variables including `CODEFORGE_DIR` -- [Changelog](/reference/changelog/) — v2.0.0 release notes -- [Architecture](/reference/architecture/) — system design and component relationships +- [Settings and Permissions](/customize/settings-and-permissions/) +- [System Prompts](/customize/system-prompts/) +- [Environment Variables](/reference/environment-variables/) +- [Changelog](/reference/changelog/) diff --git a/docs/src/content/docs/use/agents-and-skills.md b/docs/src/content/docs/use/agents-and-skills.md index c000830..41754cc 100755 --- a/docs/src/content/docs/use/agents-and-skills.md +++ b/docs/src/content/docs/use/agents-and-skills.md @@ -18,7 +18,7 @@ Examples: - Ask for an implementation plan and the **architect** agent takes the lead. - Ask to explore the codebase and the **explorer** agent is used. -- Ask for tests and the **test-writer** agent handles framework-aware test generation. +- Ask for help implementing a feature and the **generalist** agent handles code generation with full tool access. ## What Agents Change diff --git a/docs/src/content/docs/use/session-basics.md b/docs/src/content/docs/use/session-basics.md index a4b9e25..62820a2 100755 --- a/docs/src/content/docs/use/session-basics.md +++ b/docs/src/content/docs/use/session-basics.md @@ -40,7 +40,7 @@ When you launch `cc`, CodeForge handles these pieces for you: ## Permissions and Defaults -Session behavior comes from `.codeforge/config/settings.json`, especially: +Session behavior comes from generated `~/.claude/settings.json`, built from `.devcontainer/defaults/codeforge/claude/settings/base.json` plus the default `opus-46-200k` profile. Project overrides go under `.codeforge/claude/settings/`. - `model` - `effortLevel` From b291f8188f7c34e095b40f121a3410f2177345a2 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Thu, 7 May 2026 20:47:25 +0000 Subject: [PATCH 16/27] docs: add debugging with logs section to troubleshooting guide Cover devcontainer build/up log capture, runtime container logs, log storage locations, and diagnostic tools (ccdiag, analyze-sessions, karma-status). --- .../content/docs/reference/troubleshooting.md | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/reference/troubleshooting.md b/docs/src/content/docs/reference/troubleshooting.md index dcbac86..ac32405 100755 --- a/docs/src/content/docs/reference/troubleshooting.md +++ b/docs/src/content/docs/reference/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -description: Symptom-first troubleshooting for install, container, authentication, commands, plugins, ports, and performance issues. +description: Symptom-first troubleshooting for install, container, authentication, commands, plugins, ports, performance, and debugging with logs. sidebar: order: 7 --- @@ -186,6 +186,84 @@ Check these in order: 5. **Docker Desktop version?** Mirrored networking requires Docker Desktop 4.26.0 or later. Update Docker Desktop if you're on an older version. +## Debugging with Logs + +Use this section when you need to capture, find, or increase the verbosity of container logs. + +### Capturing Build and Startup Logs + +The devcontainer CLI writes structured JSON to stdout and human-readable progress to stderr. Separate them to capture both: + +```bash +# Capture build logs (stderr) and result (stdout) separately +devcontainer build --workspace-folder . > result.json 2> build.log + +# Capture startup logs the same way +devcontainer up --workspace-folder . > result.json 2> up.log + +# Increase verbosity with --log-level (trace, debug, info, warn, error) +devcontainer build --workspace-folder . --log-level trace 2> build.log + +# Machine-parseable log lines on stderr +devcontainer up --workspace-folder . --log-format json 2> up.log +``` + +For lower-level Docker build output, disable BuildKit's fancy progress display: + +```bash +BUILDKIT_PROGRESS=plain devcontainer build --workspace-folder . 2> build.log +``` + +### Capturing Runtime Container Logs + +Once a container is running, use `docker logs` to capture its output: + +```bash +# Snapshot to file +docker logs > runtime.log 2>&1 + +# Stream to file in the background +docker logs -f > runtime.log 2>&1 & +``` + +Find your container ID with `docker ps`. + +### Where Logs Are Stored + +| Location | Contents | +|----------|----------| +| `~/.claude/` | Claude Code session data, credentials, project state | +| `~/.lamarck/` | Skill analysis state; runtime logs at `/tmp/lamarck.log` | +| `~/.claude-mem/` | Memory system (settings, SQLite DB, Chroma vectors, logs) | +| `/tmp/` | Transient logs from background processes | + +### Diagnostic Tools + +Three built-in tools help analyze session behavior and errors: + +**ccdiag** — Session diagnostics (Go CLI): + +```bash +ccdiag orphans # find orphaned tool calls (started but never completed) +ccdiag errors # analyze session errors and failure patterns +ccdiag tokens # token usage breakdown per session/tool +ccdiag proxy # launch API proxy on port 9119 for traffic inspection +``` + +**analyze-sessions** — Session quality metrics (Python): + +```bash +# Analyze sessions from a specific date +analyze-sessions ~/.claude/projects/ --start 2026-01-01 + +# JSON output for scripting +analyze-sessions ~/.claude/projects/ --format json +``` + +Metrics include thinking depth, Read:Edit ratio, tool call patterns, and frustration indicators (repeated failures, context resets). + +**karma-status** — Process status and logs for Claude Code Karma. + ## Reset Options Use the smallest reset that solves the problem: From e281f2a158ee9fd083a99a8c275e11ffabe10036 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 02:42:36 +0000 Subject: [PATCH 17/27] fix(container): run gh auth setup-git unconditionally on container start gh auth setup-git was nested inside the GH_TOKEN secret block, so the git credential helper was never configured when users relied on manual gh auth login. Now runs on every container start regardless of token presence. Also detects persisted GitHub CLI credentials from the Docker named volume and derives git identity without requiring a secret. --- container/.devcontainer/AGENTS.md | 2 +- container/.devcontainer/CHANGELOG.md | 1 + container/.devcontainer/scripts/setup-auth.sh | 74 +++++++++++-------- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/container/.devcontainer/AGENTS.md b/container/.devcontainer/AGENTS.md index 22625d7..f25c3cc 100755 --- a/container/.devcontainer/AGENTS.md +++ b/container/.devcontainer/AGENTS.md @@ -101,7 +101,7 @@ The `~/.claude/` directory is backed by a Docker named volume (`codeforge-claude **Claude Code auth:** Set `claude_code_oauth_token` (from `claude setup-token`) in `.codeforge/secrets/`. The token is exported as `CLAUDE_CODE_OAUTH_TOKEN` env var for Claude Code's native headless auth. **WARNING:** `CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. -**GitHub auth + identity:** Set `gh_token` in `.codeforge/secrets/`. On container start, `setup-auth.sh` authenticates via `gh auth login`, configures the git credential helper, and derives `user.name` and `user.email` from the GitHub API. Override identity via `.codeforge/container.json` `identity` section. +**GitHub auth + identity:** Two options: (1) set `gh_token` in `.codeforge/secrets/` for fully automated auth, or (2) run `gh auth login` manually after container build — credentials persist via the `codeforge-gh-config` Docker named volume across rebuilds. On container start, `setup-auth.sh` detects either source, derives `user.name` and `user.email` from the GitHub API, and configures the git credential helper. The credential helper (`gh auth setup-git`) runs unconditionally on every start, so git operations work immediately after a manual `gh auth login`. Override identity via `.codeforge/container.json` `identity` section. **Codex CLI:** Set `openai_api_key` in `.codeforge/secrets/`. Credentials (`~/.codex/`) are backed by a separate Docker named volume. diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index 1749e47..631c669 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -12,6 +12,7 @@ ### Bug Fixes +- **Fix git credential helper not configured without `GH_TOKEN` secret** — `gh auth setup-git` was nested inside the `GH_TOKEN` block, so it only ran when a token secret was provided. Now runs unconditionally on every container start, enabling manual `gh auth login` to work immediately for git operations. Also detects persisted GitHub CLI credentials (from Docker named volume) and derives git identity without requiring a secret. - **Fix named volume ownership for all mount points** — `setup.sh` only fixed `root:root` ownership on `~/.claude`, leaving 6 other Docker named volumes unfixed. `~/.config/gh` and `~/.bun/install/cache` were actively broken (`gh auth login` would fail with `permission denied`). Now loops over all volume mount points from `docker-compose.yml`. ### Developer Tooling diff --git a/container/.devcontainer/scripts/setup-auth.sh b/container/.devcontainer/scripts/setup-auth.sh index 60fab14..3c6d27c 100755 --- a/container/.devcontainer/scripts/setup-auth.sh +++ b/container/.devcontainer/scripts/setup-auth.sh @@ -40,6 +40,38 @@ jq_val() { AUTH_CONFIGURED=false +# --- Git identity from GitHub API --- +# Derives user.name and user.email from gh CLI or container.json overrides. +# Requires an active gh auth session. +_configure_git_identity() { + local _identity_name _identity_email _gh_id + + _identity_name=$(jq_val '.identity.name // empty') + _identity_email=$(jq_val '.identity.email // empty') + + if [ -z "$_identity_name" ]; then + _identity_name=$(gh api user -q .login 2>/dev/null || true) + fi + if [ -z "$_identity_email" ]; then + _identity_email=$(gh api user/emails -q '.[] | select(.primary) | .email' 2>/dev/null || true) + if [ -z "$_identity_email" ]; then + _gh_id=$(gh api user -q .id 2>/dev/null || true) + if [ -n "$_gh_id" ] && [ -n "$_identity_name" ]; then + _identity_email="${_gh_id}+${_identity_name}@users.noreply.github.com" + fi + fi + fi + + if [ -n "$_identity_name" ]; then + git config --global user.name "$_identity_name" + echo "[setup-auth] Git user.name set to $_identity_name" + fi + if [ -n "$_identity_email" ]; then + git config --global user.email "$_identity_email" + echo "[setup-auth] Git user.email set to $_identity_email" + fi +} + # --- GitHub CLI auth --- if _gh_token=$(read_secret gh_token GH_TOKEN); then echo "[setup-auth] Authenticating GitHub CLI..." @@ -47,42 +79,26 @@ if _gh_token=$(read_secret gh_token GH_TOKEN); then unset GH_TOKEN if gh auth login --with-token <<< "$_gh_token" 2>/dev/null; then echo "[setup-auth] GitHub CLI authenticated" - gh auth setup-git 2>/dev/null && echo "[setup-auth] Git credential helper configured" AUTH_CONFIGURED=true - - # Derive git identity from GitHub API - _identity_name=$(jq_val '.identity.name // empty') - _identity_email=$(jq_val '.identity.email // empty') - - if [ -z "$_identity_name" ]; then - _identity_name=$(gh api user -q .login 2>/dev/null || true) - fi - if [ -z "$_identity_email" ]; then - _identity_email=$(gh api user/emails -q '.[] | select(.primary) | .email' 2>/dev/null || true) - if [ -z "$_identity_email" ]; then - _gh_id=$(gh api user -q .id 2>/dev/null || true) - if [ -n "$_gh_id" ] && [ -n "$_identity_name" ]; then - _identity_email="${_gh_id}+${_identity_name}@users.noreply.github.com" - fi - fi - fi - - if [ -n "$_identity_name" ]; then - git config --global user.name "$_identity_name" - echo "[setup-auth] Git user.name set to $_identity_name" - fi - if [ -n "$_identity_email" ]; then - git config --global user.email "$_identity_email" - echo "[setup-auth] Git user.email set to $_identity_email" - fi + _configure_git_identity else echo "[setup-auth] WARNING: GitHub CLI authentication failed" fi - unset _gh_token _identity_name _identity_email _gh_id + unset _gh_token +elif gh auth status &>/dev/null; then + # No token secret provided, but gh is already authenticated from a previous + # manual `gh auth login` (credentials persisted via Docker named volume). + echo "[setup-auth] GitHub CLI already authenticated (persisted credentials)" + AUTH_CONFIGURED=true + _configure_git_identity else - echo "[setup-auth] GH_TOKEN not set, skipping GitHub CLI auth" + echo "[setup-auth] GH_TOKEN not set and no persisted login, skipping GitHub CLI auth" fi +# Always configure git credential helper — works with or without active login. +# This ensures manual `gh auth login` works immediately for git operations. +gh auth setup-git 2>/dev/null && echo "[setup-auth] Git credential helper configured" + # --- NPM auth --- if _npm_token=$(read_secret npm_token NPM_TOKEN); then echo "[setup-auth] Configuring NPM registry auth..." From aaf9358a4248df5157e02f2d603e98f40399c1ec Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 03:15:49 +0000 Subject: [PATCH 18/27] feat(container): add browser opener for gh auth login in devcontainers Tools like `gh auth login` try to open a browser but fail with "executable not found" in containers. Add a BROWSER fallback script that uses VS Code's remote CLI when available, or prints the URL cleanly for manual copy. Does not override VS Code's native BROWSER in integrated terminals. --- container/.devcontainer/CHANGELOG.md | 4 +++ .../.devcontainer/scripts/open-browser.sh | 30 +++++++++++++++++++ .../.devcontainer/scripts/setup-aliases.sh | 5 ++++ 3 files changed, 39 insertions(+) create mode 100755 container/.devcontainer/scripts/open-browser.sh diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index 631c669..020504c 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -10,6 +10,10 @@ - **Container timezone** — new `timezone` field in `.codeforge/container.json` (default: `America/Chicago`). Set to any IANA timezone (e.g., `America/New_York`, `Europe/London`). Applied via `TZ` env var on container start. +### Authentication + +- **Browser opener for `gh auth login`** — tools that need to open a browser (like `gh auth login`) now get a friendly fallback instead of a wall of "executable not found" errors. In VS Code terminals, URLs open on the host automatically via VS Code's built-in forwarding. In external terminals (Windows Terminal, tmux, etc.), the URL is printed cleanly for manual copy. Set via `$BROWSER` env var with conditional fallback — does not override VS Code's native browser handler. + ### Bug Fixes - **Fix git credential helper not configured without `GH_TOKEN` secret** — `gh auth setup-git` was nested inside the `GH_TOKEN` block, so it only ran when a token secret was provided. Now runs unconditionally on every container start, enabling manual `gh auth login` to work immediately for git operations. Also detects persisted GitHub CLI credentials (from Docker named volume) and derives git identity without requiring a secret. diff --git a/container/.devcontainer/scripts/open-browser.sh b/container/.devcontainer/scripts/open-browser.sh new file mode 100755 index 0000000..0b72220 --- /dev/null +++ b/container/.devcontainer/scripts/open-browser.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-only +# Copyright (c) 2026 Marcus Krueger +# Browser opener for devcontainer environments. +# +# Set as $BROWSER so tools (gh, npm, etc.) can open URLs on the host. +# Detection order: +# 1. VS Code remote CLI (code --open-url) — works in VS Code terminals +# 2. Friendly fallback — prints the URL for manual copy +# +# Always exits 0 so the calling tool continues normally. + +url="${1:?Usage: open-browser.sh }" + +# --- VS Code remote CLI --- +# Available when VS Code Server is running and the IPC socket is connected. +# Only works in VS Code's integrated terminal, not external terminals. +if [ -n "$VSCODE_IPC_HOOK_CLI" ] && command -v code >/dev/null 2>&1; then + code --open-url "$url" 2>/dev/null && exit 0 +fi + +# --- Friendly fallback --- +# No browser available — print the URL clearly for manual copy. +echo "" +echo " Open this URL in your browser:" +echo "" +echo " $url" +echo "" + +exit 0 diff --git a/container/.devcontainer/scripts/setup-aliases.sh b/container/.devcontainer/scripts/setup-aliases.sh index 9e1fe5a..a38b33b 100755 --- a/container/.devcontainer/scripts/setup-aliases.sh +++ b/container/.devcontainer/scripts/setup-aliases.sh @@ -104,6 +104,11 @@ export DEVCONTAINER_SCRIPTS="${DEVCONTAINER_SCRIPTS}" export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 +# Browser opener — lets tools (gh, npm, etc.) open URLs on the host. +# VS Code sets BROWSER in its own terminals; this fallback covers external +# terminals (Windows Terminal, tmux, etc.) with a friendly "copy this URL" message. +export BROWSER="\${BROWSER:-${DEVCONTAINER_SCRIPTS}/open-browser.sh}" + # Terminal color defaults — Docker sets TERM=xterm (8 colors); upgrade to 256-color if [ "\$TERM" = "xterm" ] || [ -z "\$TERM" ]; then export TERM=xterm-256color From 578f11f78d812ebb18f19efc8e97b7fe9ced3ed2 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 03:34:46 +0000 Subject: [PATCH 19/27] feat(container): add Docker-native port forwarding and fix VS Code detection Docker Compose port mappings provide reliable port forwarding independent of VS Code, working with WSL mirrored networking and all devcontainer clients. Switch VS Code from broken hybrid mode to output-based detection as a supplementary mechanism. - Map 6 service ports in docker-compose.yml bound to 127.0.0.1 - Change autoForwardPortsSource from hybrid to output - Add missing portsAttributes for Astro (4321) and mitmproxy (8081) - Update accessing-services docs to reflect Docker Compose as primary --- container/.devcontainer/CHANGELOG.md | 6 ++ container/.devcontainer/devcontainer.json | 10 ++- container/.devcontainer/docker-compose.yml | 9 +++ .../content/docs/use/accessing-services.md | 77 +++++++++++++------ 4 files changed, 78 insertions(+), 24 deletions(-) diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index 020504c..1a70ae8 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Networking + +- **Docker-native port forwarding** — all service ports are now mapped in `docker-compose.yml` via `ports:` bound to `127.0.0.1`. This provides reliable port forwarding independent of VS Code, and works with WSL mirrored networking out of the box. Mapped ports: Karma Dashboard (7847), Karma API (7848), Claude-Mem Worker (37777), Astro docs dev server (4321), mitmproxy (8081), ccdiag API Proxy (9119). +- **Switch VS Code port detection to `output` mode** — `remote.autoForwardPortsSource` changed from `hybrid` to `output`. The `hybrid` mode has known reliability issues (silently stops working after detecting 20+ ports). The `output` mode is less aggressive but more reliable, and Docker Compose port mappings now handle the primary forwarding. +- **Add missing port labels** — added `portsAttributes` entries for the Astro docs dev server (4321) and mitmproxy/codeforge proxy (8081) so VS Code shows proper labels when these ports are detected. + ### Status Line - **Rate limit reset times** — the 5-hour and 7-day rate limit widgets now display when limits reset (e.g., `5h: 42% (14:30)` and `7d: 15% (Mon 09:00)`). Uses custom-command scripts instead of built-in ccstatusline types. diff --git a/container/.devcontainer/devcontainer.json b/container/.devcontainer/devcontainer.json index 53f29c0..4f950d2 100755 --- a/container/.devcontainer/devcontainer.json +++ b/container/.devcontainer/devcontainer.json @@ -234,6 +234,14 @@ "label": "ccdiag API Proxy", "onAutoForward": "silent" }, + "4321": { + "label": "Astro Docs Dev Server", + "onAutoForward": "notify" + }, + "8081": { + "label": "mitmproxy (codeforge proxy)", + "onAutoForward": "notify" + }, "*": { "onAutoForward": "notify" } @@ -280,7 +288,7 @@ "wenbopan.vscode-terminal-osc-notifier": ["ui"] }, "remote.defaultExtensionsIfInstalledLocally": [], - "remote.autoForwardPortsSource": "hybrid", + "remote.autoForwardPortsSource": "output", // --- Git --- "git.autofetch": false, diff --git a/container/.devcontainer/docker-compose.yml b/container/.devcontainer/docker-compose.yml index fe7d418..2a187a7 100644 --- a/container/.devcontainer/docker-compose.yml +++ b/container/.devcontainer/docker-compose.yml @@ -1,6 +1,15 @@ services: codeforge: image: mcr.microsoft.com/devcontainers/python:3.14 + ports: + # Auto-started services + - "127.0.0.1:7847:7847" # Claude Code Karma Dashboard + - "127.0.0.1:7848:7848" # Claude Code Karma API + - "127.0.0.1:37777:37777" # Claude-Mem Worker + # On-demand services + - "127.0.0.1:4321:4321" # Astro docs dev server + - "127.0.0.1:8081:8081" # mitmproxy (codeforge proxy) + - "127.0.0.1:9119:9119" # ccdiag API Proxy volumes: # Persistent config volumes - codeforge-claude-config:/home/vscode/.claude diff --git a/docs/src/content/docs/use/accessing-services.md b/docs/src/content/docs/use/accessing-services.md index e9cd031..4757d8d 100755 --- a/docs/src/content/docs/use/accessing-services.md +++ b/docs/src/content/docs/use/accessing-services.md @@ -5,7 +5,7 @@ sidebar: order: 10 --- -CodeForge runs inside a Docker container. When a service inside the container listens on a port (e.g., a dev server on port 3000), you need a forwarding mechanism to access it from your host machine. Which mechanism to use depends on your DevContainer client. +CodeForge runs inside a Docker container. When a service inside the container listens on a port (e.g., a dev server on port 4321), you need a forwarding mechanism to access it from your host machine. Which mechanism to use depends on your DevContainer client. :::tip[When to read this page] If you use VS Code, you usually only need this page when automatic forwarding is not enough. If you use the DevContainer CLI, JetBrains, or direct SSH, this page matters much earlier. @@ -15,11 +15,29 @@ If you use VS Code, you usually only need this page when automatic forwarding is | Mechanism | Client | Discovery | Setup Required | |-----------|--------|-----------|----------------| -| VS Code auto-detect | VS Code only | Dynamic — all ports | None | +| Docker Compose port mapping | Any client | Static — configured ports | None (built-in) | +| VS Code auto-detect | VS Code only | Dynamic — output-based | None | | devcontainer-bridge (`dbr`) | Any terminal client | Dynamic — polls `/proc/net/tcp` | Host daemon required | | SSH tunneling | Any SSH client | Manual | Per-port command | -Default CodeForge services include Claude Code Karma on port `7847` and the Karma API on port `7848`. +### Docker Compose Port Mapping (Primary) + +CodeForge maps all known service ports in `docker-compose.yml`, bound to `127.0.0.1`. This is the primary forwarding mechanism — it works with every client, requires no setup, and is independent of VS Code. + +| Port | Service | Lifecycle | +|------|---------|-----------| +| 7847 | Claude Code Karma Dashboard | Auto-started | +| 7848 | Claude Code Karma API | Auto-started | +| 37777 | Claude-Mem Worker | Auto-started | +| 4321 | Astro docs dev server | On-demand (`npm run dev` in `docs/`) | +| 8081 | mitmproxy | On-demand (`codeforge proxy`) | +| 9119 | ccdiag API Proxy | On-demand (`ccdiag proxy`) | + +With **WSL mirrored networking**, these ports are accessible on `localhost` from Windows with no additional configuration. On macOS/Linux, Docker Desktop forwards them to `localhost` natively. + +:::note +On-demand ports (4321, 8081, 9119) are mapped at container startup but only become accessible when the corresponding service is running. Connections are refused until the service starts. +::: ## Windows: Mirrored Networking (Recommended) @@ -33,18 +51,18 @@ This replaces the need for `dbr` (which does not support Windows) and eliminates ## VS Code Auto-Detect -VS Code automatically detects ports opened inside the container and forwards them to your host. CodeForge configures this in `devcontainer.json`: - -- **All ports** are auto-forwarded with a notification prompt +VS Code provides supplementary port detection on top of the Docker Compose mappings. CodeForge uses `output` mode (`remote.autoForwardPortsSource`), which detects ports printed to VS Code terminals. -No setup required — ports appear in the VS Code **Ports** panel as services start. Click the local address to open in your browser. +:::caution[VS Code forwarding is supplementary, not primary] +Docker Compose port mappings handle all known service ports reliably. VS Code auto-detect is a bonus for dynamically-allocated ports (e.g., OAuth callbacks, ephemeral dev servers). Do not rely on it as your only forwarding mechanism — it only works while VS Code is actively connected and has known reliability issues when backgrounded. +::: -:::caution[Requires VS Code to be running] -Port forwarding only works while VS Code is open and connected to the devcontainer. If you close VS Code or disconnect, all forwarded ports become inaccessible. For persistent port forwarding that doesn't depend on your editor, use [devcontainer-bridge](#devcontainer-bridge-dbr). +:::note[Why not `hybrid` mode?] +VS Code's `hybrid` port detection mode has [known reliability issues](https://github.com/microsoft/vscode/issues/200795) — it silently stops working after detecting 20+ ports, which is common in feature-rich devcontainers. The `output` mode is less aggressive but more reliable. Docker Compose port mappings compensate for the reduced detection scope. ::: :::note -This only works inside VS Code and GitHub Codespaces. The `devcontainer` CLI, JetBrains Gateway, and DevPod ignore `portsAttributes` in `devcontainer.json`. +`portsAttributes` labels only work inside VS Code and GitHub Codespaces. The `devcontainer` CLI, JetBrains Gateway, and DevPod ignore them — but Docker Compose port mappings work with all clients. ::: ## devcontainer-bridge (`dbr`) @@ -116,15 +134,17 @@ This requires SSH access to the container, which is available when connecting vi ## Which Should I Use? -| If you use... | Recommended mechanism | -|---------------|----------------------| +Docker Compose port mappings work with **all clients** — known service ports are always forwarded. The table below covers supplementary mechanisms for dynamic ports or enhanced UX. + +| If you use... | Supplementary mechanism | +|---------------|------------------------| | **Windows (any client)** | **Mirrored networking** — [zero config after setup](/start-here/windows-networking/) | -| VS Code | Auto-detect (built-in, zero config) | -| DevContainer CLI | `dbr` (dynamic, automatic) — see the [CLI guide](/start-here/devcontainer-cli/) | +| VS Code | Auto-detect labels + notifications (built-in) | +| DevContainer CLI | `dbr` for dynamic ports — see the [CLI guide](/start-here/devcontainer-cli/) | | JetBrains Gateway | Gateway's built-in forwarding, or `dbr` as fallback | | Codespaces | Auto-detect (built-in to Codespaces) | | DevPod | DevPod's built-in SSH tunneling, or `dbr` | -| Direct SSH | SSH tunneling for specific ports, or `dbr` for all ports | +| Direct SSH | SSH tunneling for dynamic ports, or `dbr` for all ports | ## Browser Automation (CDP) @@ -132,17 +152,28 @@ For browser automation using agent-browser's host Chrome connection, Windows use ## Configuration -Port forwarding behavior is configured in `.devcontainer/devcontainer.json`: +Port forwarding is configured at two levels: + +### Docker Compose (all clients) + +Service ports are mapped in `.devcontainer/docker-compose.yml` under `ports:`, bound to `127.0.0.1` for security. This is the primary forwarding mechanism and works with every client. + +To add a new port, add a line to the `ports:` section: + +```yaml +ports: + - "127.0.0.1:YOUR_PORT:YOUR_PORT" # Description +``` + +### VS Code labels (VS Code / Codespaces only) + +Port labels and notification behavior are configured via `portsAttributes` in `.devcontainer/devcontainer.json`: ```jsonc "portsAttributes": { - "*": { - "onAutoForward": "notify" - } + "7847": { "label": "Claude Code Karma", "onAutoForward": "notify" }, + "*": { "onAutoForward": "notify" } } ``` -- `forwardPorts` — static port list for specific services you always want forwarded -- `portsAttributes` — labels and behavior for auto-detected ports (VS Code / Codespaces only) - -These settings are ignored by non-VS Code clients. Use `dbr` or SSH tunneling instead. +These settings are ignored by non-VS Code clients. Docker Compose port mappings and `dbr` work regardless. From 3ad29703ec6267363791dbbf05e9c0da528f3a1c Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 04:30:09 +0000 Subject: [PATCH 20/27] feat(container): add AI-CONTEXT.md, /codeforge skill, and remove skill-suggester Ship machine-readable environment documentation for AI assistants and a complementary /codeforge skill for on-demand deep context. Remove the deprecated skill auto-suggestion system and the Codex AGENTS.md bad pattern. - Add AI-CONTEXT.md with toolchain, filesystem, constraints, auth, persistence - Add /codeforge skill with detailed reference files (toolchain, filesystem, constraints) - Update AGENTS.md with AI context reference and maintenance directive - Update README.md with user-facing AI context guidance - Remove skill-suggester.py and UserPromptSubmit hook - Remove Codex AGENTS.md from defaults and file-manifest - Update skill-engine plugin metadata and README --- container/.devcontainer/AGENTS.md | 38 ++- container/.devcontainer/AI-CONTEXT.md | 66 +++++ container/.devcontainer/CHANGELOG.md | 13 + container/.devcontainer/README.md | 19 +- .../defaults/codeforge/codex/AGENTS.md | 37 --- .../defaults/codeforge/file-manifest.json | 8 - .../.claude-plugin/marketplace.json | 4 +- .../skill-engine/.claude-plugin/plugin.json | 6 +- .../plugins/skill-engine/README.md | 59 +---- .../plugins/skill-engine/hooks/hooks.json | 16 +- .../skill-engine/scripts/skill-suggester.py | 232 ------------------ .../skill-engine/skills/codeforge/SKILL.md | 45 ++++ .../codeforge/references/constraints.md | 97 ++++++++ .../skills/codeforge/references/filesystem.md | 83 +++++++ .../skills/codeforge/references/toolchain.md | 85 +++++++ container/test.js | 1 - 16 files changed, 455 insertions(+), 354 deletions(-) create mode 100644 container/.devcontainer/AI-CONTEXT.md delete mode 100644 container/.devcontainer/defaults/codeforge/codex/AGENTS.md delete mode 100644 container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py create mode 100644 container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/SKILL.md create mode 100644 container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md create mode 100644 container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/filesystem.md create mode 100644 container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/toolchain.md diff --git a/container/.devcontainer/AGENTS.md b/container/.devcontainer/AGENTS.md index f25c3cc..90abc68 100755 --- a/container/.devcontainer/AGENTS.md +++ b/container/.devcontainer/AGENTS.md @@ -23,6 +23,15 @@ CodeForge devcontainer for AI-assisted development with Claude Code. Config files deploy via `defaults/codeforge/file-manifest.json` on every container start. Most deploy to `~/.claude/`; ccstatusline config deploys to `~/.config/ccstatusline/`. Each entry supports `overwrite`: `"if-changed"` (default, sha256), `"always"`, or `"never"`. Supported variables: `${WORKSPACE_ROOT}`, `${CODEFORGE_DIR}`, `${HOME}`. +## AI Environment Reference + +Machine-readable environment context for AI assistants is in `AI-CONTEXT.md`. +Users should reference this file from their project's AGENTS.md or CLAUDE.md: + + @.devcontainer/AI-CONTEXT.md + +For deeper context, use the `/codeforge` skill (toolchain, filesystem, constraints). + ## Commands | Command | Purpose | @@ -63,7 +72,7 @@ Declared in `settings.json` under `enabledPlugins`, auto-activated on start: ### Active - **agent-system** — 4 custom agents (architect, claude-guide, explorer, generalist) + built-in agent redirection + `/verify-tests` skill -- **skill-engine** — 2 coding knowledge packs (`/team`, `/agent-browser`) + auto-suggestion +- **skill-engine** — 24 coding knowledge packs loaded on demand via `/skill` (e.g., `/codeforge`, `/team`, `/agent-browser`) - **auto-code-quality** — File tracking, syntax validation, `/cq` quality gate (format + lint + test on demand) - **session-context** — Git state injection, TODO harvesting, commit reminders - **workspace-scope-guard** — Blocks writes outside working directory @@ -215,3 +224,30 @@ Available since Claude Code v2.1.78. Resolves to a dedicated data directory per **Current state:** Not used in CodeForge plugins. Plugins store transient state in `/tmp/{prefix}-{session_id}`. **Future use:** When a plugin needs persistent state across sessions (cached configs, learned preferences, usage frequency), use `${CLAUDE_PLUGIN_DATA}` in hook commands instead of `/tmp/`. + +## AI Documentation Maintenance + +`AI-CONTEXT.md` is documentation FOR AI assistants, not humans. + +### Update Triggers + +- Feature added or removed from devcontainer.json +- Safety plugin constraint changed +- Tool added, removed, or replaced +- Filesystem topology changed (new volumes, mount points) +- Resource limits changed + +### Quality Standard + +- Facts, not prose. Declarative statements only. +- Constraints before capabilities. +- Tables for tool lists. No version numbers (they go stale). +- ~700 token target, hard ceiling ~800 tokens. +- No tutorials, no examples, no explanations of "why." +- Test: "Would an AI need this to avoid an error?" If no, cut it. + +### /codeforge Skill Maintenance + +When AI-CONTEXT.md changes, check if the corresponding skill reference +file needs updating. AI-CONTEXT.md is the summary; skill references +are the deep dive. diff --git a/container/.devcontainer/AI-CONTEXT.md b/container/.devcontainer/AI-CONTEXT.md new file mode 100644 index 0000000..057c2df --- /dev/null +++ b/container/.devcontainer/AI-CONTEXT.md @@ -0,0 +1,66 @@ +# CodeForge Container — AI Context + +## Environment + +- Debian-based devcontainer, `vscode` user, `bash` shell +- Memory: 6 GB hard limit, no swap +- Workspace: `/workspaces/` (bind mount from host) +- Projects: `/workspaces/projects//` + +## Filesystem + +| Path | Purpose | Persistence | +|------|---------|-------------| +| `/workspaces/` | Host-synced workspace (bind mount) | Host filesystem | +| `~/.claude/` | Claude Code config, sessions, rules | Named volume (survives rebuild) | +| `~/.config/gh/` | GitHub CLI credentials | Named volume | +| `~/.bun/` | Bun cache | Named volume | +| `~/.cache/` | General caches (uv, npm, pip) | Named volume | +| `~/.codex/` | Codex CLI config | Named volume | +| `~/.claude-mem/` | Claude-Mem persistent memory | Named volume | +| `.codeforge/` | Project-level config overrides | In workspace (host-synced) | +| `/tmp/` | Ephemeral scratch space | Lost on rebuild | + +Config resolution: `.codeforge/` overrides → `.devcontainer/defaults/codeforge/` defaults. + +## Toolchain + +| Category | Tools | +|----------|-------| +| Languages | Python 3, Node.js (LTS), Go, Rust, Bun | +| Dev tools | gh, docker, git, jq, curl, tmux, uv, npm, pip | +| Formatters | biome, ruff, shfmt, dprint | +| Linters | shellcheck, hadolint, ruff, biome | +| Code intelligence | ast-grep, tree-sitter, Pyright | +| AI tools | claude (Claude Code), codeforge, codex, hermes, agent-browser | + +## Constraints + +- **Blocked commands**: Destructive bash blocked by `dangerous-command-blocker` plugin (rm -rf, sudo rm, chmod 777, force push, etc.) +- **Protected files**: `.git/`, lock files, credentials blocked by `protected-files-guard` plugin +- **Scope restriction**: Writes confined to project directory by `workspace-scope-guard` plugin +- **Memory**: 6 GB hard limit, no swap — avoid memory-intensive operations +- **No apt installs**: Use devcontainer features for system packages + +## Authentication + +- **GitHub**: `gh auth login` or `.codeforge/secrets/gh_token` +- **Claude Code**: `.codeforge/secrets/claude_code_oauth_token` +- **API keys**: Individual files in `.codeforge/secrets/` (gh_token, npm_token, claude_code_oauth_token, openai_api_key, anthropic_api_key, deepseek_api_key, gemini_api_key, openrouter_api_key) +- All credentials persist via Docker named volumes across rebuilds + +## Persistence Model + +| Layer | Survives rebuild | Examples | +|-------|-----------------|----------| +| Named volumes | Yes | ~/.claude, ~/.config/gh, caches | +| Bind mount | Yes (host-synced) | /workspaces | +| Container filesystem | No | /tmp, installed packages | + +## Anti-Patterns + +- Don't install packages with `apt` (use devcontainer features) +- Don't modify `.devcontainer/defaults/` (use `.codeforge/` overrides) +- Don't assume unlimited memory (6 GB, no swap) +- Don't write outside the project directory (scope guard blocks it) +- Don't hardcode tool versions (they update on rebuild) diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index 1a70ae8..a9daa5b 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -29,6 +29,19 @@ - **Enable shfmt, dprint, shellcheck, hadolint** — previously disabled (`"version": "none"`), now set to `"latest"`. Provides shell formatting, markdown/TOML/Dockerfile formatting, shell linting, and Dockerfile linting out of the box. +### Documentation + +- **Add AI-CONTEXT.md** — machine-readable environment reference for AI assistants. Covers toolchain, filesystem, constraints, auth, and persistence in ~700 tokens. Referenced from AGENTS.md with user guidance in README.md. + +### Skill Engine + +- **Add `/codeforge` skill** — on-demand deep container context (toolchain inventory, filesystem map, safety constraints). Complements the static AI-CONTEXT.md with detailed reference files. +- **Remove skill auto-suggestion** — removed `skill-suggester.py` and the `UserPromptSubmit` hook. Skills are now loaded on demand via `/skill` only. The auto-suggestion system had only 2 active matchers and added latency to every prompt. + +### Removed + +- **Remove Codex AGENTS.md** — removed the single-line `@AGENTS.md` self-referencing file from packaged defaults and file-manifest. Codex config.toml is unaffected. + ### Secrets & Configuration - **Docker Compose secrets** — secrets now use Docker Compose file-based secrets mounted at `/run/secrets/`. Place secret files in `.codeforge/secrets/` (one file per secret, raw value only). The `generate-compose.mjs` init script auto-discovers secrets and generates the compose override. Supported secrets: `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. Env vars (Codespaces) remain supported as a fallback. diff --git a/container/.devcontainer/README.md b/container/.devcontainer/README.md index 898042f..ba88f31 100755 --- a/container/.devcontainer/README.md +++ b/container/.devcontainer/README.md @@ -406,11 +406,11 @@ Agent definitions in `plugins/devs-marketplace/plugins/agent-system/agents/` pro 15 previously active agents have been archived to `agents/_archived/`. -### General Skills (23) — `skill-engine` plugin +### General Skills (24) — `skill-engine` plugin Skills in `plugins/devs-marketplace/plugins/skill-engine/skills/` provide domain-specific coding references: -`agent-browser` · `api-design` · `ast-grep-patterns` · `claude-agent-sdk` · `claude-code-headless` · `debugging` · `dependency-management` · `docker` · `docker-py` · `documentation-patterns` · `fastapi` · `git-forensics` · `migration-patterns` · `performance-profiling` · `pydantic-ai` · `refactoring-patterns` · `security-checklist` · `skill-building` · `sqlite` · `svelte5` · `team` · `testing` · `worktree` +`agent-browser` · `api-design` · `ast-grep-patterns` · `claude-agent-sdk` · `claude-code-headless` · `codeforge` · `debugging` · `dependency-management` · `docker` · `docker-py` · `documentation-patterns` · `fastapi` · `git-forensics` · `migration-patterns` · `performance-profiling` · `pydantic-ai` · `refactoring-patterns` · `security-checklist` · `skill-building` · `sqlite` · `svelte5` · `team` · `testing` · `worktree` ### Spec Skills (3) — `spec-workflow` plugin @@ -418,6 +418,21 @@ Skills in `plugins/devs-marketplace/plugins/spec-workflow/skills/`: `spec` · `build` · `specs` +### AI Environment Context + +CodeForge ships machine-readable environment documentation for AI assistants. +Reference it from your project's `AGENTS.md` or `CLAUDE.md`: + +``` +@.devcontainer/AI-CONTEXT.md +``` + +This gives your AI knowledge of the container's toolchain, filesystem layout, +safety constraints, and resource limits — preventing common errors like +running blocked commands or writing outside the project directory. + +For deeper context on demand, use the `/codeforge` skill in Claude Code. + ## Specification Workflow CodeForge includes a specification-driven development workflow using directory-based "spec packages." Every non-trivial feature gets a spec package before implementation begins. diff --git a/container/.devcontainer/defaults/codeforge/codex/AGENTS.md b/container/.devcontainer/defaults/codeforge/codex/AGENTS.md deleted file mode 100644 index 1a523b5..0000000 --- a/container/.devcontainer/defaults/codeforge/codex/AGENTS.md +++ /dev/null @@ -1,37 +0,0 @@ -# RTK — Token Compression - -RTK (`rtk`) is installed in this environment. It compresses command output to save context window tokens. - -## Instruction - -**Always prefix shell commands with `rtk` when running supported commands.** - -Examples: -- `rtk git status` instead of `git status` -- `rtk npm test` instead of `npm test` -- `rtk cargo build` instead of `cargo build` -- `rtk ls -la` instead of `ls -la` - -## Supported Commands - -git, npm, npx, yarn, pnpm, bun, bunx, cargo, rustup, pip, uv, uvx, -python, node, deno, go, docker, kubectl, helm, terraform, -aws, gcloud, az, make, cmake, gradle, mvn, -cat, ls, find, grep, rg, fd, tree, file, wc, du, df, -curl, wget, jq, yq, sed, awk, cut, sort, uniq, head, tail, -ps, top, free, uptime, env, printenv, -tar, zip, unzip, diff, patch, -pytest, jest, vitest, mocha, -eslint, prettier, biome, ruff, mypy, pyright, tsc, gh - -## When NOT to Use RTK - -- Interactive commands (vim, nano, less) -- Commands where you need exact byte-level output -- Piped commands where RTK would interfere with downstream parsing - -## Meta-Commands - -- `rtk gain` — show token savings -- `rtk discover` — list supported commands -- `rtk status` — version and config diff --git a/container/.devcontainer/defaults/codeforge/file-manifest.json b/container/.devcontainer/defaults/codeforge/file-manifest.json index 48283aa..b5f7208 100755 --- a/container/.devcontainer/defaults/codeforge/file-manifest.json +++ b/container/.devcontainer/defaults/codeforge/file-manifest.json @@ -212,13 +212,5 @@ "destFilename": "config.toml", "enabled": true, "overwrite": "if-changed" - }, - { - "id": "codex.agents", - "src": "codex/AGENTS.md", - "dest": "${HOME}/.codex", - "destFilename": "AGENTS.md", - "enabled": true, - "overwrite": "if-changed" } ] diff --git a/container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json b/container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json index 45cb78f..d2b6d9a 100644 --- a/container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +++ b/container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json @@ -60,11 +60,11 @@ }, { "name": "skill-engine", - "description": "2 coding knowledge packs with auto-suggestion (21 archived for rewrite)", + "description": "Coding knowledge packs loaded on demand via /skill", "version": "1.0.0", "source": "./plugins/skill-engine", "category": "development", - "keywords": ["skills", "knowledge", "auto-suggestion"] + "keywords": ["skills", "knowledge"] }, { "name": "spec-workflow", diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json index 72e800e..a4cc9ce 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json @@ -1,7 +1,5 @@ { "name": "skill-engine", - "description": "2 coding knowledge packs with auto-suggestion (21 archived for rewrite)", - "author": { - "name": "AnExiledDev" - } + "description": "Coding knowledge packs loaded on demand via /skill", + "author": { "name": "AnExiledDev" } } diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md index 8faeed9..ac3eaf8 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md @@ -1,14 +1,10 @@ # skill-engine -Claude Code plugin that provides 23 coding knowledge packs (skills) with automatic suggestion based on user prompts. Each skill contains domain-specific instructions and reference material that Claude loads on demand via the `/skill` command. +Claude Code plugin that provides 24 coding knowledge packs (skills). Each skill contains domain-specific instructions and reference material that Claude loads on demand via the `/skill` command. ## What It Does -Two capabilities: - -1. **Skill library** — 23 skills covering frameworks, tools, and development patterns. Each skill is a structured knowledge pack with a `SKILL.md` entrypoint and `references/` subdirectory containing detailed reference docs. - -2. **Auto-suggestion** — A `UserPromptSubmit` hook watches user prompts for keyword matches and suggests relevant skills as context, so Claude can proactively load the right knowledge. +A skill library of 24 skills covering frameworks, tools, and development patterns. Each skill is a structured knowledge pack with a `SKILL.md` entrypoint and `references/` subdirectory containing detailed reference docs. ### Skill Catalog @@ -19,6 +15,7 @@ Two capabilities: | ast-grep-patterns | Semantic code search patterns by language | | claude-agent-sdk | Building custom agents with the Agent SDK (TypeScript) | | claude-code-headless | CLI flags, output parsing, SDK and MCP integration | +| codeforge | Container environment context: toolchain, filesystem, constraints | | debugging | Error patterns, log locations, diagnosis procedures | | dependency-management | Package managers, ecosystem commands, license compliance | | docker | Dockerfile patterns, docker-compose services | @@ -38,36 +35,8 @@ Two capabilities: | testing | Testing frameworks, FastAPI testing, Svelte testing | | worktree | Git worktree lifecycle, EnterWorktree, parallel development | -### Auto-Suggestion - -The `skill-suggester.py` hook scores user prompts against keyword maps for each skill using weighted matching. Suggestions are ranked by confidence and capped at **3 skills maximum** per prompt. - -Each skill defines: -- **Phrases** — `(substring, weight)` tuples. Weight 0.0–1.0 reflects specificity (e.g., `("build a fastapi app", 1.0)` vs `("pydantic model", 0.3)`) -- **Terms** — Whole-word regex patterns, all scored at 0.6 -- **Negative patterns** — Substrings that instantly disqualify a skill (e.g., `"pydanticai"` suppresses `fastapi`) -- **Context guards** — Required co-occurring words for low-confidence matches. When the best score is below 0.6, at least one guard word must appear in the prompt or the match is dropped -- **Priority** — Integer tie-breaker (10 = commands, 7 = tech, 5 = patterns, 3 = generic) - ## How It Works -### Hook Lifecycle - -``` -User submits a prompt - | - +-> UserPromptSubmit fires - | - +-> skill-suggester.py - | - +-> Check negative patterns (instant disqualify) - +-> Score phrases (best weight) and terms (0.6) - +-> Enforce context guards on low-confidence matches - +-> Rank by score desc, priority desc - +-> Return top 3 as additionalContext - +-> No matches above threshold? -> Silent (no output) -``` - ### Skill Structure Each skill follows a standard layout: @@ -83,20 +52,6 @@ skills/ Skills are loaded via Claude Code's `/skill` slash command (e.g., `/skill fastapi`). The `SKILL.md` file is the primary document Claude reads; references are loaded as needed for deeper detail. -### Exit Code Behavior - -| Exit Code | Meaning | -|-----------|---------| -| 0 | Suggestion injected (or no match — silent) | - -The hook never blocks operations. - -### Timeouts - -| Hook | Timeout | -|------|---------| -| Skill suggestion (UserPromptSubmit) | 3s | - ## Installation ### CodeForge DevContainer @@ -132,15 +87,14 @@ skill-engine/ +-- .claude-plugin/ | +-- plugin.json # Plugin metadata +-- hooks/ -| +-- hooks.json # UserPromptSubmit hook registration -+-- scripts/ -| +-- skill-suggester.py # Weighted scoring skill auto-suggestion +| +-- hooks.json # Hook registration (empty — no active hooks) +-- skills/ -| +-- agent-browser/ # 23 skill directories +| +-- agent-browser/ # 24 skill directories | +-- api-design/ | +-- ast-grep-patterns/ | +-- claude-agent-sdk/ | +-- claude-code-headless/ +| +-- codeforge/ | +-- debugging/ | +-- dependency-management/ | +-- docker/ @@ -164,5 +118,4 @@ skill-engine/ ## Requirements -- Python 3.11+ - Claude Code with plugin hook support (skills) diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json index 28a5151..d1d194e 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json @@ -1,16 +1,4 @@ { - "description": "Skill auto-suggestion on user prompts", - "hooks": { - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/skill-suggester.py", - "timeout": 3 - } - ] - } - ] - } + "description": "Coding knowledge packs loaded on demand via /skill", + "hooks": {} } diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py deleted file mode 100644 index a2507ba..0000000 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python3 -"""Skill suggester hook for UserPromptSubmit and SubagentStart events. - -Detects which hook event called it via input JSON shape: -- UserPromptSubmit: {"prompt": "..."} -> {"additionalContext": "..."} -- SubagentStart: {"subagent_type": "Plan", "prompt": "..."} -> {"additionalContext": "..."} - -Uses weighted scoring with negative patterns and context guards to suggest -the most relevant skills. Returns at most MAX_SKILLS suggestions, ranked -by confidence score. -""" - -import json -import re -import sys -import os - -# Hook gate — check ~/.claude/disabled-hooks.json -_dh = os.path.join(os.path.expanduser("~"), ".claude", "disabled-hooks.json") -if os.path.exists(_dh): - with open(_dh) as _f: - if os.path.basename(__file__).replace(".py", "") in json.load(_f).get( - "disabled", [] - ): - sys.exit(0) - -# Maximum number of skills to suggest per prompt. -MAX_SKILLS = 3 - -# Minimum score for a match to appear in final results. Set to 0 so that -# even low-weight phrases can survive if they pass context guard checks. -# The context guard + MAX_SKILLS cap handle quality control instead. -MIN_SCORE = 0.0 - -# Fixed score assigned to whole-word term matches (regex \b...\b). -TERM_WEIGHT = 0.6 - -# Threshold below which context guards are enforced. Matches scoring below -# this value must have at least one context guard word present in the prompt, -# otherwise the match is discarded as low-confidence. -CONTEXT_GUARD_THRESHOLD = 0.6 - -# --------------------------------------------------------------------------- -# Skill definitions -# -# Each skill has: -# phrases — list of (substring, weight) tuples. Weight 0.0-1.0 -# reflects how confidently the phrase indicates the skill. -# terms — list of whole-word regex terms (case-insensitive). -# All term matches receive TERM_WEIGHT. -# negative — (optional) list of substrings that instantly disqualify -# the skill, even if phrases/terms matched. -# context_guards — (optional) list of substrings. When the best match -# score is below CONTEXT_GUARD_THRESHOLD, at least one -# guard must be present in the prompt or the match is -# dropped. -# priority — integer tie-breaker. Higher = preferred when scores -# are equal. 10 = explicit commands, 7 = technology, -# 5 = practice/pattern, 3 = meta/generic. -# --------------------------------------------------------------------------- - -SKILLS: dict[str, dict] = { - # ------------------------------------------------------------------ - # Technology skills (priority 7) - # ------------------------------------------------------------------ - "agent-browser": { - "phrases": [ - ("agent-browser", 1.0), - ("agent browser", 1.0), - ("headless browser", 0.8), - ("browser automation", 0.9), - ("open a webpage", 0.7), - ("navigate a site", 0.7), - ("take a screenshot of a page", 0.8), - ("fill a form on a website", 0.8), - ("accessibility tree", 0.8), - ("scrape a page", 0.5), - ("interact with a website", 0.5), - ("automate browser", 0.9), - ], - "terms": ["agent-browser", "agent_browser"], - "negative": ["playwright test", "cypress", "puppeteer"], - "context_guards": [ - "browser", - "webpage", - "website", - "page", - "url", - "screenshot", - "headless", - "navigate", - "form", - ], - "priority": 7, - }, - # ------------------------------------------------------------------ - # Practice / pattern skills (priority 5) - # ------------------------------------------------------------------ - "team": { - "phrases": [ - ("spawn a team", 1.0), - ("create a team", 0.8), - ("team of agents", 0.9), - ("use a swarm", 0.8), - ("work in parallel", 0.4), - ("coordinate multiple agents", 0.9), - ("split this across agents", 0.9), - ("team up", 0.5), - ], - "terms": ["TeamCreate", "SendMessage"], - # Note: "parallel", "swarm", "coordinate", "team" omitted — overlap phrases - "context_guards": ["agent", "agents", "teammate", "teammates"], - "priority": 5, - }, -} - -# --------------------------------------------------------------------------- -# Pre-compile term patterns for whole-word matching -# --------------------------------------------------------------------------- - -_TERM_PATTERNS: dict[str, re.Pattern[str]] = {} -for _skill, _cfg in SKILLS.items(): - for _term in _cfg["terms"]: - if _term not in _TERM_PATTERNS: - _TERM_PATTERNS[_term] = re.compile( - r"\b" + re.escape(_term) + r"\b", re.IGNORECASE - ) - - -# --------------------------------------------------------------------------- -# Scoring engine -# --------------------------------------------------------------------------- - - -def _score_skill(cfg: dict, prompt: str, lowered: str) -> float: - """Return a confidence score for how well *prompt* matches *cfg*. - - Returns 0.0 when the skill should not be suggested. - """ - - # 1. Negative patterns — instant disqualification - for neg in cfg.get("negative", []): - if neg in lowered: - return 0.0 - - # 2. Phrase scoring — take the highest matching weight - best_phrase: float = 0.0 - for phrase, weight in cfg["phrases"]: - if phrase in lowered: - if weight > best_phrase: - best_phrase = weight - - # 3. Term scoring — fixed weight for any whole-word match - term_score: float = 0.0 - for term in cfg["terms"]: - if _TERM_PATTERNS[term].search(prompt): - term_score = TERM_WEIGHT - break - - base = max(best_phrase, term_score) - if base < MIN_SCORE: - return 0.0 - - # 4. Context guard — low-confidence matches need confirmation - if base < CONTEXT_GUARD_THRESHOLD: - guards = cfg.get("context_guards") - if guards and not any(g in lowered for g in guards): - return 0.0 - - return base - - -def match_skills(prompt: str) -> list[str]: - """Return up to MAX_SKILLS skill names, ranked by confidence score.""" - lowered = prompt.lower() - scored: list[tuple[str, float, int]] = [] - - for skill, cfg in SKILLS.items(): - score = _score_skill(cfg, prompt, lowered) - if score > 0.0: - scored.append((skill, score, cfg.get("priority", 0))) - - # Sort by score descending, then priority descending for ties - scored.sort(key=lambda x: (x[1], x[2]), reverse=True) - - return [name for name, _, _ in scored[:MAX_SKILLS]] - - -# --------------------------------------------------------------------------- -# Hook entry point -# --------------------------------------------------------------------------- - - -def main() -> None: - """Read a hook event from stdin, score skills, and print suggestions to stdout.""" - raw = sys.stdin.read().strip() - if not raw: - return - - try: - data = json.loads(raw) - except json.JSONDecodeError: - return - - prompt = data.get("prompt", "") - if not prompt: - return - - skills = match_skills(prompt) - if not skills: - return - - skill_list = ", ".join(f'"{s}"' for s in skills) - - output = { - "hookSpecificOutput": { - "hookEventName": "UserPromptSubmit", - "additionalContext": ( - f"MANDATORY — Skill activation required. The user's prompt matches: {skill_list}. " - f"Before responding, evaluate each matched skill: is it relevant to this specific request? " - f"For each relevant skill, activate it using the Skill tool NOW. " - f"Skip any that are not relevant to the user's actual intent. " - f"Do not proceed with implementation until relevant skills are loaded." - ), - } - } - - json.dump(output, sys.stdout) - - -if __name__ == "__main__": - main() diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/SKILL.md new file mode 100644 index 0000000..2c1c113 --- /dev/null +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/SKILL.md @@ -0,0 +1,45 @@ +--- +name: codeforge +description: >- + CodeForge devcontainer deep context: toolchain inventory, filesystem map, + safety constraints, and resource limits. Use when an AI assistant needs + to understand the container environment — what's installed, where things + live, what's blocked, and what persists. +version: 0.1.0 +allowed-tools: Bash, Read +argument-hint: "[toolchain | filesystem | constraints | all]" +effort: low +--- + +# CodeForge Container Context + +On-demand deep reference for the CodeForge devcontainer environment. +Complements the static `AI-CONTEXT.md` with full detail. + +## Quick Environment Summary + +- Debian-based container, `vscode` user, bash shell, 6 GB RAM, no swap +- Workspace at `/workspaces/`, projects at `/workspaces/projects//` +- Config: `.codeforge/` overrides → `.devcontainer/defaults/codeforge/` defaults +- Named Docker volumes persist `~/.claude/`, `~/.config/gh/`, caches across rebuilds +- Safety plugins block destructive commands, protect sensitive files, enforce project scope +- Tools: Python 3, Node.js, Go, Rust, Bun, gh, docker, ast-grep, tree-sitter, biome, ruff + +--- + +## Reference Files + +| File | Description | +|------|-------------| +| `references/toolchain.md` | Full tool inventory by category with binary paths and preferred-tool guidance | +| `references/filesystem.md` | Directory tree, volume mounts, config resolution, file-manifest system | +| `references/constraints.md` | Safety plugins, resource limits, auth model, container lifecycle | + +--- + +## Ambiguity Policy + +- No argument or `all` → load all three reference files +- `toolchain` → load `references/toolchain.md` only +- `filesystem` → load `references/filesystem.md` only +- `constraints` → load `references/constraints.md` only diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md new file mode 100644 index 0000000..70e0749 --- /dev/null +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md @@ -0,0 +1,97 @@ +# Constraints Reference + +## Safety Plugins + +### dangerous-command-blocker + +Blocks destructive bash commands via PreToolUse hook. + +**Blocked patterns include:** +- `rm -rf /`, `sudo rm`, `rm -rf *` +- `chmod 777` +- `git push --force` to main/master +- `mkfs`, `dd if=`, `:(){ :|:& };:` +- Other destructive system commands + +**Behavior:** Hook returns `{"decision": "block"}` — command is rejected before execution. + +### protected-files-guard + +Blocks edits to sensitive files via PreToolUse hook. + +**Protected patterns:** +- `.git/` directory contents +- Lock files (`package-lock.json`, `bun.lock`, `yarn.lock`, `poetry.lock`, `Cargo.lock`, `uv.lock`) +- Credential files (`.env`, `secrets/`, `credentials`) +- Generated settings (`~/.claude/settings*.json`) + +**Behavior:** Edit/Write tool calls targeting protected files are blocked. + +### workspace-scope-guard + +Confines file operations to the current project directory. + +**Behavior:** +- Write/Edit operations outside the project directory are blocked +- Read operations outside the project directory generate warnings +- Bash commands are annotated with working directory context + +## Resource Limits + +| Resource | Limit | Notes | +|----------|-------|-------| +| Memory | 6 GB | Hard limit set in docker-compose.yml | +| Swap | 0 | No swap available | +| Disk | Host filesystem | Bound by host disk space | + +**Implications:** +- Avoid loading large files entirely into memory +- Avoid running multiple memory-intensive processes simultaneously +- Language server processes (Pyright, TSServer) consume significant memory +- Large `npm install` or `cargo build` can hit the memory ceiling + +## Authentication Model + +### Secrets + +Secrets are individual files in `.codeforge/secrets/` (gitignored). Docker Compose mounts them at `/run/secrets/`. + +**Resolution order:** +1. Environment variables (e.g., Codespaces secrets) +2. Docker secrets (`.codeforge/secrets/` files) +3. Interactive login (e.g., `gh auth login`) + +**Supported secrets:** `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key` + +### Known Auth Caveat + +`CLAUDE_CODE_OAUTH_TOKEN` does not work when `ANTHROPIC_API_KEY` is also set. Use one or the other. + +## Container Lifecycle + +### Rebuild + +- Named volumes survive (credentials, config, caches) +- Container filesystem is replaced (installed packages, /tmp) +- Config re-deploys from manifest on start +- Features re-install from devcontainer.json + +### Restart + +- Everything survives (volumes + container filesystem) +- Config re-deploys from manifest +- Background services restart (claude-mem-worker, karma, etc.) + +### What to Expect + +- `apt install` packages are lost on rebuild — use devcontainer features instead +- Manual changes to `~/.claude/settings*.json` are overwritten on restart (use `.codeforge/` overrides) +- Manual changes to `~/.claude/rules/` are overwritten on restart (use manifest overrides) + +## Known Limitations + +- **No GUI**: Headless container — use `agent-browser` for web interaction +- **Docker-in-Docker**: Docker available but runs inside the container +- **WSL networking**: In WSL mirrored mode, Docker ports bind to all interfaces via `127.0.0.1` mapping in docker-compose.yml +- **tmux required for teams**: Agent team split panes only work inside tmux sessions +- **RTK is transparent**: Command output is automatically compressed by RTK — the AI sees compressed output, not raw. Use `command ` to bypass if raw output is needed. diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/filesystem.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/filesystem.md new file mode 100644 index 0000000..f18b160 --- /dev/null +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/filesystem.md @@ -0,0 +1,83 @@ +# Filesystem Reference + +## Directory Tree + +``` +/workspaces/ # Bind mount from host +└── projects/ + └── / # Working project directory + ├── .codeforge/ # Project-level overrides (user-owned) + │ ├── container.json # Setup flags, identity, timezone, version lock + │ ├── secrets/ # Docker Compose secrets (gitignored) + │ ├── mounts.json # Volume mount configuration + │ └── file-manifest.json # Override manifest entries by id + └── .devcontainer/ # Container definition (repo-owned) + ├── devcontainer.json # Features, compose config + ├── docker-compose.yml # Image, volumes, resource limits + ├── defaults/codeforge/ # Packaged config defaults + │ ├── file-manifest.json + │ ├── claude/ # Claude Code configs + │ │ ├── settings/ # Generated settings profiles + │ │ ├── system-prompts/ + │ │ ├── rules/ + │ │ ├── hooks/ + │ │ ├── statusline/ + │ │ └── router/ + │ ├── codex/ # Codex CLI config + │ └── rtk/ # RTK config + ├── plugins/ # Claude Code plugins + ├── scripts/ # Setup and lifecycle scripts + └── features/ # Devcontainer features + +~/.claude/ # Claude Code home (named volume) +├── settings.json # Symlink to active profile +├── settings-*.json # Model/context profiles +├── rules/ # Auto-loaded rules (.md files) +├── hooks/ # Hook scripts +├── projects/ # Session data per project +└── disabled-hooks.json # Disable specific hooks by name + +~/.config/gh/ # GitHub CLI credentials (named volume) +~/.codex/ # Codex CLI config (named volume) +~/.claude-mem/ # Claude-Mem data (named volume) +~/.cache/ # Package manager caches (named volume) +``` + +## Volume Mounts + +| Mount | Type | Survives Rebuild | Purpose | +|-------|------|-----------------|---------| +| `/workspaces` | Bind mount | Yes (host) | Source code, project configs | +| `~/.claude` | Named volume | Yes | Claude Code config, sessions, credentials | +| `~/.config/gh` | Named volume | Yes | GitHub CLI auth state | +| `~/.bun/install/cache` | Named volume | Yes | Bun package cache | +| `~/.cache` | Named volume | Yes | npm, pip, uv caches | +| `~/.codex` | Named volume | Yes | Codex CLI config | +| `~/.claude-mem` | Named volume | Yes | Claude-Mem persistent memory | + +## Config Resolution Order + +1. **Project overrides** — `.codeforge/` (user-created, gitignored) +2. **Generated configs** — `.devcontainer/.generated/codeforge/` (auto-generated from defaults + profiles) +3. **Packaged defaults** — `.devcontainer/defaults/codeforge/` (repo-owned, version-controlled) + +Config deployment happens via `file-manifest.json` on every container start. Each entry has: +- `id` — stable identifier (used by override manifests) +- `src` — source path relative to defaults directory +- `dest` — deployment target (supports `${HOME}`, `${WORKSPACE_ROOT}`, `${CODEFORGE_DIR}`) +- `overwrite` — `"if-changed"` (sha256), `"always"`, or `"never"` + +## File-Manifest System + +The manifest controls which config files deploy and when. Override entries by matching `id` in `.codeforge/file-manifest.json`. Set `"enabled": false` to suppress a default, or change `"src"` to deploy a different file. + +## Writable vs Read-Only + +| Location | Writable | Notes | +|----------|----------|-------| +| Project directory | Yes | Scope-guarded to current project | +| `.codeforge/` | Yes | User overrides | +| `/tmp/` | Yes | Ephemeral | +| `.devcontainer/defaults/` | Avoid | Use `.codeforge/` overrides instead | +| `~/.claude/settings*.json` | Avoid | Managed by manifest; changes overwritten on restart | +| `~/.claude/rules/` | Avoid | Managed by manifest | diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/toolchain.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/toolchain.md new file mode 100644 index 0000000..31f4d01 --- /dev/null +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/toolchain.md @@ -0,0 +1,85 @@ +# Toolchain Reference + +## Languages & Runtimes + +| Tool | Binary | Purpose | +|------|--------|---------| +| Python 3 | `python3` | General-purpose scripting, AI/ML | +| Node.js (LTS) | `node` | JavaScript runtime | +| Go | `go` | Compiled systems language | +| Rust | `rustc`, `cargo` | Systems programming | +| Bun | `bun` | Fast JS/TS runtime, package manager, test runner | + +## Package Managers + +| Tool | Binary | Purpose | +|------|--------|---------| +| npm | `npm` | Node.js packages | +| pip | `pip` | Python packages (prefer uv) | +| uv | `uv` | Fast Python package manager and resolver | +| bun | `bun install` | Bun/Node packages | +| cargo | `cargo` | Rust crates | + +## Dev Tools + +| Tool | Binary | Purpose | +|------|--------|---------| +| gh | `gh` | GitHub CLI (issues, PRs, repos, auth) | +| docker | `docker` | Container management (Docker-in-Docker) | +| git | `git` | Version control | +| jq | `jq` | JSON processing | +| curl | `curl` | HTTP requests | +| tmux | `tmux` | Terminal multiplexer (required for agent teams) | + +## Formatters + +| Tool | Binary | Formats | +|------|--------|---------| +| biome | `biome` | JS, TS, JSON, CSS | +| ruff | `ruff format` | Python | +| shfmt | `shfmt` | Shell scripts | +| dprint | `dprint` | Markdown, TOML, Dockerfile | + +## Linters + +| Tool | Binary | Lints | +|------|--------|-------| +| biome | `biome lint` | JS, TS | +| ruff | `ruff check` | Python | +| shellcheck | `shellcheck` | Shell scripts | +| hadolint | `hadolint` | Dockerfiles | + +## Code Intelligence + +| Tool | Binary | Purpose | +|------|--------|---------| +| ast-grep | `sg` | Structural code search and transform (syntax-aware) | +| tree-sitter | `tree-sitter` | Parse trees, syntax extraction | +| Pyright | `pyright` | Python type checking | + +## AI Tools + +| Tool | Binary | Purpose | +|------|--------|---------| +| Claude Code | `claude` | AI coding assistant (primary) | +| codeforge | `codeforge` | Container management CLI (doctor, config, sessions) | +| Codex | `codex` | OpenAI Codex CLI agent | +| Hermes | `hermes` | Nous Research agent CLI | +| agent-browser | `agent-browser` | Headless Chromium (Playwright-based) | +| RTK | `rtk` | Token compression proxy (transparent, auto-applied) | + +## Preferred Tool for Common Tasks + +| Task | Use | Not | +|------|-----|-----| +| Structural code search | `sg` (ast-grep) | regex grep | +| Python formatting | `ruff format` | black, autopep8 | +| Python linting | `ruff check` | pylint, flake8 | +| JS/TS formatting | `biome format` | prettier | +| JS/TS linting | `biome lint` | eslint | +| Shell formatting | `shfmt` | manual | +| Shell linting | `shellcheck` | manual | +| Dockerfile linting | `hadolint` | manual | +| JSON processing | `jq` | python -c | +| Python packages | `uv` | pip (fallback) | +| HTTP requests | `curl` | wget | diff --git a/container/test.js b/container/test.js index 881b79c..f1fcba0 100755 --- a/container/test.js +++ b/container/test.js @@ -95,7 +95,6 @@ const requiredFiles = [ ".devcontainer/defaults/codeforge/claude/statusline/settings.json", ".devcontainer/defaults/codeforge/claude/router/config.json", ".devcontainer/defaults/codeforge/codex/config.toml", - ".devcontainer/defaults/codeforge/codex/AGENTS.md", ".devcontainer/defaults/codeforge/rtk/config.toml", ".devcontainer/features/oh-my-claude/devcontainer-feature.json", ".devcontainer/features/claude-code-karma/devcontainer-feature.json", From 828148d36e0dc516ee8e825e5471f45cfc166a33 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 06:13:48 +0000 Subject: [PATCH 21/27] feat(cli): add hook management CLI and config apply merge Add `codeforge hooks list|disable|enable|status` subcommands for managing plugin hook enablement via ~/.claude/disabled-hooks.json, replacing the need to hand-edit JSON. Includes security warnings for safety-critical plugins and dual-write to both deployed and source configs. Add enabledPlugins merge logic to `codeforge config apply` and setup.js so user plugin disable/enable choices survive config redeployment. Includes hooks-loader utility, 18 unit tests, and documentation updates to AGENTS.md and constraints.md. --- cli/src/commands/config/apply.ts | 55 +++- cli/src/commands/hooks/disable.ts | 91 ++++++ cli/src/commands/hooks/enable.ts | 74 +++++ cli/src/commands/hooks/list.ts | 101 +++++++ cli/src/commands/hooks/status.ts | 65 +++++ cli/src/index.ts | 13 + cli/src/loaders/hooks-loader.ts | 113 +++++++ cli/tests/hooks-loader.test.ts | 276 ++++++++++++++++++ container/.devcontainer/AGENTS.md | 35 ++- .../codeforge/references/constraints.md | 17 ++ container/setup.js | 40 ++- 11 files changed, 870 insertions(+), 10 deletions(-) create mode 100644 cli/src/commands/hooks/disable.ts create mode 100644 cli/src/commands/hooks/enable.ts create mode 100644 cli/src/commands/hooks/list.ts create mode 100644 cli/src/commands/hooks/status.ts create mode 100644 cli/src/loaders/hooks-loader.ts create mode 100644 cli/tests/hooks-loader.test.ts diff --git a/cli/src/commands/config/apply.ts b/cli/src/commands/config/apply.ts index a4e423e..5261abd 100755 --- a/cli/src/commands/config/apply.ts +++ b/cli/src/commands/config/apply.ts @@ -1,7 +1,13 @@ import chalk from "chalk"; import { spawnSync } from "child_process"; import type { Command } from "commander"; -import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs"; +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from "fs"; import { homedir } from "os"; import { basename, dirname, resolve } from "path"; import { @@ -51,6 +57,32 @@ function filesAreIdentical(a: string, b: string): boolean { } } +function mergeSettingsForDeploy(srcPath: string, destPath: string): boolean { + try { + const srcContent = readFileSync(srcPath, "utf-8"); + const destContent = readFileSync(destPath, "utf-8"); + const srcSettings = JSON.parse(srcContent); + const destSettings = JSON.parse(destContent); + + if (!destSettings.enabledPlugins) return false; + + // Start with source, overlay user's false values for enabledPlugins + const merged = { ...srcSettings }; + if (!merged.enabledPlugins) merged.enabledPlugins = {}; + + for (const [key, value] of Object.entries(destSettings.enabledPlugins)) { + if (value === false) { + merged.enabledPlugins[key] = false; + } + } + + writeFileSync(destPath, JSON.stringify(merged, null, 2) + "\n"); + return true; + } catch { + return false; + } +} + export function registerConfigApplyCommand(parent: Command): void { parent .command("apply") @@ -161,11 +193,22 @@ export function registerConfigApplyCommand(parent: Command): void { ); } else { mkdirSync(dirname(dest), { recursive: true }); - copyFileSync(src, dest); - updated++; - console.log( - ` ${chalk.green("\u2713")} ${entry.src} \u2192 ${displayDest} (updated)`, - ); + if ( + entry.id.startsWith("claude.settings") && + destExists && + mergeSettingsForDeploy(src, dest) + ) { + updated++; + console.log( + ` ${chalk.green("\u2713")} ${entry.src} \u2192 ${displayDest} (merged)`, + ); + } else { + copyFileSync(src, dest); + updated++; + console.log( + ` ${chalk.green("\u2713")} ${entry.src} \u2192 ${displayDest} (updated)`, + ); + } } } diff --git a/cli/src/commands/hooks/disable.ts b/cli/src/commands/hooks/disable.ts new file mode 100644 index 0000000..11309b4 --- /dev/null +++ b/cli/src/commands/hooks/disable.ts @@ -0,0 +1,91 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { + loadDisabledHooks, + resolveHookNames, + writeDisabledHooks, + writeDisabledHooksSource, +} from "../../loaders/hooks-loader.js"; +import { loadInstalledPlugins } from "../../loaders/plugin-loader.js"; + +const SECURITY_PLUGINS = new Set([ + "workspace-scope-guard", + "dangerous-command-blocker", + "protected-files-guard", +]); + +interface HooksDisableOptions { + color?: boolean; +} + +export function registerHooksDisableCommand(parent: Command): void { + parent + .command("disable ") + .description("Disable a hook by script or plugin name") + .option("--no-color", "Disable colored output") + .action(async (name: string, options: HooksDisableOptions) => { + try { + if (!options.color) { + chalk.level = 0; + } + + const plugins = await loadInstalledPlugins(); + const scripts = resolveHookNames(name, plugins); + + if (scripts.length === 0) { + console.error(`Unknown hook or plugin: ${name}`); + const allPlugins = plugins.map((p) => p.name).join(", "); + console.error(`Available plugins: ${allPlugins}`); + process.exit(1); + } + + // Security warning for safety-critical plugins + const plugin = plugins.find( + (p) => p.name === name || p.qualifiedName === name, + ); + if (plugin && SECURITY_PLUGINS.has(plugin.name)) { + console.log( + chalk.yellow( + "⚠ Warning: This is a security hook. Disabling it removes a safety boundary.", + ), + ); + } + + const disabled = await loadDisabledHooks(); + const added: string[] = []; + + for (const script of scripts) { + if (!disabled.includes(script)) { + disabled.push(script); + added.push(script); + } + } + + if (added.length === 0) { + console.log( + chalk.yellow("Already disabled:"), + scripts.join(", "), + ); + return; + } + + await writeDisabledHooks(disabled); + const sourcePath = await writeDisabledHooksSource(disabled); + + console.log( + `${chalk.red("✓")} Disabled ${added.length} hook${added.length > 1 ? "s" : ""}:`, + ); + for (const script of added) { + console.log(` - ${script}`); + } + console.log(" Updated: ~/.claude/disabled-hooks.json"); + if (sourcePath) { + console.log(` Updated: ${sourcePath}`); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } + }); +} diff --git a/cli/src/commands/hooks/enable.ts b/cli/src/commands/hooks/enable.ts new file mode 100644 index 0000000..71a88cc --- /dev/null +++ b/cli/src/commands/hooks/enable.ts @@ -0,0 +1,74 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { + loadDisabledHooks, + resolveHookNames, + writeDisabledHooks, + writeDisabledHooksSource, +} from "../../loaders/hooks-loader.js"; +import { loadInstalledPlugins } from "../../loaders/plugin-loader.js"; + +interface HooksEnableOptions { + color?: boolean; +} + +export function registerHooksEnableCommand(parent: Command): void { + parent + .command("enable ") + .description("Re-enable a disabled hook by script or plugin name") + .option("--no-color", "Disable colored output") + .action(async (name: string, options: HooksEnableOptions) => { + try { + if (!options.color) { + chalk.level = 0; + } + + const plugins = await loadInstalledPlugins(); + const scripts = resolveHookNames(name, plugins); + + if (scripts.length === 0) { + console.error(`Unknown hook or plugin: ${name}`); + const allPlugins = plugins.map((p) => p.name).join(", "); + console.error(`Available plugins: ${allPlugins}`); + process.exit(1); + } + + const disabled = await loadDisabledHooks(); + const toRemove = new Set(scripts); + const removed: string[] = []; + const updated = disabled.filter((s) => { + if (toRemove.has(s)) { + removed.push(s); + return false; + } + return true; + }); + + if (removed.length === 0) { + console.log( + chalk.yellow("Already enabled:"), + scripts.join(", "), + ); + return; + } + + await writeDisabledHooks(updated); + const sourcePath = await writeDisabledHooksSource(updated); + + console.log( + `${chalk.green("✓")} Enabled ${removed.length} hook${removed.length > 1 ? "s" : ""}:`, + ); + for (const script of removed) { + console.log(` - ${script}`); + } + console.log(" Updated: ~/.claude/disabled-hooks.json"); + if (sourcePath) { + console.log(` Updated: ${sourcePath}`); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } + }); +} diff --git a/cli/src/commands/hooks/list.ts b/cli/src/commands/hooks/list.ts new file mode 100644 index 0000000..2d7323b --- /dev/null +++ b/cli/src/commands/hooks/list.ts @@ -0,0 +1,101 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { + extractScriptName, + loadDisabledHooks, +} from "../../loaders/hooks-loader.js"; +import { loadInstalledPlugins } from "../../loaders/plugin-loader.js"; + +interface HooksListOptions { + format: string; + color?: boolean; +} + +export function registerHooksListCommand(parent: Command): void { + parent + .command("list") + .description("List all hooks with enabled/disabled status") + .option("-f, --format ", "Output format: text|json", "text") + .option("--no-color", "Disable colored output") + .action(async (options: HooksListOptions) => { + try { + if (!options.color) { + chalk.level = 0; + } + + const plugins = await loadInstalledPlugins(); + const disabled = new Set(await loadDisabledHooks()); + + if (options.format === "json") { + const output: { + plugin: string; + event: string; + matcher: string | undefined; + timeout: number; + script: string; + enabled: boolean; + }[] = []; + + for (const plugin of plugins) { + for (const hook of plugin.hooks) { + for (const cmd of hook.commands) { + const script = extractScriptName(cmd.command); + output.push({ + plugin: plugin.name, + event: hook.event, + matcher: hook.matcher, + timeout: cmd.timeout, + script, + enabled: !disabled.has(script), + }); + } + } + } + console.log(JSON.stringify(output, null, 2)); + } else { + const lines: string[] = []; + lines.push( + chalk.bold( + ` ${"Plugin".padEnd(27)}${"Event".padEnd(19)}${"Script".padEnd(28)}Status`, + ), + ); + + let enabledCount = 0; + let disabledCount = 0; + + for (const plugin of plugins) { + for (const hook of plugin.hooks) { + for (const cmd of hook.commands) { + const script = extractScriptName(cmd.command); + const isDisabled = disabled.has(script); + if (isDisabled) { + disabledCount++; + } else { + enabledCount++; + } + const status = isDisabled + ? chalk.red("disabled") + : chalk.green("enabled"); + lines.push( + ` ${plugin.name.padEnd(27)}${hook.event.padEnd(19)}${script.padEnd(28)}${status}`, + ); + } + } + } + + const total = enabledCount + disabledCount; + lines.push(""); + lines.push( + chalk.dim( + ` ${total} hooks (${enabledCount} enabled, ${disabledCount} disabled)`, + ), + ); + console.log(lines.join("\n")); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } + }); +} diff --git a/cli/src/commands/hooks/status.ts b/cli/src/commands/hooks/status.ts new file mode 100644 index 0000000..815d4c8 --- /dev/null +++ b/cli/src/commands/hooks/status.ts @@ -0,0 +1,65 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { + extractScriptName, + loadDisabledHooks, +} from "../../loaders/hooks-loader.js"; +import { loadInstalledPlugins } from "../../loaders/plugin-loader.js"; + +interface HooksStatusOptions { + color?: boolean; +} + +export function registerHooksStatusCommand(parent: Command): void { + parent + .command("status") + .description("Show summary of hook enablement") + .option("--no-color", "Disable colored output") + .action(async (options: HooksStatusOptions) => { + try { + if (!options.color) { + chalk.level = 0; + } + + const plugins = await loadInstalledPlugins(); + const disabled = new Set(await loadDisabledHooks()); + + let total = 0; + let disabledCount = 0; + const disabledList: { plugin: string; script: string }[] = []; + + for (const plugin of plugins) { + for (const hook of plugin.hooks) { + for (const cmd of hook.commands) { + total++; + const script = extractScriptName(cmd.command); + if (disabled.has(script)) { + disabledCount++; + disabledList.push({ + plugin: plugin.name, + script, + }); + } + } + } + } + + const enabledCount = total - disabledCount; + console.log( + `Hooks: ${total} total, ${chalk.green(String(enabledCount))} enabled, ${chalk.red(String(disabledCount))} disabled`, + ); + + if (disabledList.length > 0) { + console.log(""); + console.log("Disabled hooks:"); + for (const { plugin, script } of disabledList) { + console.log(` ${chalk.red("✗")} ${script} (${plugin})`); + } + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } + }); +} diff --git a/cli/src/index.ts b/cli/src/index.ts index 7dfe79a..ea18bbb 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -24,6 +24,10 @@ import { registerPluginHooksCommand } from "./commands/plugin/hooks.js"; import { registerPluginListCommand } from "./commands/plugin/list.js"; import { registerPluginShowCommand } from "./commands/plugin/show.js"; import { registerPluginSkillsCommand } from "./commands/plugin/skills.js"; +import { registerHooksDisableCommand } from "./commands/hooks/disable.js"; +import { registerHooksEnableCommand } from "./commands/hooks/enable.js"; +import { registerHooksListCommand } from "./commands/hooks/list.js"; +import { registerHooksStatusCommand } from "./commands/hooks/status.js"; import { registerProxyCommand } from "./commands/proxy.js"; import { registerListCommand } from "./commands/session/list.js"; import { registerSearchCommand } from "./commands/session/search.js"; @@ -75,6 +79,15 @@ registerPluginHooksCommand(plugin); registerPluginAgentsCommand(plugin); registerPluginSkillsCommand(plugin); +const hooks = program + .command("hooks") + .description("Manage plugin hook enablement"); + +registerHooksListCommand(hooks); +registerHooksDisableCommand(hooks); +registerHooksEnableCommand(hooks); +registerHooksStatusCommand(hooks); + const config = program .command("config") .description("Manage Claude Code configuration"); diff --git a/cli/src/loaders/hooks-loader.ts b/cli/src/loaders/hooks-loader.ts new file mode 100644 index 0000000..8056be6 --- /dev/null +++ b/cli/src/loaders/hooks-loader.ts @@ -0,0 +1,113 @@ +import { existsSync, mkdirSync } from "fs"; +import { homedir } from "os"; +import { basename, resolve } from "path"; +import type { PluginInfo } from "../schemas/plugin.js"; + +interface DisabledHooksFile { + disabled: string[]; +} + +export function extractScriptName(command: string): string { + const parts = command.trim().split(/\s+/); + let name = parts[parts.length - 1] ?? command; + for (let i = parts.length - 1; i >= 0; i--) { + if (parts[i].includes("/")) { + name = basename(parts[i]); + break; + } + } + // Strip .py extension to match disabled-hooks.json convention + return name.replace(/\.py$/, ""); +} + +export async function loadDisabledHooks( + filePath?: string, +): Promise { + const target = filePath ?? resolve(homedir(), ".claude/disabled-hooks.json"); + try { + const data: DisabledHooksFile = await Bun.file(target).json(); + return Array.isArray(data.disabled) ? data.disabled : []; + } catch { + return []; + } +} + +export async function writeDisabledHooks( + disabled: string[], + filePath?: string, +): Promise { + const target = filePath ?? resolve(homedir(), ".claude/disabled-hooks.json"); + const dir = resolve(target, ".."); + mkdirSync(dir, { recursive: true }); + const sorted = [...new Set(disabled)].sort(); + await Bun.write( + target, + JSON.stringify({ disabled: sorted }, null, 2) + "\n", + ); +} + +export function findDisabledHooksSource(): string | null { + let dir = process.cwd(); + while (true) { + for (const candidate of [ + resolve(dir, ".codeforge/claude/disabled-hooks.json"), + resolve( + dir, + ".devcontainer/defaults/codeforge/claude/disabled-hooks.json", + ), + ]) { + if (existsSync(candidate)) { + return candidate; + } + } + const parent = resolve(dir, ".."); + if (parent === dir) break; + dir = parent; + } + return null; +} + +export async function writeDisabledHooksSource( + disabled: string[], +): Promise { + const source = findDisabledHooksSource(); + if (!source) return null; + const sorted = [...new Set(disabled)].sort(); + await Bun.write( + source, + JSON.stringify({ disabled: sorted }, null, 2) + "\n", + ); + return source; +} + +export function resolveHookNames( + input: string, + plugins: PluginInfo[], +): string[] { + // Check if input matches a plugin name + const plugin = plugins.find( + (p) => p.name === input || p.qualifiedName === input, + ); + if (plugin) { + const scripts: string[] = []; + for (const hook of plugin.hooks) { + for (const cmd of hook.commands) { + scripts.push(extractScriptName(cmd.command)); + } + } + return [...new Set(scripts)]; + } + + // Check if input matches a hook script name + for (const p of plugins) { + for (const hook of p.hooks) { + for (const cmd of hook.commands) { + if (extractScriptName(cmd.command) === input) { + return [input]; + } + } + } + } + + return []; +} diff --git a/cli/tests/hooks-loader.test.ts b/cli/tests/hooks-loader.test.ts new file mode 100644 index 0000000..ae07e7b --- /dev/null +++ b/cli/tests/hooks-loader.test.ts @@ -0,0 +1,276 @@ +import { describe, expect, test } from "bun:test"; +import { mkdtempSync, rmSync } from "fs"; +import { tmpdir } from "os"; +import { resolve } from "path"; +import { + extractScriptName, + loadDisabledHooks, + resolveHookNames, + writeDisabledHooks, +} from "../src/loaders/hooks-loader.js"; +import type { PluginInfo } from "../src/schemas/plugin.js"; + +function makeTempDir(): string { + return mkdtempSync(resolve(tmpdir(), "codeforge-hooks-test-")); +} + +function makePlugin(overrides: Partial = {}): PluginInfo { + return { + name: "test-plugin", + marketplace: "test-mp", + qualifiedName: "test-plugin@test-mp", + enabled: true, + version: "1.0.0", + installPath: "/tmp/test", + description: "Test plugin", + author: "test", + installedAt: "2026-01-01", + hooks: [], + agents: [], + skills: [], + scripts: [], + ...overrides, + }; +} + +describe("extractScriptName", () => { + test("extracts basename from path argument", () => { + expect( + extractScriptName("/usr/local/bin/guard-workspace-scope"), + ).toBe("guard-workspace-scope"); + }); + + test("extracts script from complex command and strips .py", () => { + expect( + extractScriptName("python3 /path/to/scripts/my-hook.py"), + ).toBe("my-hook"); + }); + + test("returns last word when no path segments", () => { + expect(extractScriptName("simple-command")).toBe("simple-command"); + }); + + test("handles command with arguments after path", () => { + expect(extractScriptName("bash /scripts/run.sh --verbose")).toBe( + "run.sh", + ); + }); + + test("strips .py extension from script name", () => { + expect( + extractScriptName("/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py"), + ).toBe("guard-workspace-scope"); + }); +}); + +describe("resolveHookNames", () => { + const plugins: PluginInfo[] = [ + makePlugin({ + name: "workspace-scope-guard", + qualifiedName: "workspace-scope-guard@devs-marketplace", + hooks: [ + { + event: "PreToolUse", + matcher: "Write|Edit", + commands: [ + { + command: "/path/to/guard-workspace-scope", + timeout: 10, + }, + ], + }, + { + event: "PreToolUse", + matcher: "Bash", + commands: [ + { + command: "/path/to/annotate-bash-scope", + timeout: 5, + }, + ], + }, + ], + }), + makePlugin({ + name: "session-context", + qualifiedName: "session-context@devs-marketplace", + hooks: [ + { + event: "SessionStart", + commands: [ + { + command: "/path/to/git-state-injector", + timeout: 10, + }, + ], + }, + ], + }), + ]; + + test("resolves plugin name to all hook scripts", () => { + const result = resolveHookNames("workspace-scope-guard", plugins); + expect(result).toContain("guard-workspace-scope"); + expect(result).toContain("annotate-bash-scope"); + expect(result).toHaveLength(2); + }); + + test("resolves qualified plugin name", () => { + const result = resolveHookNames( + "workspace-scope-guard@devs-marketplace", + plugins, + ); + expect(result).toContain("guard-workspace-scope"); + expect(result).toHaveLength(2); + }); + + test("resolves script name to itself", () => { + const result = resolveHookNames("git-state-injector", plugins); + expect(result).toEqual(["git-state-injector"]); + }); + + test("returns empty array for unknown name", () => { + const result = resolveHookNames("nonexistent", plugins); + expect(result).toEqual([]); + }); + + test("deduplicates scripts when plugin has duplicate commands", () => { + const dupePlugins: PluginInfo[] = [ + makePlugin({ + name: "dupe-plugin", + qualifiedName: "dupe-plugin@mp", + hooks: [ + { + event: "PreToolUse", + matcher: "Write", + commands: [ + { command: "/path/to/my-hook", timeout: 10 }, + ], + }, + { + event: "PreToolUse", + matcher: "Edit", + commands: [ + { command: "/path/to/my-hook", timeout: 10 }, + ], + }, + ], + }), + ]; + const result = resolveHookNames("dupe-plugin", dupePlugins); + expect(result).toEqual(["my-hook"]); + }); +}); + +describe("loadDisabledHooks", () => { + test("reads and parses the disabled array", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + await Bun.write( + path, + JSON.stringify({ disabled: ["hook-a", "hook-b"] }), + ); + + const result = await loadDisabledHooks(path); + expect(result).toEqual(["hook-a", "hook-b"]); + + rmSync(dir, { recursive: true }); + }); + + test("returns empty array for missing file", async () => { + const result = await loadDisabledHooks( + "/tmp/nonexistent-codeforge-disabled-hooks.json", + ); + expect(result).toEqual([]); + }); + + test("returns empty array for invalid JSON", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + await Bun.write(path, "not valid json {{{"); + + const result = await loadDisabledHooks(path); + expect(result).toEqual([]); + + rmSync(dir, { recursive: true }); + }); + + test("returns empty array when disabled is not an array", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + await Bun.write(path, JSON.stringify({ disabled: "not-an-array" })); + + const result = await loadDisabledHooks(path); + expect(result).toEqual([]); + + rmSync(dir, { recursive: true }); + }); +}); + +describe("writeDisabledHooks", () => { + test("writes valid JSON with sorted deduplicated array", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + + await writeDisabledHooks( + ["hook-c", "hook-a", "hook-b", "hook-a"], + path, + ); + + const raw = await Bun.file(path).text(); + expect(raw.endsWith("\n")).toBe(true); + + const parsed = JSON.parse(raw); + expect(parsed.disabled).toEqual(["hook-a", "hook-b", "hook-c"]); + + // Verify 2-space indentation + expect(raw).toContain(' "disabled"'); + + rmSync(dir, { recursive: true }); + }); + + test("creates parent directories if needed", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "nested/deep/disabled-hooks.json"); + + await writeDisabledHooks(["hook-a"], path); + + const result = await loadDisabledHooks(path); + expect(result).toEqual(["hook-a"]); + + rmSync(dir, { recursive: true }); + }); +}); + +describe("round-trip", () => { + test("write then read preserves data", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + + await writeDisabledHooks(["hook-b", "hook-a", "hook-c"], path); + const result = await loadDisabledHooks(path); + + expect(result).toEqual(["hook-a", "hook-b", "hook-c"]); + + rmSync(dir, { recursive: true }); + }); + + test("disabling already-disabled hook does not duplicate", async () => { + const dir = makeTempDir(); + const path = resolve(dir, "disabled-hooks.json"); + + await writeDisabledHooks(["hook-a", "hook-b"], path); + + // Simulate disable: load, add if missing, write + const disabled = await loadDisabledHooks(path); + if (!disabled.includes("hook-a")) { + disabled.push("hook-a"); + } + await writeDisabledHooks(disabled, path); + + const result = await loadDisabledHooks(path); + expect(result).toEqual(["hook-a", "hook-b"]); + + rmSync(dir, { recursive: true }); + }); +}); diff --git a/container/.devcontainer/AGENTS.md b/container/.devcontainer/AGENTS.md index 90abc68..cc6fc7e 100755 --- a/container/.devcontainer/AGENTS.md +++ b/container/.devcontainer/AGENTS.md @@ -64,6 +64,10 @@ For deeper context, use the `/codeforge` skill (toolchain, filesystem, constrain | `analyze-sessions` | Session quality metrics: thinking depth, Read:Edit ratio, frustration indicators | | `lamarck skill ` | Skill analysis and improvement suggestions from session history | | `sandcastle` | AI agent orchestration — parallel workflows, branch strategies, iteration loops | +| `codeforge hooks list` | Show all hooks with enabled/disabled status | +| `codeforge hooks disable ` | Disable a hook by script or plugin name | +| `codeforge hooks enable ` | Re-enable a disabled hook | +| `codeforge hooks status` | Quick summary of disabled hooks | ## Plugins @@ -212,9 +216,38 @@ TypeScript library/CLI for orchestrating AI coding agents in isolated environmen 6. **Disable setup steps**: Set flags to `false` in `.codeforge/container.json` under `setup` 7. **Customize status bar**: Edit `defaults/codeforge/claude/statusline/settings.json` 8. **Lock Claude Code version**: Set `"versionLock": "2.1.80"` under `claude` in `.codeforge/container.json` — the update script installs that exact version on container start instead of updating to latest. Set to `null` to resume auto-updates. -9. **Disable individual hooks**: Add script name (without `.py`) to `disabled` array in `~/.claude/disabled-hooks.json` or override source `.codeforge/claude/disabled-hooks.json` +9. **Disable individual hooks**: Use `codeforge hooks disable ` (accepts script name or plugin name), or manually add script name (without `.py`) to `disabled` array in `~/.claude/disabled-hooks.json` or override source `.codeforge/claude/disabled-hooks.json` 10. **Change container timezone**: Set `"timezone": "America/New_York"` (or any IANA timezone) in `.codeforge/container.json`. Default is `America/Chicago` (Central Time). Applied on container start. +### Hook Management + +Disable or enable individual plugin hooks at runtime: + +| Command | Purpose | +|---------|---------| +| `codeforge hooks list` | Show all hooks with enabled/disabled status | +| `codeforge hooks disable ` | Disable a hook by script or plugin name | +| `codeforge hooks enable ` | Re-enable a disabled hook | +| `codeforge hooks status` | Quick summary of disabled hooks | + +Accepts both hook script names (`guard-workspace-scope`) and plugin names (`workspace-scope-guard` — disables all hooks for that plugin). + +Changes persist across container restarts (written to both deployed and source config). + +### AI Safety — workspace-scope-guard + +**PROHIBITION: AI assistants MUST NOT disable, bypass, or weaken the `workspace-scope-guard` hook without EXPLICIT user instruction.** "Explicit" means the user has directly and unambiguously stated they want it disabled — e.g., "disable workspace-scope-guard" or "turn off the scope guard." The following DO NOT constitute approval: +- The user asking to access files outside the project directory +- The user expressing frustration with scope restrictions +- The user saying "do whatever you need to" +- Any inferred or implied intent + +When in doubt, ask. This is a security boundary. + +### Config Apply Plugin Merge + +`codeforge config apply` merges `enabledPlugins` when deploying settings files. If you've disabled a plugin via `/plugins` or `codeforge plugin disable`, the `false` value is preserved through redeployment. Other settings keys are replaced from source. + ## Plugin Development Notes ### `${CLAUDE_PLUGIN_DATA}` — Persistent Plugin Storage diff --git a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md index 70e0749..331a3d3 100644 --- a/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md +++ b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/codeforge/references/constraints.md @@ -36,6 +36,23 @@ Confines file operations to the current project directory. - Read operations outside the project directory generate warnings - Bash commands are annotated with working directory context +## Hook Management + +Manage plugin hook enablement at runtime via `codeforge hooks`: + +| Command | Purpose | +|---------|---------| +| `codeforge hooks list` | Show all hooks with enabled/disabled status | +| `codeforge hooks disable ` | Disable a hook by script or plugin name | +| `codeforge hooks enable ` | Re-enable a disabled hook | +| `codeforge hooks status` | Quick summary of disabled hooks | + +Accepts both hook script names (`guard-workspace-scope`) and plugin names (`workspace-scope-guard` — disables all hooks for that plugin). + +### AI Safety Constraint + +**AI assistants MUST NOT disable `workspace-scope-guard` without explicit user instruction.** Asking to access files outside the project, expressing frustration with scope restrictions, or saying "do whatever you need to" do NOT constitute approval. When in doubt, ask. See AGENTS.md for the full prohibition. + ## Resource Limits | Resource | Limit | Notes | diff --git a/container/setup.js b/container/setup.js index e6d6cb0..a5d2335 100755 --- a/container/setup.js +++ b/container/setup.js @@ -614,9 +614,19 @@ function configApply() { } } - fs.copyFileSync(srcPath, destPath); - console.log(" Deployed: " + entry.src + " → " + destPath); - deployed++; + if ( + entry.id && + entry.id.startsWith("claude.settings") && + fs.existsSync(destPath) && + mergeSettingsFile(srcPath, destPath) + ) { + console.log(" Deployed: " + entry.src + " → " + destPath + " (merged)"); + deployed++; + } else { + fs.copyFileSync(srcPath, destPath); + console.log(" Deployed: " + entry.src + " → " + destPath); + deployed++; + } } console.log(""); @@ -625,6 +635,30 @@ function configApply() { ); } +function mergeSettingsFile(srcPath, destPath) { + try { + const src = JSON.parse(fs.readFileSync(srcPath, "utf-8")); + const dest = JSON.parse(fs.readFileSync(destPath, "utf-8")); + + if (!dest.enabledPlugins) return false; + + // Start with source, overlay user's false values for enabledPlugins + const merged = { ...src }; + if (!merged.enabledPlugins) merged.enabledPlugins = {}; + + for (const [key, value] of Object.entries(dest.enabledPlugins)) { + if (value === false) { + merged.enabledPlugins[key] = false; + } + } + + fs.writeFileSync(destPath, JSON.stringify(merged, null, 2) + "\n"); + return true; + } catch { + return false; + } +} + function mergeManifestEntries(defaultEntries, userEntries) { const byId = new Map(); for (const entry of [...defaultEntries, ...userEntries]) { From 3f3f0b98c676822ad2879468c4568890167ddad5 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 06:32:05 +0000 Subject: [PATCH 22/27] chore: update statusline metadata, upgrade docs to Astro 6, add changelog - Update ccstatusline session-usage and weekly-usage widgets with compact, cursor, and absolute metadata; switch reset-timer to dateMode/hourFormat - Upgrade docs dependencies to Astro 6.x and Starlight 0.39.x, remove zod override - Add unreleased changelog entries for networking, status line, config, auth fixes, tooling, AI-CONTEXT.md, /codeforge skill, and removals --- .../codeforge/claude/statusline/settings.json | 16 +- docs/package-lock.json | 19255 ++++++++-------- docs/package.json | 29 +- docs/src/content/docs/reference/changelog.md | 40 + 4 files changed, 9331 insertions(+), 10009 deletions(-) diff --git a/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json b/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json index 47d8f60..3e12fe6 100644 --- a/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json +++ b/container/.devcontainer/defaults/codeforge/claude/statusline/settings.json @@ -154,6 +154,11 @@ "id": "session-usage", "type": "session-usage", "rawValue": true, + "metadata": { + "absolute": "false", + "compact": "true", + "cursor": "true" + }, "color": "brightWhite", "backgroundColor": "bgBlue" }, @@ -162,7 +167,8 @@ "type": "reset-timer", "rawValue": true, "metadata": { - "absolute": "true", + "dateMode": "true", + "hourFormat": "true", "compact": "true" }, "color": "black", @@ -172,6 +178,11 @@ "id": "weekly-usage", "type": "weekly-usage", "rawValue": true, + "metadata": { + "absolute": "false", + "compact": "true", + "cursor": "true" + }, "color": "brightWhite", "backgroundColor": "bgMagenta" }, @@ -180,7 +191,8 @@ "type": "weekly-reset-timer", "rawValue": true, "metadata": { - "absolute": "true", + "dateMode": "true", + "hourFormat": "true", "compact": "true" }, "color": "brightWhite", diff --git a/docs/package-lock.json b/docs/package-lock.json index 82ca6e8..95df6a0 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,9993 +1,9266 @@ { - "name": "codeforge-docs", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "codeforge-docs", - "version": "0.1.0", - "dependencies": { - "@astrojs/sitemap": "3.7.0", - "@astrojs/starlight": "^0.37.6", - "@astrojs/starlight-tailwind": "^4.0.2", - "@fontsource-variable/plus-jakarta-sans": "^5.2.8", - "@tailwindcss/vite": "^4.2.1", - "astro": "^5.18.0", - "astro-mermaid": "^1.3.1", - "sharp": "^0.34.5", - "starlight-image-zoom": "^0.13.2", - "starlight-kbd": "^0.3.0", - "starlight-links-validator": "^0.19.2", - "starlight-llms-txt": "^0.7.0", - "starlight-scroll-to-top": "^0.4.0", - "starlight-sidebar-topics": "^0.6.2", - "starlight-tags": "^0.4.0", - "starlight-versions": "^0.7.0", - "tailwindcss": "^4.2.1" - } - }, - "node_modules/@antfu/install-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", - "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", - "license": "MIT" - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", - "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", - "license": "MIT" - }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.11", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", - "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" - } - }, - "node_modules/@astrojs/mdx": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz", - "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==", - "license": "MIT", - "dependencies": { - "@astrojs/markdown-remark": "6.3.11", - "@mdx-js/mdx": "^3.1.1", - "acorn": "^8.15.0", - "es-module-lexer": "^1.7.0", - "estree-util-visit": "^2.0.0", - "hast-util-to-html": "^9.0.5", - "piccolore": "^0.1.3", - "rehype-raw": "^7.0.0", - "remark-gfm": "^4.0.1", - "remark-smartypants": "^3.0.2", - "source-map": "^0.7.6", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.3" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - }, - "peerDependencies": { - "astro": "^5.0.0" - } - }, - "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/sitemap": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.0.tgz", - "integrity": "sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA==", - "license": "MIT", - "dependencies": { - "sitemap": "^8.0.2", - "stream-replace-string": "^2.0.0", - "zod": "^3.25.76" - } - }, - "node_modules/@astrojs/starlight": { - "version": "0.37.7", - "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.7.tgz", - "integrity": "sha512-KyBnou8aKIlPJUSNx6a1SN7XyH22oj/VAvTGC+Edld4Bnei1A//pmCRTBvSrSeoGrdUjK0ErFUfaEhhO1bPfDg==", - "license": "MIT", - "dependencies": { - "@astrojs/markdown-remark": "^6.3.1", - "@astrojs/mdx": "^4.2.3", - "@astrojs/sitemap": "^3.3.0", - "@pagefind/default-ui": "^1.3.0", - "@types/hast": "^3.0.4", - "@types/js-yaml": "^4.0.9", - "@types/mdast": "^4.0.4", - "astro-expressive-code": "^0.41.1", - "bcp-47": "^2.1.0", - "hast-util-from-html": "^2.0.1", - "hast-util-select": "^6.0.2", - "hast-util-to-string": "^3.0.0", - "hastscript": "^9.0.0", - "i18next": "^23.11.5", - "js-yaml": "^4.1.0", - "klona": "^2.0.6", - "magic-string": "^0.30.17", - "mdast-util-directive": "^3.0.0", - "mdast-util-to-markdown": "^2.1.0", - "mdast-util-to-string": "^4.0.0", - "pagefind": "^1.3.0", - "rehype": "^13.0.1", - "rehype-format": "^5.0.0", - "remark-directive": "^3.0.0", - "ultrahtml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.2" - }, - "peerDependencies": { - "astro": "^5.5.0" - } - }, - "node_modules/@astrojs/starlight-tailwind": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@astrojs/starlight-tailwind/-/starlight-tailwind-4.0.2.tgz", - "integrity": "sha512-SYN/6zq6hJO5tWqbQ2tWT9/jd8ubUkzkBCcF94vByC/ZJ20Mi5GPjFvAh89Yky/aIM+jXxT6W5q4p6l58GKHiQ==", - "license": "MIT", - "peerDependencies": { - "@astrojs/starlight": ">=0.34.0", - "tailwindcss": "^4.0.0" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@braintree/sanitize-url": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", - "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", - "license": "MIT", - "peer": true - }, - "node_modules/@capsizecss/unpack": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", - "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", - "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@chevrotain/gast": "12.0.0", - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/gast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", - "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/regexp-to-ast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", - "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/@chevrotain/types": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", - "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/@chevrotain/utils": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", - "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/@ctrl/tinycolor": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", - "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@expressive-code/core": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", - "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", - "license": "MIT", - "dependencies": { - "@ctrl/tinycolor": "^4.0.4", - "hast-util-select": "^6.0.2", - "hast-util-to-html": "^9.0.1", - "hast-util-to-text": "^4.0.1", - "hastscript": "^9.0.0", - "postcss": "^8.4.38", - "postcss-nested": "^6.0.1", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1" - } - }, - "node_modules/@expressive-code/plugin-frames": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", - "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.7" - } - }, - "node_modules/@expressive-code/plugin-shiki": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", - "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.7", - "shiki": "^3.2.2" - } - }, - "node_modules/@expressive-code/plugin-text-markers": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", - "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.7" - } - }, - "node_modules/@fontsource-variable/plus-jakarta-sans": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.2.8.tgz", - "integrity": "sha512-iQecBizIdZxezODNHzOn4SvvRMrZL/S8k4MEXGDynCmUrImVW0VmX+tIAMqnADwH4haXlHSXqMgU6+kcfBQJdw==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT", - "peer": true - }, - "node_modules/@iconify/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@antfu/install-pkg": "^1.1.0", - "@iconify/types": "^2.0.0", - "mlly": "^1.8.0" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mermaid-js/parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", - "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", - "license": "MIT", - "peer": true, - "dependencies": { - "langium": "^4.0.0" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, - "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@pagefind/darwin-arm64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", - "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pagefind/darwin-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", - "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pagefind/default-ui": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz", - "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==", - "license": "MIT" - }, - "node_modules/@pagefind/freebsd-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", - "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@pagefind/linux-arm64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", - "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pagefind/linux-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", - "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pagefind/windows-arm64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", - "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@pagefind/windows-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", - "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", - "license": "MIT", - "peer": true - }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", - "cpu": [ - "arm" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", - "cpu": [ - "loong64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", - "cpu": [ - "loong64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", - "cpu": [ - "ppc64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", - "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", - "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-x64": "4.2.2", - "@tailwindcss/oxide-freebsd-x64": "4.2.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-x64-musl": "4.2.2", - "@tailwindcss/oxide-wasm32-wasi": "4.2.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", - "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", - "tailwindcss": "4.2.2" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/braces": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.5.tgz", - "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==", - "license": "MIT" - }, - "node_modules/@types/d3": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", - "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-array": "*", - "@types/d3-axis": "*", - "@types/d3-brush": "*", - "@types/d3-chord": "*", - "@types/d3-color": "*", - "@types/d3-contour": "*", - "@types/d3-delaunay": "*", - "@types/d3-dispatch": "*", - "@types/d3-drag": "*", - "@types/d3-dsv": "*", - "@types/d3-ease": "*", - "@types/d3-fetch": "*", - "@types/d3-force": "*", - "@types/d3-format": "*", - "@types/d3-geo": "*", - "@types/d3-hierarchy": "*", - "@types/d3-interpolate": "*", - "@types/d3-path": "*", - "@types/d3-polygon": "*", - "@types/d3-quadtree": "*", - "@types/d3-random": "*", - "@types/d3-scale": "*", - "@types/d3-scale-chromatic": "*", - "@types/d3-selection": "*", - "@types/d3-shape": "*", - "@types/d3-time": "*", - "@types/d3-time-format": "*", - "@types/d3-timer": "*", - "@types/d3-transition": "*", - "@types/d3-zoom": "*" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-axis": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", - "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-brush": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", - "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-chord": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", - "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-contour": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", - "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-array": "*", - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-dispatch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", - "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-drag": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", - "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-dsv": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", - "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-fetch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", - "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-dsv": "*" - } - }, - "node_modules/@types/d3-force": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", - "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-format": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", - "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-hierarchy": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", - "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-polygon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", - "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-quadtree": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", - "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-random": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", - "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-selection": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-time-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", - "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/d3-transition": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-zoom": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", - "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/d3-interpolate": "*", - "@types/d3-selection": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/micromatch": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.10.tgz", - "integrity": "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ==", - "license": "MIT", - "dependencies": { - "@types/braces": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/picomatch": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.2.tgz", - "integrity": "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA==", - "license": "MIT" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@upsetjs/venn.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", - "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", - "license": "MIT", - "peer": true, - "optionalDependencies": { - "d3-selection": "^3.0.0", - "d3-transition": "^3.0.1" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", - "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/astro": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.1.tgz", - "integrity": "sha512-m4VWilWZ+Xt6NPoYzC4CgGZim/zQUO7WFL0RHCH0AiEavF1153iC3+me2atDvXpf/yX4PyGUeD8wZLq1cirT3g==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/markdown-remark": "6.3.11", - "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^4.0.0", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.3.0", - "acorn": "^8.15.0", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.3.1", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.1.1", - "cssesc": "^3.0.0", - "debug": "^4.4.3", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.6.2", - "diff": "^8.0.3", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.7.0", - "esbuild": "^0.27.3", - "estree-walker": "^3.0.3", - "flattie": "^1.1.1", - "fontace": "~0.4.0", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.2.0", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "magic-string": "^0.30.21", - "magicast": "^0.5.1", - "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.1", - "package-manager-detector": "^1.6.0", - "piccolore": "^0.1.3", - "picomatch": "^4.0.3", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.3", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "svgo": "^4.0.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", - "ultrahtml": "^1.6.0", - "unifont": "~0.7.3", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.4", - "vfile": "^6.0.3", - "vite": "^6.4.1", - "vitefu": "^1.1.1", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.3", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.1", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.34.0" - } - }, - "node_modules/astro-expressive-code": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", - "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", - "license": "MIT", - "dependencies": { - "rehype-expressive-code": "^0.41.7" - }, - "peerDependencies": { - "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" - } - }, - "node_modules/astro-mermaid": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/astro-mermaid/-/astro-mermaid-1.4.0.tgz", - "integrity": "sha512-VYfUT0LC0kMqnbBUE17yZBcabO3XrJydD4t5vpj7A59G/cIn/igba23snRJ2HHSH73TYjmXLLIU0ssbmNAJh8w==", - "license": "MIT", - "dependencies": { - "import-meta-resolve": "^4.2.0", - "mdast-util-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "peerDependencies": { - "@mermaid-js/layout-elk": "^0.2.0", - "astro": ">=4", - "mermaid": "^10.0.0 || ^11.0.0" - }, - "peerDependenciesMeta": { - "@mermaid-js/layout-elk": { - "optional": true - } - } - }, - "node_modules/astro/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/astro/node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/astro/node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, - "node_modules/bcp-47": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", - "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/bcp-47-match": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", - "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chevrotain": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", - "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@chevrotain/cst-dts-gen": "12.0.0", - "@chevrotain/gast": "12.0.0", - "@chevrotain/regexp-to-ast": "12.0.0", - "@chevrotain/types": "12.0.0", - "@chevrotain/utils": "12.0.0" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/chevrotain-allstar": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", - "integrity": "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==", - "license": "MIT", - "peer": true, - "dependencies": { - "lodash-es": "^4.17.21" - }, - "peerDependencies": { - "chevrotain": "^12.0.0" - } - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT", - "peer": true - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cookie-es": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", - "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", - "license": "MIT" - }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "license": "MIT", - "peer": true, - "dependencies": { - "layout-base": "^1.0.0" - } - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-selector-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", - "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/cytoscape": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.2.tgz", - "integrity": "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "license": "MIT", - "peer": true, - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", - "license": "MIT", - "peer": true - }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "peer": true, - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "license": "ISC", - "peer": true, - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "license": "ISC", - "peer": true, - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC", - "peer": true - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "license": "ISC", - "peer": true, - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dagre-d3-es": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", - "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", - "license": "MIT", - "peer": true, - "dependencies": { - "d3": "^7.9.0", - "lodash-es": "^4.17.21" - } - }, - "node_modules/dayjs": { - "version": "1.11.20", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", - "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", - "license": "MIT", - "peer": true - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/defu": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", - "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", - "license": "MIT" - }, - "node_modules/delaunator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", - "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", - "license": "ISC", - "peer": true, - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/devalue": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", - "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/direction": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", - "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", - "license": "MIT", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", - "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", - "license": "(MPL-2.0 OR Apache-2.0)", - "peer": true, - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "license": "MIT" - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/expressive-code": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", - "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.7", - "@expressive-code/plugin-frames": "^0.41.7", - "@expressive-code/plugin-shiki": "^0.41.7", - "@expressive-code/plugin-text-markers": "^0.41.7" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", - "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.2" - } - }, - "node_modules/fontkitten": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", - "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", - "license": "MIT", - "dependencies": { - "tiny-inflate": "^1.0.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/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==", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", - "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.3", - "crossws": "^0.3.5", - "defu": "^6.1.6", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.4", - "radix3": "^1.1.2", - "ufo": "^1.6.3", - "uncrypto": "^0.1.3" - } - }, - "node_modules/hachure-fill": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", - "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", - "license": "MIT", - "peer": true - }, - "node_modules/has-flag": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", - "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hast-util-embedded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", - "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-format": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", - "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-minify-whitespace": "^1.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "html-whitespace-sensitive-tag-names": "^3.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-has-property": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", - "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-body-ok-link": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", - "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-minify-whitespace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", - "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-phrasing": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", - "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-has-property": "^3.0.0", - "hast-util-is-body-ok-link": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-select": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", - "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "bcp-47-match": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "css-selector-parser": "^3.0.0", - "devlop": "^1.0.0", - "direction": "^2.0.0", - "hast-util-has-property": "^3.0.0", - "hast-util-to-string": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "nth-check": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-mdast": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.2.tgz", - "integrity": "sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "hast-util-to-text": "^4.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-minify-whitespace": "^6.0.0", - "trim-trailing-lines": "^2.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-string": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", - "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-whitespace-sensitive-tag-names": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", - "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/i18next": { - "version": "23.16.8", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", - "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-absolute-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", - "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/katex": { - "version": "0.16.45", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", - "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "peer": true, - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", - "peer": true - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/langium": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.2.tgz", - "integrity": "sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@chevrotain/regexp-to-ast": "~12.0.0", - "chevrotain": "~12.0.0", - "chevrotain-allstar": "~0.4.1", - "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.1.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", - "license": "MIT", - "peer": true - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash-es": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", - "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", - "license": "MIT", - "peer": true - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "source-map-js": "^1.2.1" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/marked": { - "version": "16.4.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", - "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", - "license": "MIT", - "peer": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", - "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" - }, - "node_modules/mermaid": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.14.0.tgz", - "integrity": "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@braintree/sanitize-url": "^7.1.1", - "@iconify/utils": "^3.0.2", - "@mermaid-js/parser": "^1.1.0", - "@types/d3": "^7.4.3", - "@upsetjs/venn.js": "^2.0.0", - "cytoscape": "^3.33.1", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.2.0", - "d3": "^7.9.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.14", - "dayjs": "^1.11.19", - "dompurify": "^3.3.1", - "katex": "^0.16.25", - "khroma": "^2.1.0", - "lodash-es": "^4.17.23", - "marked": "^16.3.0", - "roughjs": "^4.6.6", - "stylis": "^4.3.6", - "ts-dedent": "^2.2.0", - "uuid": "^11.1.0" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", - "license": "MIT", - "peer": true, - "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", - "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/ofetch": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", - "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.5", - "node-fetch-native": "^1.6.7", - "ufo": "^1.6.1" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "license": "MIT" - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", - "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.1.0", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", - "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-manager-detector": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", - "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", - "license": "MIT" - }, - "node_modules/pagefind": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", - "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", - "license": "MIT", - "bin": { - "pagefind": "lib/runner/bin.cjs" - }, - "optionalDependencies": { - "@pagefind/darwin-arm64": "1.5.2", - "@pagefind/darwin-x64": "1.5.2", - "@pagefind/freebsd-x64": "1.5.2", - "@pagefind/linux-arm64": "1.5.2", - "@pagefind/linux-x64": "1.5.2", - "@pagefind/windows-arm64": "1.5.2", - "@pagefind/windows-x64": "1.5.2" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-data-parser": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", - "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", - "license": "MIT", - "peer": true - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT", - "peer": true - }, - "node_modules/piccolore": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", - "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", - "license": "ISC" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/points-on-curve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", - "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", - "license": "MIT", - "peer": true - }, - "node_modules/points-on-path": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", - "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", - "license": "MIT", - "peer": true, - "dependencies": { - "path-data-parser": "0.1.0", - "points-on-curve": "0.2.0" - } - }, - "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", - "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-expressive-code": { - "version": "0.41.7", - "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", - "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", - "license": "MIT", - "dependencies": { - "expressive-code": "^0.41.7" - } - }, - "node_modules/rehype-format": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", - "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-format": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-minify-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz", - "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-minify-whitespace": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-remark": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-remark/-/rehype-remark-10.0.1.tgz", - "integrity": "sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "hast-util-to-mdast": "^10.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", - "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", - "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", - "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", - "license": "Unlicense", - "peer": true - }, - "node_modules/rolldown": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@oxc-project/types": "=0.124.0", - "@rolldown/pluginutils": "1.0.0-rc.15" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-x64": "1.0.0-rc.15", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" - } - }, - "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/roughjs": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", - "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "hachure-fill": "^0.5.2", - "path-data-parser": "^0.1.0", - "points-on-curve": "^0.2.0", - "points-on-path": "^0.2.1" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", - "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/engine-javascript": "3.23.0", - "@shikijs/engine-oniguruma": "3.23.0", - "@shikijs/langs": "3.23.0", - "@shikijs/themes": "3.23.0", - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/sitemap": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.3.tgz", - "integrity": "sha512-9Ew1tR2WYw8RGE2XLy7GjkusvYXy8Rg6y8TYuBuQMfIEdGcWoJpY2Wr5DzsEiL/TKCw56+YKTCCUHglorEYK+A==", - "license": "MIT", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.4.1" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=14.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", - "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/starlight-image-zoom": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/starlight-image-zoom/-/starlight-image-zoom-0.13.2.tgz", - "integrity": "sha512-fDJrx+UZXhkbhEeXKoRogTKAYtrYVJPw6wmSUI3nHUTA0vuRM6EI//2Z8bzv3Ecvz0pHKD1vAxtS01mLyessBA==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx-jsx": "^3.1.3", - "rehype-raw": "^7.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.32.0" - } - }, - "node_modules/starlight-kbd": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/starlight-kbd/-/starlight-kbd-0.3.0.tgz", - "integrity": "sha512-bhG1kWGEXCkuV8pkYW6sWEVmwD2bnpQpVIxq4QDJp7tLsprAbIKnD7RKFCP/QtITfwaVgKq3uMNK0+p4TUAgGg==", - "license": "MIT", - "engines": { - "node": ">=18.17.1" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.32.0" - } - }, - "node_modules/starlight-links-validator": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.19.2.tgz", - "integrity": "sha512-IHeK3R78fsmv53VfRkGbXkwK1CQEUBHM9QPzBEyoAxjZ/ssi5gjV+F4oNNUppTR48iPp+lEY0MTAmvkX7yNnkw==", - "license": "MIT", - "dependencies": { - "@types/picomatch": "^3.0.1", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-has-property": "^3.0.0", - "is-absolute-url": "^4.0.1", - "kleur": "^4.1.5", - "mdast-util-mdx-jsx": "^3.1.3", - "mdast-util-to-string": "^4.0.0", - "picomatch": "^4.0.2", - "terminal-link": "^5.0.0", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=18.17.1" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.32.0", - "astro": ">=5.1.5" - } - }, - "node_modules/starlight-links-validator/node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/starlight-llms-txt": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/starlight-llms-txt/-/starlight-llms-txt-0.7.0.tgz", - "integrity": "sha512-KAay6JLXqB0GiNQ481z3Z/h/y4xeAU55TUGLz+npjxcRvN3h/7rDxjmyLiphZF8xfoqqSTduQPanl5Ct4Je6kA==", - "license": "MIT", - "dependencies": { - "@astrojs/mdx": "^4.3.13", - "@types/hast": "^3.0.4", - "@types/micromatch": "^4.0.10", - "github-slugger": "^2.0.0", - "hast-util-select": "^6.0.4", - "micromatch": "^4.0.8", - "rehype-parse": "^9.0.1", - "rehype-remark": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-stringify": "^11.0.0", - "unified": "^11.0.5", - "unist-util-remove": "^4.0.0" - }, - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.31", - "astro": "^5.15.9" - } - }, - "node_modules/starlight-scroll-to-top": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/starlight-scroll-to-top/-/starlight-scroll-to-top-0.4.0.tgz", - "integrity": "sha512-lxsW5Sv+oKCI8CYZQ6Ue957cExiHMozK73LmmbsvpBKWryW+AKU4OXmX/1bTQNx+mVLZcpm2qTwKa1KX5VdEaQ==", - "license": "MIT", - "engines": { - "node": "^18.17.1 || ^20.3.0 || >=21.0.0" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.35" - } - }, - "node_modules/starlight-sidebar-topics": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/starlight-sidebar-topics/-/starlight-sidebar-topics-0.6.2.tgz", - "integrity": "sha512-SNCTUZS/hcVor0ZcaXbaSVU37+V+qtvzNirkvnOg3Mqu/awuGpthkH5+uKpiZqWxLffp6TrOlsv5E5QsxrndNg==", - "license": "MIT", - "dependencies": { - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.32.0" - } - }, - "node_modules/starlight-tags": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/starlight-tags/-/starlight-tags-0.4.0.tgz", - "integrity": "sha512-CX8Wh9J2rT+SzlPwKlWtb/qvyqIDxHgjgA7VYt/Vj8iSujgDXMMjQ2GyqERWz0i6PuINXSnVVUGqDrJuwh4p5A==", - "license": "MIT", - "dependencies": { - "js-yaml": "^4.1.1" - }, - "engines": { - "node": ">=18.17.1" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.35.0" - } - }, - "node_modules/starlight-versions": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/starlight-versions/-/starlight-versions-0.7.1.tgz", - "integrity": "sha512-Z8uZdmrGVoSIb0iljudRpNDdjIJqNkM5A1XCVSWpYsrB0Cg6hAZgTDI8/k7bDVJzzteUOK7ul6wlU7OA1FuiOg==", - "license": "MIT", - "dependencies": { - "@pagefind/default-ui": "^1.2.0", - "github-slugger": "^2.0.0", - "mdast-util-mdx-jsx": "^3.1.3", - "mdast-util-mdxjs-esm": "^2.0.1", - "remark": "^15.0.1", - "remark-directive": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-mdx": "^3.1.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.3", - "yaml": "^2.6.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.32.0" - } - }, - "node_modules/starlight-versions/node_modules/micromark-extension-directive": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", - "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/starlight-versions/node_modules/remark-directive": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz", - "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/stream-replace-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", - "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", - "license": "MIT", - "peer": true - }, - "node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-hyperlinks": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", - "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", - "license": "MIT", - "dependencies": { - "has-flag": "^5.0.1", - "supports-color": "^10.2.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" - } - }, - "node_modules/svgo": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", - "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.5.0" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terminal-link": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz", - "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "supports-hyperlinks": "^4.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "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/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trim-trailing-lines": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz", - "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.10" - } - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", - "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", - "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", - "license": "MIT", - "dependencies": { - "css-tree": "^3.1.0", - "ofetch": "^1.5.1", - "ohash": "^2.0.11" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", - "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", - "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^5.0.0", - "destr": "^2.0.5", - "h3": "^1.15.10", - "lru-cache": "^11.2.7", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.5.1", - "ufo": "^1.6.3" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6 || ^7 || ^8", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/functions": "^2.2.12 || ^3.0.0", - "@vercel/kv": "^1 || ^2 || ^3", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/functions": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "peer": true, - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", - "license": "MIT", - "peer": true, - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.15", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "license": "MIT", - "peer": true, - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "license": "MIT", - "peer": true, - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "license": "MIT", - "peer": true - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT", - "peer": true - }, - "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "license": "MIT", - "peer": true - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", - "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", - "license": "MIT" - }, - "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } + "name": "codeforge-docs", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codeforge-docs", + "version": "3.0.0", + "dependencies": { + "@astrojs/sitemap": "^3.7.2", + "@astrojs/starlight": "^0.39.1", + "@astrojs/starlight-tailwind": "^5.0.0", + "@fontsource-variable/plus-jakarta-sans": "^5.2.8", + "@tailwindcss/vite": "^4.2.1", + "astro": "^6.1.6", + "astro-mermaid": "^2.0.1", + "sharp": "^0.34.5", + "starlight-image-zoom": "^0.14.1", + "starlight-kbd": "^0.4.0", + "starlight-links-validator": "^0.24.0", + "starlight-llms-txt": "^0.8.1", + "starlight-scroll-to-top": "^1.0.1", + "starlight-sidebar-topics": "^0.7.1", + "starlight-tags": "^1.0.1", + "starlight-versions": "^0.8.2", + "tailwindcss": "^4.2.1" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@astrojs/compiler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz", + "integrity": "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.9.0.tgz", + "integrity": "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg==", + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.4" + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.1.tgz", + "integrity": "sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.9.0", + "@astrojs/prism": "4.0.1", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.0", + "smol-toml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-5.0.4.tgz", + "integrity": "sha512-tSbuuYueNODiFAFaME7pjHY5lOLoxBYJi1cKd6scw9+a4ZO7C7UGdafEoVAQvOV2eO8a6RaHSAJYGVPL1w8BPA==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "7.1.1", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.16.0", + "es-module-lexer": "^2.0.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "astro": "^6.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.1.tgz", + "integrity": "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.2.tgz", + "integrity": "sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.39.1.tgz", + "integrity": "sha512-9kIAXBwcqAuFQ7Ft419fr6rz6p0SPg7Yfgc28TfzoCcuOH9utZgIola9yOq5YIMDrR8rmm8kyl7pCsfrPVmLhQ==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "^7.1.1", + "@astrojs/mdx": "^5.0.4", + "@astrojs/sitemap": "^3.7.2", + "@pagefind/default-ui": "^1.3.0", + "@types/hast": "^3.0.4", + "@types/js-yaml": "^4.0.9", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.42.0", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.3", + "hast-util-select": "^6.0.4", + "hast-util-to-string": "^3.0.1", + "hastscript": "^9.0.1", + "i18next": "^26.0.7", + "js-yaml": "^4.1.1", + "klona": "^2.0.6", + "magic-string": "^0.30.21", + "mdast-util-directive": "^3.1.0", + "mdast-util-to-markdown": "^2.1.2", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.3.0", + "rehype": "^13.0.2", + "rehype-format": "^5.0.1", + "remark-directive": "^4.0.0", + "ultrahtml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "peerDependencies": { + "astro": "^6.0.0" + } + }, + "node_modules/@astrojs/starlight-tailwind": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/starlight-tailwind/-/starlight-tailwind-5.0.0.tgz", + "integrity": "sha512-VivF+bWg++4ma/ffr5sgHsd/ONtGdVJIKAaRZ6jmL4yqxy7bviu59MGNi5aW3nd8psP9i/aivBTrpwGxRM1XyA==", + "license": "MIT", + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0", + "tailwindcss": "^4.0.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz", + "integrity": "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "is-wsl": "^3.1.1", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT", + "peer": true + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", + "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", + "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/gast": "12.0.0", + "@chevrotain/types": "12.0.0" + } + }, + "node_modules/@chevrotain/gast": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", + "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/types": "12.0.0" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", + "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@chevrotain/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", + "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@chevrotain/utils": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", + "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@clack/core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.3.0.tgz", + "integrity": "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.3.0.tgz", + "integrity": "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.3.0", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.42.0.tgz", + "integrity": "sha512-MN11+9nfmaC7sYu2BZJXAXqwkBRt8t1xTSqP+Ti1NfTEskgl6xUnzDxoaiQkg0BMzpglA0pys4dpDKquP/cyIw==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.42.0.tgz", + "integrity": "sha512-XtkPm+941Uta7Y+81Acv+OA/20F1NJmJhCX6UYGKpqEIGqplNh3PTOhcURp6tcruhlzJcWcvpWy6Oigz3SrjqA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.42.0" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.42.0.tgz", + "integrity": "sha512-PMKey/kLmewttAHQezL+Y5Fx3vVssfDi3+FJOYQQS2mXP3tQspFELtKKAfsXfmSXdToZYgwoO69HJndqfE+09g==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.42.0", + "shiki": "^4.0.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.42.0.tgz", + "integrity": "sha512-l59lUx8fq1v5g6SpmbDjiU0+7IdfbiWnAyRmtTVSpfhyq+nZMN4UcmYyu2b9Mynhzt7Gr+O+cXyEPDNb2AVWVQ==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.42.0" + } + }, + "node_modules/@fontsource-variable/plus-jakarta-sans": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.2.8.tgz", + "integrity": "sha512-iQecBizIdZxezODNHzOn4SvvRMrZL/S8k4MEXGDynCmUrImVW0VmX+tIAMqnADwH4haXlHSXqMgU6+kcfBQJdw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT", + "peer": true + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", + "license": "MIT", + "peer": true, + "dependencies": { + "langium": "^4.0.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", + "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", + "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz", + "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==", + "license": "MIT" + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", + "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "license": "MIT", + "peer": true + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz", + "integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.0.2", + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz", + "integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz", + "integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz", + "integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz", + "integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz", + "integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz", + "integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/braces": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.5.tgz", + "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==", + "license": "MIT" + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/micromatch": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.10.tgz", + "integrity": "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ==", + "license": "MIT", + "dependencies": { + "@types/braces": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==", + "license": "MIT" + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "peer": true, + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/astro/-/astro-6.3.1.tgz", + "integrity": "sha512-atz6dmkE3Gu24bDgb7g2RE/BYnKqPYIHd6hTUM1UXvu/i7qNZOKLAqEHvgYpv9PQVcgWsXpk4/OOXZ0E/FzvSQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^4.0.0", + "@astrojs/internal-helpers": "0.9.0", + "@astrojs/markdown-remark": "7.1.1", + "@astrojs/telemetry": "3.3.2", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^1.1.1", + "devalue": "^5.6.3", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.27.3", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "rehype": "^13.0.2", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unist-util-visit": "^5.1.0", + "unstorage": "^1.17.5", + "vfile": "^6.0.3", + "vite": "^7.3.2", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0" + } + }, + "node_modules/astro-expressive-code": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.42.0.tgz", + "integrity": "sha512-aiTePi2Cn0mJPYWZSzP1GcxCinX9mNtJyCCshVVPSg1yRwM7ADvFJOx0FnS440M9t65hp8JH//dc2qr22Bm4ag==", + "license": "MIT", + "dependencies": { + "rehype-expressive-code": "^0.42.0" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + } + }, + "node_modules/astro-mermaid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/astro-mermaid/-/astro-mermaid-2.0.1.tgz", + "integrity": "sha512-JTyB62FMFJpwS/1yUplmrugL0yr0flBVYZw6mf3s7pVlh+CHCeZTXyj3KCCWtFhnPLAdM+PYld0D+gxN/SysLQ==", + "license": "MIT", + "dependencies": { + "import-meta-resolve": "^4.2.0", + "mdast-util-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "@mermaid-js/layout-elk": "^0.2.0", + "astro": ">=4", + "mermaid": "^10.0.0 || ^11.0.0" + }, + "peerDependenciesMeta": { + "@mermaid-js/layout-elk": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", + "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chevrotain": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", + "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "12.0.0", + "@chevrotain/gast": "12.0.0", + "@chevrotain/regexp-to-ast": "12.0.0", + "@chevrotain/types": "12.0.0", + "@chevrotain/utils": "12.0.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", + "integrity": "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==", + "license": "MIT", + "peer": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^12.0.0" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT", + "peer": true + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "peer": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/cytoscape": { + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.2.tgz", + "integrity": "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "peer": true, + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT", + "peer": true + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "peer": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "peer": true, + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "peer": true, + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC", + "peer": true + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "peer": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "license": "MIT", + "peer": true, + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT", + "peer": true + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", + "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "peer": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expressive-code": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.42.0.tgz", + "integrity": "sha512-V5DtJLEKuj4wf9O6IRtPtRObkMVy2ggR+S0MdjrTw6m58krZnDioyhW1si3Y04c5YPeooP4nd85Yq9NwEVHS4g==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.42.0", + "@expressive-code/plugin-frames": "^0.42.0", + "@expressive-code/plugin-shiki": "^0.42.0", + "@expressive-code/plugin-text-markers": "^0.42.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==", + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", + "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/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==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT", + "peer": true + }, + "node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-mdast": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.2.tgz", + "integrity": "sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "hast-util-to-text": "^4.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-minify-whitespace": "^6.0.0", + "trim-trailing-lines": "^2.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/i18next": { + "version": "26.0.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.10.tgz", + "integrity": "sha512-k3yGPAlWR2RdMYoVXJoDZDT87qeHIWKH7gVksdZMpRty7QX/D9QZeYGvN08KGbKHke9wn01eYT+EEsrqX/YTlw==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-absolute-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-5.0.0.tgz", + "integrity": "sha512-sdJyNpBnQHuVnBunfzjAecOhZr2+A30ywfFvu3EnxtKLUWfwGgyWUmqHbGZiU6vTfHpCPm5GvLe4BAvlU9n8VQ==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/katex": { + "version": "0.16.45", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", + "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "peer": true, + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "peer": true + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/langium": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.2.tgz", + "integrity": "sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chevrotain/regexp-to-ast": "~12.0.0", + "chevrotain": "~12.0.0", + "chevrotain-allstar": "~0.4.1", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.1.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT", + "peer": true + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT", + "peer": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/mermaid": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.14.0.tgz", + "integrity": "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.1.0", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "katex": "^0.16.25", + "khroma": "^2.1.0", + "lodash-es": "^4.17.23", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.2.0.tgz", + "integrity": "sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, + "node_modules/pagefind": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", + "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.5.2", + "@pagefind/darwin-x64": "1.5.2", + "@pagefind/freebsd-x64": "1.5.2", + "@pagefind/linux-arm64": "1.5.2", + "@pagefind/linux-x64": "1.5.2", + "@pagefind/windows-arm64": "1.5.2", + "@pagefind/windows-x64": "1.5.2" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT", + "peer": true + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT", + "peer": true + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT", + "peer": true + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.42.0.tgz", + "integrity": "sha512-8rp/1YMEVVSYbtz+bFBx+uSx3vA4i4T8RwRm5Q/IWbucQnnQqQ0hDqtmKOr8tv+59Cik6cu5aH3WPo0I7csuTA==", + "license": "MIT", + "dependencies": { + "expressive-code": "^0.42.0" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-minify-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz", + "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-remark": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-remark/-/rehype-remark-10.0.1.tgz", + "integrity": "sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "hast-util-to-mdast": "^10.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", + "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz", + "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/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==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense", + "peer": true + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "peer": true + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shiki": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz", + "integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.0.2", + "@shikijs/engine-javascript": "4.0.2", + "@shikijs/engine-oniguruma": "4.0.2", + "@shikijs/langs": "4.0.2", + "@shikijs/themes": "4.0.2", + "@shikijs/types": "4.0.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/starlight-image-zoom": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/starlight-image-zoom/-/starlight-image-zoom-0.14.1.tgz", + "integrity": "sha512-HFY+/779bq65tiEgiQVk8XIH6HPP34PTxWDmdlIo9pwidLyJUUrSH030/1Iu/r+KjAD7W/jpPWjC8Nkn7+uPhw==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx-jsx": "^3.2.0", + "rehype-raw": "^7.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/starlight-kbd": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/starlight-kbd/-/starlight-kbd-0.4.0.tgz", + "integrity": "sha512-Uz/MimnPaRqHsv2EllVnkD78ab2k3SQbVls3VZ53nUMbPMjIzg0+evgicqxrF8ZukQSRdve5sykKlbBEdH4xzQ==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/starlight-links-validator": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.24.0.tgz", + "integrity": "sha512-bsZf77oRJmY92KWOcu3vYK8Y12KJNvO3jQca1BgOBs+XskNfjPXrkgVtT7ls/FnLoomfsIV0wLdJfJs7kzGojA==", + "license": "MIT", + "dependencies": { + "@types/picomatch": "^4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "is-absolute-url": "^5.0.0", + "mdast-util-mdx-jsx": "^3.2.0", + "mdast-util-to-hast": "^13.2.1", + "picomatch": "^4.0.3", + "terminal-link": "^5.0.0", + "unist-util-visit": "^5.1.0", + "yaml": "^2.8.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0", + "astro": ">=6.0.0" + } + }, + "node_modules/starlight-llms-txt": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/starlight-llms-txt/-/starlight-llms-txt-0.8.1.tgz", + "integrity": "sha512-bRMck9OGNiKXyeJzA6Qy2N/gqC40aERpucOOagl+dPz5s/XeY+9p5dx4wBk3Qiicy3dF/F62Zt9iPPff/POpvA==", + "license": "MIT", + "dependencies": { + "@astrojs/mdx": "^5.0.3", + "@types/hast": "^3.0.4", + "@types/micromatch": "^4.0.10", + "github-slugger": "^2.0.0", + "hast-util-select": "^6.0.4", + "micromatch": "^4.0.8", + "rehype-parse": "^9.0.1", + "rehype-remark": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-stringify": "^11.0.0", + "unified": "^11.0.5", + "unist-util-remove": "^4.0.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0", + "astro": "^6.0.0" + } + }, + "node_modules/starlight-scroll-to-top": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/starlight-scroll-to-top/-/starlight-scroll-to-top-1.0.1.tgz", + "integrity": "sha512-OkSVwEQBzHkYPgBYL/O9xSiFn75j6HvrmjvdL8+3Kk0oxgt4sNmkOwhB0bGjB/T7M7SAJ4AFk1Ojza1HVxOeHQ==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/starlight-sidebar-topics": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/starlight-sidebar-topics/-/starlight-sidebar-topics-0.7.1.tgz", + "integrity": "sha512-2PBR05ZUvnKNoJtbL2u6GoE1qmQD0tFcd5+inYEJHJkx3LE2P+vlNslofTGHLtzch2XzyNbHUBQQu35bouA6NQ==", + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/starlight-tags": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/starlight-tags/-/starlight-tags-1.0.1.tgz", + "integrity": "sha512-h6LbISwiYiP5pAyD59CyU6KTY0g0wmQi3WJcXDMYfjEeejye3UH6jNOhG1JlcGk/mMpAQKDQ0zj8OKWtHiYcYw==", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/starlight-versions": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/starlight-versions/-/starlight-versions-0.8.2.tgz", + "integrity": "sha512-3PnBDgBLc0qDx7X6xY9RmzJ3NelvOhIKYhVkEZM3nusxf1EKZrvIgoJ+K+qlTpePYHNrRpSmSSClHcGEFE/Ejw==", + "license": "MIT", + "dependencies": { + "@pagefind/default-ui": "^1.3.0", + "github-slugger": "^2.0.0", + "mdast-util-mdx-jsx": "^3.2.0", + "mdast-util-mdxjs-esm": "^2.0.1", + "remark": "^15.0.1", + "remark-directive": "^4.0.0", + "remark-frontmatter": "^5.0.0", + "remark-mdx": "^3.1.1", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3", + "yaml": "^2.8.2" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT", + "peer": true + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-hyperlinks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", + "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", + "license": "MIT", + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/svgo": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terminal-link": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz", + "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^4.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.12.tgz", + "integrity": "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "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/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trim-trailing-lines": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz", + "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", + "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT", + "peer": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT", + "peer": true + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT", + "peer": true + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } } diff --git a/docs/package.json b/docs/package.json index d55f9fe..9280436 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,25 +11,22 @@ "astro": "astro" }, "dependencies": { - "@astrojs/sitemap": "3.7.0", - "@astrojs/starlight": "^0.37.6", - "@astrojs/starlight-tailwind": "^4.0.2", + "@astrojs/sitemap": "^3.7.2", + "@astrojs/starlight": "^0.39.1", + "@astrojs/starlight-tailwind": "^5.0.0", "@fontsource-variable/plus-jakarta-sans": "^5.2.8", "@tailwindcss/vite": "^4.2.1", - "astro": "^5.18.0", - "astro-mermaid": "^1.3.1", + "astro": "^6.1.6", + "astro-mermaid": "^2.0.1", "sharp": "^0.34.5", - "starlight-image-zoom": "^0.13.2", - "starlight-kbd": "^0.3.0", - "starlight-links-validator": "^0.19.2", - "starlight-llms-txt": "^0.7.0", - "starlight-scroll-to-top": "^0.4.0", - "starlight-sidebar-topics": "^0.6.2", - "starlight-tags": "^0.4.0", - "starlight-versions": "^0.7.0", + "starlight-image-zoom": "^0.14.1", + "starlight-kbd": "^0.4.0", + "starlight-links-validator": "^0.24.0", + "starlight-llms-txt": "^0.8.1", + "starlight-scroll-to-top": "^1.0.1", + "starlight-sidebar-topics": "^0.7.1", + "starlight-tags": "^1.0.1", + "starlight-versions": "^0.8.2", "tailwindcss": "^4.2.1" - }, - "overrides": { - "zod": "3.25.76" } } diff --git a/docs/src/content/docs/reference/changelog.md b/docs/src/content/docs/reference/changelog.md index af28de7..1f6c8d5 100755 --- a/docs/src/content/docs/reference/changelog.md +++ b/docs/src/content/docs/reference/changelog.md @@ -49,6 +49,46 @@ For minor and patch updates, you can usually just rebuild the container. Check t ## Unreleased +### Networking + +- **Docker-native port forwarding** — all service ports are now mapped in `docker-compose.yml` via `ports:` bound to `127.0.0.1`. This provides reliable port forwarding independent of VS Code, and works with WSL mirrored networking out of the box. Mapped ports: Karma Dashboard (7847), Karma API (7848), Claude-Mem Worker (37777), Astro docs dev server (4321), mitmproxy (8081), ccdiag API Proxy (9119). +- **Switch VS Code port detection to `output` mode** — `remote.autoForwardPortsSource` changed from `hybrid` to `output`. The `hybrid` mode has known reliability issues (silently stops working after detecting 20+ ports). The `output` mode is less aggressive but more reliable, and Docker Compose port mappings now handle the primary forwarding. +- **Add missing port labels** — added `portsAttributes` entries for the Astro docs dev server (4321) and mitmproxy/codeforge proxy (8081) so VS Code shows proper labels when these ports are detected. + +### Status Line + +- **Rate limit reset times** — the 5-hour and 7-day rate limit widgets now display when limits reset (e.g., `5h: 42% (14:30)` and `7d: 15% (Mon 09:00)`). Uses custom-command scripts instead of built-in ccstatusline types. + +### Configuration + +- **Container timezone** — new `timezone` field in `.codeforge/container.json` (default: `America/Chicago`). Set to any IANA timezone (e.g., `America/New_York`, `Europe/London`). Applied via `TZ` env var on container start. + +### Authentication + +- **Browser opener for `gh auth login`** — tools that need to open a browser (like `gh auth login`) now get a friendly fallback instead of a wall of "executable not found" errors. In VS Code terminals, URLs open on the host automatically via VS Code's built-in forwarding. In external terminals (Windows Terminal, tmux, etc.), the URL is printed cleanly for manual copy. Set via `$BROWSER` env var with conditional fallback — does not override VS Code's native browser handler. + +### Bug Fixes + +- **Fix git credential helper not configured without `GH_TOKEN` secret** — `gh auth setup-git` was nested inside the `GH_TOKEN` block, so it only ran when a token secret was provided. Now runs unconditionally on every container start, enabling manual `gh auth login` to work immediately for git operations. Also detects persisted GitHub CLI credentials (from Docker named volume) and derives git identity without requiring a secret. +- **Fix named volume ownership for all mount points** — `setup.sh` only fixed `root:root` ownership on `~/.claude`, leaving 6 other Docker named volumes unfixed. `~/.config/gh` and `~/.bun/install/cache` were actively broken (`gh auth login` would fail with `permission denied`). Now loops over all volume mount points from `docker-compose.yml`. + +### Developer Tooling + +- **Enable shfmt, dprint, shellcheck, hadolint** — previously disabled (`"version": "none"`), now set to `"latest"`. Provides shell formatting, markdown/TOML/Dockerfile formatting, shell linting, and Dockerfile linting out of the box. + +### Documentation + +- **Add AI-CONTEXT.md** — machine-readable environment reference for AI assistants. Covers toolchain, filesystem, constraints, auth, and persistence in ~700 tokens. Referenced from AGENTS.md with user guidance in README.md. + +### Skill Engine + +- **Add `/codeforge` skill** — on-demand deep container context (toolchain inventory, filesystem map, safety constraints). Complements the static AI-CONTEXT.md with detailed reference files. +- **Remove skill auto-suggestion** — removed `skill-suggester.py` and the `UserPromptSubmit` hook. Skills are now loaded on demand via `/skill` only. The auto-suggestion system had only 2 active matchers and added latency to every prompt. + +### Removed + +- **Remove Codex AGENTS.md** — removed the single-line `@AGENTS.md` self-referencing file from packaged defaults and file-manifest. Codex config.toml is unaffected. + ### Secrets & Configuration - **Docker Compose secrets** — secrets now use Docker Compose file-based secrets mounted at `/run/secrets/`. Place secret files in `.codeforge/secrets/` (one file per secret, raw value only). The `generate-compose.mjs` init script auto-discovers secrets and generates the compose override. Supported secrets: `gh_token`, `npm_token`, `claude_code_oauth_token`, `openai_api_key`, `anthropic_api_key`, `deepseek_api_key`, `gemini_api_key`, `openrouter_api_key`. Env vars (Codespaces) remain supported as a fallback. From 093582288abb7d74b935e77ba87124dfa48fdc67 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 06:48:12 +0000 Subject: [PATCH 23/27] feat(cli,container): add git safe directory check, mount CLI, deeper volume scan, rebuild messaging - Increase doctor volume detection depth from 4 to 8 to catch nested projects - Add git safe directory doctor check with immediate fix (no rebuild needed) - Auto-detect and register all git repos as safe.directory on container startup - Add `codeforge mount add ` command for manual volume registration - Extract shared mounts.json utilities to dedicated module - Standardize rebuild messaging with specific VS Code and CLI instructions - Add rebuildType field to FixAction for normal vs full rebuild guidance --- cli/src/commands/doctor/checks/git.ts | 115 +++++++++++++++++ cli/src/commands/doctor/checks/volumes.ts | 39 ++---- cli/src/commands/doctor/fix.ts | 25 +++- cli/src/commands/doctor/format.ts | 1 + cli/src/commands/doctor/index.ts | 18 +-- cli/src/commands/doctor/mounts.ts | 33 +++++ cli/src/commands/doctor/types.ts | 3 +- cli/src/commands/mount/add.ts | 90 +++++++++++++ cli/src/index.ts | 7 ++ cli/tests/doctor.test.ts | 146 +++++++++++++++++++++- cli/tests/mount.test.ts | 141 +++++++++++++++++++++ container/.devcontainer/CHANGELOG.md | 14 +++ container/.devcontainer/scripts/setup.sh | 11 +- 13 files changed, 593 insertions(+), 50 deletions(-) create mode 100644 cli/src/commands/doctor/checks/git.ts create mode 100644 cli/src/commands/doctor/mounts.ts create mode 100644 cli/src/commands/mount/add.ts create mode 100644 cli/tests/mount.test.ts diff --git a/cli/src/commands/doctor/checks/git.ts b/cli/src/commands/doctor/checks/git.ts new file mode 100644 index 0000000..bc6a582 --- /dev/null +++ b/cli/src/commands/doctor/checks/git.ts @@ -0,0 +1,115 @@ +import { dirname } from "node:path"; +import type { CheckResult } from "../types.js"; +import { spawn } from "../util.js"; + +export async function checkGitSafeDirectories( + workspaceRoot: string, +): Promise { + // Find all git repos (directories) and worktrees (files with gitdir:) + const [dirResult, fileResult] = await Promise.all([ + spawn("find", [ + workspaceRoot, + "-maxdepth", + "6", + "-name", + ".git", + "-type", + "d", + ]), + spawn("find", [ + workspaceRoot, + "-maxdepth", + "6", + "-name", + ".git", + "-type", + "f", + ]), + ]); + + const gitPaths = [ + ...(dirResult.exitCode === 0 && dirResult.stdout + ? dirResult.stdout.split("\n").filter(Boolean) + : []), + ...(fileResult.exitCode === 0 && fileResult.stdout + ? fileResult.stdout.split("\n").filter(Boolean) + : []), + ]; + + if (gitPaths.length === 0) { + return { + name: "Git safe directories", + category: "git", + status: "pass", + message: "no git repositories found", + }; + } + + // Get project roots from .git paths + const projectDirs = [...new Set(gitPaths.map((p) => dirname(p)))]; + + // Get current safe.directory entries + const { stdout: safeOutput } = await spawn("git", [ + "config", + "--global", + "--get-all", + "safe.directory", + ]); + + const safeDirs = new Set( + safeOutput ? safeOutput.split("\n").filter(Boolean) : [], + ); + + // Find project dirs not in the safe list + const missing = projectDirs.filter((dir) => !safeDirs.has(dir)); + + if (missing.length === 0) { + return { + name: "Git safe directories", + category: "git", + status: "pass", + message: `all ${projectDirs.length} project${projectDirs.length !== 1 ? "s" : ""} registered`, + }; + } + + return { + name: "Git safe directories", + category: "git", + status: "warn", + message: `${missing.length} project${missing.length !== 1 ? "s" : ""} missing safe.directory`, + hint: "Run codeforge doctor --fix --only git", + fix: { + label: `Add ${missing.length} safe.directory entr${missing.length !== 1 ? "ies" : "y"}`, + detail: `Registers ${missing.length} project director${missing.length !== 1 ? "ies" : "y"} as git safe directories to prevent "dubious ownership" errors (CVE-2022-24765). Takes effect immediately.`, + impact: `${missing.length} git config change${missing.length !== 1 ? "s" : ""}`, + requiresRebuild: false, + apply: async () => { + const added: string[] = []; + for (const dir of missing) { + const { exitCode } = await spawn("git", [ + "config", + "--global", + "--add", + "safe.directory", + dir, + ]); + if (exitCode === 0) { + added.push(dir); + } + } + + if (added.length === 0) { + return { + applied: false, + message: "Failed to add any safe.directory entries", + }; + } + + return { + applied: true, + message: `Added ${added.length} safe.directory entr${added.length !== 1 ? "ies" : "y"}: ${added.join(", ")}`, + }; + }, + }, + }; +} diff --git a/cli/src/commands/doctor/checks/volumes.ts b/cli/src/commands/doctor/checks/volumes.ts index 01721e3..e416847 100644 --- a/cli/src/commands/doctor/checks/volumes.ts +++ b/cli/src/commands/doctor/checks/volumes.ts @@ -1,6 +1,8 @@ import { existsSync } from "node:fs"; -import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { mkdir } from "node:fs/promises"; import { basename, dirname, join, relative } from "node:path"; +import { readMountsJson, writeMountsJson } from "../mounts.js"; +import type { MountEntry } from "../mounts.js"; import type { CheckResult } from "../types.js"; import { SLOW_FS_TYPES, spawn } from "../util.js"; @@ -9,6 +11,7 @@ interface VolumeCandidate { contextFiles: string[]; } + const VOLUME_CANDIDATES: VolumeCandidate[] = [ { pattern: "node_modules", contextFiles: ["package.json"] }, { @@ -34,18 +37,6 @@ const VOLUME_CANDIDATES: VolumeCandidate[] = [ const CANDIDATE_NAMES = VOLUME_CANDIDATES.map((c) => c.pattern); -interface MountsJson { - version: number; - volumes: MountEntry[]; -} - -interface MountEntry { - path: string; - source: "auto" | "user"; - signal: string; - added: string; -} - function hasContextFile( dirPath: string, contextFiles: string[], @@ -77,19 +68,6 @@ function todayIso(): string { return new Date().toISOString().slice(0, 10); } -async function readMountsJson(filePath: string): Promise { - try { - const raw = await readFile(filePath, "utf-8"); - const parsed = JSON.parse(raw) as MountsJson; - if (parsed.version === 1 && Array.isArray(parsed.volumes)) { - return parsed; - } - } catch { - // file doesn't exist or is invalid - } - return { version: 1, volumes: [] }; -} - function sanitizeName(name: string): string { return name.replace(/[^a-zA-Z0-9-]/g, "-").replace(/^-+|-+$/g, ""); } @@ -104,7 +82,7 @@ export async function checkVolumeCandidates( const { stdout, exitCode } = await spawn("find", [ workspaceRoot, "-maxdepth", - "4", + "8", "-type", "d", "(", @@ -161,6 +139,7 @@ export async function checkVolumeCandidates( detail: `Registers ${relPath} in .codeforge/mounts.json so CodeForge can mount it as a Docker volume instead of a bind mount, significantly improving I/O performance.`, impact: "requires rebuild", requiresRebuild: true, + rebuildType: "normal", apply: async (): Promise<{ applied: boolean; message: string; @@ -193,11 +172,7 @@ export async function checkVolumeCandidates( mounts.volumes.push(entry); } - await writeFile( - mountsPath, - JSON.stringify(mounts, null, "\t") + "\n", - "utf-8", - ); + await writeMountsJson(mountsPath, mounts); // Check if Docker Compose infrastructure exists const composeExists = existsSync( diff --git a/cli/src/commands/doctor/fix.ts b/cli/src/commands/doctor/fix.ts index 606f097..e505d01 100644 --- a/cli/src/commands/doctor/fix.ts +++ b/cli/src/commands/doctor/fix.ts @@ -18,6 +18,7 @@ interface FixOptions { const CATEGORY_LABELS: Record = { auth: "Authentication", environment: "Environment", + git: "Git", volumes: "Volumes", wsl: "WSL", }; @@ -26,6 +27,7 @@ const categoryMap: Record = { auth: "auth", env: "environment", environment: "environment", + git: "git", volumes: "volumes", wsl: "wsl", }; @@ -79,9 +81,24 @@ async function applyFixes(fixes: CheckResult[]): Promise { } } - const needsRebuild = fixes.some((f) => f.fix!.requiresRebuild); - if (needsRebuild) { - log.warn("Some changes require a container rebuild to take effect."); + const rebuildFixes = fixes.filter((f) => f.fix!.requiresRebuild); + if (rebuildFixes.length > 0) { + const needsFull = rebuildFixes.some( + (f) => f.fix!.rebuildType === "full", + ); + if (needsFull) { + log.warn( + "Some changes require a full container rebuild (no cache) to take effect.\n" + + " From VS Code: Ctrl+Shift+P → 'Dev Containers: Rebuild Without Cache'\n" + + " From host terminal: codeforge container rebuild --no-cache", + ); + } else { + log.warn( + "Some changes require a container rebuild to take effect.\n" + + " From VS Code: Ctrl+Shift+P → 'Dev Containers: Rebuild Container'\n" + + " From host terminal: codeforge container rebuild", + ); + } } } @@ -107,7 +124,7 @@ export async function runFixMode( const cat = categoryMap[options.only]; if (!cat) { console.error( - `Unknown category: ${options.only}. Valid: auth, env, volumes, wsl`, + `Unknown category: ${options.only}. Valid: auth, env, git, volumes, wsl`, ); process.exit(1); } diff --git a/cli/src/commands/doctor/format.ts b/cli/src/commands/doctor/format.ts index 0c79c37..efb4599 100644 --- a/cli/src/commands/doctor/format.ts +++ b/cli/src/commands/doctor/format.ts @@ -31,6 +31,7 @@ export function formatText(report: DoctorReport, useColor: boolean): string { const categoryConfig: { key: CheckCategory; label: string }[] = [ { key: "auth", label: "Authentication" }, { key: "environment", label: "Environment" }, + { key: "git", label: "Git" }, { key: "volumes", label: "Volumes" }, { key: "wsl", label: "WSL" }, ]; diff --git a/cli/src/commands/doctor/index.ts b/cli/src/commands/doctor/index.ts index 8f8c140..57608ac 100644 --- a/cli/src/commands/doctor/index.ts +++ b/cli/src/commands/doctor/index.ts @@ -6,6 +6,7 @@ import { checkTmpdir, checkWorkspaceFs, } from "./checks/environment.js"; +import { checkGitSafeDirectories } from "./checks/git.js"; import { checkVolumeCandidates } from "./checks/volumes.js"; import { checkDefenderExclusions, checkWslConfig } from "./checks/wsl.js"; import { runFixMode } from "./fix.js"; @@ -21,7 +22,7 @@ export function registerDoctorCommand(parent: Command): void { .option("--fix", "Enter interactive fix mode") .option("-y, --yes", "Skip TUI prompts, apply all fixes") .option("--dry-run", "Show what --fix would change without applying") - .option("--only ", "Filter fixes: auth, env, volumes, wsl") + .option("--only ", "Filter fixes: auth, env, git, volumes, wsl") .action(async (options) => { const workspaceRoot = process.env.WORKSPACE_ROOT || "/workspaces"; @@ -41,12 +42,14 @@ export function registerDoctorCommand(parent: Command): void { (c) => c.name === "Workspace filesystem" && c.status === "warn", ); - // Run volume + WSL checks (WSL checks return null when not WSL) - const [volumeChecks, wslConfigCheck, defenderCheck] = await Promise.all([ - checkVolumeCandidates(workspaceRoot), - checkWslConfig(isWsl), - checkDefenderExclusions(isWsl), - ]); + // Run git + volume + WSL checks (WSL checks return null when not WSL) + const [gitCheck, volumeChecks, wslConfigCheck, defenderCheck] = + await Promise.all([ + checkGitSafeDirectories(workspaceRoot), + checkVolumeCandidates(workspaceRoot), + checkWslConfig(isWsl), + checkDefenderExclusions(isWsl), + ]); // Assemble all checks, filtering out nulls from WSL checks const wslChecks = [wslConfigCheck, defenderCheck].filter( @@ -56,6 +59,7 @@ export function registerDoctorCommand(parent: Command): void { const checks: CheckResult[] = [ ...authChecks, ...envChecks, + gitCheck, ...volumeChecks, ...wslChecks, ]; diff --git a/cli/src/commands/doctor/mounts.ts b/cli/src/commands/doctor/mounts.ts new file mode 100644 index 0000000..62759ff --- /dev/null +++ b/cli/src/commands/doctor/mounts.ts @@ -0,0 +1,33 @@ +import { readFile, writeFile } from "node:fs/promises"; + +export interface MountsJson { + version: number; + volumes: MountEntry[]; +} + +export interface MountEntry { + path: string; + source: "auto" | "user"; + signal: string; + added: string; +} + +export async function readMountsJson(filePath: string): Promise { + try { + const raw = await readFile(filePath, "utf-8"); + const parsed = JSON.parse(raw) as MountsJson; + if (parsed.version === 1 && Array.isArray(parsed.volumes)) { + return parsed; + } + } catch { + // file doesn't exist or is invalid + } + return { version: 1, volumes: [] }; +} + +export async function writeMountsJson( + filePath: string, + data: MountsJson, +): Promise { + await writeFile(filePath, JSON.stringify(data, null, "\t") + "\n", "utf-8"); +} diff --git a/cli/src/commands/doctor/types.ts b/cli/src/commands/doctor/types.ts index 58492be..5d0f191 100644 --- a/cli/src/commands/doctor/types.ts +++ b/cli/src/commands/doctor/types.ts @@ -1,4 +1,4 @@ -export type CheckCategory = "auth" | "environment" | "volumes" | "wsl"; +export type CheckCategory = "auth" | "environment" | "git" | "volumes" | "wsl"; export interface FixResult { applied: boolean; @@ -11,6 +11,7 @@ export interface FixAction { detail: string; // full explanation impact: string; // "1 env var change" or "requires rebuild" requiresRebuild: boolean; + rebuildType?: "normal" | "full"; // "normal" = recreate container, "full" = rebuild image (no cache) apply: () => Promise; } diff --git a/cli/src/commands/mount/add.ts b/cli/src/commands/mount/add.ts new file mode 100644 index 0000000..4a59d6e --- /dev/null +++ b/cli/src/commands/mount/add.ts @@ -0,0 +1,90 @@ +import { existsSync, statSync } from "node:fs"; +import { mkdir } from "node:fs/promises"; +import { isAbsolute, join, relative } from "node:path"; +import type { Command } from "commander"; +import { readMountsJson, writeMountsJson } from "../doctor/mounts.js"; + +function todayIso(): string { + return new Date().toISOString().slice(0, 10); +} + +export function registerMountAddCommand(parent: Command): void { + parent + .command("add ") + .description( + "Register a directory for Docker volume mounting (e.g., projects/my-app/node_modules)", + ) + .action(async (rawPath: string) => { + const workspaceRoot = process.env.WORKSPACE_ROOT || "/workspaces"; + + // Resolve the path — accept both absolute and relative + let absPath: string; + if (isAbsolute(rawPath)) { + absPath = rawPath; + } else { + absPath = join(workspaceRoot, rawPath); + } + + // Validate the path exists and is a directory + if (!existsSync(absPath)) { + console.error(`Error: path does not exist: ${absPath}`); + process.exit(1); + } + + const stat = statSync(absPath); + if (!stat.isDirectory()) { + console.error(`Error: path is not a directory: ${absPath}`); + process.exit(1); + } + + // Convert to relative path from workspace root + const relPath = relative(workspaceRoot, absPath); + if (relPath.startsWith("..")) { + console.error( + `Error: path must be within workspace root (${workspaceRoot})`, + ); + process.exit(1); + } + + // Read existing mounts.json + const codeforgeDir = join(workspaceRoot, ".codeforge"); + const mountsPath = join(codeforgeDir, "mounts.json"); + + if (!existsSync(codeforgeDir)) { + await mkdir(codeforgeDir, { recursive: true }); + } + + const mounts = await readMountsJson(mountsPath); + + // Check for duplicates + const existing = mounts.volumes.find((v) => v.path === relPath); + if (existing) { + console.log( + `Already registered: ${relPath} (source: ${existing.source}, added: ${existing.added})`, + ); + return; + } + + // Add the entry + mounts.volumes.push({ + path: relPath, + source: "user", + signal: "manual", + added: todayIso(), + }); + + await writeMountsJson(mountsPath, mounts); + + console.log(`Added ${relPath} to .codeforge/mounts.json`); + console.log(""); + console.log( + "A container rebuild is needed for volume mounts to take effect.", + ); + console.log( + " From VS Code: Ctrl+Shift+P → 'Dev Containers: Rebuild Container'", + ); + console.log( + " From host terminal: codeforge container rebuild", + ); + }); +} diff --git a/cli/src/index.ts b/cli/src/index.ts index ea18bbb..19430ca 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -10,6 +10,7 @@ import { registerContainerRebuildCommand } from "./commands/container/rebuild.js import { registerContainerShellCommand } from "./commands/container/shell.js"; import { registerContainerUpCommand } from "./commands/container/up.js"; import { registerDoctorCommand } from "./commands/doctor/index.js"; +import { registerMountAddCommand } from "./commands/mount/add.js"; import { registerIndexBuildCommand } from "./commands/index/build.js"; import { registerIndexCleanCommand } from "./commands/index/clean.js"; import { registerIndexSearchCommand } from "./commands/index/search.js"; @@ -117,6 +118,12 @@ registerContainerExecCommand(container); registerContainerLsCommand(container); registerContainerShellCommand(container); +const mount = program + .command("mount") + .description("Manage volume mount configuration"); + +registerMountAddCommand(mount); + registerProxyCommand(program); registerDoctorCommand(program); diff --git a/cli/tests/doctor.test.ts b/cli/tests/doctor.test.ts index ccc77a8..eea2585 100644 --- a/cli/tests/doctor.test.ts +++ b/cli/tests/doctor.test.ts @@ -183,6 +183,20 @@ describe("formatText", () => { expect(output).toContain("Volumes:"); }); + test("renders Git category when git checks present", () => { + const report = makeReport([ + makeCheck({ + name: "Git safe directories", + category: "git", + status: "warn", + message: "2 projects missing safe.directory", + hint: "Run codeforge doctor --fix --only git", + }), + ]); + const output = formatText(report, false); + expect(output).toContain("Git:"); + }); + test("hides WSL category when no WSL checks present", () => { const report = makeReport([ makeCheck({ @@ -348,7 +362,57 @@ describe("type/structure", () => { }); // --------------------------------------------------------------------------- -// 4. Volume detection tests (checks/volumes.ts) +// 4. Git safe directory tests (checks/git.ts) +// --------------------------------------------------------------------------- + +describe("checkGitSafeDirectories", () => { + const { + checkGitSafeDirectories, + } = require("../src/commands/doctor/checks/git.js"); + const { mkdirSync, rmSync } = require("node:fs"); + + const GIT_TEST_ROOT = "/tmp/git-safe-test"; + + beforeEach(() => { + rmSync(GIT_TEST_ROOT, { recursive: true, force: true }); + }); + + afterEach(() => { + rmSync(GIT_TEST_ROOT, { recursive: true, force: true }); + }); + + test("returns pass when no git repositories found", async () => { + mkdirSync(GIT_TEST_ROOT, { recursive: true }); + const result = await checkGitSafeDirectories(GIT_TEST_ROOT); + expect(result.status).toBe("pass"); + expect(result.category).toBe("git"); + expect(result.message).toContain("no git repositories"); + }); + + test("returns correct category and name", async () => { + mkdirSync(`${GIT_TEST_ROOT}/my-project/.git`, { recursive: true }); + const result = await checkGitSafeDirectories(GIT_TEST_ROOT); + expect(result.category).toBe("git"); + expect(result.name).toBe("Git safe directories"); + }); + + test("detects projects and returns pass or warn with fix", async () => { + mkdirSync(`${GIT_TEST_ROOT}/project-a/.git`, { recursive: true }); + mkdirSync(`${GIT_TEST_ROOT}/project-b/.git`, { recursive: true }); + + const result = await checkGitSafeDirectories(GIT_TEST_ROOT); + expect(result).toBeDefined(); + expect(result.category).toBe("git"); + expect(["pass", "warn"]).toContain(result.status); + if (result.status === "warn") { + expect(result.fix).toBeDefined(); + expect(result.fix!.requiresRebuild).toBe(false); + } + }); +}); + +// --------------------------------------------------------------------------- +// 5. Volume detection tests (checks/volumes.ts) // --------------------------------------------------------------------------- describe("checkVolumeCandidates", () => { @@ -379,7 +443,7 @@ describe("checkVolumeCandidates", () => { }); // --------------------------------------------------------------------------- -// 5. WSL checks tests (checks/wsl.ts) +// 6. WSL checks tests (checks/wsl.ts) // --------------------------------------------------------------------------- describe("checkWslConfig", () => { @@ -450,7 +514,7 @@ describe("checkDefenderExclusions", () => { }); // --------------------------------------------------------------------------- -// 6. Fix mode tests (fix.ts) +// 7. Fix mode tests (fix.ts) // --------------------------------------------------------------------------- describe("runFixMode", () => { @@ -631,6 +695,82 @@ describe("runFixMode", () => { expect(authApply).not.toHaveBeenCalled(); }); + test("category filter works with 'git'", async () => { + const gitApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed git" }), + ); + const envApply = mock(() => + Promise.resolve({ applied: true, message: "Fixed env" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Git check", + category: "git", + status: "warn", + fix: makeFix({ apply: gitApply }), + }), + makeCheck({ + name: "Env check", + category: "environment", + status: "warn", + fix: makeFix({ apply: envApply }), + }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false, only: "git" }); + + expect(gitApply).toHaveBeenCalledTimes(1); + expect(envApply).not.toHaveBeenCalled(); + }); + + test("rebuildType renders specific rebuild guidance", async () => { + const applyFn = mock(() => + Promise.resolve({ applied: true, message: "Added volume" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Volume check", + category: "volumes", + status: "warn", + fix: makeFix({ + apply: applyFn, + requiresRebuild: true, + rebuildType: "normal", + }), + }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false }); + + expect(applyFn).toHaveBeenCalledTimes(1); + // The rebuild warning should have been logged (via @clack/prompts) + }); + + test("rebuildType 'full' renders no-cache rebuild guidance", async () => { + const applyFn = mock(() => + Promise.resolve({ applied: true, message: "Changed image" }), + ); + + const checks: CheckResult[] = [ + makeCheck({ + name: "Image check", + category: "environment", + status: "warn", + fix: makeFix({ + apply: applyFn, + requiresRebuild: true, + rebuildType: "full", + }), + }), + ]; + + await runFixMode(checks, { yes: true, dryRun: false }); + + expect(applyFn).toHaveBeenCalledTimes(1); + }); + test("unknown --only category exits with error", async () => { const checks: CheckResult[] = [ makeCheck({ diff --git a/cli/tests/mount.test.ts b/cli/tests/mount.test.ts new file mode 100644 index 0000000..da710c3 --- /dev/null +++ b/cli/tests/mount.test.ts @@ -0,0 +1,141 @@ +import { + afterEach, + beforeEach, + describe, + expect, + spyOn, + test, +} from "bun:test"; +import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { Command } from "commander"; +import { registerMountAddCommand } from "../src/commands/mount/add.js"; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +const TEST_ROOT = "/tmp/mount-test-workspace"; +const CODEFORGE_DIR = join(TEST_ROOT, ".codeforge"); +const MOUNTS_PATH = join(CODEFORGE_DIR, "mounts.json"); + +function setupTestWorkspace(): void { + rmSync(TEST_ROOT, { recursive: true, force: true }); + mkdirSync(join(TEST_ROOT, "projects", "my-app", "node_modules"), { + recursive: true, + }); + mkdirSync(CODEFORGE_DIR, { recursive: true }); +} + +function cleanTestWorkspace(): void { + rmSync(TEST_ROOT, { recursive: true, force: true }); +} + +async function runMountAdd(path: string): Promise { + const program = new Command(); + const mount = program.command("mount"); + registerMountAddCommand(mount); + await program.parseAsync(["node", "test", "mount", "add", path]); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("mount add", () => { + let logSpy: ReturnType; + let errorSpy: ReturnType; + let exitSpy: ReturnType; + let originalWorkspaceRoot: string | undefined; + + beforeEach(() => { + setupTestWorkspace(); + logSpy = spyOn(console, "log").mockImplementation(() => {}); + errorSpy = spyOn(console, "error").mockImplementation(() => {}); + exitSpy = spyOn(process, "exit").mockImplementation((() => { + throw new Error("process.exit called"); + }) as any); + originalWorkspaceRoot = process.env.WORKSPACE_ROOT; + process.env.WORKSPACE_ROOT = TEST_ROOT; + }); + + afterEach(() => { + logSpy.mockRestore(); + errorSpy.mockRestore(); + exitSpy.mockRestore(); + if (originalWorkspaceRoot !== undefined) { + process.env.WORKSPACE_ROOT = originalWorkspaceRoot; + } else { + delete process.env.WORKSPACE_ROOT; + } + cleanTestWorkspace(); + }); + + test("writes entry to mounts.json with source 'user'", async () => { + await runMountAdd("projects/my-app/node_modules"); + + const raw = await readFile(MOUNTS_PATH, "utf-8"); + const mounts = JSON.parse(raw); + + expect(mounts.version).toBe(1); + expect(mounts.volumes).toHaveLength(1); + expect(mounts.volumes[0].path).toBe("projects/my-app/node_modules"); + expect(mounts.volumes[0].source).toBe("user"); + expect(mounts.volumes[0].signal).toBe("manual"); + expect(mounts.volumes[0].added).toMatch(/^\d{4}-\d{2}-\d{2}$/); + }); + + test("rejects non-existent paths", async () => { + expect( + runMountAdd("projects/nonexistent/node_modules"), + ).rejects.toThrow(); + expect(exitSpy).toHaveBeenCalledWith(1); + }); + + test("rejects duplicate entries gracefully", async () => { + // Pre-populate mounts.json + writeFileSync( + MOUNTS_PATH, + JSON.stringify({ + version: 1, + volumes: [ + { + path: "projects/my-app/node_modules", + source: "auto", + signal: "package.json", + added: "2026-01-01", + }, + ], + }), + ); + + await runMountAdd("projects/my-app/node_modules"); + + // Should not have exited with error + expect(exitSpy).not.toHaveBeenCalled(); + + // Should still have exactly one entry (not duplicated) + const raw = await readFile(MOUNTS_PATH, "utf-8"); + const mounts = JSON.parse(raw); + expect(mounts.volumes).toHaveLength(1); + }); + + test("shows rebuild guidance in output", async () => { + await runMountAdd("projects/my-app/node_modules"); + + const allOutput = logSpy.mock.calls.map((c) => c[0]).join("\n"); + expect(allOutput).toContain("rebuild"); + expect(allOutput).toContain("Rebuild Container"); + }); + + test("registers as a subcommand of mount", () => { + const program = new Command(); + const mount = program.command("mount"); + registerMountAddCommand(mount); + + const addCmd = mount.commands.find((c) => c.name() === "add"); + expect(addCmd).toBeDefined(); + expect(addCmd!.description()).toContain("volume"); + }); +}); diff --git a/container/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md index a9daa5b..17bedda 100755 --- a/container/.devcontainer/CHANGELOG.md +++ b/container/.devcontainer/CHANGELOG.md @@ -2,6 +2,20 @@ ## Unreleased +### Performance + +- **Deeper volume detection** — `codeforge doctor` volume scan now searches to depth 8 (was 4), catching `node_modules` in deeply nested project structures like `projects////node_modules`. +- **Auto-detect git safe directories on startup** — container startup now scans for all git repositories under the workspace and registers them as `safe.directory`, preventing "dubious ownership" errors. Previously only `/workspaces` itself was registered. + +### Doctor + +- **New git safe directory check** — `codeforge doctor` now detects project directories missing from git's `safe.directory` list and offers an immediate fix via `--fix --only git`. No rebuild required. +- **Standardized rebuild messaging** — fix mode now shows specific rebuild instructions (normal vs. full/no-cache) with exact VS Code and CLI commands, replacing the previous generic warning. + +### CLI + +- **New `codeforge mount add` command** — manually register directories for Docker volume mounting without going through doctor's auto-detection. Usage: `codeforge mount add `. + ### Networking - **Docker-native port forwarding** — all service ports are now mapped in `docker-compose.yml` via `ports:` bound to `127.0.0.1`. This provides reliable port forwarding independent of VS Code, and works with WSL mirrored networking out of the box. Mapped ports: Karma Dashboard (7847), Karma API (7848), Claude-Mem Worker (37777), Astro docs dev server (4321), mitmproxy (8081), ccdiag API Proxy (9119). diff --git a/container/.devcontainer/scripts/setup.sh b/container/.devcontainer/scripts/setup.sh index 39ba042..9f05762 100755 --- a/container/.devcontainer/scripts/setup.sh +++ b/container/.devcontainer/scripts/setup.sh @@ -83,9 +83,14 @@ for _vol in "${_VOLUME_MOUNTS[@]}"; do done unset _VOLUME_MOUNTS _OWNER _vol -# Mark workspace as safe for Git — bind-mounted workspace may have -# different uid than container user, causing "dubious ownership" -# errors (CVE-2022-24765) +# Mark all project directories as safe for Git — bind-mounted workspace may +# have different uid than container user, causing "dubious ownership" +# errors (CVE-2022-24765). Scans for .git dirs and worktree files. +while IFS= read -r gitdir; do + project_dir="$(dirname "$gitdir")" + git config --global --add safe.directory "$project_dir" 2>/dev/null +done < <(find "$WORKSPACE_ROOT" -maxdepth 6 \( -name .git -type d -o -name .git -type f \) 2>/dev/null) +# Also add workspace root as a catch-all if ! git config --global --add safe.directory "${WORKSPACE_ROOT:-/workspaces}" 2>/dev/null; then echo "[setup] WARNING: Could not configure git safe.directory — git operations may show 'dubious ownership' errors" fi From a9ed933bb202e85641738495f19ab10a6d7f8b71 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 07:52:51 +0000 Subject: [PATCH 24/27] ci: fix CI failures on feat/wsl-performance-easy-wins - Remove npm cache config from test/lint jobs (container has no lockfile) - Skip WSL .wslconfig fix test on non-Linux platforms (/proc/meminfo unavailable) --- .github/workflows/ci.yml | 6 ------ cli/tests/doctor.test.ts | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 944756e..d682e8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,6 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 18 - cache: npm - cache-dependency-path: container/package-lock.json - run: npm test working-directory: container @@ -28,10 +26,6 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 18 - cache: npm - cache-dependency-path: container/package-lock.json - - run: npm ci - working-directory: container - run: npx @biomejs/biome check setup.js test.js working-directory: container diff --git a/cli/tests/doctor.test.ts b/cli/tests/doctor.test.ts index eea2585..6a32d27 100644 --- a/cli/tests/doctor.test.ts +++ b/cli/tests/doctor.test.ts @@ -469,7 +469,7 @@ describe("checkWslConfig", () => { expect(result!.message).toMatch(/RAM|meminfo/i); }); - test("includes fix action with .wslconfig generation", async () => { + test.skipIf(process.platform !== "linux")("includes fix action with .wslconfig generation", async () => { const result = await checkWslConfig(true); expect(result).not.toBeNull(); expect(result!.fix).toBeDefined(); From c8e68450e7630542dba0f80eb2bf8119dd866a02 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 08:14:59 +0000 Subject: [PATCH 25/27] fix(ci): template literal for biome lint, normalize mount paths for Windows - Use template literal in setup.js to satisfy biome linter - Normalize stored mount paths to forward slashes for cross-platform consistency --- cli/src/commands/mount/add.ts | 2 +- container/setup.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/commands/mount/add.ts b/cli/src/commands/mount/add.ts index 4a59d6e..2cc1b12 100644 --- a/cli/src/commands/mount/add.ts +++ b/cli/src/commands/mount/add.ts @@ -38,7 +38,7 @@ export function registerMountAddCommand(parent: Command): void { } // Convert to relative path from workspace root - const relPath = relative(workspaceRoot, absPath); + const relPath = relative(workspaceRoot, absPath).replaceAll("\\", "/"); if (relPath.startsWith("..")) { console.error( `Error: path must be within workspace root (${workspaceRoot})`, diff --git a/container/setup.js b/container/setup.js index a5d2335..9fb914f 100755 --- a/container/setup.js +++ b/container/setup.js @@ -479,7 +479,7 @@ function configApply() { if (!fs.existsSync(defaultManifest)) { console.error( - "Error: default file-manifest.json not found at " + defaultManifest, + `Error: default file-manifest.json not found at ${defaultManifest}`, ); console.error("Are you in a CodeForge project directory?"); process.exit(1); From 763231edeffc23ae981d05931591cd12212fb09d Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 08:18:21 +0000 Subject: [PATCH 26/27] fix(container): resolve remaining biome lint errors in setup.js - Use template literal instead of string concat (line 109) - Add radix parameter to parseInt calls (line 157) --- container/setup.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/container/setup.js b/container/setup.js index 9fb914f..d3f3b46 100755 --- a/container/setup.js +++ b/container/setup.js @@ -106,7 +106,7 @@ function writeChecksums(codeforgeDir, version, checksums) { }; fs.writeFileSync( path.join(checksumsDir, `${version}.json`), - JSON.stringify(data, null, "\t") + "\n", + `${JSON.stringify(data, null, "\t")}\n`, ); } @@ -154,7 +154,9 @@ function readChecksums(codeforgeDir) { .sort((a, b) => { const parse = (v) => { const m = v.replace(".json", "").match(/^(\d+)\.(\d+)\.(\d+)/); - return m ? [parseInt(m[1]), parseInt(m[2]), parseInt(m[3])] : [0, 0, 0]; + return m + ? [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)] + : [0, 0, 0]; }; const pa = parse(a); const pb = parse(b); From 20d1763dd310a9263542005f078df8a60bd734c6 Mon Sep 17 00:00:00 2001 From: AnExiledDev <696222+AnExiledDev@users.noreply.github.com> Date: Fri, 8 May 2026 08:20:20 +0000 Subject: [PATCH 27/27] fix(container): resolve all biome lint errors in setup.js and test.js --- container/setup.js | 12 +++++----- container/test.js | 59 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/container/setup.js b/container/setup.js index d3f3b46..38eaa94 100755 --- a/container/setup.js +++ b/container/setup.js @@ -591,7 +591,7 @@ function configApply() { ); if (!destAllowed) { console.log( - " Skip: " + entry.dest + " (destination outside allowed directories)", + ` Skip: ${entry.dest} (destination outside allowed directories)`, ); skipped++; continue; @@ -602,7 +602,7 @@ function configApply() { fs.mkdirSync(destDir, { recursive: true }); if (entry.overwrite === "never" && fs.existsSync(destPath)) { - console.log(" Skip: " + filename + " (exists, overwrite=never)"); + console.log(` Skip: ${filename} (exists, overwrite=never)`); skipped++; continue; } @@ -622,18 +622,18 @@ function configApply() { fs.existsSync(destPath) && mergeSettingsFile(srcPath, destPath) ) { - console.log(" Deployed: " + entry.src + " → " + destPath + " (merged)"); + console.log(` Deployed: ${entry.src} → ${destPath} (merged)`); deployed++; } else { fs.copyFileSync(srcPath, destPath); - console.log(" Deployed: " + entry.src + " → " + destPath); + console.log(` Deployed: ${entry.src} → ${destPath}`); deployed++; } } console.log(""); console.log( - "Config apply complete: " + deployed + " deployed, " + skipped + " skipped", + `Config apply complete: ${deployed} deployed, ${skipped} skipped`, ); } @@ -654,7 +654,7 @@ function mergeSettingsFile(srcPath, destPath) { } } - fs.writeFileSync(destPath, JSON.stringify(merged, null, 2) + "\n"); + fs.writeFileSync(destPath, `${JSON.stringify(merged, null, 2)}\n`); return true; } catch { return false; diff --git a/container/test.js b/container/test.js index f1fcba0..36d2d18 100755 --- a/container/test.js +++ b/container/test.js @@ -124,9 +124,14 @@ assert( "package does not publish a .codeforge defaults tree", ); -const scaffoldDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-scaffold-")); +const scaffoldDir = fs.mkdtempSync( + path.join(os.tmpdir(), "codeforge-scaffold-"), +); ensureCodeforgeScaffold(scaffoldDir); -assert(fs.existsSync(path.join(scaffoldDir, "README.md")), "scaffold writes README"); +assert( + fs.existsSync(path.join(scaffoldDir, "README.md")), + "scaffold writes README", +); assert( fs.existsSync(path.join(scaffoldDir, ".markers")), "scaffold creates marker directory", @@ -146,10 +151,19 @@ assert( "generateChecksums returns SHA-256 hex values", ); -const generatorMarkerDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-marker-")); +const generatorMarkerDir = fs.mkdtempSync( + path.join(os.tmpdir(), "codeforge-marker-"), +); execFileSync( process.execPath, - [path.join(root, ".devcontainer", "scripts", "generate-settings-profiles.js")], + [ + path.join( + root, + ".devcontainer", + "scripts", + "generate-settings-profiles.js", + ), + ], { cwd: root, stdio: "inherit", @@ -161,7 +175,9 @@ execFileSync( }, ); -const generatedDefault = readJson(path.join(generatedSettingsDir, "settings.json")); +const generatedDefault = readJson( + path.join(generatedSettingsDir, "settings.json"), +); const generatedOpus46 = readJson( path.join(generatedSettingsDir, "settings-opus-46-200k.json"), ); @@ -194,12 +210,16 @@ for (const output of [ ); } assert( - fs.existsSync(path.join(generatorMarkerDir, ".markers", "settings-generated-v3")), + fs.existsSync( + path.join(generatorMarkerDir, ".markers", "settings-generated-v3"), + ), "settings generator writes v3 marker", ); fs.rmSync(generatorMarkerDir, { recursive: true, force: true }); -const legacyProfileDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeforge-legacy-profile-")); +const legacyProfileDir = fs.mkdtempSync( + path.join(os.tmpdir(), "codeforge-legacy-profile-"), +); const legacyOverrideDir = path.join( legacyProfileDir, "claude", @@ -209,7 +229,7 @@ const legacyOverrideDir = path.join( fs.mkdirSync(legacyOverrideDir, { recursive: true }); fs.writeFileSync( path.join(legacyOverrideDir, "opus-46-200k.json"), - JSON.stringify( + `${JSON.stringify( { model: "claude-opus-4-6", autoCompactWindow: 200000, @@ -220,11 +240,18 @@ fs.writeFileSync( }, null, "\t", - ) + "\n", + )}\n`, ); execFileSync( process.execPath, - [path.join(root, ".devcontainer", "scripts", "generate-settings-profiles.js")], + [ + path.join( + root, + ".devcontainer", + "scripts", + "generate-settings-profiles.js", + ), + ], { cwd: root, stdio: "inherit", @@ -246,8 +273,12 @@ fs.rmSync(legacyProfileDir, { recursive: true, force: true }); const manifest = readJson(path.join(defaultsDir, "file-manifest.json")); assert(manifest.length > 0, "default manifest has entries"); -assert(manifest.every((entry) => entry.id), "default manifest entries have stable ids"); assert( + manifest.every((entry) => entry.id), + "default manifest entries have stable ids", +); +assert( + // biome-ignore lint/suspicious/noTemplateCurlyInString: literal env var placeholder manifest.find((entry) => entry.id === "claude.state")?.dest === "${HOME}", "Claude state deploys to home directory", ); @@ -256,6 +287,7 @@ const merged = mergeManifestEntries(manifest, [ { id: "custom.example", src: "claude/system-prompts/main.md", + // biome-ignore lint/suspicious/noTemplateCurlyInString: literal env var placeholder dest: "${CLAUDE_CONFIG_DIR}", overwrite: "if-changed", }, @@ -286,7 +318,10 @@ assert( "v3 migration script writes marker and report", ); -const setupScript = fs.readFileSync(path.join(root, ".devcontainer", "scripts", "setup.sh"), "utf8"); +const setupScript = fs.readFileSync( + path.join(root, ".devcontainer", "scripts", "setup.sh"), + "utf8", +); assert( setupScript.includes("setup-migrate-codeforge-v3.sh") && setupScript.includes("ensure-settings-generated.sh"),