diff --git a/blog/src/_data/media.json b/blog/src/_data/media.json index e1c7d2131b..232106d9ca 100644 --- a/blog/src/_data/media.json +++ b/blog/src/_data/media.json @@ -3332,6 +3332,50 @@ }, "inline": {} }, + "antigravity-alternatives": { + "title": "Antigravity Alternatives: 6 Picks by Why You Are Leaving (Nov 2026)", + "category": "comparisons", + "hero": { + "file": "assets/media/antigravity-alternatives/hero.png", + "alt": "Title card on white. A pill reading Alternatives, the title Antigravity alternatives, the line 6 picks by why you are leaving and a Checked 4 Nov 2026 line. Six small tiles named Claude Code, Munder Difflin, Codex, Cursor, Cline and Zed slide in under a grey tile labelled Antigravity.", + "prompt": "Title card: the hero scene in assets/media/antigravity-alternatives/motion.js, shot at 1600 by 900.", + "status": "ready" + }, + "inline": {} + }, + "agent-tools-today-2026-11-05": { + "title": "Agent Tools Today, 5 Nov 2026: Conductor Joins Meta, OpenAI to Buy Pierre, Gemini Training", + "category": "news", + "hero": { + "file": "assets/media/agent-tools-today/hero.png", + "alt": "Hand drawn sketch: a front page with five numbered lines, a clock, and a yellow character reading it out", + "prompt": "Series hero for the Agent Tools Today daily brief, drawn in code (media-src/topic-tiles.html?t=news). Reused by every brief.", + "status": "ready" + }, + "inline": {} + }, + "roo-code-alternatives": { + "title": "Roo Code Alternatives: 6 Picks Now That Roo Code Is Shut Down (Nov 2026)", + "category": "comparisons", + "hero": { + "file": "assets/media/roo-code-alternatives/hero.png", + "alt": "Title card on white. A pill reading Alternatives, the title Roo Code alternatives, the line 6 picks now that Roo Code is shut down and a Checked 4 Nov 2026 line. Six small tiles named ZooCode, Munder Difflin, Cline, Kilo Code, Roomote and Claude Code slide in under a grey tile labelled Roo Code.", + "prompt": "Title card: the hero scene in assets/media/roo-code-alternatives/motion.js, shot at 1600 by 900.", + "status": "ready" + }, + "inline": {} + }, + "gemini-cli-alternatives": { + "title": "Gemini CLI Alternatives: 6 Picks Now the Free Tier Has Moved (Nov 2026)", + "category": "comparisons", + "hero": { + "file": "assets/media/gemini-cli-alternatives/hero.png", + "alt": "Title card on white. A pill reading Alternatives, the title Gemini CLI alternatives, the line 6 picks now the free tier has moved and a Checked 4 Nov 2026 line. Six small tiles named Antigravity CLI, Munder Difflin, Claude Code, Codex CLI, OpenCode and Qwen Code slide in under a grey tile labelled Gemini CLI.", + "prompt": "Title card: the hero scene in assets/media/gemini-cli-alternatives/motion.js, shot at 1600 by 900.", + "status": "ready" + }, + "inline": {} + }, "the-hook-shim-pattern": { "title": "The Hook Shim Pattern: Wiring Agent Hooks to a Live Process", "category": "internals", diff --git a/blog/src/assets/media/agent-tools-today-2026-11-05/motion.css b/blog/src/assets/media/agent-tools-today-2026-11-05/motion.css new file mode 100644 index 0000000000..6c8c02a15c --- /dev/null +++ b/blog/src/assets/media/agent-tools-today-2026-11-05/motion.css @@ -0,0 +1,13 @@ +/* Bright coded scene for "Agent tools today, 5 Nov 2026". Pairs with motion.js. */ +.prose figure.mg { margin: 42px 0; } +.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; } +.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; } +.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; } +.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; } +.mg .mg-hit { cursor: pointer; outline: none; } +.mg [role="slider"] { cursor: grab; touch-action: none; } +.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; } +.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; } +.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; } +@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } } +@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } } diff --git a/blog/src/assets/media/agent-tools-today-2026-11-05/motion.js b/blog/src/assets/media/agent-tools-today-2026-11-05/motion.js new file mode 100644 index 0000000000..48c27571c8 --- /dev/null +++ b/blog/src/assets/media/agent-tools-today-2026-11-05/motion.js @@ -0,0 +1,104 @@ +/* Bright coded scene for "Agent tools today, 5 Nov 2026". Vanilla JS, no library. +
holds a still image; this script swaps in a live SVG. + Plays only in view, honours reduced motion, "?mgstill" freezes each scene on its still frame. */ +(() => { + const NS = "http://www.w3.org/2000/svg"; + const INK = "#1A1320", YEL = "#FFCA54", BLUE = "#6C8EF5", LIL = "#B69CFF", MINT = "#7FD8AE", SKY = "#9AD8FF", SOFT = "#F1ECF9", MUTE = "#7A6A88"; + const SANS = '"Space Grotesk", system-ui, sans-serif', MONO = '"JetBrains Mono", ui-monospace, monospace'; + const el = (t, a = {}, p) => { const n = document.createElementNS(NS, t); for (const k in a) n.setAttribute(k, a[k]); if (p) p.appendChild(n); return n; }; + const txt = (p, x, y, s, a = {}) => { const n = el("text", Object.assign({ x, y, "font-family": SANS, "font-size": 24, "font-weight": 600, fill: INK }, a), p); n.textContent = s; return n; }; + const cl = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v)); + const seg = (t, a, b) => cl((t - a) / (b - a)); + const oc = (x) => 1 - Math.pow(1 - x, 3); + const ob = (x) => { const c = 1.70158; return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); }; + const io = (x) => (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); + const tf = (n, x, y, s = 1, r = 0) => n.setAttribute("transform", `translate(${x} ${y}) rotate(${r}) scale(${s})`); + const button = (g, label, fn) => { + g.setAttribute("role", "button"); g.setAttribute("tabindex", "0"); g.setAttribute("aria-label", label); g.classList.add("mg-hit"); + g.addEventListener("click", fn); + g.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); fn(); } }); + }; + const strip = (svg, y, w = 680) => { + el("rect", { x: (800 - w) / 2, y, width: w, height: 48, rx: 24, fill: SOFT }, svg); + return txt(svg, 400, y + 31, "", { "text-anchor": "middle", "font-size": 20, "font-weight": 500 }); + }; + + const SC = {}; + + /* ---- a toy xylophone: three bars, a mallet taps each in turn ---- */ + SC.xylophone = { + dur: 7, still: 6.6, + build(svg, api) { + const BARS = [["Conductor joins Meta", BLUE, 680], ["OpenAI to buy Pierre", LIL, 630], ["Gemini training", MINT, 580]]; + const CY = [130, 270, 410], BH = 100, TX = 380, HX = 605, REST = [470, 548], T = [1.3, 2.5, 3.7]; + const railX = (side, y) => side < 0 ? 110 + 40 * (y - 60) / 420 : 690 - 40 * (y - 60) / 420; + [-1, 1].forEach((s) => { + const d = `M${railX(s, 60)} 60 L${railX(s, 480)} 480`; + el("path", { d, stroke: INK, "stroke-width": 32, "stroke-linecap": "round" }, svg); + el("path", { d, stroke: YEL, "stroke-width": 26, "stroke-linecap": "round" }, svg); + }); + const taps = [-99, -99, -99]; + const bars = BARS.map(([label, col, w], i) => { + const g = el("g", {}, svg), x = 400 - w / 2, y = CY[i] - BH / 2; + el("rect", { x: x - 8, y: y - 8, width: w + 16, height: BH + 16, rx: 22, fill: "none", stroke: INK, "stroke-width": 2.5, "stroke-dasharray": "4 6", class: "mg-ring" }, g); + el("rect", { x, y, width: w, height: BH, rx: 16, fill: col, stroke: INK, "stroke-width": 3 }, g); + [-1, 1].forEach((s) => el("circle", { cx: railX(s, CY[i]), cy: CY[i], r: 7, fill: "#fff", stroke: INK, "stroke-width": 2.5 }, g)); + txt(g, TX, CY[i] + 11, label, { "text-anchor": "middle", "font-family": MONO, "font-size": 30, "font-weight": 700 }); + const ring = el("g", {}, svg), cx = x - 8, arcs = [16, 28, 40].map((r) => { + const a = 50 * Math.PI / 180, px = cx - r * Math.cos(a), py = r * Math.sin(a); + return el("path", { d: `M${px} ${CY[i] - py} A${r} ${r} 0 0 0 ${px} ${CY[i] + py}`, stroke: INK, "stroke-width": 3, fill: "none", "stroke-linecap": "round", opacity: 0 }, ring); + }); + button(g, label, () => { taps[i] = api.now(); api.poke(); }); + return { g, arcs }; + }); + const mallet = el("g", {}, svg); + el("rect", { x: 20, y: -6, width: 158, height: 12, rx: 6, fill: SOFT, stroke: INK, "stroke-width": 3 }, mallet); + el("circle", { r: 24, fill: YEL, stroke: INK, "stroke-width": 3 }, mallet); + const bounce = (s) => (s < 0 || s > 0.9 ? 0 : 9 * Math.exp(-5 * s) * Math.sin(16 * s)); + const hitL = (s) => (s < -0.22 || s > 0.35 ? 1 : s < 0 ? 1 - Math.pow(seg(s, -0.22, 0), 2) : oc(seg(s, 0, 0.35))); + return (t, now) => { + let x, y, L; + if (t < 1) { const k = io(seg(t, 0.3, 1)); x = REST[0] + (HX - REST[0]) * k; y = REST[1] + (CY[0] - REST[1]) * k; L = k; } + else if (t < 4.1) { x = HX; y = CY[0] + (CY[1] - CY[0]) * io(seg(t, T[0] + 0.35, T[1] - 0.25)) + (CY[2] - CY[1]) * io(seg(t, T[1] + 0.35, T[2] - 0.25)); L = T.reduce((m, h) => m * hitL(t - h), 1); } + else { const k = io(seg(t, 4.1, 4.9)); x = HX + (REST[0] - HX) * k; y = CY[2] + (REST[1] - CY[2]) * k; L = 1 - k; } + mallet.setAttribute("transform", `translate(${x} ${y}) rotate(${28 * L} 175 0)`); + bars.forEach((b, i) => { + const s1 = t - T[i], s2 = now - taps[i], s = s2 >= 0 && s2 < 0.9 ? s2 : s1; + b.g.setAttribute("transform", `translate(0 ${bounce(s1) + bounce(s2)})`); + b.arcs.forEach((a, j) => a.setAttribute("opacity", s < 0 ? 0 : seg(s, j * 0.08, j * 0.08 + 0.1) * (1 - seg(s, 0.4, 0.9)))); + }); + }; + }, + }; + + /* ---- player ---- */ + const q = new URLSearchParams(location.search), frozen = q.has("mgstill"), fixed = parseFloat(q.get("mgstill")); + const calm = matchMedia("(prefers-reduced-motion: reduce)").matches; + const mount = (fig) => { + const sc = SC[fig.dataset.scene], img = fig.querySelector("img"); if (!sc || !img) return; + const W = sc.w || 800, H = sc.h || 600; + const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, class: "mg-svg", role: "group", "aria-label": img.alt || "" }); + svg.style.aspectRatio = `${W} / ${H}`; + el("rect", { width: W, height: H, fill: "#fff" }, svg); + img.replaceWith(svg); + let off = 0, raf = 0, seen = false, held = calm; + const base = performance.now(), clock = () => (performance.now() - base) / 1000; + let draw = () => {}; + const frame = () => { const now = clock(); draw(held ? sc.still : (((now + off) % sc.dur) + sc.dur) % sc.dur, now); }; + const tick = () => { cancelAnimationFrame(raf); frame(); if (seen && !held) raf = requestAnimationFrame(tick); }; + const api = { now: clock, seek: (s) => { off = s - clock(); held = false; tick(); }, poke: () => { held = false; tick(); } }; + draw = sc.build(svg, api); + if (frozen) { draw(isNaN(fixed) ? sc.still : fixed, 0); return; } + new IntersectionObserver(([e]) => { + seen = e.isIntersecting; + if (seen) { tick(); if (!fig.dataset.sent && window.posthog) { fig.dataset.sent = 1; window.posthog.capture("blog_scene_view", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } } + else cancelAnimationFrame(raf); + }, { threshold: 0.25 }).observe(fig); + svg.addEventListener("click", () => { if (!fig.dataset.hit && window.posthog) { fig.dataset.hit = 1; window.posthog.capture("blog_scene_interact", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } }); + frame(); + }; + const go = () => document.querySelectorAll("figure.mg[data-scene]").forEach(mount); + const fonts = document.fonts && document.fonts.load ? Promise.all([document.fonts.load('700 32px "Space Grotesk"'), document.fonts.load('500 20px "Space Grotesk"'), document.fonts.load('700 20px "JetBrains Mono"')]).catch(() => {}) : Promise.resolve(); + const start = () => fonts.then(go, go); + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start(); +})(); diff --git a/blog/src/assets/media/agent-tools-today-2026-11-05/xylophone.png b/blog/src/assets/media/agent-tools-today-2026-11-05/xylophone.png new file mode 100644 index 0000000000..3a3a292c76 Binary files /dev/null and b/blog/src/assets/media/agent-tools-today-2026-11-05/xylophone.png differ diff --git a/blog/src/assets/media/antigravity-alternatives/calendar.png b/blog/src/assets/media/antigravity-alternatives/calendar.png new file mode 100644 index 0000000000..0624d8c28e Binary files /dev/null and b/blog/src/assets/media/antigravity-alternatives/calendar.png differ diff --git a/blog/src/assets/media/antigravity-alternatives/hero.png b/blog/src/assets/media/antigravity-alternatives/hero.png new file mode 100644 index 0000000000..d6067a41e0 Binary files /dev/null and b/blog/src/assets/media/antigravity-alternatives/hero.png differ diff --git a/blog/src/assets/media/antigravity-alternatives/motion.css b/blog/src/assets/media/antigravity-alternatives/motion.css new file mode 100644 index 0000000000..e2d54f64b5 --- /dev/null +++ b/blog/src/assets/media/antigravity-alternatives/motion.css @@ -0,0 +1,13 @@ +/* Bright coded scenes for "Antigravity alternatives". Pairs with motion.js. */ +.prose figure.mg { margin: 42px 0; } +.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; } +.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; } +.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; } +.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; } +.mg .mg-hit { cursor: pointer; outline: none; } +.mg [role="slider"] { cursor: grab; touch-action: none; } +.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; } +.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; } +.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; } +@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } } +@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } } diff --git a/blog/src/assets/media/antigravity-alternatives/motion.js b/blog/src/assets/media/antigravity-alternatives/motion.js new file mode 100644 index 0000000000..0470dbaec3 --- /dev/null +++ b/blog/src/assets/media/antigravity-alternatives/motion.js @@ -0,0 +1,237 @@ +/* Bright coded scenes for "Antigravity alternatives". Vanilla JS, no library. +
holds a still image; this script swaps in a live SVG. + Plays only in view, honours reduced motion, "?mgstill" freezes each scene on its still frame. */ +(() => { + const NS = "http://www.w3.org/2000/svg"; + const INK = "#1A1320", YEL = "#FFCA54", BLUE = "#6C8EF5", LIL = "#B69CFF", MINT = "#7FD8AE", SKY = "#9AD8FF", SOFT = "#F1ECF9", MUTE = "#7A6A88", GREY = "#DCD6E2"; + const SANS = '"Space Grotesk", system-ui, sans-serif', MONO = '"JetBrains Mono", ui-monospace, monospace'; + const el = (t, a = {}, p) => { const n = document.createElementNS(NS, t); for (const k in a) n.setAttribute(k, a[k]); if (p) p.appendChild(n); return n; }; + const txt = (p, x, y, s, a = {}) => { const n = el("text", Object.assign({ x, y, "font-family": SANS, "font-size": 24, "font-weight": 600, fill: INK }, a), p); n.textContent = s; return n; }; + const cl = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v)); + const seg = (t, a, b) => cl((t - a) / (b - a)); + const oc = (x) => 1 - Math.pow(1 - x, 3); + const ob = (x) => { const c = 1.70158; return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); }; + const io = (x) => (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); + const tf = (n, x, y, s = 1, r = 0) => n.setAttribute("transform", `translate(${x} ${y}) rotate(${r}) scale(${s})`); + const button = (g, label, fn) => { + g.setAttribute("role", "button"); g.setAttribute("tabindex", "0"); g.setAttribute("aria-label", label); g.classList.add("mg-hit"); + g.addEventListener("click", fn); + g.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); fn(); } }); + }; + + const SC = {}; + + /* ---- 1. a desk calendar flips to November 18 and a note lands ---- */ + SC.calendar = { + dur: 4.2, + build(svg, api) { + const X = 200, Y = 110, W = 400, H = 420, FL = [0.4, 0.95, 1.5, 2.05], FD = 0.5; + const cal = el("g", {}, svg); + el("rect", { x: X - 28, y: Y - 34, width: W + 56, height: H + 62, rx: 24, fill: "#fff", "fill-opacity": 0, class: "mg-ring", stroke: INK, "stroke-width": 2.5, "stroke-dasharray": "4 8" }, cal); + el("rect", { x: X - 60, y: Y + H + 8, width: W + 120, height: 34, rx: 17, fill: BLUE, stroke: INK, "stroke-width": 3 }, cal); + el("rect", { x: X - 16, y: Y - 22, width: W + 32, height: H + 36, rx: 18, fill: LIL, stroke: INK, "stroke-width": 3 }, cal); + const HEAD = [SKY, MINT, LIL, BLUE]; + const face = (i) => { + const g = el("g", {}, cal); + el("rect", { x: X, y: Y, width: W, height: H, rx: 12, fill: "#fff", stroke: INK, "stroke-width": 3 }, g); + if (i < 4) { + el("rect", { x: X, y: Y, width: W, height: 74, rx: 12, fill: HEAD[i], stroke: INK, "stroke-width": 3 }, g); + for (let r = 0; r < 4; r++) for (let c = 0; c < 5; c++) el("rect", { x: X + 34 + c * 70, y: Y + 110 + r * 72, width: 52, height: 52, rx: 10, fill: SOFT }, g); + } else { + el("rect", { x: X, y: Y, width: W, height: 74, rx: 12, fill: YEL, stroke: INK, "stroke-width": 3 }, g); + txt(g, X + W / 2, Y + 50, "November", { "text-anchor": "middle", "font-size": 34, "font-weight": 700 }); + txt(g, X + W / 2, Y + 236, "18", { "text-anchor": "middle", "font-size": 150, "font-weight": 700 }); + } + return g; + }; + const under = [0, 1, 2, 3, 4].map(face); + const flip = el("g", {}, cal); + const flipFace = [0, 1, 2, 3].map((i) => { const f = face(i); flip.appendChild(f); return f; }); + const back = el("rect", { x: X, y: Y, width: W, height: H, rx: 12, fill: SOFT, stroke: INK, "stroke-width": 3 }, flip); + for (let i = 0; i < 8; i++) { + const x = X + 40 + i * 45; + el("rect", { x: x - 6, y: Y - 18, width: 12, height: 34, rx: 6, fill: "#fff", stroke: INK, "stroke-width": 3 }, cal); + } + const note = el("g", {}, cal); + el("rect", { x: -130, y: -64, width: 260, height: 128, rx: 8, fill: YEL, stroke: INK, "stroke-width": 3 }, note); + ["Claude 4.6 and", "GPT OSS 120b", "leave Antigravity"].forEach((s, i) => txt(note, 0, -22 + i * 32, s, { "text-anchor": "middle", "font-size": 23, "font-weight": 700 })); + button(cal, "Calendar: flip the pages again", () => api.seek(0)); + return (t) => { + const n = FL.filter((s) => t >= s).length, cur = n - 1, p = n ? seg(t, FL[cur], FL[cur] + FD) : 1; + const show = n === 0 ? 0 : p < 1 ? cur + 1 : n; + under.forEach((g, i) => g.setAttribute("display", i === show || (n === 0 && i === 0) ? "inline" : "none")); + if (n === 0 || p >= 1) flip.setAttribute("display", "none"); + else { + const s = 1 - 1.25 * io(p); + flip.setAttribute("display", "inline"); + flip.setAttribute("transform", `translate(0 ${Y}) scale(1 ${Math.abs(s) < 0.01 ? 0.01 : s}) translate(0 ${-Y})`); + flipFace.forEach((f, i) => f.setAttribute("display", i === cur && s > 0 ? "inline" : "none")); + back.setAttribute("display", s > 0 ? "none" : "inline"); + } + const k = seg(t, 2.8, 3.4), d = ob(k); + tf(note, 460, 440 - 260 * (1 - d), 1.25 - 0.25 * oc(k), -4 + 10 * (1 - oc(k))); + note.setAttribute("opacity", k <= 0 ? 0 : cl(k * 4)); + }; + }, + }; + + /* ---- 2. a vending machine with six snacks, each card says where it runs ---- */ + SC.vending = { + dur: 3, + build(svg, api) { + const NAMES = ["Claude Code", "Munder Difflin", "Codex", "Cursor", "Cline", "Zed"]; + const RUNS = ["Terminal, IDE, desktop, web", "Your computer", "CLI, IDE, desktop app, web", "Desktop, CLI, cloud agents", "VS Code, JetBrains, terminal, desktop", "Your computer"]; + const COL = [YEL, MINT, LIL, SKY, YEL, MINT]; + const WX = 116, WY = 50, WW = 568, WH = 240, slot = WW / 6, cx = (i) => WX + slot * (i + 0.5); + let pick = 1; + el("rect", { x: 90, y: 24, width: 620, height: 556, rx: 28, fill: BLUE, stroke: INK, "stroke-width": 4 }, svg); + el("rect", { x: WX, y: WY, width: WW, height: WH, rx: 16, fill: "#fff", stroke: INK, "stroke-width": 3 }, svg); + const clip = el("clipPath", { id: "mgv-win" }, el("defs", {}, svg)); + el("rect", { x: WX + 2, y: WY + 2, width: WW - 4, height: 206 - WY - 3, rx: 14 }, clip); + el("path", { d: `M${WX} 206 H${WX + WW}`, stroke: INK, "stroke-width": 3 }, svg); + NAMES.forEach((s, i) => { + const w = s.split(" "); + w.forEach((p, j) => txt(svg, cx(i), (w.length > 1 ? 238 : 250) + j * 24, p, { "text-anchor": "middle", "font-family": MONO, "font-size": 16, "font-weight": 700 })); + }); + const win = el("g", { "clip-path": "url(#mgv-win)" }, svg); + const items = NAMES.map((s, i) => { + const coil = el("circle", { cx: 0, cy: 0, r: 38, fill: "none", stroke: INK, "stroke-width": 3, "stroke-dasharray": "190 49", "stroke-linecap": "round" }, win); + tf(coil, cx(i), 140); + const snack = el("g", {}, win); + el("rect", { x: -30, y: -44, width: 60, height: 88, rx: 8, fill: COL[i], stroke: INK, "stroke-width": 3 }, snack); + el("rect", { x: -34, y: -54, width: 68, height: 14, rx: 4, fill: COL[i], stroke: INK, "stroke-width": 3 }, snack); + el("rect", { x: -34, y: 40, width: 68, height: 14, rx: 4, fill: COL[i], stroke: INK, "stroke-width": 3 }, snack); + el("circle", { cx: 0, cy: 0, r: 13, fill: "#fff", stroke: INK, "stroke-width": 2.5 }, snack); + return { coil, snack }; + }); + el("rect", { x: WX, y: 316, width: WW, height: 56, rx: 28, fill: SOFT, stroke: INK, "stroke-width": 3 }, svg); + const btns = NAMES.map((s, i) => { + const g = el("g", {}, svg); + el("circle", { r: 31, fill: "#fff", "fill-opacity": 0, class: "mg-ring", stroke: INK, "stroke-width": 2.5 }, g); + const face = el("circle", { r: 19, fill: YEL, stroke: INK, "stroke-width": 3 }, g); + el("path", { d: "M-7 -3 L0 5 L7 -3", stroke: INK, "stroke-width": 3, fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }, g); + tf(g, cx(i), 344); + button(g, `${s}: ${RUNS[i]}`, () => { pick = i; api.seek(0); }); + return face; + }); + el("rect", { x: 140, y: 400, width: 520, height: 152, rx: 20, fill: SOFT, stroke: INK, "stroke-width": 3 }, svg); + const tclip = el("clipPath", { id: "mgv-tray" }, el("defs", {}, svg)); + el("rect", { x: 142, y: 402, width: 516, height: 148, rx: 18 }, tclip); + const card = el("g", {}, el("g", { "clip-path": "url(#mgv-tray)" }, svg)); + el("rect", { x: 160, y: 424, width: 480, height: 108, rx: 14, fill: "#fff", stroke: INK, "stroke-width": 3 }, card); + const cName = txt(card, 400, 462, "", { "text-anchor": "middle", "font-family": MONO, "font-size": 17, "font-weight": 700, fill: MUTE }); + const cRun = txt(card, 400, 504, "", { "text-anchor": "middle", "font-size": 24, "font-weight": 700 }); + return (t) => { + items.forEach((it, i) => { + const on = i === pick, r = on ? 360 * io(seg(t, 0.2, 1.2)) : 0, f = on ? seg(t, 1.0, 1.6) : 0; + tf(it.coil, cx(i), 140, 1, r - 40); + tf(it.snack, cx(i), 140 + 230 * f * f); + it.snack.setAttribute("opacity", 1 - seg(f, 0.75, 1)); + btns[i].setAttribute("fill", on ? MINT : YEL); + }); + cName.textContent = NAMES[pick]; cRun.textContent = RUNS[pick]; + card.setAttribute("transform", `translate(0 ${140 * (1 - ob(seg(t, 1.7, 2.4)))})`); + }; + }, + }; + + /* ---- 3. a wallet opens and three keys slide up ---- */ + SC.wallet = { + dur: 4.4, start: 1.2, + build(svg, api) { + const HX = 400, T = 290, B = 540, PW = 270, LABEL = ["Anthropic key", "OpenAI key", "Google key"], COL = [YEL, LIL, MINT], TOP = [104, 160, 216]; + const wal = el("g", { transform: "translate(0 -36)" }, svg); + el("rect", { x: HX - PW - 24, y: 80, width: 2 * PW + 48, height: B - 56, rx: 26, fill: "#fff", "fill-opacity": 0, class: "mg-ring", stroke: INK, "stroke-width": 2.5, "stroke-dasharray": "4 8" }, wal); + el("rect", { x: HX, y: T, width: PW, height: B - T, rx: 20, fill: BLUE, stroke: INK, "stroke-width": 4 }, wal); + const cards = LABEL.map((s, i) => { + const g = el("g", {}, wal); + el("rect", { x: HX + 24, y: 0, width: PW - 48, height: 150, rx: 14, fill: COL[i], stroke: INK, "stroke-width": 3 }, g); + txt(g, HX + 44, 38, s, { "font-size": 23, "font-weight": 700 }); + return g; + }); + el("rect", { x: HX, y: 336, width: PW, height: B - 336, rx: 20, fill: SKY, stroke: INK, "stroke-width": 4 }, wal); + el("path", { d: `M${HX + 18} 356 H${HX + PW - 18}`, stroke: INK, "stroke-width": 2, "stroke-dasharray": "6 7", "stroke-linecap": "round" }, wal); + const flap = el("g", {}, wal); + const inside = el("g", {}, flap); + el("rect", { x: -PW, y: T, width: PW, height: B - T, rx: 20, fill: LIL, stroke: INK, "stroke-width": 4 }, inside); + el("rect", { x: -PW + 30, y: T + 34, width: PW - 60, height: 172, rx: 10, fill: "#fff", stroke: INK, "stroke-width": 3 }, inside); + ["Free for", "individual", "developers"].forEach((s, i) => txt(inside, -PW / 2, T + 92 + i * 42, s, { "text-anchor": "middle", "font-size": 29, "font-weight": 700 })); + const outside = el("g", {}, flap); + el("rect", { x: -PW, y: T, width: PW, height: B - T, rx: 20, fill: BLUE, stroke: INK, "stroke-width": 4 }, outside); + el("rect", { x: -PW + 16, y: T + 16, width: PW - 32, height: B - T - 32, rx: 12, fill: "none", stroke: INK, "stroke-width": 2, "stroke-dasharray": "6 7" }, outside); + button(wal, "Wallet: close it and open it again", () => api.seek(0)); + return (t) => { + const fk = t < 1.1 ? 1 - io(seg(t, 0.4, 1.0)) : io(seg(t, 1.4, 2.2)), sx = -1 + 2 * fk; + flap.setAttribute("transform", `translate(${HX} 0) scale(${Math.abs(sx) < 0.01 ? 0.01 : sx} 1)`); + inside.setAttribute("display", sx > 0 ? "inline" : "none"); outside.setAttribute("display", sx > 0 ? "none" : "inline"); + cards.forEach((g, i) => { + const k = t < 1.1 ? 1 - io(seg(t, 0, 0.5)) : ob(seg(t, 2.4 + 0.5 * i, 2.9 + 0.5 * i)); + g.setAttribute("transform", `translate(0 ${350 + (TOP[i] - 350) * k})`); + }); + }; + }, + }; + + /* ---- title card, 16 by 9 ---- */ + SC.hero = { + w: 960, h: 540, dur: 3.6, + build(svg) { + el("rect", { x: 64, y: 44, width: 150, height: 38, rx: 19, fill: LIL, stroke: INK, "stroke-width": 2.5 }, svg); + txt(svg, 139, 69, "Alternatives", { "text-anchor": "middle", "font-size": 18, "font-weight": 700 }); + txt(svg, 64, 158, "Antigravity alternatives", { "font-size": 62, "font-weight": 700 }); + el("path", { d: "M66 182 H420", stroke: YEL, "stroke-width": 12, "stroke-linecap": "round" }, svg); + txt(svg, 64, 230, "6 picks by why you are leaving", { "font-size": 27, "font-weight": 500, fill: MUTE }); + txt(svg, 64, 266, "Checked 4 Nov 2026", { "font-family": MONO, "font-size": 17, "font-weight": 700, fill: MUTE }); + const NAMES = ["Claude Code", "Munder Difflin", "Codex", "Cursor", "Cline", "Zed"], COL = [YEL, MINT, LIL, SKY, YEL, MINT]; + const TW = 132, GAP = (832 - 6 * TW) / 5, tx = (i) => 64 + i * (TW + GAP); + el("rect", { x: 380, y: 300, width: 200, height: 52, rx: 14, fill: GREY, stroke: INK, "stroke-width": 2.5 }, svg); + txt(svg, 480, 333, "Antigravity", { "text-anchor": "middle", "font-family": MONO, "font-size": 19, "font-weight": 700, fill: MUTE }); + const wire = el("path", { d: `M480 352 V386 M${tx(0) + TW / 2} 386 H${tx(5) + TW / 2}`, stroke: INK, "stroke-width": 2.5, fill: "none", "stroke-linecap": "round" }, svg); + const tiles = NAMES.map((s, i) => { + const g = el("g", {}, svg); + el("path", { d: `M${tx(i) + TW / 2} 386 V420`, stroke: INK, "stroke-width": 2.5, "stroke-linecap": "round" }, g); + el("rect", { x: tx(i), y: 420, width: TW, height: 56, rx: 14, fill: COL[i], stroke: INK, "stroke-width": 2.5 }, g); + txt(g, tx(i) + TW / 2, 454, s, { "text-anchor": "middle", "font-family": MONO, "font-size": 13.5, "font-weight": 700 }); + return g; + }); + return (t) => { + wire.setAttribute("opacity", oc(seg(t, 0.2, 0.6))); + tiles.forEach((g, i) => { const k = seg(t, 0.5 + i * 0.35, 1.1 + i * 0.35); g.setAttribute("transform", `translate(0 ${50 * (1 - ob(k))})`); g.setAttribute("opacity", oc(k)); }); + }; + }, + }; + + /* ---- player: each scene plays once when it comes into view, then rests on its final frame ---- */ + const q = new URLSearchParams(location.search), frozen = q.has("mgstill"), fixed = parseFloat(q.get("mgstill")); + const calm = matchMedia("(prefers-reduced-motion: reduce)").matches; + const mount = (fig) => { + const sc = SC[fig.dataset.scene], img = fig.querySelector("img"); if (!sc || !img) return; + const W = sc.w || 800, H = sc.h || 600, still = sc.still != null ? sc.still : sc.dur, t0 = sc.start || 0; + const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, class: "mg-svg", role: "group", "aria-label": img.alt || "" }); + svg.style.aspectRatio = `${W} / ${H}`; + el("rect", { width: W, height: H, fill: "#fff" }, svg); + img.replaceWith(svg); + let off = 0, raf = 0, seen = false, begun = false; + const base = performance.now(), clock = () => (performance.now() - base) / 1000; + let draw = () => {}; + const pos = () => Math.min(clock() + off, sc.dur); + const frame = () => draw(calm ? still : pos(), clock()); + const tick = () => { cancelAnimationFrame(raf); frame(); if (seen && !calm && clock() + off < sc.dur) raf = requestAnimationFrame(tick); }; + const api = { now: clock, seek: (s) => { off = s - clock(); tick(); }, poke: () => tick() }; + draw = sc.build(svg, api); + if (frozen) { draw(isNaN(fixed) ? still : fixed, 0); return; } + new IntersectionObserver(([e]) => { + seen = e.isIntersecting; + if (seen) { + if (!begun) { begun = true; api.seek(t0); } else tick(); + if (!fig.dataset.sent && window.posthog) { fig.dataset.sent = 1; window.posthog.capture("blog_scene_view", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } + } else cancelAnimationFrame(raf); + }, { threshold: 0.25 }).observe(fig); + svg.addEventListener("click", () => { if (!fig.dataset.hit && window.posthog) { fig.dataset.hit = 1; window.posthog.capture("blog_scene_interact", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } }); + draw(calm ? still : t0, 0); + }; + const go = () => document.querySelectorAll("figure.mg[data-scene]").forEach(mount); + const fonts = document.fonts && document.fonts.load ? Promise.all([document.fonts.load('700 32px "Space Grotesk"'), document.fonts.load('500 20px "Space Grotesk"'), document.fonts.load('700 20px "JetBrains Mono"')]).catch(() => {}) : Promise.resolve(); + const start = () => fonts.then(go, go); + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start(); +})(); diff --git a/blog/src/assets/media/antigravity-alternatives/vending.png b/blog/src/assets/media/antigravity-alternatives/vending.png new file mode 100644 index 0000000000..3550933172 Binary files /dev/null and b/blog/src/assets/media/antigravity-alternatives/vending.png differ diff --git a/blog/src/assets/media/antigravity-alternatives/wallet.png b/blog/src/assets/media/antigravity-alternatives/wallet.png new file mode 100644 index 0000000000..73095b631e Binary files /dev/null and b/blog/src/assets/media/antigravity-alternatives/wallet.png differ diff --git a/blog/src/assets/media/gemini-cli-alternatives/door.png b/blog/src/assets/media/gemini-cli-alternatives/door.png new file mode 100644 index 0000000000..9dc14ba2f0 Binary files /dev/null and b/blog/src/assets/media/gemini-cli-alternatives/door.png differ diff --git a/blog/src/assets/media/gemini-cli-alternatives/faucet.png b/blog/src/assets/media/gemini-cli-alternatives/faucet.png new file mode 100644 index 0000000000..6100a58b2b Binary files /dev/null and b/blog/src/assets/media/gemini-cli-alternatives/faucet.png differ diff --git a/blog/src/assets/media/gemini-cli-alternatives/hero.png b/blog/src/assets/media/gemini-cli-alternatives/hero.png new file mode 100644 index 0000000000..447f66a874 Binary files /dev/null and b/blog/src/assets/media/gemini-cli-alternatives/hero.png differ diff --git a/blog/src/assets/media/gemini-cli-alternatives/magnets.png b/blog/src/assets/media/gemini-cli-alternatives/magnets.png new file mode 100644 index 0000000000..593b382abb Binary files /dev/null and b/blog/src/assets/media/gemini-cli-alternatives/magnets.png differ diff --git a/blog/src/assets/media/gemini-cli-alternatives/motion.css b/blog/src/assets/media/gemini-cli-alternatives/motion.css new file mode 100644 index 0000000000..8baedb582c --- /dev/null +++ b/blog/src/assets/media/gemini-cli-alternatives/motion.css @@ -0,0 +1,13 @@ +/* Bright coded scenes for "Gemini CLI alternatives". Pairs with motion.js. */ +.prose figure.mg { margin: 42px 0; } +.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; } +.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; } +.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; } +.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; } +.mg .mg-hit { cursor: pointer; outline: none; } +.mg [role="slider"] { cursor: grab; touch-action: none; } +.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; } +.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; } +.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; } +@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } } +@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } } diff --git a/blog/src/assets/media/gemini-cli-alternatives/motion.js b/blog/src/assets/media/gemini-cli-alternatives/motion.js new file mode 100644 index 0000000000..1ab0f07291 --- /dev/null +++ b/blog/src/assets/media/gemini-cli-alternatives/motion.js @@ -0,0 +1,215 @@ +/* Bright coded scenes for "Gemini CLI alternatives". Vanilla JS, no library. +
holds a still image; this script swaps in a live SVG. + Plays only in view, honours reduced motion, "?mgstill" freezes each scene on its still frame. */ +(() => { + const NS = "http://www.w3.org/2000/svg"; + const INK = "#1A1320", YEL = "#FFCA54", BLUE = "#6C8EF5", LIL = "#B69CFF", MINT = "#7FD8AE", SKY = "#9AD8FF", SOFT = "#F1ECF9", MUTE = "#7A6A88", CAP = "#6B5878"; + const SANS = '"Space Grotesk", system-ui, sans-serif', MONO = '"JetBrains Mono", ui-monospace, monospace'; + const el = (t, a = {}, p) => { const n = document.createElementNS(NS, t); for (const k in a) n.setAttribute(k, a[k]); if (p) p.appendChild(n); return n; }; + const txt = (p, x, y, s, a = {}) => { const n = el("text", Object.assign({ x, y, "font-family": SANS, "font-size": 24, "font-weight": 600, fill: INK }, a), p); n.textContent = s; return n; }; + const cl = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v)); + const seg = (t, a, b) => cl((t - a) / (b - a)); + const oc = (x) => 1 - Math.pow(1 - x, 3); + const ob = (x) => { const c = 1.70158; return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); }; + const io = (x) => (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); + const tf = (n, x, y, s = 1, r = 0) => n.setAttribute("transform", `translate(${x} ${y}) rotate(${r}) scale(${s})`); + const button = (g, label, fn) => { + g.setAttribute("role", "button"); g.setAttribute("tabindex", "0"); g.setAttribute("aria-label", label); g.classList.add("mg-hit"); + g.addEventListener("click", fn); + g.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); fn(); } }); + }; + /* a two sided card: p runs 0 (front) to 1 (back); it squeezes on one axis and swaps faces half way */ + const flipTo = (g, front, back, p, cx, cy, axis) => { + const s = Math.max(0.03, Math.abs(Math.cos(Math.PI * p))); + g.setAttribute("transform", axis === "x" ? `translate(${cx} 0) scale(${s} 1) translate(${-cx} 0)` : `translate(0 ${cy}) scale(1 ${s}) translate(0 ${-cy})`); + front.setAttribute("display", p < 0.5 ? "inline" : "none"); back.setAttribute("display", p < 0.5 ? "none" : "inline"); + }; + /* a tap flips from whatever face shows now; the tap then owns that card */ + const tapFlip = () => { + const st = { o: null, last: 0 }; + st.p = (auto, now) => { st.last = st.o ? st.o.from + (st.o.to - st.o.from) * io(seg(now - st.o.at, 0, 0.6)) : auto; return st.last; }; + st.tap = (now) => { const cur = Math.round(st.last); st.o = { from: cur, to: 1 - cur, at: now }; }; + return st; + }; + const PICKS = [["Antigravity CLI", "Plan $0", YEL], ["Munder Difflin", "Free and open source", BLUE], ["Claude Code", "Pro from $17", LIL], ["Codex CLI", "Plus $20", SKY], ["OpenCode", "MIT", MINT], ["Qwen Code", "Apache 2.0", YEL]]; + + const SC = {}; + + /* ---- 1. a shop door sign swings, then flips to its other side ---- */ + SC.door = { + dur: 10, still: 8.5, + build(svg, api) { + el("path", { d: "M70 552 H730", stroke: INK, "stroke-width": 4, "stroke-linecap": "round" }, svg); + el("rect", { x: 196, y: 30, width: 408, height: 522, rx: 10, fill: SKY, stroke: INK, "stroke-width": 4 }, svg); + el("rect", { x: 236, y: 438, width: 150, height: 90, rx: 8, fill: "none", stroke: INK, "stroke-width": 3 }, svg); + el("rect", { x: 414, y: 438, width: 150, height: 90, rx: 8, fill: "none", stroke: INK, "stroke-width": 3 }, svg); + el("circle", { cx: 572, cy: 412, r: 12, fill: YEL, stroke: INK, "stroke-width": 3 }, svg); + const swing = el("g", {}, svg), card = el("g", {}, swing); + el("path", { d: "M400 96 L292 172 M400 96 L508 172", stroke: INK, "stroke-width": 3, fill: "none", "stroke-linecap": "round" }, card); + el("rect", { x: 210, y: 160, width: 380, height: 232, rx: 26, fill: "none", stroke: INK, "stroke-width": 2, "stroke-dasharray": "3 7", class: "mg-ring" }, card); + const front = el("g", {}, card), back = el("g", {}, card); + el("rect", { x: 220, y: 170, width: 360, height: 212, rx: 18, fill: YEL, stroke: INK, "stroke-width": 4 }, front); + txt(front, 400, 230, "Gemini CLI", { "text-anchor": "middle", "font-family": MONO, "font-size": 36, "font-weight": 700 }); + el("path", { d: "M276 258 H524", stroke: INK, "stroke-width": 2, "stroke-linecap": "round" }, front); + txt(front, 400, 304, "Free and Google AI Pro:", { "text-anchor": "middle", "font-size": 24 }); + txt(front, 400, 340, "moved to Antigravity CLI", { "text-anchor": "middle", "font-size": 24 }); + el("rect", { x: 220, y: 170, width: 360, height: 212, rx: 18, fill: MINT, stroke: INK, "stroke-width": 4 }, back); + txt(back, 400, 226, "Code Assist Standard", { "text-anchor": "middle", "font-size": 26 }); + txt(back, 400, 262, "and Enterprise:", { "text-anchor": "middle", "font-size": 26 }); + txt(back, 400, 342, "open", { "text-anchor": "middle", "font-size": 62, "font-weight": 700 }); + el("circle", { cx: 400, cy: 96, r: 7, fill: INK }, svg); + const flip = tapFlip(); + button(card, "Flip the sign", () => { flip.tap(api.now()); api.poke(); }); + return (t, now) => { + const ring = (s, a) => (s > 0 ? a * Math.exp(-1.1 * s) * Math.sin(s * 4.4) : 0); + const tapAt = flip.o ? now - flip.o.at : -1; + const a = ring(t, 9) + (flip.o ? ring(tapAt - 0.5, 5) : ring(t - 4.2, 5)); + swing.setAttribute("transform", `rotate(${a} 400 96)`); + flipTo(card, front, back, flip.p(io(seg(t, 3.4, 4.2)) - io(seg(t, 9.2, 9.9)), now), 400, 0, "x"); + }; + }, + }; + + /* ---- 2. a faucet fills two glasses and passes over the third ---- */ + SC.faucet = { + dur: 10, still: 8.6, + build(svg, api) { + const X = [150, 400, 650], TOP = 300, BOT = 470, FULL = 140, uid = "mgf" + (SC._n = (SC._n || 0) + 1); + el("rect", { x: 40, y: 140, width: 720, height: 12, rx: 6, fill: SOFT }, svg); + el("rect", { x: 30, y: BOT, width: 740, height: 18, rx: 9, fill: SOFT, stroke: INK, "stroke-width": 3 }, svg); + const stream = el("rect", { x: -7, width: 14, rx: 7, fill: SKY }, svg); + const drops = [0, 1, 2].map(() => el("circle", { r: 5, fill: SKY, stroke: INK, "stroke-width": 1.5 }, svg)); + const LAB = [["Standard,", "1,500 requests a day"], ["Enterprise,", "2,000 requests a day"], ["Free"]]; + const water = X.map((cx, i) => { + const shape = `M${cx - 60} ${TOP} L${cx - 48} ${BOT} H${cx + 48} L${cx + 60} ${TOP}`; + const cp = el("clipPath", { id: `${uid}${i}` }, svg); el("path", { d: shape + " Z" }, cp); + const w = el("g", { "clip-path": `url(#${uid}${i})` }, svg); + const body = el("rect", { x: cx - 62, width: 124, height: 180, fill: SKY }, w); + const top = el("rect", { x: cx - 62, width: 124, height: 6, fill: BLUE }, w); + el("path", { d: shape, fill: "none", stroke: INK, "stroke-width": 4, "stroke-linejoin": "round", "stroke-linecap": "round" }, svg); + el("path", { d: `M${cx - 40} ${TOP + 22} L${cx - 34} ${BOT - 26}`, stroke: SOFT, "stroke-width": 6, "stroke-linecap": "round" }, svg); + txt(svg, cx, 530, LAB[i][0], { "text-anchor": "middle", "font-size": 25, "font-weight": 700 }); + if (LAB[i][1]) txt(svg, cx, 560, LAB[i][1], { "text-anchor": "middle", "font-size": 19, "font-weight": 500, fill: MUTE }); + return (lv) => { const y = BOT - FULL * lv; body.setAttribute("y", y); top.setAttribute("y", y); body.setAttribute("opacity", lv > 0.01 ? 1 : 0); top.setAttribute("opacity", lv > 0.01 ? 1 : 0); return y; }; + }); + const tap = el("g", {}, svg); + el("rect", { x: -34, y: 22, width: 136, height: 132, rx: 18, fill: "#fff", "fill-opacity": 0, stroke: INK, "stroke-width": 2, "stroke-dasharray": "3 7", class: "mg-ring" }, tap); + el("path", { d: "M58 128 V72 Q58 40 28 40 Q0 40 0 70 V110", stroke: INK, "stroke-width": 21, fill: "none", "stroke-linecap": "round" }, tap); + el("path", { d: "M58 128 V72 Q58 40 28 40 Q0 40 0 70 V110", stroke: BLUE, "stroke-width": 13, fill: "none", "stroke-linecap": "round" }, tap); + el("rect", { x: -12, y: 106, width: 24, height: 16, rx: 4, fill: BLUE, stroke: INK, "stroke-width": 3 }, tap); + el("rect", { x: 36, y: 126, width: 44, height: 14, rx: 4, fill: BLUE, stroke: INK, "stroke-width": 3 }, tap); + const lever = el("g", {}, tap); + el("path", { d: "M58 90 L86 76", stroke: INK, "stroke-width": 7, "stroke-linecap": "round" }, lever); + el("circle", { cx: 88, cy: 75, r: 8, fill: YEL, stroke: INK, "stroke-width": 3 }, lever); + button(tap, "Pour again", () => api.seek(0.6)); + const POUR = [[1.45, 3.3], [4.25, 6.1]]; + return (t) => { + let hx = 400 - 250 * io(seg(t, 0.6, 1.4)) + 250 * io(seg(t, 3.4, 4.2)) + 250 * io(seg(t, 6.2, 7)) - 250 * io(seg(t, 9.2, 9.9)); + tap.setAttribute("transform", `translate(${hx} 0)`); + const drain = 1 - seg(t, 9.2, 9.8); + const wl = POUR.map(([a, b], i) => water[i](oc(seg(t, a + 0.15, b)) * drain)); + water[2](0); + let on = -1; POUR.forEach(([a, b], i) => { if (t >= a && t <= b + 0.2) on = i; }); + const open = on < 0 ? 0 : seg(t, POUR[on][0] - 0.2, POUR[on][0]) * (1 - seg(t, POUR[on][1], POUR[on][1] + 0.2)); + lever.setAttribute("transform", `rotate(${-24 * open} 58 90)`); + if (on < 0) { stream.setAttribute("opacity", 0); drops.forEach((d) => d.setAttribute("opacity", 0)); return; } + const [a, b] = POUR[on], y0 = 122, y1 = wl[on], top = y0 + (y1 - y0) * seg(t, b, b + 0.2), bot = y0 + (y1 - y0) * seg(t, a, a + 0.2); + stream.setAttribute("opacity", 1); stream.setAttribute("x", X[on] - 7); stream.setAttribute("y", top); stream.setAttribute("height", Math.max(0, bot - top)); + drops.forEach((d, i) => { const ph = (t * 3 + i / 3) % 1, live = t > a + 0.2 && t < b; d.setAttribute("cx", X[on] + (i - 1) * 22 * ph); d.setAttribute("cy", y1 - 26 * Math.sin(ph * Math.PI)); d.setAttribute("opacity", live ? 1 - ph : 0); }); + }; + }, + }; + + /* ---- 3. six fridge magnets slide on, then flip to show a second line ---- */ + SC.magnets = { + dur: 10, still: 8.8, + build(svg, api) { + el("rect", { x: 110, y: 12, width: 580, height: 576, rx: 30, fill: SOFT, stroke: INK, "stroke-width": 4 }, svg); + el("rect", { x: 636, y: 190, width: 18, height: 220, rx: 9, fill: "#fff", stroke: INK, "stroke-width": 3 }, svg); + const X = 170, W = 400, H = 72, cx = X + W / 2; + const mags = PICKS.map(([name, line, col], i) => { + const y = 36 + i * 90, cy = y + H / 2, g = el("g", {}, svg), f = el("g", {}, g); + el("rect", { x: X - 7, y: y - 7, width: W + 14, height: H + 14, rx: 24, fill: "none", stroke: INK, "stroke-width": 2, "stroke-dasharray": "3 7", class: "mg-ring" }, g); + const front = el("g", {}, f), back = el("g", {}, f); + el("rect", { x: X, y, width: W, height: H, rx: 18, fill: col, stroke: INK, "stroke-width": 3 }, front); + txt(front, cx, cy + 9, name, { "text-anchor": "middle", "font-family": MONO, "font-size": 26, "font-weight": 700 }); + el("rect", { x: X, y, width: W, height: H, rx: 18, fill: "#fff", stroke: INK, "stroke-width": 3 }, back); + el("circle", { cx: X + 30, cy, r: 10, fill: col, stroke: INK, "stroke-width": 2.5 }, back); + txt(back, cx, cy - 7, name, { "text-anchor": "middle", "font-family": MONO, "font-size": 19, "font-weight": 700 }); + txt(back, cx, cy + 22, line, { "text-anchor": "middle", "font-size": 21, "font-weight": 600 }); + const st = tapFlip(); + button(g, `${name}: ${line}`, () => { st.tap(api.now()); api.poke(); }); + return { g, f, front, back, cy, st, i }; + }); + return (t, now) => { + const fade = 1 - seg(t, 9.4, 9.9); + mags.forEach((m) => { + const s = 0.2 + m.i * 0.28, k = ob(seg(t, s, s + 0.6)); + m.g.setAttribute("transform", `translate(${-640 * (1 - k)} 0) rotate(${-6 * (1 - k)} ${cx} ${m.cy})`); + m.g.setAttribute("opacity", fade); + const a = 2.6 + m.i * 0.55; + flipTo(m.f, m.front, m.back, m.st.p(io(seg(t, a, a + 0.45)), now), cx, m.cy, "y"); + }); + }; + }, + }; + + /* ---- title card, 16 by 9 ---- */ + SC.hero = { + w: 960, h: 540, dur: 24, still: 3.6, + build(svg) { + el("rect", { x: 64, y: 50, width: 164, height: 40, rx: 20, fill: YEL, stroke: INK, "stroke-width": 2.5 }, svg); + txt(svg, 146, 77, "Alternatives", { "text-anchor": "middle", "font-size": 19, "font-weight": 700 }); + txt(svg, 62, 170, "Gemini CLI alternatives", { "font-size": 62, "font-weight": 700 }); + txt(svg, 64, 228, "6 picks now the free tier has moved", { "font-size": 26, "font-weight": 500, fill: MUTE }); + txt(svg, 64, 264, "Checked 4 Nov 2026", { "font-size": 18, "font-weight": 500, fill: CAP }); + const gem = el("g", {}, svg); + el("rect", { x: 64, y: 304, width: 170, height: 48, rx: 12, fill: SOFT, stroke: MUTE, "stroke-width": 2.5 }, gem); + txt(gem, 149, 335, "Gemini CLI", { "text-anchor": "middle", "font-family": MONO, "font-size": 19, "font-weight": 700, fill: MUTE }); + let x = 64; const Y = 424, tiles = PICKS.map(([name, , col]) => { const w = name.length * 9 + 28, c = x + w / 2; x += w + 12; return { name, col, w, c }; }); + const wire = el("path", { d: `M149 352 V392 M${tiles[0].c} 392 H${tiles[5].c} ${tiles.map((tl) => `M${tl.c} 392 V${Y}`).join(" ")}`, stroke: INK, "stroke-width": 2.5, fill: "none", "stroke-linecap": "round" }, svg); + const gs = tiles.map((tl) => { + const g = el("g", {}, svg); + el("rect", { x: tl.c - tl.w / 2, y: Y, width: tl.w, height: 50, rx: 12, fill: tl.col, stroke: INK, "stroke-width": 2.5 }, g); + txt(g, tl.c, Y + 31, tl.name, { "text-anchor": "middle", "font-family": MONO, "font-size": 15, "font-weight": 700 }); + return g; + }); + return (t) => { + gem.setAttribute("opacity", oc(seg(t, 0, 0.3))); + wire.setAttribute("opacity", oc(seg(t, 0.25, 0.55))); + gs.forEach((g, i) => { const s = 0.5 + i * 0.3, k = ob(seg(t, s, s + 0.55)); g.setAttribute("transform", `translate(0 ${70 * (1 - k)})`); g.setAttribute("opacity", seg(t, s, s + 0.2)); }); + }; + }, + }; + + /* ---- player ---- */ + const q = new URLSearchParams(location.search), frozen = q.has("mgstill"), fixed = parseFloat(q.get("mgstill")); + const calm = matchMedia("(prefers-reduced-motion: reduce)").matches; + const mount = (fig) => { + const sc = SC[fig.dataset.scene], img = fig.querySelector("img"); if (!sc || !img) return; + const W = sc.w || 800, H = sc.h || 600; + const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, class: "mg-svg", role: "group", "aria-label": img.alt || "" }); + svg.style.aspectRatio = `${W} / ${H}`; + el("rect", { width: W, height: H, fill: "#fff" }, svg); + img.replaceWith(svg); + let off = 0, raf = 0, seen = false, held = calm; + const base = performance.now(), clock = () => (performance.now() - base) / 1000; + let draw = () => {}; + const frame = () => { const now = clock(); draw(held ? sc.still : (((now + off) % sc.dur) + sc.dur) % sc.dur, now); }; + const tick = () => { cancelAnimationFrame(raf); frame(); if (seen && !held) raf = requestAnimationFrame(tick); }; + const api = { now: clock, seek: (s) => { off = s - clock(); held = false; tick(); }, poke: () => { held = false; tick(); } }; + draw = sc.build(svg, api); + if (frozen) { draw(isNaN(fixed) ? sc.still : fixed, 0); return; } + new IntersectionObserver(([e]) => { + seen = e.isIntersecting; + if (seen) { tick(); if (!fig.dataset.sent && window.posthog) { fig.dataset.sent = 1; window.posthog.capture("blog_scene_view", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } } + else cancelAnimationFrame(raf); + }, { threshold: 0.25 }).observe(fig); + svg.addEventListener("click", () => { if (!fig.dataset.hit && window.posthog) { fig.dataset.hit = 1; window.posthog.capture("blog_scene_interact", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } }); + frame(); + }; + const go = () => document.querySelectorAll("figure.mg[data-scene]").forEach(mount); + const fonts = document.fonts && document.fonts.load ? Promise.all([document.fonts.load('700 32px "Space Grotesk"'), document.fonts.load('500 20px "Space Grotesk"'), document.fonts.load('700 20px "JetBrains Mono"')]).catch(() => {}) : Promise.resolve(); + const start = () => fonts.then(go, go); + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start(); +})(); diff --git a/blog/src/assets/media/roo-code-alternatives/candle.png b/blog/src/assets/media/roo-code-alternatives/candle.png new file mode 100644 index 0000000000..9510caa0c8 Binary files /dev/null and b/blog/src/assets/media/roo-code-alternatives/candle.png differ diff --git a/blog/src/assets/media/roo-code-alternatives/chairs.png b/blog/src/assets/media/roo-code-alternatives/chairs.png new file mode 100644 index 0000000000..4182e98eb2 Binary files /dev/null and b/blog/src/assets/media/roo-code-alternatives/chairs.png differ diff --git a/blog/src/assets/media/roo-code-alternatives/hero.png b/blog/src/assets/media/roo-code-alternatives/hero.png new file mode 100644 index 0000000000..ba2560834b Binary files /dev/null and b/blog/src/assets/media/roo-code-alternatives/hero.png differ diff --git a/blog/src/assets/media/roo-code-alternatives/hooks.png b/blog/src/assets/media/roo-code-alternatives/hooks.png new file mode 100644 index 0000000000..df699f26bc Binary files /dev/null and b/blog/src/assets/media/roo-code-alternatives/hooks.png differ diff --git a/blog/src/assets/media/roo-code-alternatives/motion.css b/blog/src/assets/media/roo-code-alternatives/motion.css new file mode 100644 index 0000000000..926a359ecc --- /dev/null +++ b/blog/src/assets/media/roo-code-alternatives/motion.css @@ -0,0 +1,13 @@ +/* Bright coded scenes for "Roo Code alternatives". Pairs with motion.js. */ +.prose figure.mg { margin: 42px 0; } +.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; } +.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; } +.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; } +.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; } +.mg .mg-hit { cursor: pointer; outline: none; } +.mg [role="slider"] { cursor: grab; touch-action: none; } +.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; } +.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; } +.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; } +@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } } +@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } } diff --git a/blog/src/assets/media/roo-code-alternatives/motion.js b/blog/src/assets/media/roo-code-alternatives/motion.js new file mode 100644 index 0000000000..54b8bdf64e --- /dev/null +++ b/blog/src/assets/media/roo-code-alternatives/motion.js @@ -0,0 +1,246 @@ +/* Bright coded scenes for "Roo Code alternatives". Vanilla JS, no library. +
holds a still image; this script swaps in a live SVG. + Plays only in view, honours reduced motion, "?mgstill" freezes each scene on its still frame. */ +(() => { + const NS = "http://www.w3.org/2000/svg"; + const INK = "#1A1320", YEL = "#FFCA54", BLUE = "#6C8EF5", LIL = "#B69CFF", MINT = "#7FD8AE", SKY = "#9AD8FF", SOFT = "#F1ECF9", MUTE = "#7A6A88"; + const SANS = '"Space Grotesk", system-ui, sans-serif', MONO = '"JetBrains Mono", ui-monospace, monospace'; + const el = (t, a = {}, p) => { const n = document.createElementNS(NS, t); for (const k in a) n.setAttribute(k, a[k]); if (p) p.appendChild(n); return n; }; + const txt = (p, x, y, s, a = {}) => { const n = el("text", Object.assign({ x, y, "font-family": SANS, "font-size": 24, "font-weight": 600, fill: INK }, a), p); n.textContent = s; return n; }; + const cl = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v)); + const seg = (t, a, b) => cl((t - a) / (b - a)); + const oc = (x) => 1 - Math.pow(1 - x, 3); + const ob = (x) => { const c = 1.70158; return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); }; + const io = (x) => (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); + const tf = (n, x, y, s = 1, r = 0) => n.setAttribute("transform", `translate(${x} ${y}) rotate(${r}) scale(${s})`); + const button = (g, label, fn) => { + g.setAttribute("role", "button"); g.setAttribute("tabindex", "0"); g.setAttribute("aria-label", label); g.classList.add("mg-hit"); + g.addEventListener("click", fn); + g.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); fn(); } }); + }; + const strip = (svg, y, w = 680) => { + el("rect", { x: (800 - w) / 2, y, width: w, height: 48, rx: 24, fill: SOFT }, svg); + return txt(svg, 400, y + 31, "", { "text-anchor": "middle", "font-size": 20, "font-weight": 500 }); + }; + + const flipper = () => { let user = null, cur = 0; return { get: (t0, now) => { cur = user ? user.from + (user.to - user.from) * io(seg(now - user.at, 0, 0.6)) : t0; return cur; }, tap: (now) => { user = { from: cur, to: cur > 0.5 ? 0 : 1, at: now }; } }; }; + const fit = (n, w) => { try { if (n.getComputedTextLength() > w) { n.setAttribute("textLength", w); n.setAttribute("lengthAdjust", "spacingAndGlyphs"); } } catch (e) {} return n; }; + + const SC = {}; + + /* ---- 1. the candle goes out and the card turns over ---- */ + SC.candle = { + dur: 10, still: 9.8, + build(svg, api) { + const glow = el("circle", { cx: 240, cy: 214, r: 70, fill: YEL, opacity: 0.25 }, svg); + el("ellipse", { cx: 240, cy: 482, rx: 150, ry: 30, fill: SOFT, stroke: INK, "stroke-width": 3 }, svg); + el("ellipse", { cx: 240, cy: 474, rx: 96, ry: 16, fill: "#fff", stroke: INK, "stroke-width": 2.5 }, svg); + el("path", { d: "M195 268 V466 Q240 482 285 466 V268", fill: SKY, stroke: INK, "stroke-width": 3, "stroke-linejoin": "round" }, svg); + el("ellipse", { cx: 240, cy: 268, rx: 45, ry: 12, fill: "#fff", stroke: INK, "stroke-width": 3 }, svg); + el("path", { d: "M212 278 V318 Q218 328 224 318 V280", fill: "#fff", stroke: INK, "stroke-width": 2.5, "stroke-linejoin": "round" }, svg); + el("path", { d: "M240 266 V246", stroke: INK, "stroke-width": 4, "stroke-linecap": "round" }, svg); + const smoke = el("path", { d: "M240 244 C222 214 258 190 240 160 S222 104 244 70", fill: "none", stroke: MUTE, "stroke-width": 3.5, "stroke-linecap": "round", pathLength: 100, "stroke-dasharray": "100 100" }, svg); + const flame = el("g", {}, svg); + el("path", { d: "M0 -58 Q24 -20 20 2 Q16 22 0 22 Q-16 22 -20 2 Q-24 -20 0 -58 Z", fill: YEL, stroke: INK, "stroke-width": 3, "stroke-linejoin": "round" }, flame); + el("path", { d: "M0 -22 Q9 -6 8 4 Q6 12 0 12 Q-6 12 -8 4 Q-9 -6 0 -22 Z", fill: "#fff" }, flame); + const card = el("g", {}, svg); + el("rect", { x: -164, y: -124, width: 338, height: 258, rx: 30, fill: "#fff", "fill-opacity": 0, stroke: INK, "stroke-width": 2, "stroke-dasharray": "4 7", class: "mg-ring" }, card); + const inner = el("g", {}, card); + el("rect", { x: -140, y: -100, width: 300, height: 220, rx: 22, fill: LIL, stroke: INK, "stroke-width": 3 }, inner); + el("rect", { x: -150, y: -110, width: 300, height: 220, rx: 22, fill: "#fff", stroke: INK, "stroke-width": 3 }, inner); + const front = el("g", {}, inner); + txt(front, 0, 14, "Roo Code", { "text-anchor": "middle", "font-family": MONO, "font-size": 40, "font-weight": 700 }); + const back = el("g", {}, inner); + txt(back, 0, -50, "Shut down,", { "text-anchor": "middle", "font-size": 30, "font-weight": 700 }); + txt(back, 0, -12, "15 May 2026", { "text-anchor": "middle", "font-size": 30, "font-weight": 700 }); + el("path", { d: "M-100 14 H100", stroke: SOFT, "stroke-width": 4, "stroke-linecap": "round" }, back); + txt(back, 0, 50, "Final release", { "text-anchor": "middle", "font-size": 22, "font-weight": 500, fill: MUTE }); + txt(back, 0, 86, "v3.54.0", { "text-anchor": "middle", "font-family": MONO, "font-size": 28, "font-weight": 700 }); + tf(card, 570, 290); + txt(svg, 570, 470, "tap the card", { "text-anchor": "middle", "font-size": 16, "font-weight": 500, fill: MUTE }); + const fl = flipper(); + button(card, "Roo Code card. Back: Shut down, 15 May 2026. Final release v3.54.0", () => { fl.tap(api.now()); api.poke(); }); + return (t, now) => { + const s = 1 - io(seg(t, 1.6, 4.2)); + flame.setAttribute("transform", `translate(240 246) scale(${s * (1 + 0.05 * Math.sin(t * 13))} ${s * (1 + 0.07 * Math.sin(t * 9))}) translate(0 -22)`); + flame.setAttribute("opacity", s > 0.03 ? 1 : 0); + glow.setAttribute("r", 70 * s); glow.setAttribute("opacity", 0.25 * s); + const sm = seg(t, 4.0, 5.8); + smoke.setAttribute("stroke-dashoffset", 100 * (1 - oc(sm))); + smoke.setAttribute("opacity", sm <= 0 ? 0 : 0.9 - 0.6 * seg(t, 5.8, 8.5)); + smoke.setAttribute("transform", `translate(${4 * Math.sin(t * 1.5) * sm} ${-5 * seg(t, 4, 10)})`); + const f = fl.get(io(seg(t, 6, 7.1)), now), c = Math.cos(Math.PI * f); + inner.setAttribute("transform", `scale(${Math.max(Math.abs(c), 0.01)} 1)`); + front.setAttribute("opacity", c >= 0 ? 1 : 0); back.setAttribute("opacity", c < 0 ? 1 : 0); + }; + }, + }; + + /* ---- 2. coats drop onto a rail of hooks, one hook stays empty ---- */ + SC.hooks = { + dur: 9.5, still: 9.3, + build(svg, api) { + const NAMES = [["ZooCode"], ["Munder", "Difflin"], ["Cline"], ["Kilo Code"], ["Roomote"], ["Claude Code"]]; + const RUNS = [["VS Code,", "JetBrains", "beta"], ["Your", "computer"], ["VS Code,", "JetBrains,", "CLI,", "desktop"], ["VS Code,", "JetBrains,", "CLI"], ["Your own", "infrastructure,", "or hosted"], ["Terminal,", "IDE,", "desktop,", "browser"]]; + const COL = [YEL, BLUE, LIL, SKY, MINT, YEL]; + const X = (i) => 70 + i * 110, HY = 166; + el("rect", { x: 22, y: 100, width: 756, height: 36, rx: 10, fill: YEL, stroke: INK, "stroke-width": 3 }, svg); + el("path", { d: "M50 112 H190 M250 124 H410 M470 112 H630 M680 124 H756", stroke: INK, "stroke-width": 2, "stroke-opacity": 0.3, "stroke-linecap": "round" }, svg); + for (let i = 0; i < 7; i++) { + const x = X(i); + el("path", { d: `M${x} 134 V160 Q${x} 176 ${x + 13} 171`, stroke: INK, "stroke-width": 5, fill: "none", "stroke-linecap": "round" }, svg); + el("circle", { cx: x, cy: 124, r: 6, fill: "#fff", stroke: INK, "stroke-width": 2.5 }, svg); + } + const tag = (g, faded) => { + el("rect", { x: -52, y: 0, width: 104, height: 114, rx: 9, fill: "#fff", stroke: faded ? MUTE : INK, "stroke-width": 2.5, "stroke-dasharray": faded ? "5 5" : "none" }, g); + el("circle", { cx: 0, cy: 12, r: 4, fill: "#fff", stroke: faded ? MUTE : INK, "stroke-width": 2 }, g); + }; + const lines = (g, arr, a) => arr.map((s, k) => fit(txt(g, 0, 67 - (arr.length - 1) * (a.lh / 2) + k * a.lh, s, Object.assign({ "text-anchor": "middle" }, a.attrs)), 94)); + const coats = NAMES.map((nm, i) => { + const g = el("g", {}, svg); + el("rect", { x: -55, y: -8, width: 110, height: 304, rx: 14, fill: "#fff", "fill-opacity": 0, stroke: INK, "stroke-width": 2, "stroke-dasharray": "4 7", class: "mg-ring" }, g); + el("path", { d: "M0 0 V12", stroke: INK, "stroke-width": 3, "stroke-linecap": "round" }, g); + el("path", { d: "M-14 10 Q0 20 14 10 L44 26 Q48 28 48 34 L50 150 Q50 156 44 156 H-44 Q-50 156 -50 150 L-48 34 Q-48 28 -44 26 Z", fill: COL[i], stroke: INK, "stroke-width": 3, "stroke-linejoin": "round" }, g); + el("path", { d: "M-14 10 L0 46 L14 10 M0 46 V156", stroke: INK, "stroke-width": 2.5, fill: "none", "stroke-linejoin": "round" }, g); + [72, 100, 128].forEach((y) => el("circle", { cx: 7, cy: y, r: 3.2, fill: INK }, g)); + el("path", { d: "M0 150 V178", stroke: INK, "stroke-width": 2 }, g); + const tg = el("g", {}, g), inner = el("g", {}, tg); + tag(inner, false); + const front = el("g", {}, inner), back = el("g", {}, inner); + lines(front, nm, { lh: 20, attrs: { "font-family": MONO, "font-size": 14, "font-weight": 700 } }); + lines(back, RUNS[i], { lh: 18, attrs: { "font-size": 13, "font-weight": 500 } }); + const fl = flipper(); + button(g, `${nm.join(" ")}: ${RUNS[i].join(" ")}`, () => { fl.tap(api.now()); api.poke(); }); + return { g, tg, inner, front, back, fl, d: 0.5 + i * 0.9 }; + }); + const empty = el("g", {}, svg); + el("path", { d: "M0 0 V176", stroke: MUTE, "stroke-width": 2 }, empty); + const et = el("g", { opacity: 0.6 }, empty); + tag(et, true); + txt(et, 0, 57, "Roo Code,", { "text-anchor": "middle", "font-family": MONO, "font-size": 14, "font-weight": 700, fill: MUTE }); + txt(et, 0, 77, "Archived", { "text-anchor": "middle", "font-size": 14, "font-weight": 600, fill: MUTE }); + txt(svg, 400, 548, "tap a coat", { "text-anchor": "middle", "font-size": 16, "font-weight": 500, fill: MUTE }); + const swing = (u) => (u > 0 ? 10 * Math.exp(-u * 3) * Math.sin(u * 10) : 0); + return (t, now) => { + coats.forEach((c, i) => { + const k = seg(t, c.d, c.d + 0.55), u = t - c.d - 0.55; + tf(c.g, X(i), HY - 260 * (1 - ob(k)), 1, swing(u) * 0.3); + c.g.setAttribute("opacity", seg(t, c.d, c.d + 0.1)); + c.tg.setAttribute("transform", `translate(0 178) rotate(${swing(u - 0.1)})`); + const cs = Math.cos(Math.PI * c.fl.get(0, now)); + c.inner.setAttribute("transform", `scale(${Math.max(Math.abs(cs), 0.01)} 1)`); + c.front.setAttribute("opacity", cs >= 0 ? 1 : 0); c.back.setAttribute("opacity", cs < 0 ? 1 : 0); + }); + const e = seg(t, 6.0, 6.8); + tf(empty, X(6), 168); empty.setAttribute("opacity", e); + et.setAttribute("transform", `translate(0 176) rotate(${swing(t - 6.4) * 0.6})`); + }; + }, + }; + + /* ---- 3. chairs fill in around a long table ---- */ + SC.chairs = { + dur: 12, still: 11.8, + build(svg, api) { + const TB = { x: 150, y: 215, w: 500, h: 170 }; + const spots = []; + [200, 300, 400, 500, 600].forEach((x) => { spots.push([x, 192, 0, 0]); spots.push([x, 408, 180, 0]); }); + [250, 350, 450, 550].forEach((x) => { spots.push([x, 192, 0, 1]); spots.push([x, 408, 180, 1]); }); + [265, 335].forEach((y) => { spots.push([122, y, -90, 1]); spots.push([678, y, 90, 1]); }); + for (let x = 175; x <= 625; x += 50) { spots.push([x, 146, 0, 2]); spots.push([x, 454, 180, 2]); } + const PC = [BLUE, LIL, MINT], cnt = [0, 0, 0]; + const table = el("g", {}, svg); + el("rect", { x: TB.x - 12, y: TB.y - 12, width: TB.w + 24, height: TB.h + 24, rx: 30, fill: "#fff", "fill-opacity": 0, stroke: INK, "stroke-width": 2, "stroke-dasharray": "4 7", class: "mg-ring" }, table); + el("rect", { x: TB.x, y: TB.y, width: TB.w, height: TB.h, rx: 22, fill: YEL, stroke: INK, "stroke-width": 3 }, table); + el("path", { d: "M178 236 H330 M470 236 H620 M178 366 H300 M500 366 H620", stroke: INK, "stroke-width": 2, "stroke-opacity": 0.3, "stroke-linecap": "round" }, table); + const card = el("g", {}, table); + el("rect", { x: -134, y: -44, width: 280, height: 100, rx: 16, fill: BLUE, stroke: INK, "stroke-width": 3 }, card); + el("rect", { x: -140, y: -50, width: 280, height: 100, rx: 16, fill: "#fff", stroke: INK, "stroke-width": 3 }, card); + const l1 = txt(card, 0, -10, "", { "text-anchor": "middle", "font-size": 24, "font-weight": 700 }); + const l2 = txt(card, 0, 30, "", { "text-anchor": "middle", "font-size": 28, "font-weight": 700, fill: MUTE }); + const TIERS = [["Up to 10 users", "Free"], ["11 to 50 users", "$249 a month"], ["51 to 100 users", "$499 a month"]]; + const chairs = spots.map(([x, y, r, ph]) => { + const g = el("g", {}, svg); + el("rect", { x: -16, y: -22, width: 32, height: 9, rx: 4.5, fill: PC[ph], stroke: INK, "stroke-width": 2.5 }, g); + el("rect", { x: -14, y: -11, width: 28, height: 24, rx: 6, fill: PC[ph], stroke: INK, "stroke-width": 2.5 }, g); + const d = ph === 0 ? 0.4 + cnt[0]++ * 0.28 : ph === 1 ? 4.2 + cnt[1]++ * 0.15 : 7.2 + cnt[2]++ * 0.1; + const dx = r === -90 ? -220 : r === 90 ? 220 : 0, dy = r === 0 ? -240 : r === 180 ? 240 : 0; + return { g, x, y, r, ph, d, dx, dy }; + }); + const sw2 = chairs.filter((c) => c.ph === 1)[0].d + 0.45, sw3 = chairs.filter((c) => c.ph === 2)[0].d + 0.45; + tf(card, 400, 300); + txt(svg, 756, 48, "tap the table", { "text-anchor": "end", "font-size": 16, "font-weight": 500, fill: MUTE }); + let lastT = 0, clr = null; + button(table, "Long table. Up to 10 users, Free. 11 to 50 users, $249 a month. 51 to 100 users, $499 a month. Clear the extra chairs and count again.", () => { clr = { at: api.now(), t: lastT }; api.seek(3.6); }); + return (t, now) => { + lastT = t; + const back = clr && now - clr.at < 0.6 ? 1 - io(seg(now - clr.at, 0, 0.5)) : -1; + chairs.forEach((c) => { + let k = oc(seg(t, c.d, c.d + 0.45)), op = seg(t, c.d, c.d + 0.1); + if (back >= 0 && c.ph > 0 && clr.t >= c.d + 0.45) { k = back; op = back; } + tf(c.g, c.x + c.dx * (1 - k), c.y + c.dy * (1 - k), 1, c.r); c.g.setAttribute("opacity", op); + }); + const tier = t >= sw3 ? 2 : t >= sw2 ? 1 : 0, sw = [0.2, sw2, sw3][tier]; + l1.textContent = TIERS[tier][0]; l2.textContent = TIERS[tier][1]; + const p = Math.sin(Math.PI * seg(t, sw, sw + 0.35)); + tf(card, 400, 300, 1 + 0.08 * p); + }; + }, + }; + + /* ---- title card, 16 by 9 ---- */ + SC.hero = { + w: 960, h: 540, dur: 3.6, still: 3.5, + build(svg) { + el("rect", { x: 64, y: 52, width: 150, height: 40, rx: 20, fill: SOFT, stroke: INK, "stroke-width": 2 }, svg); + txt(svg, 139, 79, "Alternatives", { "text-anchor": "middle", "font-size": 18, "font-weight": 600 }); + txt(svg, 64, 172, "Roo Code alternatives", { "font-size": 66, "font-weight": 700 }); + el("path", { d: "M66 198 H430", stroke: YEL, "stroke-width": 12, "stroke-linecap": "round" }, svg); + txt(svg, 64, 248, "6 picks now that Roo Code is shut down", { "font-size": 26, "font-weight": 500, fill: MUTE }); + txt(svg, 64, 284, "Checked 4 Nov 2026", { "font-size": 18, "font-weight": 500, fill: MUTE }); + el("rect", { x: 400, y: 318, width: 160, height: 44, rx: 12, fill: SOFT, stroke: MUTE, "stroke-width": 2, "stroke-dasharray": "5 5" }, svg); + txt(svg, 480, 346, "Roo Code", { "text-anchor": "middle", "font-family": MONO, "font-size": 18, "font-weight": 700, fill: MUTE }); + const NAMES = ["ZooCode", "Munder Difflin", "Cline", "Kilo Code", "Roomote", "Claude Code"], COL = [YEL, BLUE, LIL, SKY, MINT, YEL]; + const tiles = NAMES.map((s, i) => { + const x = 40 + i * 148, cx = x + 70; + const ln = el("path", { d: `M480 362 C480 400 ${cx} 392 ${cx} 432`, stroke: MUTE, "stroke-width": 2, fill: "none", "stroke-dasharray": "3 6", "stroke-linecap": "round" }, svg); + const g = el("g", {}, svg); + el("rect", { x, y: 432, width: 140, height: 52, rx: 12, fill: COL[i], stroke: INK, "stroke-width": 2.5 }, g); + txt(g, cx, 463, s, { "text-anchor": "middle", "font-family": MONO, "font-size": 14, "font-weight": 700 }); + return { g, ln, d: 0.5 + i * 0.3 }; + }); + return (t) => tiles.forEach((o) => { const k = seg(t, o.d, o.d + 0.5); o.g.setAttribute("transform", `translate(0 ${40 * (1 - ob(k))})`); o.g.setAttribute("opacity", seg(t, o.d, o.d + 0.15)); o.ln.setAttribute("opacity", seg(t, o.d, o.d + 0.3)); }); + }, + }; + + /* ---- player ---- */ + const q = new URLSearchParams(location.search), frozen = q.has("mgstill"), fixed = parseFloat(q.get("mgstill")); + const calm = matchMedia("(prefers-reduced-motion: reduce)").matches; + const mount = (fig) => { + const sc = SC[fig.dataset.scene], img = fig.querySelector("img"); if (!sc || !img) return; + const W = sc.w || 800, H = sc.h || 600; + const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, class: "mg-svg", role: "group", "aria-label": img.alt || "" }); + svg.style.aspectRatio = `${W} / ${H}`; + el("rect", { width: W, height: H, fill: "#fff" }, svg); + img.replaceWith(svg); + let off = 0, raf = 0, seen = false, held = calm; + const base = performance.now(), clock = () => (performance.now() - base) / 1000; + let draw = () => {}; + const frame = () => { const now = clock(); draw(held ? sc.still : (((now + off) % sc.dur) + sc.dur) % sc.dur, now); }; + const tick = () => { cancelAnimationFrame(raf); frame(); if (seen && !held) raf = requestAnimationFrame(tick); }; + const api = { now: clock, seek: (s) => { off = s - clock(); held = false; tick(); }, poke: () => { held = false; tick(); } }; + draw = sc.build(svg, api); + if (frozen) { draw(isNaN(fixed) ? sc.still : fixed, 0); return; } + new IntersectionObserver(([e]) => { + seen = e.isIntersecting; + if (seen) { tick(); if (!fig.dataset.sent && window.posthog) { fig.dataset.sent = 1; window.posthog.capture("blog_scene_view", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } } + else cancelAnimationFrame(raf); + }, { threshold: 0.25 }).observe(fig); + svg.addEventListener("click", () => { if (!fig.dataset.hit && window.posthog) { fig.dataset.hit = 1; window.posthog.capture("blog_scene_interact", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } }); + frame(); + }; + const go = () => document.querySelectorAll("figure.mg[data-scene]").forEach(mount); + const fonts = document.fonts && document.fonts.load ? Promise.all([document.fonts.load('700 32px "Space Grotesk"'), document.fonts.load('500 20px "Space Grotesk"'), document.fonts.load('700 20px "JetBrains Mono"')]).catch(() => {}) : Promise.resolve(); + const start = () => fonts.then(go, go); + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start(); +})(); diff --git a/blog/src/posts/agent-tools-today-2026-11-05.md b/blog/src/posts/agent-tools-today-2026-11-05.md new file mode 100644 index 0000000000..b51a475aaa --- /dev/null +++ b/blog/src/posts/agent-tools-today-2026-11-05.md @@ -0,0 +1,58 @@ +--- +title: "Agent Tools Today, 5 Nov 2026: Conductor Joins Meta, OpenAI to Buy Pierre, Gemini Training" +seoTitle: "Agent Tools Today (5 Nov 2026): Launches, Skills, MCP Servers" +description: "The top launches, skills, multi agent moves, rising repos and talking points in AI agents from 4 to 5 Nov 2026, each with its source and date." +date: 2026-11-05 +category: news +categoryLabel: News +type: Non-technical +series: agent-tools-today +primaryKeyword: "agent tools today 5 nov 2026" +secondaryKeywords: ["conductor joining meta", "kimi k3.1 pro weights", "gemini keep activity training", "openai pierre acquisition", "t3 code 0.1.7", "qwen code 0.26.7"] +tags: ["Daily Brief", "Codex", "Skills", "Research"] +--- + +Agent tools today, 5 Nov 2026, in one line: Conductor is joining Meta, OpenAI will acquire Pierre, the company behind diff tools in the Codex app, and Google's privacy page says saved Gemini chats can train its models. It covers 4 Nov 04:00 UTC to 5 Nov 04:00 UTC, checked 5 Nov. The previous edition is [4 Nov](/blog/agent-tools-today-2026-11-04/). + +
Animation. On a white background, a toy xylophone has three wide bars in blue, lilac and mint. The first bar reads Conductor joins Meta, the second reads OpenAI to buy Pierre, the third reads Gemini training. A mallet taps each bar in turn and the bar bounces. Tapping a bar makes it bounce and ring again.
The day's three lead items, from Conductor's announcement, OpenAI's Pierre post and Google's Gemini privacy page, read 5 Nov 2026.
+ +## Top 3 launches + +1. **Kimi K3.1 Pro weights.** Our [26 Oct edition](/blog/agent-tools-today-2026-10-26/) said full weights were due by 4 Nov; the Hugging Face repo dates from 2 Nov, and a Hacker News post saying they are out drew 413 points by 5 Nov. The model card lists 2.8T total parameters, 104B activated and a context length of 1,048,576 tokens. [Source](https://huggingface.co/moonshotai/Kimi-K3.1-Pro) +2. **Ripple-65B Preview.** Ricursive Intelligence says it "trained a language model from scratch" and released this "general-purpose variant" on 4 Nov, claiming "U.S. open-weight state-of-the-art (80.52) on TerminalBench 2.1" (the vendor's own figures). Its 4 Nov page puts the full R&D cost at "$32M". 241 points on Hacker News on 5 Nov. [Source](https://www.ricursive.com/ripple) +3. **AutoCua.** "Automate with computer-use agent (ComputerUse / CUA) for macOS, Windows, Linux, Browser, iOS & Android." The README table marks iPhone use as macOS only, and it installs with `pip install AutoCua`. MIT, 1,245 stars on 5 Nov; its 4 Nov Show HN had 3 points on 5 Nov. [Source](https://github.com/ComputerUseAgent/AutoCua) + +## Top 2 skills, MCP servers and plugins + +1. **Scenario Agent Skills.** "Agent Skills that teach AI coding agents (Claude Code, Cursor, Codex, Copilot, and 70+ others) how to create production-ready content with Scenario through the Scenario MCP server". A second set drives "ZBrush, Blender, Maya, Unreal Engine, Unity, Godot, Roblox Studio" and needs the app, not the MCP server. MIT, 2,516 stars, 224 in a day on GitHub trending on 5 Nov. See [Claude Code skills](/blog/claude-code-skills/). [Source](https://github.com/scenario-labs/skills) +2. **Gauge CLI.** "An open-source CLI for hillclimbing Agent Experience." It "runs real agents against your product, captures where they get stuck", and "The full loop runs locally with Docker and your existing agent subscriptions or API keys." MIT, 53 stars, 23 points on Hacker News on 5 Nov. [Source](https://github.com/gauge-sh/gauge-cli) + +## Top 5 moves from multi agent tools + +1. **Conductor is joining Meta.** Conductor's post says that from 4 Nov new users "can no longer sign up for Conductor or download the app", and pro users "can keep using pro features for free". Conductor Cloud goes read only on 13 Nov and data deletion begins on 20 Nov, so export before then. Local Conductor "will continue to receive new models and critical bug fixes until early 2027." 135 points and 159 comments on Hacker News on 5 Nov. [Source](https://www.conductor.build/announcement) +2. **OpenAI to acquire Pierre.** OpenAI says "If you’ve browsed files or reviewed a change in the Codex app, you’ve already used Pierre’s Trees and Diffs libraries." It adds that "Pierre’s open-source projects will remain open-source", and the deal still needs regulatory approvals. See [Codex Max plan tips](/blog/codex-max-plan-tips/). 207 points on Hacker News on 5 Nov. [Source](https://openai.com/index/openai-to-acquire-pierre/) +3. **T3 Code 0.1.7.** The stable release, published 4 Nov at 06:39 UTC, counts "659 changes from 160 contributors". It adds "Kiro (beta)" as an Early Access provider, schedules that run once, and a way to "Respond to delegated child approvals directly from the parent conversation." [Source](https://github.com/pingdotgg/t3code/releases/tag/v0.1.7) +4. **Qwen Code 0.26.7.** Published 4 Nov at 04:03 UTC. It "Requires explicit folder trust before repository-supplied hooks, permission grants, and project language servers can run", and fixes "tool-choice failures on Claude Opus 5.5, Sonnet 5.5, Fable 5.1, and Mythos 5.1". [Source](https://github.com/QwenLM/qwen-code/releases/tag/v0.26.7) +5. **Gemini CLI 0.67.0.** Published 4 Nov at 20:27 UTC, it adds a `models` command and a `--list-models` flag "for model discovery". [Source](https://github.com/google-gemini/gemini-cli/releases/tag/v0.67.0) + +## Top 4 rising repos + +1. **Agent Autoloop.** Its English README says it "turns your command-line agents into a background loop." Any CLI tool "that takes a prompt and prints a final reply can plug in", and decision cards go to "Feishu, DingTalk, WeCom, Slack, Discord, or desktop notifications". macOS and Linux only for now. MIT, created 3 Nov, 2,691 stars on 5 Nov. [Source](https://github.com/KKKKhazix/agent-autoloop) +2. **Muse4AI.** "let your AI agent make the pictures and the videos. Local Windows bridge for Muse via REST, MCP, and OAuth." MIT, created 3 Nov, 508 stars on 5 Nov. [Source](https://github.com/andrew-babo/muse4ai) +3. **shim.** "Tiny local-first AI harness: any model, MCP and Agent Skills, natural-language CLI. Zero dependencies." MIT, created 2 Nov, 452 stars on 5 Nov. [Source](https://github.com/Balanov/shim) +4. **verdict.** From the same account as shim: "A 197 KB local semantic verifier for AI agent tool calls. Exit code 0 is not proof." Anyone who has trusted a green build at 2 am knew the second sentence already. Apache 2.0, created 2 Nov, 236 stars on 5 Nov. [Source](https://github.com/Balanov/verdict) + +Stars can rise for reasons other than use: names to watch, not picks. + +## Top 4 things people are talking about + +1. **Does Gemini train on your saved chats?** Yes, when Keep Activity is on: Google's Gemini Apps privacy page says "Google uses your activity to provide, develop, and improve its services (including training generative AI models)". Data reviewed by service providers is "saved for 3 years". With it off, chats are "retained with your account for 72 hours". A Tell HN on the setting had 634 points and 164 comments on 5 Nov. [Source](https://support.google.com/gemini/answer/13594961?hl=en) +2. **Did Codex wreck a server?** A GitHub issue opened 3 Nov, filed against Codex CLI 0.166.0 on a Business plan, is titled "Told to reboot a server, it destroyed systemd, destroyed containers, ran random scripts, cost me $10,000+". These are the reporter's claims. When we read the issue on 5 Nov it had 321 comments and no reply from OpenAI staff. 45 points on Hacker News on 5 Nov. [Source](https://github.com/openai/codex/issues/59060) +3. **How far can agents go on the Linux kernel?** OpenAI says its prototype "identified and analyzed 2,308 candidate Linux kernel findings, dynamically reproduced 1,724 vulnerabilities, and produced 948 local privilege escalation (LPE) exploits". The pipeline runs on "an upcoming cyber model", and security researchers review each exploit and patch before reporting. 180 points on Hacker News on 5 Nov. [Source](https://openai.com/index/a-thousand-fewer-paths-to-root/) +4. **What agent setup are people using?** An Ask HN on 4 Nov asks "What coding harnesses and interfaces are you using?" and which AI subscriptions people pay for. 154 points and 182 comments on 5 Nov. [Source](https://news.ycombinator.com/item?id=50366117) + +## How this list is built + +We read tracked tools' GitHub releases, GitHub trending, the Hacker News front page and a scout report, and opened every page linked above. No item here comes from X or Reddit. A name only makes a list with an opened link and a signal: points, stars or a dated release. Claude Code shipped no release in the window; Codex shipped only alphas with empty notes, and nightlies are left out. + + diff --git a/blog/src/posts/antigravity-alternatives.md b/blog/src/posts/antigravity-alternatives.md new file mode 100644 index 0000000000..9b4e8985b2 --- /dev/null +++ b/blog/src/posts/antigravity-alternatives.md @@ -0,0 +1,90 @@ +--- +title: "Antigravity Alternatives: 6 Picks by Why You Are Leaving (Nov 2026)" +seoTitle: "6 Google Antigravity Alternatives (Nov 2026): Models and Prices" +description: "Antigravity has no bring your own key option for more rate limits and drops Claude 4.6 on 18 Nov. Six alternatives, prices checked 4 Nov 2026." +date: 2026-11-04 +category: comparisons +categoryLabel: Comparisons +type: Technical +primaryKeyword: "antigravity alternatives" +secondaryKeywords: ["antigravity alternative", "google antigravity alternatives", "is google antigravity free", "antigravity bring your own key", "open source antigravity alternative", "antigravity vs cursor"] +tags: ["Comparisons", "Antigravity", "Claude Code", "IDE", "Open Source", "Tools"] +faq: + - q: "Is Google Antigravity free?" + a: "Yes, with limits. Google's pricing page, read 4 Nov 2026, lists the Individual plan at $0 a month with basic weekly rate limits. Google's plans post of 19 May 2026 puts Google AI Pro at $20 a month and Google AI Ultra at $100 or $200 a month for higher limits." + - q: "Can I use my own API key in Antigravity?" + a: "Not for the app's rate limits. Google's plans docs, read 4 Nov 2026, say there is currently no support for \"Bring-your-own-key (BYOK) or bring-your-own-endpoint for additional rate limits\". The Antigravity SDK does take a Gemini API key. If your own keys matter, Cline is built around them." + - q: "Which Claude models does Antigravity have?" + a: "Google's models page, read 4 Nov 2026, lists Claude Sonnet 5.5 and Claude Opus 5.5 (thinking) on paid Google AI Pro, Ultra and Enterprise. Claude Sonnet 4.6 and Opus 4.6 are on Free and Pro, with a note that they will be removed on 18 Nov 2026." + - q: "Can Antigravity agents keep working with my computer off?" + a: "On Google AI Pro and Ultra, as remote agents roll out. Google's remote agents page, read 4 Nov 2026, says they run in a Google managed environment, need no local machine to stay on, and are for Google AI Pro and Ultra subscribers, not the free plan." + - q: "What is the best open source Antigravity alternative?" + a: "Our view: Cline, if you want a coding agent that takes your own API keys. Munder Difflin is free and open source too, if you want a team of agents on your computer. Zed and the Codex CLI are open source as well. Checked 4 Nov 2026." +--- + +Our view: Claude Code is the first Google Antigravity alternative to try, because it gets Anthropic's newest models such as Fable 5.1, which Antigravity's models page doesn't list. If price or your own API keys are why you're leaving, the open source picks below fit better. Checked 4 Nov 2026. + +[Munder Difflin](https://harnessmd.com/download) is free and open source: a desktop app that runs a team of AI agents for all kinds of knowledge work, coding included, on your own computer. For the head to head, read [Claude Code vs Antigravity](/blog/claude-code-vs-antigravity/). More pages like this are in our [Comparisons hub](/blog/topics/comparisons/). + +## What is Google Antigravity? + +Google Antigravity is Google's agent platform for building software, sold as a desktop app, a CLI, an IDE, IDE extensions and an SDK. That is five surfaces, which is more than most of us have monitors. Its [homepage](https://antigravity.google), read 4 Nov 2026, calls it "our agentic development platform" and the app "Your command center to manage multiple local agents in parallel". It is "Available on macOS, Windows, Linux, and Googlebook". + +The [changelog](https://antigravity.google/docs/changelog) shows App v2.29.0 on 3 Nov 2026, which starts rolling out remote agents to Google AI Ultra and Pro subscribers. The [remote agents page](https://antigravity.google/docs/remote-agents) says they aren't available on the free plan. + +## Which models does Antigravity use, and what does it cost? + +Gemini first, with Claude on paid plans. Google's [models page](https://antigravity.google/docs/models), read 4 Nov 2026, gives every plan Gemini 3.8 Flash. Gemini 4 Argon, Claude Sonnet 5.5 and Claude Opus 5.5 need a paid Google AI Pro plan or higher (Argon also comes on some trials). Claude Sonnet 4.6, Claude Opus 4.6 and GPT-OSS-120b carry a note: "Will be removed on November 18, 2026." + +
Animation. On a white background, a desk calendar flips its pages and stops on November 18. A sticky note on the page reads Claude 4.6 and GPT OSS 120b leave Antigravity. Tapping the calendar flips the pages again and the note lands once more.
Google's models page, read 4 Nov 2026: Claude 4.6 models and GPT-OSS-120b will be removed on 18 Nov 2026.
+ +The [pricing page](https://antigravity.google/pricing), read 4 Nov 2026, lists the Individual plan at "$0/month" with "Basic weekly rate limits". Google's [plans post of 19 May 2026](https://antigravity.google/blog/changes-to-antigravity-plans) puts Google AI Pro at $20 a month and Ultra at $100 or $200, with 5X and 20X Pro's tokens. The [plans docs](https://antigravity.google/docs/plans) say "Usage limits for this service are subject to modification" and list no support for "Bring-your-own-key (BYOK)". Our view: that last line is the strongest reason to look elsewhere. + +## What are the best Antigravity alternatives? + +Six, sorted by why you would leave. Every page below was read on 4 Nov 2026. + +| Alternative | What it is | Where it runs | Price or licence, 4 Nov 2026 | +| --- | --- | --- | --- | +| Claude Code | Anthropic's coding agent | Terminal, IDE, desktop, web | Not in Claude Free. Pro from $17 a month. | +| Munder Difflin | A team of AI agents for knowledge work, coding included | Your computer | Free and open source. | +| Codex | OpenAI's coding agent | CLI, IDE, desktop app, web | Apache 2.0 CLI. Plus $20 a month, or API pricing. | +| Cursor | Coding agent and editor | Desktop, CLI, cloud agents | Free plan. Premium $30 a month. | +| Cline | Open source coding agent | VS Code, JetBrains, terminal, desktop | Apache 2.0. Free; you pay for inference. | +| Zed | Open source code editor with AI | Your computer | Mostly GPL 3.0 or later. Personal $0, Pro $10 a month. | + +
Animation. On a white background, a vending machine holds six snacks in a row, each labelled: Claude Code, Munder Difflin, Codex, Cursor, Cline, Zed. A coil turns, one snack drops, and a card slides out of the tray that reads where it runs, such as Your computer. Tapping a button drops that snack and shows its card.
Six Antigravity alternatives, from each vendor's own pages, read 4 Nov 2026.
+ +## 1. Claude Code: if you want Anthropic's newest models + +Our view: Claude Code is the pick if Claude was why you used Antigravity. Its [model docs](https://code.claude.com/docs/en/model-config), read 4 Nov 2026, map `opus` to Opus 5.5 on Anthropic's API and the `fable` alias to Fable 5.1, which Antigravity's list doesn't show. The [overview](https://code.claude.com/docs/en/overview) says it runs on "the terminal, IDE extensions, a desktop app, and the web". + +[Claude's pricing page](https://claude.com/pricing), read 4 Nov 2026, leaves Claude Code out of Free and includes it in Pro at $17 a month billed annually or $20 monthly, then Max at $100 and Ultra at $200. More in [what Claude Code costs](/blog/how-much-does-claude-code-cost/). + +## 2. Munder Difflin: if the work is more than code + +[Munder Difflin](https://harnessmd.com/download) is what we make. Pick it if research, writing and ops sit next to the coding on your list. The catch: agents run while your computer runs. A Cloud Office and a mobile app are coming soon. Setup is in [how to install and use Munder Difflin](/blog/how-to-install-and-use-munder-difflin/). + +## 3. Codex: if you already pay for ChatGPT + +Your ChatGPT plan may already include Codex. [OpenAI's Codex pricing page](https://developers.openai.com/codex/pricing), read 4 Nov 2026, lists Free and Go ($8 a month) with GPT-6 Luna "in the desktop app, subject to rollout". Plus at $20 a month adds "Codex on the web, in the CLI, in the IDE extension, and on iOS" with GPT-6.1 Sol and GPT-6 Luna, and Pro starts at $100. With an API key you pay API pricing. The CLI is Apache 2.0 per its [GitHub repo](https://github.com/openai/codex), with rust-v0.166.0 released on 29 Oct 2026. See [Codex pricing](/blog/codex-pricing/). + +## 4. Cursor: if you want an editor with cloud agents + +Cursor fits if you want one vendor for the editor, the CLI and cloud agents, but read its plans first. Its [pricing page](https://cursor.com/pricing), read 4 Nov 2026, lists Free at $0 with "Limited agent requests", Plus at $8 a month where "Only Grok models are available on this plan", and Premium at $30 a month with cloud agents and "Third-party models at API list price". The [models docs](https://cursor.com/docs/models) say Claude, GPT and Gemini "aren't part of the included pool". See [Cursor pricing](/blog/cursor-pricing/). + +## 5. Cline: if you want your own API keys + +We think Cline is the closest answer to Antigravity's missing BYOK. Its [README](https://github.com/cline/cline) lists a CLI, a VS Code extension, a JetBrains plugin and "a native app for macOS and Windows". It's Apache 2.0 and shipped v4.1.26 on 26 Oct 2026. The [pricing page](https://cline.bot/pricing), read 4 Nov 2026, says "Cline is free for individual developers" and that you can "bring your own API keys from providers like OpenAI, Anthropic, Google, and others." + +
Animation. On a white background, a wallet opens and three cards slide up one by one: Anthropic key, OpenAI key, Google key. A note on the flap reads Free for individual developers. Tapping the wallet closes it and opens it again so the cards slide up once more.
Cline's pricing page, read 4 Nov 2026: free for individual developers, with your own API keys.
+ +## 6. Zed: if you want local control + +Zed is an open source editor where AI is optional and local models are on the menu. Its [pricing page](https://zed.dev/pricing), read 4 Nov 2026, lists Personal at "$0 forever" with "Unlimited use with your API keys or external agents like Claude Agent, Codex CLI, and more", and Pro at $10 a month. The same page names Ollama and LM Studio among its providers. The [README](https://github.com/zed-industries/zed) says Zed is "licensed primarily under GPL-3.0-or-later", and v1.26.2 shipped on 28 Oct 2026. + +## When should you stay on Antigravity? + +Stay if Gemini is your main model and the free limits are enough. Google's plans docs give every plan "Access to all product features, such as scheduled tasks and the CLI". Stay too if you want agents in Google's cloud on Pro or Ultra: the remote agents page says they keep working "even when you close your browser, put your laptop to sleep, or switch between desktop and mobile devices." + + diff --git a/blog/src/posts/claude-code-vs-antigravity.md b/blog/src/posts/claude-code-vs-antigravity.md index 428371596b..907c933afa 100644 --- a/blog/src/posts/claude-code-vs-antigravity.md +++ b/blog/src/posts/claude-code-vs-antigravity.md @@ -63,7 +63,7 @@ An Antigravity agent launches `agy`, and auto mode adds `--dangerously-skip-perm ## Which should you pick, Claude Code or Antigravity? -Pick Antigravity for a free start and visual, browser heavy work; pick Claude Code for terminal and CI work on the newest Claude models. +Pick Antigravity for a free start and visual, browser heavy work; pick Claude Code for terminal and CI work on the newest Claude models. If you are leaving Antigravity for something else entirely, our [Antigravity alternatives](/blog/antigravity-alternatives/) page sorts six options by why you would switch. * **No budget, or just curious about agents:** Antigravity. Google's plans docs include the CLI and scheduled tasks on every plan. * **Frontend work you want to watch:** Antigravity 2.0 or the IDE, where a built in browser subagent runs interactive browser tests. Claude Code has a Chrome integration too. diff --git a/blog/src/posts/gemini-cli-alternatives.md b/blog/src/posts/gemini-cli-alternatives.md new file mode 100644 index 0000000000..abea80bb80 --- /dev/null +++ b/blog/src/posts/gemini-cli-alternatives.md @@ -0,0 +1,94 @@ +--- +title: "Gemini CLI Alternatives: 6 Picks Now the Free Tier Has Moved (Nov 2026)" +seoTitle: "6 Gemini CLI Alternatives (Nov 2026): Models and Prices" +description: "Gemini CLI lost its free tier on 18 Jun 2026. Six alternatives by reason for leaving, with models and prices checked 4 Nov 2026." +date: 2026-11-04 +category: comparisons +categoryLabel: Comparisons +type: Technical +primaryKeyword: "gemini cli alternatives" +secondaryKeywords: ["gemini cli alternative", "is gemini cli still free", "gemini cli replacement", "open source gemini cli alternative", "gemini cli vs antigravity cli"] +tags: ["Comparisons", "CLI Agents", "Claude Code", "Open Source", "Tools"] +faq: + - q: "Is Gemini CLI still free?" + a: "Not for individuals. Google's announcement of 19 May 2026 said that on 18 Jun 2026 Gemini CLI would stop serving requests for Google AI Pro and Ultra and for people using it free of charge. The README, read 4 Nov 2026, now says \"No free tier or Google AI plans.\" The code itself stays Apache 2.0." + - q: "What replaced Gemini CLI?" + a: "Antigravity CLI, run as agy. Gemini CLI's README points free users and Google AI Pro and Ultra subscribers there. Antigravity's migration guide, read 4 Nov 2026, gives agy plugin import gemini to turn old extensions into plugins." + - q: "Who can still use Gemini CLI?" + a: "Organisations on Gemini Code Assist Standard or Enterprise, Code Assist for GitHub on Google Cloud, and people with paid API or Vertex keys. Google's quota page, read 4 Nov 2026, lists 1,500 model requests per user per day on Standard and 2,000 on Enterprise." + - q: "Is there an open source Gemini CLI alternative?" + a: "Yes. Qwen Code is Apache 2.0 and started from Gemini CLI's code. Munder Difflin is free and open source too. OpenCode is MIT, and Codex CLI is Apache 2.0. Claude Code is not open source. Checked 4 Nov 2026." + - q: "Can I keep using Gemini models without Gemini CLI?" + a: "Yes. Antigravity CLI runs Gemini models, Qwen Code's README says it supports Gemini APIs alongside OpenAI, Anthropic and Qwen, and OpenCode's providers page lists Google Vertex AI. Checked 4 Nov 2026." +--- + +Our view: Antigravity CLI is the first Gemini CLI alternative to try, because Google moved free users and Google AI Pro and Ultra subscribers to it on 18 Jun 2026. If you would rather leave Google, Claude Code, Codex CLI, OpenCode and Qwen Code are the main options. Checked 4 Nov 2026. + +[Munder Difflin](https://harnessmd.com/download) is free and open source: a desktop app that runs a team of AI agents for all kinds of knowledge work, coding included, on your own computer. More pages like this sit in our [Comparisons hub](/blog/topics/comparisons/). + +## What is Gemini CLI, and what does it cost now? + +Gemini CLI (`gemini`) is Google's open source AI agent for the terminal, Apache 2.0, and for individuals it is now paid only. Its [README](https://github.com/google-gemini/gemini-cli), read 4 Nov 2026, opens with a notice: "No free tier or Google AI plans". Free individual access and Google AI Pro and Ultra "moved to Antigravity CLI on June 18, 2026". What remains is Gemini Code Assist Standard or Enterprise, Code Assist for GitHub on Google Cloud, and "legacy paid API / Vertex keys". + +[Google's announcement](https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/) of 19 May 2026 set that date and said Gemini CLI "will remain accessible via paid Gemini and Gemini Enterprise Agent Platform API keys." Stable [v0.67.0](https://github.com/google-gemini/gemini-cli/releases/tag/v0.67.0) shipped on 4 Nov 2026. + +
Animation. On a white background, a shop door holds a hanging sign that reads Gemini CLI. Free and Google AI Pro: moved to Antigravity CLI. The sign swings and flips to its other side: Code Assist Standard and Enterprise: open. Tapping the sign flips it back.
Gemini CLI's README, read 4 Nov 2026: free and Google AI plans moved to Antigravity CLI on 18 Jun 2026.
+ +## Which models and quotas does Gemini CLI have? + +Gemini 3 and Gemini 2.5 models, not Gemini 4. Its [model docs](https://geminicli.com/docs/cli/model/), read 4 Nov 2026, offer Auto (Gemini 3) with `gemini-3-pro-preview` and `gemini-3-flash-preview`, Auto (Gemini 2.5), or Manual. The README says it is "Not tuned for the Gemini 4 series" and sends Gemini 4 Argon users to Antigravity CLI. + +The [quota page](https://geminicli.com/docs/resources/quota-and-pricing/), read 4 Nov 2026, gives 1,500 model requests per user per day on Code Assist Standard and 2,000 on Enterprise. Its table still shows 1,000 free requests a day for a personal sign in, but the banner above it says Gemini CLI "was replaced by Antigravity CLI on June 18th, 2026" for unpaid and Google One users. Trust the banner. Our guide to [installing Gemini CLI](/blog/how-to-install-gemini-cli/) covers the sign ins that still work. + +
Animation. On a white background, a kitchen faucet fills two glasses. One label reads Standard, 1,500 requests a day and the other reads Enterprise, 2,000 requests a day. A third glass labelled Free stays empty. Tapping the faucet pours again.
Daily model requests per user on Gemini Code Assist, from Gemini CLI's quota page, read 4 Nov 2026.
+ +## What are the best Gemini CLI alternatives? + +Six, sorted by why you would leave. Every page below was read on 4 Nov 2026. + +| Alternative | What it is | Where it runs | Price or licence, 4 Nov 2026 | +| --- | --- | --- | --- | +| Antigravity CLI | Google's terminal for Antigravity agents | Terminal | Individual plan $0 a month, or Pro and Ultra. | +| Munder Difflin | A team of AI agents for knowledge work, coding included | Your computer | Free and open source. | +| Claude Code | Anthropic's coding agent | Terminal, IDE, desktop, web | All rights reserved. Pro from $17 a month (4 Nov). | +| Codex CLI | OpenAI's coding agent | Your computer | Apache 2.0. Plus $20 a month (4 Nov) or API pricing. | +| OpenCode | Open source coding agent | Terminal, beta desktop app | MIT. Provider costs. | +| Qwen Code | Open source agent that began as a Gemini CLI fork | Terminal, editor, desktop, browser, chat | Apache 2.0. Provider costs. | + +
Animation. On a white background, six fridge magnets slide onto a fridge door in a column: Antigravity CLI, Munder Difflin, Claude Code, Codex CLI, OpenCode, Qwen Code. Each one flips in turn to show a second line: Plan $0; Free and open source; Pro from $17; Plus $20; MIT; Apache 2.0. Tapping a magnet flips it.
Price or licence for each pick, from each vendor's own page, read 4 Nov 2026.
+ +## 1. Antigravity CLI: if you want to stay with Google + +Our view: this is the default move, since Google built it as the successor. [Antigravity's CLI page](https://antigravity.google/product/antigravity-cli) calls it "the terminal-first surface to interact with Antigravity agents". The [pricing page](https://antigravity.google/pricing), read 4 Nov 2026, has an individual plan at $0 a month with "Basic weekly rate limits", then Google AI Pro and Ultra with more. + +The [models page](https://antigravity.google/docs/models/) lists Gemini 4 Argon, Gemini 3.8 Flash and Claude models, though Argon is not on the free plan and three older models "Will be removed on November 18, 2026." The [migration guide](https://antigravity.google/docs/cli/gcli-migration) says `agy plugin import gemini` converts extensions, and workspace skills move from `.gemini/skills/` to `.agents/skills/`. More in [Claude Code vs Antigravity](/blog/claude-code-vs-antigravity/). + +## 2. Munder Difflin: if one agent at a time is the bottleneck + +[Munder Difflin](https://harnessmd.com/download) is ours, for several agents on research, writing, ops and code side by side. Engines such as Claude Code, Codex and Gemini CLI do the work, so Gemini CLI can stay as one engine. Slack and webhooks bring jobs in; MCP servers, skills and plugins add tools. A Cloud Office and a mobile app are coming soon. Today, agents run while your computer runs. See [how to install and use Munder Difflin](/blog/how-to-install-and-use-munder-difflin/). + +## 3. Claude Code: if you want Anthropic's models + +Our view: Claude Code is the swap if model quality matters more to you than open source. Its [overview](https://code.claude.com/docs/en/overview), read 4 Nov 2026, says it is "Available in your terminal, IDE, desktop app, and browser". + +[Anthropic's pricing page](https://claude.com/pricing), read 4 Nov 2026, leaves Claude Code out of Free and includes it in Pro at $17 a month billed annually or $20 monthly, with Max at $100 and Ultra at $200. Our [Gemini CLI vs Claude Code](/blog/gemini-cli-vs-claude-code/) page goes feature by feature. + +## 4. Codex CLI: if you already pay for ChatGPT + +Codex CLI is "a coding agent from OpenAI that runs locally on your computer", per its [README](https://github.com/openai/codex), Apache 2.0, with rust-v0.166.0 out on 29 Oct 2026. On [OpenAI's pricing page](https://developers.openai.com/codex/pricing), read 4 Nov 2026, Plus at $20 a month is the first plan with "Codex on the web, in the CLI, in the IDE extension, and on iOS", using GPT-6.1 Sol and GPT-6 Luna. Pro starts at $100 a month as of 4 Nov 2026, and an API key bills at API pricing. See [Codex vs Gemini CLI](/blog/codex-vs-gemini-cli/). + +## 5. OpenCode: if you want any provider + +We think OpenCode suits you if you liked choosing your own key. It is MIT licensed and shipped [v2.2.5](https://github.com/anomalyco/opencode/releases) on 30 Oct 2026. Its [providers page](https://opencode.ai/docs/providers/), read 4 Nov 2026, claims "75+ LLM providers" plus local models, Google Vertex AI among them. The [v2.2.3 notes](https://github.com/anomalyco/opencode/releases/tag/v2.2.3) add a Claude Agent provider "to use your Claude Code login and models", in OpenCode's words. + +## 6. Qwen Code: if you liked Gemini CLI itself + +We think Qwen Code is the most familiar swap: its [README](https://github.com/QwenLM/qwen-code) says it "was originally based on Google Gemini CLI v0.8.2" before it "stopped syncing with upstream". It is Apache 2.0 and shipped v0.26.7 on 4 Nov 2026. It "Supports OpenAI, Anthropic, Gemini, and Qwen APIs", plus local models through Ollama or vLLM. + +If you came for a free tier, keep walking. The [auth docs](https://qwenlm.github.io/qwen-code-docs/en/users/configuration/auth/), read 4 Nov 2026, say "The Qwen OAuth free tier was discontinued on 2026-04-15", so you bring an API key or an Alibaba Cloud Coding Plan. + +## When should you stay on Gemini CLI? + +Stay if your company already pays for Gemini Code Assist Standard or Enterprise. Google's announcement tells those customers "your access remains unchanged" and promises "access to the latest Gemini models and other updates." Scripts built on `gemini -p --output-format json` are another reason not to rush. + + diff --git a/blog/src/posts/how-to-install-gemini-cli.md b/blog/src/posts/how-to-install-gemini-cli.md index 4ca31108ec..285cc61fda 100644 --- a/blog/src/posts/how-to-install-gemini-cli.md +++ b/blog/src/posts/how-to-install-gemini-cli.md @@ -32,7 +32,7 @@ You can install it and keep it current by hand, or use [Munder Difflin](https:// Organisations on a Gemini Code Assist Standard or Enterprise licence can, and so can anyone paying for API access, including Vertex AI, which Google now calls [Gemini Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform). [Google's announcement of 19 May 2026](https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/) said that on 18 Jun 2026 Gemini CLI would "stop serving requests for Google AI Pro and Ultra, as well as those using it free of charge", and that it "will remain accessible via paid Gemini and Gemini Enterprise Agent Platform API keys". The docs now carry a banner saying it "was replaced by Antigravity CLI on June 18th, 2026" for unpaid and Google One users. -The [quota page](https://geminicli.com/docs/resources/quota-and-pricing/) and the README still advertise 1,000 free requests a day for a personal sign in. That is history. The page also lists an unpaid API key tier on Flash, but the announcement only names paid keys, so don't build on it. Individuals on AI Pro or Ultra are pointed to Antigravity CLI, and [Claude Code vs Antigravity](/blog/claude-code-vs-antigravity/) covers that tool. +The [quota page](https://geminicli.com/docs/resources/quota-and-pricing/) and the README still advertise 1,000 free requests a day for a personal sign in. That is history. The page also lists an unpaid API key tier on Flash, but the announcement only names paid keys, so don't build on it. Individuals on AI Pro or Ultra are pointed to Antigravity CLI, and [Claude Code vs Antigravity](/blog/claude-code-vs-antigravity/) covers that tool. If you are leaving Gemini CLI altogether, our [Gemini CLI alternatives](/blog/gemini-cli-alternatives/) page sorts six options by why you would go. ## Can Gemini CLI use Gemini 4? diff --git a/blog/src/posts/opencode-vs-claude-code.md b/blog/src/posts/opencode-vs-claude-code.md index 83ff3fea19..93df43885b 100644 --- a/blog/src/posts/opencode-vs-claude-code.md +++ b/blog/src/posts/opencode-vs-claude-code.md @@ -3,6 +3,7 @@ title: "OpenCode vs Claude Code: Licence, Price and Models Compared" seoTitle: "OpenCode vs Claude Code (Oct 2026): Price, Models, Licence" description: "OpenCode vs Claude Code, checked 6 Oct 2026: licence, price to start, models, where each runs, and whether a Claude subscription works in OpenCode." date: 2026-10-06 +updated: 2026-11-04 category: comparisons categoryLabel: Comparisons type: Non-technical @@ -13,7 +14,7 @@ faq: - q: "Is OpenCode better than Claude Code?" a: "Neither wins outright. We think OpenCode is the better fit if you want open source code and a free choice of model, and Claude Code is the better fit if you already pay for a Claude plan. On 6 Oct 2026 OpenCode was MIT licensed and Claude Code's licence file said all rights reserved." - q: "Can I use my Claude subscription in OpenCode?" - a: "Not in a way either vendor supports. On 6 Oct 2026 OpenCode's providers page said plugins for Claude Pro and Max exist, that Anthropic explicitly prohibits this, and that OpenCode stopped bundling them as of 1.3.0. Anthropic's legal page says third party developers may not route requests through Free, Pro or Max plan credentials." + a: "OpenCode's providers page, read 4 Nov 2026, says of plugins for Claude Pro/Max models \"Anthropic explicitly prohibits this\", and OpenCode dropped them in 1.3.0. Its 2.2.3 notes (30 Oct 2026) add a Claude Agent provider using your Claude Code login. An Anthropic API key works, billed per token." - q: "Can I use Claude models in OpenCode?" a: "Yes, with a key you pay for by the token. The Anthropic section of OpenCode's providers page shows a Manually enter API Key option. OpenCode Zen also lists Claude models: Claude Opus 5.5 was $4.00 per 1M input tokens and $20.00 per 1M output tokens on 6 Oct 2026." - q: "Does OpenCode have free models?" @@ -38,7 +39,7 @@ OpenCode is open and model neutral, and Claude Code is closed and built around C | Price to start (6 Oct 2026) | Free models; Go, $10 a month | Claude Pro, $20 a month | | Models | 75+ providers, local models included | Claude models | | Runs in | Terminal, desktop app, IDE extension | Terminal, IDE, desktop app, web | -| Claude Pro or Max login | No | Yes | +| Claude Pro or Max login | Through the Claude Agent provider (2.2.3, 30 Oct 2026), which uses your Claude Code login | Yes | | Anthropic API key | Yes | Yes | Sources: [opencode.ai](https://opencode.ai/), the [OpenCode repo](https://github.com/sst/opencode) (which GitHub now serves as anomalyco/opencode), [Anthropic's pricing page](https://claude.com/pricing), the [Claude Code docs](https://code.claude.com/docs/en/overview) and the [Claude Code repo](https://github.com/anthropics/claude-code). @@ -78,13 +79,15 @@ OpenCode has model choice and open code. Claude Code has cloud features tied to ## Can you use a Claude subscription in OpenCode? -No, not in a way either vendor supports. OpenCode's [providers page](https://opencode.ai/docs/providers/) said on 6 Oct 2026: "There are plugins that allow you to use your Claude Pro/Max models with OpenCode. Anthropic explicitly prohibits this." It adds that older versions bundled those plugins, "but that is no longer the case as of 1.3.0". +OpenCode's [providers page](https://opencode.ai/docs/providers/), read 4 Nov 2026, says: "There are plugins that allow you to use your Claude Pro/Max models with OpenCode. Anthropic explicitly prohibits this." It adds that older versions bundled those plugins, "but that is no longer the case as of 1.3.0". -Anthropic's [legal page](https://code.claude.com/docs/en/legal-and-compliance) says it "does not permit third-party developers to offer Claude.ai login into their own applications, or to route requests through Free, Pro, or Max plan credentials on behalf of their users". +Anthropic's [legal page](https://code.claude.com/docs/en/legal-and-compliance), read 4 Nov 2026, says it "does not permit third-party developers to offer Claude.ai login into their own applications, or to route requests through Free, Pro, Max, or Ultra plan credentials on behalf of their users", though this does not prevent "an end user from signing in to the unmodified Claude Code binary with their own Claude subscription". + +OpenCode's [v2.2.3 notes](https://github.com/anomalyco/opencode/releases/tag/v2.2.3) of 30 Oct 2026 add a built in Claude Agent provider: install Claude Code, run `claude auth login`, and pick Claude Agent in `/connect` "to use your Claude Code login and models". Anthropic's page does not name OpenCode. An Anthropic API key does work. The auth menu on the providers page shows "Manually enter API Key". Zen sells Claude too: on 6 Oct 2026 Claude Opus 5.5 was $4.00 per 1M input tokens and $20.00 per 1M output tokens. The key still cuts fine. The lock was changed. -
Animation. Three labelled keys take turns at a padlock marked OpenCode. The key labelled API key turns and the padlock opens. The key labelled ChatGPT Plus turns and it opens again. The key labelled Claude Pro and Max does not fit and swings back to its place.
What OpenCode's providers page said about logins on 6 Oct 2026.
+
Animation. Three labelled keys take turns at a padlock marked OpenCode. The key labelled API key turns and the padlock opens. The key labelled ChatGPT Plus turns and it opens again. The key labelled Claude Pro and Max does not fit and swings back to its place.
What OpenCode's providers page said about logins on 6 Oct 2026, before the 2.2.3 Claude Agent provider.
## OpenCode vs Codex diff --git a/blog/src/posts/roo-code-alternatives.md b/blog/src/posts/roo-code-alternatives.md new file mode 100644 index 0000000000..822e176724 --- /dev/null +++ b/blog/src/posts/roo-code-alternatives.md @@ -0,0 +1,98 @@ +--- +title: "Roo Code Alternatives: 6 Picks Now That Roo Code Is Shut Down (Nov 2026)" +seoTitle: "6 Roo Code Alternatives (Nov 2026): Licences and Prices" +description: "Roo Code was shut down on 15 May 2026. Six alternatives sorted by why you are leaving, with licences and prices checked 4 Nov 2026." +date: 2026-11-04 +category: comparisons +categoryLabel: Comparisons +type: Technical +primaryKeyword: "roo code alternatives" +secondaryKeywords: ["roo code alternative", "roo code replacement", "zoocode vs kilo code", "roo code vs cline", "roo code shut down what to use", "open source roo code alternative"] +tags: ["Comparisons", "Open Source", "IDE Agents", "Editors"] +faq: + - q: "What replaced Roo Code?" + a: "Roo Code's README, read 4 Nov 2026, points to two projects: ZooCode, \"a fork started by the Roo Code community\", and Cline, \"from where Roo Code originated\". The Roo team itself moved to Roomote, a cloud coding agent. Kilo Code, which began as a Roo fork, publishes its own migration guide." + - q: "Is ZooCode the same as Roo Code?" + a: "It is a continuation of the same extension, with the same five modes. Its README, read 4 Nov 2026, says ZooCode \"continues development of this project after the Roo team wound down active Roo Code work\". It is Apache 2.0 licensed, and v3.94.0 shipped on 31 Oct 2026." + - q: "Can I move my Roo Code settings to another tool?" + a: "Yes, with some limits. ZooCode's migration guide, read 4 Nov 2026, has you export the settings file from Roo Code and import it, and warns the file can include API keys. Kilo's guide says API key storage, Orchestrator mode and codebase indexing break in the move." + - q: "Is Roomote open source?" + a: "Not in the Apache 2.0 sense. Roomote's LICENSE file, read 4 Nov 2026, is the Fair Core License, FCL-1.0-ALv2, and its homepage calls it source available. Self hosting is free for up to 10 users, per that page on the same day." + - q: "What is the best free Roo Code alternative?" + a: "Our view: ZooCode if you want the same extension. Munder Difflin is free and open source too, if you want a team of agents on your computer. Cline or Kilo Code if you want an older, larger project. ZooCode's and Cline's pricing pages, read 4 Nov 2026, list a free option, and you pay your model provider." +--- + +Our view: ZooCode is the first Roo Code alternative to try, because it continues the same extension with the same modes and imports your Roo Code settings. Roo Code itself was shut down on 15 May 2026 and its repository is archived. Checked 4 Nov 2026. + +[Munder Difflin](https://harnessmd.com/download) is free and open source: a desktop app that runs a team of AI agents for all kinds of knowledge work, coding included, on your own computer. More pages like this are in our [Comparisons hub](/blog/topics/comparisons/). + +## What was Roo Code? + +Roo Code was a free, open source AI coding agent for VS Code that worked with almost any model. Its [docs](https://docs.roocode.com), read 4 Nov 2026, say it is "compatible with dozens of providers and hundreds of models". The [providers page](https://roocodeinc.github.io/Roo-Code/providers/) lists Anthropic, OpenAI, Google Gemini, DeepSeek, OpenRouter and Ollama, among others. + +The [README](https://github.com/RooCodeInc/Roo-Code) lists five modes: Code, Architect, Ask, Debug and Custom Modes. The [FAQ](https://roocodeinc.github.io/Roo-Code/faq) says "The Roo Code extension is free and open-source", and you paid your model provider per token. The licence is Apache 2.0, per the GitHub API. + +## Is Roo Code shut down? + +Yes. Its README, read 4 Nov 2026, says: "The Roo Code Extension was shut down on May 15th." The GitHub API shows the repository archived, with its last push and final release, v3.54.0, on 15 May 2026. + +No Roo Code Cloud pricing page is left to check: roocode.com redirects to [roomote.dev](https://roomote.dev/). The README now reads like a forwarding address, naming ZooCode and Cline. Our view: move before a model or VS Code update breaks it. The full story is in [what is Roo Code](/blog/what-is-roo-code/). + +
Animation. On a white background, a candle on a saucer stands beside a card that reads Roo Code. The flame shrinks and goes out, a thin line of smoke rises, and the card turns over to read Shut down, 15 May 2026, Final release v3.54.0. Tapping the card turns it back to the front.
Shut down on 15 May 2026, final release v3.54.0, per Roo Code's README and the GitHub API, read 4 Nov 2026.
+ +## What are the best Roo Code alternatives? + +Six, sorted by why you would leave. Every page below was read on 4 Nov 2026. + +| Alternative | What it is | Where it runs | Price or licence, 4 Nov 2026 | +| --- | --- | --- | --- | +| ZooCode | Community fork of Roo Code | VS Code, JetBrains beta | Apache 2.0. Free with your keys; Team $25 a seat a month (4 Nov). | +| Munder Difflin | A team of AI agents for knowledge work, coding included | Your computer | Free and open source. | +| Cline | The project Roo Code came from | VS Code, JetBrains, CLI, desktop | Apache 2.0. Free for individuals; you pay for inference. | +| Kilo Code | Open source agent that began as a Roo fork | VS Code, JetBrains, CLI | MIT. Provider rates with zero markup. | +| Roomote | The Roo team's cloud coding agent | Your own infrastructure, or hosted | FCL 1.0. Self hosted free up to 10 users. | +| Claude Code | Anthropic's coding agent | Terminal, IDE, desktop, browser | All rights reserved. Pro from $17 a month (4 Nov). | + +
Animation. On a white background, a row of six coat hooks on a wooden rail. A coat drops onto each hook in turn, each with a paper tag: ZooCode, Munder Difflin, Cline, Kilo Code, Roomote, Claude Code. One empty hook at the end has a faded tag that reads Roo Code, Archived. Tapping a coat flips its tag to show where it runs.
Six alternatives and where each runs, from each vendor's pages, read 4 Nov 2026.
+ +## 1. ZooCode: if you want Roo Code to keep going + +ZooCode is the closest swap: the same extension, new maintainers. Its [README](https://github.com/Zoo-Code-Org/Zoo-Code), read 4 Nov 2026, says it "continues development of this project after the Roo team wound down active Roo Code work to focus on Roomote". It keeps the five modes, is Apache 2.0, and shipped v3.94.0 on 31 Oct 2026. + +The [migration guide](https://docs.zoocode.dev/roo-to-zoo-migration) is two steps: export your Roo Code settings, then import the file. It warns: "The exported file can include sensitive settings, including API keys." The [pricing page](https://www.zoocode.dev/pricing) lists Free at $0 with your own keys, and Team at $25 per seat a month, on 4 Nov 2026. + +## 2. Munder Difflin: if you want a team, not one agent in one editor + +[Munder Difflin](https://harnessmd.com/download) is what we make, a team of AI agents on your own computer. It runs on engines such as Claude Code, Codex and Gemini CLI, and connects tools through MCP servers, skills and plugins. Agents run while your computer runs. A Cloud Office and a mobile app are coming soon. Setup is in [how to install and use Munder Difflin](/blog/how-to-install-and-use-munder-difflin/). + +## 3. Cline: if you want the project Roo Code came from + +Cline is where Roo Code started: Roo Code's README calls it the project "from where Roo Code originated". Cline's [README](https://github.com/cline/cline), read 4 Nov 2026, offers a VS Code extension, a JetBrains plugin, a CLI and a desktop app for macOS and Windows. It is Apache 2.0 and shipped v4.1.26 on 26 Oct 2026. + +Its [pricing page](https://cline.bot/pricing), read 4 Nov 2026, says "Cline is free for individual developers" and you pay only for inference. That page lists the JetBrains extension under Enterprise. More options are in [Cline alternatives](/blog/cline-alternatives/). + +## 4. Kilo Code: if you want more editors and a CLI + +Kilo Code began as a Roo fork; [Kilo's migration guide](https://kilo.ai/articles/roo-to-kilo-migration-guide), read 4 Nov 2026, says "We started Kilo as a Roo fork in 2025." Its [README](https://github.com/Kilo-Org/kilocode) covers VS Code, JetBrains and a CLI, offers "500+ models", and says you "pay the model provider's rate with zero markup". It is MIT licensed; the latest stable release was v7.8.18 on 26 Oct 2026. + +The guide is frank: "What breaks: API key storage, Orchestrator mode, codebase indexing." In return it lists "parallel subagents, an Agent Manager with git worktrees". We set it against Cline in [Kilo Code vs Cline](/blog/kilo-code-vs-cline/). + +## 5. Roomote: if you want to follow the Roo team to the cloud + +Roomote is where the Roo team went; Kilo's guide gives the stated reason as a "Pivot to Roomote, Roo's cloud agent". The [Roomote homepage](https://roomote.dev/), read 4 Nov 2026, calls it "The cloud coding agent you actually own", self hostable or hosted by them. + +Self hosted pricing on that page, 4 Nov 2026: free for up to 10 users, $249 a month for 11 to 50 users and $499 a month for 51 to 100. "You pay your infrastructure and inference providers directly." The [LICENSE file](https://github.com/RooCodeInc/Roomote/blob/develop/LICENSE) is the Fair Core License, FCL-1.0-ALv2, and v1.20.0 shipped on 4 Nov 2026. We think that is a different deal from Apache 2.0, so read it first. + +
Animation. On a white background, chairs slide in around a long table while a small card on the table counts them. At 10 chairs the card reads Up to 10 users, Free. More chairs slide in and the card changes to 11 to 50 users, $249 a month, then 51 to 100 users, $499 a month. Tapping the table clears the extra chairs and starts the count again.
Roomote self hosted pricing: free up to 10 users, then $249 and $499 a month, from roomote.dev, read 4 Nov 2026.
+ +## 6. Claude Code: if you want a first party agent + +Claude Code is Anthropic's own agent, and its [overview](https://code.claude.com/docs/en/overview), read 4 Nov 2026, says it is "Available in your terminal, IDE, desktop app, and browser." The [LICENSE file](https://github.com/anthropics/claude-code/blob/main/LICENSE.md) says "All rights reserved". It shipped v2.1.318 on 4 Nov 2026. + +[Anthropic's pricing page](https://claude.com/pricing), read 4 Nov 2026, leaves Claude Code out of Free and includes it in Pro at $17 a month billed annually or $20 monthly. See [what Claude Code costs](/blog/how-much-does-claude-code-cost/). + +## Which one should you pick? + +Our view: ZooCode if you want your modes and settings back today. Munder Difflin if you want several agents on research, writing, ops and coding at once. Cline or Kilo Code for a larger project with a CLI and JetBrains. Roomote if your team wants cloud agents and accepts its licence. Claude Code if you already pay Anthropic. + + diff --git a/blog/src/posts/what-is-roo-code.md b/blog/src/posts/what-is-roo-code.md index 730cc36da2..fa3f3ea790 100644 --- a/blog/src/posts/what-is-roo-code.md +++ b/blog/src/posts/what-is-roo-code.md @@ -76,7 +76,7 @@ Pick a successor, then carry your settings across by hand where the guides say s ## What to use instead -Roo Code's README points to ZooCode, covered above, and Cline. Here are five ways to go, Cline included, each with its own source: +Roo Code's README points to ZooCode, covered above, and Cline. For six picks sorted by why you are leaving, with prices checked 4 Nov 2026, see [Roo Code alternatives](/blog/roo-code-alternatives/). Here are five ways to go, Cline included, each with its own source: 1. **Cline.** Its [README](https://github.com/cline/cline) says "The open source coding agent in your IDE, terminal, & desktop." Apache 2.0; latest extension release v4.1.25 (16 Oct 2026). See [Cline vs Munder Difflin](/blog/cline-vs-munder-difflin/). 2. **Munder Difflin.** [Munder Difflin](https://harnessmd.com/download) is what we make, and it is not an editor extension. It is free and open source: a desktop app that runs a team of AI agents for all kinds of knowledge work, coding included, on your own computer. Start with [how to install and use Munder Difflin](/blog/how-to-install-and-use-munder-difflin/). diff --git a/docs/blog/agent-desktop-alternatives/index.html b/docs/blog/agent-desktop-alternatives/index.html index d69e14811d..9295b1d209 100644 --- a/docs/blog/agent-desktop-alternatives/index.html +++ b/docs/blog/agent-desktop-alternatives/index.html @@ -326,56 +326,56 @@

FAQ