| that doesn't contain | ]*>(.*?) | ' + tds = re.findall(td_pattern, row, flags=re.DOTALL) + for td_content in tds: + stripped = re.sub(r'<[^>]+>', '', td_content).strip() + if stripped and '
tags
+ # * -> {'*'}
+ table = re.sub(
+ r"([^<]*\*[^<]*)",
+ lambda cm: "" + cm.group(1).replace("*", "{'*'}") + "",
+ table,
+ )
+ # Escape bare * between tags that could be interpreted as emphasis
+ # Only in table cells: > * < pattern
+ table = re.sub(r">\s*\*\s*<", "> {'*'} <", table)
+ return table
+
+ return re.sub(r".*?
", _sanitize_table, content, flags=re.DOTALL)
+
+
+def fix_html_void_elements(content: str) -> str:
+ """Convert void HTML elements to self-closing for MDX compatibility."""
+ #
->
,
->
,
->
+ for tag in ["br", "hr", "img"]:
+ content = re.sub(
+ rf"<({tag})(\s[^>]*)?>(?!\s*/)",
+ lambda m: f"<{m.group(1)}{m.group(2) or ''} />",
+ content,
+ )
+ return content
+
+
+def convert_autolinks(content: str) -> str:
+ """Convert markdown autolinks `` to inline link form.
+
+ MDX treats `<...>` as JSX, so the bare-URL autolink syntax that's valid
+ GitHub-Flavored Markdown breaks the MDX parser. Rewrite each occurrence as
+ `[https://example.com](https://example.com)` so the rendered link is
+ identical.
+ """
+ return re.sub(
+ r"<((?:https?|mailto):[^\s<>]+)>",
+ lambda m: f"[{m.group(1)}]({m.group(1)})",
+ content,
+ )
+
+
+# ---------------------------------------------------------------------------
+# Asset prefix calculation
+# ---------------------------------------------------------------------------
+
+
+def compute_rel_asset_prefix(dst_rel_path: Path, space: str) -> str:
+ """Compute relative path from a doc file to src/assets//.
+
+ dst_rel_path is relative to the space docs dir (e.g. 'capabilities/skills.mdx').
+ The docs dir is at src/content/docs// and assets at src/assets//.
+ """
+ depth = len(dst_rel_path.parent.parts)
+ # From content/docs// up to src/: 3 + depth
+ ups = 3 + depth
+ return "../" * ups + f"assets/{space}/"
+
+
+# ---------------------------------------------------------------------------
+# Main conversion
+# ---------------------------------------------------------------------------
+
+
+def convert_file(
+ src_path: Path, dst_path: Path, space: str, gitbook_root: Path
+) -> str:
+ """Convert a single GitBook markdown file to Starlight MDX."""
+ content = src_path.read_text(encoding="utf-8")
+
+ # Determine relative directory within the space
+ try:
+ file_rel = src_path.relative_to(gitbook_root)
+ except ValueError:
+ file_rel = Path(src_path.name)
+ file_rel_dir = str(file_rel.parent) if str(file_rel.parent) != "." else ""
+
+ # Compute asset prefix from destination path
+ dst_str = str(dst_path)
+ space_marker = f"docs/{space}/"
+ if space_marker in dst_str:
+ after_space = dst_str.split(space_marker, 1)[1]
+ dst_rel_in_space = Path(after_space)
+ else:
+ dst_rel_in_space = Path(dst_path.name)
+
+ rel_asset_prefix = compute_rel_asset_prefix(dst_rel_in_space, space)
+
+ # 1. Parse and rebuild frontmatter
+ fm, body = parse_frontmatter(content)
+ for key in list(fm.keys()):
+ if key not in KEEP_FRONTMATTER_KEYS:
+ del fm[key]
+ new_fm = build_frontmatter(fm, body)
+ body = strip_first_h1(body)
+
+ # 2. Apply transforms (order matters)
+ body = convert_hints(body)
+ body = convert_embeds(body)
+ body = convert_tabs(body)
+ body = convert_code_blocks(body)
+ body = convert_steppers(body)
+ body = convert_gitbook_card_tables(body, rel_asset_prefix, src_label=str(file_rel))
+ body = strip_gitbook_content_refs(body)
+ body = convert_figures(body, rel_asset_prefix)
+ body = convert_inline_images(body, rel_asset_prefix)
+ body = convert_links(body, file_rel_dir, space)
+ body = convert_html_comments(body)
+ body = strip_custom_anchors(body)
+ body = convert_horizontal_rules(body)
+ body = sanitize_html_tables(body)
+ body = fix_html_void_elements(body)
+ body = convert_autolinks(body)
+
+ # 3. Reassemble
+ result = new_fm + "\n" + body
+
+ # 4. Inject MDX imports
+ result = inject_imports(result)
+
+ # 5. Clean up excessive blank lines
+ result = re.sub(r"\n{3,}", "\n\n", result)
+
+ if not result.endswith("\n"):
+ result += "\n"
+
+ return result
+
+
+def sanitize_asset_filename(name: str) -> str:
+ """Replace spaces and parens in asset filenames for web compatibility."""
+ return name.replace(" ", "-").replace("(", "").replace(")", "")
+
+
+def collect_referenced_assets(gitbook_root: Path) -> dict[str, str]:
+ """Find all asset filenames referenced in GitBook source files.
+
+ Returns a dict mapping original_filename -> sanitized_filename.
+ """
+ assets: dict[str, str] = {}
+ for md_file in gitbook_root.rglob("*.md"):
+ if md_file.name == "SUMMARY.md":
+ continue
+ content = md_file.read_text(encoding="utf-8")
+ # Match .gitbook/assets/FILENAME where filename may contain spaces
+ for m in re.finditer(r'\.gitbook/assets/([^"]+)', content):
+ original = m.group(1).strip()
+ if original:
+ assets[original] = sanitize_asset_filename(original)
+ return assets
+
+
+def main():
+ if len(sys.argv) < 4:
+ print(__doc__)
+ sys.exit(1)
+
+ gitbook_root = Path(sys.argv[1]).resolve()
+ starlight_docs = Path(sys.argv[2]).resolve()
+ space = sys.argv[3]
+ skip_existing = "--skip-existing" in sys.argv
+
+ if not gitbook_root.is_dir():
+ print(f"Error: {gitbook_root} is not a directory")
+ sys.exit(1)
+
+ # Collect all .md files (skip SUMMARY.md)
+ md_files = sorted(
+ p for p in gitbook_root.rglob("*.md") if p.name != "SUMMARY.md"
+ )
+
+ # Determine which files to skip (already migrated)
+ skip_set: set[str] = set()
+ if skip_existing:
+ for existing in starlight_docs.rglob("*.mdx"):
+ try:
+ rel = existing.relative_to(starlight_docs)
+ except ValueError:
+ continue
+ src_name = (
+ "README.md" if rel.name == "index.mdx" else rel.stem + ".md"
+ )
+ src_rel = rel.parent / src_name
+ skip_set.add(str(src_rel))
+
+ converted = 0
+ skipped = 0
+
+ for src_path in md_files:
+ rel = src_path.relative_to(gitbook_root)
+ if str(rel) in skip_set:
+ skipped += 1
+ print(f" ⊘ {rel} (already exists)")
+ continue
+
+ # Compute destination path
+ dst_rel = rel
+ if dst_rel.name == "README.md":
+ dst_rel = dst_rel.parent / "index.mdx"
+ else:
+ dst_rel = dst_rel.with_suffix(".mdx")
+
+ dst_path = starlight_docs / dst_rel
+ dst_path.parent.mkdir(parents=True, exist_ok=True)
+
+ result = convert_file(src_path, dst_path, space, gitbook_root)
+ dst_path.write_text(result, encoding="utf-8")
+ converted += 1
+ print(f" ✓ {rel} → {dst_rel}")
+
+ print(f"\nConverted: {converted}, Skipped: {skipped}")
+
+ # Copy referenced assets
+ gitbook_assets = gitbook_root / ".gitbook" / "assets"
+ if gitbook_assets.is_dir():
+ referenced = collect_referenced_assets(gitbook_root)
+ src_dir = starlight_docs.parents[2] # up to src/
+ asset_dst = src_dir / "assets" / space
+ asset_dst.mkdir(parents=True, exist_ok=True)
+
+ copied = 0
+ for original_name, safe_name in sorted(referenced.items()):
+ asset_src = gitbook_assets / original_name
+ if asset_src.exists():
+ dest = asset_dst / safe_name
+ if not dest.exists():
+ shutil.copy2(asset_src, dest)
+ copied += 1
+ print(f"Assets copied: {copied} (of {len(referenced)} referenced)")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/fix-external-urls.py b/scripts/fix-external-urls.py
new file mode 100755
index 0000000..fc4b88e
--- /dev/null
+++ b/scripts/fix-external-urls.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python3
+"""Convert hardcoded https://docs.warp.dev/ URLs to internal links."""
+import os, re
+
+docs_dir = 'src/content/docs'
+
+# Build valid internal paths
+valid_paths = set()
+for root, dirs, files in os.walk(docs_dir):
+ for f in files:
+ if not f.endswith('.mdx'): continue
+ rel = os.path.relpath(os.path.join(root, f), docs_dir)
+ slug = '/' + rel.replace('.mdx', '/').replace('/index/', '/')
+ if slug == '/index/': slug = '/'
+ valid_paths.add(slug.rstrip('/'))
+
+# Old gitbook path -> new docs path mapping
+PATH_REMAPPING = {
+ '/agent-platform/warp-agents/agent-profiles-permissions': '/agent-platform/capabilities/agent-profiles-permissions',
+ '/agent-platform/warp-agents/skills': '/agent-platform/capabilities/skills',
+ '/agent-platform/warp-agents/planning': '/agent-platform/capabilities/planning',
+ '/agent-platform/warp-agents/task-lists': '/agent-platform/capabilities/task-lists',
+ '/agent-platform/warp-agents/model-choice': '/agent-platform/capabilities/model-choice',
+ '/agent-platform/warp-agents/rules': '/agent-platform/capabilities/rules',
+ '/agent-platform/warp-agents/full-terminal-use': '/agent-platform/capabilities/full-terminal-use',
+ '/agent-platform/warp-agents/computer-use': '/agent-platform/capabilities/computer-use',
+ '/agent-platform/warp-agents/codebase-context': '/agent-platform/capabilities/codebase-context',
+ '/agent-platform/warp-agents/web-search': '/agent-platform/capabilities/web-search',
+ '/agent-platform/warp-agents/mcp': '/agent-platform/capabilities/mcp',
+ '/agent-platform/warp-agents/slash-commands': '/agent-platform/capabilities/slash-commands',
+ '/agent-platform/warp-agents/agent-notifications': '/agent-platform/capabilities/agent-notifications',
+ '/agent-platform/warp-agents/active-ai': '/agent-platform/local-agents/active-ai',
+ '/agent-platform/warp-agents/code-diffs': '/agent-platform/local-agents/code-diffs',
+ '/agent-platform/warp-agents/session-sharing': '/agent-platform/local-agents/session-sharing',
+ '/agent-platform/warp-agents/cloud-conversations': '/agent-platform/local-agents/cloud-conversations',
+ '/agent-platform/warp-agents/interactive-code-review': '/agent-platform/local-agents/interactive-code-review',
+ '/agent-platform/warp-agents': '/agent-platform/local-agents/overview',
+ '/agent-platform/warp-agents/interacting-with-agents': '/agent-platform/local-agents/interacting-with-agents',
+ '/agent-platform/warp-agents/interacting-with-agents/terminal-and-agent-modes': '/agent-platform/local-agents/interacting-with-agents/terminal-and-agent-modes',
+ '/agent-platform/local-agents/interacting-with-agents/agent-modality': '/agent-platform/local-agents/interacting-with-agents/terminal-and-agent-modes',
+ '/agent-platform/warp-agents/interacting-with-agents/conversation-forking': '/agent-platform/local-agents/interacting-with-agents/conversation-forking',
+ '/agent-platform/warp-agents/interacting-with-agents/voice': '/agent-platform/local-agents/interacting-with-agents/voice',
+ '/agent-platform/warp-agents/agent-context': '/agent-platform/local-agents/agent-context',
+ '/agent-platform/warp-agents/agent-context/blocks-as-context': '/agent-platform/local-agents/agent-context/blocks-as-context',
+ '/agent-platform/warp-agents/agent-context/images-as-context': '/agent-platform/local-agents/agent-context/images-as-context',
+ '/agent-platform/warp-agents/agent-context/urls-as-context': '/agent-platform/local-agents/agent-context/urls-as-context',
+ '/agent-platform/warp-agents/agent-context/selection-as-context': '/agent-platform/local-agents/agent-context/selection-as-context',
+ '/agent-platform/warp-agents/agent-context/using-to-add-context': '/agent-platform/local-agents/agent-context/using-to-add-context',
+ '/agent-platform/third-party-agents': '/agent-platform/cli-agents/overview',
+ '/agent-platform/cloud-agents/cloud-agents-overview': '/agent-platform/cloud-agents/overview',
+ '/warp/code/code-review': '/code/code-review',
+ '/warp/code/git-worktrees': '/code/git-worktrees',
+ '/warp/knowledge-and-collaboration/warp-drive/agent-mode-context': '/knowledge-and-collaboration/warp-drive/agent-mode-context',
+ '/warp/terminal/windows/vertical-tabs': '/terminal/windows/vertical-tabs',
+ '/guides': '/university',
+ '/guides/integrations/how-to-set-up-claude-code': '/university/integrations/how-to-set-up-claude-code',
+ '/guides/integrations/how-to-set-up-codex-cli': '/university/integrations/how-to-set-up-codex-cli',
+ '/guides/integrations/how-to-set-up-gemini-cli': '/university/integrations/how-to-set-up-gemini-cli',
+ '/guides/integrations/how-to-set-up-opencode': '/university/integrations/how-to-set-up-opencode',
+ '/knowledge-and-collaboration/rules': '/agent-platform/capabilities/rules',
+ '/knowledge-and-collaboration/mcp': '/agent-platform/capabilities/mcp',
+ '/code/codebase-context': '/agent-platform/capabilities/codebase-context',
+ '/getting-started/installation-and-setup': '/getting-started/quickstart/installation-and-setup',
+ '/getting-started/keyboard-shortcuts.md': '/getting-started/keyboard-shortcuts',
+ '/support-and-community/plans-and-billing/plans-and-pricing': '/support-and-community/plans-and-billing/plans-pricing-refunds',
+ '/reference/api-and-sdk/agent': '/reference/api-and-sdk',
+ '/reference/cli/cli': '/reference/cli',
+ '/reference/cli/mcp-for-cloud-agents': '/reference/cli/mcp-servers',
+ # Additional remappings discovered during the 2026-04-29 sync.
+ '/warp/code/code-editor': '/code/code-editor',
+ '/warp/code/code-editor/file-tree': '/code/code-editor/file-tree',
+ '/warp/code/code-editor/code-editor-vim-keybindings': '/code/code-editor/code-editor-vim-keybindings',
+ '/warp/code/code-editor/find-and-replace': '/code/code-editor/find-and-replace',
+ '/warp/code/code-editor/language-server-protocol': '/code/code-editor/language-server-protocol',
+ '/warp/code/overview': '/code/overview',
+ '/warp/code/ssh-feature-support': '/code/ssh-feature-support',
+ '/warp/getting-started': '/getting-started/quickstart/installation-and-setup',
+ '/warp/getting-started/installation-and-setup': '/getting-started/quickstart/installation-and-setup',
+ '/warp/getting-started/coding-in-warp': '/getting-started/quickstart/coding-in-warp',
+ '/warp/getting-started/customizing-warp': '/getting-started/quickstart/customizing-warp',
+ '/warp/getting-started/migrate-to-warp': '/getting-started/migrate-to-warp',
+ '/warp/getting-started/keyboard-shortcuts': '/getting-started/keyboard-shortcuts',
+ '/warp/getting-started/supported-shells': '/getting-started/supported-shells',
+ '/warp/getting-started/what-is-warp': '/',
+ '/warp/getting-started/quickstart': '/quickstart',
+ '/warp/terminal/windows/tab-configs': '/terminal/windows/tab-configs',
+ '/warp/terminal/windows/configurable-toolbar': '/terminal/windows/configurable-toolbar',
+ '/warp/terminal/settings': '/terminal/settings',
+ '/warp/terminal/settings/all-settings': '/terminal/settings/all-settings',
+ '/warp/knowledge-and-collaboration/admin-panel': '/knowledge-and-collaboration/admin-panel',
+ '/warp/knowledge-and-collaboration/warp-drive': '/knowledge-and-collaboration/warp-drive',
+ '/agent-platform/cli-agents': '/agent-platform/cli-agents/overview',
+ '/guides/mcp-servers/github-mcp-summarizing-open-prs-and-creating-gh-issues': '/university/mcp-servers/github-mcp-summarizing-open-prs-and-creating-gh-issues',
+ '/guides/mcp-servers/sentry-mcp-fix-sentry-error-in-empower-website': '/university/mcp-servers/sentry-mcp-fix-sentry-error-in-empower-website',
+ '/guides/mcp-servers/linear-mcp-retrieve-issue-data': '/university/mcp-servers/linear-mcp-retrieve-issue-data',
+ '/guides/mcp-servers/puppeteer-mcp-scraping-amazon-web-reviews': '/university/mcp-servers/puppeteer-mcp-scraping-amazon-web-reviews',
+ '/guides/mcp-servers/context7-mcp-update-astro-project-with-best-practices': '/university/mcp-servers/context7-mcp-update-astro-project-with-best-practices',
+}
+
+stats = [0, 0, 0] # total, converted, remapped
+
+def replace_url(m):
+ full_url = m.group(0)
+ url_path = m.group(1)
+ fragment = m.group(2) or ''
+ stats[0] += 1
+
+ clean_path = url_path.rstrip('/')
+
+ if clean_path in valid_paths:
+ stats[1] += 1
+ return clean_path + '/' + fragment
+
+ new_path = PATH_REMAPPING.get(clean_path)
+ if new_path and new_path.rstrip('/') in valid_paths:
+ stats[2] += 1
+ return new_path + '/' + fragment
+
+ return full_url
+
+for root, dirs, files in os.walk(docs_dir):
+ for f in files:
+ if not f.endswith('.mdx'): continue
+ if 'changelog' in root: continue
+
+ path = os.path.join(root, f)
+ content = open(path).read()
+
+ new_content = re.sub(
+ r'https://docs\.warp\.dev(/[^)\s"#]*)(#[^)\s"]*)?',
+ replace_url,
+ content
+ )
+
+ if new_content != content:
+ open(path, 'w').write(new_content)
+
+print(f"Total external URLs found: {stats[0]}")
+print(f"Converted (direct match): {stats[1]}")
+print(f"Converted (remapped): {stats[2]}")
+print(f"Remaining unconverted: {stats[0] - stats[1] - stats[2]}")
diff --git a/scripts/generate-og-api.mjs b/scripts/generate-og-api.mjs
new file mode 100644
index 0000000..984d63f
--- /dev/null
+++ b/scripts/generate-og-api.mjs
@@ -0,0 +1,60 @@
+// One-shot script to render public/assets/og/api.png — the social-card image
+// (1200×630) used by /api's og:image and twitter:image meta tags.
+//
+// Run with: node scripts/generate-og-api.mjs
+//
+// We construct the SVG inline so the Warp wordmark uses the same path data as
+// src/assets/warp-logo-dark.svg (no font dependency on the social scraper).
+// Sharp rasterizes the SVG to a PNG that's safe to embed everywhere
+// (Facebook/Twitter/LinkedIn don't reliably accept SVG og:image).
+import { mkdirSync } from 'node:fs';
+import { dirname, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import sharp from 'sharp';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const outPath = resolve(__dirname, '../public/assets/og/api.png');
+mkdirSync(dirname(outPath), { recursive: true });
+
+// Warp wordmark paths copied verbatim from src/assets/warp-logo-dark.svg.
+// Two `M…Z` mark paths and one wordmark path. viewBox "91 26 582 135".
+const WARP_LOGO = `
+
+
+
+
+
+`;
+
+// 1200×630 OG card. The logo is centered horizontally, with subtitle text
+// rendered as flat using a generic sans-serif that maps reliably across
+// scraper-side renderers. Warp's accent blue underlines the wordmark.
+const svg = `
+
+`;
+
+await sharp(Buffer.from(svg)).png().toFile(outPath);
+console.log(`Wrote ${outPath}`);
diff --git a/scripts/merge_seo_redirects.py b/scripts/merge_seo_redirects.py
new file mode 100644
index 0000000..68bf2d6
--- /dev/null
+++ b/scripts/merge_seo_redirects.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+"""Merge seo/static-assets redirects on top of main's vercel.json.
+
+Run this from the repo root after:
+ git checkout seo/static-assets
+ git merge --no-commit --no-ff origin/main
+ git checkout --theirs vercel.json # take main's vercel.json into worktree
+
+This script then layers seo's net-new redirects on top, with three rules:
+
+- Group A: drop. seo had 8 entries `/university/X[/]? -> /university/`. Those
+ destinations no longer exist on main; main's catch-all
+ `/university/(.*) -> /guides/$1` plus its 13 explicit directory entries
+ supersede them.
+
+- Group B: rewrite the destination. seo has 107 entries `/old-path ->
+ /university/` whose destinations are now gone. Build a map from main's
+ 67 explicit `/university/X -> /guides//X` redirects and use it to
+ rewrite the destination. 4 destinations have no explicit main mapping
+ (newly-ported guides PR #56 added); supply hardcoded fallbacks.
+
+- Group C: keep as-is. seo has 768 entries that don't touch /university/ on
+ either side - the bulk of the SEO work (sitemap-only, GSC, trailing-slash
+ variants, getting-started/features renames, agent-platform self-hosting
+ renames, etc.).
+
+The catch-all `/university/(.*) -> /guides/$1` is moved to the very end of
+the resulting array so it can't shadow more specific rules.
+
+Output: vercel.json is overwritten in place with 2-space indent and a
+trailing newline, matching the existing file's formatting.
+"""
+
+from __future__ import annotations
+
+import json
+import subprocess
+import sys
+from pathlib import Path
+
+REPO_ROOT = Path(__file__).resolve().parents[1]
+VERCEL_JSON = REPO_ROOT / "vercel.json"
+
+# 4 destinations on seo that do not have an explicit /university/X -> /guides/...
+# mapping in main, because PR #56 ported these as brand-new guides rather than
+# moving an existing university page. Hardcode the canonical /guides/ path.
+HARDCODED_FALLBACKS: dict[str, str] = {
+ "/university/developer-workflows/power-user/how-to-set-up-self-serve-data-analytics-with-skills": "/guides/configuration/how-to-set-up-self-serve-data-analytics-with-skills/",
+ "/university/developer-workflows/power-user/how-to-set-up-self-serve-data-analytics-with-skills/": "/guides/configuration/how-to-set-up-self-serve-data-analytics-with-skills/",
+ "/university/developer-workflows/warp-for-product-managers": "/guides/agent-workflows/warp-for-product-managers/",
+ "/university/developer-workflows/warp-for-product-managers/": "/guides/agent-workflows/warp-for-product-managers/",
+}
+
+CATCHALL_SOURCE = "/university/(.*)"
+
+
+def load_main_redirects() -> list[dict]:
+ """vercel.json in the worktree is currently main's version (taken via --theirs)."""
+ return json.loads(VERCEL_JSON.read_text())["redirects"]
+
+
+def load_seo_redirects() -> list[dict]:
+ """Pull seo/static-assets's vercel.json from git directly so we don't lose info."""
+ raw = subprocess.check_output(
+ ["git", "show", "seo/static-assets:vercel.json"], cwd=REPO_ROOT, text=True
+ )
+ return json.loads(raw)["redirects"]
+
+
+def build_univ_to_guides_map(main_redirects: list[dict]) -> dict[str, str]:
+ """Return a dict of /university/X (no trailing slash) -> /guides///.
+
+ Only includes entries where main maps an explicit /university/X to a /guides/
+ destination. The catch-all and directory-fallback entries are excluded so that
+ rewriting prefers a specific destination when one exists.
+ """
+ mapping: dict[str, str] = {}
+ for r in main_redirects:
+ s = r["source"].rstrip("/")
+ d = r["destination"]
+ if (
+ s.startswith("/university/")
+ and d.startswith("/guides/")
+ and d != "/guides/" # skip directory-fallback entries
+ and "(.*)" not in s # skip catch-all
+ ):
+ mapping[s] = d
+ return mapping
+
+
+def classify_and_rewrite_seo_only(
+ seo_only: list[dict], univ_map: dict[str, str]
+) -> tuple[list[dict], dict[str, int]]:
+ """Walk seo_only entries; drop/rewrite/keep per the plan."""
+ kept: list[dict] = []
+ counters = {"group_a_dropped": 0, "group_b_rewritten": 0, "group_c_kept": 0,
+ "rewrite_self_dropped": 0, "rewrite_unmapped_kept": 0}
+ for r in seo_only:
+ s, d = r["source"], r["destination"]
+ if s.startswith("/university") and d.startswith("/university"):
+ # Group A - drop, main supersedes.
+ counters["group_a_dropped"] += 1
+ continue
+ if d.startswith("/university"):
+ # Group B - rewrite destination.
+ d_no_slash = d.rstrip("/")
+ new_dest = univ_map.get(d_no_slash) or HARDCODED_FALLBACKS.get(d) or HARDCODED_FALLBACKS.get(d_no_slash)
+ if new_dest is None:
+ # Should not happen given audit, but fail loudly if it does
+ print(f"WARN: no /university->/guides mapping for destination {d!r} (source {s!r}); dropping", file=sys.stderr)
+ counters["rewrite_unmapped_kept"] += 1
+ continue
+ if s.rstrip("/") == new_dest.rstrip("/"):
+ # Source and rewritten destination collapse - drop self-redirect.
+ counters["rewrite_self_dropped"] += 1
+ continue
+ kept.append({**r, "destination": new_dest})
+ counters["group_b_rewritten"] += 1
+ continue
+ # Group C - keep as-is.
+ kept.append(r)
+ counters["group_c_kept"] += 1
+ return kept, counters
+
+
+def merge() -> None:
+ main_redirects = load_main_redirects()
+ seo_redirects = load_seo_redirects()
+
+ main_pairs = {(r["source"], r["destination"]) for r in main_redirects}
+ seo_pairs = {(r["source"], r["destination"]) for r in seo_redirects}
+ seo_only_pairs = seo_pairs - main_pairs
+
+ # Preserve order from seo: walk seo_redirects, keep only the entries whose
+ # (source, destination) pair is in seo_only_pairs.
+ seen: set[tuple[str, str]] = set()
+ seo_only: list[dict] = []
+ for r in seo_redirects:
+ key = (r["source"], r["destination"])
+ if key in seo_only_pairs and key not in seen:
+ seo_only.append(r)
+ seen.add(key)
+
+ print(f"main entries: {len(main_redirects)}")
+ print(f"seo entries: {len(seo_redirects)}")
+ print(f"seo-only entries: {len(seo_only)}")
+
+ univ_map = build_univ_to_guides_map(main_redirects)
+ print(f"main /univ->/guides explicit mappings: {len(univ_map)}")
+
+ seo_kept, counters = classify_and_rewrite_seo_only(seo_only, univ_map)
+ print(f"\nGroup A dropped (seo /university/* -> /university/*): {counters['group_a_dropped']}")
+ print(f"Group B rewritten (seo /old -> /university/X => /guides/Y): {counters['group_b_rewritten']}")
+ print(f"Group C kept (seo non-university entries): {counters['group_c_kept']}")
+ print(f"Rewrites collapsed to self-redirect (dropped): {counters['rewrite_self_dropped']}")
+ if counters["rewrite_unmapped_kept"]:
+ print(f"WARN: rewrites with no mapping (dropped): {counters['rewrite_unmapped_kept']}")
+
+ # Pull main's catch-all aside; we want it to stay at the very end after appending seo entries.
+ main_no_catchall: list[dict] = []
+ catchall: dict | None = None
+ for r in main_redirects:
+ if r["source"] == CATCHALL_SOURCE:
+ catchall = r
+ else:
+ main_no_catchall.append(r)
+ if catchall is None:
+ raise RuntimeError(f"Expected catch-all {CATCHALL_SOURCE!r} in main redirects; not found")
+
+ # Final order: main's explicit redirects (in main's order), then seo additions
+ # (in seo's order, deduped against main and against itself), then the catch-all.
+ final: list[dict] = list(main_no_catchall)
+ final_pairs: set[tuple[str, str]] = {(r["source"], r["destination"]) for r in final}
+ for r in seo_kept:
+ key = (r["source"], r["destination"])
+ if key in final_pairs:
+ continue
+ final.append(r)
+ final_pairs.add(key)
+ # Catch-all goes last so it can't shadow more specific rules.
+ final.append(catchall)
+
+ print(f"\nfinal redirect count: {len(final)}")
+
+ # Preserve formatting: load full top-level dict, replace redirects, write with 2-space indent + trailing newline.
+ full = json.loads(VERCEL_JSON.read_text())
+ full["redirects"] = final
+ VERCEL_JSON.write_text(json.dumps(full, indent=2) + "\n")
+ print(f"wrote {VERCEL_JSON}")
+
+
+if __name__ == "__main__":
+ merge()
diff --git a/scripts/migrate-all.py b/scripts/migrate-all.py
new file mode 100755
index 0000000..cad2a2b
--- /dev/null
+++ b/scripts/migrate-all.py
@@ -0,0 +1,694 @@
+#!/usr/bin/env python3
+"""Master migration: convert ALL GitBook content to Astro Starlight.
+
+Handles the structural remapping between repos:
+ - gitbook/docs/warp/* → docs top-level (terminal/, code/, getting-started/, knowledge-and-collaboration/)
+ - gitbook/docs/agent-platform/warp-agents/* → split into capabilities/ and local-agents/
+ - gitbook/docs/agent-platform/cli-agents/* → agent-platform/cli-agents/
+ - gitbook/guides/* → university/
+ - README.md → index.mdx everywhere
+
+Usage:
+ python scripts/migrate-all.py /path/to/gitbook
+"""
+
+import json
+import re
+import shutil
+import sys
+from pathlib import Path
+
+# Import conversion functions from existing script
+sys.path.insert(0, str(Path(__file__).parent))
+from importlib.machinery import SourceFileLoader
+convert_mod = SourceFileLoader("convert_gitbook", str(Path(__file__).parent / "convert-gitbook.py")).load_module()
+
+# Re-export all conversion functions
+parse_frontmatter = convert_mod.parse_frontmatter
+build_frontmatter = convert_mod.build_frontmatter
+strip_first_h1 = convert_mod.strip_first_h1
+convert_hints = convert_mod.convert_hints
+convert_embeds = convert_mod.convert_embeds
+convert_tabs = convert_mod.convert_tabs
+convert_code_blocks = convert_mod.convert_code_blocks
+convert_steppers = convert_mod.convert_steppers
+strip_gitbook_card_tables = convert_mod.strip_gitbook_card_tables
+strip_gitbook_content_refs = convert_mod.strip_gitbook_content_refs
+convert_figures = convert_mod.convert_figures
+convert_inline_images = convert_mod.convert_inline_images
+convert_html_comments = convert_mod.convert_html_comments
+strip_custom_anchors = convert_mod.strip_custom_anchors
+convert_horizontal_rules = convert_mod.convert_horizontal_rules
+sanitize_html_tables = convert_mod.sanitize_html_tables
+fix_html_void_elements = convert_mod.fix_html_void_elements
+convert_autolinks = convert_mod.convert_autolinks
+inject_imports = convert_mod.inject_imports
+sanitize_asset_filename = convert_mod.sanitize_asset_filename
+KEEP_FRONTMATTER_KEYS = convert_mod.KEEP_FRONTMATTER_KEYS
+
+# ---------------------------------------------------------------------------
+# Path mapping configuration
+# ---------------------------------------------------------------------------
+
+# warp-agents files that go to capabilities/
+CAPABILITIES_FILES = {
+ "agent-profiles-permissions", "skills", "planning", "task-lists",
+ "model-choice", "rules", "full-terminal-use", "computer-use",
+ "codebase-context", "web-search", "mcp", "slash-commands",
+ "agent-notifications",
+}
+
+# warp-agents files that go to local-agents/
+LOCAL_AGENTS_FILES = {
+ "active-ai", "code-diffs", "session-sharing", "cloud-conversations",
+ "interactive-code-review",
+}
+
+# warp-agents/README.md → local-agents/overview.mdx
+# warp-agents/capabilities-overview.md → capabilities/index.mdx
+# warp-agents/interacting-with-agents/* → local-agents/interacting-with-agents/*
+# warp-agents/agent-context/* → local-agents/agent-context/*
+
+# Rename map for specific files
+RENAME_MAP = {
+ "warp-agents/README.md": "local-agents/overview.mdx",
+ "warp-agents/capabilities-overview.md": "capabilities/index.mdx",
+ # The legacy `agent-modality.mdx` file was renamed to
+ # `terminal-and-agent-modes.mdx` in the 2026-04-29 sync to align with
+ # AGENTS.md style guidance ("Agent Modality" was an internal name).
+ "warp-agents/interacting-with-agents/terminal-and-agent-modes.md": "local-agents/interacting-with-agents/terminal-and-agent-modes.mdx",
+}
+
+# Per-space rename map for files that the docs repo intentionally moved away
+# from the gitbook layout. Values are paths relative to the docs root
+# (`src/content/docs/`).
+WARP_RENAME_MAP = {
+ # Quickstart pages live under getting-started/quickstart/ in the docs
+ # repo even though gitbook keeps them flat under getting-started/.
+ "getting-started/coding-in-warp.md": "getting-started/quickstart/coding-in-warp.mdx",
+ "getting-started/customizing-warp.md": "getting-started/quickstart/customizing-warp.mdx",
+ "getting-started/installation-and-setup.md": "getting-started/quickstart/installation-and-setup.mdx",
+ # Top-level landing pages were renamed during the original migration.
+ "getting-started/quickstart.md": "quickstart.mdx",
+ "README.md": "index.mdx",
+}
+
+# Per-space skip set for orphaned/duplicate gitbook files that the docs repo
+# intentionally drops. Values are gitbook-relative paths.
+SKIP_PATHS = {
+ "support-and-community": {
+ # Duplicate of logging-out-and-uninstalling.md kept around in gitbook
+ # only as a redirect target.
+ "troubleshooting-and-support/uninstalling-warp.md",
+ },
+}
+
+
+def compute_docs_url_path(gitbook_space: str, gitbook_rel: str) -> str:
+ """Given a gitbook space and relative file path, compute the URL path in the docs site.
+
+ This is used for link rewriting. Returns a URL path like /agent-platform/capabilities/skills/
+ """
+ # Apply the structural remapping
+ dest = map_gitbook_path(gitbook_space, gitbook_rel)
+ # Convert to URL path
+ url = dest.replace(".mdx", "/")
+ if url.endswith("/index/"):
+ url = url[:-6] # /foo/index/ -> /foo/
+ if not url.startswith("/"):
+ url = "/" + url
+ if not url.endswith("/"):
+ url += "/"
+ return url
+
+
+def map_gitbook_path(space: str, rel_path: str) -> str:
+ """Map a gitbook file path to its docs destination path.
+
+ Args:
+ space: One of 'warp', 'agent-platform', 'reference', 'support-and-community',
+ 'enterprise', 'changelog', 'guides'
+ rel_path: Path relative to the space root (e.g. 'terminal/blocks/block-basics.md')
+
+ Returns:
+ Path relative to src/content/docs/ (e.g. 'terminal/blocks/block-basics.mdx')
+ """
+ p = rel_path
+
+ if space == "warp":
+ # warp/ content strips the prefix and goes to top-level sections.
+ # First check the docs-specific rename map for files that landed at
+ # different paths during the original migration (e.g. quickstart pages
+ # were folded into getting-started/quickstart/).
+ if p in WARP_RENAME_MAP:
+ return WARP_RENAME_MAP[p]
+ # Otherwise the rel_path is already relative to warp/, so just convert
+ # the extension below.
+ pass
+ elif space == "agent-platform":
+ # Handle warp-agents/ split
+ if p.startswith("warp-agents/"):
+ inner = p[len("warp-agents/"):]
+ # Check explicit rename map first. RENAME_MAP values are paths
+ # relative to the agent-platform/ root, so we still need to add
+ # the agent-platform/ prefix and then return early before the
+ # README.md → index.mdx normalization runs.
+ if p in RENAME_MAP:
+ return "agent-platform/" + RENAME_MAP[p]
+ # interacting-with-agents/* → local-agents/interacting-with-agents/*
+ if inner.startswith("interacting-with-agents/"):
+ p = "local-agents/" + inner
+ # agent-context/* → local-agents/agent-context/*
+ elif inner.startswith("agent-context/"):
+ p = "local-agents/" + inner
+ else:
+ stem = inner.replace(".md", "").replace("/README", "")
+ if stem in CAPABILITIES_FILES:
+ p = "capabilities/" + inner
+ elif stem in LOCAL_AGENTS_FILES:
+ p = "local-agents/" + inner
+ else:
+ # Default to capabilities for unknown
+ p = "capabilities/" + inner
+ p = "agent-platform/" + p
+ else:
+ p = "agent-platform/" + p
+ elif space == "guides":
+ p = "university/" + p
+ else:
+ # reference, support-and-community, enterprise, changelog
+ p = space + "/" + p
+
+ # README.md → index.mdx
+ if p.endswith("/README.md"):
+ p = p[:-len("/README.md")] + "/index.mdx"
+ elif p == "README.md":
+ p = "index.mdx"
+ else:
+ p = p[:-3] + ".mdx"
+
+ return p
+
+
+def map_asset_space(space: str) -> str:
+ """Map a gitbook space to an asset directory name."""
+ if space == "warp":
+ return "terminal" # warp assets go to terminal/
+ elif space == "guides":
+ return "university"
+ else:
+ return space
+
+
+# ---------------------------------------------------------------------------
+# Enhanced link rewriting
+# ---------------------------------------------------------------------------
+
+# Cross-space link patterns that need special handling
+CROSS_SPACE_LINK_MAP = {
+ # From warp space, links to agent-platform
+ "../agent-platform/": "/agent-platform/",
+ # From agent-platform, links to warp
+ "../warp/": "/",
+}
+
+
+def convert_links_enhanced(content: str, file_rel_dir: str, space: str) -> str:
+ """Rewrite relative .md links to absolute URL paths with structural remapping."""
+
+ def _rewrite_link(m):
+ text = m.group(1)
+ href = m.group(2)
+ fragment = ""
+
+ # Skip external, absolute, anchor-only, and mailto links
+ if href.startswith(("http://", "https://", "/", "#", "mailto:")):
+ return m.group(0)
+
+ # Separate fragment
+ if "#" in href:
+ href, fragment = href.split("#", 1)
+ fragment = "#" + fragment
+
+ if not href:
+ return f"[{text}](#{fragment[1:]})" if fragment else m.group(0)
+
+ # Handle cross-space links (../agent-platform/, ../warp/, etc.)
+ cross_space = None
+ cross_rel = href
+ for prefix, url_prefix in CROSS_SPACE_LINK_MAP.items():
+ if href.startswith(prefix):
+ cross_rel = href[len(prefix):]
+ if prefix.startswith("../agent-platform"):
+ cross_space = "agent-platform"
+ elif prefix.startswith("../warp"):
+ cross_space = "warp"
+ break
+
+ if cross_space:
+ # Resolve the cross-space link
+ parts = cross_rel.split("/")
+ normalised = [p for p in parts if p and p != "."]
+ resolved = []
+ for p in normalised:
+ if p == "..":
+ if resolved:
+ resolved.pop()
+ else:
+ resolved.append(p)
+ cross_file = "/".join(resolved)
+ if not cross_file.endswith(".md"):
+ if not cross_file:
+ cross_file = "README.md"
+ else:
+ cross_file += ".md" if "." not in cross_file.split("/")[-1] else ""
+ url = compute_docs_url_path(cross_space, cross_file)
+ return f"[{text}]({url}{fragment})"
+
+ # Normalise the path relative to the current file's directory
+ parts = ((file_rel_dir + "/" + href) if file_rel_dir else href).split("/")
+ normalised = []
+ for p in parts:
+ if p == "..":
+ if normalised:
+ normalised.pop()
+ elif p and p != ".":
+ normalised.append(p)
+ rel = "/".join(normalised)
+
+ # Compute URL from the resolved gitbook-relative path
+ if rel.endswith(".md"):
+ url = compute_docs_url_path(space, rel)
+ else:
+ # Treat as directory → README.md
+ readme = (rel + "/README.md") if rel else "README.md"
+ url = compute_docs_url_path(space, readme)
+
+ return f"[{text}]({url}{fragment})"
+
+ # Negative lookbehind to skip image links 
+ return re.sub(r"(? str:
+ """Compute relative path from a doc file to its asset directory."""
+ try:
+ rel_to_content = dst_path.relative_to(docs_root / "src" / "content" / "docs")
+ except ValueError:
+ return "../../../../assets/terminal/"
+
+ # Determine which asset directory to use based on the content path
+ parts = rel_to_content.parts
+ if len(parts) > 0 and parts[0] == "agent-platform":
+ asset_dir = "agent-platform"
+ elif len(parts) > 0 and parts[0] == "reference":
+ asset_dir = "reference"
+ elif len(parts) > 0 and parts[0] == "support-and-community":
+ asset_dir = "support-and-community"
+ elif len(parts) > 0 and parts[0] == "university":
+ asset_dir = "university"
+ elif len(parts) > 0 and parts[0] == "enterprise":
+ asset_dir = "enterprise"
+ else:
+ asset_dir = "terminal"
+
+ depth = len(rel_to_content.parent.parts)
+ # From content/docs/ up to src/: 2 + depth (content/docs = 2 levels)
+ ups = 2 + depth
+ return "../" * ups + f"assets/{asset_dir}/"
+
+
+def convert_file_enhanced(
+ src_path: Path,
+ dst_path: Path,
+ space: str,
+ gitbook_space_root: Path,
+ docs_root: Path,
+) -> str:
+ """Convert a single GitBook markdown file to Starlight MDX with enhanced link rewriting."""
+ content = src_path.read_text(encoding="utf-8")
+
+ # Determine relative directory within the space
+ try:
+ file_rel = src_path.relative_to(gitbook_space_root)
+ except ValueError:
+ file_rel = Path(src_path.name)
+ file_rel_dir = str(file_rel.parent) if str(file_rel.parent) != "." else ""
+
+ # Compute asset prefix from destination path
+ rel_asset_prefix = compute_rel_asset_prefix(dst_path, docs_root)
+
+ # 1. Parse and rebuild frontmatter
+ fm, body = parse_frontmatter(content)
+ for key in list(fm.keys()):
+ if key not in KEEP_FRONTMATTER_KEYS:
+ del fm[key]
+ new_fm = build_frontmatter(fm, body)
+ body = strip_first_h1(body)
+
+ # 2. Apply transforms (order matters)
+ body = convert_hints(body)
+ body = convert_embeds(body)
+ body = convert_tabs(body)
+ body = convert_code_blocks(body)
+ body = convert_steppers(body)
+ body = strip_gitbook_card_tables(body)
+ body = strip_gitbook_content_refs(body)
+ body = convert_figures(body, rel_asset_prefix)
+ body = convert_inline_images(body, rel_asset_prefix)
+ body = convert_links_enhanced(body, file_rel_dir, space)
+ body = convert_html_comments(body)
+ body = strip_custom_anchors(body)
+ body = convert_horizontal_rules(body)
+ body = sanitize_html_tables(body)
+ body = fix_html_void_elements(body)
+ body = convert_autolinks(body)
+
+ # 3. Reassemble
+ result = new_fm + "\n" + body
+
+ # 4. Inject MDX imports
+ result = inject_imports(result)
+
+ # 5. Clean up excessive blank lines
+ result = re.sub(r"\n{3,}", "\n\n", result)
+
+ if not result.endswith("\n"):
+ result += "\n"
+
+ return result
+
+
+# ---------------------------------------------------------------------------
+# Asset collection and copying
+# ---------------------------------------------------------------------------
+
+
+def copy_space_assets(gitbook_space_root: Path, asset_dir: Path) -> int:
+ """Copy all referenced assets from a gitbook space to the docs asset directory."""
+ gitbook_assets = gitbook_space_root / ".gitbook" / "assets"
+ if not gitbook_assets.is_dir():
+ return 0
+
+ asset_dir.mkdir(parents=True, exist_ok=True)
+
+ # Collect all referenced asset filenames
+ referenced = {}
+ for md_file in gitbook_space_root.rglob("*.md"):
+ if md_file.name == "SUMMARY.md":
+ continue
+ try:
+ content = md_file.read_text(encoding="utf-8")
+ except Exception:
+ continue
+ for m in re.finditer(r'\.gitbook/assets/([^")\s]+)', content):
+ original = m.group(1).strip()
+ if original:
+ referenced[original] = sanitize_asset_filename(original)
+
+ # Also copy any unreferenced assets (they might be referenced by converted content)
+ for asset_file in gitbook_assets.iterdir():
+ if asset_file.is_file():
+ name = asset_file.name
+ if name not in referenced:
+ referenced[name] = sanitize_asset_filename(name)
+
+ copied = 0
+ for original_name, safe_name in sorted(referenced.items()):
+ asset_src = gitbook_assets / original_name
+ if asset_src.exists():
+ dest = asset_dir / safe_name
+ if not dest.exists() or asset_src.stat().st_size != dest.stat().st_size:
+ shutil.copy2(asset_src, dest)
+ copied += 1
+
+ return copied
+
+
+# ---------------------------------------------------------------------------
+# Redirect extraction
+# ---------------------------------------------------------------------------
+
+
+def parse_gitbook_yaml_redirects(yaml_path: Path) -> dict:
+ """Parse redirects from a .gitbook.yaml file. Returns dict of source->target."""
+ if not yaml_path.exists():
+ return {}
+
+ content = yaml_path.read_text(encoding="utf-8")
+ redirects = {}
+ in_redirects = False
+
+ for line in content.split("\n"):
+ stripped = line.strip()
+ if stripped == "redirects:":
+ in_redirects = True
+ continue
+ if in_redirects:
+ if not line.startswith(" ") and not line.startswith("\t") and stripped and not stripped.startswith("#"):
+ break
+ m = re.match(r"\s+([^#:][^:]*?):\s+(.+)", line)
+ if m:
+ source = m.group(1).strip()
+ target = m.group(2).strip()
+ redirects[source] = target
+
+ return redirects
+
+
+def transform_redirect_target(space: str, target: str) -> str:
+ """Transform a gitbook redirect target to a docs URL path."""
+ # Remove .md extension and README
+ t = target
+ if t.endswith(".md"):
+ t = t[:-3]
+ t = re.sub(r"/README$", "", t)
+ if t == "README":
+ t = ""
+
+ # Handle fragment
+ fragment = ""
+ if "#" in t:
+ t, fragment = t.split("#", 1)
+ fragment = "#" + fragment
+
+ # Apply space prefix and structural remapping
+ if space == "warp":
+ # warp space content goes to top level (no prefix)
+ url = "/" + t if t else "/"
+ elif space == "agent-platform":
+ # Handle warp-agents/ split
+ if t.startswith("warp-agents/"):
+ inner = t[len("warp-agents/"):]
+ stem = inner.split("/")[0] if "/" in inner else inner
+ if stem in CAPABILITIES_FILES:
+ url = "/agent-platform/capabilities/" + inner
+ elif stem in LOCAL_AGENTS_FILES:
+ url = "/agent-platform/local-agents/" + inner
+ elif inner.startswith("interacting-with-agents"):
+ url = "/agent-platform/local-agents/" + inner
+ elif inner.startswith("agent-context"):
+ url = "/agent-platform/local-agents/" + inner
+ elif inner == "" or inner == "README":
+ url = "/agent-platform/local-agents/overview"
+ else:
+ url = "/agent-platform/capabilities/" + inner
+ elif t.startswith("cli-agents/"):
+ url = "/agent-platform/" + t
+ else:
+ url = "/agent-platform/" + t if t else "/agent-platform/"
+ elif space == "guides":
+ url = "/university/" + t if t else "/university/"
+ else:
+ url = "/" + space + "/" + t if t else "/" + space + "/"
+
+ # Clean up
+ url = url.rstrip("/") + "/"
+ while "//" in url:
+ url = url.replace("//", "/")
+
+ return url + fragment
+
+
+def generate_all_redirects(gitbook_root: Path) -> list:
+ """Parse all .gitbook.yaml files and generate vercel.json redirect entries."""
+ spaces = {
+ "warp": gitbook_root / "docs" / "warp",
+ "agent-platform": gitbook_root / "docs" / "agent-platform",
+ "reference": gitbook_root / "docs" / "reference",
+ "support-and-community": gitbook_root / "docs" / "support-and-community",
+ "enterprise": gitbook_root / "docs" / "enterprise",
+ "changelog": gitbook_root / "docs" / "changelog",
+ "guides": gitbook_root / "guides",
+ }
+
+ all_redirects = []
+ seen_sources = set()
+
+ for space_name, space_dir in spaces.items():
+ yaml_path = space_dir / ".gitbook.yaml"
+ raw_redirects = parse_gitbook_yaml_redirects(yaml_path)
+
+ for source, target in raw_redirects.items():
+ # Build the full source URL (space prefix)
+ if space_name == "warp":
+ full_source = "/" + source
+ elif space_name == "agent-platform":
+ full_source = "/agent-platform/" + source
+ elif space_name == "guides":
+ full_source = "/university/" + source
+ else:
+ full_source = "/" + space_name + "/" + source
+
+ # Clean up source
+ full_source = full_source.rstrip("/")
+ while "//" in full_source:
+ full_source = full_source.replace("//", "/")
+
+ # Transform target
+ dest = transform_redirect_target(space_name, target)
+
+ # Skip self-redirects
+ if full_source.rstrip("/") == dest.rstrip("/"):
+ continue
+
+ if full_source not in seen_sources:
+ seen_sources.add(full_source)
+ all_redirects.append({
+ "source": full_source,
+ "destination": dest,
+ "statusCode": 308,
+ })
+
+ return sorted(all_redirects, key=lambda r: r["source"])
+
+
+# ---------------------------------------------------------------------------
+# Main migration
+# ---------------------------------------------------------------------------
+
+
+def migrate_space(
+ gitbook_space_root: Path,
+ docs_root: Path,
+ space: str,
+) -> tuple[int, int]:
+ """Migrate all files from a gitbook space.
+
+ Returns (converted_count, asset_count).
+ """
+ starlight_docs = docs_root / "src" / "content" / "docs"
+
+ md_files = sorted(
+ p for p in gitbook_space_root.rglob("*.md")
+ if p.name != "SUMMARY.md" and "/_book/" not in str(p) and "/.gitbook/" not in str(p)
+ )
+
+ converted = 0
+ for src_path in md_files:
+ rel = str(src_path.relative_to(gitbook_space_root))
+ dst_rel = map_gitbook_path(space, rel)
+ dst_path = starlight_docs / dst_rel
+ dst_path.parent.mkdir(parents=True, exist_ok=True)
+
+ result = convert_file_enhanced(src_path, dst_path, space, gitbook_space_root, docs_root)
+ dst_path.write_text(result, encoding="utf-8")
+ converted += 1
+ print(f" ✓ {rel} → {dst_rel}")
+
+ # Copy assets
+ asset_dir_name = map_asset_space(space)
+ asset_dir = docs_root / "src" / "assets" / asset_dir_name
+ asset_count = copy_space_assets(gitbook_space_root, asset_dir)
+
+ return converted, asset_count
+
+
+def main():
+ if len(sys.argv) < 2:
+ print(__doc__)
+ sys.exit(1)
+
+ gitbook_root = Path(sys.argv[1]).resolve()
+ docs_root = Path(__file__).parent.parent.resolve()
+
+ if not (gitbook_root / "docs").is_dir():
+ print(f"Error: {gitbook_root}/docs is not a directory")
+ sys.exit(1)
+
+ spaces = [
+ ("warp", gitbook_root / "docs" / "warp"),
+ ("agent-platform", gitbook_root / "docs" / "agent-platform"),
+ ("reference", gitbook_root / "docs" / "reference"),
+ ("support-and-community", gitbook_root / "docs" / "support-and-community"),
+ ("enterprise", gitbook_root / "docs" / "enterprise"),
+ ("changelog", gitbook_root / "docs" / "changelog"),
+ ("guides", gitbook_root / "guides"),
+ ]
+
+ # Also handle the top-level README.md
+ top_readme = gitbook_root / "docs" / "README.md"
+
+ total_converted = 0
+ total_assets = 0
+
+ for space_name, space_dir in spaces:
+ if not space_dir.is_dir():
+ print(f"\n⚠ Skipping {space_name} (not found: {space_dir})")
+ continue
+ print(f"\n{'='*60}")
+ print(f" Migrating: {space_name}")
+ print(f"{'='*60}")
+ converted, assets = migrate_space(space_dir, docs_root, space_name)
+ total_converted += converted
+ total_assets += assets
+ print(f" → {converted} files converted, {assets} assets copied")
+
+ # Convert top-level README.md → index.mdx
+ if top_readme.exists():
+ print(f"\n Converting top-level README.md → index.mdx")
+ starlight_docs = docs_root / "src" / "content" / "docs"
+ dst_path = starlight_docs / "index.mdx"
+ result = convert_file_enhanced(
+ top_readme, dst_path, "warp",
+ gitbook_root / "docs", docs_root,
+ )
+ dst_path.write_text(result, encoding="utf-8")
+ total_converted += 1
+
+ # Generate redirects
+ print(f"\n{'='*60}")
+ print(f" Generating redirects")
+ print(f"{'='*60}")
+ redirects = generate_all_redirects(gitbook_root)
+
+ # Read existing vercel.json and merge
+ vercel_json_path = docs_root / "vercel.json"
+ if vercel_json_path.exists():
+ existing = json.loads(vercel_json_path.read_text(encoding="utf-8"))
+ else:
+ existing = {}
+
+ # Keep non-redirect config, replace redirects entirely
+ existing["redirects"] = redirects
+ vercel_json_path.write_text(
+ json.dumps(existing, indent=2) + "\n",
+ encoding="utf-8",
+ )
+ print(f" → {len(redirects)} redirects written to vercel.json")
+
+ print(f"\n{'='*60}")
+ print(f" MIGRATION COMPLETE")
+ print(f" Total files converted: {total_converted}")
+ print(f" Total assets copied: {total_assets}")
+ print(f" Total redirects: {len(redirects)}")
+ print(f"{'='*60}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/seo-audit.mjs b/scripts/seo-audit.mjs
new file mode 100644
index 0000000..8facf63
--- /dev/null
+++ b/scripts/seo-audit.mjs
@@ -0,0 +1,260 @@
+#!/usr/bin/env node
+/**
+ * SEO audit script.
+ *
+ * Verifies that every page we ship to docs.warp.dev has the same SEO tags
+ * the legacy GitBook docs emit today (title, description, canonical, robots,
+ * Open Graph, Twitter, alternate markdown link, apple-touch-icon, etc).
+ *
+ * Usage:
+ * # Audit a built site on disk (run after `npm run build`)
+ * node scripts/seo-audit.mjs --dist ./dist
+ *
+ * # Audit live URLs (handy for spot-checking the legacy GitBook output)
+ * node scripts/seo-audit.mjs --base https://docs.warp.dev /agent-platform/cloud-agents/oz-web-app /changelog
+ *
+ * # Diff between two sources, e.g. legacy vs. our preview deploy
+ * node scripts/seo-audit.mjs \\
+ * --base https://docs.warp.dev \\
+ * --compare https://docs-preview.warp.dev \\
+ * /agent-platform/cloud-agents/oz-web-app
+ *
+ * The script is intentionally dependency-free (uses regex over the raw HTML
+ * head) so it runs in CI without installing extra packages.
+ */
+import { readFile } from 'node:fs/promises';
+import path from 'node:path';
+
+const DEFAULT_PATHS = [
+ '/',
+ '/quickstart/',
+ '/agent-platform/',
+ '/agent-platform/cloud-agents/oz-web-app/',
+ '/agent-platform/capabilities/skills/',
+ '/reference/cli/',
+ '/reference/api-and-sdk/',
+ '/changelog/',
+ '/university/',
+];
+
+/** Tags GitBook produces that we want to keep at parity. */
+const REQUIRED_TAGS = [
+ { key: 'title', label: '' },
+ { key: 'meta:description', label: 'meta name="description"' },
+ { key: 'meta:robots', label: 'meta name="robots"' },
+ { key: 'link:canonical', label: 'link rel="canonical"' },
+ { key: 'meta:og:title', label: 'meta property="og:title"' },
+ { key: 'meta:og:description', label: 'meta property="og:description"' },
+ { key: 'meta:og:image', label: 'meta property="og:image"' },
+ { key: 'meta:og:url', label: 'meta property="og:url"' },
+ { key: 'meta:og:site_name', label: 'meta property="og:site_name"' },
+ { key: 'meta:twitter:card', label: 'meta name="twitter:card"' },
+ { key: 'meta:twitter:title', label: 'meta name="twitter:title"' },
+ { key: 'meta:twitter:description', label: 'meta name="twitter:description"' },
+ { key: 'meta:twitter:image', label: 'meta name="twitter:image"' },
+ { key: 'meta:apple-mobile-web-app-title', label: 'meta name="apple-mobile-web-app-title"' },
+ { key: 'link:apple-touch-icon', label: 'link rel="apple-touch-icon"' },
+ { key: 'link:alternate-markdown', label: 'link rel="alternate" type="text/markdown"' },
+ { key: 'link:sitemap-or-icon', label: 'link rel="sitemap" or "icon"/"shortcut icon"' },
+];
+
+function parseArgs(argv) {
+ const args = { dist: null, base: null, compare: null, paths: [] };
+ for (let i = 0; i < argv.length; i++) {
+ const arg = argv[i];
+ if (arg === '--dist') args.dist = argv[++i];
+ else if (arg === '--base') args.base = argv[++i];
+ else if (arg === '--compare') args.compare = argv[++i];
+ else if (arg === '--help' || arg === '-h') args.help = true;
+ else args.paths.push(arg);
+ }
+ return args;
+}
+
+function extractHead(html) {
+ const headMatch = html.match(/]*>([\s\S]*?)<\/head>/i);
+ return headMatch ? headMatch[1] : html;
+}
+
+function getTitle(head) {
+ const m = head.match(/]*>([\s\S]*?)<\/title>/i);
+ return m ? m[1].trim() : null;
+}
+
+function getMeta(head, attr, value) {
+ // Match either name="x" or property="x" (and either order of attrs).
+ const re = new RegExp(
+ `]*(?:${attr}=["']${value}["'][^>]*content=["']([^"']*)["']|content=["']([^"']*)["'][^>]*${attr}=["']${value}["'])[^>]*>`,
+ 'i',
+ );
+ const m = head.match(re);
+ return m ? (m[1] ?? m[2]) : null;
+}
+
+function getLink(head, rel, type) {
+ const typePart = type ? `type=["']${type}["']` : null;
+ // Try both attribute orders. Astro sometimes emits href before rel.
+ const patterns = [
+ new RegExp(
+ `]*rel=["']${rel}["']${typePart ? `[^>]*${typePart}` : ''}[^>]*href=["']([^"']*)["'][^>]*>`,
+ 'i',
+ ),
+ new RegExp(
+ `]*href=["']([^"']*)["'][^>]*rel=["']${rel}["']${typePart ? `[^>]*${typePart}` : ''}[^>]*>`,
+ 'i',
+ ),
+ ];
+ for (const re of patterns) {
+ const m = head.match(re);
+ if (m) return m[1];
+ }
+ return null;
+}
+
+function audit(html) {
+ const head = extractHead(html);
+ const result = {
+ 'title': getTitle(head),
+ 'meta:description': getMeta(head, 'name', 'description'),
+ 'meta:robots': getMeta(head, 'name', 'robots'),
+ 'link:canonical': getLink(head, 'canonical'),
+ 'meta:og:title': getMeta(head, 'property', 'og:title'),
+ 'meta:og:description': getMeta(head, 'property', 'og:description'),
+ 'meta:og:image': getMeta(head, 'property', 'og:image'),
+ 'meta:og:url': getMeta(head, 'property', 'og:url'),
+ 'meta:og:site_name': getMeta(head, 'property', 'og:site_name'),
+ 'meta:twitter:card': getMeta(head, 'name', 'twitter:card'),
+ 'meta:twitter:title': getMeta(head, 'name', 'twitter:title'),
+ 'meta:twitter:description': getMeta(head, 'name', 'twitter:description'),
+ 'meta:twitter:image': getMeta(head, 'name', 'twitter:image'),
+ 'meta:apple-mobile-web-app-title': getMeta(head, 'name', 'apple-mobile-web-app-title'),
+ 'link:apple-touch-icon': getLink(head, 'apple-touch-icon'),
+ 'link:alternate-markdown': getLink(head, 'alternate', 'text/markdown'),
+ 'link:sitemap-or-icon': getLink(head, 'sitemap') ?? getLink(head, 'icon') ?? getLink(head, 'shortcut icon'),
+ };
+ return result;
+}
+
+async function loadFromDist(distRoot, urlPath) {
+ const trimmed = urlPath.replace(/^\/+|\/+$/g, '');
+ const candidates = [
+ path.join(distRoot, trimmed, 'index.html'),
+ path.join(distRoot, trimmed + '.html'),
+ // Root path
+ ...(trimmed === '' ? [path.join(distRoot, 'index.html')] : []),
+ ];
+ for (const candidate of candidates) {
+ try {
+ return await readFile(candidate, 'utf8');
+ } catch {
+ // Try next candidate.
+ }
+ }
+ throw new Error(`Could not find built HTML for ${urlPath} (looked in ${candidates.join(', ')})`);
+}
+
+async function loadFromUrl(base, urlPath) {
+ const url = new URL(urlPath, base).href;
+ const res = await fetch(url, {
+ headers: {
+ 'User-Agent': 'Mozilla/5.0 (compatible; warp-seo-audit/1.0)',
+ 'Accept': 'text/html',
+ },
+ redirect: 'follow',
+ });
+ if (!res.ok) {
+ throw new Error(`${url} → HTTP ${res.status}`);
+ }
+ return await res.text();
+}
+
+function format(value, width = 80) {
+ if (value == null) return '— missing —';
+ const single = value.replace(/\s+/g, ' ').trim();
+ return single.length > width ? `${single.slice(0, width - 1)}…` : single;
+}
+
+async function fetchOne(args, urlPath) {
+ if (args.dist) {
+ return loadFromDist(path.resolve(args.dist), urlPath);
+ }
+ if (args.base) {
+ return loadFromUrl(args.base, urlPath);
+ }
+ throw new Error('Must pass --dist or --base ');
+}
+
+async function main() {
+ const args = parseArgs(process.argv.slice(2));
+ if (args.help) {
+ console.log(
+ [
+ 'SEO audit — checks for the meta tags GitBook emitted on every page',
+ 'so we don\'t regress when migrating to Astro/Starlight.',
+ '',
+ 'Usage:',
+ ' node scripts/seo-audit.mjs --dist ./dist [paths...]',
+ ' node scripts/seo-audit.mjs --base https://docs.warp.dev [paths...]',
+ ' node scripts/seo-audit.mjs --base --compare [paths...]',
+ '',
+ 'If no paths are passed, a default high-traffic set is audited.',
+ ].join('\n'),
+ );
+ return;
+ }
+
+ if (!args.dist && !args.base) {
+ console.error('Error: provide --dist (built site) or --base (live site).');
+ process.exit(2);
+ }
+
+ const paths = args.paths.length ? args.paths : DEFAULT_PATHS;
+ let totalMissing = 0;
+
+ for (const p of paths) {
+ console.log(`\n=== ${p} ===`);
+ let primary;
+ try {
+ primary = audit(await fetchOne(args, p));
+ } catch (err) {
+ console.error(` ERROR: ${err.message}`);
+ totalMissing += REQUIRED_TAGS.length;
+ continue;
+ }
+
+ let compare = null;
+ if (args.compare) {
+ try {
+ compare = audit(await loadFromUrl(args.compare, p));
+ } catch (err) {
+ console.error(` compare ERROR: ${err.message}`);
+ }
+ }
+
+ for (const { key, label } of REQUIRED_TAGS) {
+ const got = primary[key];
+ const cmp = compare?.[key];
+ const status = got == null ? '✗' : '✓';
+ if (got == null) totalMissing += 1;
+ let line = ` ${status} ${label.padEnd(48)} ${format(got)}`;
+ if (compare) {
+ line += `\n compare: ${format(cmp)}`;
+ if (got !== cmp && got != null && cmp != null) {
+ line += '\n (values differ — review for parity)';
+ }
+ }
+ console.log(line);
+ }
+ }
+
+ if (totalMissing > 0) {
+ console.error(`\n${totalMissing} missing tag(s) across ${paths.length} page(s).`);
+ process.exit(1);
+ }
+ console.log(`\nAll required tags present across ${paths.length} page(s).`);
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/scripts/sync-gitbook-changes.py b/scripts/sync-gitbook-changes.py
new file mode 100755
index 0000000..3e72b43
--- /dev/null
+++ b/scripts/sync-gitbook-changes.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python3
+"""Incrementally sync new/modified gitbook files into the Starlight docs repo.
+
+Reuses the conversion logic in `migrate-all.py` (which itself imports
+`convert-gitbook.py`) and limits the work to the files reported by
+`git diff --name-status ..` in the gitbook repo.
+
+Usage:
+ python scripts/sync-gitbook-changes.py /path/to/gitbook \\
+ [--baseline bf06b82f] [--head main] [--dry-run]
+
+Behavior:
+- Files added or modified under `docs//...` or `guides/...` are
+ converted via `migrate_all.convert_file_enhanced` and written into
+ `src/content/docs/...` using the same path mapping as the original migration.
+- Files deleted in gitbook are deleted in docs.
+- README.md/SUMMARY.md/.gitbook/* changes that aren't content are skipped.
+- Existing docs files that were intentionally diverged from gitbook (e.g.
+ `agent-modality.mdx` keeping the legacy slug) are still overwritten when
+ their gitbook source changed; the caller is expected to do post-processing
+ for renames or splits (self-hosting/, migrate-to-warp/, terminal/settings/).
+
+This script never touches `vercel.json` or `src/sidebar.ts` — those are
+edited separately so reviewers can see the navigation and redirect changes
+clearly.
+"""
+
+from __future__ import annotations
+
+import argparse
+import shutil
+import subprocess
+import sys
+from importlib.machinery import SourceFileLoader
+from pathlib import Path
+from typing import Iterable
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+DOCS_ROOT = SCRIPT_DIR.parent
+STARLIGHT_DOCS = DOCS_ROOT / "src" / "content" / "docs"
+
+# Load the existing conversion machinery
+migrate_all = SourceFileLoader(
+ "migrate_all", str(SCRIPT_DIR / "migrate-all.py")
+).load_module()
+
+
+SPACE_PREFIXES = (
+ ("docs/warp/", "warp"),
+ ("docs/agent-platform/", "agent-platform"),
+ ("docs/reference/", "reference"),
+ ("docs/support-and-community/", "support-and-community"),
+ ("docs/enterprise/", "enterprise"),
+ ("docs/changelog/", "changelog"),
+ ("guides/", "guides"),
+)
+
+# These paths are content but should not be auto-converted; they need manual
+# handling (see plan).
+SKIP_FILES = {
+ # SUMMARY.md drives sidebars; we map them to src/sidebar.ts manually.
+ "SUMMARY.md",
+ # .gitbook.yaml drives redirects; we port them to vercel.json manually.
+ ".gitbook.yaml",
+}
+
+# JSX components that are docs-only design improvements (introduced after the
+# original full migration, e.g. the FileTree migration in PR #50 and the
+# DemoVideo component). When the docs file already uses one of these, the
+# sync script will skip the file and report it instead of overwriting; the
+# operator should hand-merge the gitbook diff in those cases.
+DOCS_ONLY_JSX_GUARDS = (
+ " Path:
+ if space == "guides":
+ return gitbook_root / "guides"
+ return gitbook_root / "docs" / space
+
+
+def classify(rel_path: str) -> tuple[str, str] | None:
+ """Return (space, rel_in_space) or None for non-content paths."""
+ name = rel_path.rsplit("/", 1)[-1]
+ if name in SKIP_FILES:
+ return None
+ if "/.gitbook/" in rel_path or rel_path.startswith(".gitbook/"):
+ return None
+ if rel_path.startswith("docs/_book/"):
+ return None
+ if not rel_path.endswith(".md"):
+ return None
+
+ for prefix, space in SPACE_PREFIXES:
+ if rel_path.startswith(prefix):
+ return space, rel_path[len(prefix):]
+ return None
+
+
+def list_changes(
+ gitbook_root: Path, baseline: str, head: str
+) -> list[tuple[str, str]]:
+ """Return [(status, path), ...] from git diff --name-status."""
+ out = subprocess.check_output(
+ [
+ "git",
+ "-C",
+ str(gitbook_root),
+ "--no-pager",
+ "diff",
+ "--name-status",
+ f"{baseline}..{head}",
+ ],
+ text=True,
+ )
+ rows: list[tuple[str, str]] = []
+ for line in out.splitlines():
+ if not line.strip():
+ continue
+ parts = line.split("\t")
+ if len(parts) < 2:
+ continue
+ status = parts[0][0]
+ # For renames, the new path is the last column.
+ path = parts[-1]
+ rows.append((status, path))
+ return rows
+
+
+def docs_dst_for(space: str, rel_in_space: str) -> Path:
+ dst_rel = migrate_all.map_gitbook_path(space, rel_in_space)
+ return STARLIGHT_DOCS / dst_rel
+
+
+def has_docs_only_jsx(dst: Path) -> list[str]:
+ """Return the list of docs-only JSX guards present in `dst`, or [] if none."""
+ if not dst.exists():
+ return []
+ text = dst.read_text(encoding="utf-8")
+ return [g for g in DOCS_ONLY_JSX_GUARDS if g in text]
+
+
+def convert_one(gitbook_root: Path, space: str, rel_in_space: str, dry_run: bool) -> str:
+ src = gitbook_space_root(gitbook_root, space) / rel_in_space
+ dst = docs_dst_for(space, rel_in_space)
+ if not src.exists():
+ return f" ⚠ source missing: {src}"
+ # Refuse to overwrite a docs file that contains docs-only JSX components.
+ # The operator must 3-way merge the gitbook diff manually (restore docs/
+ # main, then re-apply just the gitbook content updates) so the design
+ # components are preserved.
+ guards = has_docs_only_jsx(dst)
+ if guards:
+ return (
+ f" ⚠ SKIPPED (docs-only JSX present, hand-merge required): "
+ f"{space}/{rel_in_space} → {dst.relative_to(DOCS_ROOT)} "
+ f"[{','.join(guards)}]"
+ )
+ if dry_run:
+ return f" [dry-run] {space}/{rel_in_space} → {dst.relative_to(DOCS_ROOT)}"
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ result = migrate_all.convert_file_enhanced(
+ src, dst, space, gitbook_space_root(gitbook_root, space), DOCS_ROOT
+ )
+ dst.write_text(result, encoding="utf-8")
+ return f" ✓ {space}/{rel_in_space} → {dst.relative_to(DOCS_ROOT)}"
+
+
+def delete_one(space: str, rel_in_space: str, dry_run: bool) -> str:
+ dst = docs_dst_for(space, rel_in_space)
+ if not dst.exists():
+ return f" ⊘ docs file already absent: {dst.relative_to(DOCS_ROOT)}"
+ if dry_run:
+ return f" [dry-run] delete {dst.relative_to(DOCS_ROOT)}"
+ dst.unlink()
+ # Clean up empty parent dirs
+ parent = dst.parent
+ while parent != STARLIGHT_DOCS and parent.exists() and not any(parent.iterdir()):
+ parent.rmdir()
+ parent = parent.parent
+ return f" ✗ deleted {dst.relative_to(DOCS_ROOT)}"
+
+
+def copy_changed_assets(
+ gitbook_root: Path, changed: Iterable[tuple[str, str]], dry_run: bool
+) -> int:
+ """Copy any asset images that changed (under docs//.gitbook/assets/)."""
+ copied = 0
+ for status, path in changed:
+ if status == "D":
+ continue
+ if "/.gitbook/assets/" not in path:
+ continue
+ # Determine which space the asset belongs to
+ for prefix, space in SPACE_PREFIXES:
+ if path.startswith(prefix):
+ break
+ else:
+ continue
+ asset_dir = migrate_all.map_asset_space(space)
+ src = gitbook_root / path
+ if not src.exists():
+ continue
+ dst = DOCS_ROOT / "src" / "assets" / asset_dir / migrate_all.sanitize_asset_filename(src.name)
+ if dry_run:
+ print(f" [dry-run] asset {src.relative_to(gitbook_root)} → {dst.relative_to(DOCS_ROOT)}")
+ continue
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(src, dst)
+ copied += 1
+ print(f" 📎 asset {src.relative_to(gitbook_root)} → {dst.relative_to(DOCS_ROOT)}")
+ return copied
+
+
+def main() -> int:
+ ap = argparse.ArgumentParser(description=__doc__)
+ ap.add_argument("gitbook", type=Path, help="Path to the gitbook repo root")
+ ap.add_argument("--baseline", default="bf06b82f", help="Git ref representing the last sync")
+ ap.add_argument("--head", default="main", help="Git ref to sync to")
+ ap.add_argument("--dry-run", action="store_true", help="Plan only; don't write")
+ args = ap.parse_args()
+
+ gitbook_root = args.gitbook.resolve()
+ if not (gitbook_root / "docs").is_dir():
+ print(f"Error: {gitbook_root}/docs is not a directory")
+ return 1
+
+ rows = list_changes(gitbook_root, args.baseline, args.head)
+ print(f"Found {len(rows)} changed paths between {args.baseline}..{args.head}")
+
+ converted = deleted = skipped = 0
+ log: list[str] = []
+ for status, path in rows:
+ cls = classify(path)
+ if cls is None:
+ skipped += 1
+ continue
+ space, rel = cls
+ # Honor docs-specific skip set for orphaned/duplicate gitbook files.
+ if rel in migrate_all.SKIP_PATHS.get(space, set()):
+ log.append(f" ⊘ skipped (intentional drop): {space}/{rel}")
+ skipped += 1
+ continue
+ if status == "D":
+ log.append(delete_one(space, rel, args.dry_run))
+ deleted += 1
+ else:
+ # A, M, or R (treated as add/modify of the new path)
+ log.append(convert_one(gitbook_root, space, rel, args.dry_run))
+ converted += 1
+
+ print()
+ for line in log:
+ print(line)
+ print()
+
+ asset_count = copy_changed_assets(gitbook_root, rows, args.dry_run)
+
+ print()
+ print(f"Summary: {converted} converted, {deleted} deleted, {skipped} skipped (non-content), {asset_count} assets copied")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/assets/agent-platform/@-reference-plans.png b/src/assets/agent-platform/@-reference-plans.png
new file mode 100644
index 0000000..bc2e9b1
Binary files /dev/null and b/src/assets/agent-platform/@-reference-plans.png differ
diff --git a/src/assets/agent-platform/Add-diff-as-context.png b/src/assets/agent-platform/Add-diff-as-context.png
new file mode 100644
index 0000000..c30d5ec
Binary files /dev/null and b/src/assets/agent-platform/Add-diff-as-context.png differ
diff --git a/src/assets/agent-platform/MCP_servers_agent_permissions.png b/src/assets/agent-platform/MCP_servers_agent_permissions.png
new file mode 100644
index 0000000..344ffcc
Binary files /dev/null and b/src/assets/agent-platform/MCP_servers_agent_permissions.png differ
diff --git a/src/assets/agent-platform/admin-panel-enabled-github-orgs.png b/src/assets/agent-platform/admin-panel-enabled-github-orgs.png
new file mode 100644
index 0000000..d5e2845
Binary files /dev/null and b/src/assets/agent-platform/admin-panel-enabled-github-orgs.png differ
diff --git a/src/assets/agent-platform/agent-conversations-new-modality.png b/src/assets/agent-platform/agent-conversations-new-modality.png
new file mode 100644
index 0000000..5e8cc92
Binary files /dev/null and b/src/assets/agent-platform/agent-conversations-new-modality.png differ
diff --git a/src/assets/agent-platform/agent-modality-conversation-view.png b/src/assets/agent-platform/agent-modality-conversation-view.png
new file mode 100644
index 0000000..6ef1506
Binary files /dev/null and b/src/assets/agent-platform/agent-modality-conversation-view.png differ
diff --git a/src/assets/agent-platform/agent-permissions-with-full-terminal-use.png b/src/assets/agent-platform/agent-permissions-with-full-terminal-use.png
new file mode 100644
index 0000000..bf381f2
Binary files /dev/null and b/src/assets/agent-platform/agent-permissions-with-full-terminal-use.png differ
diff --git a/src/assets/agent-platform/agent-plans-synced.png b/src/assets/agent-platform/agent-plans-synced.png
new file mode 100644
index 0000000..b6a3aa0
Binary files /dev/null and b/src/assets/agent-platform/agent-plans-synced.png differ
diff --git a/src/assets/agent-platform/agent-plans-tasks.png b/src/assets/agent-platform/agent-plans-tasks.png
new file mode 100644
index 0000000..770f738
Binary files /dev/null and b/src/assets/agent-platform/agent-plans-tasks.png differ
diff --git a/src/assets/agent-platform/agent-plans-versioning.png b/src/assets/agent-platform/agent-plans-versioning.png
new file mode 100644
index 0000000..cf42bf2
Binary files /dev/null and b/src/assets/agent-platform/agent-plans-versioning.png differ
diff --git a/src/assets/agent-platform/agent-profiles-allow-and-denylists.png b/src/assets/agent-platform/agent-profiles-allow-and-denylists.png
new file mode 100644
index 0000000..01a396a
Binary files /dev/null and b/src/assets/agent-platform/agent-profiles-allow-and-denylists.png differ
diff --git a/src/assets/agent-platform/agent-profiles-settings.png b/src/assets/agent-platform/agent-profiles-settings.png
new file mode 100644
index 0000000..1560870
Binary files /dev/null and b/src/assets/agent-platform/agent-profiles-settings.png differ
diff --git a/src/assets/agent-platform/agent-profiles.png b/src/assets/agent-platform/agent-profiles.png
new file mode 100644
index 0000000..3268384
Binary files /dev/null and b/src/assets/agent-platform/agent-profiles.png differ
diff --git a/src/assets/agent-platform/agent-session-sharing-command-palette.png b/src/assets/agent-platform/agent-session-sharing-command-palette.png
new file mode 100644
index 0000000..8343825
Binary files /dev/null and b/src/assets/agent-platform/agent-session-sharing-command-palette.png differ
diff --git a/src/assets/agent-platform/agent-session-sharing-right-click-menu.png b/src/assets/agent-platform/agent-session-sharing-right-click-menu.png
new file mode 100644
index 0000000..f6498d5
Binary files /dev/null and b/src/assets/agent-platform/agent-session-sharing-right-click-menu.png differ
diff --git a/src/assets/agent-platform/agent-tips.png b/src/assets/agent-platform/agent-tips.png
new file mode 100644
index 0000000..10b0adc
Binary files /dev/null and b/src/assets/agent-platform/agent-tips.png differ
diff --git a/src/assets/agent-platform/allow-refine-takeover.png b/src/assets/agent-platform/allow-refine-takeover.png
new file mode 100644
index 0000000..6ae5c11
Binary files /dev/null and b/src/assets/agent-platform/allow-refine-takeover.png differ
diff --git a/src/assets/agent-platform/at-context-app.png b/src/assets/agent-platform/at-context-app.png
new file mode 100644
index 0000000..6d78304
Binary files /dev/null and b/src/assets/agent-platform/at-context-app.png differ
diff --git a/src/assets/agent-platform/at-context-styles.png b/src/assets/agent-platform/at-context-styles.png
new file mode 100644
index 0000000..6fa227e
Binary files /dev/null and b/src/assets/agent-platform/at-context-styles.png differ
diff --git a/src/assets/agent-platform/at-context.png b/src/assets/agent-platform/at-context.png
new file mode 100644
index 0000000..d7cba00
Binary files /dev/null and b/src/assets/agent-platform/at-context.png differ
diff --git a/src/assets/agent-platform/auto-sync-plans-1.png b/src/assets/agent-platform/auto-sync-plans-1.png
new file mode 100644
index 0000000..4309952
Binary files /dev/null and b/src/assets/agent-platform/auto-sync-plans-1.png differ
diff --git a/src/assets/agent-platform/classic-input-follow-up.png b/src/assets/agent-platform/classic-input-follow-up.png
new file mode 100644
index 0000000..10ca9fc
Binary files /dev/null and b/src/assets/agent-platform/classic-input-follow-up.png differ
diff --git a/src/assets/agent-platform/classic-input-new-convo.png b/src/assets/agent-platform/classic-input-new-convo.png
new file mode 100644
index 0000000..4d44c07
Binary files /dev/null and b/src/assets/agent-platform/classic-input-new-convo.png differ
diff --git a/src/assets/agent-platform/cli-agent-toolbelt.png b/src/assets/agent-platform/cli-agent-toolbelt.png
new file mode 100644
index 0000000..a6357f5
Binary files /dev/null and b/src/assets/agent-platform/cli-agent-toolbelt.png differ
diff --git a/src/assets/agent-platform/cloud-agents-infra.png b/src/assets/agent-platform/cloud-agents-infra.png
new file mode 100644
index 0000000..b64a206
Binary files /dev/null and b/src/assets/agent-platform/cloud-agents-infra.png differ
diff --git a/src/assets/agent-platform/codebase-context-main.png b/src/assets/agent-platform/codebase-context-main.png
new file mode 100644
index 0000000..caec9a8
Binary files /dev/null and b/src/assets/agent-platform/codebase-context-main.png differ
diff --git a/src/assets/agent-platform/codebase-context-statuses.png b/src/assets/agent-platform/codebase-context-statuses.png
new file mode 100644
index 0000000..dba9d09
Binary files /dev/null and b/src/assets/agent-platform/codebase-context-statuses.png differ
diff --git a/src/assets/agent-platform/completion-markers.png b/src/assets/agent-platform/completion-markers.png
new file mode 100644
index 0000000..d265035
Binary files /dev/null and b/src/assets/agent-platform/completion-markers.png differ
diff --git a/src/assets/agent-platform/context-derived-from-memory.png b/src/assets/agent-platform/context-derived-from-memory.png
new file mode 100644
index 0000000..87a00de
Binary files /dev/null and b/src/assets/agent-platform/context-derived-from-memory.png differ
diff --git a/src/assets/agent-platform/context-references-memory.png b/src/assets/agent-platform/context-references-memory.png
new file mode 100644
index 0000000..580cbe2
Binary files /dev/null and b/src/assets/agent-platform/context-references-memory.png differ
diff --git a/src/assets/agent-platform/context-window-1.png b/src/assets/agent-platform/context-window-1.png
new file mode 100644
index 0000000..87b334f
Binary files /dev/null and b/src/assets/agent-platform/context-window-1.png differ
diff --git a/src/assets/agent-platform/context-window-2-1.png b/src/assets/agent-platform/context-window-2-1.png
new file mode 100644
index 0000000..e40fc2d
Binary files /dev/null and b/src/assets/agent-platform/context-window-2-1.png differ
diff --git a/src/assets/agent-platform/context-window-2.png b/src/assets/agent-platform/context-window-2.png
new file mode 100644
index 0000000..94ab5eb
Binary files /dev/null and b/src/assets/agent-platform/context-window-2.png differ
diff --git a/src/assets/agent-platform/context-window-3.png b/src/assets/agent-platform/context-window-3.png
new file mode 100644
index 0000000..f52b179
Binary files /dev/null and b/src/assets/agent-platform/context-window-3.png differ
diff --git a/src/assets/agent-platform/conversation-fork-and-compact.png b/src/assets/agent-platform/conversation-fork-and-compact.png
new file mode 100644
index 0000000..6b203ff
Binary files /dev/null and b/src/assets/agent-platform/conversation-fork-and-compact.png differ
diff --git a/src/assets/agent-platform/conversation-forking-chip.png b/src/assets/agent-platform/conversation-forking-chip.png
new file mode 100644
index 0000000..4ad3561
Binary files /dev/null and b/src/assets/agent-platform/conversation-forking-chip.png differ
diff --git a/src/assets/agent-platform/conversation-forking-footer.png b/src/assets/agent-platform/conversation-forking-footer.png
new file mode 100644
index 0000000..4d05d9b
Binary files /dev/null and b/src/assets/agent-platform/conversation-forking-footer.png differ
diff --git a/src/assets/agent-platform/conversation-forking-open-conversations.png b/src/assets/agent-platform/conversation-forking-open-conversations.png
new file mode 100644
index 0000000..78a0cc4
Binary files /dev/null and b/src/assets/agent-platform/conversation-forking-open-conversations.png differ
diff --git a/src/assets/agent-platform/conversation-forking-palette.png b/src/assets/agent-platform/conversation-forking-palette.png
new file mode 100644
index 0000000..7692396
Binary files /dev/null and b/src/assets/agent-platform/conversation-forking-palette.png differ
diff --git a/src/assets/agent-platform/conversation-segmentation.png b/src/assets/agent-platform/conversation-segmentation.png
new file mode 100644
index 0000000..1c939ae
Binary files /dev/null and b/src/assets/agent-platform/conversation-segmentation.png differ
diff --git a/src/assets/agent-platform/customer-dedicated-saas.png b/src/assets/agent-platform/customer-dedicated-saas.png
new file mode 100644
index 0000000..64ead33
Binary files /dev/null and b/src/assets/agent-platform/customer-dedicated-saas.png differ
diff --git a/src/assets/agent-platform/delete-warpy.png b/src/assets/agent-platform/delete-warpy.png
new file mode 100644
index 0000000..d1cf0fe
Binary files /dev/null and b/src/assets/agent-platform/delete-warpy.png differ
diff --git a/src/assets/agent-platform/editting_diff.png b/src/assets/agent-platform/editting_diff.png
new file mode 100644
index 0000000..3e295be
Binary files /dev/null and b/src/assets/agent-platform/editting_diff.png differ
diff --git a/src/assets/agent-platform/enable-cli-agent-notifications.png b/src/assets/agent-platform/enable-cli-agent-notifications.png
new file mode 100644
index 0000000..7cc1559
Binary files /dev/null and b/src/assets/agent-platform/enable-cli-agent-notifications.png differ
diff --git a/src/assets/agent-platform/export-notebooks.png b/src/assets/agent-platform/export-notebooks.png
new file mode 100644
index 0000000..03cb4b5
Binary files /dev/null and b/src/assets/agent-platform/export-notebooks.png differ
diff --git a/src/assets/agent-platform/follow-up-universal-input.png b/src/assets/agent-platform/follow-up-universal-input.png
new file mode 100644
index 0000000..53193ed
Binary files /dev/null and b/src/assets/agent-platform/follow-up-universal-input.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-build.png b/src/assets/agent-platform/full-terminal-use-build.png
new file mode 100644
index 0000000..27e8e6f
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-build.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-dev-monitor.png b/src/assets/agent-platform/full-terminal-use-dev-monitor.png
new file mode 100644
index 0000000..58d9e6f
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-dev-monitor.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-handoff.png b/src/assets/agent-platform/full-terminal-use-handoff.png
new file mode 100644
index 0000000..77a9086
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-handoff.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-options-2.png b/src/assets/agent-platform/full-terminal-use-options-2.png
new file mode 100644
index 0000000..947c497
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-options-2.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-tag-hint.png b/src/assets/agent-platform/full-terminal-use-tag-hint.png
new file mode 100644
index 0000000..340aea2
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-tag-hint.png differ
diff --git a/src/assets/agent-platform/full-terminal-use-takeover.png b/src/assets/agent-platform/full-terminal-use-takeover.png
new file mode 100644
index 0000000..efa5d3d
Binary files /dev/null and b/src/assets/agent-platform/full-terminal-use-takeover.png differ
diff --git a/src/assets/agent-platform/generate-psql.png b/src/assets/agent-platform/generate-psql.png
new file mode 100644
index 0000000..8715662
Binary files /dev/null and b/src/assets/agent-platform/generate-psql.png differ
diff --git a/src/assets/agent-platform/generate-vim.png b/src/assets/agent-platform/generate-vim.png
new file mode 100644
index 0000000..338c694
Binary files /dev/null and b/src/assets/agent-platform/generate-vim.png differ
diff --git a/src/assets/agent-platform/generated_blocklist_diff.png b/src/assets/agent-platform/generated_blocklist_diff.png
new file mode 100644
index 0000000..b46a55c
Binary files /dev/null and b/src/assets/agent-platform/generated_blocklist_diff.png differ
diff --git a/src/assets/agent-platform/git-diff-full-view.png b/src/assets/agent-platform/git-diff-full-view.png
new file mode 100644
index 0000000..47423aa
Binary files /dev/null and b/src/assets/agent-platform/git-diff-full-view.png differ
diff --git a/src/assets/agent-platform/image-as-context-classic.png b/src/assets/agent-platform/image-as-context-classic.png
new file mode 100644
index 0000000..9df3481
Binary files /dev/null and b/src/assets/agent-platform/image-as-context-classic.png differ
diff --git a/src/assets/agent-platform/image-as-context-universal.png b/src/assets/agent-platform/image-as-context-universal.png
new file mode 100644
index 0000000..80b86c4
Binary files /dev/null and b/src/assets/agent-platform/image-as-context-universal.png differ
diff --git a/src/assets/agent-platform/in-progress-tasklist.png b/src/assets/agent-platform/in-progress-tasklist.png
new file mode 100644
index 0000000..8fc56bb
Binary files /dev/null and b/src/assets/agent-platform/in-progress-tasklist.png differ
diff --git a/src/assets/agent-platform/init-setup-flow-1.png b/src/assets/agent-platform/init-setup-flow-1.png
new file mode 100644
index 0000000..6f17d3c
Binary files /dev/null and b/src/assets/agent-platform/init-setup-flow-1.png differ
diff --git a/src/assets/agent-platform/init-setup-flow-2.png b/src/assets/agent-platform/init-setup-flow-2.png
new file mode 100644
index 0000000..a32d318
Binary files /dev/null and b/src/assets/agent-platform/init-setup-flow-2.png differ
diff --git a/src/assets/agent-platform/interactive-code-review-adding-comment.png b/src/assets/agent-platform/interactive-code-review-adding-comment.png
new file mode 100644
index 0000000..6d67e7a
Binary files /dev/null and b/src/assets/agent-platform/interactive-code-review-adding-comment.png differ
diff --git a/src/assets/agent-platform/interactive-code-review-batch-comments.png b/src/assets/agent-platform/interactive-code-review-batch-comments.png
new file mode 100644
index 0000000..fbdc702
Binary files /dev/null and b/src/assets/agent-platform/interactive-code-review-batch-comments.png differ
diff --git a/src/assets/agent-platform/linear-warp-on-web.png b/src/assets/agent-platform/linear-warp-on-web.png
new file mode 100644
index 0000000..0486003
Binary files /dev/null and b/src/assets/agent-platform/linear-warp-on-web.png differ
diff --git a/src/assets/agent-platform/management-view-scannable-list.png b/src/assets/agent-platform/management-view-scannable-list.png
new file mode 100644
index 0000000..0b4cbbf
Binary files /dev/null and b/src/assets/agent-platform/management-view-scannable-list.png differ
diff --git a/src/assets/agent-platform/manually-trigger-plan.png b/src/assets/agent-platform/manually-trigger-plan.png
new file mode 100644
index 0000000..6a2f4dc
Binary files /dev/null and b/src/assets/agent-platform/manually-trigger-plan.png differ
diff --git a/src/assets/agent-platform/mcp-servers-add-cli.png b/src/assets/agent-platform/mcp-servers-add-cli.png
new file mode 100644
index 0000000..bba6b7d
Binary files /dev/null and b/src/assets/agent-platform/mcp-servers-add-cli.png differ
diff --git a/src/assets/agent-platform/mcp-servers-add-sse.png b/src/assets/agent-platform/mcp-servers-add-sse.png
new file mode 100644
index 0000000..99cfe94
Binary files /dev/null and b/src/assets/agent-platform/mcp-servers-add-sse.png differ
diff --git a/src/assets/agent-platform/mcp-servers-list.png b/src/assets/agent-platform/mcp-servers-list.png
new file mode 100644
index 0000000..283d902
Binary files /dev/null and b/src/assets/agent-platform/mcp-servers-list.png differ
diff --git a/src/assets/agent-platform/mcp-servers-share.png b/src/assets/agent-platform/mcp-servers-share.png
new file mode 100644
index 0000000..ef9c07a
Binary files /dev/null and b/src/assets/agent-platform/mcp-servers-share.png differ
diff --git a/src/assets/agent-platform/model-selector-dropdown.png b/src/assets/agent-platform/model-selector-dropdown.png
new file mode 100644
index 0000000..ac1ed11
Binary files /dev/null and b/src/assets/agent-platform/model-selector-dropdown.png differ
diff --git a/src/assets/agent-platform/most-flexible-platform-for-building-with-agents.png b/src/assets/agent-platform/most-flexible-platform-for-building-with-agents.png
new file mode 100644
index 0000000..794a0e0
Binary files /dev/null and b/src/assets/agent-platform/most-flexible-platform-for-building-with-agents.png differ
diff --git a/src/assets/agent-platform/next-command.png b/src/assets/agent-platform/next-command.png
new file mode 100644
index 0000000..289af9c
Binary files /dev/null and b/src/assets/agent-platform/next-command.png differ
diff --git a/src/assets/agent-platform/notification-mailbox.png b/src/assets/agent-platform/notification-mailbox.png
new file mode 100644
index 0000000..ff9410e
Binary files /dev/null and b/src/assets/agent-platform/notification-mailbox.png differ
diff --git a/src/assets/agent-platform/open-ai-commands.png b/src/assets/agent-platform/open-ai-commands.png
new file mode 100644
index 0000000..7b0133b
Binary files /dev/null and b/src/assets/agent-platform/open-ai-commands.png differ
diff --git a/src/assets/agent-platform/oz-diagram.png b/src/assets/agent-platform/oz-diagram.png
new file mode 100644
index 0000000..f179326
Binary files /dev/null and b/src/assets/agent-platform/oz-diagram.png differ
diff --git a/src/assets/agent-platform/oz-github-app-installation.png b/src/assets/agent-platform/oz-github-app-installation.png
new file mode 100644
index 0000000..b10354e
Binary files /dev/null and b/src/assets/agent-platform/oz-github-app-installation.png differ
diff --git a/src/assets/agent-platform/oz-use-cases.png b/src/assets/agent-platform/oz-use-cases.png
new file mode 100644
index 0000000..b30fd26
Binary files /dev/null and b/src/assets/agent-platform/oz-use-cases.png differ
diff --git a/src/assets/agent-platform/oz-web-app-agents.png b/src/assets/agent-platform/oz-web-app-agents.png
new file mode 100644
index 0000000..98ef2bd
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-agents.png differ
diff --git a/src/assets/agent-platform/oz-web-app-environments.png b/src/assets/agent-platform/oz-web-app-environments.png
new file mode 100644
index 0000000..0d98531
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-environments.png differ
diff --git a/src/assets/agent-platform/oz-web-app-integrations.png b/src/assets/agent-platform/oz-web-app-integrations.png
new file mode 100644
index 0000000..3516593
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-integrations.png differ
diff --git a/src/assets/agent-platform/oz-web-app-new-agent.png b/src/assets/agent-platform/oz-web-app-new-agent.png
new file mode 100644
index 0000000..1adef01
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-new-agent.png differ
diff --git a/src/assets/agent-platform/oz-web-app-new-environment.png b/src/assets/agent-platform/oz-web-app-new-environment.png
new file mode 100644
index 0000000..245f257
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-new-environment.png differ
diff --git a/src/assets/agent-platform/oz-web-app-runs-view.png b/src/assets/agent-platform/oz-web-app-runs-view.png
new file mode 100644
index 0000000..f579408
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-runs-view.png differ
diff --git a/src/assets/agent-platform/oz-web-app-schedules.png b/src/assets/agent-platform/oz-web-app-schedules.png
new file mode 100644
index 0000000..59bff9b
Binary files /dev/null and b/src/assets/agent-platform/oz-web-app-schedules.png differ
diff --git a/src/assets/agent-platform/plan-slash-command.png b/src/assets/agent-platform/plan-slash-command.png
new file mode 100644
index 0000000..2dac8a2
Binary files /dev/null and b/src/assets/agent-platform/plan-slash-command.png differ
diff --git a/src/assets/agent-platform/planning-main-view.png b/src/assets/agent-platform/planning-main-view.png
new file mode 100644
index 0000000..2b9f06a
Binary files /dev/null and b/src/assets/agent-platform/planning-main-view.png differ
diff --git a/src/assets/agent-platform/plans-in-warp-drive-side-panel.png b/src/assets/agent-platform/plans-in-warp-drive-side-panel.png
new file mode 100644
index 0000000..8d1a407
Binary files /dev/null and b/src/assets/agent-platform/plans-in-warp-drive-side-panel.png differ
diff --git a/src/assets/agent-platform/project-scoped-rules-pane.png b/src/assets/agent-platform/project-scoped-rules-pane.png
new file mode 100644
index 0000000..e998471
Binary files /dev/null and b/src/assets/agent-platform/project-scoped-rules-pane.png differ
diff --git a/src/assets/agent-platform/prompt-suggestions-example-1.png b/src/assets/agent-platform/prompt-suggestions-example-1.png
new file mode 100644
index 0000000..9068c40
Binary files /dev/null and b/src/assets/agent-platform/prompt-suggestions-example-1.png differ
diff --git a/src/assets/agent-platform/prompt-suggestions-setting-1.png b/src/assets/agent-platform/prompt-suggestions-setting-1.png
new file mode 100644
index 0000000..010f0d6
Binary files /dev/null and b/src/assets/agent-platform/prompt-suggestions-setting-1.png differ
diff --git a/src/assets/agent-platform/remote-control.png b/src/assets/agent-platform/remote-control.png
new file mode 100644
index 0000000..97e2300
Binary files /dev/null and b/src/assets/agent-platform/remote-control.png differ
diff --git a/src/assets/agent-platform/remove-slack-app.png b/src/assets/agent-platform/remove-slack-app.png
new file mode 100644
index 0000000..2241f1e
Binary files /dev/null and b/src/assets/agent-platform/remove-slack-app.png differ
diff --git a/src/assets/agent-platform/remove_all_untracked_files.png b/src/assets/agent-platform/remove_all_untracked_files.png
new file mode 100644
index 0000000..974d0c1
Binary files /dev/null and b/src/assets/agent-platform/remove_all_untracked_files.png differ
diff --git a/src/assets/agent-platform/rich-input-button.png b/src/assets/agent-platform/rich-input-button.png
new file mode 100644
index 0000000..263925c
Binary files /dev/null and b/src/assets/agent-platform/rich-input-button.png differ
diff --git a/src/assets/agent-platform/rich-input-prompt.png b/src/assets/agent-platform/rich-input-prompt.png
new file mode 100644
index 0000000..93fa9a9
Binary files /dev/null and b/src/assets/agent-platform/rich-input-prompt.png differ
diff --git a/src/assets/agent-platform/rich-input-settings.png b/src/assets/agent-platform/rich-input-settings.png
new file mode 100644
index 0000000..5bbe88c
Binary files /dev/null and b/src/assets/agent-platform/rich-input-settings.png differ
diff --git a/src/assets/agent-platform/run-until-completion.png b/src/assets/agent-platform/run-until-completion.png
new file mode 100644
index 0000000..d0711cc
Binary files /dev/null and b/src/assets/agent-platform/run-until-completion.png differ
diff --git a/src/assets/agent-platform/selection-as-context.png b/src/assets/agent-platform/selection-as-context.png
new file mode 100644
index 0000000..a89c369
Binary files /dev/null and b/src/assets/agent-platform/selection-as-context.png differ
diff --git a/src/assets/agent-platform/send-to-agent.png b/src/assets/agent-platform/send-to-agent.png
new file mode 100644
index 0000000..538ed0d
Binary files /dev/null and b/src/assets/agent-platform/send-to-agent.png differ
diff --git a/src/assets/agent-platform/slash-commands-agent-modality.png b/src/assets/agent-platform/slash-commands-agent-modality.png
new file mode 100644
index 0000000..7baed27
Binary files /dev/null and b/src/assets/agent-platform/slash-commands-agent-modality.png differ
diff --git a/src/assets/agent-platform/slash-commands-menu.png b/src/assets/agent-platform/slash-commands-menu.png
new file mode 100644
index 0000000..d74394b
Binary files /dev/null and b/src/assets/agent-platform/slash-commands-menu.png differ
diff --git a/src/assets/agent-platform/slash-commands-prompts.png b/src/assets/agent-platform/slash-commands-prompts.png
new file mode 100644
index 0000000..15807b8
Binary files /dev/null and b/src/assets/agent-platform/slash-commands-prompts.png differ
diff --git a/src/assets/agent-platform/slash-commands-terminal-modality.png b/src/assets/agent-platform/slash-commands-terminal-modality.png
new file mode 100644
index 0000000..1eba593
Binary files /dev/null and b/src/assets/agent-platform/slash-commands-terminal-modality.png differ
diff --git a/src/assets/agent-platform/suggested-code-diffs-generating-fix.png b/src/assets/agent-platform/suggested-code-diffs-generating-fix.png
new file mode 100644
index 0000000..0dabe7c
Binary files /dev/null and b/src/assets/agent-platform/suggested-code-diffs-generating-fix.png differ
diff --git a/src/assets/agent-platform/suggested-code-diffs.png b/src/assets/agent-platform/suggested-code-diffs.png
new file mode 100644
index 0000000..a659d3c
Binary files /dev/null and b/src/assets/agent-platform/suggested-code-diffs.png differ
diff --git a/src/assets/agent-platform/tasklist-popup.png b/src/assets/agent-platform/tasklist-popup.png
new file mode 100644
index 0000000..9180348
Binary files /dev/null and b/src/assets/agent-platform/tasklist-popup.png differ
diff --git a/src/assets/agent-platform/tasklist-small.png b/src/assets/agent-platform/tasklist-small.png
new file mode 100644
index 0000000..6846a8f
Binary files /dev/null and b/src/assets/agent-platform/tasklist-small.png differ
diff --git a/src/assets/agent-platform/terminal-modality.png b/src/assets/agent-platform/terminal-modality.png
new file mode 100644
index 0000000..7727585
Binary files /dev/null and b/src/assets/agent-platform/terminal-modality.png differ
diff --git a/src/assets/agent-platform/toast-notification.png b/src/assets/agent-platform/toast-notification.png
new file mode 100644
index 0000000..e6f1e1c
Binary files /dev/null and b/src/assets/agent-platform/toast-notification.png differ
diff --git a/src/assets/agent-platform/universal-input-new-convo.png b/src/assets/agent-platform/universal-input-new-convo.png
new file mode 100644
index 0000000..c6ce01b
Binary files /dev/null and b/src/assets/agent-platform/universal-input-new-convo.png differ
diff --git a/src/assets/agent-platform/update-agent-mid-plan.png b/src/assets/agent-platform/update-agent-mid-plan.png
new file mode 100644
index 0000000..ad09cd9
Binary files /dev/null and b/src/assets/agent-platform/update-agent-mid-plan.png differ
diff --git a/src/assets/agent-platform/url-as-context.png b/src/assets/agent-platform/url-as-context.png
new file mode 100644
index 0000000..176c44d
Binary files /dev/null and b/src/assets/agent-platform/url-as-context.png differ
diff --git a/src/assets/agent-platform/voice-in-find.png b/src/assets/agent-platform/voice-in-find.png
new file mode 100644
index 0000000..f221503
Binary files /dev/null and b/src/assets/agent-platform/voice-in-find.png differ
diff --git a/src/assets/agent-platform/voice-settings.png b/src/assets/agent-platform/voice-settings.png
new file mode 100644
index 0000000..8ec7b05
Binary files /dev/null and b/src/assets/agent-platform/voice-settings.png differ
diff --git a/src/assets/agent-platform/web-search-results.png b/src/assets/agent-platform/web-search-results.png
new file mode 100644
index 0000000..3233fd8
Binary files /dev/null and b/src/assets/agent-platform/web-search-results.png differ
diff --git a/src/assets/agent-platform/web-search-settings.png b/src/assets/agent-platform/web-search-settings.png
new file mode 100644
index 0000000..e895e9f
Binary files /dev/null and b/src/assets/agent-platform/web-search-settings.png differ
diff --git a/src/assets/guides/Screenshot-2025-10-07-at-10.44.14-AM.png b/src/assets/guides/Screenshot-2025-10-07-at-10.44.14-AM.png
new file mode 100644
index 0000000..dd853bd
Binary files /dev/null and b/src/assets/guides/Screenshot-2025-10-07-at-10.44.14-AM.png differ
diff --git a/src/assets/guides/code-review-button.png b/src/assets/guides/code-review-button.png
new file mode 100644
index 0000000..9872bea
Binary files /dev/null and b/src/assets/guides/code-review-button.png differ
diff --git a/src/assets/guides/code-review-file-sidebar.png b/src/assets/guides/code-review-file-sidebar.png
new file mode 100644
index 0000000..0b26557
Binary files /dev/null and b/src/assets/guides/code-review-file-sidebar.png differ
diff --git a/src/assets/guides/code-review-inline-comment.png b/src/assets/guides/code-review-inline-comment.png
new file mode 100644
index 0000000..f6e5635
Binary files /dev/null and b/src/assets/guides/code-review-inline-comment.png differ
diff --git a/src/assets/guides/code-review-panel-with-diffs.png b/src/assets/guides/code-review-panel-with-diffs.png
new file mode 100644
index 0000000..04963f4
Binary files /dev/null and b/src/assets/guides/code-review-panel-with-diffs.png differ
diff --git a/src/assets/guides/image-as-context.png b/src/assets/guides/image-as-context.png
new file mode 100644
index 0000000..c1f62b1
Binary files /dev/null and b/src/assets/guides/image-as-context.png differ
diff --git a/src/assets/guides/tab-notification-indicator.png b/src/assets/guides/tab-notification-indicator.png
new file mode 100644
index 0000000..44767c4
Binary files /dev/null and b/src/assets/guides/tab-notification-indicator.png differ
diff --git a/src/assets/guides/voice-input-microphone.png b/src/assets/guides/voice-input-microphone.png
new file mode 100644
index 0000000..a1b430b
Binary files /dev/null and b/src/assets/guides/voice-input-microphone.png differ
diff --git a/src/assets/reference/api-key-management.png b/src/assets/reference/api-key-management.png
new file mode 100644
index 0000000..1f1422a
Binary files /dev/null and b/src/assets/reference/api-key-management.png differ
diff --git a/src/assets/reference/mcp-server-id.png b/src/assets/reference/mcp-server-id.png
new file mode 100644
index 0000000..98e9259
Binary files /dev/null and b/src/assets/reference/mcp-server-id.png differ
diff --git a/src/assets/support-and-community/Pricing-Blog-BYOK.png b/src/assets/support-and-community/Pricing-Blog-BYOK.png
new file mode 100644
index 0000000..c28ac66
Binary files /dev/null and b/src/assets/support-and-community/Pricing-Blog-BYOK.png differ
diff --git a/src/assets/support-and-community/activity-monitor-sample-process.png b/src/assets/support-and-community/activity-monitor-sample-process.png
new file mode 100644
index 0000000..f03d99b
Binary files /dev/null and b/src/assets/support-and-community/activity-monitor-sample-process.png differ
diff --git a/src/assets/support-and-community/auth-token-flow.png b/src/assets/support-and-community/auth-token-flow.png
new file mode 100644
index 0000000..274f689
Binary files /dev/null and b/src/assets/support-and-community/auth-token-flow.png differ
diff --git a/src/assets/support-and-community/byok-keys.png b/src/assets/support-and-community/byok-keys.png
new file mode 100644
index 0000000..7120565
Binary files /dev/null and b/src/assets/support-and-community/byok-keys.png differ
diff --git a/src/assets/support-and-community/fallback.png b/src/assets/support-and-community/fallback.png
new file mode 100644
index 0000000..39201d8
Binary files /dev/null and b/src/assets/support-and-community/fallback.png differ
diff --git a/src/assets/support-and-community/inline-credit-usage-footer.png b/src/assets/support-and-community/inline-credit-usage-footer.png
new file mode 100644
index 0000000..380fc3f
Binary files /dev/null and b/src/assets/support-and-community/inline-credit-usage-footer.png differ
diff --git a/src/assets/support-and-community/mac-ssh-permission.png b/src/assets/support-and-community/mac-ssh-permission.png
new file mode 100644
index 0000000..6886ea3
Binary files /dev/null and b/src/assets/support-and-community/mac-ssh-permission.png differ
diff --git a/src/assets/support-and-community/overages-settings.png b/src/assets/support-and-community/overages-settings.png
new file mode 100644
index 0000000..20297bb
Binary files /dev/null and b/src/assets/support-and-community/overages-settings.png differ
diff --git a/src/assets/support-and-community/permission-error-macos.png b/src/assets/support-and-community/permission-error-macos.png
new file mode 100644
index 0000000..370abd6
Binary files /dev/null and b/src/assets/support-and-community/permission-error-macos.png differ
diff --git a/src/assets/support-and-community/privacy-settings-after-signup-1.png b/src/assets/support-and-community/privacy-settings-after-signup-1.png
new file mode 100644
index 0000000..116d317
Binary files /dev/null and b/src/assets/support-and-community/privacy-settings-after-signup-1.png differ
diff --git a/src/assets/support-and-community/reload-credits.png b/src/assets/support-and-community/reload-credits.png
new file mode 100644
index 0000000..7235dd1
Binary files /dev/null and b/src/assets/support-and-community/reload-credits.png differ
diff --git a/src/assets/support-and-community/send-feedback-debugging-information.png b/src/assets/support-and-community/send-feedback-debugging-information.png
new file mode 100644
index 0000000..e7404c6
Binary files /dev/null and b/src/assets/support-and-community/send-feedback-debugging-information.png differ
diff --git a/src/assets/support-and-community/update-available-bar.png b/src/assets/support-and-community/update-available-bar.png
new file mode 100644
index 0000000..5b32279
Binary files /dev/null and b/src/assets/support-and-community/update-available-bar.png differ
diff --git a/src/assets/support-and-community/update-available.png b/src/assets/support-and-community/update-available.png
new file mode 100644
index 0000000..976978f
Binary files /dev/null and b/src/assets/support-and-community/update-available.png differ
diff --git a/src/assets/terminal/Blocklist-with-review-changes.png b/src/assets/terminal/Blocklist-with-review-changes.png
new file mode 100644
index 0000000..62fa47f
Binary files /dev/null and b/src/assets/terminal/Blocklist-with-review-changes.png differ
diff --git a/src/assets/terminal/Edit_Workflow.png b/src/assets/terminal/Edit_Workflow.png
new file mode 100644
index 0000000..8e33c4c
Binary files /dev/null and b/src/assets/terminal/Edit_Workflow.png differ
diff --git a/src/assets/terminal/Open_Warp_Drive.png b/src/assets/terminal/Open_Warp_Drive.png
new file mode 100644
index 0000000..0288f90
Binary files /dev/null and b/src/assets/terminal/Open_Warp_Drive.png differ
diff --git a/src/assets/terminal/Warp_Drive_Zero_State.png b/src/assets/terminal/Warp_Drive_Zero_State.png
new file mode 100644
index 0000000..d0cb979
Binary files /dev/null and b/src/assets/terminal/Warp_Drive_Zero_State.png differ
diff --git a/src/assets/terminal/Warp_Drive_with_Team.png b/src/assets/terminal/Warp_Drive_with_Team.png
new file mode 100644
index 0000000..062dd65
Binary files /dev/null and b/src/assets/terminal/Warp_Drive_with_Team.png differ
diff --git a/src/assets/terminal/active-directory-chip.png b/src/assets/terminal/active-directory-chip.png
new file mode 100644
index 0000000..fbafa08
Binary files /dev/null and b/src/assets/terminal/active-directory-chip.png differ
diff --git a/src/assets/terminal/adeberry.png b/src/assets/terminal/adeberry.png
new file mode 100644
index 0000000..5c5a5a4
Binary files /dev/null and b/src/assets/terminal/adeberry.png differ
diff --git a/src/assets/terminal/admin-panel-overview.png b/src/assets/terminal/admin-panel-overview.png
new file mode 100644
index 0000000..9a756ef
Binary files /dev/null and b/src/assets/terminal/admin-panel-overview.png differ
diff --git a/src/assets/terminal/agent-mode-locked-universal-input.png b/src/assets/terminal/agent-mode-locked-universal-input.png
new file mode 100644
index 0000000..44313f3
Binary files /dev/null and b/src/assets/terminal/agent-mode-locked-universal-input.png differ
diff --git a/src/assets/terminal/agent-mode-suggestion-1.png b/src/assets/terminal/agent-mode-suggestion-1.png
new file mode 100644
index 0000000..36ee84d
Binary files /dev/null and b/src/assets/terminal/agent-mode-suggestion-1.png differ
diff --git a/src/assets/terminal/agent-status-badges.png b/src/assets/terminal/agent-status-badges.png
new file mode 100644
index 0000000..6fb9108
Binary files /dev/null and b/src/assets/terminal/agent-status-badges.png differ
diff --git a/src/assets/terminal/ai_control_panel_buttons_larger_view.png b/src/assets/terminal/ai_control_panel_buttons_larger_view.png
new file mode 100644
index 0000000..9026ae0
Binary files /dev/null and b/src/assets/terminal/ai_control_panel_buttons_larger_view.png differ
diff --git a/src/assets/terminal/annotated_blocks-1.png b/src/assets/terminal/annotated_blocks-1.png
new file mode 100644
index 0000000..73638f2
Binary files /dev/null and b/src/assets/terminal/annotated_blocks-1.png differ
diff --git a/src/assets/terminal/arbitrary-branch-diff.png b/src/assets/terminal/arbitrary-branch-diff.png
new file mode 100644
index 0000000..c0aacdd
Binary files /dev/null and b/src/assets/terminal/arbitrary-branch-diff.png differ
diff --git a/src/assets/terminal/attach-diff-hunk-as-context.png b/src/assets/terminal/attach-diff-hunk-as-context.png
new file mode 100644
index 0000000..d7bca69
Binary files /dev/null and b/src/assets/terminal/attach-diff-hunk-as-context.png differ
diff --git a/src/assets/terminal/aurora-icon.png b/src/assets/terminal/aurora-icon.png
new file mode 100644
index 0000000..ae67114
Binary files /dev/null and b/src/assets/terminal/aurora-icon.png differ
diff --git a/src/assets/terminal/auto-detection-agent-mode-1.png b/src/assets/terminal/auto-detection-agent-mode-1.png
new file mode 100644
index 0000000..3004058
Binary files /dev/null and b/src/assets/terminal/auto-detection-agent-mode-1.png differ
diff --git a/src/assets/terminal/auto-detection-off-agent-mode.png b/src/assets/terminal/auto-detection-off-agent-mode.png
new file mode 100644
index 0000000..18af28c
Binary files /dev/null and b/src/assets/terminal/auto-detection-off-agent-mode.png differ
diff --git a/src/assets/terminal/auto-detection-off-terminal-mode.png b/src/assets/terminal/auto-detection-off-terminal-mode.png
new file mode 100644
index 0000000..3347460
Binary files /dev/null and b/src/assets/terminal/auto-detection-off-terminal-mode.png differ
diff --git a/src/assets/terminal/auto-detection-terminal-mode.png b/src/assets/terminal/auto-detection-terminal-mode.png
new file mode 100644
index 0000000..4a1d11d
Binary files /dev/null and b/src/assets/terminal/auto-detection-terminal-mode.png differ
diff --git a/src/assets/terminal/banner_for_auto-detection_first_experience.png b/src/assets/terminal/banner_for_auto-detection_first_experience.png
new file mode 100644
index 0000000..33884cb
Binary files /dev/null and b/src/assets/terminal/banner_for_auto-detection_first_experience.png differ
diff --git a/src/assets/terminal/classic1-icon.png b/src/assets/terminal/classic1-icon.png
new file mode 100644
index 0000000..8a3c52e
Binary files /dev/null and b/src/assets/terminal/classic1-icon.png differ
diff --git a/src/assets/terminal/classic2-icon.png b/src/assets/terminal/classic2-icon.png
new file mode 100644
index 0000000..6a59c1f
Binary files /dev/null and b/src/assets/terminal/classic2-icon.png differ
diff --git a/src/assets/terminal/classic3-icon.png b/src/assets/terminal/classic3-icon.png
new file mode 100644
index 0000000..555ebd0
Binary files /dev/null and b/src/assets/terminal/classic3-icon.png differ
diff --git a/src/assets/terminal/code-review-header.png b/src/assets/terminal/code-review-header.png
new file mode 100644
index 0000000..07c81ea
Binary files /dev/null and b/src/assets/terminal/code-review-header.png differ
diff --git a/src/assets/terminal/code-review-panel-update.png b/src/assets/terminal/code-review-panel-update.png
new file mode 100644
index 0000000..380b08f
Binary files /dev/null and b/src/assets/terminal/code-review-panel-update.png differ
diff --git a/src/assets/terminal/code_mode.png b/src/assets/terminal/code_mode.png
new file mode 100644
index 0000000..a4cf0a1
Binary files /dev/null and b/src/assets/terminal/code_mode.png differ
diff --git a/src/assets/terminal/comets-icon.png b/src/assets/terminal/comets-icon.png
new file mode 100644
index 0000000..0403858
Binary files /dev/null and b/src/assets/terminal/comets-icon.png differ
diff --git a/src/assets/terminal/command-history-rich.png b/src/assets/terminal/command-history-rich.png
new file mode 100644
index 0000000..abf97bc
Binary files /dev/null and b/src/assets/terminal/command-history-rich.png differ
diff --git a/src/assets/terminal/command-palette-panel.png b/src/assets/terminal/command-palette-panel.png
new file mode 100644
index 0000000..4c11592
Binary files /dev/null and b/src/assets/terminal/command-palette-panel.png differ
diff --git a/src/assets/terminal/command-search-panel.png b/src/assets/terminal/command-search-panel.png
new file mode 100644
index 0000000..0416002
Binary files /dev/null and b/src/assets/terminal/command-search-panel.png differ
diff --git a/src/assets/terminal/conversation-management-chip-universal-input.png b/src/assets/terminal/conversation-management-chip-universal-input.png
new file mode 100644
index 0000000..e6aebd7
Binary files /dev/null and b/src/assets/terminal/conversation-management-chip-universal-input.png differ
diff --git a/src/assets/terminal/custom-dock-icon-dropdown.png b/src/assets/terminal/custom-dock-icon-dropdown.png
new file mode 100644
index 0000000..e6181d5
Binary files /dev/null and b/src/assets/terminal/custom-dock-icon-dropdown.png differ
diff --git a/src/assets/terminal/cyber-wave.png b/src/assets/terminal/cyber-wave.png
new file mode 100644
index 0000000..2016bed
Binary files /dev/null and b/src/assets/terminal/cyber-wave.png differ
diff --git a/src/assets/terminal/dark-city.png b/src/assets/terminal/dark-city.png
new file mode 100644
index 0000000..17befdf
Binary files /dev/null and b/src/assets/terminal/dark-city.png differ
diff --git a/src/assets/terminal/default-icon.png b/src/assets/terminal/default-icon.png
new file mode 100644
index 0000000..5cd5e81
Binary files /dev/null and b/src/assets/terminal/default-icon.png differ
diff --git a/src/assets/terminal/diff-dropdown-to-change-base-from-the-code-review-pane.png b/src/assets/terminal/diff-dropdown-to-change-base-from-the-code-review-pane.png
new file mode 100644
index 0000000..067d344
Binary files /dev/null and b/src/assets/terminal/diff-dropdown-to-change-base-from-the-code-review-pane.png differ
diff --git a/src/assets/terminal/discard-all-changes.png b/src/assets/terminal/discard-all-changes.png
new file mode 100644
index 0000000..ee48079
Binary files /dev/null and b/src/assets/terminal/discard-all-changes.png differ
diff --git a/src/assets/terminal/docker-extension.png b/src/assets/terminal/docker-extension.png
new file mode 100644
index 0000000..77ea093
Binary files /dev/null and b/src/assets/terminal/docker-extension.png differ
diff --git a/src/assets/terminal/dracula.png b/src/assets/terminal/dracula.png
new file mode 100644
index 0000000..1e0219a
Binary files /dev/null and b/src/assets/terminal/dracula.png differ
diff --git a/src/assets/terminal/edit-prompt-modal.png b/src/assets/terminal/edit-prompt-modal.png
new file mode 100644
index 0000000..8ed66a0
Binary files /dev/null and b/src/assets/terminal/edit-prompt-modal.png differ
diff --git a/src/assets/terminal/edit-workflow-pane.png b/src/assets/terminal/edit-workflow-pane.png
new file mode 100644
index 0000000..ac0ba78
Binary files /dev/null and b/src/assets/terminal/edit-workflow-pane.png differ
diff --git a/src/assets/terminal/embed.png b/src/assets/terminal/embed.png
new file mode 100644
index 0000000..d9229fc
Binary files /dev/null and b/src/assets/terminal/embed.png differ
diff --git a/src/assets/terminal/embedding-a-workflow.png b/src/assets/terminal/embedding-a-workflow.png
new file mode 100644
index 0000000..fd9bc4d
Binary files /dev/null and b/src/assets/terminal/embedding-a-workflow.png differ
diff --git a/src/assets/terminal/env-var-create.png b/src/assets/terminal/env-var-create.png
new file mode 100644
index 0000000..6003162
Binary files /dev/null and b/src/assets/terminal/env-var-create.png differ
diff --git a/src/assets/terminal/env-var-dynamic-variables.png b/src/assets/terminal/env-var-dynamic-variables.png
new file mode 100644
index 0000000..a21c85e
Binary files /dev/null and b/src/assets/terminal/env-var-dynamic-variables.png differ
diff --git a/src/assets/terminal/env-var-load-in-session.png b/src/assets/terminal/env-var-load-in-session.png
new file mode 100644
index 0000000..1700f9b
Binary files /dev/null and b/src/assets/terminal/env-var-load-in-session.png differ
diff --git a/src/assets/terminal/env-var-load-to-input-1.png b/src/assets/terminal/env-var-load-to-input-1.png
new file mode 100644
index 0000000..72be659
Binary files /dev/null and b/src/assets/terminal/env-var-load-to-input-1.png differ
diff --git a/src/assets/terminal/env-var-password-mgrs.png b/src/assets/terminal/env-var-password-mgrs.png
new file mode 100644
index 0000000..a2842e0
Binary files /dev/null and b/src/assets/terminal/env-var-password-mgrs.png differ
diff --git a/src/assets/terminal/env-var-static-variable-load.png b/src/assets/terminal/env-var-static-variable-load.png
new file mode 100644
index 0000000..84d2941
Binary files /dev/null and b/src/assets/terminal/env-var-static-variable-load.png differ
diff --git a/src/assets/terminal/env-var-static-variable-save.png b/src/assets/terminal/env-var-static-variable-save.png
new file mode 100644
index 0000000..9d2ebaf
Binary files /dev/null and b/src/assets/terminal/env-var-static-variable-save.png differ
diff --git a/src/assets/terminal/execute-a-workflow.png b/src/assets/terminal/execute-a-workflow.png
new file mode 100644
index 0000000..b41e3c7
Binary files /dev/null and b/src/assets/terminal/execute-a-workflow.png differ
diff --git a/src/assets/terminal/fancy-dracula.png b/src/assets/terminal/fancy-dracula.png
new file mode 100644
index 0000000..625611e
Binary files /dev/null and b/src/assets/terminal/fancy-dracula.png differ
diff --git a/src/assets/terminal/file-tree-context-menu.png b/src/assets/terminal/file-tree-context-menu.png
new file mode 100644
index 0000000..6a53ac2
Binary files /dev/null and b/src/assets/terminal/file-tree-context-menu.png differ
diff --git a/src/assets/terminal/file-tree-new-file.png b/src/assets/terminal/file-tree-new-file.png
new file mode 100644
index 0000000..af5ce16
Binary files /dev/null and b/src/assets/terminal/file-tree-new-file.png differ
diff --git a/src/assets/terminal/file-tree-project-explorer-tools-panel.png b/src/assets/terminal/file-tree-project-explorer-tools-panel.png
new file mode 100644
index 0000000..b0938a3
Binary files /dev/null and b/src/assets/terminal/file-tree-project-explorer-tools-panel.png differ
diff --git a/src/assets/terminal/filetree-main.png b/src/assets/terminal/filetree-main.png
new file mode 100644
index 0000000..39320c4
Binary files /dev/null and b/src/assets/terminal/filetree-main.png differ
diff --git a/src/assets/terminal/git-branch-chip.png b/src/assets/terminal/git-branch-chip.png
new file mode 100644
index 0000000..e0404cd
Binary files /dev/null and b/src/assets/terminal/git-branch-chip.png differ
diff --git a/src/assets/terminal/git-chip-tooltip-1.png b/src/assets/terminal/git-chip-tooltip-1.png
new file mode 100644
index 0000000..cf1d23a
Binary files /dev/null and b/src/assets/terminal/git-chip-tooltip-1.png differ
diff --git a/src/assets/terminal/git-diff-change-base-dropdown.png b/src/assets/terminal/git-diff-change-base-dropdown.png
new file mode 100644
index 0000000..1e44143
Binary files /dev/null and b/src/assets/terminal/git-diff-change-base-dropdown.png differ
diff --git a/src/assets/terminal/glass-sky-icon.png b/src/assets/terminal/glass-sky-icon.png
new file mode 100644
index 0000000..6ede70b
Binary files /dev/null and b/src/assets/terminal/glass-sky-icon.png differ
diff --git a/src/assets/terminal/glitch-icon.png b/src/assets/terminal/glitch-icon.png
new file mode 100644
index 0000000..5864365
Binary files /dev/null and b/src/assets/terminal/glitch-icon.png differ
diff --git a/src/assets/terminal/glow-icon.png b/src/assets/terminal/glow-icon.png
new file mode 100644
index 0000000..6652a65
Binary files /dev/null and b/src/assets/terminal/glow-icon.png differ
diff --git a/src/assets/terminal/gruvbox-dark.png b/src/assets/terminal/gruvbox-dark.png
new file mode 100644
index 0000000..f6e839e
Binary files /dev/null and b/src/assets/terminal/gruvbox-dark.png differ
diff --git a/src/assets/terminal/gruvbox-light.png b/src/assets/terminal/gruvbox-light.png
new file mode 100644
index 0000000..94f2d3b
Binary files /dev/null and b/src/assets/terminal/gruvbox-light.png differ
diff --git a/src/assets/terminal/holographic-icon.png b/src/assets/terminal/holographic-icon.png
new file mode 100644
index 0000000..d42a9b0
Binary files /dev/null and b/src/assets/terminal/holographic-icon.png differ
diff --git a/src/assets/terminal/images-as-context-chip.png b/src/assets/terminal/images-as-context-chip.png
new file mode 100644
index 0000000..7d042eb
Binary files /dev/null and b/src/assets/terminal/images-as-context-chip.png differ
diff --git a/src/assets/terminal/input-toolbar.png b/src/assets/terminal/input-toolbar.png
new file mode 100644
index 0000000..355d68a
Binary files /dev/null and b/src/assets/terminal/input-toolbar.png differ
diff --git a/src/assets/terminal/jellyfish.png b/src/assets/terminal/jellyfish.png
new file mode 100644
index 0000000..d42e247
Binary files /dev/null and b/src/assets/terminal/jellyfish.png differ
diff --git a/src/assets/terminal/keybinds-conflict.png b/src/assets/terminal/keybinds-conflict.png
new file mode 100644
index 0000000..b1deaff
Binary files /dev/null and b/src/assets/terminal/keybinds-conflict.png differ
diff --git a/src/assets/terminal/koi.png b/src/assets/terminal/koi.png
new file mode 100644
index 0000000..d7f6bf7
Binary files /dev/null and b/src/assets/terminal/koi.png differ
diff --git a/src/assets/terminal/leafy.png b/src/assets/terminal/leafy.png
new file mode 100644
index 0000000..70e5bf4
Binary files /dev/null and b/src/assets/terminal/leafy.png differ
diff --git a/src/assets/terminal/marble.png b/src/assets/terminal/marble.png
new file mode 100644
index 0000000..09d390f
Binary files /dev/null and b/src/assets/terminal/marble.png differ
diff --git a/src/assets/terminal/markdown-element-types.png b/src/assets/terminal/markdown-element-types.png
new file mode 100644
index 0000000..bace71e
Binary files /dev/null and b/src/assets/terminal/markdown-element-types.png differ
diff --git a/src/assets/terminal/migrate-to-warp.png b/src/assets/terminal/migrate-to-warp.png
new file mode 100644
index 0000000..355fb38
Binary files /dev/null and b/src/assets/terminal/migrate-to-warp.png differ
diff --git a/src/assets/terminal/mono-icon.png b/src/assets/terminal/mono-icon.png
new file mode 100644
index 0000000..42acf2b
Binary files /dev/null and b/src/assets/terminal/mono-icon.png differ
diff --git a/src/assets/terminal/moody-dev-default-icon.png b/src/assets/terminal/moody-dev-default-icon.png
new file mode 100644
index 0000000..52bddd7
Binary files /dev/null and b/src/assets/terminal/moody-dev-default-icon.png differ
diff --git a/src/assets/terminal/multi-agents.png b/src/assets/terminal/multi-agents.png
new file mode 100644
index 0000000..f5f53cc
Binary files /dev/null and b/src/assets/terminal/multi-agents.png differ
diff --git a/src/assets/terminal/neon-icon.png b/src/assets/terminal/neon-icon.png
new file mode 100644
index 0000000..d593827
Binary files /dev/null and b/src/assets/terminal/neon-icon.png differ
diff --git a/src/assets/terminal/node-version-chip.png b/src/assets/terminal/node-version-chip.png
new file mode 100644
index 0000000..70bfe90
Binary files /dev/null and b/src/assets/terminal/node-version-chip.png differ
diff --git a/src/assets/terminal/notebook-cmd-block-run.png b/src/assets/terminal/notebook-cmd-block-run.png
new file mode 100644
index 0000000..a6dd0c1
Binary files /dev/null and b/src/assets/terminal/notebook-cmd-block-run.png differ
diff --git a/src/assets/terminal/notebook-cmd-with-params.png b/src/assets/terminal/notebook-cmd-with-params.png
new file mode 100644
index 0000000..7dd0793
Binary files /dev/null and b/src/assets/terminal/notebook-cmd-with-params.png differ
diff --git a/src/assets/terminal/notebook-code-block.png b/src/assets/terminal/notebook-code-block.png
new file mode 100644
index 0000000..74d6ea0
Binary files /dev/null and b/src/assets/terminal/notebook-code-block.png differ
diff --git a/src/assets/terminal/notebook-import-modal.png b/src/assets/terminal/notebook-import-modal.png
new file mode 100644
index 0000000..74cb6c7
Binary files /dev/null and b/src/assets/terminal/notebook-import-modal.png differ
diff --git a/src/assets/terminal/notebook-view-mode.png b/src/assets/terminal/notebook-view-mode.png
new file mode 100644
index 0000000..f50062b
Binary files /dev/null and b/src/assets/terminal/notebook-view-mode.png differ
diff --git a/src/assets/terminal/original-icon.png b/src/assets/terminal/original-icon.png
new file mode 100644
index 0000000..bd37029
Binary files /dev/null and b/src/assets/terminal/original-icon.png differ
diff --git a/src/assets/terminal/p10k-grey-arrow-prompt.png b/src/assets/terminal/p10k-grey-arrow-prompt.png
new file mode 100644
index 0000000..3a6ac0a
Binary files /dev/null and b/src/assets/terminal/p10k-grey-arrow-prompt.png differ
diff --git a/src/assets/terminal/padding-settings.png b/src/assets/terminal/padding-settings.png
new file mode 100644
index 0000000..f389a59
Binary files /dev/null and b/src/assets/terminal/padding-settings.png differ
diff --git a/src/assets/terminal/phenomenon.png b/src/assets/terminal/phenomenon.png
new file mode 100644
index 0000000..d94d4ec
Binary files /dev/null and b/src/assets/terminal/phenomenon.png differ
diff --git a/src/assets/terminal/pink-city.png b/src/assets/terminal/pink-city.png
new file mode 100644
index 0000000..c9c84d0
Binary files /dev/null and b/src/assets/terminal/pink-city.png differ
diff --git a/src/assets/terminal/prompts-command-palette.png b/src/assets/terminal/prompts-command-palette.png
new file mode 100644
index 0000000..390738e
Binary files /dev/null and b/src/assets/terminal/prompts-command-palette.png differ
diff --git a/src/assets/terminal/prompts-command-search.png b/src/assets/terminal/prompts-command-search.png
new file mode 100644
index 0000000..b88aa05
Binary files /dev/null and b/src/assets/terminal/prompts-command-search.png differ
diff --git a/src/assets/terminal/prompts-command-view.png b/src/assets/terminal/prompts-command-view.png
new file mode 100644
index 0000000..4a7a759
Binary files /dev/null and b/src/assets/terminal/prompts-command-view.png differ
diff --git a/src/assets/terminal/prompts-edit-view.png b/src/assets/terminal/prompts-edit-view.png
new file mode 100644
index 0000000..b5f0aa5
Binary files /dev/null and b/src/assets/terminal/prompts-edit-view.png differ
diff --git a/src/assets/terminal/raycast-terminal-tip.png b/src/assets/terminal/raycast-terminal-tip.png
new file mode 100644
index 0000000..3d1d420
Binary files /dev/null and b/src/assets/terminal/raycast-terminal-tip.png differ
diff --git a/src/assets/terminal/rectangular-selection.png b/src/assets/terminal/rectangular-selection.png
new file mode 100644
index 0000000..98d2d2b
Binary files /dev/null and b/src/assets/terminal/rectangular-selection.png differ
diff --git a/src/assets/terminal/red-rock.png b/src/assets/terminal/red-rock.png
new file mode 100644
index 0000000..8cb7c11
Binary files /dev/null and b/src/assets/terminal/red-rock.png differ
diff --git a/src/assets/terminal/revert-diff-hunk.png b/src/assets/terminal/revert-diff-hunk.png
new file mode 100644
index 0000000..f53f691
Binary files /dev/null and b/src/assets/terminal/revert-diff-hunk.png differ
diff --git a/src/assets/terminal/review-changes-in-footer.png b/src/assets/terminal/review-changes-in-footer.png
new file mode 100644
index 0000000..593ea47
Binary files /dev/null and b/src/assets/terminal/review-changes-in-footer.png differ
diff --git a/src/assets/terminal/save-new-tab-config.png b/src/assets/terminal/save-new-tab-config.png
new file mode 100644
index 0000000..615f861
Binary files /dev/null and b/src/assets/terminal/save-new-tab-config.png differ
diff --git a/src/assets/terminal/saved-tab-config-menu.png b/src/assets/terminal/saved-tab-config-menu.png
new file mode 100644
index 0000000..c4f05e7
Binary files /dev/null and b/src/assets/terminal/saved-tab-config-menu.png differ
diff --git a/src/assets/terminal/search-files-icon.png b/src/assets/terminal/search-files-icon.png
new file mode 100644
index 0000000..68913a0
Binary files /dev/null and b/src/assets/terminal/search-files-icon.png differ
diff --git a/src/assets/terminal/search-workflow-command-palette.png b/src/assets/terminal/search-workflow-command-palette.png
new file mode 100644
index 0000000..375626c
Binary files /dev/null and b/src/assets/terminal/search-workflow-command-palette.png differ
diff --git a/src/assets/terminal/settings-not-synced.png b/src/assets/terminal/settings-not-synced.png
new file mode 100644
index 0000000..697734a
Binary files /dev/null and b/src/assets/terminal/settings-not-synced.png differ
diff --git a/src/assets/terminal/settings-sync-account.png b/src/assets/terminal/settings-sync-account.png
new file mode 100644
index 0000000..6629864
Binary files /dev/null and b/src/assets/terminal/settings-sync-account.png differ
diff --git a/src/assets/terminal/settings-sync-palette.png b/src/assets/terminal/settings-sync-palette.png
new file mode 100644
index 0000000..15ccabe
Binary files /dev/null and b/src/assets/terminal/settings-sync-palette.png differ
diff --git a/src/assets/terminal/shared_block.png b/src/assets/terminal/shared_block.png
new file mode 100644
index 0000000..221d43e
Binary files /dev/null and b/src/assets/terminal/shared_block.png differ
diff --git a/src/assets/terminal/smart-selection.png b/src/assets/terminal/smart-selection.png
new file mode 100644
index 0000000..193352c
Binary files /dev/null and b/src/assets/terminal/smart-selection.png differ
diff --git a/src/assets/terminal/snowy.png b/src/assets/terminal/snowy.png
new file mode 100644
index 0000000..aeb0783
Binary files /dev/null and b/src/assets/terminal/snowy.png differ
diff --git a/src/assets/terminal/soft-wrapping.png b/src/assets/terminal/soft-wrapping.png
new file mode 100644
index 0000000..e80844a
Binary files /dev/null and b/src/assets/terminal/soft-wrapping.png differ
diff --git a/src/assets/terminal/solar-flare.png b/src/assets/terminal/solar-flare.png
new file mode 100644
index 0000000..be7d03d
Binary files /dev/null and b/src/assets/terminal/solar-flare.png differ
diff --git a/src/assets/terminal/solarized-dark.png b/src/assets/terminal/solarized-dark.png
new file mode 100644
index 0000000..67eab27
Binary files /dev/null and b/src/assets/terminal/solarized-dark.png differ
diff --git a/src/assets/terminal/solarized-light.png b/src/assets/terminal/solarized-light.png
new file mode 100644
index 0000000..b039867
Binary files /dev/null and b/src/assets/terminal/solarized-light.png differ
diff --git a/src/assets/terminal/ssh-1.png b/src/assets/terminal/ssh-1.png
new file mode 100644
index 0000000..d1dfbe0
Binary files /dev/null and b/src/assets/terminal/ssh-1.png differ
diff --git a/src/assets/terminal/starburst-icon.png b/src/assets/terminal/starburst-icon.png
new file mode 100644
index 0000000..6c51867
Binary files /dev/null and b/src/assets/terminal/starburst-icon.png differ
diff --git a/src/assets/terminal/sticker-icon.png b/src/assets/terminal/sticker-icon.png
new file mode 100644
index 0000000..c4485a4
Binary files /dev/null and b/src/assets/terminal/sticker-icon.png differ
diff --git a/src/assets/terminal/styling-menu.png b/src/assets/terminal/styling-menu.png
new file mode 100644
index 0000000..a44f76d
Binary files /dev/null and b/src/assets/terminal/styling-menu.png differ
diff --git a/src/assets/terminal/tab-hover-detail-card.png b/src/assets/terminal/tab-hover-detail-card.png
new file mode 100644
index 0000000..cf0ae7f
Binary files /dev/null and b/src/assets/terminal/tab-hover-detail-card.png differ
diff --git a/src/assets/terminal/tabbed-file-viewer.png b/src/assets/terminal/tabbed-file-viewer.png
new file mode 100644
index 0000000..30d23d4
Binary files /dev/null and b/src/assets/terminal/tabbed-file-viewer.png differ
diff --git a/src/assets/terminal/team-creation-settings.png b/src/assets/terminal/team-creation-settings.png
new file mode 100644
index 0000000..912c5f1
Binary files /dev/null and b/src/assets/terminal/team-creation-settings.png differ
diff --git a/src/assets/terminal/teams-invite-demo.png b/src/assets/terminal/teams-invite-demo.png
new file mode 100644
index 0000000..b8ce320
Binary files /dev/null and b/src/assets/terminal/teams-invite-demo.png differ
diff --git a/src/assets/terminal/termbench_regular.png b/src/assets/terminal/termbench_regular.png
new file mode 100644
index 0000000..fed348f
Binary files /dev/null and b/src/assets/terminal/termbench_regular.png differ
diff --git a/src/assets/terminal/termbench_small.png b/src/assets/terminal/termbench_small.png
new file mode 100644
index 0000000..f9dfb02
Binary files /dev/null and b/src/assets/terminal/termbench_small.png differ
diff --git a/src/assets/terminal/undo_my_git_commit.png b/src/assets/terminal/undo_my_git_commit.png
new file mode 100644
index 0000000..f886e83
Binary files /dev/null and b/src/assets/terminal/undo_my_git_commit.png differ
diff --git a/src/assets/terminal/universal-input-header.png b/src/assets/terminal/universal-input-header.png
new file mode 100644
index 0000000..766a60f
Binary files /dev/null and b/src/assets/terminal/universal-input-header.png differ
diff --git a/src/assets/terminal/universal-input-terminal-mode.png b/src/assets/terminal/universal-input-terminal-mode.png
new file mode 100644
index 0000000..333abe5
Binary files /dev/null and b/src/assets/terminal/universal-input-terminal-mode.png differ
diff --git a/src/assets/terminal/universal-input.png b/src/assets/terminal/universal-input.png
new file mode 100644
index 0000000..d17dca1
Binary files /dev/null and b/src/assets/terminal/universal-input.png differ
diff --git a/src/assets/terminal/using-agents-universal-input.png b/src/assets/terminal/using-agents-universal-input.png
new file mode 100644
index 0000000..459e3d0
Binary files /dev/null and b/src/assets/terminal/using-agents-universal-input.png differ
diff --git a/src/assets/terminal/vertical-tab-settings.png b/src/assets/terminal/vertical-tab-settings.png
new file mode 100644
index 0000000..85e7775
Binary files /dev/null and b/src/assets/terminal/vertical-tab-settings.png differ
diff --git a/src/assets/terminal/vertical-tabs-condensed.png b/src/assets/terminal/vertical-tabs-condensed.png
new file mode 100644
index 0000000..81210d2
Binary files /dev/null and b/src/assets/terminal/vertical-tabs-condensed.png differ
diff --git a/src/assets/terminal/vertical-tabs-multi-agent.png b/src/assets/terminal/vertical-tabs-multi-agent.png
new file mode 100644
index 0000000..c1433bf
Binary files /dev/null and b/src/assets/terminal/vertical-tabs-multi-agent.png differ
diff --git a/src/assets/terminal/vtebench_avg.png b/src/assets/terminal/vtebench_avg.png
new file mode 100644
index 0000000..4fd6246
Binary files /dev/null and b/src/assets/terminal/vtebench_avg.png differ
diff --git a/src/assets/terminal/vtebench_p90.png b/src/assets/terminal/vtebench_p90.png
new file mode 100644
index 0000000..2ff8b63
Binary files /dev/null and b/src/assets/terminal/vtebench_p90.png differ
diff --git a/src/assets/terminal/warp-ai-permissions.png b/src/assets/terminal/warp-ai-permissions.png
new file mode 100644
index 0000000..6872236
Binary files /dev/null and b/src/assets/terminal/warp-ai-permissions.png differ
diff --git a/src/assets/terminal/warp-ai-viewing-commands.png b/src/assets/terminal/warp-ai-viewing-commands.png
new file mode 100644
index 0000000..85e9e1e
Binary files /dev/null and b/src/assets/terminal/warp-ai-viewing-commands.png differ
diff --git a/src/assets/terminal/warp-dark.png b/src/assets/terminal/warp-dark.png
new file mode 100644
index 0000000..72c990c
Binary files /dev/null and b/src/assets/terminal/warp-dark.png differ
diff --git a/src/assets/terminal/warp-light.png b/src/assets/terminal/warp-light.png
new file mode 100644
index 0000000..b0abfd1
Binary files /dev/null and b/src/assets/terminal/warp-light.png differ
diff --git a/src/assets/terminal/warp-oz-welcome.png b/src/assets/terminal/warp-oz-welcome.png
new file mode 100644
index 0000000..6224a45
Binary files /dev/null and b/src/assets/terminal/warp-oz-welcome.png differ
diff --git a/src/assets/terminal/warp_drive_nav1.png b/src/assets/terminal/warp_drive_nav1.png
new file mode 100644
index 0000000..b270a22
Binary files /dev/null and b/src/assets/terminal/warp_drive_nav1.png differ
diff --git a/src/assets/terminal/warp_drive_nav2.png b/src/assets/terminal/warp_drive_nav2.png
new file mode 100644
index 0000000..c471c8a
Binary files /dev/null and b/src/assets/terminal/warp_drive_nav2.png differ
diff --git a/src/assets/terminal/warp_drive_offline.png b/src/assets/terminal/warp_drive_offline.png
new file mode 100644
index 0000000..09dfac9
Binary files /dev/null and b/src/assets/terminal/warp_drive_offline.png differ
diff --git a/src/assets/terminal/warpify_ssh_auto_script.png b/src/assets/terminal/warpify_ssh_auto_script.png
new file mode 100644
index 0000000..b2a6821
Binary files /dev/null and b/src/assets/terminal/warpify_ssh_auto_script.png differ
diff --git a/src/assets/terminal/warpify_ssh_prompt.png b/src/assets/terminal/warpify_ssh_prompt.png
new file mode 100644
index 0000000..d750cd2
Binary files /dev/null and b/src/assets/terminal/warpify_ssh_prompt.png differ
diff --git a/src/assets/terminal/wd-copy-link-menu.png b/src/assets/terminal/wd-copy-link-menu.png
new file mode 100644
index 0000000..cf376fa
Binary files /dev/null and b/src/assets/terminal/wd-copy-link-menu.png differ
diff --git a/src/assets/terminal/wd-open-links-preference.png b/src/assets/terminal/wd-open-links-preference.png
new file mode 100644
index 0000000..b94827a
Binary files /dev/null and b/src/assets/terminal/wd-open-links-preference.png differ
diff --git a/src/assets/terminal/wd-popup-message.png b/src/assets/terminal/wd-popup-message.png
new file mode 100644
index 0000000..6c15d2a
Binary files /dev/null and b/src/assets/terminal/wd-popup-message.png differ
diff --git a/src/assets/terminal/wd-share-button.png b/src/assets/terminal/wd-share-button.png
new file mode 100644
index 0000000..f914bba
Binary files /dev/null and b/src/assets/terminal/wd-share-button.png differ
diff --git a/src/assets/terminal/wd-share-dialog.png b/src/assets/terminal/wd-share-dialog.png
new file mode 100644
index 0000000..6b2434f
Binary files /dev/null and b/src/assets/terminal/wd-share-dialog.png differ
diff --git a/src/assets/terminal/wd-switch-viewer.png b/src/assets/terminal/wd-switch-viewer.png
new file mode 100644
index 0000000..2d8b447
Binary files /dev/null and b/src/assets/terminal/wd-switch-viewer.png differ
diff --git a/src/assets/terminal/wd-view-on-web.png b/src/assets/terminal/wd-view-on-web.png
new file mode 100644
index 0000000..43d213d
Binary files /dev/null and b/src/assets/terminal/wd-view-on-web.png differ
diff --git a/src/assets/terminal/wd-web-team-workflow.png b/src/assets/terminal/wd-web-team-workflow.png
new file mode 100644
index 0000000..52d2100
Binary files /dev/null and b/src/assets/terminal/wd-web-team-workflow.png differ
diff --git a/src/assets/terminal/whole-UDI-bar.png b/src/assets/terminal/whole-UDI-bar.png
new file mode 100644
index 0000000..7c440f8
Binary files /dev/null and b/src/assets/terminal/whole-UDI-bar.png differ
diff --git a/src/assets/terminal/whole-git-diff-view-with-one-file-collapsed.png b/src/assets/terminal/whole-git-diff-view-with-one-file-collapsed.png
new file mode 100644
index 0000000..6b0cfb0
Binary files /dev/null and b/src/assets/terminal/whole-git-diff-view-with-one-file-collapsed.png differ
diff --git a/src/assets/terminal/willow-dream.png b/src/assets/terminal/willow-dream.png
new file mode 100644
index 0000000..aab7a9f
Binary files /dev/null and b/src/assets/terminal/willow-dream.png differ
diff --git a/src/assets/terminal/zero-state-universal-input.png b/src/assets/terminal/zero-state-universal-input.png
new file mode 100644
index 0000000..d5dcbeb
Binary files /dev/null and b/src/assets/terminal/zero-state-universal-input.png differ
diff --git a/src/assets/warp-logo-dark.svg b/src/assets/warp-logo-dark.svg
new file mode 100644
index 0000000..9cc17ea
--- /dev/null
+++ b/src/assets/warp-logo-dark.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/assets/warp-logo-light.svg b/src/assets/warp-logo-light.svg
new file mode 100644
index 0000000..324ef18
--- /dev/null
+++ b/src/assets/warp-logo-light.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/components/CopyPageButton.astro b/src/components/CopyPageButton.astro
new file mode 100644
index 0000000..71cb7ed
--- /dev/null
+++ b/src/components/CopyPageButton.astro
@@ -0,0 +1,384 @@
+---
+/**
+ * CopyPageButton — Scalar-style dropdown with:
+ * - Copy page (copies raw MDX body as markdown)
+ * - View as Markdown (opens .md URL in new tab)
+ * - Export as PDF (window.print)
+ */
+interface Props {
+ body: string;
+ title: string;
+}
+const { body, title } = Astro.props;
+const markdownContent = `# ${title}\n\n${body}`;
+const pageUrl = Astro.url.href;
+const prompt = encodeURIComponent(`Read ${pageUrl}. I want to ask questions about it.`);
+const chatgptUrl = `https://chatgpt.com/?q=${prompt}`;
+const claudeUrl = `https://claude.ai/new?q=${prompt}`;
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Open in ChatGPT ↗
+ Ask ChatGPT about this page
+
+
+
+
+ {/* Official Claude (Anthropic) symbol — stylized starburst mark.
+ Source: simple-icons (https://github.com/simple-icons/simple-icons/blob/develop/icons/claude.svg).
+ Trademark of Anthropic; used here as a small in-line link affordance. */}
+
+
+ Open in Claude ↗
+ Ask Claude about this page
+
+
+
+
+
+
+
+
+ Copied!
+
+
+
+{markdownContent}
+
+
+
+
diff --git a/src/components/CustomHead.astro b/src/components/CustomHead.astro
new file mode 100644
index 0000000..d039447
--- /dev/null
+++ b/src/components/CustomHead.astro
@@ -0,0 +1,50 @@
+---
+import Default from '@astrojs/starlight/components/Head.astro';
+import Analytics from '@vercel/analytics/astro';
+import SpeedInsights from '@vercel/speed-insights/astro';
+import RudderStackAnalytics from './RudderStackAnalytics.astro';
+
+// Note: ` ` (Astro View Transitions) is intentionally NOT
+// enabled here. Starlight's sidebar scroll persistence relies on the browser
+// `pagehide` event to save `sessionStorage.sl-sidebar-state` and on inline
+// `
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/CustomHeader.astro b/src/components/CustomHeader.astro
new file mode 100644
index 0000000..59eac2e
--- /dev/null
+++ b/src/components/CustomHeader.astro
@@ -0,0 +1,79 @@
+---
+// Custom Header override.
+//
+// Three-region layout: SiteTitle (left) | WarpTopicNav (middle) |
+// SocialIcons + ThemeSelect + LanguageSelect (right).
+//
+// Search + Ask AI live inside the sidebar (Scalar pattern, see
+// CustomSidebar.astro). The middle region carries the topic tabs (Terminal,
+// Agents, Reference, etc.) as a horizontal nav (see WarpTopicNav.astro) —
+// previously these lived in the sidebar but were moved up here for a more
+// app-like top-level navigation.
+//
+// Default at: node_modules/@astrojs/starlight/components/Header.astro
+import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
+import SiteTitle from 'virtual:starlight/components/SiteTitle';
+import SocialIcons from 'virtual:starlight/components/SocialIcons';
+import ThemeSelect from 'virtual:starlight/components/ThemeSelect';
+import WarpTopicNav from './WarpTopicNav.astro';
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/CustomPageSidebar.astro b/src/components/CustomPageSidebar.astro
new file mode 100644
index 0000000..d215fdd
--- /dev/null
+++ b/src/components/CustomPageSidebar.astro
@@ -0,0 +1,139 @@
+---
+// Custom PageSidebar override.
+// Adds a sticky footer to the "On this page" panel with edit link,
+// community link, and last-updated timestamp. The footer pins to the
+// bottom of the fixed-height right sidebar on desktop.
+import MobileTableOfContents from 'virtual:starlight/components/MobileTableOfContents';
+import TableOfContents from 'virtual:starlight/components/TableOfContents';
+
+const { editUrl, lastUpdated, lang } = Astro.locals.starlightRoute;
+---
+
+{
+ Astro.locals.starlightRoute.toc && (
+ <>
+
+
+
+
+ >
+ )
+}
+
+
diff --git a/src/components/CustomPageTitle.astro b/src/components/CustomPageTitle.astro
new file mode 100644
index 0000000..fc93fe1
--- /dev/null
+++ b/src/components/CustomPageTitle.astro
@@ -0,0 +1,57 @@
+---
+import CopyPageButton from './CopyPageButton.astro';
+
+// Starlight types `entry.body` as `string | undefined`; fall back to ''
+// so consumers (CopyPageButton) get a stable `string`.
+const title = Astro.locals.starlightRoute.entry.data.title;
+// Surface the page's `description` frontmatter as a visible subtitle below
+// the H1. Starlight's default `PageTitle` only uses `description` for the
+// `` tag and OG/Twitter previews, so without this
+// every page is missing the editorial lede that GitBook used to render.
+// All 314 docs MDX files have a populated description today; the
+// `&& ` guard keeps any future page that omits the field from emitting
+// an empty paragraph.
+const description = Astro.locals.starlightRoute.entry.data.description;
+const body = Astro.locals.starlightRoute.entry.body ?? '';
+---
+
+
+ {title}
+
+
+{description && {description}
}
+
+
diff --git a/src/components/CustomSidebar.astro b/src/components/CustomSidebar.astro
new file mode 100644
index 0000000..8ded00a
--- /dev/null
+++ b/src/components/CustomSidebar.astro
@@ -0,0 +1,35 @@
+---
+// Custom Sidebar override.
+//
+// Scalar-style: search lives at the top of the sidebar, NOT in the top nav
+// (see CustomHeader.astro for the matching header change). The Kapa "Ask"
+// launcher sits beside the search pill on the same row, rendering as paired
+// chrome via the shared `--warp-control-*` palette in `warp-components.css`
+// §16. Below the row, the default Starlight sidebar nav renders with its
+// items already filtered to the current topic (the
+// `starlight-sidebar-topics` middleware rewrites `starlightRoute.sidebar`
+// upstream).
+//
+// Topics themselves — Terminal, Agents, Reference, etc. — are rendered as
+// horizontal links in the top header (see WarpTopicNav.astro). On narrow
+// viewports (<50rem) the header collapses, so we still render the plugin's
+// vertical topic list inside the mobile drawer via `md:sl-hidden` so users
+// keep a way to switch topics.
+//
+// Reuses Starlight's built-in ` ` directly. The ``
+// custom element binds `⌘K` to `window`, so the trigger button can live
+// anywhere in the DOM and the dialog (portaled to ``) keeps working.
+import Default from '@astrojs/starlight/components/Sidebar.astro';
+import Search from '@astrojs/starlight/components/Search.astro';
+import StarlightSidebarTopicsSidebar from 'starlight-sidebar-topics/components/Sidebar.astro';
+import KapaLauncher from './KapaLauncher.astro';
+---
+
+
+
+
+
+
diff --git a/src/components/CustomSiteTitle.astro b/src/components/CustomSiteTitle.astro
new file mode 100644
index 0000000..2d63138
--- /dev/null
+++ b/src/components/CustomSiteTitle.astro
@@ -0,0 +1,82 @@
+---
+// Custom SiteTitle override.
+//
+// Why this exists:
+// Starlight's default SiteTitle (`@astrojs/starlight/components/SiteTitle.astro`)
+// renders the logo as two `
` tags pointing at hashed SVG files (one for
+// light theme, one for dark). Because we have View Transitions disabled in
+// `CustomHead.astro` (so Starlight's sidebar scroll persistence keeps
+// working), every internal link is a full document navigation: the header is
+// torn down and rebuilt on each click. With `
`-based SVGs, the surrounding
+// header background paints a frame or two before the image decodes, which
+// reads as a flicker on every nav.
+//
+// This override inlines both SVGs directly into the HTML via Vite's `?raw`
+// import, so the logo paints in the same frame as the rest of the header. We
+// keep Starlight's `light:sl-hidden` / `dark:sl-hidden` toggle classes so the
+// light/dark switch keeps working with zero JS, and we keep the sr-only site
+// title for accessibility.
+import config from 'virtual:starlight/user-config';
+import logoDark from '../assets/warp-logo-dark.svg?raw';
+import logoLight from '../assets/warp-logo-light.svg?raw';
+
+// We deliberately ignore Starlight's `siteTitleHref` (which defaults to `/`)
+// and point the brand wordmark at warp.dev instead. Convention across the
+// product family: the logo is the marketing-site jump; navigation back to
+// docs home lives in the sidebar topic nav.
+const { siteTitle } = Astro.locals.starlightRoute;
+---
+
+
+
+
+
+ {siteTitle}
+
+
+
+
diff --git a/src/components/DemoVideo.astro b/src/components/DemoVideo.astro
new file mode 100644
index 0000000..b1bb6f1
--- /dev/null
+++ b/src/components/DemoVideo.astro
@@ -0,0 +1,36 @@
+---
+/**
+ * Renders the canonical autoplay demo video used throughout the docs.
+ *
+ * Each demo video has a matching JPEG poster in `public/assets/...` named
+ * `.poster.jpg` (generated by ffmpeg from the first frame). The
+ * component derives the poster URL automatically so callers only need to
+ * pass `src` and a descriptive `label`.
+ *
+ * Use `preload="none"` so off-screen videos don't consume bandwidth on
+ * page load. Browsers will still fetch and start playback once the
+ * `