diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..49599f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + backend: + name: backend (ruff · mypy · pytest) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "0.5.x" + + - name: Install project (+ dev extras) + run: uv sync --extra dev + + - name: ruff + run: uv run ruff check skillforge + + - name: mypy + run: uv run mypy skillforge + + - name: pytest + # Live tests are gated behind SKILLFORGE_LIVE_TESTS=1 and stay opt-in. + env: + SKILLFORGE_LIVE_TESTS: "0" + run: uv run pytest tests/ --tb=short + + frontend: + name: frontend (eslint · prettier · tsc · vitest) + runs-on: ubuntu-latest + timeout-minutes: 10 + defaults: + run: + working-directory: frontend + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: frontend/package-lock.json + + - name: npm install + run: npm ci + + - name: eslint + run: npm run lint + + - name: prettier + run: npm run format:check + + - name: typecheck + build + run: npm run build + + - name: vitest + run: npm run test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..57f135d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.4 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + # Formatter is opt-in per-file for now (Wave 2 will enable broadly). + # Running with `--check` would bloat this PR; leaving ruff lint + # as the gate for now. + stages: [manual] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.13.0 + hooks: + - id: mypy + name: mypy (skillforge) + files: ^skillforge/ + args: [--config-file=pyproject.toml] + additional_dependencies: + - pydantic>=2.9 + - types-PyYAML + + - repo: local + hooks: + - id: prettier + name: prettier (frontend) + entry: bash -c 'cd frontend && npx prettier --check .' + language: system + files: ^frontend/.*\.(ts|tsx|js|json|css|md)$ + pass_filenames: false + + - id: eslint + name: eslint (frontend) + entry: bash -c 'cd frontend && npx eslint .' + language: system + files: ^frontend/.*\.(ts|tsx)$ + pass_filenames: false + + - id: vitest + name: vitest (frontend) + entry: bash -c 'cd frontend && npm run test' + language: system + files: ^frontend/src/.*\.(ts|tsx)$ + pass_filenames: false + stages: [manual] # opt-in — vitest is full suite, too slow for every commit diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..2fde09f --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,5 @@ +dist +node_modules +coverage +*.min.js +*.min.css diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 0000000..f9f4161 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "printWidth": 100, + "singleQuote": false, + "trailingComma": "all", + "semi": true, + "arrowParens": "always", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..770134d --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,36 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import prettier from "eslint-config-prettier"; + +export default tseslint.config( + { ignores: ["dist", "node_modules", "coverage"] }, + { + files: ["**/*.{ts,tsx}"], + extends: [js.configs.recommended, ...tseslint.configs.recommended, prettier], + languageOptions: { + ecmaVersion: 2022, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + + // Clean-code doc enforcement (see docs/clean-code.md §8 React/TS) + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + "no-console": ["warn", { allow: ["warn", "error"] }], + eqeqeq: ["error", "always", { null: "ignore" }], + "prefer-const": "error", + }, + }, +); diff --git a/frontend/index.html b/frontend/index.html index b2c37a2..38daf63 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,26 +3,38 @@ - - + + SKLD.run — Evolve Claude Agent Skills - + - + - + - + @@ -34,7 +46,7 @@ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8586b82..5f7ccf0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,15 +19,24 @@ "remark-gfm": "^4.0.1" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@testing-library/react": "^16.3.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "globals": "^15.14.0", "jsdom": "^25.0.1", "postcss": "^8.4.47", + "prettier": "^3.4.2", + "prettier-plugin-tailwindcss": "^0.6.9", "tailwindcss": "^3.4.14", "typescript": "^5.6.3", + "typescript-eslint": "^8.19.0", "vite": "^5.4.10", "vitest": "^2.1.9" } @@ -863,6 +872,229 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1551,6 +1783,13 @@ "@types/unist": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -1604,6 +1843,301 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -1744,6 +2278,29 @@ "url": "https://opencollective.com/vitest" } }, + "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==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -1754,6 +2311,23 @@ "node": ">= 14" } }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1807,6 +2381,13 @@ "dev": true, "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==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -1882,6 +2463,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.10.17", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.17.tgz", @@ -1908,6 +2496,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1979,6 +2578,16 @@ "node": ">= 0.4" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -2037,6 +2646,39 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -2134,6 +2776,26 @@ "node": ">=6" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2167,6 +2829,13 @@ "node": ">= 6" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2187,6 +2856,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2415,6 +3099,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2640,6 +3331,199 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/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", @@ -2660,6 +3544,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -2682,6 +3576,13 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-equals": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", @@ -2721,6 +3622,20 @@ "node": ">= 6" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -2731,6 +3646,19 @@ "reusify": "^1.0.4" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2744,6 +3672,44 @@ "node": ">=8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -2862,6 +3828,19 @@ "node": ">=10.13.0" } }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2875,6 +3854,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -3021,6 +4010,43 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", @@ -3161,6 +4187,13 @@ "dev": true, "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -3177,6 +4210,19 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsdom": { "version": "25.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", @@ -3231,6 +4277,27 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3244,6 +4311,30 @@ "node": ">=6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -3264,12 +4355,35 @@ "dev": true, "license": "MIT" }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4230,6 +5344,19 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4267,6 +5394,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.37", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", @@ -4310,6 +5444,69 @@ "node": ">= 6" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -4348,6 +5545,26 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -4575,6 +5792,119 @@ "dev": true, "license": "MIT" }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", + "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -4949,6 +6279,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -5081,6 +6421,29 @@ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -5136,6 +6499,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/style-to-js": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", @@ -5177,6 +6553,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -5435,6 +6824,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -5442,6 +6844,19 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -5456,6 +6871,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -5574,6 +7013,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5841,6 +7290,22 @@ "node": ">=18" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -5858,6 +7323,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ws": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", @@ -5904,6 +7379,19 @@ "dev": true, "license": "ISC" }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 59880e4..dbd9de4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,12 @@ "dev": "vite", "build": "tsc -b && vite build", "preview": "vite preview", - "test": "vitest run" + "test": "vitest run", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", + "typecheck": "tsc -b --noEmit" }, "dependencies": { "@types/diff": "^7.0.2", @@ -21,15 +26,24 @@ "remark-gfm": "^4.0.1" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@testing-library/react": "^16.3.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "globals": "^15.14.0", "jsdom": "^25.0.1", "postcss": "^8.4.47", + "prettier": "^3.4.2", + "prettier-plugin-tailwindcss": "^0.6.9", "tailwindcss": "^3.4.14", "typescript": "^5.6.3", + "typescript-eslint": "^8.19.0", "vite": "^5.4.10", "vitest": "^2.1.9" } diff --git a/frontend/src/components/AgentRegistry.tsx b/frontend/src/components/AgentRegistry.tsx index 3df5266..149498b 100644 --- a/frontend/src/components/AgentRegistry.tsx +++ b/frontend/src/components/AgentRegistry.tsx @@ -50,10 +50,7 @@ export default function AgentRegistry() { }, []); const completed = useMemo( - () => - (runs ?? []).filter( - (r) => r.status === "complete" && r.id !== SEED_RUN_ID, - ), + () => (runs ?? []).filter((r) => r.status === "complete" && r.id !== SEED_RUN_ID), [runs], ); @@ -68,9 +65,7 @@ export default function AgentRegistry() { if (modeFilter !== "all") list = list.filter((r) => r.mode === modeFilter); if (search.trim()) { const q = search.toLowerCase(); - list = list.filter( - (r) => r.specialization.toLowerCase().includes(q) || r.id.includes(q), - ); + list = list.filter((r) => r.specialization.toLowerCase().includes(q) || r.id.includes(q)); } const sorted = [...list]; if (sortKey === "fitness") { @@ -89,8 +84,7 @@ export default function AgentRegistry() { const filteredSeeds = useMemo(() => { let list = seeds ?? []; - if (categoryFilter !== "all") - list = list.filter((s) => s.category === categoryFilter); + if (categoryFilter !== "all") list = list.filter((s) => s.category === categoryFilter); if (search.trim()) { const q = search.toLowerCase(); list = list.filter( @@ -115,9 +109,8 @@ export default function AgentRegistry() { Skill Registry

- Browse curated Gen 0 Skills and completed evolution runs. Deploy - any Skill directly or fork-and-evolve it with your own - specialization. + Browse curated Gen 0 Skills and completed evolution runs. Deploy any Skill directly or + fork-and-evolve it with your own specialization.

@@ -133,11 +126,7 @@ export default function AgentRegistry() { /> - {error && ( -
- {error} -
- )} + {error &&
{error}
} {/* ─── Curated Seeds Section ─────────────────────────────────────── */}
@@ -146,12 +135,10 @@ export default function AgentRegistry() {

✦ Curated Library

-

- Gen 0 Skills -

+

Gen 0 Skills

- Production-ready Skills you can deploy immediately or fork as the - starting point for evolution. + Production-ready Skills you can deploy immediately or fork as the starting point for + evolution.

@@ -209,7 +196,7 @@ export default function AgentRegistry() { {seed.traits.slice(0, 3).map((t) => ( {t} @@ -219,7 +206,7 @@ export default function AgentRegistry() {
View @@ -243,9 +230,7 @@ export default function AgentRegistry() {

Completed Runs

-

- Community Evolutions -

+

Community Evolutions

{completed.length} runs diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index c067298..701fe45 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -201,10 +201,7 @@ export default function AppShell() { GitHub - + /llms.txt diff --git a/frontend/src/components/AtomicLineageView.tsx b/frontend/src/components/AtomicLineageView.tsx index 21f71d0..1660f96 100644 --- a/frontend/src/components/AtomicLineageView.tsx +++ b/frontend/src/components/AtomicLineageView.tsx @@ -17,9 +17,7 @@ interface AtomicLineageViewProps { function parseCompositeSections(compositeMd: string): Map { const out = new Map(); // Strip frontmatter if present. - const fmStripped = - compositeMd.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/)?.[1] ?? - compositeMd; + const fmStripped = compositeMd.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/)?.[1] ?? compositeMd; const lines = fmStripped.split("\n"); let currentHeading: string | null = null; let currentBody: string[] = []; @@ -27,10 +25,7 @@ function parseCompositeSections(compositeMd: string): Map { const m = line.match(/^##\s+(.+)$/); if (m) { if (currentHeading) { - out.set( - normalizeHeading(currentHeading), - currentBody.join("\n").trim(), - ); + out.set(normalizeHeading(currentHeading), currentBody.join("\n").trim()); } currentHeading = m[1]; currentBody = []; @@ -93,11 +88,7 @@ function stripFrontmatter(md: string): string { * SKILL.md inline (no fake diff). A user can see exactly what each parent * contributed without being told lies about what "changed." */ -export default function AtomicLineageView({ - nodes, - edges, - genomes, -}: AtomicLineageViewProps) { +export default function AtomicLineageView({ nodes, edges, genomes }: AtomicLineageViewProps) { const [expandedId, setExpandedId] = useState(null); // Partition nodes into composite (generation 1, engineer_composite) and @@ -118,9 +109,7 @@ export default function AtomicLineageView({ return { composite: undefined, parents: [] as LineageNode[] }; } const parentIds = new Set( - edges - .filter((e) => e.child_id === composite!.id) - .map((e) => e.parent_id), + edges.filter((e) => e.child_id === composite!.id).map((e) => e.parent_id), ); const parentNodes = nodes.filter((n) => parentIds.has(n.id)); return { composite, parents: parentNodes }; @@ -135,9 +124,7 @@ export default function AtomicLineageView({ // side-by-side "what this parent contributed" view when a parent card is // clicked. The composite genome is the one with meta_strategy === 'engineer_composite'. const compositeSections = useMemo(() => { - const compositeGenome = genomes.find( - (g) => g.meta_strategy === "engineer_composite", - ); + const compositeGenome = genomes.find((g) => g.meta_strategy === "engineer_composite"); if (!compositeGenome) return new Map(); return parseCompositeSections(compositeGenome.skill_md_content); }, [genomes]); @@ -195,27 +182,23 @@ export default function AtomicLineageView({

- Atomic composites are assembled{" "} - from many independent parents, not mutated from one. - Running a line-by-line diff between a parent (e.g. the - heex-and-verified-routes variant) and the composite would produce - nonsense — the parent's ~30 lines all appear "removed" and the - composite's ~300 lines all appear "added", because they're about - completely different topics. The traditional Lineage Diff Viewer - is reserved for molecular{" "} - mutation chains where parent → child is a real edit. + Atomic composites are assembled from many + independent parents, not mutated from one. Running a line-by-line diff between + a parent (e.g. the heex-and-verified-routes variant) and the composite would produce + nonsense — the parent's ~30 lines all appear "removed" and the composite's ~300 lines + all appear "added", because they're about completely different topics. The traditional + Lineage Diff Viewer is reserved for{" "} + molecular mutation chains where parent → + child is a real edit.

Instead, this view shows a{" "} - contribution view: - click a parent card below to see its SKILL.md side-by-side with - the matching section in the composite. The composite's body is - organized by dimension — the heex-routes parent contributes the - ## HEEx + Verified Routes{" "} - section, the streams parent contributes the{" "} - ## Streams + Collections{" "} - section, and so on. You can read both sides and see exactly - what survived into the final skill. + contribution view: click a parent card + below to see its SKILL.md side-by-side with the matching section in the composite. The + composite's body is organized by dimension — the heex-routes parent contributes the + ## HEEx + Verified Routes section, the streams parent + contributes the ## Streams + Collections section, and + so on. You can read both sides and see exactly what survived into the final skill.

@@ -231,9 +214,7 @@ export default function AtomicLineageView({ {compositeGenome?.frontmatter && typeof compositeGenome.frontmatter === "object" && "name" in compositeGenome.frontmatter - ? String( - (compositeGenome.frontmatter as { name: string }).name, - ) + ? String((compositeGenome.frontmatter as { name: string }).name) : composite.id}

@@ -274,9 +255,7 @@ export default function AtomicLineageView({ }`} >
- - {dim} - + {dim} {isFoundation && ( FND @@ -332,9 +311,8 @@ export default function AtomicLineageView({ ) : (

- No matching section found in the composite body for - this parent's dimension. The parent may have - contributed cross-cutting rules rather than a single + No matching section found in the composite body for this parent's dimension. + The parent may have contributed cross-cutting rules rather than a single named section.

)} @@ -343,10 +321,9 @@ export default function AtomicLineageView({

Top: the parent variant's full SKILL.md. Bottom: the matching{" "} - ##{" "} - section from the composite where this parent's guidance was - distilled. Sections may have been compressed, reworded, or - merged during Engineer assembly. + ## section from the + composite where this parent's guidance was distilled. Sections may have been + compressed, reworded, or merged during Engineer assembly.

) : ( @@ -355,14 +332,13 @@ export default function AtomicLineageView({ Contribution view

- Select a parent from the list on the left to see its - SKILL.md alongside the matching section in the composite. + Select a parent from the list on the left to see its SKILL.md alongside the matching + section in the composite.

)} - ); } diff --git a/frontend/src/components/AtomicRunDetail.tsx b/frontend/src/components/AtomicRunDetail.tsx index cab69b4..df386ea 100644 --- a/frontend/src/components/AtomicRunDetail.tsx +++ b/frontend/src/components/AtomicRunDetail.tsx @@ -95,7 +95,7 @@ export default function AtomicRunDetail({ useEffect(() => { if (hasPropDims) return; fetch(`/api/runs/${runId}/dimensions`) - .then((r) => r.ok ? r.json() as Promise : []) + .then((r) => (r.ok ? (r.json() as Promise) : [])) .then(setDimensionsFetched) .catch(() => {}); }, [runId, hasPropDims]); @@ -205,10 +205,7 @@ export default function AtomicRunDetail({ }, [variants]); const strippedSpecialization = useMemo( - () => - runDetail.specialization - .replace(/\s*\[(mock|seed)_v[a-f0-9]+\]\s*/gi, " ") - .trim(), + () => runDetail.specialization.replace(/\s*\[(mock|seed)_v[a-f0-9]+\]\s*/gi, " ").trim(), [runDetail.specialization], ); @@ -226,9 +223,7 @@ export default function AtomicRunDetail({ const totalCost = report?.metadata.total_cost_usd ?? runDetail.total_cost_usd; const durationMin = - report?.metadata.duration_sec != null - ? (report.metadata.duration_sec / 60).toFixed(1) - : null; + report?.metadata.duration_sec != null ? (report.metadata.duration_sec / 60).toFixed(1) : null; const genomes = report?.skill_genomes ?? []; return ( @@ -242,9 +237,7 @@ export default function AtomicRunDetail({

{report?.taxonomy?.family_label ?? runDetail.specialization}

-

- {strippedSpecialization} -

+

{strippedSpecialization}

{runDetail.status} @@ -255,8 +248,7 @@ export default function AtomicRunDetail({ "This run has 1 generation. A re-evolution would produce Gen 2 seeded by this Gen 1 composite + newly-spawned alternatives." } > - Gen {competitionScores?.generation ?? 1} /{" "} - {competitionScores?.total_generations ?? 1} + Gen {competitionScores?.generation ?? 1} / {competitionScores?.total_generations ?? 1} {variants?.length ?? "—"} dimensions @@ -287,9 +279,7 @@ export default function AtomicRunDetail({

Baseline:{" "} - - {runDetail.baseline_fitness.toFixed(3)} - + {runDetail.baseline_fitness.toFixed(3)}

{runDetail.best_fitness != null && runDetail.baseline_fitness > 0 && (

@@ -313,9 +303,7 @@ export default function AtomicRunDetail({ )}

)} -

- ${totalCost.toFixed(2)} -

+

${totalCost.toFixed(2)}

- - Preview → - + Preview →
@@ -348,9 +334,8 @@ export default function AtomicRunDetail({ report={report} seedWinnerCount={seedWinnerDimensions.size} perfectFitnessCount={ - variants?.filter( - (v) => v.tier === "capability" && v.fitness_score >= 0.999, - ).length ?? 0 + variants?.filter((v) => v.tier === "capability" && v.fitness_score >= 0.999).length ?? + 0 } /> @@ -364,7 +349,7 @@ export default function AtomicRunDetail({ )} {/* Sticky tab bar */} -
+
{TABS.map((tab) => (
)} - {activeTab === "tests" && report && ( - - )} + {activeTab === "tests" && report && } {activeTab === "narrative" && report && ( + )} {activeTab === "lineage" && !lineage && (

Loading lineage…

@@ -476,9 +457,7 @@ export default function AtomicRunDetail({ challenges={report.challenges} learningLog={runDetail.learning_log ?? report.learning_log} runId={runId} - familyLabel={ - report.taxonomy?.family_label ?? runDetail.specialization - } + familyLabel={report.taxonomy?.family_label ?? runDetail.specialization} /> )} {activeTab === "package" && !report && ( @@ -488,9 +467,7 @@ export default function AtomicRunDetail({ {/* Error fallback */} {reportError && ( -

- Report endpoint error: {reportError} -

+

Report endpoint error: {reportError}

)}
); @@ -525,8 +502,7 @@ function DimensionsOverview({ const capabilities = dimensions.filter((d) => d.tier === "capability"); const avgFitness = - dimensions.reduce((sum, d) => sum + (d.fitness_score ?? 0), 0) / - Math.max(dimensions.length, 1); + dimensions.reduce((sum, d) => sum + (d.fitness_score ?? 0), 0) / Math.max(dimensions.length, 1); return (
@@ -536,9 +512,7 @@ function DimensionsOverview({

Dimensions

-

- {dimensions.length} -

+

{dimensions.length}

{foundations.length} foundation · {capabilities.length} capability

@@ -570,13 +544,10 @@ function DimensionsOverview({ }`} > {bestFitness > baselineFitness ? "+" : ""} - {(((bestFitness - baselineFitness) / baselineFitness) * 100).toFixed(0)} - % + {(((bestFitness - baselineFitness) / baselineFitness) * 100).toFixed(0)}%

) : ( -

- — -

+

)}
@@ -587,8 +558,8 @@ function DimensionsOverview({ Per-Dimension Fitness vs Baseline

- Each dimension's evolved fitness compared to raw Sonnet on the same challenges. - Copper = skill fitness. Gray = raw Sonnet baseline. + Each dimension's evolved fitness compared to raw Sonnet on the same challenges. Copper = + skill fitness. Gray = raw Sonnet baseline.

@@ -599,11 +570,7 @@ function DimensionsOverview({ Foundation

{foundations.map((d) => ( - + ))} )} @@ -615,11 +582,7 @@ function DimensionsOverview({ Capabilities

{capabilities.map((d) => ( - + ))} )} @@ -633,7 +596,8 @@ function DimensionsOverview({ Benchmark Tier Breakdown

- Raw Sonnet performance by challenge difficulty tier on {benchDetail.total_challenges} challenges. + Raw Sonnet performance by challenge difficulty tier on {benchDetail.total_challenges}{" "} + challenges.

@@ -648,16 +612,18 @@ function DimensionsOverview({ {benchDetail.tiers.map((t) => ( - - + ))} - - - - + + + +
+
{t.tier} @@ -683,21 +649,12 @@ function DimensionsOverview({ ); } -function DimensionBar({ - dim, - baseline, -}: { - dim: DimensionStatus; - baseline?: number; -}) { +function DimensionBar({ dim, baseline }: { dim: DimensionStatus; baseline?: number }) { const fitness = dim.fitness_score ?? 0; const maxVal = Math.max(fitness, baseline ?? 0, 0.01); const fitnessPct = (fitness / maxVal) * 100; const baselinePct = baseline != null ? (baseline / maxVal) * 100 : 0; - const lift = - baseline != null && baseline > 0 - ? ((fitness - baseline) / baseline) * 100 - : null; + const lift = baseline != null && baseline > 0 ? ((fitness - baseline) / baseline) * 100 : null; return (
@@ -719,9 +676,7 @@ function DimensionBar({ />
- - {fitness.toFixed(3)} - + {fitness.toFixed(3)} {lift != null && ( = { +const STATUS_STYLES: Record = { pending: { dot: "bg-surface-container-high", label: "text-on-surface-dim", @@ -30,11 +27,7 @@ const STATUS_STYLES: Record< }, }; -export default function AtomicSidebar({ - runId, - dimensions, - activeDimension, -}: AtomicSidebarProps) { +export default function AtomicSidebar({ runId, dimensions, activeDimension }: AtomicSidebarProps) { const completed = dimensions.filter((d) => d.status === "complete").length; const total = dimensions.length; const foundations = dimensions.filter((d) => d.tier === "foundation"); @@ -47,9 +40,7 @@ export default function AtomicSidebar({

Atomic Evolution

-

- {runId.slice(0, 20)} -

+

{runId.slice(0, 20)}

@@ -77,11 +68,7 @@ export default function AtomicSidebar({ Foundation

{foundations.map((dim) => ( - + ))}
)} @@ -93,11 +80,7 @@ export default function AtomicSidebar({ Capabilities

{capabilities.map((dim) => ( - + ))}
)} @@ -116,32 +99,19 @@ export default function AtomicSidebar({ ); } -function DimensionRow({ - dim, - isActive, -}: { - dim: DimensionStatus; - isActive: boolean; -}) { +function DimensionRow({ dim, isActive }: { dim: DimensionStatus; isActive: boolean }) { const styles = STATUS_STYLES[dim.status]; const label = dim.dimension.replace(/-/g, " "); return (
-
+
-

+

{label}

@@ -151,9 +121,7 @@ function DimensionRow({ )} {dim.status === "running" && ( - - live - + live )}
); diff --git a/frontend/src/components/BibleBrowser.tsx b/frontend/src/components/BibleBrowser.tsx index 89757ae..041522a 100644 --- a/frontend/src/components/BibleBrowser.tsx +++ b/frontend/src/components/BibleBrowser.tsx @@ -46,12 +46,7 @@ export default function BibleBrowser() { const allEntries = useMemo(() => { if (!data) return []; - return [ - ...(data.books ?? []), - ...data.patterns, - ...data.findings, - ...data.anti_patterns, - ]; + return [...(data.books ?? []), ...data.patterns, ...data.findings, ...data.anti_patterns]; }, [data]); const selected = useMemo( @@ -79,18 +74,13 @@ export default function BibleBrowser() { The SKLD Bible

- Empirical knowledge about building skills for AI coding agents. - Every finding is backed by measured data from 867 controlled - experiments, not theory or intuition. + Empirical knowledge about building skills for AI coding agents. Every finding is backed + by measured data from 867 controlled experiments, not theory or intuition.

- {error && ( -
- {error} -
- )} + {error &&
{error}
}
{/* Sidebar */} @@ -140,9 +130,7 @@ export default function BibleBrowser() { {selected.filename}

- - {selected.body} - + {selected.body}
) : ( diff --git a/frontend/src/components/BreedingReport.tsx b/frontend/src/components/BreedingReport.tsx index 67396c2..3f48a8b 100644 --- a/frontend/src/components/BreedingReport.tsx +++ b/frontend/src/components/BreedingReport.tsx @@ -24,10 +24,7 @@ export default function BreedingReport({ report, lessons }: BreedingReportProps)

    {lessons.map((lesson, i) => ( -
  • +
  • {lesson}
  • diff --git a/frontend/src/components/ChallengeGallery.tsx b/frontend/src/components/ChallengeGallery.tsx index 3536647..4e0f3c7 100644 --- a/frontend/src/components/ChallengeGallery.tsx +++ b/frontend/src/components/ChallengeGallery.tsx @@ -42,9 +42,7 @@ export default function ChallengeGallery({ challenges }: ChallengeGalleryProps)

    Challenge Gallery

    -

    - No challenges recorded for this run. -

    +

    No challenges recorded for this run.

); } @@ -85,9 +83,7 @@ export default function ChallengeGallery({ challenges }: ChallengeGalleryProps) {c.difficulty}
-

- {c.prompt} -

+

{c.prompt}

))} diff --git a/frontend/src/components/CodeViewer.tsx b/frontend/src/components/CodeViewer.tsx index fb30884..e4cb3d9 100644 --- a/frontend/src/components/CodeViewer.tsx +++ b/frontend/src/components/CodeViewer.tsx @@ -12,8 +12,10 @@ Prism.languages.bash = { { pattern: /'[^']*'/, greedy: true }, ], variable: /\$(?:\w+|[!#?*@$]|\{[^}]+\})/, - keyword: /\b(?:if|then|else|elif|fi|for|while|do|done|in|case|esac|function|return|local|export|readonly|declare|typeset|unset|set|shift|exit|exec|eval|source|true|false)\b/, - builtin: /\b(?:echo|printf|read|cd|pwd|pushd|popd|test|grep|sed|awk|find|xargs|cat|head|tail|sort|uniq|wc|tr|cut|tee|mkdir|rm|cp|mv|ln|chmod|chown|curl|wget)\b/, + keyword: + /\b(?:if|then|else|elif|fi|for|while|do|done|in|case|esac|function|return|local|export|readonly|declare|typeset|unset|set|shift|exit|exec|eval|source|true|false)\b/, + builtin: + /\b(?:echo|printf|read|cd|pwd|pushd|popd|test|grep|sed|awk|find|xargs|cat|head|tail|sort|uniq|wc|tr|cut|tee|mkdir|rm|cp|mv|ln|chmod|chown|curl|wget)\b/, operator: /&&|\|\||[!=<>]=?|<<|>>|[|&;]/, function: { pattern: /\b\w+(?=\s*\()/, greedy: true }, number: /\b\d+\b/, @@ -29,7 +31,8 @@ Prism.languages.hcl = { { pattern: /\/\*[\s\S]*?\*\//, greedy: true }, ], string: { pattern: /"(?:[^"\\]|\\.)*"/, greedy: true }, - keyword: /\b(?:resource|data|variable|output|module|provider|terraform|locals|dynamic|for_each|count|depends_on|lifecycle)\b/, + keyword: + /\b(?:resource|data|variable|output|module|provider|terraform|locals|dynamic|for_each|count|depends_on|lifecycle)\b/, boolean: /\b(?:true|false)\b/, number: /\b\d+(?:\.\d+)?\b/, punctuation: /[{}[\]=]/, @@ -41,7 +44,8 @@ Prism.languages.tf = Prism.languages.hcl; // Docker Prism.languages.docker = { comment: { pattern: /#.*/, greedy: true }, - keyword: /\b(?:FROM|AS|RUN|CMD|LABEL|MAINTAINER|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ARG|ONBUILD|STOPSIGNAL|HEALTHCHECK|SHELL)\b/i, + keyword: + /\b(?:FROM|AS|RUN|CMD|LABEL|MAINTAINER|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ARG|ONBUILD|STOPSIGNAL|HEALTHCHECK|SHELL)\b/i, string: { pattern: /"(?:[^"\\]|\\.)*"/, greedy: true }, variable: /\$(?:\w+|\{[^}]+\})/, operator: /&&|\|\|/, @@ -60,10 +64,11 @@ Prism.languages.elixir = { ], atom: { pattern: /:[a-zA-Z_]\w*[?!]?/, greedy: true }, boolean: /\b(?:true|false|nil)\b/, - keyword: /\b(?:def|defp|defmodule|do|end|if|else|unless|case|cond|when|with|fn|raise|rescue|try|catch|after|for|in|and|or|not|use|import|alias|require|quote|unquote)\b/, + keyword: + /\b(?:def|defp|defmodule|do|end|if|else|unless|case|cond|when|with|fn|raise|rescue|try|catch|after|for|in|and|or|not|use|import|alias|require|quote|unquote)\b/, module: { pattern: /\b[A-Z]\w*(?:\.[A-Z]\w*)*/, greedy: true }, function: { pattern: /\b\w+(?=[?!]?\s*[(\s])/, greedy: true }, - operator: /\|>|<>|<-|->|=>|=~|~>|::|\.\.\.|&&|\|\||[!=<>]=?|[+\-*\/\\^|&]/, + operator: /\|>|<>|<-|->|=>|=~|~>|::|\.\.\.|&&|\|\||[!=<>]=?|[+\-*/\\^|&]/, number: /\b(?:0x[\da-fA-F_]+|0b[01_]+|0o[0-7_]+|\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?)\b/, punctuation: /[()[\]{},;.@#%]/, }; diff --git a/frontend/src/components/CompetitionBracket.tsx b/frontend/src/components/CompetitionBracket.tsx index b1e4c74..2aa7803 100644 --- a/frontend/src/components/CompetitionBracket.tsx +++ b/frontend/src/components/CompetitionBracket.tsx @@ -2,11 +2,7 @@ import { useMemo, useState } from "react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; -import type { - CompetitionMatch, - CompetitionScoresPayload, - RunReportGenome, -} from "../types"; +import type { CompetitionMatch, CompetitionScoresPayload, RunReportGenome } from "../types"; interface CompetitionBracketProps { scores: CompetitionScoresPayload; @@ -41,14 +37,13 @@ export default function CompetitionBracket({ // Guard against a malformed/partial payload — an older shape (pre-schema- // alignment) or a hand-constructed entry may be missing `matches`. Render // a graceful placeholder instead of crashing the whole page. - const matches = Array.isArray(scores?.matches) ? scores.matches : []; - const sortedMatches = useMemo(() => { + const matches = Array.isArray(scores?.matches) ? scores.matches : []; return [...matches].sort((a, b) => { if (a.tier !== b.tier) return a.tier === "foundation" ? -1 : 1; return b.winning_fitness - a.winning_fitness; }); - }, [matches]); + }, [scores?.matches]); // Empty / malformed payload: render the disclaimer only, with a notice. if (sortedMatches.length === 0) { @@ -58,11 +53,8 @@ export default function CompetitionBracket({ Competition

- This run has no competition bracket data available. The seed run - either pre-dates the{" "} - - [competition_scores] - {" "} + This run has no competition bracket data available. The seed run either pre-dates the{" "} + [competition_scores]{" "} learning-log format, or its payload was malformed during export.

@@ -83,38 +75,37 @@ export default function CompetitionBracket({

- Each dimension ran a 1-round{" "} - competition: the pre-existing seed variant vs. one freshly-spawned - alternative. Both variants were scored on{" "} - - {challengesPerVariant} sampled challenges - {" "} - drawn from the dimension's 108-challenge training pool (27 held-out - for future test-time evaluation). The variant with the higher{" "} + Each dimension ran a 1-round competition: + the pre-existing seed variant vs. one freshly-spawned alternative. Both variants were + scored on{" "} + {challengesPerVariant} sampled challenges{" "} + drawn from the dimension's 108-challenge training pool (27 held-out for future test-time + evaluation). The variant with the higher{" "} mean L1 fitness won.

- Scoring criteria per - challenge: the L1 scorer runs regex checks against the generated - output file. Each challenge defines{" "} - - must_contain - {" "} - required substrings,{" "} - - must_not_contain - {" "} - forbidden substrings, and a set of dimension-specific anti-pattern - detectors. The fraction of passing checks is weighted by each - check's weight, and the weighted pass rate becomes the score. + Scoring criteria per challenge: the L1 + scorer runs regex checks against the generated output file. Each challenge defines{" "} + must_contain required + substrings,{" "} + must_not_contain{" "} + forbidden substrings, and a set of dimension-specific anti-pattern detectors. The + fraction of passing checks is weighted by each check's weight, and the weighted pass + rate becomes the score.

- Limitations for this run:{" "} - this is generation{" "} + Limitations for this run: this is + generation{" "} {generation}/{totalGenerations} {" "} - (single-round). {baselineRan ? "A baseline run on the full challenge pool was completed before this competition." : "No baseline pass over the full 108-challenge pool was run before sampling."} Higher reviewer layers (L2 trigger accuracy, L3 trace, L4 comparative, L5 trait attribution) were not exercised. Real production runs would layer these on top for richer signal. + (single-round).{" "} + {baselineRan + ? "A baseline run on the full challenge pool was completed before this competition." + : "No baseline pass over the full 108-challenge pool was run before sampling."}{" "} + Higher reviewer layers (L2 trigger accuracy, L3 trace, L4 comparative, L5 trait + attribution) were not exercised. Real production runs would layer these on top for + richer signal.

@@ -122,39 +113,25 @@ export default function CompetitionBracket({ {/* Matches */} {sortedMatches.map((match) => { const isExpanded = expandedKey === match.dimension; - const winnerLabel = - match.winner_slot === 1 - ? match.variant_1_label - : match.variant_2_label; + const winnerLabel = match.winner_slot === 1 ? match.variant_1_label : match.variant_2_label; const winnerScores = - match.winner_slot === 1 - ? match.variant_1_scores - : match.variant_2_scores; + match.winner_slot === 1 ? match.variant_1_scores : match.variant_2_scores; const loserScores = - match.winner_slot === 1 - ? match.variant_2_scores - : match.variant_1_scores; - const winnerMean = - match.winner_slot === 1 ? match.variant_1_mean : match.variant_2_mean; - const loserMean = - match.winner_slot === 1 ? match.variant_2_mean : match.variant_1_mean; + match.winner_slot === 1 ? match.variant_2_scores : match.variant_1_scores; + const winnerMean = match.winner_slot === 1 ? match.variant_1_mean : match.variant_2_mean; + const loserMean = match.winner_slot === 1 ? match.variant_2_mean : match.variant_1_mean; // Find the winning genome for the expand-on-click view. const winnerGenome = genomes.find( (g) => - g.meta_strategy === "seed_pipeline_winner" && - deriveDimFromId(g.id) === match.dimension, + g.meta_strategy === "seed_pipeline_winner" && deriveDimFromId(g.id) === match.dimension, ); const rationale = buildRationale(match, winnerMean, loserMean); // Look up raw baseline scores for this match's challenges - const rawScores = match.challenge_ids.map( - (id) => rawBaselineMap[id] ?? null, - ); - const validRawScores = rawScores.filter( - (s): s is number => s !== null, - ); + const rawScores = match.challenge_ids.map((id) => rawBaselineMap[id] ?? null); + const validRawScores = rawScores.filter((s): s is number => s !== null); const rawMean = validRawScores.length > 0 ? validRawScores.reduce((a, b) => a + b, 0) / validRawScores.length @@ -169,9 +146,7 @@ export default function CompetitionBracket({ {/* Header: dimension + tier */}
-

- {match.dimension} -

+

{match.dimension}

{hasRaw && ( @@ -241,11 +214,9 @@ export default function CompetitionBracket({ {/* Rationale + drill-down toggle */}

- {winnerLabel} won - with mean fitness{" "} + {winnerLabel} won with mean fitness{" "} {winnerMean.toFixed(3)} vs{" "} - {loserMean.toFixed(3)}{" "} - (winning scores:{" "} + {loserMean.toFixed(3)} (winning scores:{" "} {winnerScores.map((s) => s.toFixed(3)).join(" / ")} @@ -258,9 +229,7 @@ export default function CompetitionBracket({ {winnerGenome && (

-

- {name} -

+

{name}

{perChallenge.map((score, i) => ( -
+
c{i + 1}
- - {score.toFixed(3)} - + {score.toFixed(3)}
))}
@@ -360,11 +322,7 @@ function SideCard({ ); } -function buildRationale( - _match: CompetitionMatch, - winnerMean: number, - loserMean: number, -): string { +function buildRationale(_match: CompetitionMatch, winnerMean: number, loserMean: number): string { const delta = winnerMean - loserMean; if (delta < 0.001) { return "Both variants scored identically across both sampled challenges — a tie. The tie-break picked the spawned variant (slot 2) by default so the Registry could show evidence of the Spawner's output."; diff --git a/frontend/src/components/CompetitorCard.tsx b/frontend/src/components/CompetitorCard.tsx index c3db9f5..f5bcd8f 100644 --- a/frontend/src/components/CompetitorCard.tsx +++ b/frontend/src/components/CompetitorCard.tsx @@ -31,11 +31,7 @@ const STATE_VARIANT: Record< error: "error", }; -const ACTIVE_STATES: ReadonlySet = new Set([ - "writing", - "testing", - "iterating", -]); +const ACTIVE_STATES: ReadonlySet = new Set(["writing", "testing", "iterating"]); export default function CompetitorCard({ competitorId, @@ -49,7 +45,7 @@ export default function CompetitorCard({ className={ "flex items-start gap-4 rounded-xl border px-5 py-4 transition-all " + (isActive - ? "border-primary/40 bg-surface-container-lowest animate-breathe-border" + ? "animate-breathe-border border-primary/40 bg-surface-container-lowest" : state === "done" ? "border-tertiary/30 bg-surface-container-lowest" : "border-outline-variant bg-surface-container-lowest hover:bg-surface-container-low") @@ -58,7 +54,7 @@ export default function CompetitorCard({
-
+

Competitor {String.fromCharCode(65 + competitorId)} @@ -76,9 +72,7 @@ export default function CompetitorCard({ skill {skillId.slice(0, 8)}

{challengeLabel && ( -

- → {challengeLabel} -

+

→ {challengeLabel}

)}
diff --git a/frontend/src/components/CompositeMarkdownView.tsx b/frontend/src/components/CompositeMarkdownView.tsx index 567c957..1b5c939 100644 --- a/frontend/src/components/CompositeMarkdownView.tsx +++ b/frontend/src/components/CompositeMarkdownView.tsx @@ -50,13 +50,9 @@ export default function CompositeMarkdownView({ Composite Skill — {bestSkillId?.slice(0, 16) ?? "—"}

{name && ( -

- {name} -

- )} - {description && ( -

{description}

+

{name}

)} + {description &&

{description}

}
) : !skillMd ? ( @@ -83,14 +79,10 @@ export default function CompositeMarkdownView({ Loading SKILL.md…
) : showRaw ? ( -
-            {skillMd}
-          
+
{skillMd}
) : (
- - {body ?? skillMd} - + {body ?? skillMd}
)}
diff --git a/frontend/src/components/EvolutionArena.tsx b/frontend/src/components/EvolutionArena.tsx index ddf9075..d601cdc 100644 --- a/frontend/src/components/EvolutionArena.tsx +++ b/frontend/src/components/EvolutionArena.tsx @@ -50,7 +50,7 @@ export default function EvolutionArena() { // Fetch bench baseline data (once) useEffect(() => { fetch("/api/bench/summary") - .then((r) => r.ok ? r.json() as Promise : null) + .then((r) => (r.ok ? (r.json() as Promise) : null)) .then((data) => { if (data?.overall) { setBenchBaseline({ @@ -68,20 +68,17 @@ export default function EvolutionArena() { if (!runId || runDetail?.status === "complete" || runDetail?.status === "failed") return; const id = setInterval(() => { fetch(`/api/runs/${runId}/dimensions`) - .then((r) => r.ok ? r.json() as Promise : []) + .then((r) => (r.ok ? (r.json() as Promise) : [])) .then(setDimensions) .catch(() => {}); }, 5000); return () => clearInterval(id); }, [runId, runDetail?.status]); - const runAlreadyDone = - runDetail?.status === "complete" || runDetail?.status === "failed"; + const runAlreadyDone = runDetail?.status === "complete" || runDetail?.status === "failed"; // Only open the WebSocket for runs that are still active - const sockState = useEvolutionSocket( - runAlreadyDone ? null : (runId ?? null), - ); + const sockState = useEvolutionSocket(runAlreadyDone ? null : (runId ?? null)); const isComplete = sockState.isComplete || runDetail?.status === "complete"; const isFailed = sockState.isFailed || runDetail?.status === "failed"; @@ -177,7 +174,9 @@ export default function EvolutionArena() { // Completed runs → AtomicRunDetail showcase if (isComplete) { - return ; + return ( + + ); } const showBreeding = !!sockState.latestBreedingReport; @@ -188,104 +187,106 @@ export default function EvolutionArena() { // --- Atomic in-progress layout --- return ( -
- - -
- {/* Header */} -
-
-

- {activeDimension - ? `Dimension ${completedDims + 1} of ${totalDims} · ${activeDimension.replace(/-/g, " ")}` - : `Evolving ${totalDims} dimensions`} -

-

- {specialization || "Atomic Evolution"} -

-
- + + +
+ {/* Header */} +
+
+

+ {activeDimension + ? `Dimension ${completedDims + 1} of ${totalDims} · ${activeDimension.replace(/-/g, " ")}` + : `Evolving ${totalDims} dimensions`} +

+

+ {specialization || "Atomic Evolution"} +

+
+ - - {isFailed ? "FAILED" : "RUNNING"} - - - {completedDims}/{totalDims} dimensions - -
+ }`} + > + + {isFailed ? "FAILED" : "RUNNING"} + + + {completedDims}/{totalDims} dimensions +
-
-
-

- Elapsed -

-

{elapsedFmt}

-

- Budget Used -

-

- ${sockState.totalCostUsd.toFixed(2)} / ${budgetCap.toFixed(2)} -

-
- {!isComplete && !isFailed && ( - - )} +
+
+
+

+ Elapsed +

+

{elapsedFmt}

+

+ Budget Used +

+

+ ${sockState.totalCostUsd.toFixed(2)} / ${budgetCap.toFixed(2)} +

+ {!isComplete && !isFailed && ( + + )}
+
- {/* Connection/error banners */} - {sockState.status === "closed" && !isComplete && !isFailed && ( -
- Connection lost. Reconnecting... -
- )} - {isFailed && ( -
- Run failed: {sockState.failureReason ?? "(no reason)"} -
- )} - -
- {/* Main column */} -
- {/* Phase status — shows what the engine is doing during long waits */} - {(() => { - const activeDim = sockState.atomicDimensions.find( - (d) => d.dimension === activeDimension, - ); - if (!activeDim?.phaseDetail) return null; - // Don't show once competitors are running - if (variantGroups.length > 0) return null; - return ( -
-
-

- {activeDim.phaseDetail} -

-
- ); - })()} + {/* Connection/error banners */} + {sockState.status === "closed" && !isComplete && !isFailed && ( +
+ Connection lost. Reconnecting... +
+ )} + {isFailed && ( +
+ Run failed: {sockState.failureReason ?? "(no reason)"} +
+ )} + +
+ {/* Main column */} +
+ {/* Phase status — shows what the engine is doing during long waits */} + {(() => { + const activeDim = sockState.atomicDimensions.find( + (d) => d.dimension === activeDimension, + ); + if (!activeDim?.phaseDetail) return null; + // Don't show once competitors are running + if (variantGroups.length > 0) return null; + return ( +
+
+

{activeDim.phaseDetail}

+
+ ); + })()} - {/* Current challenge — single card, not a list */} - {challenges.length > 0 && (() => { + {/* Current challenge — single card, not a list */} + {challenges.length > 0 && + (() => { const ch = challenges[challenges.length - 1]; return (
@@ -300,220 +301,241 @@ export default function EvolutionArena() { > {ch.difficulty} -

- {ch.prompt} -

+

{ch.prompt}

); })()} - {/* Competition */} -
-
-
-

- Competition -

-

- Baseline vs seed vs spawn — scored with 6-layer composite. + {/* Competition */} +

+
+
+

Competition

+

+ Baseline vs seed vs spawn — scored with 6-layer composite. +

+
+ + {variantGroups.length > 0 ? `${variantGroups.length} competitors` : "waiting"} + +
+
+ {variantGroups.length === 0 ? ( +
+
+

+ Generating skill variants — each variant is a complete SKILL.md package with + scripts, references, and examples. This typically takes 1-2 minutes per + dimension.

- - {variantGroups.length > 0 ? `${variantGroups.length} competitors` : "waiting"} - -
-
- {variantGroups.length === 0 ? ( -
-
-

- Generating skill variants — each variant is a complete SKILL.md package with scripts, - references, and examples. This typically takes 1-2 minutes per dimension. -

-
- ) : ( - [...variantGroups].reverse().map((g) => { - const isBaseline = g.competitorId === 0; - const labels = ["Baseline (Raw Sonnet)", "Seed (V1)", "Spawn (V2)"]; - return ( - - ); - }) - )} -
+ ) : ( + [...variantGroups].reverse().map((g) => { + const isBaseline = g.competitorId === 0; + const labels = ["Baseline (Raw Sonnet)", "Seed (V1)", "Spawn (V2)"]; + return ( + + ); + }) + )}
- - {showBreeding && ( - - )} - -
- {/* Right column: completed dimensions + judging */} -
- {/* Completed dimensions summary */} - {completedDims > 0 && ( -
-

- Completed Dimensions -

-
- {effectiveDimensions - .filter((d) => d.status === "complete") - .map((d) => ( -
- - {d.dimension.replace(/-/g, " ")} - - - {d.fitness_score?.toFixed(2) ?? "—"} - -
- ))} -
-
-
- - Avg Fitness - - - {( - effectiveDimensions - .filter((d) => d.status === "complete" && d.fitness_score != null) - .reduce((sum, d) => sum + (d.fitness_score ?? 0), 0) / - Math.max(completedDims, 1) - ).toFixed(3)} - -
-
-
- )} + {showBreeding && ( + + )} + + +
- {/* Composite Scoring */} + {/* Right column: completed dimensions + judging */} +
+ {/* Completed dimensions summary */} + {completedDims > 0 && (

- Composite Scoring -

-

- Each variant is scored through 6 layers, weighted into one composite fitness. + Completed Dimensions

- {[ - { label: "Behavioral Tests", weight: "40%", desc: "ExUnit — does the code work?" }, - { label: "Compilation", weight: "15%", desc: "mix compile — does it build?" }, - { label: "AST Quality", weight: "15%", desc: "Structure, coverage, pipes" }, - { label: "String Match", weight: "10%", desc: "L0 expected patterns" }, - { label: "Template", weight: "10%", desc: "Modern HEEx idioms" }, - { label: "Brevity", weight: "10%", desc: "Conciseness" }, - ].map((layer) => ( -
- - {layer.weight} - - {layer.label} - {layer.desc} -
- ))} + {effectiveDimensions + .filter((d) => d.status === "complete") + .map((d) => ( +
+ + {d.dimension.replace(/-/g, " ")} + + + {d.fitness_score?.toFixed(2) ?? "—"} + +
+ ))}
- {activeDimension && sockState.generations.at(-1)?.best_fitness != null && ( -
-
- - Best This Dimension - - - {sockState.generations.at(-1)!.best_fitness!.toFixed(3)} - -
+
+
+ + Avg Fitness + + + {( + effectiveDimensions + .filter((d) => d.status === "complete" && d.fitness_score != null) + .reduce((sum, d) => sum + (d.fitness_score ?? 0), 0) / + Math.max(completedDims, 1) + ).toFixed(3)} +
- )} +
+ )} - {/* Baseline Context */} -
-

- Baseline — Raw Sonnet -

-

- What Claude Sonnet scores with no skill on the same challenges. - The goal: evolved skill consistently beats baseline. -

- {benchBaseline ? ( -
-
- Raw Composite - - {benchBaseline.rawComposite?.toFixed(3) ?? "—"} - -
-
- Challenges - - {benchBaseline.challenges} - -
-
- Families - - {benchBaseline.families} - -
- {runDetail?.baseline_fitness != null && ( -
- This Family Baseline - - {runDetail.baseline_fitness.toFixed(3)} - -
- )} + {/* Composite Scoring */} +
+

+ Composite Scoring +

+

+ Each variant is scored through 6 layers, weighted into one composite fitness. +

+
+ {[ + { + label: "Behavioral Tests", + weight: "40%", + desc: "ExUnit — does the code work?", + }, + { label: "Compilation", weight: "15%", desc: "mix compile — does it build?" }, + { label: "AST Quality", weight: "15%", desc: "Structure, coverage, pipes" }, + { label: "String Match", weight: "10%", desc: "L0 expected patterns" }, + { label: "Template", weight: "10%", desc: "Modern HEEx idioms" }, + { label: "Brevity", weight: "10%", desc: "Conciseness" }, + ].map((layer) => ( +
+ + {layer.weight} + + {layer.label} + + {layer.desc} +
- ) : ( -

Loading baseline data...

- )} + ))}
+ {activeDimension && sockState.generations.at(-1)?.best_fitness != null && ( +
+
+ + Best This Dimension + + + {sockState.generations.at(-1)!.best_fitness!.toFixed(3)} + +
+
+ )} +
- {/* Per-dimension pipeline steps */} -
-

- Per-Dimension Pipeline -

-
- {[ - { step: "1", label: "Design focused challenge", done: challenges.length > 0 }, - { step: "2", label: "Spawn seed + alternative", done: variantGroups.length >= 2 }, - { step: "3", label: "Compete on challenge", done: sockState.finishedCompetitors >= 2 }, - { step: "4", label: "Score (6-layer composite)", done: sockState.currentJudgingLayer >= 5 }, - { step: "5", label: "Pick winner", done: sockState.generations.at(-1)?.status === "complete" }, - ].map((s) => ( -
- - {s.done ? "✓" : s.step} - - - {s.label} + {/* Baseline Context */} +
+

+ Baseline — Raw Sonnet +

+

+ What Claude Sonnet scores with no skill on the same challenges. The goal: evolved + skill consistently beats baseline. +

+ {benchBaseline ? ( +
+
+ Raw Composite + + {benchBaseline.rawComposite?.toFixed(3) ?? "—"} + +
+
+ Challenges + + {benchBaseline.challenges} + +
+
+ Families + + {benchBaseline.families} + +
+ {runDetail?.baseline_fitness != null && ( +
+ This Family Baseline + + {runDetail.baseline_fitness.toFixed(3)}
- ))} + )}
+ ) : ( +

Loading baseline data...

+ )} +
+ + {/* Per-dimension pipeline steps */} +
+

+ Per-Dimension Pipeline +

+
+ {[ + { step: "1", label: "Design focused challenge", done: challenges.length > 0 }, + { step: "2", label: "Spawn seed + alternative", done: variantGroups.length >= 2 }, + { + step: "3", + label: "Compete on challenge", + done: sockState.finishedCompetitors >= 2, + }, + { + step: "4", + label: "Score (6-layer composite)", + done: sockState.currentJudgingLayer >= 5, + }, + { + step: "5", + label: "Pick winner", + done: sockState.generations.at(-1)?.status === "complete", + }, + ].map((s) => ( +
+ + {s.done ? "✓" : s.step} + + + {s.label} + +
+ ))}
- ); +
+ ); } diff --git a/frontend/src/components/EvolutionCard.tsx b/frontend/src/components/EvolutionCard.tsx index ddc9fbf..dbcfa2a 100644 --- a/frontend/src/components/EvolutionCard.tsx +++ b/frontend/src/components/EvolutionCard.tsx @@ -18,10 +18,7 @@ const STATUS_LABEL: Record = { failed: "FAILED", }; -const STATUS_VARIANT: Record< - RunStatus, - "running" | "success" | "error" | "neutral" -> = { +const STATUS_VARIANT: Record = { pending: "neutral", running: "running", complete: "success", @@ -47,9 +44,7 @@ export default function EvolutionCard({ {STATUS_LABEL[status]}
- - {id.slice(0, 8)} - + {id.slice(0, 8)}

diff --git a/frontend/src/components/EvolutionDashboard.tsx b/frontend/src/components/EvolutionDashboard.tsx index 34b4b42..6049317 100644 --- a/frontend/src/components/EvolutionDashboard.tsx +++ b/frontend/src/components/EvolutionDashboard.tsx @@ -69,9 +69,7 @@ export default function EvolutionDashboard() { const completed = runs?.filter((r) => r.status === "complete") ?? []; const avgFitness = completed.length > 0 - ? completed - .map((r) => r.best_fitness ?? 0) - .reduce((a, b) => a + b, 0) / completed.length + ? completed.map((r) => r.best_fitness ?? 0).reduce((a, b) => a + b, 0) / completed.length : 0; const totalSpent = (runs ?? []).reduce((sum, r) => sum + r.total_cost_usd, 0); @@ -88,13 +86,11 @@ export default function EvolutionDashboard() {

Evolve Agent Skills
- Through{" "} - Natural Selection + Through Natural Selection

- Deploy autonomous populations into adversarial environments. - Watch them compete, mutate, and survive to forge the ultimate - cognitive skillsets. + Deploy autonomous populations into adversarial environments. Watch them compete, + mutate, and survive to forge the ultimate cognitive skillsets.

@@ -117,28 +113,16 @@ export default function EvolutionDashboard() {

- - Bench Challenges - - - 867 - + Bench Challenges + 867
- - Skill Families - - - 7 - + Skill Families + 7
- - Scoring Layers - - - 6 - + Scoring Layers + 6
Your Runs @@ -180,11 +164,9 @@ export default function EvolutionDashboard() { Backed by Research

- SKLD didn't emerge from a vacuum. The techniques below come from - active ML and applied-AI research; SKLD composes them for a - specific artifact (Claude Agent Skills) with a controlled - benchmark. Each source is mapped to what we took and what we - didn't. + SKLD didn't emerge from a vacuum. The techniques below come from active ML and + applied-AI research; SKLD composes them for a specific artifact (Claude Agent Skills) + with a controlled benchmark. Each source is mapped to what we took and what we didn't.

- Proved population-based evolution of natural-language prompts - outperforms hand-tuning. SKLD's evolution loop skeleton. + Proved population-based evolution of natural-language prompts outperforms hand-tuning. + SKLD's evolution loop skeleton.

Guo et al. · arXiv:2309.08532 ↗ @@ -232,9 +214,8 @@ export default function EvolutionDashboard() { Reflective mutation + Pareto selection

- Trace-informed diagnosis before mutation, multi-objective - Pareto fronts. The two techniques that make evolution - intelligent instead of a random walk. + Trace-informed diagnosis before mutation, multi-objective Pareto fronts. The two + techniques that make evolution intelligent instead of a random walk.

Agrawal et al. · github.com/gepa-ai ↗ @@ -255,9 +236,8 @@ export default function EvolutionDashboard() { Joint multi-component optimization

- Mutating one agent component (a prompt, a tool, a parameter) - often requires matching changes elsewhere. SKLD applies this - to the interdependent parts of a SKILL.md. + Mutating one agent component (a prompt, a tool, a parameter) often requires matching + changes elsewhere. SKLD applies this to the interdependent parts of a SKILL.md.

TurinTech · arXiv:2512.09108 ↗ @@ -278,9 +258,8 @@ export default function EvolutionDashboard() { Persistent learning log + multi-parent crossover

- Accumulated lessons inject into every mutation prompt, so the - population never re-discovers failures it already explored. - SKLD promotes these to the public Bible. + Accumulated lessons inject into every mutation prompt, so the population never + re-discovers failures it already explored. SKLD promotes these to the public Bible.

Imbue Research · imbue.com ↗ @@ -301,9 +280,9 @@ export default function EvolutionDashboard() { Trigger accuracy + A/B comparator

- Skills have two reliability problems: activation (does it - trigger on the right queries?) and execution (does it produce - good output?). SKLD's L2 and L4 layers borrow this directly. + Skills have two reliability problems: activation (does it trigger on the right + queries?) and execution (does it produce good output?). SKLD's L2 and L4 layers borrow + this directly.

Anthropic · claude.com/blog ↗ @@ -324,9 +303,8 @@ export default function EvolutionDashboard() { Trace-based behavioral verification

- You can't assert output == expected for LLM - behavior. Execution traces answer what Claude did, - which is what SKLD's L3 layer scores against. + You can't assert output == expected for LLM behavior. Execution traces + answer what Claude did, which is what SKLD's L3 layer scores against.

MLflow · mlflow.org/blog ↗ @@ -352,9 +330,7 @@ export default function EvolutionDashboard() {

-

- Recent Evolutions -

+

Recent Evolutions

Completed Runs

diff --git a/frontend/src/components/EvolutionResults.tsx b/frontend/src/components/EvolutionResults.tsx index 6b89d59..7e490bc 100644 --- a/frontend/src/components/EvolutionResults.tsx +++ b/frontend/src/components/EvolutionResults.tsx @@ -15,11 +15,7 @@ interface EvolutionResultsProps { runDetail: RunDetail | null; } -export default function EvolutionResults({ - runId, - sockState, - runDetail, -}: EvolutionResultsProps) { +export default function EvolutionResults({ runId, sockState, runDetail }: EvolutionResultsProps) { const finalGen = sockState.generations.at(-1); const bestFitness = finalGen?.best_fitness ?? runDetail?.best_fitness ?? 0; @@ -153,9 +149,7 @@ export default function EvolutionResults({ ↓ Export Agent SDK Config - - Open Export Preview → - + Open Export Preview → - - {isOpen ? "▾" : "▸"} - + {isOpen ? "▾" : "▸"} 📁 {node.name}/ @@ -94,19 +92,18 @@ function TreeNodeRow({ className={`flex w-full items-center gap-1 rounded px-1 py-1 text-left font-mono text-[0.6875rem] transition-colors ${ isSelected ? "bg-primary/10 text-primary" - : "text-on-surface-dim hover:text-on-surface hover:bg-surface-container-mid" + : "hover:bg-surface-container-mid text-on-surface-dim hover:text-on-surface" }`} style={{ paddingLeft: `${indent + 4}px` }} title={node.path} > - - {fileIcon(node.name)} - + {fileIcon(node.name)} {node.name} )} - {node.isDir && isOpen && + {node.isDir && + isOpen && node.children.map((child) => ( - - + +
diff --git a/frontend/src/components/FitnessExplainer.tsx b/frontend/src/components/FitnessExplainer.tsx index e5bc944..87e8ee7 100644 --- a/frontend/src/components/FitnessExplainer.tsx +++ b/frontend/src/components/FitnessExplainer.tsx @@ -11,24 +11,22 @@ export default function FitnessExplainer() { What does "fitness" mean?

- Fitness is the average of the L1 - deterministic checks across all sampled challenges for a - variant. Each challenge defines a list of required substrings - (must_contain) and forbidden ones - (must_not_contain); the score is the fraction of checks - that pass, weighted by the challenge's own weight. + Fitness is the average of the{" "} + L1 deterministic checks across all sampled + challenges for a variant. Each challenge defines a list of required substrings ( + must_contain) and forbidden ones (must_not_contain); the score is + the fraction of checks that pass, weighted by the challenge's own weight.

- 1.0 = every required - substring was present and zero forbidden substrings appeared.{" "} - 0.5 = half the checks - passed. The composite skill's fitness is the average across all 12 - dimension winners. + 1.0 = every required substring was present and + zero forbidden substrings appeared. 0.5 = half + the checks passed. The composite skill's fitness is the average across all 12 dimension + winners.

- Higher reviewer layers — trigger accuracy (L2), trace analysis (L3), - comparative pairwise (L4), trait attribution (L5) — are part of the - production pipeline but were not run for this showcase. + Higher reviewer layers — trigger accuracy (L2), trace analysis (L3), comparative pairwise + (L4), trait attribution (L5) — are part of the production pipeline but were not run for this + showcase.

); diff --git a/frontend/src/components/FitnessRadar.tsx b/frontend/src/components/FitnessRadar.tsx index e3d37aa..9b1fc00 100644 --- a/frontend/src/components/FitnessRadar.tsx +++ b/frontend/src/components/FitnessRadar.tsx @@ -1,10 +1,4 @@ -import { - PolarAngleAxis, - PolarGrid, - Radar, - RadarChart, - ResponsiveContainer, -} from "recharts"; +import { PolarAngleAxis, PolarGrid, Radar, RadarChart, ResponsiveContainer } from "recharts"; import { useCssVar } from "../hooks/useCssVar"; @@ -35,17 +29,8 @@ export default function FitnessRadar({ objectives }: FitnessRadarProps) { - - + +
diff --git a/frontend/src/components/InviteGate.tsx b/frontend/src/components/InviteGate.tsx index d3026a4..eff1116 100644 --- a/frontend/src/components/InviteGate.tsx +++ b/frontend/src/components/InviteGate.tsx @@ -141,13 +141,10 @@ export default function InviteGate({ onValidated }: InviteGateProps) {

🔒 Invite Only

-

- Start an Evolution Run -

+

Start an Evolution Run

- Real evolution runs consume API budget, so SKLD.run is currently - invite-only. Enter your code below, or request an invite and we'll - email you one. + Real evolution runs consume API budget, so SKLD.run is currently invite-only. Enter your + code below, or request an invite and we'll email you one.

{/* Tabs */} @@ -194,9 +191,7 @@ export default function InviteGate({ onValidated }: InviteGateProps) { autoFocus className="mt-2 w-full rounded-lg border border-outline-variant bg-surface-container-low px-4 py-2.5 font-mono text-sm text-on-surface placeholder:text-on-surface-dim/60 focus:border-primary focus:outline-none" /> - {codeError && ( -

{codeError}

- )} + {codeError &&

{codeError}

}
@@ -263,12 +257,10 @@ export default function InviteGate({ onValidated }: InviteGateProps) { className="mt-2 w-full rounded-lg border border-outline-variant bg-surface-container-low px-4 py-2.5 text-sm text-on-surface placeholder:text-on-surface-dim/60 focus:border-primary focus:outline-none" />
- {requestError && ( -

{requestError}

- )} + {requestError &&

{requestError}

}

- Submitting does not automatically grant access. We'll review - and email a code if we can onboard you. + Submitting does not automatically grant access. We'll review and email a code if we + can onboard you.

- {error && ( -
- {error} -
- )} + {error &&
{error}
} {/* Mobile entry selector — visible below lg */}
@@ -143,9 +139,7 @@ export default function JournalBrowser() { #{String(entry.number).padStart(3, "0")} - - {entry.title} - + {entry.title}
{entry.date && (

@@ -183,9 +177,7 @@ export default function JournalBrowser() { #{String(entry.number).padStart(3, "0")} - - {entry.title} - + {entry.title}

{entry.date && (

@@ -208,9 +200,7 @@ export default function JournalBrowser() { Entry #{String(selected.number).padStart(3, "0")}

{selected.date && ( -

- {selected.date} -

+

{selected.date}

)} {selected.duration && (

@@ -219,9 +209,7 @@ export default function JournalBrowser() { )}

- - {selected.body} - + {selected.body}
{/* Prev / Next navigation */}
@@ -231,26 +219,32 @@ export default function JournalBrowser() { className="group flex items-center gap-2 text-sm text-on-surface-dim transition-colors hover:text-on-surface" > - #{String(entries![selectedIndex - 1].number).padStart(3, "0")} + + #{String(entries![selectedIndex - 1].number).padStart(3, "0")} + {entries![selectedIndex - 1].title} - ) :
} + ) : ( +
+ )} {hasNext ? ( - ) :
} + ) : ( +
+ )}
) : ( -

- Select an entry from the left. -

+

Select an entry from the left.

)}
diff --git a/frontend/src/components/JudgingPipelinePill.tsx b/frontend/src/components/JudgingPipelinePill.tsx index 965eef7..5e383c7 100644 --- a/frontend/src/components/JudgingPipelinePill.tsx +++ b/frontend/src/components/JudgingPipelinePill.tsx @@ -12,11 +12,7 @@ const STATUS_CLASS: Record = { complete: "bg-tertiary/15 text-tertiary", }; -export default function JudgingPipelinePill({ - layer, - label, - status, -}: JudgingPipelinePillProps) { +export default function JudgingPipelinePill({ layer, label, status }: JudgingPipelinePillProps) { return (
-
+
{events.length === 0 ? (

- + Waiting for events from the engine...

) : ( events.map((ev, i) => ( -
+
{ev.event.toUpperCase()}: - - {formatEvent(ev)} - + {formatEvent(ev)}
)) )} diff --git a/frontend/src/components/ModeCard.tsx b/frontend/src/components/ModeCard.tsx index a89ddfa..92b402d 100644 --- a/frontend/src/components/ModeCard.tsx +++ b/frontend/src/components/ModeCard.tsx @@ -40,13 +40,9 @@ export default function ModeCard({
{icon}
-

- {title} -

+

{title}

{description}

- {selected && ( - - )} + {selected && } ); } diff --git a/frontend/src/components/OverallAssessment.tsx b/frontend/src/components/OverallAssessment.tsx index 32c50d3..b101038 100644 --- a/frontend/src/components/OverallAssessment.tsx +++ b/frontend/src/components/OverallAssessment.tsx @@ -37,53 +37,40 @@ export default function OverallAssessment({

- What this is. A - production-ready Claude Agent Skill for writing{" "} - {family}. It enforces a - component-forward architecture — parent LiveView as a thin - coordinator, LiveComponents own stateful regions — covers the{" "} - {numDims - 1} most important capability areas (HEEx templates, - function components, streams, forms, mount lifecycle, event - handlers, PubSub, navigation, auth) and ships with a 14-rule - anti-patterns catalog that includes mechanical grep-based - detectors a reviewer can run directly. + What this is. A production-ready Claude Agent + Skill for writing {family}. It enforces a + component-forward architecture — parent LiveView as a thin coordinator, LiveComponents own + stateful regions — covers the {numDims - 1} most important capability areas (HEEx + templates, function components, streams, forms, mount lifecycle, event handlers, PubSub, + navigation, auth) and ships with a 14-rule anti-patterns catalog that includes mechanical + grep-based detectors a reviewer can run directly.

- How it was built.{" "} - {numDims} dimensions of the skill were evolved in parallel. For - each, a pre-existing seed variant competed against a freshly-spawned - alternative on 2 sampled challenges. The higher-scoring variant - became the dimension's winner. An Engineer then assembled the{" "} - {numDims} winners into one coherent composite, resolving{" "} - 3 conflicts between capability stances (mount vs. - async-load discipline, event funnel vs. PubSub direct handling, - composite granularity). The final composite weighs ~20KB of - Markdown across {numDims} named sections plus the detector - catalog. + How it was built. {numDims} dimensions of the + skill were evolved in parallel. For each, a pre-existing seed variant competed against a + freshly-spawned alternative on 2 sampled challenges. The higher-scoring variant became the + dimension's winner. An Engineer then assembled the {numDims} winners into one coherent + composite, resolving 3 conflicts between capability stances (mount vs. + async-load discipline, event funnel vs. PubSub direct handling, composite granularity). + The final composite weighs ~20KB of Markdown across {numDims} named sections plus the + detector catalog.

Quality signal.{" "} - {fitness.toFixed(3)}{" "} - average fitness across{" "} - {numChallenges * 2} test runs - (L1 deterministic checks — required-substring presence + - forbidden-substring absence).{" "} - {perfectFitnessCount} of{" "} - {numDims - 1} capability dimensions hit a perfect 1.000. The{" "} - {seedWinnerCount} dimensions - where the pre-existing seed beat the spawned alternative - (heex-and-verified-routes, streams-and-collections, - navigation-patterns) flag weak scorer discrimination on those - facets — the production pipeline would layer L2 trigger accuracy, - L3 trace analysis, and L4 comparative pairwise review on top to - get richer signal. Bottom line:{" "} - - publishable as a showcase - - ; {spawnWinnerCount}/{numDims} wins for the Spawner are a healthy - signal that atomic evolution isn't just elitism. + {fitness.toFixed(3)} average fitness across{" "} + {numChallenges * 2} test runs (L1 deterministic checks + — required-substring presence + forbidden-substring absence).{" "} + {perfectFitnessCount} of {numDims - 1} capability + dimensions hit a perfect 1.000. The {seedWinnerCount}{" "} + dimensions where the pre-existing seed beat the spawned alternative + (heex-and-verified-routes, streams-and-collections, navigation-patterns) flag weak scorer + discrimination on those facets — the production pipeline would layer L2 trigger accuracy, + L3 trace analysis, and L4 comparative pairwise review on top to get richer signal. Bottom + line: publishable as a showcase;{" "} + {spawnWinnerCount}/{numDims} wins for the Spawner are a healthy signal that atomic + evolution isn't just elitism.

diff --git a/frontend/src/components/PackageExplorer.tsx b/frontend/src/components/PackageExplorer.tsx index 387ac53..e838ac2 100644 --- a/frontend/src/components/PackageExplorer.tsx +++ b/frontend/src/components/PackageExplorer.tsx @@ -73,9 +73,7 @@ export default function PackageExplorer({ // assets/*) produced by post-assembly enrichment OR by a production // engine that natively generates rich directory packages. Sorted so // directories group together visually. - const composite = genomes.find( - (g) => g.meta_strategy === "engineer_composite", - ); + const composite = genomes.find((g) => g.meta_strategy === "engineer_composite"); const supportingFiles = composite?.supporting_files ?? {}; const sortedPaths = Object.keys(supportingFiles).sort((a, b) => { // Group by top-level directory, then alphabetical within. @@ -94,9 +92,7 @@ export default function PackageExplorer({ } // 3. PACKAGE.md — metadata, synthesized. - const winnerGenomes = genomes.filter( - (g) => g.meta_strategy === "seed_pipeline_winner", - ); + const winnerGenomes = genomes.filter((g) => g.meta_strategy === "seed_pipeline_winner"); meta.push({ path: "_meta/PACKAGE.md", content: buildPackageMd({ @@ -112,9 +108,7 @@ export default function PackageExplorer({ }); // 4. REPORT.md — integration report from learning_log - const reportEntry = learningLog.find((e) => - e.startsWith("[integration_report] "), - ); + const reportEntry = learningLog.find((e) => e.startsWith("[integration_report] ")); if (reportEntry) { meta.push({ path: "_meta/REPORT.md", @@ -146,19 +140,9 @@ export default function PackageExplorer({ } return { installable: inst, metadata: meta }; - }, [ - compositeSkillMd, - genomes, - challenges, - learningLog, - runId, - familyLabel, - ]); - - const allFiles = useMemo( - () => [...installable, ...metadata], - [installable, metadata], - ); + }, [compositeSkillMd, genomes, challenges, learningLog, runId, familyLabel]); + + const allFiles = useMemo(() => [...installable, ...metadata], [installable, metadata]); // SKILL.md is the first installable file and the default selection. const [selectedPath, setSelectedPath] = useState("SKILL.md"); @@ -198,9 +182,7 @@ export default function PackageExplorer({ path: "test_fixtures/", label: "test_fixtures/ (sample input files)", kind: "optional", - present: Array.from(present).some((p) => - p.startsWith("test_fixtures/"), - ), + present: Array.from(present).some((p) => p.startsWith("test_fixtures/")), }, { path: "assets/", @@ -214,9 +196,7 @@ export default function PackageExplorer({ const isMarkdown = selectedFile?.language === "markdown"; const bodyOnly = useMemo(() => { if (!selectedFile || !isMarkdown) return ""; - const m = selectedFile.content.match( - /^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/, - ); + const m = selectedFile.content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/); return m?.[1] ?? selectedFile.content; }, [selectedFile, isMarkdown]); @@ -231,13 +211,11 @@ export default function PackageExplorer({

- {installable.length} installable{" "} - {installable.length === 1 ? "file" : "files"} + {installable.length} installable {installable.length === 1 ? "file" : "files"} {" "} - ship in the downloadable zip and are loaded by Claude at - runtime. {metadata.length} additional metadata files are - preserved for auditing the evolution process but are NOT part - of the deployable package. + ship in the downloadable zip and are loaded by Claude at runtime. {metadata.length}{" "} + additional metadata files are preserved for auditing the evolution process but are NOT + part of the deployable package.

{isMarkdown ? (
- - {bodyOnly} - + {bodyOnly}
) : ( - + )}
@@ -327,10 +300,7 @@ export default function PackageExplorer({

What this package contains vs. the Skill Authoring Constraints in{" "} - - CLAUDE.md - - . + CLAUDE.md.

{checklist.map((item) => ( @@ -345,15 +315,11 @@ export default function PackageExplorer({ > {item.present ? "✓" : "○"} -
-

- {item.label} -

+
+

{item.label}

{item.kind} ·{" "} - {item.present - ? "present in this package" - : "not generated by this run"} + {item.present ? "present in this package" : "not generated by this run"}

@@ -425,9 +391,7 @@ function FileTreeSection({ const grouped = new Map(); const dirNames: string[] = []; for (const f of files) { - const rest = stripPrefix - ? f.path.replace(new RegExp(`^${stripPrefix}`), "") - : f.path; + const rest = stripPrefix ? f.path.replace(new RegExp(`^${stripPrefix}`), "") : f.path; if (!rest.includes("/")) { top.push(f); } else { @@ -481,9 +445,7 @@ function FileTreeSection({ className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left font-mono text-[0.6875rem] text-on-surface-dim transition-colors hover:text-on-surface" style={{ paddingLeft: `${8 + indent * 12}px` }} > - - {isOpen ? "▾" : "▸"} - + {isOpen ? "▾" : "▸"} 📁 {dir}/ · {dirFiles.length} diff --git a/frontend/src/components/ParameterInput.tsx b/frontend/src/components/ParameterInput.tsx index f4d5ed8..160ed9a 100644 --- a/frontend/src/components/ParameterInput.tsx +++ b/frontend/src/components/ParameterInput.tsx @@ -26,8 +26,7 @@ export default function ParameterInput({ if (!Number.isNaN(next)) onChange(next); }; - const accentClass = - accent === "tertiary" ? "text-tertiary" : "text-on-surface"; + const accentClass = accent === "tertiary" ? "text-tertiary" : "text-on-surface"; return (
@@ -35,9 +34,7 @@ export default function ParameterInput({ {label}

- {prefix && ( - {prefix} - )} + {prefix && {prefix}}
diff --git a/frontend/src/components/PerDimensionFitnessBar.tsx b/frontend/src/components/PerDimensionFitnessBar.tsx index 322e7bc..801eccc 100644 --- a/frontend/src/components/PerDimensionFitnessBar.tsx +++ b/frontend/src/components/PerDimensionFitnessBar.tsx @@ -21,13 +21,11 @@ export default function PerDimensionFitnessBar({ seedWinnerDimensions, }: PerDimensionFitnessBarProps) { const rows = useMemo(() => { - return variants - .slice() - .sort((a, b) => { - // Foundation first, then by fitness DESC within each tier. - if (a.tier !== b.tier) return a.tier === "foundation" ? -1 : 1; - return b.fitness_score - a.fitness_score; - }); + return variants.slice().sort((a, b) => { + // Foundation first, then by fitness DESC within each tier. + if (a.tier !== b.tier) return a.tier === "foundation" ? -1 : 1; + return b.fitness_score - a.fitness_score; + }); }, [variants]); if (rows.length === 0) { @@ -53,9 +51,7 @@ export default function PerDimensionFitnessBar({
diff --git a/frontend/src/components/PipelineOverview.tsx b/frontend/src/components/PipelineOverview.tsx index 60cb09b..8c4257b 100644 --- a/frontend/src/components/PipelineOverview.tsx +++ b/frontend/src/components/PipelineOverview.tsx @@ -37,15 +37,10 @@ export default function PipelineOverview({ report }: PipelineOverviewProps) {

This run decomposed the skill into{" "} {winners} dimensions, spawned{" "} - {startingVariants} candidate variants, - competed them on{" "} - {challenges} challenges, and - assembled the winners into{" "} - one composite skill at{" "} - - {fitness.toFixed(2)} average fitness - - . + {startingVariants} candidate variants, competed + them on {challenges} challenges, and assembled + the winners into one composite skill at{" "} + {fitness.toFixed(2)} average fitness.

{/* Mini pipeline diagram */} @@ -56,18 +51,9 @@ export default function PipelineOverview({ report }: PipelineOverviewProps) { caption={`${winners} seeds + ${winners} spawns`} /> - + - +

@@ -102,9 +88,7 @@ function PipelineStage({ label, count, caption, accent }: PipelineStageProps) {

{count}

-

- {caption} -

+

{caption}

); } diff --git a/frontend/src/components/PipelineSteps.tsx b/frontend/src/components/PipelineSteps.tsx index 68fcf39..d2bdac6 100644 --- a/frontend/src/components/PipelineSteps.tsx +++ b/frontend/src/components/PipelineSteps.tsx @@ -17,22 +17,46 @@ function VisualResearch() { {/* nodes */} {[ - [20, 40], [45, 15], [45, 65], [70, 30], [70, 55], - [95, 20], [95, 45], [95, 70], + [20, 40], + [45, 15], + [45, 65], + [70, 30], + [70, 55], + [95, 20], + [95, 45], + [95, 70], ].map(([cx, cy], i) => ( - + ))} {/* edges */} {[ - [20, 40, 45, 15], [20, 40, 45, 65], [45, 15, 70, 30], - [45, 65, 70, 55], [70, 30, 95, 20], [70, 30, 95, 45], - [70, 55, 95, 45], [70, 55, 95, 70], + [20, 40, 45, 15], + [20, 40, 45, 65], + [45, 15, 70, 30], + [45, 65, 70, 55], + [70, 30, 95, 20], + [70, 30, 95, 45], + [70, 55, 95, 45], + [70, 55, 95, 70], ].map(([x1, y1, x2, y2], i) => ( - + ))} ); @@ -44,13 +68,27 @@ function VisualSelect() { return ( {heights.map((h, i) => ( - + ))} - + ); } @@ -59,20 +97,53 @@ function VisualSelect() { function VisualDecompose() { return ( - + {[20, 50, 80].map((x, i) => ( - - + + {[x - 8, x, x + 8].map((cx, j) => ( - - + + ))} @@ -93,14 +164,30 @@ function VisualChallenges() { {tiers.map((tier, ti) => Array.from({ length: tier.count }).map((_, i) => ( - + )), )} {["E", "M", "H", "L"].map((label, i) => ( - {label} + + {label} + ))} ); @@ -117,12 +204,31 @@ function VisualBaseline() { {bars.map((bar, i) => ( - {bar.label} - - + + {bar.label} + + + {bar.w}% @@ -145,12 +251,24 @@ function VisualSeed() { {files.map((f, i) => ( - - + {f.name} + fontSize="6" + fontFamily="monospace" + > + {f.name} + ))} @@ -163,17 +281,37 @@ function VisualSpawn() { {/* center seed */} - S + + S + {/* branch lines + variant dots */} {[15, 30, 45, 60].map((y, i) => ( - + - + ))} @@ -185,18 +323,56 @@ function VisualCompete() { return ( {/* left variant */} - - V1 + + + V1 + {/* VS */} - vs + + vs + {/* right variant */} - - V2 + + + V2 + {/* challenge dots */} {[28, 40, 52].map((y, i) => ( @@ -220,10 +396,23 @@ function VisualScore() { {layers.map((l, i) => ( - - + + {l.label} @@ -238,25 +427,59 @@ function VisualBreed() { {/* population dots top */} {[20, 35, 50, 65, 80, 95].map((x, i) => ( - + ))} {/* funnel lines */} - - + + {/* winners bottom */} {[35, 55, 75].map((x, i) => ( - + ))} {/* arrows */} - + - + @@ -270,24 +493,68 @@ function VisualAssemble() { {/* incoming pieces */} {[ - [10, 10], [10, 30], [10, 50], [10, 70], - [35, 10], [35, 30], [35, 50], [35, 70], + [10, 10], + [10, 30], + [10, 50], + [10, 70], + [35, 10], + [35, 30], + [35, 50], + [35, 70], ].map(([x, y], i) => ( - - + + ))} {/* composite result */} - - SKILL - .md + + + SKILL + + + .md + ); } @@ -299,23 +566,41 @@ function VisualShip() { {/* checkmarks */} {Array.from({ length: 7 }).map((_, i) => ( - - + + ))} {/* arrow up */} - - + + {/* Registry label */} - - Registry + + + Registry + ); } @@ -444,21 +729,19 @@ export default function PipelineSteps() { return (
-

- How SKLD Works -

+

How SKLD Works

The Evolution Pipeline

- From ecosystem research to a shipped, tested skill package — every - step backed by measured data from real experiments. + From ecosystem research to a shipped, tested skill package — every step backed by measured + data from real experiments.

{/* Vertical line */} -
+
{STEPS.map((step, i) => { const isVisible = visibleSteps.has(i); @@ -473,9 +756,7 @@ export default function PipelineSteps() { }} data-step-idx={i} className={`relative mb-10 transition-all duration-700 ease-out ${ - isVisible - ? "translate-y-0 opacity-100" - : "translate-y-8 opacity-0" + isVisible ? "translate-y-0 opacity-100" : "translate-y-8 opacity-0" }`} > {/* Timeline dot */} @@ -491,9 +772,7 @@ export default function PipelineSteps() { {/* Content card */}
{String(step.number).padStart(2, "0")} -

- {step.title} -

+

{step.title}

{step.isLoop && ( loop )}
-

- {step.description} -

-

- {step.metric} -

+

{step.description}

+

{step.metric}

{/* Visual illustration */}
diff --git a/frontend/src/components/PrimaryButton.tsx b/frontend/src/components/PrimaryButton.tsx index 555be8c..140be8b 100644 --- a/frontend/src/components/PrimaryButton.tsx +++ b/frontend/src/components/PrimaryButton.tsx @@ -4,18 +4,14 @@ interface PrimaryButtonProps extends ButtonHTMLAttributes { children: ReactNode; } -export default function PrimaryButton({ - children, - className = "", - ...rest -}: PrimaryButtonProps) { +export default function PrimaryButton({ children, className = "", ...rest }: PrimaryButtonProps) { return ( {showReport && ( -
- - {integrationReportMd} - +
+ {integrationReportMd}
)}
diff --git a/frontend/src/components/SeedDetailView.tsx b/frontend/src/components/SeedDetailView.tsx index 7cd7ddf..b08d471 100644 --- a/frontend/src/components/SeedDetailView.tsx +++ b/frontend/src/components/SeedDetailView.tsx @@ -37,9 +37,7 @@ export default function SeedDetailView() { const files = useMemo(() => { if (!skill) return []; - const list = [ - { path: "SKILL.md", content: skill.skill_md_content || "" }, - ]; + const list = [{ path: "SKILL.md", content: skill.skill_md_content || "" }]; if (skill.supporting_files) { for (const [path, content] of Object.entries(skill.supporting_files)) { list.push({ path, content: content as string }); @@ -55,9 +53,7 @@ export default function SeedDetailView() { if (error) { return (
-
- {error} -
+
{error}
); } @@ -71,15 +67,10 @@ export default function SeedDetailView() { } // Strip YAML frontmatter for the main markdown render - const bodyOnly = skill.skill_md_content.replace( - /^---\n[\s\S]*?\n---\n?/, - "", - ); + const bodyOnly = skill.skill_md_content.replace(/^---\n[\s\S]*?\n---\n?/, ""); // Extract frontmatter values for the sidebar - const frontmatterMatch = skill.skill_md_content.match( - /^---\n([\s\S]*?)\n---/, - ); + const frontmatterMatch = skill.skill_md_content.match(/^---\n([\s\S]*?)\n---/); const frontmatter = frontmatterMatch ? frontmatterMatch[1] : ""; const nameMatch = frontmatter.match(/^name:\s*(.+)$/m); const descMatch = frontmatter.match(/description:\s*>-\n((?:\s+.*\n?)+)/); @@ -124,7 +115,7 @@ export default function SeedDetailView() { {/* File tree sidebar (only when there are supporting files) */} {hasMultipleFiles && (
-

+

Skill Package

+
{hasMultipleFiles && (

{selectedFile} @@ -194,7 +185,7 @@ export default function SeedDetailView() {

↓ Download .zip @@ -202,7 +193,7 @@ export default function SeedDetailView() { href={`/api/runs/${runId}/export?format=skill_md`} target="_blank" rel="noreferrer" - className="block rounded-lg bg-surface-container-mid px-3 py-2 text-center text-xs text-on-surface transition-colors hover:bg-surface-container-high" + className="bg-surface-container-mid block rounded-lg px-3 py-2 text-center text-xs text-on-surface transition-colors hover:bg-surface-container-high" > ↓ Download SKILL.md @@ -210,7 +201,7 @@ export default function SeedDetailView() { href={`/api/runs/${runId}/export?format=agent_sdk_config`} target="_blank" rel="noreferrer" - className="block rounded-lg bg-surface-container-mid px-3 py-2 text-center text-xs text-on-surface transition-colors hover:bg-surface-container-high" + className="bg-surface-container-mid block rounded-lg px-3 py-2 text-center text-xs text-on-surface transition-colors hover:bg-surface-container-high" > ↓ Agent SDK Config diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index c86c6c1..f9b47d3 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -10,12 +10,7 @@ interface SidebarProps { phases: PhaseState[]; } -export default function Sidebar({ - runId, - generation, - totalGenerations, - phases, -}: SidebarProps) { +export default function Sidebar({ runId, generation, totalGenerations, phases }: SidebarProps) { return (
{/* Process flow diagram */} diff --git a/frontend/src/components/SkillContentModal.tsx b/frontend/src/components/SkillContentModal.tsx index e8585ac..ed3c9fa 100644 --- a/frontend/src/components/SkillContentModal.tsx +++ b/frontend/src/components/SkillContentModal.tsx @@ -57,7 +57,10 @@ export default function SkillContentModal({ {traits && traits.length > 0 && (
{traits.map((t) => ( - + {t} ))} @@ -72,7 +75,10 @@ export default function SkillContentModal({

{mutations.map((m) => ( - + {m} ))} @@ -82,9 +88,7 @@ export default function SkillContentModal({ {/* Mutation rationale */} {mutationRationale && ( -

- {mutationRationale} -

+

{mutationRationale}

)} {/* Supporting files list */} @@ -95,15 +99,12 @@ export default function SkillContentModal({

{supportingFiles.map((path) => ( -

+

{path}

))}
-

+

Full package available in the skill registry detail view.

@@ -115,7 +116,11 @@ export default function SkillContentModal({ SKILL.md

{skillMdContent ? ( - + ) : (

Skill content not available (demo run or skill not yet persisted). diff --git a/frontend/src/components/SkillDiffViewer.tsx b/frontend/src/components/SkillDiffViewer.tsx index 90aec2f..e6a1f0e 100644 --- a/frontend/src/components/SkillDiffViewer.tsx +++ b/frontend/src/components/SkillDiffViewer.tsx @@ -131,11 +131,9 @@ export default function SkillDiffViewer() { return diffLines(parent.skill_md_content, child.skill_md_content); }, [parent, child]); - const nodeById = (nodeId: string) => - lineage?.nodes.find((n) => n.id === nodeId); + const nodeById = (nodeId: string) => lineage?.nodes.find((n) => n.id === nodeId); - const currentEdge = - lineage && selectedIdx != null ? lineage.edges[selectedIdx] : null; + const currentEdge = lineage && selectedIdx != null ? lineage.edges[selectedIdx] : null; // Atomic-mode render path: skip the diff machinery entirely and render // the new AtomicLineageView that explains the 12→1 assembly without @@ -152,9 +150,8 @@ export default function SkillDiffViewer() { Lineage Assembly View

- Run {runId?.slice(0, 12)} · this composite was assembled from - many parents rather than mutated from one, so there's no diff - to show — browse each parent below. + Run {runId?.slice(0, 12)} · this composite was assembled from many parents rather than + mutated from one, so there's no diff to show — browse each parent below.

Diff Viewer

- Run {runId?.slice(0, 12)} · pick a parent→child transition to see - what changed. + Run {runId?.slice(0, 12)} · pick a parent→child transition to see what changed.

- {error && ( -
- {error} -
- )} + {error &&
{error}
}

{child.mutation_rationale || ( - - (no rationale recorded) - + (no rationale recorded) )}

{child.mutations.length > 0 && ( @@ -324,9 +307,7 @@ export default function SkillDiffViewer() { {lines .map((line, j) => - j < lines.length - 1 || line - ? prefix + line + "\n" - : "", + j < lines.length - 1 || line ? prefix + line + "\n" : "", ) .join("")} diff --git a/frontend/src/components/SkillExportPreview.tsx b/frontend/src/components/SkillExportPreview.tsx index a237ddb..c284cf1 100644 --- a/frontend/src/components/SkillExportPreview.tsx +++ b/frontend/src/components/SkillExportPreview.tsx @@ -11,7 +11,9 @@ export default function SkillExportPreview() { useEffect(() => { if (!runId) return; - fetch(`/api/runs/${runId}`).then((r) => r.json()).then(setRun); + fetch(`/api/runs/${runId}`) + .then((r) => r.json()) + .then(setRun); fetch(`/api/runs/${runId}/export?format=skill_md`) .then((r) => r.text()) .then(setSkillMd) @@ -44,9 +46,7 @@ export default function SkillExportPreview() {

Skill Directory

-

- Compressed Binary Structure -

+

Compressed Binary Structure

             {skillMd ? skillMd.slice(0, 800) + (skillMd.length > 800 ? "\n..." : "") : "(loading)"}
           
@@ -62,11 +62,11 @@ export default function SkillExportPreview() {

Agent SDK Config

-

- Skill Schema Definition -

+

Skill Schema Definition

-            {sdkConfig ? sdkConfig.slice(0, 800) + (sdkConfig.length > 800 ? "\n..." : "") : "(loading)"}
+            {sdkConfig
+              ? sdkConfig.slice(0, 800) + (sdkConfig.length > 800 ? "\n..." : "")
+              : "(loading)"}
           
-

- {label ?? `Variant ${letter}`} -

+

{label ?? `Variant ${letter}`}

{isControl && ( {controlLabel ?? "Control"} @@ -110,11 +106,7 @@ export default function SkillVariantCard({ {allDone @@ -152,9 +144,7 @@ export default function SkillVariantCard({ style={{ width: `${Math.min(1, s.value) * 100}%` }} />
- - {s.label} - + {s.label} {typeof s.value === "number" ? s.value.toFixed(2) : s.value} @@ -168,7 +158,10 @@ export default function SkillVariantCard({ {firstCompetitor?.traits && firstCompetitor.traits.length > 0 && (
{firstCompetitor.traits.slice(0, 3).map((t) => ( - + {t} ))} @@ -177,7 +170,7 @@ export default function SkillVariantCard({ {/* Mutation rationale */} {!isControl && firstCompetitor?.mutationRationale && ( -

+

{firstCompetitor.mutationRationale}

)} @@ -232,7 +225,8 @@ export default function SkillVariantCard({
{match?.state === "writing" && match.turn != null && ( - Turn {match.turn}{match.lastTool ? ` \u00b7 ${match.lastTool}` : ""} + Turn {match.turn} + {match.lastTool ? ` \u00b7 ${match.lastTool}` : ""} )}
@@ -251,7 +245,8 @@ export default function SkillVariantCard({ > {showCode ? "▾ Hide Output" : "▸ View Output"}{" "} - ({Object.keys(firstCompetitor!.outputFiles!).length} file{Object.keys(firstCompetitor!.outputFiles!).length > 1 ? "s" : ""}) + ({Object.keys(firstCompetitor!.outputFiles!).length} file + {Object.keys(firstCompetitor!.outputFiles!).length > 1 ? "s" : ""}) {showCode && ( @@ -264,7 +259,11 @@ export default function SkillVariantCard({ {content.split("\n").length} lines
- +
))}
diff --git a/frontend/src/components/SkldBench.tsx b/frontend/src/components/SkldBench.tsx index 0a1f2b3..86c0bfc 100644 --- a/frontend/src/components/SkldBench.tsx +++ b/frontend/src/components/SkldBench.tsx @@ -50,10 +50,9 @@ export default function SkldBench() { {overall.challenges} Elixir Challenges

- A controlled evaluation benchmark for measuring whether Claude Agent - Skills actually improve code generation. Each challenge is scored - through multiple layers: string matching, compilation, AST analysis, - and behavioral testing. + A controlled evaluation benchmark for measuring whether Claude Agent Skills actually + improve code generation. Each challenge is scored through multiple layers: string + matching, compilation, AST analysis, and behavioral testing.

@@ -75,15 +74,11 @@ export default function SkldBench() { key={item.label} className="rounded-lg bg-surface-container-lowest p-3 text-center" > -

- {item.weight} -

+

{item.weight}

{item.label}

-

- {item.desc} -

+

{item.desc}

))}
@@ -220,30 +215,23 @@ export default function SkldBench() { : "—"}
- {fam.compile_pct != null - ? `${(fam.compile_pct * 100).toFixed(0)}%` - : "—"} + {fam.compile_pct != null ? `${(fam.compile_pct * 100).toFixed(0)}%` : "—"}
Overall - {overall.challenges} - - {overall.raw_composite?.toFixed(3) ?? "—"} - Overall{overall.challenges}{overall.raw_composite?.toFixed(3) ?? "—"}

- Click a family name to see per-challenge detail, tier breakdowns, and - score distributions. + Click a family name to see per-challenge detail, tier breakdowns, and score distributions.

diff --git a/frontend/src/components/SkldBenchFamily.tsx b/frontend/src/components/SkldBenchFamily.tsx index 0e99b66..0f8e0e7 100644 --- a/frontend/src/components/SkldBenchFamily.tsx +++ b/frontend/src/components/SkldBenchFamily.tsx @@ -63,11 +63,7 @@ export default function SkldBenchFamily() { case "dimension": return a.dimension.localeCompare(b.dimension) * dir; case "compiles": - return ( - (Number(a.raw?.compiles ?? false) - - Number(b.raw?.compiles ?? false)) * - dir - ); + return (Number(a.raw?.compiles ?? false) - Number(b.raw?.compiles ?? false)) * dir; default: return 0; } @@ -107,9 +103,7 @@ export default function SkldBenchFamily() { > ← SKLD-bench -

- {data.label} -

+

{data.label}

{data.total_challenges} challenges scored with composite evaluation

@@ -134,10 +128,7 @@ export default function SkldBenchFamily() { {data.tiers.map((t) => ( - + @@ -200,7 +191,8 @@ export default function SkldBenchFamily() { Score Distribution

- How many challenges scored in each composite range — most challenges cluster around 0.5–0.8 + How many challenges scored in each composite range — most challenges cluster around + 0.5–0.8

{data.histogram.counts.map((count, i) => { @@ -315,9 +307,7 @@ export default function SkldBenchFamily() { const rawComp = c.raw?.composite ?? 0; const skillComp = c.skill?.composite; const lift = - skillComp != null && rawComp > 0 - ? (skillComp - rawComp) / rawComp - : null; + skillComp != null && rawComp > 0 ? (skillComp - rawComp) / rawComp : null; return ( - {lift != null - ? `${lift > 0 ? "+" : ""}${(lift * 100).toFixed(0)}%` - : "—"} + {lift != null ? `${lift > 0 ? "+" : ""}${(lift * 100).toFixed(0)}%` : "—"} ); @@ -390,11 +378,7 @@ function SortHeader({ const arrow = current === sortKey ? (dir === "asc" ? " ↑" : " ↓") : ""; return ( - diff --git a/frontend/src/components/SpecAssistantChat.tsx b/frontend/src/components/SpecAssistantChat.tsx index 592864a..f00fab7 100644 --- a/frontend/src/components/SpecAssistantChat.tsx +++ b/frontend/src/components/SpecAssistantChat.tsx @@ -21,10 +21,7 @@ interface SpecAssistantChatProps { onPackageReady?: (pkg: GeneratedPackage) => void; } -export default function SpecAssistantChat({ - onSpecReady, - onPackageReady, -}: SpecAssistantChatProps) { +export default function SpecAssistantChat({ onSpecReady, onPackageReady }: SpecAssistantChatProps) { const [open, setOpen] = useState(false); const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); @@ -104,10 +101,7 @@ export default function SpecAssistantChat({ message: string; final_spec?: string | null; }; - setMessages((prev) => [ - ...prev, - { role: "assistant", content: data.message }, - ]); + setMessages((prev) => [...prev, { role: "assistant", content: data.message }]); if (data.final_spec) { onSpecReady(data.final_spec); setFinalized(true); @@ -132,10 +126,7 @@ export default function SpecAssistantChat({ const submit = async () => { const text = input.trim(); if (!text || sending || finalized) return; - const next: ChatMessage[] = [ - ...messages, - { role: "user", content: text }, - ]; + const next: ChatMessage[] = [...messages, { role: "user", content: text }]; setMessages(next); setInput(""); await sendMessage(next); @@ -205,18 +196,12 @@ export default function SpecAssistantChat({
{/* Messages */} -
+
{messages.length === 0 && !sending && (

Starting conversation…

)} {messages.map((msg, i) => ( -
+
{error && ( -
- {error} -
+
{error}
)} {/* Package generation status */} diff --git a/frontend/src/components/SpecializationInput.tsx b/frontend/src/components/SpecializationInput.tsx index 1c4c21f..1f2e8cb 100644 --- a/frontend/src/components/SpecializationInput.tsx +++ b/frontend/src/components/SpecializationInput.tsx @@ -57,17 +57,13 @@ export default function SpecializationInput() { const [searchParams] = useSearchParams(); const seedParam = searchParams.get("seed"); - const [sourceMode, setSourceMode] = useState( - seedParam ? "fork" : "scratch", - ); + const [sourceMode, setSourceMode] = useState(seedParam ? "fork" : "scratch"); const [specialization, setSpecialization] = useState(""); const [populationSize, setPopulationSize] = useState(5); const [numGenerations, setNumGenerations] = useState(3); const [budget, setBudget] = useState(10); // v2.0 — Auto lets the Taxonomist decide; Atomic and Classic force the mode. - const [evolutionMode, setEvolutionMode] = useState<"auto" | "atomic" | "molecular">( - "auto", - ); + const [evolutionMode, setEvolutionMode] = useState<"auto" | "atomic" | "molecular">("auto"); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [upload, setUpload] = useState(null); @@ -107,9 +103,8 @@ export default function SpecializationInput() { : ["all"]; const visibleSeeds = - allSeeds?.filter( - (s) => seedCategoryFilter === "all" || s.category === seedCategoryFilter, - ) ?? []; + allSeeds?.filter((s) => seedCategoryFilter === "all" || s.category === seedCategoryFilter) ?? + []; const submit = async () => { setSubmitting(true); @@ -217,9 +212,7 @@ export default function SpecializationInput() {

Protocol: New Evolution

-

- Start an Evolution Run -

+

Start an Evolution Run

{/* Source mode toggle */}
@@ -297,14 +290,14 @@ export default function SpecializationInput() {

⑂ Forking from

-

- {forkedSeed.title} -

+

{forkedSeed.title}

{forkedSeed.category} - + {forkedSeed.difficulty}

@@ -403,8 +396,8 @@ export default function SpecializationInput() { ✓ Skill Package Ready

- {Object.keys(generatedPackage.supportingFiles).length + 1} files - will be used as the Gen 0 seed + {Object.keys(generatedPackage.supportingFiles).length + 1} files will be used as the + Gen 0 seed

@@ -558,8 +539,7 @@ export default function SpecializationInput() { Competitor Runs

- {competitorRuns} ({populationSize}×{numGenerations}× - {CHALLENGES_PER_GEN}) + {competitorRuns} ({populationSize}×{numGenerations}×{CHALLENGES_PER_GEN})

@@ -569,9 +549,8 @@ export default function SpecializationInput() {
{overBudget && (

- ⚠ Estimated cost exceeds your ${budget} budget cap. The run - will abort when the cap is hit — increase the cap or reduce - population/generations. + ⚠ Estimated cost exceeds your ${budget} budget cap. The run will abort when the cap + is hit — increase the cap or reduce population/generations.

)}
diff --git a/frontend/src/components/StatCard.tsx b/frontend/src/components/StatCard.tsx index 280d441..be4a137 100644 --- a/frontend/src/components/StatCard.tsx +++ b/frontend/src/components/StatCard.tsx @@ -11,29 +11,20 @@ const ACCENT_BORDER: Record, string> = { tertiary: "before:bg-tertiary", }; -export default function StatCard({ - label, - value, - hint, - accent = "primary", -}: StatCardProps) { +export default function StatCard({ label, value, hint, accent = "primary" }: StatCardProps) { return (

{label}

-

- {value} -

- {hint && ( -

{hint}

- )} +

{value}

+ {hint &&

{hint}

}
); } diff --git a/frontend/src/components/StatusGlow.tsx b/frontend/src/components/StatusGlow.tsx index 5c4d711..d4b0097 100644 --- a/frontend/src/components/StatusGlow.tsx +++ b/frontend/src/components/StatusGlow.tsx @@ -18,8 +18,7 @@ export default function StatusGlow({ className = "", pulse, }: StatusGlowProps) { - const shouldPulse = - pulse ?? (variant === "running" || variant === "warning"); + const shouldPulse = pulse ?? (variant === "running" || variant === "warning"); return ( { if (filter.domain_id && f.domain_id !== filter.domain_id) return false; if (filter.focus_id && f.focus_id !== filter.focus_id) return false; @@ -91,10 +88,7 @@ export default function TaxonomyBrowser() { .catch((err) => setError(String(err))); }, []); - const domains = useMemo( - () => (nodes ?? []).filter((n) => n.level === "domain"), - [nodes], - ); + const domains = useMemo(() => (nodes ?? []).filter((n) => n.level === "domain"), [nodes]); const focusesByDomain = useMemo(() => { const map = new Map(); @@ -123,9 +117,7 @@ export default function TaxonomyBrowser() { const isLoading = nodes == null || families == null; const hasNoFilter = - filter.domain_id == null && - filter.focus_id == null && - filter.language_id == null; + filter.domain_id == null && filter.focus_id == null && filter.language_id == null; return (
@@ -138,8 +130,8 @@ export default function TaxonomyBrowser() { Skill Taxonomy

- Browse the Domain → Focus → Language hierarchy. Pick any node to - filter the family list to variants that live underneath it. + Browse the Domain → Focus → Language hierarchy. Pick any node to filter the family list + to variants that live underneath it.

@@ -147,11 +139,7 @@ export default function TaxonomyBrowser() {
- {error && ( -
- {error} -
- )} + {error &&
{error}
} {isLoading ? (

Loading taxonomy…

@@ -173,11 +161,7 @@ export default function TaxonomyBrowser() {
    {domains.map((dom) => { - const domCount = countFamiliesUnderNode( - dom, - nodes ?? [], - families ?? [], - ); + const domCount = countFamiliesUnderNode(dom, nodes ?? [], families ?? []); const isDomActive = filter.domain_id === dom.id; const focuses = focusesByDomain.get(dom.id) ?? []; return ( @@ -205,11 +189,7 @@ export default function TaxonomyBrowser() { {isDomActive && focuses.length > 0 && (
      {focuses.map((foc) => { - const focCount = countFamiliesUnderNode( - foc, - nodes ?? [], - families ?? [], - ); + const focCount = countFamiliesUnderNode(foc, nodes ?? [], families ?? []); const isFocActive = filter.focus_id === foc.id; const languages = languagesByFocus.get(foc.id) ?? []; return ( @@ -242,8 +222,7 @@ export default function TaxonomyBrowser() { nodes ?? [], families ?? [], ); - const isLngActive = - filter.language_id === lng.id; + const isLngActive = filter.language_id === lng.id; return (