From 90f79656d649a9a82ad89db04771cfae17903c5a Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Sun, 23 Aug 2026 16:18:51 -0700 Subject: [PATCH 1/3] marketing: interactive blog post on MCP/CLI/API tool calling --- apps/marketing/astro.config.mjs | 3 +- apps/marketing/package.json | 3 +- apps/marketing/public/llms.txt | 5 + .../components/blog/context-flood-demo.tsx | 180 +++++ .../components/blog/harness-spread-demo.tsx | 158 +++++ .../src/components/blog/install-race-demo.tsx | 134 ++++ .../src/components/blog/mcp-to-cli-demo.tsx | 155 ++++ .../src/components/blog/same-call-demo.tsx | 173 +++++ apps/marketing/src/components/blog/shared.ts | 72 ++ apps/marketing/src/content.config.ts | 9 +- .../src/content/blog/mcp-cli-same-thing.mdx | 94 +++ apps/marketing/src/styles/global.css | 668 ++++++++++++++++++ bun.lock | 57 +- 13 files changed, 1683 insertions(+), 28 deletions(-) create mode 100644 apps/marketing/src/components/blog/context-flood-demo.tsx create mode 100644 apps/marketing/src/components/blog/harness-spread-demo.tsx create mode 100644 apps/marketing/src/components/blog/install-race-demo.tsx create mode 100644 apps/marketing/src/components/blog/mcp-to-cli-demo.tsx create mode 100644 apps/marketing/src/components/blog/same-call-demo.tsx create mode 100644 apps/marketing/src/components/blog/shared.ts create mode 100644 apps/marketing/src/content/blog/mcp-cli-same-thing.mdx diff --git a/apps/marketing/astro.config.mjs b/apps/marketing/astro.config.mjs index 877af5b812..30275f77a4 100644 --- a/apps/marketing/astro.config.mjs +++ b/apps/marketing/astro.config.mjs @@ -5,6 +5,7 @@ import { unstable_readConfig } from "wrangler"; import tailwindcss from "@tailwindcss/vite"; import react from "@astrojs/react"; +import mdx from "@astrojs/mdx"; import cloudflare from "@astrojs/cloudflare"; @@ -29,7 +30,7 @@ const wranglerPublicDefine = () => { export default defineConfig({ site: "https://executor.sh", output: "server", - integrations: [react()], + integrations: [react(), mdx()], vite: { plugins: [tailwindcss()], define: wranglerPublicDefine(), diff --git a/apps/marketing/package.json b/apps/marketing/package.json index 6b3592652e..44365e82f4 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -15,10 +15,11 @@ }, "dependencies": { "@astrojs/cloudflare": "^13.0.0", + "@astrojs/mdx": "^6.0.3", "@astrojs/react": "^5.0.4", "@executor-js/react": "workspace:*", "@tailwindcss/vite": "^4.2.2", - "astro": "^6.1.3", + "astro": "^6.4.8", "clsx": "^2.1.1", "motion": "^12.38.0", "posthog-js": "^1.372.5", diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt index d98199e407..26f4f03c91 100644 --- a/apps/marketing/public/llms.txt +++ b/apps/marketing/public/llms.txt @@ -23,6 +23,11 @@ Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted - [Pricing](https://executor.sh/#pricing): plans for Executor Cloud. - [Install](https://executor.sh/#install): install the CLI and connect your first agent. +## Blog + +- [MCPs, CLIs, and APIs are the same thing](https://executor.sh/blog/mcp-cli-same-thing): interactive tour of tool-calling envelopes, why context bloat is a client problem, and what the MCP-vs-CLI paper actually measured. +- [Why MCP had so many growing pains](https://executor.sh/blog/why-mcp-had-growing-pains): what went wrong in MCP's first year and where it goes from here. + ## Source - [GitHub](https://github.com/UsefulSoftwareCo/executor): source for the integration layer, plugins, and hosts. diff --git a/apps/marketing/src/components/blog/context-flood-demo.tsx b/apps/marketing/src/components/blog/context-flood-demo.tsx new file mode 100644 index 0000000000..a30c324a7c --- /dev/null +++ b/apps/marketing/src/components/blog/context-flood-demo.tsx @@ -0,0 +1,180 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + ))} + + + ); +} + +export function ContextFloodDemo() { + const [iface, setIface] = useState("mcp"); + const [loading, setLoading] = useState("eager"); + + const segments: ReadonlyArray = [ + { label: "system prompt", tok: SYSTEM_TOK }, + ...CELLS[iface][loading], + ]; + const total = cellTotal(iface, loading); + const totalDisplay = useAnimatedNumber(total); + const flooding = segments.some((s) => s.flood); + + return ( +
+

+ {iface === "cli" ? "CLI" : "MCP"} with tools loaded{" "} + {loading === "lazy" ? "on demand" : "up front"}: about {fmt(total)} tokens spent before your + first message. +

+ +
+ setIface(v as Interface_)} + options={[ + { id: "cli", label: "CLI" }, + { id: "mcp", label: "MCP server" }, + ]} + /> + setLoading(v as Loading)} + options={[ + { id: "lazy", label: "On demand" }, + { id: "eager", label: "Everything up front" }, + ]} + /> +
+ +
+
+ {segments.map((s) => ( +
+
+ {s.label} + ~{fmt(s.tok)} tok +
+
+
+
+
+ ))} +
+
+
~{fmt(totalDisplay)}
+
tokens before your first message
+
+ resent with every request +
+
+
+ +
+ {(["cli", "mcp"] as const).flatMap((i) => + (["lazy", "eager"] as const).map((l) => ( + + )), + )} +
+ +
+ Flip the interface: the number barely moves. Flip the loading strategy: ~20x. The bloat + lives on one axis, and it is not the protocol axis. +
+
+ ); +} diff --git a/apps/marketing/src/components/blog/harness-spread-demo.tsx b/apps/marketing/src/components/blog/harness-spread-demo.tsx new file mode 100644 index 0000000000..3a889f34b0 --- /dev/null +++ b/apps/marketing/src/components/blog/harness-spread-demo.tsx @@ -0,0 +1,158 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system +
+ ) : ( +
+ + None of them. No MCP server was attached to any run in this chart — + the 28x spread between the cheapest and most expensive harness is pure harness + overhead. + + +
+ )} + + ) : ( + <> +
+ Cost with MCP attached ÷ cost without, per harness +
+
+ + {TABLE3.map((p) => ( +
+ + {p.name}{" "} + + {p.ratio}×{p.note ? ` (${p.note})` : ""} + + +
+ +
+
+ ))} +
+ ← cheaper with MCP + more expensive with MCP → +
+
+
+ + Four of five harnesses got cheaper with the MCP server attached. + Across the thirteen strictly paired runs the median ratio is 0.93 — + the authors call the comparison inconclusive. + + +
+ + )} + + ); +} diff --git a/apps/marketing/src/components/blog/install-race-demo.tsx b/apps/marketing/src/components/blog/install-race-demo.tsx new file mode 100644 index 0000000000..a33828ca78 --- /dev/null +++ b/apps/marketing/src/components/blog/install-race-demo.tsx @@ -0,0 +1,134 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + + +
+ {LANES.map((lane) => { + const shown = visibleSteps(lane); + const laneDone = shown >= lane.steps.length && tick >= 0; + return ( +
+
+ {lane.title} + {lane.sub} +
+
+ {lane.steps.map((s, idx) => ( +
+
+ ))} +
+
+ {laneDone + ? `${lane.steps.length} steps · ${lane.steps.some((s) => s.bad) ? "1 restart, session lost" : "no restart"}` + : " "} +
+
+ ); + })} +
+ +
+ The restart is not in the protocol. MCP lets a client connect to a new server whenever it + wants and notifications/tools/list_changed exists so + the tool list can update in place. The middle lane is a client implementation choice. +
+ + ); +} diff --git a/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx b/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx new file mode 100644 index 0000000000..c85ffb9d59 --- /dev/null +++ b/apps/marketing/src/components/blog/mcp-to-cli-demo.tsx @@ -0,0 +1,155 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls; the product design-system + + +
+
+
+ MCP server github · 44 tools +
+
+ {TOOLS.map((t) => ( +
+ {t.slug} + {t.help} + {t.destructive ? destructive : null} +
+ ))} +
… {REST} more
+
+
+ + + +
+
+ Generated CLI{" "} + {done ? "github · 44 commands" : "—"} +
+
+
+ + + + + +
+
+              
+                {"$ "}
+                {"github --help\n"}
+                {compiled ? (
+                  <>
+                    {"Commands:\n"}
+                    {TOOLS.slice(0, Math.min(built, TOOLS.length)).map((t) => (
+                      
+                        {"  "}
+                        {t.cli.padEnd(17)}
+                        {t.help}
+                        {t.destructive ?  [destructive] : null}
+                        {"\n"}
+                      
+                    ))}
+                    {built >= totalRows ? (
+                      {`  … ${REST} more\n`}
+                    ) : null}
+                  
+                ) : (
+                  {"\n(nothing here yet — press Compile to CLI)"}
+                )}
+              
+            
+
+
+
+ +
+ {done + ? "44 tools in, 44 commands out. Names, schemas, descriptions, and the destructive flag all survived. The only thing that changed is how you invoke them." + : " "} +
+ + ); +} diff --git a/apps/marketing/src/components/blog/same-call-demo.tsx b/apps/marketing/src/components/blog/same-call-demo.tsx new file mode 100644 index 0000000000..104f1fd085 --- /dev/null +++ b/apps/marketing/src/components/blog/same-call-demo.tsx @@ -0,0 +1,173 @@ +"use client"; + +/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled + controls (segmented toggles, mono labels); the product design-system + + ))} + + +
+
+ + + + + + create an issue on acme/api +
+
+          
+            
+          
+        
+
+
+ The highlighted parts are the operation. Everything else is + the envelope. +
+ + ); +} diff --git a/apps/marketing/src/components/blog/shared.ts b/apps/marketing/src/components/blog/shared.ts new file mode 100644 index 0000000000..84e38547c2 --- /dev/null +++ b/apps/marketing/src/components/blog/shared.ts @@ -0,0 +1,72 @@ +import { useEffect, useRef, useState } from "react"; + +/** Formats 12345 as "12,345". */ +export const fmt = (n: number) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + +export function usePrefersReducedMotion(): boolean { + const [reduced, setReduced] = useState(false); + useEffect(() => { + const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); + const sync = () => setReduced(mq.matches); + sync(); + mq.addEventListener("change", sync); + return () => mq.removeEventListener("change", sync); + }, []); + return reduced; +} + +/** Eases a displayed integer toward `target` with requestAnimationFrame. */ +export function useAnimatedNumber(target: number): number { + const reduced = usePrefersReducedMotion(); + const [display, setDisplay] = useState(target); + const fromRef = useRef(target); + const rafRef = useRef(null); + + useEffect(() => { + if (reduced) { + setDisplay(target); + fromRef.current = target; + return; + } + const from = fromRef.current; + if (from === target) return; + const start = performance.now(); + const dur = 450; + const tick = (now: number) => { + const t = Math.min(1, (now - start) / dur); + const eased = 1 - Math.pow(1 - t, 3); + setDisplay(Math.round(from + (target - from) * eased)); + if (t < 1) { + rafRef.current = requestAnimationFrame(tick); + } else { + fromRef.current = target; + } + }; + rafRef.current = requestAnimationFrame(tick); + return () => { + if (rafRef.current) cancelAnimationFrame(rafRef.current); + fromRef.current = target; + }; + }, [target, reduced]); + + return display; +} + +/** True while `ref`'s element is at least partially on screen. Autoplaying + * widgets gate their timers on this so nothing animates offscreen. */ +export function useInView(ref: React.RefObject): boolean { + const [inView, setInView] = useState(false); + useEffect(() => { + const el = ref.current; + if (!el || typeof IntersectionObserver === "undefined") { + setInView(true); + return; + } + const io = new IntersectionObserver(([entry]) => setInView(entry?.isIntersecting ?? false), { + threshold: 0.2, + }); + io.observe(el); + return () => io.disconnect(); + }, [ref]); + return inView; +} diff --git a/apps/marketing/src/content.config.ts b/apps/marketing/src/content.config.ts index 11e6607ac2..d50e478c65 100644 --- a/apps/marketing/src/content.config.ts +++ b/apps/marketing/src/content.config.ts @@ -1,11 +1,12 @@ import { defineCollection, z } from "astro:content"; import { glob } from "astro/loaders"; -// Blog posts authored as Markdown under src/content/blog. The glob loader -// turns each file into a collection entry whose `id` is the slug used in the -// /blog/[...slug] route (filename without extension). +// Blog posts authored as Markdown or MDX under src/content/blog. The glob +// loader turns each file into a collection entry whose `id` is the slug used +// in the /blog/[...slug] route (filename without extension). MDX is for posts +// that embed interactive island components. const blog = defineCollection({ - loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }), + loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }), schema: z.object({ title: z.string(), description: z.string(), diff --git a/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx new file mode 100644 index 0000000000..83464bbdf0 --- /dev/null +++ b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx @@ -0,0 +1,94 @@ +--- +title: "MCPs, CLIs, and APIs are the same thing" +description: "You can compile an MCP server into a CLI without changing a single tool. An interactive look at what tool calling actually is, why context bloat is a client problem, and what that viral paper really measured." +date: 2026-08-23 +author: "Rhys Sullivan" +--- + +import { SameCallDemo } from "../../components/blog/same-call-demo"; +import { McpToCliDemo } from "../../components/blog/mcp-to-cli-demo"; +import { ContextFloodDemo } from "../../components/blog/context-flood-demo"; +import { HarnessSpreadDemo } from "../../components/blog/harness-spread-demo"; +import { InstallRaceDemo } from "../../components/blog/install-race-demo"; + +Last week a [paper](https://arxiv.org/abs/2608.08654) made the rounds claiming that agents finish software tasks just as reliably without MCP, and 5 to 28 times cheaper. The replies sorted themselves into the usual camps: MCP is dead, CLIs won, protocols were a mistake. + +The whole argument is built on a category error. MCP servers, CLIs, and APIs are not three competing technologies. They are three envelopes around the same thing: an agent invoking a tool. + +Here is one operation — create a GitHub issue — in all three envelopes. Flip between them and watch what actually changes. + + + +Same operation, same arguments, same GitHub on the other end. What changes is the delivery mechanism: a JSON-RPC message from a connected client, a process spawned in a shell, an HTTP request with a bearer token. Every take of the form "CLIs beat MCP" is a claim about envelopes, not about capabilities. + +And because they are just envelopes, you can convert between them freely. Let's do that. + +## Compile an MCP server into a CLI + +The official GitHub MCP server publishes 44 tools. Each one has a name, a JSON schema for its arguments, a description, and annotations like whether it is destructive. Press `Compile to CLI`. + + + +Nothing about the tools changed. Not the count, not the schemas, not the descriptions. What changed is the execution environment you need to invoke them: on the left, a client that speaks JSON-RPC; on the right, a shell, a filesystem, a `PATH`, and a process per invocation. + +Note the direction of that arrow, though. MCP to CLI is mechanical, because the catalog declares its entire action space up front. The reverse is not. A CLI declares nothing — the only way to find out what `gh` can do is to recursively run `--help` and hope the help text is complete. Keep that asymmetry in mind; it comes back later. + +If context bloat were a property of MCP, this compile step would be a miracle cure: same 44 tools, bloat gone. That is obviously absurd, which tells you the bloat was never coming from the protocol. + +## Context bloat is a loading strategy + +When someone says "MCP floods your context window," the thing being described is a client that takes every tool schema from every connected server and pastes all of them into every request. That is a decision the client made. The protocol did not make it. + +There are two independent choices hiding in every setup: which interface you use, and how tools get loaded into context. Try both axes and watch which one the number follows. + + + +The number follows the loading axis. A CLI feels cheap because no one would ever dream of inlining the help text for every `gh` subcommand up front — the model runs `--help` for the two commands it needs, when it needs them. Then we built MCP clients that do exactly the thing nobody would do to a CLI, and blamed the protocol for the result. + +This is not a hypothetical; it is in the paper's own data. Against the same 44-tool GitHub server, the harnesses that resent the full catalog with every request spent a median of 216,986 tokens per completed run. The one harness that exposed a two-tool gateway — list tools, invoke tool — and fetched descriptions on demand spent 70,836. A 3.1x difference the authors themselves call "a property of the agent scaffolding rather than of the protocol." They also cite [Anthropic's write-up on code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) as establishing that eager transmission "is a design choice rather than a requirement of the protocol." + +## What the paper actually measured + +So where did 5 to 28x come from? Here is the chart behind the headline: seven harnesses, the same GitHub task, median input tokens per completed run. Before reading on, answer the question under the chart. + + + +The 28x is a comparison between harnesses with **no MCP server attached to any run**. That is not my gotcha — it is the abstract's own phrasing: the two lean harnesses were "5.0× to 28× cheaper than the five scaffoldings that do support MCP, comparing CLI runs alone with no MCP server attached anywhere." Claude Code starts around 22,000 tokens before the task says anything, because it ships 27 built-in tool schemas with every request. pi finishes the _entire task_ in 14,660. That spread — the headline number — is pure harness overhead, measured in a room MCP never entered. + +When the authors did attach the server, four of five harnesses got _cheaper_, the median paired ratio was 0.93, and both arms failed the same number of times. Their own stated noise floor is a factor of two: anything smaller "cannot be distinguished from run-to-run variation." + +To be fair to the authors, they know this — the paper is literally titled _The Scaffolding Matters More Than the Interface_. My complaints are that it tests one task against one server, nearly every cell is a single run, and it still got passed around as "MCP costs 28x more." The measured content of the paper is one of the better arguments for the thesis of this post. + +## The restart is not in the spec + +The strongest real advantage CLIs have today has nothing to do with tokens. It is this: + + + +Tell an agent to install a CLI and it is using it seconds later, mid-conversation. Add an MCP server to most clients and you are editing a config file, restarting the client, losing your session, and re-authenticating. That friction is real and I will not defend it. + +But locate it precisely. The protocol lets a client connect to a new server at any point in a session, and `notifications/tools/list_changed` exists so the tool list can update in place. The middle lane is a client implementation gap — the same species of gap as eager loading. Bash had 37 years of polish behind it when agents arrived; most MCP clients were built in months. That is an argument about maturity, not about which envelope is correct. + +## So when do you reach for each? + +If they are all the same thing underneath, choosing between them is about what each envelope buys you. + +**MCP** is the right envelope for interactions that are _about_ the agent session: elicitation (ask the human before doing something), MCP Apps (render UI inside the chat), triggers and notifications (push state changes back into a running session). None of that maps onto a CLI or a bare API. You also get the enumerable action space and destructive-action annotations that made the compile step above possible. + +**APIs** are right for raw data access and bulk work, and they are the fastest way to make an existing product agent-accessible. The OpenAPI spec already carries descriptions and marks what is destructive. If your MCP server is a thin mirror of your API, the spec alone was probably enough. + +**CLIs** are the best debugging experience an agent can have today, because bash composability — chain commands, grep the output, retry — is unmatched. The price is a shell and a sandbox per agent, plus an action space you cannot enumerate. Over a long enough horizon I think CLIs are for humans; today they are often the pragmatic choice. + +I wrote more about how MCP ended up with its scar tissue in [Why MCP had so many growing pains](/blog/why-mcp-had-growing-pains). + +## It's all tool calling + +You can convert an API into an MCP server, the MCP server into a CLI, and the CLI back into API calls, and at no point did the agent's actual capabilities change. What changed is the dependencies you carry — a client, a shell, an HTTP stack — and the interaction patterns you unlock. + +That is the bet behind [Executor](https://executor.sh): the harness should not care which envelope you bring. MCP server, OpenAPI spec, GraphQL, CLI — it all lands in one catalog, loaded on demand, behind one `execute` tool. My personal instance has around 10,000 tools in it, a mix of MCP servers and raw APIs, and agents work through it comfortably — not because the envelopes are exotic, but because the loading strategy is right. + +Stop arguing about envelopes. Fix the client. + +--- + +_Numbers in this post are from [arXiv:2608.08654](https://arxiv.org/abs/2608.08654), Tables 2, 3, and 5: median input tokens per completed run. Token figures in the loading-strategy widget are illustrative, anchored to the paper's measurements (~800 tokens per tool schema, 44 tools on the official GitHub MCP server)._ diff --git a/apps/marketing/src/styles/global.css b/apps/marketing/src/styles/global.css index b23ec2755a..8462eba494 100644 --- a/apps/marketing/src/styles/global.css +++ b/apps/marketing/src/styles/global.css @@ -1673,3 +1673,671 @@ body { .blog-prose h3 + p { margin-top: 0; } + +/* ══ Blog interactive widgets (bw-*) ══════════════════════════════ + Shared frame + primitives for the interactive diagrams embedded in + MDX posts (src/components/blog/*). Grayscale only, per design.md: + hierarchy comes from tone, hatching, and mono labels, never hue. */ +.bw { + border: 1px solid var(--color-rule); + border-radius: 14px; + background: white; + padding: 1.1rem 1.2rem 1rem; + font-size: 13.5px; + line-height: 1.5; + color: var(--color-ink); +} +.blog-prose .bw { + margin: 2.25rem 0; +} +/* Reset the editorial pre/code treatment inside widgets so .code-window + panes keep their own chrome. */ +.blog-prose .bw pre { + margin: 0; + padding: 1.1rem 1.2rem; + background: transparent; + border: 0; + border-radius: 0; + font-size: 12.5px; + line-height: 1.7; +} +.blog-prose .bw code { + background: transparent; + border: 0; + padding: 0; + font-size: inherit; +} + +.bw-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + flex-wrap: wrap; + margin-bottom: 0.9rem; +} +.bw-head--stack { + justify-content: flex-start; + gap: 1.5rem; +} +.bw-eyebrow { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ink-3); +} +.bw-foot { + margin-top: 0.85rem; + padding-top: 0.75rem; + border-top: 1px solid var(--color-rule); + font-size: 12.5px; + line-height: 1.55; + color: var(--color-ink-2); + min-height: 2em; +} +.bw-mono-note { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink-3); + white-space: nowrap; +} +.bw-mono-inline { + font-family: var(--font-mono); + font-size: 0.92em; +} +.bw-dim { + color: var(--color-ink-3); +} +/* reset: the invariant-fact highlight. */ +.bw-hl { + background: color-mix(in srgb, var(--color-accent) 7%, transparent); + color: var(--color-ink); + font-weight: 600; + border-radius: 3px; + padding: 0 2px; +} + +/* Segmented control */ +.bw-seg { + display: inline-flex; + gap: 2px; + padding: 2px; + border: 1px solid var(--color-rule); + border-radius: 8px; + background: var(--color-surface-2); +} +.bw-seg__btn { + font-family: var(--font-mono); + font-size: 11.5px; + padding: 4px 10px; + border-radius: 6px; + border: 1px solid transparent; + color: var(--color-ink-2); + cursor: pointer; + white-space: nowrap; + transition: + color 0.15s ease, + background 0.15s ease; +} +.bw-seg__btn:hover { + color: var(--color-ink); +} +.bw-seg__btn[data-on="true"] { + background: white; + border-color: var(--color-rule); + color: var(--color-ink); + box-shadow: 0 1px 2px rgba(13, 13, 16, 0.05); +} +.bw-seg__btn:focus-visible, +.bw-btn:focus-visible, +.bw-map__cell:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +/* Secondary action button */ +.bw-btn { + font-size: 13px; + font-weight: 500; + padding: 0 13px; + height: 30px; + border: 1px solid var(--color-rule-strong); + border-radius: 8px; + background: white; + color: var(--color-ink); + cursor: pointer; + white-space: nowrap; + transition: + border-color 0.15s ease, + background 0.15s ease; +} +.bw-btn:hover { + border-color: var(--color-ink-3); + background: var(--color-surface-2); +} +.bw-btn:disabled { + opacity: 0.5; + cursor: default; +} + +/* Code-window pane inside a widget: drop the heavy marketing shadow. */ +.bw-window { + box-shadow: none; + border-radius: 10px; +} +.bw-code { + white-space: pre-wrap; + word-break: break-word; +} +.bw-code--term { + min-height: 200px; +} + +/* ── Compile pane pair (mcp-to-cli) ── */ +.bw-pair { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; + align-items: stretch; +} +@media (min-width: 720px) { + .bw-pair { + grid-template-columns: 1fr auto 1fr; + } +} +.bw-pane { + min-width: 0; + display: flex; + flex-direction: column; + transition: opacity 0.3s ease; +} +.bw-pane[data-dim="true"] { + opacity: 0.6; +} +.bw-pane__title { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; + font-size: 13px; + font-weight: 600; + margin-bottom: 0.5rem; +} +.bw-pane__arrow { + align-self: center; + color: var(--color-ink-3); + font-size: 18px; + padding: 0 0.2rem; + transform: rotate(90deg); +} +@media (min-width: 720px) { + .bw-pane__arrow { + transform: none; + } +} +.bw-window--fill { + flex: 1; +} +.bw-rows { + border: 1px solid var(--color-rule); + border-radius: 10px; + overflow: hidden; + flex: 1; +} +.bw-row { + display: flex; + align-items: baseline; + gap: 0.6rem; + padding: 0.42rem 0.75rem; + border-top: 1px solid var(--color-rule); + min-width: 0; +} +.bw-row:first-child { + border-top: 0; +} +.bw-row__name { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-ink); + white-space: nowrap; +} +.bw-row__desc { + flex: 1; + min-width: 0; + font-size: 12px; + color: var(--color-ink-2); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.bw-row--rest { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink-3); +} +.bw-tag { + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-ink-2); + border: 1px solid var(--color-rule-strong); + border-radius: 4px; + padding: 1px 5px; + white-space: nowrap; +} +.bw-fade-in { + animation: bw-fade 0.25s ease both; +} +@keyframes bw-fade { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* ── Context flood (2×2) ── */ +.bw-axis { + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.bw-axis__label { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-ink-3); +} +.bw-flood { + display: grid; + grid-template-columns: 1fr; + gap: 1.25rem; + align-items: center; + margin-bottom: 1rem; +} +@media (min-width: 640px) { + .bw-flood { + grid-template-columns: 1fr 200px; + } +} +.bw-flood__rows { + display: flex; + flex-direction: column; + gap: 0.7rem; + min-width: 0; +} +.bw-flood__meta { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.6rem; + margin-bottom: 0.3rem; + font-size: 12.5px; +} +.bw-flood__name { + color: var(--color-ink-2); +} +.bw-flood__tok { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink-3); + white-space: nowrap; +} +.bw-flood__track { + height: 12px; + border-radius: 3px; + background: var(--color-surface-2); + border: 1px solid var(--color-rule); + overflow: hidden; +} +.bw-flood__fill { + height: 100%; + min-width: 3px; + background: rgba(13, 13, 16, 0.45); + transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1); +} +.bw-flood__fill[data-flood="true"] { + background: repeating-linear-gradient( + -45deg, + rgba(13, 13, 16, 0.55) 0 3px, + rgba(13, 13, 16, 0.18) 3px 7px + ); +} +.bw-flood__total { + text-align: center; +} +.bw-flood__num { + font-family: var(--font-mono); + font-size: 27px; + font-weight: 600; + font-variant-numeric: tabular-nums; + letter-spacing: -0.02em; + color: var(--color-ink); +} +.bw-flood__cap { + font-size: 12px; + color: var(--color-ink-2); + margin-top: 0.15rem; +} +.bw-badge { + display: inline-block; + margin-top: 0.55rem; + font-family: var(--font-mono); + font-size: 10.5px; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--color-ink-2); + border: 1px solid var(--color-rule-strong); + border-radius: 999px; + padding: 2px 9px; + visibility: hidden; +} +.bw-badge[data-show="true"] { + visibility: visible; +} +.bw-map { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; +} +.bw-map__cell { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; + padding: 0.5rem 0.7rem; + border: 1px solid var(--color-rule); + border-radius: 8px; + background: white; + cursor: pointer; + text-align: left; + transition: + border-color 0.15s ease, + background 0.15s ease; +} +.bw-map__cell:hover { + border-color: var(--color-rule-strong); +} +.bw-map__cell[data-on="true"] { + border-color: var(--color-ink); + background: var(--color-surface-2); +} +.bw-map__label { + font-size: 12px; + font-weight: 500; + color: var(--color-ink); +} +.bw-map__val { + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-ink-3); + white-space: nowrap; +} + +/* ── Harness spread bars + paired ratios ── */ +.bw-bars { + display: flex; + flex-direction: column; + gap: 0.45rem; +} +.bw-bars__row { + display: grid; + grid-template-columns: 92px 1fr 64px; + align-items: center; + gap: 0.6rem; +} +.bw-bars__name { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.bw-bars__track { + height: 13px; + background: var(--color-surface-2); + border-radius: 3px; + overflow: hidden; +} +.bw-bars__fill { + height: 100%; + background: rgba(13, 13, 16, 0.55); + border-radius: 3px; +} +.bw-bars__val { + font-family: var(--font-mono); + font-size: 11px; + font-variant-numeric: tabular-nums; + color: var(--color-ink-3); + text-align: right; + white-space: nowrap; +} +.bw-quiz { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.9rem; + flex-wrap: wrap; + margin-top: 1rem; + padding-top: 0.85rem; + border-top: 1px solid var(--color-rule); + font-size: 13.5px; + color: var(--color-ink-2); +} +.bw-quiz--answer { + color: var(--color-ink-2); +} +.bw-quiz strong { + color: var(--color-ink); + font-weight: 600; +} +.bw-quiz > span { + flex: 1; + min-width: 240px; +} +.bw-ratio { + display: flex; + flex-direction: column; + gap: 0.55rem; +} +.bw-ratio__row { + display: grid; + grid-template-columns: 150px 1fr; + align-items: center; + gap: 0.6rem; +} +.bw-ratio__row--axis { + margin-bottom: 0.1rem; +} +.bw-ratio__name { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.bw-ratio__val { + color: var(--color-ink-3); +} +.bw-ratio__axis { + position: relative; + height: 14px; +} +.bw-ratio__axis span { + position: absolute; + transform: translateX(-50%); + font-family: var(--font-mono); + font-size: 10px; + color: var(--color-ink-3); + white-space: nowrap; +} +.bw-ratio__track { + position: relative; + height: 18px; + background: var(--color-surface-2); + border-radius: 3px; +} +/* The 1× line every track shares, positioned by --one-x on .bw-ratio. */ +.bw-ratio__track::after { + content: ""; + position: absolute; + top: -2px; + bottom: -2px; + left: var(--one-x); + width: 1px; + background: var(--color-rule-strong); +} +.bw-ratio__marker { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 9px; + height: 9px; + border-radius: 999px; + background: white; + border: 2px solid var(--color-ink-3); + z-index: 1; +} +.bw-ratio__marker[data-cheaper="true"] { + background: var(--color-ink); + border-color: var(--color-ink); +} +.bw-ratio__legend { + display: flex; + justify-content: space-between; + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-ink-3); + margin-top: 0.2rem; +} + +/* ── Install race lanes ── */ +.bw-lanes { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; +} +@media (min-width: 720px) { + .bw-lanes { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} +.bw-lane { + border: 1px solid var(--color-rule); + border-radius: 10px; + padding: 0.75rem 0.85rem 0.6rem; + display: flex; + flex-direction: column; + transition: border-color 0.2s ease; +} +.bw-lane[data-done="true"] { + border-color: var(--color-rule-strong); +} +.bw-lane__head { + display: flex; + align-items: baseline; + gap: 0.5rem; + margin-bottom: 0.65rem; +} +.bw-lane__title { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.bw-lane__sub { + font-size: 11.5px; + color: var(--color-ink-3); +} +.bw-lane__steps { + display: flex; + flex-direction: column; + gap: 0.42rem; + flex: 1; +} +.bw-step { + display: flex; + align-items: baseline; + gap: 0.5rem; + transition: opacity 0.25s ease; +} +.bw-step[data-state="off"] { + opacity: 0.32; +} +.bw-step__dot { + width: 7px; + height: 7px; + border-radius: 999px; + flex-shrink: 0; + border: 1.5px solid var(--color-rule-strong); + background: white; + align-self: center; +} +.bw-step[data-state="on"] .bw-step__dot, +.bw-step[data-state="done"] .bw-step__dot { + background: var(--color-ink); + border-color: var(--color-ink); +} +.bw-step[data-state="bad"] .bw-step__dot { + background: white; + border-color: var(--color-ink); +} +.bw-step__text { + font-family: var(--font-mono); + font-size: 11.5px; + color: var(--color-ink-2); + min-width: 0; +} +.bw-step[data-state="bad"] .bw-step__text { + text-decoration: underline; + text-decoration-style: wavy; + text-decoration-color: var(--color-ink-3); + text-underline-offset: 3px; +} +.bw-step__ok { + margin-left: auto; + font-family: var(--font-mono); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-ink); + border: 1px solid var(--color-rule-strong); + border-radius: 4px; + padding: 0 4px; + white-space: nowrap; +} +.bw-lane__tally { + margin-top: 0.6rem; + padding-top: 0.5rem; + border-top: 1px solid var(--color-rule); + font-family: var(--font-mono); + font-size: 10.5px; + color: var(--color-ink-3); + min-height: 1.6em; +} + +@media (prefers-reduced-motion: reduce) { + .bw-flood__fill, + .bw-pane, + .bw-step, + .bw-seg__btn, + .bw-btn, + .bw-map__cell, + .bw-lane { + transition: none; + } + .bw-fade-in { + animation: none; + } +} diff --git a/bun.lock b/bun.lock index 46d91d7cee..2eb065d532 100644 --- a/bun.lock +++ b/bun.lock @@ -330,10 +330,11 @@ "version": "0.0.5", "dependencies": { "@astrojs/cloudflare": "^13.0.0", + "@astrojs/mdx": "^6.0.3", "@astrojs/react": "^5.0.4", "@executor-js/react": "workspace:*", "@tailwindcss/vite": "^4.2.2", - "astro": "^6.1.3", + "astro": "^6.4.8", "clsx": "^2.1.1", "motion": "^12.38.0", "posthog-js": "^1.372.5", @@ -1312,17 +1313,19 @@ "@astrojs/cloudflare": ["@astrojs/cloudflare@13.1.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/underscore-redirects": "1.0.3", "@cloudflare/vite-plugin": "^1.25.6", "piccolore": "^0.1.3", "tinyglobby": "^0.2.15", "vite": "^7.3.1" }, "peerDependencies": { "astro": "^6.0.0", "wrangler": "^4.61.1" } }, "sha512-Ogl8p4MifPMHbpHKJL78eqEL+I3wbHrYmo83P/FkKZQ9VzzKi2A1RjnbaiiPAwrA8xQOqIUo4zlN7+Mse0aJAQ=="], - "@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="], + "@astrojs/compiler": ["@astrojs/compiler@4.0.0", "", {}, "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA=="], "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.8.0", "", { "dependencies": { "picomatch": "^4.0.3" } }, "sha512-J56GrhEiV+4dmrGLPNOl2pZjpHXAndWVyiVDYGDuw6MWKpBSEMLdFxHzeM/6sqaknw9M+HFfHZAcvi3OfT3D/w=="], - "@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.1.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/prism": "4.0.1", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "retext-smartypants": "^6.2.0", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-P+HnCsu2js3BoTc8kFmu+E9gOcFeMdPris75g+Zl4sY8+bBRbSQV6xzcBDbZ27eE7yBGEGQoqjpChx+KJYIPYQ=="], + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.2.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.0", "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw=="], - "@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="], + "@astrojs/mdx": ["@astrojs/mdx@6.0.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.10.0", "@astrojs/markdown-remark": "7.2.0", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-satteri": "0.3.0", "astro": "^6.4.0" }, "optionalPeers": ["@astrojs/markdown-satteri"] }, "sha512-+4P3ZvwsRAqAbBgY+uZMewFo3ficlIBPZfu/Luk+v4ia/ZOuFhpsw7r+7672uT2Fc1UPdp7yW0eU5egvSq0wbw=="], + + "@astrojs/prism": ["@astrojs/prism@4.0.2", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA=="], "@astrojs/react": ["@astrojs/react@5.0.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.9.0", "@vitejs/plugin-react": "^5.2.0", "devalue": "^5.6.4", "ultrahtml": "^1.6.0", "vite": "^7.3.2" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-yDNE4VnKOzCjH9dCBi7pT4F6kpI3M9TkS+uxnCB0sGIS6t5vKonOY+Hs/UUnSajJGT5jeBRfpI9IQp+r/n1fBA=="], - "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], + "@astrojs/telemetry": ["@astrojs/telemetry@3.3.2", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", "which-pm-runs": "^1.1.0" } }, "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ=="], "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.3", "", {}, "sha512-cxnGSw+sJigBLdX4TMSZKkzV6C3gMLJMucDk2W+n281Xhie68T2/9f1+1NMNDCZsc5i0FED7Qt5I10g2O9wtZg=="], @@ -3366,7 +3369,7 @@ "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], - "astro": ["astro@6.1.7", "", { "dependencies": { "@astrojs/compiler": "^3.0.1", "@astrojs/internal-helpers": "0.8.0", "@astrojs/markdown-remark": "7.1.0", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^7.3.1", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-pvZysIUV2C2nRv8N7cXAkCLcfDQz/axAxF09SqiTz1B+xnvbhy6KzL2I6J15ZBXk8k0TfMD75dJ151QyQmAqZA=="], + "astro": ["astro@6.4.8", "", { "dependencies": { "@astrojs/compiler": "^4.0.0", "@astrojs/internal-helpers": "0.10.0", "@astrojs/markdown-remark": "7.2.0", "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.8.1", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.5", "vfile": "^6.0.3", "vite": "^7.3.2", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "./bin/astro.mjs" } }, "sha512-KK5lX90uU9EeVaTjINyj3sy9/NFXVa59aowaqbWBDDKLXZh4rr7GwIaCFYVetE22MJtsCNFerQXn0vlCLmpP/Q=="], "async": ["async@2.6.4", "", { "dependencies": { "lodash": "^4.17.14" } }, "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA=="], @@ -3808,8 +3811,6 @@ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "dmg-builder": ["dmg-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg=="], "dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="], @@ -4328,7 +4329,7 @@ "is-descriptor": ["is-descriptor@1.0.3", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw=="], - "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], "is-electron": ["is-electron@2.2.2", "", {}, "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="], @@ -5614,8 +5615,6 @@ "ts-md5": ["ts-md5@2.0.1", "", {}, "sha512-yF35FCoEOFBzOclSkMNEUbFQZuv89KEQ+5Xz03HrMSGUGB1+r+El+JiGOFwsP4p9RFNzwlrydYoTLvPOuICl9w=="], - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="], @@ -5864,6 +5863,14 @@ "@astrojs/cloudflare/vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], + + "@astrojs/mdx/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], + + "@astrojs/mdx/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "@astrojs/mdx/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + "@astrojs/react/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.9.0", "", { "dependencies": { "picomatch": "^4.0.4" } }, "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg=="], "@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], @@ -6434,14 +6441,22 @@ "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], - "astro/@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="], + "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.10.0", "", { "dependencies": { "@types/hast": "^3.0.4", "@types/mdast": "^4.0.4", "js-yaml": "^4.1.1", "picomatch": "^4.0.4", "retext-smartypants": "^6.2.0", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "unified": "^11.0.5" } }, "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw=="], - "astro/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "astro/devalue": ["devalue@5.9.1", "", {}, "sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw=="], + + "astro/es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + + "astro/get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="], - "astro/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "astro/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "astro/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "astro/vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], + "astro/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "atlas-api-client/rxjs": ["rxjs@5.5.12", "", { "dependencies": { "symbol-observable": "1.0.1" } }, "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw=="], "atmn/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], @@ -6594,6 +6609,8 @@ "ink-confirm-input/ink-text-input": ["ink-text-input@3.3.0", "", { "dependencies": { "chalk": "^3.0.0", "prop-types": "^15.5.10" }, "peerDependencies": { "ink": "^2.0.0", "react": "^16.5.2" } }, "sha512-gO4wrOf2ie3YuEARTIwGlw37lMjFn3Gk6CKIDrMlHb46WFMagZU7DplohjM24zynlqfnXA5UDEIfC2NBcvD8kg=="], + "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "jake/async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "jsonwebtoken/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -6798,6 +6815,10 @@ "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + + "@astrojs/mdx/@astrojs/internal-helpers/js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], + "@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], "@azure/identity/@azure/msal-node/@azure/msal-common": ["@azure/msal-common@16.4.1", "", {}, "sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw=="], @@ -7266,12 +7287,6 @@ "app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], - "astro/@clack/prompts/@clack/core": ["@clack/core@1.2.0", "", { "dependencies": { "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg=="], - - "astro/@clack/prompts/fast-string-width": ["fast-string-width@1.1.0", "", { "dependencies": { "fast-string-truncated-width": "^1.2.0" } }, "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ=="], - - "astro/@clack/prompts/fast-wrap-ansi": ["fast-wrap-ansi@0.1.6", "", { "dependencies": { "fast-string-width": "^1.1.0" } }, "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w=="], - "broadcast-channel/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], "broadcast-channel/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], @@ -7766,8 +7781,6 @@ "agents/yargs/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], - "astro/@clack/prompts/fast-string-width/fast-string-truncated-width": ["fast-string-truncated-width@1.2.1", "", {}, "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow=="], - "dir-compare/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], From 9dc6cb75f529394751532c84ed95c8d8d98b00db Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Sun, 23 Aug 2026 16:25:50 -0700 Subject: [PATCH 2/3] marketing: drop the paper section from the MCP/CLI post --- apps/marketing/public/llms.txt | 2 +- .../components/blog/context-flood-demo.tsx | 7 +- .../components/blog/harness-spread-demo.tsx | 158 ------------------ .../src/content/blog/mcp-cli-same-thing.mdx | 21 +-- apps/marketing/src/styles/global.css | 141 ---------------- 5 files changed, 8 insertions(+), 321 deletions(-) delete mode 100644 apps/marketing/src/components/blog/harness-spread-demo.tsx diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt index 26f4f03c91..754a10eaea 100644 --- a/apps/marketing/public/llms.txt +++ b/apps/marketing/public/llms.txt @@ -25,7 +25,7 @@ Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted ## Blog -- [MCPs, CLIs, and APIs are the same thing](https://executor.sh/blog/mcp-cli-same-thing): interactive tour of tool-calling envelopes, why context bloat is a client problem, and what the MCP-vs-CLI paper actually measured. +- [MCPs, CLIs, and APIs are the same thing](https://executor.sh/blog/mcp-cli-same-thing): interactive tour of tool-calling envelopes, why context bloat is a client problem, and why the MCP-vs-CLI debate is a category error. - [Why MCP had so many growing pains](https://executor.sh/blog/why-mcp-had-growing-pains): what went wrong in MCP's first year and where it goes from here. ## Source diff --git a/apps/marketing/src/components/blog/context-flood-demo.tsx b/apps/marketing/src/components/blog/context-flood-demo.tsx index a30c324a7c..ddef8551f1 100644 --- a/apps/marketing/src/components/blog/context-flood-demo.tsx +++ b/apps/marketing/src/components/blog/context-flood-demo.tsx @@ -12,10 +12,9 @@ import { fmt, useAnimatedNumber } from "./shared"; * (on demand or everything up front). Flipping the interface barely moves the * number; flipping the loading strategy moves it ~20x. * - * Figures are illustrative but anchored: the arXiv paper everyone cited - * measured the official GitHub MCP server at 44 tools, eager clients resending - * the whole catalog every request, and ~22k tokens for 27 built-in tool - * schemas (~800 tokens per schema). + * Figures are illustrative: the official GitHub MCP server's 44-tool catalog + * at roughly 800 tokens per tool schema, resent every request by eager + * clients. */ type Interface_ = "cli" | "mcp"; diff --git a/apps/marketing/src/components/blog/harness-spread-demo.tsx b/apps/marketing/src/components/blog/harness-spread-demo.tsx deleted file mode 100644 index 3a889f34b0..0000000000 --- a/apps/marketing/src/components/blog/harness-spread-demo.tsx +++ /dev/null @@ -1,158 +0,0 @@ -"use client"; - -/* eslint-disable react/forbid-elements -- blog widgets use bespoke styled - controls; the product design-system - - ) : ( -
- - None of them. No MCP server was attached to any run in this chart — - the 28x spread between the cheapest and most expensive harness is pure harness - overhead. - - -
- )} - - ) : ( - <> -
- Cost with MCP attached ÷ cost without, per harness -
-
- - {TABLE3.map((p) => ( -
- - {p.name}{" "} - - {p.ratio}×{p.note ? ` (${p.note})` : ""} - - -
- -
-
- ))} -
- ← cheaper with MCP - more expensive with MCP → -
-
-
- - Four of five harnesses got cheaper with the MCP server attached. - Across the thirteen strictly paired runs the median ratio is 0.93 — - the authors call the comparison inconclusive. - - -
- - )} - - ); -} diff --git a/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx index 83464bbdf0..efc6d1e044 100644 --- a/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx +++ b/apps/marketing/src/content/blog/mcp-cli-same-thing.mdx @@ -1,6 +1,6 @@ --- title: "MCPs, CLIs, and APIs are the same thing" -description: "You can compile an MCP server into a CLI without changing a single tool. An interactive look at what tool calling actually is, why context bloat is a client problem, and what that viral paper really measured." +description: "You can compile an MCP server into a CLI without changing a single tool. An interactive look at what tool calling actually is, why context bloat is a client problem, and why the MCP-vs-CLI debate is a category error." date: 2026-08-23 author: "Rhys Sullivan" --- @@ -8,10 +8,9 @@ author: "Rhys Sullivan" import { SameCallDemo } from "../../components/blog/same-call-demo"; import { McpToCliDemo } from "../../components/blog/mcp-to-cli-demo"; import { ContextFloodDemo } from "../../components/blog/context-flood-demo"; -import { HarnessSpreadDemo } from "../../components/blog/harness-spread-demo"; import { InstallRaceDemo } from "../../components/blog/install-race-demo"; -Last week a [paper](https://arxiv.org/abs/2608.08654) made the rounds claiming that agents finish software tasks just as reliably without MCP, and 5 to 28 times cheaper. The replies sorted themselves into the usual camps: MCP is dead, CLIs won, protocols were a mistake. +Every few weeks the debate comes back around: CLIs beat MCP, MCP is dead, agents just need bash. Benchmarks get run, threads get written, and everyone sorts into camps. The whole argument is built on a category error. MCP servers, CLIs, and APIs are not three competing technologies. They are three envelopes around the same thing: an agent invoking a tool. @@ -45,19 +44,7 @@ There are two independent choices hiding in every setup: which interface you use The number follows the loading axis. A CLI feels cheap because no one would ever dream of inlining the help text for every `gh` subcommand up front — the model runs `--help` for the two commands it needs, when it needs them. Then we built MCP clients that do exactly the thing nobody would do to a CLI, and blamed the protocol for the result. -This is not a hypothetical; it is in the paper's own data. Against the same 44-tool GitHub server, the harnesses that resent the full catalog with every request spent a median of 216,986 tokens per completed run. The one harness that exposed a two-tool gateway — list tools, invoke tool — and fetched descriptions on demand spent 70,836. A 3.1x difference the authors themselves call "a property of the agent scaffolding rather than of the protocol." They also cite [Anthropic's write-up on code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) as establishing that eager transmission "is a design choice rather than a requirement of the protocol." - -## What the paper actually measured - -So where did 5 to 28x come from? Here is the chart behind the headline: seven harnesses, the same GitHub task, median input tokens per completed run. Before reading on, answer the question under the chart. - - - -The 28x is a comparison between harnesses with **no MCP server attached to any run**. That is not my gotcha — it is the abstract's own phrasing: the two lean harnesses were "5.0× to 28× cheaper than the five scaffoldings that do support MCP, comparing CLI runs alone with no MCP server attached anywhere." Claude Code starts around 22,000 tokens before the task says anything, because it ships 27 built-in tool schemas with every request. pi finishes the _entire task_ in 14,660. That spread — the headline number — is pure harness overhead, measured in a room MCP never entered. - -When the authors did attach the server, four of five harnesses got _cheaper_, the median paired ratio was 0.93, and both arms failed the same number of times. Their own stated noise floor is a factor of two: anything smaller "cannot be distinguished from run-to-run variation." - -To be fair to the authors, they know this — the paper is literally titled _The Scaffolding Matters More Than the Interface_. My complaints are that it tests one task against one server, nearly every cell is a single run, and it still got passed around as "MCP costs 28x more." The measured content of the paper is one of the better arguments for the thesis of this post. +The good clients already work this way. Lazy loading, tool search, and code mode all defer tool descriptions until the model actually needs them — [Anthropic's write-up on code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) describes cutting token use dramatically by having the model write code that calls tools instead of front-loading every definition. Eager transmission is a client design choice, not a requirement of the protocol. Any benchmark that measures an eager client and reports the result as a property of MCP is measuring the client. ## The restart is not in the spec @@ -91,4 +78,4 @@ Stop arguing about envelopes. Fix the client. --- -_Numbers in this post are from [arXiv:2608.08654](https://arxiv.org/abs/2608.08654), Tables 2, 3, and 5: median input tokens per completed run. Token figures in the loading-strategy widget are illustrative, anchored to the paper's measurements (~800 tokens per tool schema, 44 tools on the official GitHub MCP server)._ +_Token figures in the loading-strategy widget are illustrative: roughly 800 tokens per tool schema, against the official GitHub MCP server's catalog of 44 tools._ diff --git a/apps/marketing/src/styles/global.css b/apps/marketing/src/styles/global.css index 8462eba494..54b0cfd54b 100644 --- a/apps/marketing/src/styles/global.css +++ b/apps/marketing/src/styles/global.css @@ -2080,147 +2080,6 @@ body { white-space: nowrap; } -/* ── Harness spread bars + paired ratios ── */ -.bw-bars { - display: flex; - flex-direction: column; - gap: 0.45rem; -} -.bw-bars__row { - display: grid; - grid-template-columns: 92px 1fr 64px; - align-items: center; - gap: 0.6rem; -} -.bw-bars__name { - font-family: var(--font-mono); - font-size: 11.5px; - color: var(--color-ink); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.bw-bars__track { - height: 13px; - background: var(--color-surface-2); - border-radius: 3px; - overflow: hidden; -} -.bw-bars__fill { - height: 100%; - background: rgba(13, 13, 16, 0.55); - border-radius: 3px; -} -.bw-bars__val { - font-family: var(--font-mono); - font-size: 11px; - font-variant-numeric: tabular-nums; - color: var(--color-ink-3); - text-align: right; - white-space: nowrap; -} -.bw-quiz { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.9rem; - flex-wrap: wrap; - margin-top: 1rem; - padding-top: 0.85rem; - border-top: 1px solid var(--color-rule); - font-size: 13.5px; - color: var(--color-ink-2); -} -.bw-quiz--answer { - color: var(--color-ink-2); -} -.bw-quiz strong { - color: var(--color-ink); - font-weight: 600; -} -.bw-quiz > span { - flex: 1; - min-width: 240px; -} -.bw-ratio { - display: flex; - flex-direction: column; - gap: 0.55rem; -} -.bw-ratio__row { - display: grid; - grid-template-columns: 150px 1fr; - align-items: center; - gap: 0.6rem; -} -.bw-ratio__row--axis { - margin-bottom: 0.1rem; -} -.bw-ratio__name { - font-family: var(--font-mono); - font-size: 11.5px; - color: var(--color-ink); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.bw-ratio__val { - color: var(--color-ink-3); -} -.bw-ratio__axis { - position: relative; - height: 14px; -} -.bw-ratio__axis span { - position: absolute; - transform: translateX(-50%); - font-family: var(--font-mono); - font-size: 10px; - color: var(--color-ink-3); - white-space: nowrap; -} -.bw-ratio__track { - position: relative; - height: 18px; - background: var(--color-surface-2); - border-radius: 3px; -} -/* The 1× line every track shares, positioned by --one-x on .bw-ratio. */ -.bw-ratio__track::after { - content: ""; - position: absolute; - top: -2px; - bottom: -2px; - left: var(--one-x); - width: 1px; - background: var(--color-rule-strong); -} -.bw-ratio__marker { - position: absolute; - top: 50%; - transform: translate(-50%, -50%); - width: 9px; - height: 9px; - border-radius: 999px; - background: white; - border: 2px solid var(--color-ink-3); - z-index: 1; -} -.bw-ratio__marker[data-cheaper="true"] { - background: var(--color-ink); - border-color: var(--color-ink); -} -.bw-ratio__legend { - display: flex; - justify-content: space-between; - font-family: var(--font-mono); - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--color-ink-3); - margin-top: 0.2rem; -} - /* ── Install race lanes ── */ .bw-lanes { display: grid; From 2760f4f5777ad7f8c7c2a616f3d7f919d75576bc Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:03:54 -0700 Subject: [PATCH 3/3] marketing: show the actual context contents in the loading-strategy widget --- .../components/blog/context-flood-demo.tsx | 255 ++++++++++++++---- apps/marketing/src/styles/global.css | 47 +--- 2 files changed, 209 insertions(+), 93 deletions(-) diff --git a/apps/marketing/src/components/blog/context-flood-demo.tsx b/apps/marketing/src/components/blog/context-flood-demo.tsx index ddef8551f1..884fd057bc 100644 --- a/apps/marketing/src/components/blog/context-flood-demo.tsx +++ b/apps/marketing/src/components/blog/context-flood-demo.tsx @@ -3,14 +3,16 @@ /* eslint-disable react/forbid-elements -- blog widgets use bespoke styled controls; the product design-system )), diff --git a/apps/marketing/src/styles/global.css b/apps/marketing/src/styles/global.css index 54b0cfd54b..0f85aeb494 100644 --- a/apps/marketing/src/styles/global.css +++ b/apps/marketing/src/styles/global.css @@ -1966,48 +1966,14 @@ body { grid-template-columns: 1fr 200px; } } -.bw-flood__rows { - display: flex; - flex-direction: column; - gap: 0.7rem; +.bw-flood__win { min-width: 0; } -.bw-flood__meta { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 0.6rem; - margin-bottom: 0.3rem; - font-size: 12.5px; -} -.bw-flood__name { - color: var(--color-ink-2); -} -.bw-flood__tok { - font-family: var(--font-mono); - font-size: 11px; - color: var(--color-ink-3); - white-space: nowrap; -} -.bw-flood__track { - height: 12px; - border-radius: 3px; - background: var(--color-surface-2); - border: 1px solid var(--color-rule); - overflow: hidden; -} -.bw-flood__fill { - height: 100%; - min-width: 3px; - background: rgba(13, 13, 16, 0.45); - transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1); -} -.bw-flood__fill[data-flood="true"] { - background: repeating-linear-gradient( - -45deg, - rgba(13, 13, 16, 0.55) 0 3px, - rgba(13, 13, 16, 0.18) 3px 7px - ); +.bw-flood__scroll { + height: 300px; + overflow-y: auto; + font-size: 12px; + line-height: 1.65; } .bw-flood__total { text-align: center; @@ -2187,7 +2153,6 @@ body { } @media (prefers-reduced-motion: reduce) { - .bw-flood__fill, .bw-pane, .bw-step, .bw-seg__btn,