{
    "id": "bhaskar/chrome-extension-page-summarizer",
    "kind": "plan",
    "title": "Chrome Extension: Summarise Any Page with Claude",
    "summary": "A Manifest V3 extension: click the icon, get a 5-bullet summary of the page you are reading. Bring-your-own API key, minimal permissions.",
    "author": "Admin",
    "stars": 0,
    "url": "https://ownlyplans.com/bhaskar/chrome-extension-page-summarizer/",
    "stack": [
        "Chrome Extension",
        "Claude API",
        "TypeScript"
    ],
    "tools": [
        "claude-code",
        "cursor"
    ],
    "difficulty": "beginner",
    "license": "cc-by-4.0",
    "version": "1.0",
    "overview": "A genuinely useful first extension, and a clean introduction to calling the Claude API. You will build the popup, read page text with the activeTab permission (so the extension cannot see anything until you click it), call the Messages API from the service worker and show the result.\r\n\r\nCosts are yours and small: you use your own Anthropic API key and pay per request.\r\n\r\nBefore you start: Chrome, Node.js 20+, an Anthropic API key and Claude Code or Cursor. About 90 minutes.",
    "steps": [
        {
            "title": "Scaffold the extension",
            "body": "Save CLAUDE.md in an empty folder. You will load the extension unpacked from a dist folder while you build.",
            "prompt": "Read CLAUDE.md. Create a Manifest V3 extension with Vite and TypeScript: manifest.json (name \"Page Summary\", action popup, background service worker, permissions activeTab + scripting + storage, host_permissions only for https://api.anthropic.com/*), popup.html with a placeholder, and a build that outputs to dist. Explain how to load it unpacked.",
            "done": "Chrome shows the extension and clicking its icon opens the placeholder popup.",
            "files": [
                "CLAUDE.md",
                "Chrome extensions docs"
            ]
        },
        {
            "title": "Settings: store the API key",
            "body": "The user pastes their key once. It is kept in chrome.storage.local on their machine only, and the popup shows a masked version so they know it is set.",
            "prompt": "Add an options page with one password field for the Anthropic API key, a Save button and a Remove button. Save to chrome.storage.local. In the popup, if no key is saved, show \"Add your API key to start\" with a button that opens the options page.",
            "done": "Saving a key changes the popup message; removing it brings the message back.",
            "files": []
        },
        {
            "title": "Read the page text",
            "body": "With activeTab, the extension only gets access to a tab when the user clicks the icon. Use chrome.scripting to grab readable text: prefer article or main, drop nav, footer, scripts and styles.",
            "prompt": "When the popup opens, use chrome.scripting.executeScript on the active tab to return { title, url, text }: text from article, else main, else body, with nav/header/footer/aside/script/style removed and whitespace collapsed, capped at 12,000 characters. Handle pages where scripting is not allowed (chrome:// pages, the Web Store) with a friendly message.",
            "done": "On a news article the popup can log the article text; on chrome://extensions it shows the friendly message.",
            "files": [
                "Chrome extensions docs"
            ]
        },
        {
            "title": "Call Claude from the service worker",
            "body": "The popup sends the page text to the service worker, which calls the Messages API with the saved key. Browser calls to the Anthropic API need the header anthropic-dangerous-direct-browser-access set to true; it is acceptable here because each user uses their own key on their own machine.",
            "prompt": "In the service worker, handle a \"summarize\" message: POST to https://api.anthropic.com/v1/messages with headers x-api-key, anthropic-version 2023-06-01, content-type application/json and anthropic-dangerous-direct-browser-access true. Use a fast current Claude model (check the docs for the model ID), max_tokens 400, a system prompt asking for exactly 5 short bullet points plus a one-line \"Why it matters\", and the page title + text as the user message. Return the text or a readable error (401 bad key, 429 rate limit, network).",
            "done": "Clicking the icon on an article returns a 5-bullet summary.",
            "files": [
                "Claude Messages API"
            ]
        },
        {
            "title": "Make the popup feel good",
            "body": "States matter more than looks here: loading, error, success. Add copy and retry so the popup is useful every time.",
            "prompt": "Design the popup at 360px wide: page title at the top, a skeleton loader while waiting, the bullets as a list, \"Why it matters\" in a highlighted line, and buttons Copy (copies Markdown) and Retry. Errors show the message and a Retry button. Support dark mode with prefers-color-scheme.",
            "done": "Loading, success and error all look intentional, in light and dark.",
            "files": []
        },
        {
            "title": "Cache and cost control",
            "body": "Opening the popup twice on the same page should not pay twice.",
            "prompt": "Cache summaries in chrome.storage.session keyed by URL for 30 minutes. Show \"Cached\" with a small Refresh link when a cached summary is shown. Also skip the call and say so when the page has less than 300 characters of text.",
            "done": "Reopening the popup on the same page is instant and makes no network request.",
            "files": []
        },
        {
            "title": "Review and package",
            "body": "Check permissions and key handling before you share it with anyone, then zip the dist folder.",
            "prompt": "Review the extension against CLAUDE.md rules: list every permission and why it is needed, confirm the key is never logged or sent anywhere except api.anthropic.com, and confirm no remote code is loaded. Fix anything that fails, then add a build:zip script.",
            "done": "The review passes and you have a zip ready for friends or the Chrome Web Store.",
            "files": [
                "CLAUDE.md"
            ]
        }
    ],
    "files": [
        {
            "type": "markdown",
            "name": "CLAUDE.md",
            "note": "Project root. Cursor users: save as AGENTS.md.",
            "file": "claude",
            "url": "https://ownlyplans.com/bhaskar/chrome-extension-page-summarizer/claude/"
        },
        {
            "type": "link",
            "name": "Chrome extensions docs",
            "note": "Manifest V3, activeTab, scripting, storage.",
            "url": "https://developer.chrome.com/docs/extensions"
        },
        {
            "type": "link",
            "name": "Claude Messages API",
            "note": "Request format, models, errors.",
            "url": "https://docs.anthropic.com/en/api/messages"
        }
    ],
    "install": "",
    "how_to_use": "Call get_step with step 1, do it, check \"done when\", then continue."
}