diff --git a/.github/workflows/apply-release-notes.yml b/.github/workflows/apply-release-notes.yml new file mode 100644 index 0000000000..823d923456 --- /dev/null +++ b/.github/workflows/apply-release-notes.yml @@ -0,0 +1,98 @@ +name: Apply release notes + +# Approval-based publish. When a member of the supabase/cli team approves a +# release-notes PR (head ref `release-notes/v`), this workflow pushes +# the proposed notes to the GitHub Release body for the corresponding tag, +# comments the release URL on the PR, and closes the PR without merging. The +# release-notes file never lands on `main`. +# +# Mirrors the fast-forward job in release.yml, which already gates on a +# `pull_request_review` + `approved` event. + +on: + pull_request_review: + types: [submitted] + +permissions: + contents: read + +jobs: + apply: + # `state == 'open'` makes re-approvals on an already-closed PR a no-op + # (a reviewer can re-approve from the GitHub UI even after close). + if: | + github.event.review.state == 'approved' && + startsWith(github.event.pull_request.head.ref, 'release-notes/') && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.state == 'open' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + # App token: needs `orgs/.../teams/.../memberships` read (the org-installed + # App has it), repo write to edit the release, and PR write to comment + # and close. Matches release.yml's fast-forward step. + - id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + + - name: Authorize approver against supabase/cli team + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + APPROVER: ${{ github.event.review.user.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + # Fail closed: any response other than an active membership means the + # approval is ignored. We post a comment so the reviewer sees why their + # approval didn't apply, then exit 0 so the workflow isn't flagged red. + run: | + set -euo pipefail + status=$(gh api \ + -H "Accept: application/vnd.github+json" \ + "orgs/supabase/teams/cli/memberships/${APPROVER}" \ + --jq '.state' 2>/dev/null || true) + if [ "$status" != "active" ]; then + echo "Approver @${APPROVER} is not an active supabase/cli team member (state='${status:-none}'); ignoring approval." >&2 + gh pr comment "$PR_NUMBER" --repo "${{ github.repository }}" --body \ + "@${APPROVER} is not an active \`supabase/cli\` team member, so this approval was ignored. Ask a team member to approve to publish the notes." + exit 0 + fi + echo "AUTHORIZED=true" >> "$GITHUB_ENV" + + # Checkout the PR head so any reviewer edits made in the GitHub UI before + # approval are captured. apply-release-notes.ts reads from the working + # tree. + - if: env.AUTHORIZED == 'true' + uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 1 + persist-credentials: false + + - if: env.AUTHORIZED == 'true' + uses: ./.github/actions/setup + + - name: Apply notes, comment, and close + if: env.AUTHORIZED == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} + APPROVER: ${{ github.event.review.user.login }} + # The branch is named `release-notes/v`, so the tag is just + # the basename. apply-release-notes.ts validates the file's existence. + run: | + set -euo pipefail + tag="${HEAD_REF##release-notes/}" + if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-(beta|alpha)\.[0-9]+)?$ ]]; then + echo "Unexpected head ref '$HEAD_REF'; cannot derive tag." >&2 + exit 1 + fi + echo "==> Applying notes for $tag" + pnpm exec bun apps/cli/scripts/apply-release-notes.ts --tag "$tag" + release_url="https://github.com/${{ github.repository }}/releases/tag/${tag}" + gh pr comment "$PR_NUMBER" --repo "${{ github.repository }}" --body \ + "Applied to [${tag}](${release_url}) after approval by @${APPROVER}." + gh pr close "$PR_NUMBER" --repo "${{ github.repository }}" --delete-branch diff --git a/.github/workflows/propose-release-notes.yml b/.github/workflows/propose-release-notes.yml new file mode 100644 index 0000000000..179e116293 --- /dev/null +++ b/.github/workflows/propose-release-notes.yml @@ -0,0 +1,75 @@ +name: Propose release notes + +# Runs after backfill-release-notes lands the raw semantic-release block in the +# GitHub Release body. Re-derives that block, asks Claude to rewrite it into +# user-centric notes per tools/release/release-notes-prompt.md, and opens a PR +# adding release-notes/v.md. Merging the PR triggers +# apply-release-notes.yml, which pushes the file's contents to the GH Release. +# +# Stable releases only on the automatic release pipeline — prerelease tags +# (-beta./-alpha.) keep the raw body unless this workflow is triggered +# manually from the Actions tab (workflow_dispatch). + +on: + workflow_call: + inputs: + tag: + description: Release tag to propose notes for (e.g. v2.101.0) + required: true + type: string + non_blocking: + description: Do not fail the workflow run when proposing fails (release pipeline) + required: false + type: boolean + default: false + workflow_dispatch: + inputs: + tag: + description: Release tag to propose notes for (e.g. v2.101.0 or v2.99.0-beta.1) + required: true + type: string + +permissions: + contents: read + +jobs: + propose: + # workflow_call (release pipeline): skip prereleases. workflow_dispatch: + # allow any tag so reviewers can opt in for beta/alpha from the Actions tab. + if: ${{ github.event_name == 'workflow_dispatch' || (!contains(inputs.tag, '-beta.') && !contains(inputs.tag, '-alpha.')) }} + runs-on: ubuntu-latest + continue-on-error: ${{ inputs.non_blocking }} + permissions: + contents: write + pull-requests: write + env: + TAG: ${{ inputs.tag }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + steps: + # App token gets us push to a protected default branch *and* PR creation + # under the App identity, matching the rest of release.yml. + - id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + + - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + with: + # Full history + tags so backfill-release-notes.ts can reach the + # commit graph it needs (semantic-release walks notes back to the + # last release on the channel). + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - uses: ./.github/actions/setup + + - name: Configure git identity + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Propose release notes + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: pnpm exec bun apps/cli/scripts/propose-release-notes.ts --tag "${TAG}" --apply diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml index 710d37b0dc..6a5422fc82 100644 --- a/.github/workflows/release-shared.yml +++ b/.github/workflows/release-shared.yml @@ -323,6 +323,20 @@ jobs: apply: true non_blocking: true + # Once the raw semantic-release block is in the release body, ask Claude to + # rewrite it into user-centric notes and open a PR for human approval. Stable + # releases only on this path — prereleases keep the raw body. Non-blocking so + # an LLM hiccup never gates a published release; reviewers can propose beta/ + # alpha notes manually from the Actions tab (workflow_dispatch). + propose-release-notes: + uses: ./.github/workflows/propose-release-notes.yml + needs: backfill-release-notes + if: ${{ !inputs.dry_run && !inputs.prerelease && needs.backfill-release-notes.result == 'success' }} + with: + tag: v${{ inputs.version }} + non_blocking: true + secrets: inherit + publish-homebrew: needs: publish if: ${{ !inputs.dry_run && inputs.publish_brew_scoop }} diff --git a/apps/cli/package.json b/apps/cli/package.json index fa98c8d27c..0bc2ba0687 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -37,10 +37,13 @@ "fix:all": "nx run-many -t lint:fix fmt:fix knip:fix --projects=$npm_package_name" }, "devDependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.3.146", + "@anthropic-ai/sdk": "^0.97.1", "@clack/prompts": "^1.4.0", "@effect/atom-react": "catalog:", "@effect/platform-bun": "catalog:", "@effect/vitest": "catalog:", + "@modelcontextprotocol/sdk": "^1.29.0", "@napi-rs/keyring": "^1.3.0", "@parcel/watcher": "^2.5.6", "@supabase/api": "workspace:*", @@ -120,7 +123,10 @@ "oxfmt", "oxlint", "oxlint-tsgolint", - "semantic-release" + "semantic-release", + "@anthropic-ai/claude-agent-sdk", + "@anthropic-ai/sdk", + "@modelcontextprotocol/sdk" ] }, "nx": { diff --git a/apps/cli/scripts/apply-release-notes.ts b/apps/cli/scripts/apply-release-notes.ts new file mode 100644 index 0000000000..9027208231 --- /dev/null +++ b/apps/cli/scripts/apply-release-notes.ts @@ -0,0 +1,37 @@ +#!/usr/bin/env bun +// Push the contents of release-notes/v.md to the GitHub Release body +// for tag v. Invoked from apply-release-notes.yml after a +// release-notes PR is merged to main. +// +// Usage: +// bun apps/cli/scripts/apply-release-notes.ts --tag v2.101.0 +import { $ } from "bun"; +import { existsSync } from "node:fs"; +import path from "node:path"; +import process from "node:process"; +import { parseArgs } from "node:util"; + +const { values } = parseArgs({ + options: { + tag: { type: "string" }, + }, + strict: true, +}); + +const tag = values.tag; +if (!tag) { + console.error("--tag is required (e.g. --tag v2.101.0)"); + process.exit(2); +} +const version = tag.replace(/^v/, ""); + +const repoRoot = (await $`git rev-parse --show-toplevel`.text()).trim(); +const notesPath = path.join(repoRoot, "release-notes", `v${version}.md`); +if (!existsSync(notesPath)) { + console.error(`No notes file at ${path.relative(repoRoot, notesPath)}`); + process.exit(1); +} + +console.error(`==> Updating GitHub Release body for ${tag}`); +await $`gh release edit ${tag} --notes-file ${notesPath}`.cwd(repoRoot); +console.error(`==> Done`); diff --git a/apps/cli/scripts/propose-release-notes.ts b/apps/cli/scripts/propose-release-notes.ts new file mode 100644 index 0000000000..33b1f2faaf --- /dev/null +++ b/apps/cli/scripts/propose-release-notes.ts @@ -0,0 +1,213 @@ +#!/usr/bin/env bun +// Generate a user-centric GitHub Release body for a Supabase CLI tag +// by running the Claude Agent SDK against tools/release/release-notes-prompt.md +// with the raw semantic-release block substituted in. +// +// Pipeline shape: +// 1. `backfill-release-notes.ts --tag ` produces the raw semantic-release +// markdown (without writing anything to the GH release). We always +// re-derive this so the proposer is decoupled from whatever happens to +// sit in the release body at the moment. +// 2. The raw block is inlined into tools/release/release-notes-prompt.md in +// place of the {{PASTE_SEMANTIC_RELEASE_BLOCK_HERE}} placeholder. +// 3. The Claude Agent SDK runs the rendered prompt with WebFetch + Bash so +// it can investigate PR bodies, linked issues, and changed files (the +// prompt's investigation step is real work, not boilerplate). +// 4. The agent's final assistant message is written to +// release-notes/v.md. +// 5. Unless --dry-run is passed, the script commits the file on a branch +// `release-notes/v` and opens a PR. Approving the PR (as a +// supabase/cli team member) triggers apply-release-notes.yml, which +// pushes the file's contents to the GH release body and closes the PR +// without merging — the file never lands on `main`. +// +// Usage: +// bun apps/cli/scripts/propose-release-notes.ts --tag v2.101.0 --dry-run +// bun apps/cli/scripts/propose-release-notes.ts --tag v2.101.0 --apply +// +// --tag Required. Release tag (e.g. v2.101.0 or v2.99.0-beta.1). +// --dry-run Print the proposed notes to stdout. Does not write any files, +// does not touch git. +// --apply Write release-notes/v.md, commit on a branch, push, +// and open a PR. Default behavior when neither flag is passed +// is `--dry-run`. +// --render-only Print the rendered prompt (template + raw notes block) +// and exit before any LLM call. Useful for prompt iteration +// and for verifying the pipeline shape without spending tokens. +// --model Optional. Override the Claude model (default: claude-haiku-4-5-20251001). +import { query, type Options } from "@anthropic-ai/claude-agent-sdk"; +import { $ } from "bun"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import path from "node:path"; +import process from "node:process"; +import { parseArgs } from "node:util"; + +const { values } = parseArgs({ + options: { + tag: { type: "string" }, + "dry-run": { type: "boolean", default: false }, + apply: { type: "boolean", default: false }, + "render-only": { type: "boolean", default: false }, + model: { type: "string", default: "claude-haiku-4-5-20251001" }, + }, + strict: true, +}); + +const tag = values.tag; +if (!tag) { + console.error("--tag is required (e.g. --tag v2.101.0)"); + process.exit(2); +} +const version = tag.replace(/^v/, ""); +const apply = values.apply === true && values["dry-run"] !== true; + +const repoRoot = (await $`git rev-parse --show-toplevel`.text()).trim(); +const promptPath = path.join(repoRoot, "tools/release/release-notes-prompt.md"); +const backfillScript = path.join(repoRoot, "apps/cli/scripts/backfill-release-notes.ts"); +const notesDir = path.join(repoRoot, "release-notes"); +const notesPath = path.join(notesDir, `v${version}.md`); + +console.error(`==> Re-deriving raw semantic-release notes for ${tag}`); +const rawNotes = (await $`bun ${backfillScript} --tag ${tag}`.cwd(repoRoot).text()).trim(); +if (!rawNotes) { + console.error(`backfill-release-notes produced no output for ${tag}`); + process.exit(1); +} + +const promptTemplate = await readFile(promptPath, "utf8"); +const placeholder = "{{PASTE_SEMANTIC_RELEASE_BLOCK_HERE}}"; +if (!promptTemplate.includes(placeholder)) { + console.error(`Prompt template at ${promptPath} is missing ${placeholder}`); + process.exit(1); +} +const rendered = promptTemplate.replace(placeholder, rawNotes); + +if (values["render-only"]) { + process.stdout.write(rendered); + process.exit(0); +} + +console.error(`==> Running Claude Agent SDK (model=${values.model})`); +const options: Options = { + model: values.model, + // The agent needs WebFetch / WebSearch to investigate PR bodies and linked + // issues per the prompt's step 3, and Bash so it can use `gh` for + // authenticated GitHub queries instead of HTML scraping. Edit/Write are + // intentionally excluded — the script owns the final file output. + allowedTools: ["WebFetch", "WebSearch", "Bash"], + // Don't load the repo's CLAUDE.md or settings.json — the prompt is + // self-contained and we don't want unrelated agent context bleeding in. + settingSources: [], + cwd: repoRoot, + effort: "low", +}; + +let finalText = ""; +let cost = 0; +const stream = query({ prompt: rendered, options }); +for await (const msg of stream) { + if (msg.type === "result") { + if (msg.subtype === "success") { + finalText = msg.result; + cost = msg.total_cost_usd; + } else { + console.error(`Agent failed: ${msg.subtype}`); + if (msg.errors?.length) console.error(msg.errors.join("\n")); + process.exit(1); + } + } +} + +if (!finalText.trim()) { + console.error("Agent returned no result text"); + process.exit(1); +} + +// Append the raw notes to the final text to ensure the output is complete. +const normalized = finalText.endsWith("\n") ? finalText : `${finalText}\n`; +console.error(`==> Agent finished (cost ~$${cost.toFixed(4)})`); + +if (!apply) { + process.stdout.write(normalized); + process.exit(0); +} + +await mkdir(notesDir, { recursive: true }); +if (existsSync(notesPath)) { + console.error( + `Refusing to overwrite existing ${path.relative(repoRoot, notesPath)}. ` + + `Delete it or rerun with --dry-run to preview.`, + ); + process.exit(1); +} +await writeFile(notesPath, normalized); +console.error(`==> Wrote ${path.relative(repoRoot, notesPath)}`); + +const branch = `release-notes/v${version}`; +const currentBranch = (await $`git rev-parse --abbrev-ref HEAD`.cwd(repoRoot).text()).trim(); +if (currentBranch !== branch) { + await $`git checkout -B ${branch}`.cwd(repoRoot); +} +await $`git add ${notesPath}`.cwd(repoRoot); +const commitMessage = `docs(release): propose user-facing notes for ${tag}`; +await $`git commit -m ${commitMessage}`.cwd(repoRoot); + +console.error(`==> Pushing ${branch}`); +let pushed = false; +for (let attempt = 0; attempt < 4; attempt++) { + const result = await $`git push -u origin ${branch}`.cwd(repoRoot).nothrow(); + if (result.exitCode === 0) { + pushed = true; + break; + } + const wait = 2 ** (attempt + 1) * 1000; + console.error(`Push failed (attempt ${attempt + 1}/4); retrying in ${wait / 1000}s`); + await new Promise((r) => setTimeout(r, wait)); +} +if (!pushed) { + console.error("git push failed after 4 attempts"); + process.exit(1); +} + +// Idempotently ensure the `do not merge` label exists on the repo, then attach +// it on PR creation. The label is a visual reminder for reviewers — the +// approval-based apply workflow never invokes the merge button — but the +// publish flow itself does not depend on it. +const labelName = "do not merge"; +await $`gh label create ${labelName} --color B60205 --description ${"Approve to apply; do not merge."} --force` + .cwd(repoRoot) + .nothrow(); + +const releaseUrl = `https://github.com/supabase/cli/releases/tag/${tag}`; +const prBody = `Proposed user-facing release notes for \`${tag}\`, generated by \`apps/cli/scripts/propose-release-notes.ts\` against \`tools/release/release-notes-prompt.md\`. + +## How to update the notes + +Edit \`release-notes/v${version}.md\` directly on this branch — use the GitHub web editor or push commits to \`${branch}\` — before approving. The applied notes will reflect the file at the approved commit. + +## How to publish + +Approve this PR as a \`supabase/cli\` team member. The \`.github/workflows/apply-release-notes.yml\` workflow will then: + +1. Overwrite the GitHub Release body for [\`${tag}\`](${releaseUrl}) with the contents of \`release-notes/v${version}.md\`. +2. Comment the release URL on this PR. +3. Close this PR and delete the \`${branch}\` branch. + +**This PR is not merged** — the \`do not merge\` label is a reminder. Nothing lands on \`main\`. + +Approvals from anyone outside the \`supabase/cli\` team are ignored; the workflow will post a comment explaining that and leave the release untouched. + +## How to abandon + +Close the PR without approving. The auto-generated semantic-release body for \`${tag}\` stays in place. + +## Re-generation + +After this PR is closed, rerun the **Propose release notes** workflow from the Actions tab against \`${tag}\` to get a fresh proposal. +`; + +await $`gh pr create --title ${`docs(release): notes for ${tag}`} --body ${prBody} --base main --head ${branch} --label ${labelName}`.cwd( + repoRoot, +); +console.error(`==> PR opened for ${branch}`); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0467e6880a..7a114320de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,12 @@ importers: apps/cli: devDependencies: + '@anthropic-ai/claude-agent-sdk': + specifier: ^0.3.146 + version: 0.3.146(@anthropic-ai/sdk@0.97.1(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3) + '@anthropic-ai/sdk': + specifier: ^0.97.1 + version: 0.97.1(zod@4.4.3) '@clack/prompts': specifier: ^1.4.0 version: 1.4.0 @@ -95,6 +101,9 @@ importers: '@effect/vitest': specifier: 'catalog:' version: 4.0.0-beta.74(effect@4.0.0-beta.74)(vitest@4.1.7) + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.29.0(zod@4.4.3) '@napi-rs/keyring': specifier: ^1.3.0 version: 1.3.0 @@ -527,6 +536,67 @@ packages: resolution: {integrity: sha512-p+CMKJ93HFmLkjXKlXiVGlMQEuRb6H0MokBSwUsX+S6BRX8eV5naFZpQJFfJHjRZY0Hmnqy1/r6UWl3x+19zYA==} engines: {node: '>=18'} + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.146': + resolution: {integrity: sha512-0IIvlEaenq2CRSVx5Bo5BaCtHQXS87GancM35WKEYveGVLn6DI+5G7ikYuTE4AKRPkMnogFtY4BJt6LulWGj+A==} + cpu: [arm64] + os: [darwin] + + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.146': + resolution: {integrity: sha512-Dk5xJ03Ff1JXbMRP1t2wc/TyfY6xF/2Ysp31wMhFPjoNiKSPHMWaIg242+T3CHdxLWmJ8plWHL1HL5cyZ/LCkw==} + cpu: [x64] + os: [darwin] + + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.146': + resolution: {integrity: sha512-QlCid0ucdrmhUAOewfQjaofN2wlokWcfFTxSFePTSj1umk35JO7TDFP700F7jU49r1fPWIdvJpPwWGyB0DeFPA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.146': + resolution: {integrity: sha512-mzBXDDWWBAC/vDtAYpO1G/dq5QvJtYSPXsqcb+sNdcDhiuf4IYnYp7ytRncYlsUNDkLmX6Gk2jkWAHUUA2Lozg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.146': + resolution: {integrity: sha512-E3coK1ThQT08KIX80RLcsq7DWXFllCKOzoOe32it/bdtY56TBgPY9xemwXhIJ+cVBHTI9/MpBSIlKBcFCt+yQA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.146': + resolution: {integrity: sha512-B2baXU1tCBT5CVlD7jJMKjpC4xdO45NUIWpqImmwuOfKvlM/PITjyTXyTY662mGZf1dBmdqBBsqirwFH/jhi8Q==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.146': + resolution: {integrity: sha512-CIwQxGX2r/yWpjCJ6ahB3smKXhghWgGTxL98+LGW52TUwqTiBnlNrH9DPqqgv1/+Hyquw6xfLrKU+StyfMgiLw==} + cpu: [arm64] + os: [win32] + + '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.146': + resolution: {integrity: sha512-qmxrsyaqA8s4HShqJls7ZCRjdoqN66Jo/hbjQNB3uHepD8tEO1iD19aPV4+osdLT7feMkhDBfLT07Q30R2NB5w==} + cpu: [x64] + os: [win32] + + '@anthropic-ai/claude-agent-sdk@0.3.146': + resolution: {integrity: sha512-hK9/Ng+hOyexUemTxdIUsSWJ9o2LFi2YNWzHwz8/YMCohUYOnFMZkBiENvUAb0WIc5hieOyBZrOIlg5OewuJMg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@anthropic-ai/sdk': '>=0.93.0' + '@modelcontextprotocol/sdk': ^1.29.0 + zod: ^4.0.0 + + '@anthropic-ai/sdk@0.97.1': + resolution: {integrity: sha512-wOf7AUeJPitcVpvKO4UMu63mWH5SaVipkGd7OOQJt/G6VYGlV8D2Gp9dLxOrttDJh/9gqPqdaBwDGcBevumeAg==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -582,6 +652,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -841,6 +915,12 @@ packages: tailwindcss: optional: true + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} @@ -1024,6 +1104,16 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} cpu: [arm64] @@ -2478,6 +2568,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2871,6 +2964,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2893,6 +2990,14 @@ packages: resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} engines: {node: '>=18'} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -3039,6 +3144,10 @@ packages: resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} @@ -3270,6 +3379,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -3310,6 +3423,10 @@ packages: cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -3613,6 +3730,14 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: + resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3628,10 +3753,20 @@ packages: express-rate-limit@5.5.1: resolution: {integrity: sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==} + express-rate-limit@8.5.2: + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@4.22.1: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -3653,6 +3788,9 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -3700,6 +3838,10 @@ packages: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-my-way-ts@0.1.6: resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==} @@ -3765,6 +3907,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -4031,6 +4177,10 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hono@4.12.21: + resolution: {integrity: sha512-uV63apnb0kyPtAUwoWgaGh9HyIFcv8lgmzPZSiTBQAFOFGIzka5EZ1dZocmGnn0XdX0+XTqJ6Tqv7selMuGLRQ==} + engines: {node: '>=16.9.0'} + hook-std@4.0.0: resolution: {integrity: sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==} engines: {node: '>=20'} @@ -4095,6 +4245,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -4162,6 +4316,10 @@ packages: resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==} engines: {node: '>=12.22.0'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4233,6 +4391,9 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4289,6 +4450,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4313,9 +4477,16 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -4615,6 +4786,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} @@ -4622,6 +4797,10 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4754,6 +4933,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -4853,6 +5036,10 @@ packages: resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -5207,6 +5394,9 @@ packages: path-to-regexp@0.1.13: resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5252,6 +5442,10 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-conf@2.1.0: resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} engines: {node: '>=4'} @@ -5346,6 +5540,10 @@ packages: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} engines: {node: '>= 0.8'} + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -5538,6 +5736,10 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5595,10 +5797,18 @@ packages: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + serve-static@1.16.3: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -5732,6 +5942,9 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -5950,6 +6163,9 @@ packages: truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -5990,6 +6206,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -6369,6 +6589,11 @@ packages: yoga-layout@3.2.1: resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -6398,6 +6623,52 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.146': + optional: true + + '@anthropic-ai/claude-agent-sdk@0.3.146(@anthropic-ai/sdk@0.97.1(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)': + dependencies: + '@anthropic-ai/sdk': 0.97.1(zod@4.4.3) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + zod: 4.4.3 + optionalDependencies: + '@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.3.146 + '@anthropic-ai/claude-agent-sdk-darwin-x64': 0.3.146 + '@anthropic-ai/claude-agent-sdk-linux-arm64': 0.3.146 + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.3.146 + '@anthropic-ai/claude-agent-sdk-linux-x64': 0.3.146 + '@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.3.146 + '@anthropic-ai/claude-agent-sdk-win32-arm64': 0.3.146 + '@anthropic-ai/claude-agent-sdk-win32-x64': 0.3.146 + + '@anthropic-ai/sdk@0.97.1(zod@4.4.3)': + dependencies: + json-schema-to-ts: 3.1.1 + standardwebhooks: 1.0.0 + optionalDependencies: + zod: 4.4.3 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -6475,6 +6746,8 @@ snapshots: dependencies: '@babel/types': 7.29.7 + '@babel/runtime@7.29.2': {} + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -6577,7 +6850,6 @@ snapshots: dependencies: '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 - optional: true '@emnapi/core@1.4.5': dependencies: @@ -6587,7 +6859,6 @@ snapshots: '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 - optional: true '@emnapi/runtime@1.4.5': dependencies: @@ -6600,7 +6871,6 @@ snapshots: '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 - optional: true '@esbuild/aix-ppc64@0.28.0': optional: true @@ -6699,6 +6969,10 @@ snapshots: '@fumadocs/tailwind@0.0.5': {} + '@hono/node-server@1.19.14(hono@4.12.21)': + dependencies: + hono: 4.12.21 + '@img/colour@1.1.0': optional: true @@ -6851,6 +7125,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.21) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.8 + express: 5.2.1 + express-rate-limit: 8.5.2(express@5.2.1) + hono: 4.12.21 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true @@ -6922,8 +7218,8 @@ snapshots: '@napi-rs/wasm-runtime@0.2.4': dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.9.0 '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': @@ -7942,6 +8238,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.1.0': {} '@supabase/auth-js@2.106.2': @@ -8413,6 +8711,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -8432,6 +8735,10 @@ snapshots: clean-stack: 5.3.0 indent-string: 5.0.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -8552,6 +8859,20 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.14.2 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + bottleneck@2.19.5: {} brace-expansion@2.1.1: @@ -8791,6 +9112,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.1.0: {} + content-type@1.0.5: {} content-type@2.0.0: {} @@ -8822,6 +9145,8 @@ snapshots: cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} + cookie@0.7.2: {} core-util-is@1.0.2: {} @@ -9121,6 +9446,12 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.0.8: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.8 + execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -9152,6 +9483,11 @@ snapshots: express-rate-limit@5.5.1: {} + express-rate-limit@8.5.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + express@4.22.1: dependencies: accepts: 1.3.8 @@ -9188,6 +9524,39 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.2 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} extsprintf@1.3.0: {} @@ -9208,6 +9577,8 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-sha256@1.3.0: {} + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -9260,6 +9631,17 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-my-way-ts@0.1.6: {} find-up-simple@1.0.1: {} @@ -9306,6 +9688,8 @@ snapshots: fresh@0.5.2: {} + fresh@2.0.0: {} + fs-constants@1.0.0: {} fs-extra@11.3.5: @@ -9656,6 +10040,8 @@ snapshots: highlight.js@10.7.3: {} + hono@4.12.21: {} + hook-std@4.0.0: {} hosted-git-info@7.0.2: @@ -9724,6 +10110,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -9809,6 +10199,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.2.0: {} + ipaddr.js@1.9.1: {} is-alphabetical@2.0.1: {} @@ -9854,6 +10246,8 @@ snapshots: is-promise@2.2.2: {} + is-promise@4.0.0: {} + is-stream@3.0.0: {} is-stream@4.0.1: {} @@ -9899,6 +10293,8 @@ snapshots: jiti@2.7.0: {} + jose@6.2.3: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -9915,8 +10311,15 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.2 + ts-algebra: 2.0.0 + json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stringify-safe@5.0.1: {} @@ -9946,7 +10349,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.0 + semver: 7.8.1 jsprim@2.0.2: dependencies: @@ -10317,10 +10720,14 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + meow@13.2.0: {} merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -10604,6 +11011,10 @@ snapshots: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mime@1.6.0: {} mime@3.0.0: {} @@ -10678,6 +11089,8 @@ snapshots: negotiator@0.6.4: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} nerf-dart@1.0.0: {} @@ -10935,7 +11348,7 @@ snapshots: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 - cli-spinners: 2.6.1 + cli-spinners: 2.9.2 is-interactive: 1.0.0 log-symbols: 4.1.0 strip-ansi: 6.0.1 @@ -11132,6 +11545,8 @@ snapshots: path-to-regexp@0.1.13: {} + path-to-regexp@8.4.2: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -11179,6 +11594,8 @@ snapshots: pirates@4.0.7: {} + pkce-challenge@5.0.1: {} + pkg-conf@2.1.0: dependencies: find-up: 2.1.0 @@ -11266,6 +11683,13 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -11540,6 +11964,16 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.2 '@rolldown/binding-win32-x64-msvc': 1.0.2 + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -11624,6 +12058,22 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + serve-static@1.16.3: dependencies: encodeurl: 2.0.0 @@ -11633,6 +12083,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + setprototypeof@1.2.0: {} sharp@0.34.5: @@ -11804,6 +12263,11 @@ snapshots: standard-as-callback@2.1.0: {} + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + statuses@2.0.2: {} std-env@4.1.0: {} @@ -12016,6 +12480,8 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 + ts-algebra@2.0.0: {} + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -12049,6 +12515,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.2 + typescript@6.0.3: {} uglify-js@3.19.3: @@ -12397,6 +12869,10 @@ snapshots: yoga-layout@3.2.1: {} + zod-to-json-schema@3.25.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/tools/release/release-notes-prompt.md b/tools/release/release-notes-prompt.md new file mode 100644 index 0000000000..ba68328962 --- /dev/null +++ b/tools/release/release-notes-prompt.md @@ -0,0 +1,164 @@ +## Output + +Generate release notes for **supabase/cli** from the pasted semantic-release block below. +**Replace** the pasted block entirely — do not extend it. + +Output **only** the final markdown release notes: no reasoning, no investigation commentary, no +`Now I have everything…`, no ` ```markdown ` wrappers. + +--- + +## Inputs + +``` +REPO: supabase/cli +PRODUCT_NAME: Supabase CLI +AUDIENCE: developers using the Supabase CLI locally and in CI +TONE: clear, direct, lightly informal, no marketing fluff +``` + +**Semantic-release changelog block** (paste between the fences): + +``` +{{PASTE_SEMANTIC_RELEASE_BLOCK_HERE}} +``` + +Example header shape: `# [2.101.0](https://github.com/supabase/cli/compare/v2.100.1...v2.101.0) (2026-05-21)` with `### Bug Fixes` / `### Features` bullets. + +--- + +## Role + +Senior devrel writer for **Supabase CLI**. Translate merged PRs into workflow-focused notes — not +PR-title summaries. Answer: **Should I upgrade?** **What's new for me?** **Any gotchas?** + +--- + +## Repo scope (apply first) + +### Two shells — only `legacy/` counts + +| Path | Status | +|------|--------| +| `apps/cli/src/legacy/` | What users run as `supabase` today — **all user-facing behavior** | +| `apps/cli/src/next/` | v3 / alpha — **not user-facing** | + +- **Drop** PRs that only touch `next/` (commands, flags, tests, alpha plumbing): no bullet, **no tail count**, never mention `next/` or v3. +- PRs touching both `legacy/`/`shared/` and `next/`: write **only** the legacy/shared impact. + +### Go → TypeScript port + +Ongoing port: `apps/cli-go/` → `apps/cli/src/legacy/`. Parity PRs are **not** features/fixes. + +- If leaf commands were ported: **one line** under **TypeScript port progress** — list leaf commands only (`db diff`, not `db`); behavior matches Go CLI; cite PRs. Omit section if none. +- Port infra (services, tests, parity scripts) → tail count only. +- Port PR that **also** fixes a real bug or adds a non-Go flag → promote that part to Bug fixes / New features; still list the command under port progress. + +### Where user-visible changes usually live + +- `apps/cli/src/legacy/commands/**` — behavior, output, flags, errors (beyond pure porting) +- `apps/cli/src/shared/**` — telemetry, global flags, output inherited by legacy +- `apps/cli-go/**` — while still the production binary +- `packages/cli-*`, `apps/cli/scripts/` — install/packaging (homebrew, scoop, build) + +Everything else is usually internal. + +--- + +## Process + +Do not skip investigation — titles alone are insufficient. + +1. **Parse** — Extract version, compare URL, date, and each PR (title, prefix/scope, number, URL). Semantic-release sections (`### Bug Fixes`, etc.) are **hints only**, not final grouping. + +2. **Prefix triage** (fast pass) + +| Prefix | Action | +|--------|--------| +| `chore:`, `ci:`, `test:` | Tail (open only if title hints user impact) | +| `docs:` | Tail unless user-read docs / in-CLI help | +| `refactor:`, `style:` | Judge | +| `perf:` | Usually investigate | +| `fix:`, `feat:` (+ product scopes `cli`, `db`, `auth`, …) | Investigate | +| `feat!:`, `fix!:`, `BREAKING CHANGE` | Investigate + breaking section | + +Tail PRs count toward "Plus N internal…". **`next/`-only PRs do not.** + +3. **Investigate** each survivor — open the PR URL: body (not just title), linked issues (`Closes`/`Fixes`/`Refs`), files changed, labels, `!` / `BREAKING CHANGE`. Unclear after that → `` — do not guess. + +4. **User-relevance gate** — Would a CLI user notice this in workflow, output, errors, or commands/flags? + - **Yes** → entry + - **No** → tail (e.g. build-time credential injection, CI smoke-test fixes, `next/`-only) + - **Borderline** (e.g. `--version` now correct) → one-liner under Bug fixes, not Highlights + +5. **Classify** — Highlights (1–4 lead items), New features, Improvements, Bug fixes, Breaking changes (separate, always if any), TypeScript port progress, Internal (tail only). **Group** related PRs into one bullet with all PR numbers. + +6. **Write entries** — `**** — . (#1234)` + +Voice: second person, active; lead with benefit; name commands/flags/env vars; short examples when helpful; no marketing filler; never mention `next/`. + +- **Bug fixes:** symptom users saw, not root cause — ✅ `` `supabase start` no longer crashes when `[db.pooler]` is missing `` not "Fixed nil pointer in resolver" +- **Breaking:** what's breaking, who's affected, exact migration step + +7. **Intro** — 1–3 sentences on the headline. Honest if mostly fixes or grab-bag. Don't lead with port progress unless a command surface meaningfully changed. + +--- + +## Output format + +From the header line extract `VERSION`, `COMPARE_URL`, `DATE`. + +```markdown +## Supabase CLI v + +<1–3 sentence intro> + +### ⚠️ Breaking changes + +- **** — . (#1234) + +### Highlights +- **** — . (#1234) + +### New features +- **** — . (#1234) + +### Improvements +- . (#1234) + +### Bug fixes +- . (#1234) + +### TypeScript port progress + +- **Now served by the TypeScript shell:** ``, ``. Behavior matches the Go CLI. (#1234) + +--- + +Plus N internal improvements and dependency updates. + +**Full changelog:** +``` + +Omit empty sections. + +--- + +## Quick examples + +| Case | ❌ | ✅ | +|------|----|----| +| Feature `feat(db): --linked on db diff` | Added `--linked` flag (#4567) | **`db diff` against your linked project, no Docker** — pass `--linked` to diff remote without a local stack; handy in CI (#4567) | +| Bug + issue | Fixed nil pointer in config parser (#5012) | `supabase start` no longer crashes when optional sections like `[db.pooler]` are missing (#5012) | +| 3 PRs, one feature | Three `db lint --json` bullets | **`db lint` machine-readable output** — `--json` for CI; empty array when clean (#4801, #4815, #4823) | +| Port only | New native `db diff` implementation | Under **TypeScript port progress** only — `db diff`; behavior unchanged (#5314) | +| Port + real bug | (same bullet as port) | **Bug fixes:** `orgs list` returns all orgs, not first 100 (#5318); **Port:** `orgs list` (#5318) | +| `fix(cli):` build inject credentials | (bullet) | Tail only — scope `cli` ≠ user impact | +| `feat(next):` only | Any mention | Silent drop | + +--- + +## Avoid + +PR titles verbatim; implementation-first wording; buried breaking changes; vague "various improvements"; +marketing tone; guessing when unclear; port PRs as features; any `next/` / v3 / alpha mention.