From 8936d9d0c52fa7dea2157512412741868bb5a834 Mon Sep 17 00:00:00 2001 From: Mateo Torres Date: Fri, 10 Apr 2026 11:25:15 -0700 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A4=96=20Friday=20changelogs=20+=20up?= =?UTF-8?q?graded=20changelog=20agent=20to=20include=20only=20production?= =?UTF-8?q?=20commits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agents/changelog/generate.ts | 153 ++++++++++----------------- app/en/references/changelog/page.mdx | 31 ++++++ 2 files changed, 87 insertions(+), 97 deletions(-) diff --git a/agents/changelog/generate.ts b/agents/changelog/generate.ts index 8f760d769..28420dbc3 100644 --- a/agents/changelog/generate.ts +++ b/agents/changelog/generate.ts @@ -7,8 +7,8 @@ import { fileURLToPath } from "url"; const REPOS = [ { owner: "ArcadeAI", repo: "docs", private: false }, - { owner: "ArcadeAI", repo: "arcade-mcp", private: false }, - { owner: "ArcadeAI", repo: "monorepo", private: true }, + { owner: "ArcadeAI", repo: "arcade-mcp", private: false, productionBranch: "production" }, + { owner: "ArcadeAI", repo: "monorepo", private: true, productionBranch: "production" }, ]; const CATEGORIES = [ @@ -51,6 +51,7 @@ type PR = { labels: string[]; merged_at: string; is_private: boolean; + merge_commit_sha: string; }; type CategorizedPR = { @@ -63,12 +64,6 @@ type CategorizedPR = { is_private: boolean; }; -type FinalEntry = { - category: string; - type: string; - description: string; - sources: { repo: string; pr_number: number }[]; -}; // --- Step 1: Compute upcoming Friday (or today if Friday) --- @@ -105,7 +100,7 @@ async function fetchMergedPRs( let page = 1; while (true) { - const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=closed&sort=updated&direction=desc&per_page=100&page=${page}`; + const url = `https://api.github.com/repos/${owner}/${repo}/pulls?state=closed&base=main&sort=updated&direction=desc&per_page=100&page=${page}`; const res = await fetch(url, { headers: { Accept: "application/vnd.github+json", @@ -134,6 +129,7 @@ async function fetchMergedPRs( labels: pr.labels?.map((l: any) => l.name) || [], merged_at: pr.merged_at, is_private: isPrivate, + merge_commit_sha: pr.merge_commit_sha, }); } } @@ -199,89 +195,48 @@ async function categorizePR(pr: PR, openai: OpenAI): Promise { }; } -// --- Step 5: Final combining call --- -const COMBINE_SCHEMA = { - name: "combined_changelog", - strict: true, - schema: { - type: "object" as const, - properties: { - entries: { - type: "array" as const, - items: { - type: "object" as const, - properties: { - category: { type: "string" as const, enum: [...CATEGORIES] }, - type: { type: "string" as const, enum: [...TYPES] }, - description: { type: "string" as const }, - sources: { - type: "array" as const, - items: { - type: "object" as const, - properties: { - repo: { type: "string" as const }, - pr_number: { type: "integer" as const }, - }, - required: ["repo", "pr_number"] as const, - additionalProperties: false, - }, - }, - }, - required: ["category", "type", "description", "sources"] as const, - additionalProperties: false, - }, - }, - }, - required: ["entries"] as const, - additionalProperties: false, - }, -}; - -async function combineEntries( - categorized: CategorizedPR[], - openai: OpenAI, -): Promise { - const model = process.env.OPENAI_MODEL || "gpt-4o-mini"; +// --- Step 5a: Filter to production-deployed PRs --- - const input = categorized - .sort((a, b) => a.merged_at.localeCompare(b.merged_at)) - .map((pr) => ({ - category: pr.category, - type: pr.type, - description: pr.description, - repo: pr.repo, - pr_number: pr.pr_number, - merged_at: pr.merged_at, - })); +async function getUndeployedSHAs( + owner: string, + repo: string, + productionBranch: string, +): Promise> { + const token = process.env.GITHUB_TOKEN; + if (!token) throw new Error("GITHUB_TOKEN env var is required"); - const res = await openai.chat.completions.create({ - model, - response_format: { type: "json_schema", json_schema: COMBINE_SCHEMA }, - messages: [ - { - role: "system", - content: [ - `You are combining changelog entries.`, - `If a docs PR and a non-docs PR are about the same feature, combine them into one entry under the non-docs category.`, - `Do not alter categories or types unless combining.`, - `Keep descriptions concise. Return ALL entries.`, - ].join(" "), - }, - { role: "user", content: JSON.stringify(input) }, - ], + const url = `https://api.github.com/repos/${owner}/${repo}/compare/${productionBranch}...main`; + const res = await fetch(url, { + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${token}`, + "X-GitHub-Api-Version": "2022-11-28", + }, }); - return JSON.parse(res.choices[0].message.content!).entries; + if (!res.ok) { + const body = await res.text(); + throw new Error(`GitHub compare API error for ${owner}/${repo}: ${res.status} ${body}`); + } + + const data: any = await res.json(); + const shas = new Set(); + for (const commit of data.commits) { + shas.add(commit.sha); + } + return shas; } // --- Step 6: Format the entry --- -function formatEntry(date: string, entries: FinalEntry[]): string { +function formatEntry(date: string, entries: CategorizedPR[]): string { const privateRepos = new Set(REPOS.filter((r) => r.private).map((r) => r.repo)); - const grouped: Record = {}; - for (const entry of entries) { + const sorted = [...entries].sort((a, b) => a.merged_at.localeCompare(b.merged_at)); + + const grouped: Record = {}; + for (const entry of sorted) { if (!grouped[entry.category]) grouped[entry.category] = []; grouped[entry.category].push(entry); } @@ -296,14 +251,7 @@ function formatEntry(date: string, entries: FinalEntry[]): string { for (const item of items) { const emoji = EMOJI[item.type] || EMOJI.maintenance; - const sources = item.sources - .map((s) => { - const prefix = privateRepos.has(s.repo) ? s.repo : s.repo; - return `${prefix} PR #${s.pr_number}`; - }) - .join(", "); - - result += `- \`[${item.type} - ${emoji}]\` ${item.description} (${sources})\n`; + result += `- \`[${item.type} - ${emoji}]\` ${item.description} (${item.repo} PR #${item.pr_number})\n`; } } @@ -351,21 +299,32 @@ async function main() { ).flat(); console.log(`Found ${allPRs.length} merged PRs since ${lastEntryDate}`); - if (allPRs.length === 0) { - console.log("No new PRs. Exiting."); + // Step 3a: Filter to only PRs deployed to production + console.log("Filtering to production-deployed PRs..."); + const undeployedByRepo: Record> = {}; + await Promise.all( + REPOS.filter((r) => r.productionBranch).map(async (r) => { + undeployedByRepo[r.repo] = await getUndeployedSHAs(r.owner, r.repo, r.productionBranch!); + }), + ); + const deployedPRs = allPRs.filter((pr) => { + const undeployed = undeployedByRepo[pr.repo]; + if (!undeployed) return true; + return !undeployed.has(pr.merge_commit_sha); + }); + console.log(`${deployedPRs.length} PRs are on production`); + + if (deployedPRs.length === 0) { + console.log("No deployed PRs. Exiting."); return; } // Step 4 console.log("Categorizing PRs..."); - const categorized = await Promise.all(allPRs.map((pr) => categorizePR(pr, openai))); + const categorized = await Promise.all(deployedPRs.map((pr) => categorizePR(pr, openai))); // Step 5 - console.log("Combining related entries..."); - const combined = await combineEntries(categorized, openai); - - // Step 6 - const entry = formatEntry(fridayDate, combined); + const entry = formatEntry(fridayDate, categorized); console.log("\nGenerated entry:\n"); console.log(entry); diff --git a/app/en/references/changelog/page.mdx b/app/en/references/changelog/page.mdx index 18c59d773..bd5a07c77 100644 --- a/app/en/references/changelog/page.mdx +++ b/app/en/references/changelog/page.mdx @@ -9,6 +9,37 @@ import { Callout } from "nextra/components"; _Here's what's new at Arcade.dev!_ +## 2026-04-10 + +**Frameworks** + +- `[documentation - 📝]` Add AG2 as a supported agent framework in the docs with a new walkthrough. +- `[documentation - 📝]` Add documentation for arcade-java and Spring AI guide including quickstart and error-handling updates. + +**Arcade MCP Servers** + +- `[feature - 🚀]` Enhanced Storybook MCP server support. +- `[feature - 🚀]` Add formatting support to GoogleDocs.CreateDocumentFromText to interpret input as Markdown. (monorepo PR #789) +- `[feature - 🚀]` Add a new PostHog toolkit with 41 tools including required server URL and API key. +- `[bugfix - 🐛]` Fix escaping and wrapping of '$search' queries in Microsoft Outlook Mail tool. +- `[bugfix - 🐛]` Fix MS Outlook Mail issues and standardize timezone-aware datetime across Mail & Calendar toolkits. +- `[bugfix - 🐛]` Fix five API response parsing bugs in the Attio toolkit to prevent silent data loss. (monorepo PR #709) +- `[documentation - 📝]` Updates documentation for new Microsoft Outlook and Granola. +- `[documentation - 📝]` Add reference docs for MCP Resources including URI templates and examples. +- `[documentation - 📝]` Updating MCP Servers documentation with toolkit metadata. +- `[documentation - 📝]` Document Linear OAuth refresh tokens for custom apps. + +**Arcade CLI** + +- `[feature - 🚀]` Adds a new CLI `rekey` subcommand to decrypt and rewrite stored secrets with a new root key. (monorepo PR #760) + +**Platform and Engine** + +- `[feature - 🚀]` Add Arcade Auth support to Claude Code gateway connect option. +- `[documentation - 📝]` Added documentation on project membership removal and reassignment process. +- `[bugfix - 🐛]` Fix dashboard navigation issue to prevent chat page redirect when navigating away. + + ## 2026-03-27 **Frameworks** From 8c1a35c6b152372b4c1a6a3bd7b94792d99c33f2 Mon Sep 17 00:00:00 2001 From: Mateo Torres Date: Fri, 10 Apr 2026 12:43:35 -0700 Subject: [PATCH 2/3] remove storybook reference --- app/en/references/changelog/page.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/en/references/changelog/page.mdx b/app/en/references/changelog/page.mdx index bd5a07c77..afe0e670b 100644 --- a/app/en/references/changelog/page.mdx +++ b/app/en/references/changelog/page.mdx @@ -18,7 +18,6 @@ _Here's what's new at Arcade.dev!_ **Arcade MCP Servers** -- `[feature - 🚀]` Enhanced Storybook MCP server support. - `[feature - 🚀]` Add formatting support to GoogleDocs.CreateDocumentFromText to interpret input as Markdown. (monorepo PR #789) - `[feature - 🚀]` Add a new PostHog toolkit with 41 tools including required server URL and API key. - `[bugfix - 🐛]` Fix escaping and wrapping of '$search' queries in Microsoft Outlook Mail tool. From 2bee6cbf6102360a44868104122fba97a91008aa Mon Sep 17 00:00:00 2001 From: Mateo Torres Date: Fri, 10 Apr 2026 12:46:02 -0700 Subject: [PATCH 3/3] addressing Eric's comments --- app/en/references/changelog/page.mdx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/en/references/changelog/page.mdx b/app/en/references/changelog/page.mdx index afe0e670b..443ed5731 100644 --- a/app/en/references/changelog/page.mdx +++ b/app/en/references/changelog/page.mdx @@ -19,7 +19,7 @@ _Here's what's new at Arcade.dev!_ **Arcade MCP Servers** - `[feature - 🚀]` Add formatting support to GoogleDocs.CreateDocumentFromText to interpret input as Markdown. (monorepo PR #789) -- `[feature - 🚀]` Add a new PostHog toolkit with 41 tools including required server URL and API key. +- `[feature - 🚀]` Add a new PostHog toolkit with 41 tools. - `[bugfix - 🐛]` Fix escaping and wrapping of '$search' queries in Microsoft Outlook Mail tool. - `[bugfix - 🐛]` Fix MS Outlook Mail issues and standardize timezone-aware datetime across Mail & Calendar toolkits. - `[bugfix - 🐛]` Fix five API response parsing bugs in the Attio toolkit to prevent silent data loss. (monorepo PR #709) @@ -28,10 +28,6 @@ _Here's what's new at Arcade.dev!_ - `[documentation - 📝]` Updating MCP Servers documentation with toolkit metadata. - `[documentation - 📝]` Document Linear OAuth refresh tokens for custom apps. -**Arcade CLI** - -- `[feature - 🚀]` Adds a new CLI `rekey` subcommand to decrypt and rewrite stored secrets with a new root key. (monorepo PR #760) - **Platform and Engine** - `[feature - 🚀]` Add Arcade Auth support to Claude Code gateway connect option.