diff --git a/.github/workflows/release-please.yml b/.github/workflows/release.yml similarity index 87% rename from .github/workflows/release-please.yml rename to .github/workflows/release.yml index d07a909..d26a45a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release.yml @@ -40,23 +40,15 @@ jobs: if: ${{ steps.release.outputs.release_created }} run: pnpm install --frozen-lockfile - - name: Package skill tarballs + - name: Build release artifacts if: ${{ steps.release.outputs.release_created }} - run: | - mkdir -p dist - - for skill_dir in skills/*; do - if [ -d "${skill_dir}" ]; then - skill_name=$(basename "${skill_dir}") - tar -czf "dist/${skill_name}.tar.gz" -C skills "${skill_name}" - fi - done + run: pnpm build:release - name: Upload release assets if: ${{ steps.release.outputs.release_created }} env: GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} - run: gh release upload ${{ steps.release.outputs.tag_name }} dist/*.tar.gz + run: gh release upload ${{ steps.release.outputs.tag_name }} dist/index.json dist/*.tar.gz - name: Generate GitHub App token (supabase-community) id: generate-token-plugin diff --git a/README.md b/README.md index 1979c54..528beca 100644 --- a/README.md +++ b/README.md @@ -119,3 +119,23 @@ Each skill follows the [Agent Skills Open Standard](https://agentskills.io/): - `SKILL.md` - Required skill manifest with frontmatter (name, description, metadata) - `references/` - (Optional) Reference files for detailed documentation + +## `.well-known` discovery + +Each release uploads a `dist/index.json` alongside the skill tarballs. This index conforms to the [agent-skills `.well-known` URI spec](https://github.com/agentskills/agentskills/pull/254) (schema v0.2.0) and is consumed by `supabase.com` to serve skills at `https://supabase.com/.well-known/agent-skills/`. + +The release artifacts are built by `scripts/build-release.ts` and triggered by [Release Please](https://github.com/googleapis/release-please) on every semver release. + +### Migrating to the official GitHub Action + +[`jonathanhefner/agentskills-build-for-well-known`](https://github.com/jonathanhefner/agentskills-build-for-well-known) is intended to be transferred to the `agentskills` org and published to the GitHub Marketplace. When that happens, `scripts/build-release.ts` and the `pnpm build:release` step in `release-please.yml` can be replaced with the Action directly: + +```yaml +- name: Build release artifacts + uses: agentskills/build-for-well-known@v1 # replaces pnpm build:release + with: + skills-dir: skills + output-dir: dist +``` + +Everything else — the Release Please trigger, the upload step, the supabase-plugin dispatch — stays exactly the same. The script and the Action produce identical output, so it is a drop-in swap. diff --git a/package.json b/package.json index 459d75c..fb74166 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,18 @@ "author": "Supabase", "license": "MIT", "description": "Official Supabase agent skills", + "type": "module", "scripts": { "test": "vitest run", - "test:sanity": "vitest run test/sanity.test.ts" + "test:sanity": "vitest run test/sanity.test.ts", + "build:release": "tsx scripts/build-release.ts" }, "devDependencies": { + "@types/node": "^22.0.0", + "gray-matter": "^4.0.3", + "tar": "^7.5.15", + "tsx": "^4.21.0", + "typescript": "^6.0.0", "vitest": "^4.1.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44cb7f9..542737a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,24 @@ importers: .: devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 + tar: + specifier: ^7.5.15 + version: 7.5.15 + tsx: + specifier: ^4.21.0 + version: 4.21.0 + typescript: + specifier: ^6.0.0 + version: 6.0.3 vitest: specifier: ^4.1.4 - version: 4.1.4(vite@7.3.1) + version: 4.1.4(@types/node@22.19.17)(vite@7.3.1(@types/node@22.19.17)(tsx@4.21.0)) packages: @@ -170,6 +185,10 @@ packages: cpu: [x64] os: [win32] + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -323,6 +342,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/node@22.19.17': + resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} + '@vitest/expect@4.1.4': resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} @@ -352,6 +374,9 @@ packages: '@vitest/utils@4.1.4': resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -360,6 +385,10 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -371,6 +400,11 @@ packages: engines: {node: '>=18'} hasBin: true + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -378,6 +412,10 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -392,9 +430,36 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -417,11 +482,18 @@ packages: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + rollup@4.60.0: resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -429,12 +501,23 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@4.0.0: resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + + tar@7.5.15: + resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} + engines: {node: '>=18'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -450,6 +533,19 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -536,6 +632,10 @@ packages: engines: {node: '>=8'} hasBin: true + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + snapshots: '@esbuild/aix-ppc64@0.27.4': @@ -616,6 +716,10 @@ snapshots: '@esbuild/win32-x64@0.27.4': optional: true + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@jridgewell/sourcemap-codec@1.5.5': {} '@rollup/rollup-android-arm-eabi@4.60.0': @@ -704,6 +808,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/node@22.19.17': + dependencies: + undici-types: 6.21.0 + '@vitest/expect@4.1.4': dependencies: '@standard-schema/spec': 1.1.0 @@ -713,13 +821,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.4(vite@7.3.1)': + '@vitest/mocker@4.1.4(vite@7.3.1(@types/node@22.19.17)(tsx@4.21.0))': dependencies: '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1 + vite: 7.3.1(@types/node@22.19.17)(tsx@4.21.0) '@vitest/pretty-format@4.1.4': dependencies: @@ -745,10 +853,16 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + assertion-error@2.0.1: {} chai@6.2.2: {} + chownr@3.0.0: {} + convert-source-map@2.0.0: {} es-module-lexer@2.0.0: {} @@ -782,12 +896,18 @@ snapshots: '@esbuild/win32-ia32': 0.27.4 '@esbuild/win32-x64': 0.27.4 + esprima@4.0.1: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 expect-type@1.3.0: {} + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -795,10 +915,36 @@ snapshots: fsevents@2.3.3: optional: true + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + is-extendable@0.1.1: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + kind-of@6.0.3: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + nanoid@3.3.11: {} obug@2.1.1: {} @@ -815,6 +961,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + resolve-pkg-maps@1.0.0: {} + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 @@ -846,14 +994,31 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + siginfo@2.0.0: {} source-map-js@1.2.1: {} + sprintf-js@1.0.3: {} + stackback@0.0.2: {} std-env@4.0.0: {} + strip-bom-string@1.0.0: {} + + tar@7.5.15: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + tinybench@2.9.0: {} tinyexec@1.1.1: {} @@ -865,7 +1030,18 @@ snapshots: tinyrainbow@3.1.0: {} - vite@7.3.1: + tsx@4.21.0: + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@6.0.3: {} + + undici-types@6.21.0: {} + + vite@7.3.1(@types/node@22.19.17)(tsx@4.21.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) @@ -874,12 +1050,14 @@ snapshots: rollup: 4.60.0 tinyglobby: 0.2.15 optionalDependencies: + '@types/node': 22.19.17 fsevents: 2.3.3 + tsx: 4.21.0 - vitest@4.1.4(vite@7.3.1): + vitest@4.1.4(@types/node@22.19.17)(vite@7.3.1(@types/node@22.19.17)(tsx@4.21.0)): dependencies: '@vitest/expect': 4.1.4 - '@vitest/mocker': 4.1.4(vite@7.3.1) + '@vitest/mocker': 4.1.4(vite@7.3.1(@types/node@22.19.17)(tsx@4.21.0)) '@vitest/pretty-format': 4.1.4 '@vitest/runner': 4.1.4 '@vitest/snapshot': 4.1.4 @@ -896,8 +1074,10 @@ snapshots: tinyexec: 1.1.1 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1 + vite: 7.3.1(@types/node@22.19.17)(tsx@4.21.0) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.17 transitivePeerDependencies: - msw @@ -905,3 +1085,5 @@ snapshots: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + + yallist@5.0.0: {} diff --git a/scripts/build-release.ts b/scripts/build-release.ts new file mode 100644 index 0000000..a5caafa --- /dev/null +++ b/scripts/build-release.ts @@ -0,0 +1,85 @@ +import { createHash } from "node:crypto" +import { + mkdirSync, + readdirSync, + readFileSync, + writeFileSync, +} from "node:fs" +import { join } from "node:path" +import { create as createTar } from "tar" +import matter from "gray-matter" + +const ROOT = join(import.meta.dirname, "..") +const SKILLS_DIR = join(ROOT, "skills") +const DIST_DIR = join(ROOT, "dist") + +const SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json" + +function listFiles(dir: string, prefix = ""): string[] { + const entries: string[] = [] + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const rel = prefix ? `${prefix}/${entry.name}` : entry.name + if (entry.isDirectory()) { + entries.push(...listFiles(join(dir, entry.name), rel)) + } else { + entries.push(rel) + } + } + return entries.sort() +} + +function sha256File(filePath: string): string { + const hash = createHash("sha256").update(readFileSync(filePath)).digest("hex") + return `sha256:${hash}` +} + +mkdirSync(DIST_DIR, { recursive: true }) + +const skillNames = readdirSync(SKILLS_DIR, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name) + .sort() + +const skills: { + name: string + type: "archive" + description: string + url: string + digest: string +}[] = [] + +for (const name of skillNames) { + const skillDir = join(SKILLS_DIR, name) + const skillMdPath = join(skillDir, "SKILL.md") + + const { data } = matter(readFileSync(skillMdPath, "utf8")) + + if (!data.name || typeof data.name !== "string") { + throw new Error(`Missing or invalid 'name' in frontmatter: ${skillMdPath}`) + } + if (!data.description || typeof data.description !== "string") { + throw new Error( + `Missing or invalid 'description' in frontmatter: ${skillMdPath}` + ) + } + + const files = listFiles(skillDir) + const artifactPath = join(DIST_DIR, `${name}.tar.gz`) + + await createTar( + { gzip: true, file: artifactPath, cwd: skillDir, portable: true, mtime: new Date(0) }, + files + ) + + const digest = sha256File(artifactPath) + + skills.push({ name, type: "archive", description: data.description, url: `${name}.tar.gz`, digest }) + console.log(` ${name}: ${digest}`) +} + +const index = { $schema: SCHEMA, skills } +writeFileSync( + join(DIST_DIR, "index.json"), + JSON.stringify(index, null, 2) + "\n" +) +console.log(`\nWrote dist/index.json with ${skills.length} skill(s)`)