summarize Documentation

Point at any URL/YouTube/Podcast or file. Get the gist. CLI and Chrome Extension.

6,538 stars TypeScript TypeScript 1 file ยท ~21,117 tokens #ai#cli#summarize#typescript
RAW Doc

1. Project Overview & Quickstart (steipete/summarize)

File: README.md




Summarize extracts clean text and produces summaries from web pages, files, YouTube videos, podcasts, and other audio or video. Use it from the Node.js CLI or from the Chrome Side Panel and Firefox Sidebar.

console
$ summarize "https://example.com" --extract --plain
This domain is for use in documentation examples without needing permission. Avoid use in operations.
[Learn more](https://iana.org/domains/example)

Install

Run it once without installing:

bash
npx -y @steipete/summarize --version

Or install the CLI with npm or Homebrew:

bash
npm install @steipete/summarize
bash
brew install summarize

The npm package requires Node.js 24 or newer. Homebrew/core and the macOS archives on GitHub Releases provide standalone builds. See the installation guide for optional media tools and platform notes.

Quick start

Extract readable content without an API key:

bash
summarize "https://example.com" --extract --plain

For a model-generated summary, configure a supported provider or use an authenticated coding CLI:

bash
summarize "https://example.com" --cli codex

The default auto model chooses among configured providers. The five-minute quickstart covers API keys, local models, files, YouTube, podcasts, and JSON output.

What it handles

Input Processing path
Web pages Readability extraction, with optional Markdown and Firecrawl fallbacks
PDFs, images, and text files File-aware extraction and model attachments where supported
YouTube and podcasts Published transcripts first, then configured transcription fallbacks
Audio and video Local or cloud transcription, with optional speaker labels
Video slides Scene-based frames, optional OCR, and timestamped output

Use --extract when you only need clean content, --json for a stable automation envelope, and --slides for video frames. The command reference documents every flag.

Browser extension

Install Summarize Side Panel from the Chrome Web Store to summarize the active tab, chat with its content, and work with video transcripts and slides.

Direct mode runs without a companion service. Browser media uses Chrome capabilities for local transcription and slide extraction; the optional daemon adds CLI model backends, native media tools, OCR, shared caches, and Firefox media support.

To pair the installed CLI, copy the token shown by the extension and run:

bash
summarize daemon install --token <TOKEN>

See the extension setup and architecture and troubleshooting guide.

Media and slides

Summarize prefers existing captions and podcast transcripts before it transcribes media. Native ffmpeg, yt-dlp, and tesseract extend codec, YouTube slide, and OCR support; a bundled WebAssembly FFmpeg path covers common media without a native install.

The focused guides describe YouTube extraction, media routing, slides, and local ONNX transcription.

Models and configuration

Model IDs use provider/model names. Summarize works with configured API providers, OpenAI-compatible endpoints, Ollama, OpenRouter free models, and authenticated coding CLIs such as Codex, Claude, Gemini, OpenClaw, and GitHub Copilot.

Configuration lives at ~/.summarize/config.json; command-line flags take precedence. Inspect the effective setup without exposing keys:

bash
summarize status

Read the model guide, automatic selection rules, CLI provider guide, and configuration reference for the full setup.

Library

For programmatic extraction without the CLI dependency surface, install @steipete/summarize-core:

bash
npm install @steipete/summarize-core

The package exports focused content and prompt entry points. See the core package README for usage.

Documentation

Start with the documentation index, then follow the guide for your task:

Development

Requires Node.js 24 and pnpm 10.34.5.

bash
pnpm install --frozen-lockfile
pnpm build
pnpm check

See CONTRIBUTING.md for repository layout and extension workflows.

License

MIT. See LICENSE.


File: apps/chrome-extension/docs/firefox.md

Firefox Compatibility Guide

This document details Firefox-specific implementation notes, API compatibility findings, and known differences between the Chrome and Firefox versions of the Summarize extension.

Chrome API Usage Investigation

Standard WebExtensions APIs (Compatible)

The following Chrome APIs are used throughout the extension and have direct Firefox equivalents via the browser.* namespace. WXT automatically polyfills these:

Core Extension APIs

  • chrome.runtime: Message passing, extension info, connections

    • runtime.onMessage, runtime.sendMessage
    • runtime.onConnect, runtime.connect, runtime.Port
    • runtime.getURL, runtime.getManifest
    • runtime.openOptionsPage
    • โœ… Firefox compatible (all methods supported)
  • chrome.tabs: Tab management and queries

    • tabs.query, tabs.get, tabs.create, tabs.update
    • tabs.sendMessage
    • tabs.onActivated, tabs.onUpdated
    • โœ… Firefox compatible
  • chrome.storage: Persistent and session storage

    • storage.local.get, storage.local.set
    • storage.session (used for ephemeral data)
    • storage.onChanged
    • โš ๏ธ Mostly compatible - storage.session requires Firefox 115+
  • chrome.scripting: Dynamic content script injection

    • scripting.executeScript
    • โœ… Firefox compatible (MV3)
  • chrome.windows: Window management

    • windows.getCurrent, windows.create, windows.update
    • โœ… Firefox compatible
  • chrome.webNavigation: Navigation events

    • webNavigation.onHistoryStateUpdated
    • โœ… Firefox compatible
  • chrome.permissions: Runtime permissions

    • permissions.contains, permissions.request
    • โœ… Firefox compatible

Chrome-Specific APIs (Requires Attention)

1. Side Panel API (Primary Incompatibility)

Chrome usage (wxt.config.ts:56, 77-79, background.ts:1583):

typescript
// Manifest
side_panel: {
  default_path: 'sidepanel/index.html',
}

// Runtime API
chrome.sidePanel?.setPanelBehavior?.({ openPanelOnActionClick: true })

Firefox equivalent: sidebar_action API (Firefox 131+)

typescript
// Manifest override needed
sidebar_action: {
  default_panel: 'sidepanel.html',
  default_title: 'Summarize',
  default_icon: 'assets/icon-128.png'
}

Migration notes:

  • Firefox sidebar is always visible in the sidebar (not a side panel that slides in)
  • No equivalent to setPanelBehavior - sidebar is opened manually
  • Same HTML content can be reused (sidepanel.html)
  • UI may need minor adjustments for Firefox sidebar dimensions

Files affected:

  • wxt.config.ts - Needs Firefox manifest override
  • src/entrypoints/background.ts:1583 - setPanelBehavior call should be Chrome-only

2. Debugger API (Advanced Features)

Usage (background.ts:407-480, automation/tools.ts:336-366):

typescript
chrome.debugger.attach({ tabId }, "1.3");
chrome.debugger.sendCommand({ tabId }, method, params);
chrome.debugger.detach({ tabId });

Firefox compatibility: โœ… Supported but may have behavioral differences

  • Firefox exposes a related debugger API, but the current Firefox build omits the permission.
  • Debugger-backed native input and the explicit debugger tool are unavailable in this build.

3. UserScripts API (Optional)

Usage (automation/userscripts.ts:14-16, background.ts, automation/repl.ts:142-171):

typescript
chrome.userScripts;
chrome.permissions.contains({ permissions: ["userScripts"] });

Firefox compatibility: โš ๏ธ Limited support

  • Firefox has experimental browser.userScripts support
  • Available in Firefox 128+ behind extensions.userScripts.enabled pref
  • Less mature than Chrome implementation
  • Implementation: Feature-detect and gracefully degrade (see automation/userscripts.ts)
    • getUserScriptsStatus() checks API availability
    • buildUserScriptsGuidance() provides user-friendly error messages
    • Automation features show clear guidance if API unavailable

Storage Quota Considerations

Current usage pattern: Caching summaries, settings, and session data

Chrome quotas:

  • storage.local: ~10 MB (can request more with unlimitedStorage)
  • storage.session: ~10 MB

Firefox quotas:

  • storage.local: 10 MB default (same as Chrome)
  • storage.session: 10 MB (Firefox 115+)

Action required:

  • Monitor cache size in production
  • Implement LRU eviction if approaching limits
  • Consider adding warnings at 80% capacity

Manifest Differences

Required Changes for Firefox

Chrome manifest (current):

json
{
  "permissions": ["sidePanel", ...],
  "side_panel": {
    "default_path": "sidepanel/index.html"
  }
}

Firefox manifest override (needed):

json
{
  "permissions": ["tabs", "activeTab", "storage", ...],
  "sidebar_action": {
    "default_panel": "sidepanel/index.html",
    "default_title": "Summarize"
  }
}

Permissions to verify:

  • Remove sidePanel (Chrome-only)
  • Verify debugger permission works in Firefox
  • Verify userScripts in optional_permissions is handled gracefully

Service Worker vs Background Page

Current: Chrome MV3 service worker (background.ts)

Firefox MV3: Also uses service workers (Firefox 109+)

  • Same lifecycle as Chrome
  • Same event-driven model
  • SSE connection handling should work identically

Testing priorities:

  1. Verify service worker restarts properly
  2. Test SSE streaming during worker lifecycle
  3. Verify port-based communication (sidepanel โ†” background)

Content Script Timing

Current injection strategy:

  • extract.content.ts: Readability-based extraction
  • hover.content.ts: Hover summaries
  • automation.content.ts: Automation features

Firefox compatibility: โœ… Should work identically

  • WXT handles content script registration
  • Same run_at timing behavior
  • Same message passing APIs

SSE/EventSource Support

Usage: Streaming summaries from daemon via SSE (src/lib/sse.ts)

Testing needed:

  • Verify EventSource works in Firefox background context
  • Test reconnection logic on Firefox
  • Verify CORS headers work with Firefox origin

Known Behavioral Differences

1. Sidebar vs Side Panel UX

Chrome Side Panel:

  • Slides in from the right
  • Programmatically opened via sidePanel.setPanelBehavior()
  • Toggles on toolbar icon click

Firefox Sidebar:

  • Always visible in sidebar area (left side by default)
  • Programmatically controlled via sidebarAction.toggle(), open(), close()
  • Toggles on toolbar icon click (implemented)
  • Keyboard shortcut: Ctrl+Shift+U (customizable by user)
  • Different width constraints than Chrome

Impact: Minimal - both browsers now support programmatic control and icon-click toggling

2. Extension Context URLs

Chrome: chrome-extension:///...
Firefox: moz-extension:///...

Impact: Minimal - WXT handles this via runtime.getURL()

3. Developer Tools Integration

Chrome: DevTools open via chrome://extensions
Firefox: DevTools open via about:debugging

Impact: Documentation only

Testing Strategy

Browser Compatibility Tags

Add tags to Playwright tests:

typescript
// @cross-browser - Runs on both Chrome and Firefox
test('@cross-browser should generate pairing token', ...)

// @firefox - Firefox-specific tests
test('@firefox should use sidebar API', ...)

// @chrome - Chrome-specific tests
test('@chrome should use Side Panel API', ...)

Test Execution

bash
# All tests (both browsers)
pnpm test

# Firefox only
pnpm test:firefox

# Chrome only
pnpm test:chrome

Critical Test Scenarios

  1. Pairing flow: Token generation and daemon connection
  2. Summary streaming: SSE stream rendering in sidebar
  3. Content extraction: Readability on various sites
  4. Auto-summarize: Navigation triggers
  5. Settings persistence: storage.local across restarts
  6. Permissions: Optional userScripts access; debugger features unavailable

Development Workflow

Building for Firefox

bash
# Development mode (watch)
pnpm dev:firefox

# Production build
pnpm build:firefox

# Output location
.output/firefox-mv3/

Loading in Firefox

  1. Open about:debugging#/runtime/this-firefox
  2. Click "Load Temporary Add-on"
  3. Select apps/chrome-extension/.output/firefox-mv3/manifest.json

Note: Temporary add-ons are removed on browser restart

Debugging

Console logs:

  • Background script: about:debugging โ†’ This Firefox โ†’ Inspect
  • Content scripts: Regular DevTools Console (per-page)
  • Sidebar: Right-click sidebar โ†’ Inspect

Common issues:

  • "Error: Extension is invalid": Check manifest syntax
  • "Loading failed": Check console for missing permissions
  • Sidebar not rendering: Verify sidebar_action in manifest

Distribution

Temporary Installation (Current)

  • Use about:debugging โ†’ Load Temporary Add-on
  • Extension removed on Firefox restart
  • Suitable for development and early beta testing

Future: AMO (Add-ons.mozilla.org)

When ready for public distribution:

  1. Submit to AMO for review
  2. Code signing required (automatic via AMO)
  3. Update mechanism via AMO (similar to Chrome Web Store)

Implementation Checklist

  • Investigate Chrome API usage
  • Document Chrome-specific APIs
  • Create WXT Firefox target configuration
  • Add sidebar_action manifest override
  • Implement toolbar icon click handler with sidebarAction.toggle()
  • Add keyboard shortcut support (Ctrl+Shift+U)
  • Handle userScripts gracefully if unavailable (checks availability, shows guidance)
  • Add browser compatibility test tags
  • Configure Playwright for Firefox
  • Run full test suite on Firefox build
  • Verify Firefox build succeeds (.output/firefox-mv3/)
  • Verify manifest permissions are correct
  • Update user-facing documentation (README.md installation guide)
  • Manual testing in Firefox Developer Edition
  • Test sidebar rendering in Firefox
  • Verify SSE streaming works
  • Keep debugger-backed automation out of the Firefox manifest

References

Open Questions

  1. UserScripts API: Automation features require userScripts - graceful degradation implemented with user guidance
  2. Sidebar width: May need CSS adjustments for Firefox sidebar dimensions (requires manual testing)
  3. Testing coverage: Aiming for 100% feature parity where browser APIs allow
  4. Distribution timeline: When to submit to AMO?
  5. Keyboard shortcut conflicts: Does Ctrl+Shift+U conflict with common Firefox shortcuts?

Last updated: 2026-01-02
Status: โœ… Implementation complete - Ready for manual testing in Firefox

Recent Changes (2026-01-02)

Firefox DOES support programmatic sidebar control via the sidebarAction API! The implementation includes:

  • Toolbar Icon Click (background.ts:1589-1599):

    • Detects Firefox builds using import.meta.env.BROWSER === 'firefox'
    • Adds action.onClicked listener that calls browser.sidebarAction.toggle()
    • Toggles sidebar visibility just like Chrome's side panel behavior
  • Keyboard Shortcut (wxt.config.ts:105-115):

    • Added _execute_sidebar_action command to manifest
    • Default: Ctrl+Shift+U (Windows/Linux)
    • Mac: Command+Shift+U
    • Users can customize this in Firefox settings: about:addons โ†’ Extensions โ†’ Manage Extension Shortcuts
  • Manifest Changes (wxt.config.ts:61):

    • Firefox builds use standard WebExtensions permissions (no special permissions needed)
    • Chrome builds continue to use sidePanel permission

User Experience:

  1. Click toolbar icon: Toggles sidebar open/close
  2. Keyboard shortcut: Ctrl+Shift+U (or Cmd+Shift+U on Mac) toggles sidebar
  3. Customizable: Users can change the shortcut in Firefox's extension settings

File: apps/chrome-extension/README.md

Summarize (Browser Extension)

Browser extension for Chrome and Firefox that streams AI-powered summaries directly into your browser's sidebar/side panel.

Supported browsers:

  • Chrome 120+ (Side Panel) - Auto-opens on toolbar icon click
  • Firefox 140+ (Sidebar) - Toggle with toolbar icon or Ctrl+Shift+U

Docs + setup: https://summarize.sh

Build

  • From repo root: pnpm install
  • Chrome dev: pnpm -C apps/chrome-extension dev
  • Firefox dev: pnpm -C apps/chrome-extension dev:firefox
  • Prod build (Chrome): pnpm -C apps/chrome-extension build
  • Debugger-enabled automation build (Chrome): pnpm -C apps/chrome-extension build:automation
  • Prod build (Firefox): pnpm -C apps/chrome-extension build:firefox
  • Build both: pnpm -C apps/chrome-extension build:all

Install in Chrome (Unpacked)

Step-by-step:

  1. Build the extension:
    • pnpm -C apps/chrome-extension build
  2. Open Chrome โ†’ go to chrome://extensions
    • Or Chrome menu โ†’ Extensions โ†’ โ€œManage Extensionsโ€
  3. Turn on Developer mode (top-right toggle).
  4. Click Load unpacked.
  5. Select the folder: apps/chrome-extension/.output/chrome-mv3
  6. You should now see โ€œSummarizeโ€ in the extensions list.
  7. (Optional) Pin the extension (puzzle icon โ†’ pin), then click it to open the Side Panel.

Developer mode is required for loading unpacked extensions.

Install in Firefox (Temporary Add-on)

Step-by-step:

  1. Build the Firefox extension:
    • pnpm -C apps/chrome-extension build:firefox
  2. Open Firefox โ†’ go to about:debugging#/runtime/this-firefox
    • Or Firefox menu โ†’ More tools โ†’ "This Firefox" (under "Debugging")
  3. Click Load Temporary Add-on
  4. Navigate to and select: apps/chrome-extension/.output/firefox-mv3/manifest.json
  5. You should now see "Summarize" in the extensions list
  6. Open the sidebar using any of these methods:
    • Click the Summarize toolbar icon (toggles sidebar open/close)
    • Keyboard shortcut: Ctrl+Shift+U (Windows/Linux) or Cmd+Shift+U (Mac)
    • Menu: View โ†’ Sidebar โ†’ Summarize

Customize keyboard shortcut (optional):

  • Go to about:addons โ†’ Extensions โ†’ โš™๏ธ (gear icon) โ†’ Manage Extension Shortcuts
  • Find "Summarize" and click the current shortcut to change it

Note: Temporary add-ons are removed when Firefox restarts. For permanent installation, the extension needs to be signed via AMO (Firefox Add-ons).

AI and Media Runtimes

The extension separates its AI connection from media/slide extraction:

  • Direct: works immediately with Auto using Chrome's built-in Gemini Nano Summarizer API and extractive fallback. A dismissible hint offers the optional daemon for faster media, OCR, and broader capabilities. Auto calls a configured OpenAI, OpenRouter, Anthropic, Gemini, xAI, Z.AI, NVIDIA, MiniMax, GitHub Models, Ollama, or overridden compatible endpoint directly from Chrome. Gemini Nano can also be selected explicitly. Provider-backed chat, automation, and hover summaries work without the daemon. Keys stay in chrome.storage.local and are sent only to the selected provider.
  • Daemon: uses the local Summarize daemon and its configured providers, CLI fallbacks, cache, and diagnostics. Explicitly selecting Gemini Nano still keeps summaries on-device while daemon-only capabilities remain available.

Media/slides can independently use Browser or Daemon. Browser media uses MediaBunny with native WebCodecs for fetchable video slides up to 128 MB, summarizes each slide with Gemini Nano, and transcribes captionless YouTube videos with local multilingual Whisper. The AI models download on first use and are cached by Chrome. Daemon media adds native tools, configurable transcription providers, OCR, broader media support, and Firefox media support.

Optional Daemon (Pairing)

  1. Install summarize (choose one):
    • npm i @steipete/summarize (requires Node.js 24+)
    • brew install summarize (macOS, Linux)
  2. Under Options โ†’ Runtime โ†’ Daemon, click Enable local companion and approve Chrome's
    optional Communicate with cooperating native applications permission. Switching either
    runtime to Daemon also starts this explicit permission flow. The side panel's Connect
    daemon hint opens this Runtime setup view directly.
  3. Switch the AI connection or media runtime to Daemon, then copy the pairing token and install command from the extension.
  4. Open Terminal:
    • macOS: Applications โ†’ Utilities โ†’ Terminal
    • Windows: Start menu โ†’ Terminal (or PowerShell) โ€” right-click โ†’ Run as administrator
    • Linux: your Terminal app
  5. Paste the command from the Setup screen and press Enter.
    • Installed binary: summarize daemon install --token <TOKEN> --port 8787
    • Repo/dev checkout: pnpm summarize daemon install --token <TOKEN> --port 8787 --dev --extension-id
    • The install registers native host com.steipete.summarize for the exact Web Store extension ID.
    • Non-default port: replace 8787, then enter the same port under Options โ†’ Runtime โ†’ Daemon โ†’ Port.
  6. Back in your browser, the Daemon runtime setup screen should disappear once the daemon is running.
  7. Verify / troubleshoot:
    • summarize daemon status
    • summarize daemon restart

Chrome communicates with the daemon only through the optional native host. The manifest retains
loopback access for configured Direct local providers, but those requests never enter the daemon
bridge. The npm-installed Windows CLI still needs a packaged native-host .exe before Daemon mode
can work there. Direct and Browser modes are unaffected.

Company administrators can keep Direct and Browser modes available while blocking all daemon
access with Chrome policy; see docs/chrome-enterprise.md.

Optional Website Automation

Summarization, chat, and browser media do not require Chrome's userScripts or debugger
permissions. Website automation is off by default. Options โ†’ Enable automation permissions
requests optional userScripts access from an explicit user click so user-requested browserjs() /
REPL code can run in the page's main world.

Chrome does not allow debugger to be declared optional. The standard Chrome build omits it and
hides the debugger tool; pnpm -C apps/chrome-extension build:automation creates the separate
debugger-enabled build for native click/type/key input and the explicit debugger tool. That build
declares debugger as required, attaches only while executing a debugger-backed command, and then
detaches.

Length Presets

  • Presets match CLI: short|medium|long|xl|xxl (or custom like 20k).
  • Tooltips show target + range + paragraph guidance.
  • Source of truth: packages/core/src/prompts/summary-lengths.ts.

File: docs/agent.md


title: "Agent / daemon"
kicker: "apps"
summary: "Automation agent for the Chrome side panel (daemon-backed)."
read_when:

  • "When working on automation tools, /v1/agent, or the side panel agent loop."

Automation Agent (Side Panel + Daemon)

Summarize can run as a website automation agent inside the Chrome side panel. This is optional and gated by a checkbox in Options.

Scope:

  • Off (default): chat is Q&A only (no tools).
  • On: chat runs a tool-capable agent for web automation.

Explicit exclusions (per product direction): no update checker, no tutorial/welcome flow, no proxy config, no API key dialog. All model calls go through the local daemon.

Architecture (High Level)

  1. Side panel maintains the agent loop + chat UI.
  2. Background handles tab data, extraction, and tool execution.
  3. Content scripts handle element picking and native-input bridge.
  4. Daemon provides /v1/agent (SSE stream of chunks + final assistant message).

Data Flow (Agent Loop)

  • User sends a message in the side panel.
  • Panel compacts history and sends panel:agent to background with:
    • messages (pi-ai Message[])
    • tools (names)
    • summary (optional current summary markdown)
  • Background builds pageContent using the latest extract (summary + transcript/text + metadata).
  • Background calls daemon POST /v1/agent (SSE).
  • Daemon streams chunk events, then an assistant event (may include tool calls).
  • Panel executes tool calls locally, appends toolResult messages, and repeats /v1/agent until no tool calls remain.

The daemon never executes tools. It only returns the next assistant message.

Settings + Permissions

Settings

automationEnabled (boolean) lives in apps/chrome-extension/src/lib/settings.ts.

  • Options UI provides the toggle.
  • When disabled, the tool list is empty and the daemon uses the chat-only prompt.

Automation Permissions

Defined in apps/chrome-extension/wxt.config.ts:

  • userScripts โ€“ optional; requested via Options for browserjs() main-world script execution.
  • debugger โ€“ required only in build:automation for native input and the debugger tool.

Neither permission is needed for summarization. The standard Chrome build omits debugger because
Chrome does not allow it to be optional,
and it hides debugger-backed tools. The separate automation build declares it as required.

Chrome: enable User Scripts (if needed)

  1. chrome://extensions
  2. Open extension details
  3. Enable Allow User Scripts
  4. Reload the tab

Daemon Endpoint

`POST /v1/agent` (SSE)

Headers

  • Authorization: Bearer <token>
  • Content-Type: application/json

Body

text
{
  "url": "https://...",
  "title": "Page title",
  "pageContent": "",
  "cacheContent": "",
  "messages": [/* pi-ai Message[] */],
  "model": "auto" | "openai/..." | "anthropic/..." | ...,
  "length": "short" | "xl" | "20k" | ...,
  "language": "auto" | "en" | "de" | ...,
  "tools": ["navigate", "repl", "ask_user_which_element", "skill", "debugger"],
  "automationEnabled": true
}

Response (SSE, default)

text
event: chunk
data: { "text": "..." }

event: assistant
data: { /* AssistantMessage */ }

event: done
data: {}

event: error
data: { "message": "..." }

Response (JSON)
Use Accept: application/json or ?format=json.

text
{ "ok": true, "assistant": { /* AssistantMessage */ } }

`POST /v1/agent/history`

Returns cached chat history for the same cache key as /v1/agent.

Body

text
{
  "url": "https://...",
  "pageContent": "",
  "cacheContent": "",
  "model": "auto" | "openai/..." | "anthropic/..." | ...,
  "length": "short" | "xl" | "20k" | ...,
  "language": "auto" | "en" | "de" | ...,
  "automationEnabled": true
}

Response

text
{ "ok": true, "messages": [/* Message[] */] }

Model Resolution (Daemon)

  • Fixed model (explicit model): parsed as /<model>. Provider base URL overrides come from config/env (OpenAI, Anthropic, Google, xAI, ZAI). OpenRouter uses OpenAI-compatible completions.
  • Auto model: uses existing auto-selection logic (buildAutoModelAttempts), preferring API-key transports and then CLI fallback when available.
  • Synthetic models: created for OpenAI-compatible base URLs (local/openrouter).
  • maxOutputTokens defaults to 2048 or maxOutputTokens override.
  • CLI models are supported as auto fallback and via explicit cli/ or cli//<model> overrides.
  • If the daemon still says no model is available after key/install changes, restart or reinstall it so the saved environment snapshot refreshes.

Page Content Payload

pageContent is built from the latest extract and includes:

  • Summary (optional): current summary markdown (truncated to settings cap).
  • Transcript/text: timed transcript when available, otherwise extracted text.
  • Metadata: URL/title, source (page vs url), extraction strategy, markdown provider, Firecrawl usage, transcript provider + cache status, media duration, word counts, truncation flags.

See buildChatPageContent() usage in apps/chrome-extension/src/entrypoints/background.ts.

Tools

1) `navigate`

Navigate the active tab.

Params:

text
{ "url": "https://...", "newTab": false }

Result:

text
{ "finalUrl": "https://...", "title": "...", "tabId": 123 }

Notes:

  • Uses chrome.tabs.update or chrome.tabs.create.
  • Waits for tab status complete (15s timeout).

2) `repl`

Execute JavaScript in a sandbox, with browserjs() to run in the page context.

Params:

text
{ "title": "...", "code": "..." }

Result:

text
{ "output": "console/return output", "files": [{ "fileName": "...", "mimeType": "...", "contentBase64": "..." }] }

REPL environment:

  • Runs in a sandboxed iframe (no DOM access to the panel).
  • browserjs(fn, ...args) runs the function in the page context.
    • Uses chrome.userScripts.execute in the main world.
  • navigate({ url }) available inside the REPL (always use for navigation).
  • sleep(ms) helper.
  • Console output is captured and returned; return values are appended as => value.
  • returnFile(name, content, mimeType) or returnFile({ fileName, content, mimeType }) attaches files to the tool result.

Safety:

  • Navigation inside REPL code (window.location, history, etc.) is rejected. Use navigate() instead.

Page-context helpers (via browserjs()):

  • Skills libraries are auto-injected when domain patterns match the active URL.
  • If debugger permission is granted, native helpers are exposed:
    • nativeClick(selector)
    • nativeType(selector, text)
    • nativePress(key)
    • nativeKeyDown(key) / nativeKeyUp(key)

3) `ask_user_which_element`

Shows a click-to-select overlay and returns element metadata.

Params:

text
{ "message": "Optional guidance" }

Result (example):

text
{
  "selector": "#submit",
  "xpath": "//button[1]",
  "text": "Send",
  "tagName": "button",
  "attributes": { "type": "submit" },
  "html": "",
  "boundingBox": { "x": 20, "y": 120, "width": 180, "height": 40 }
}

Overlay UX:

  • Hover highlights element under cursor.
  • Click selects.
  • โ†‘ / โ†“ moves up or down the DOM tree.
  • Esc cancels.

4) `skill`

CRUD for domain-specific libraries (stored in chrome.storage.local).

Storage keys:

  • automation.skills (map of name โ†’ skill)
  • automation.skillsSeeded (one-time default seed)

Actions:

  • list (optionally filtered by URL)
  • get (optionally includes library code)
  • create / rewrite
  • update (string replacement)
  • delete

Default skills seed from apps/chrome-extension/src/automation/default-skills.json.

Matching:

  • Glob-like domain patterns (supports * and **).
  • Match is done against hostname + path (e.g. github.com/*/issues).

Notes:

  • update is intended for in-place string replacements; use rewrite to rename.
  • Skills libraries run inside browserjs() and must avoid navigation.

5) `debugger` (optional)

Runs JavaScript in the main world via the Chrome debugger. Last resort (shows Chrome debug banner).

Params:

text
{ "action": "eval", "code": "..." }

Result:

text
{ "text": "...", "details": { ... } }

6) `summarize`

Run the Summarize pipeline against a URL (summary or extract-only).

Params:

text
{
  "url": "https://...",
  "extractOnly": false,
  "format": "text",
  "markdownMode": "readability",
  "model": "openai/gpt-5-mini",
  "length": "short",
  "language": "auto",
  "prompt": "Optional override",
  "timeout": "2m",
  "maxOutputTokens": "2k",
  "noCache": false,
  "firecrawl": "auto",
  "preprocess": "auto",
  "youtube": "auto",
  "videoMode": "auto",
  "timestamps": false,
  "maxCharacters": 120000
}

Result (summary):

text
{ "output": "..." }

Result (extract-only):

text
{ "output": "...", "details": { "content": "...", "title": "...", "wordCount": 123, ... } }

Notes:

  • URL-only (uses daemon URL pipeline).
  • Use extractOnly: true + format: "markdown" to return extracted Markdown.
  • When format: "markdown", markdownMode defaults to readability.

Native Input Bridge (Debugger Permission)

Native input events use the Chrome debugger protocol:

  • browserjs() posts a message to the content script.
  • Content script forwards to background (automation:native-input).
  • Background attaches debugger and dispatches:
    • Input.dispatchMouseEvent (click)
    • Input.insertText (type)
    • Input.dispatchKeyEvent (press/keydown/keyup)

If permission is missing, the call fails and the tool reports the error.

UX Notes

  • Automation is opt-in via Options checkbox.
  • Regular summarize flows remain unchanged.
  • Tool results are treated as data, not instructions. The system prompt still asks the assistant to repeat important tool-derived info in plain text.
  • Tool results are currently rendered in the chat UI for debugging; they may be hidden in the future.
  • When the active tab URL changes during a conversation, the panel appends a navigation tool result with the new URL, title, and matching skills. This keeps the agent aware of user-driven navigation.
  • REPL executions that call browserjs() show a small overlay with an Abort action; aborting stops the current agent loop (best-effort).

Where Things Live

  • Daemon: src/daemon/server.ts, src/daemon/agent.ts
  • Automation tools: apps/chrome-extension/src/automation/
  • Element picker + native input bridge: apps/chrome-extension/src/entrypoints/automation.content.ts
  • Background agent proxy: apps/chrome-extension/src/entrypoints/background.ts
  • Side panel agent loop: apps/chrome-extension/src/entrypoints/sidepanel/
  • Options toggle: apps/chrome-extension/src/entrypoints/options/

File: docs/cache.md


title: "Cache"
kicker: "internals"
summary: "CLI cache design, keys, config, and eviction."
read_when:

  • "When changing cache behavior, keys, or defaults."

Cache (design)

Lightweight, CLI-only SQLite cache. Single DB file.

Goals

  • Avoid repeated transcripts/extractions/summaries.
  • No file sprawl; bounded disk usage.
  • Safe defaults; easy opt-out.
  • Native SQLite only (Node 24 + Bun).

Storage

  • Default path: ~/.summarize/cache.sqlite
  • Override: cache.path in config.
  • SQLite pragmas: WAL, NORMAL sync, busy timeout, incremental vacuum.

Media cache (downloads)

Separate file cache for downloaded media (yt-dlp, direct media URLs). This is not the SQLite DB.

  • Default path: ~/.summarize/cache/media
  • TTL: 7 days
  • Size cap: 2048 MB
  • Config: cache.media (see below)
  • CLI: --no-media-cache disables media caching only
  • Note: --no-cache does not disable the media cache

What we cache

  • Transcripts
    • key: sha256({url, namespace, fileMtime?, formatVersion}) (local file paths include fileMtime for invalidation)
  • Extracted content (URL โ†’ text/markdown)
    • key: sha256({url, extractSettings, formatVersion})
  • Summaries
    • key: sha256({contentHash, promptHash, model, length, language, formatVersion})
    • cache hit even if URL differs (content hash wins).
    • contentHash comes from the <content> block actually sent to the model (so slide timelines / transcript extras affect the key), or from attached binary bytes when the prompt content is empty.
  • Slides (manifest + on-disk images in the slides output dir)
    • key: sha256({url, slideSettings, formatVersion})

Keys / hashes

  • sha256 from Node crypto / Bun crypto.
  • contentHash from normalized prompt <content> (fallback: normalized extracted content when no tagged block exists).
  • promptHash from instruction block (custom prompt or default).
  • formatVersion: hardcoded constant to invalidate on prompt format changes.

Config

json
{
  "cache": {
    "enabled": true,
    "maxMb": 512,
    "ttlDays": 30,
    "path": "~/.summarize/cache.sqlite",
    "media": {
      "enabled": true,
      "maxMb": 2048,
      "ttlDays": 7,
      "path": "~/.summarize/cache/media",
      "verify": "size"
    }
  }
}

Defaults: enabled=true, maxMb=512, ttlDays=30, path unset.

CLI flags

  • --no-cache: bypass summary cache reads + writes (LLM output). Extract/transcript caches still apply.
  • --cache-stats: print cache stats and exit.
  • --clear-cache: delete cache DB (and WAL/SHM); must be used alone.

Eviction policy

  • TTL sweep on read/write.
  • Size cap: if DB > maxMb, delete oldest entries by last_accessed_at until under cap.
  • Optional count cap if needed later.

Media cache eviction:

  • TTL sweep on read/write.
  • Size cap: evict least-recently-used files until under cap.
  • verify controls integrity checks: size (default), hash, or none.

Notes

  • No extension cache (daemon uses CLI cache).
  • No third-party SQLite deps.
  • Transcript namespace currently includes the YouTube mode (e.g. yt:auto, yt:web).

File: docs/chrome-extension.md


title: "Chrome extension"
kicker: "apps"
summary: "Chrome side panel extension + daemon architecture, setup, and troubleshooting."
read_when:

  • "When working on the extension, daemon, or side panel UX."

Browser Side Panel (Chrome + Firefox Extension + Daemon)

Goal: Chrome Side Panel (โ€œreal sidebarโ€) summarizes what you see on the current tab. Panel open โ†’ navigation โ†’ auto summarize (optional) โ†’ streaming Markdown rendered in-panel.

Quickstart:

  • Build/load extension: apps/chrome-extension/README.md
  • Direct mode works immediately without a daemon. Auto uses a configured provider, otherwise Gemini Nano when available with extractive fallback.
  • Configured direct providers support summaries, chat, automation, hover summaries, and public-URL extraction without a daemon. Provider keys stay in extension-local storage.
  • Browser media provides daemonless transcription, slide extraction, and Gemini Nano per-slide summaries. OCR, process/log tools, CLI fallbacks, and broader native media support require the daemon.
  • Optional: install summarize for daemon-backed media support:
    • npm i @steipete/summarize
    • brew install summarize (macOS, Linux)
  • Firefox sidebar build: pnpm -C apps/chrome-extension build:firefox (load via about:debugging โ†’ temporary add-on)
  • Open side panel โ†’ copy token install command โ†’ run for daemon mode:
    • summarize daemon install --token <TOKEN> --port 8787 (macOS: LaunchAgent, Linux: systemd user, Windows: Scheduled Task)
    • Non-default port: replace 8787, then set the same value in Options โ†’ Runtime โ†’ Daemon โ†’ Port.
  • Chrome asks for optional Native Messaging access only when the user explicitly enables a Daemon
    runtime. Retained loopback host access serves configured Direct local providers only, not daemon
    traffic. Managed deployments can browser-disable both boundaries; see
    Chrome enterprise policy.
  • The side panel's daemon Connect hint opens Options โ†’ Runtime so users can verify the
    permission, token, port, and native-host setup in one place.
  • Verify:
    • summarize daemon status
    • Restart (if needed): summarize daemon restart

Firefox notes:

  • Sidebar UX differs from Chromeโ€™s side panel (persistent sidebar instead of slide-in panel).
  • Firefox loading is smoke-tested with Mozilla web-ext; Playwright page-level Firefox tests remain diagnostic-only. See apps/chrome-extension/tests/README-firefox.md.
  • Compatibility details: apps/chrome-extension/docs/firefox.md.

Dev (repo checkout):

  • Use: pnpm summarize daemon install --token <TOKEN> --dev --extension-id (copy the ID from chrome://extensions; autostart runs src/cli.ts with no dist/ build required).
  • E2E (Playwright): pnpm -C apps/chrome-extension test:e2e
    • First run: pnpm -C apps/chrome-extension exec playwright install chromium
    • Chromium runs headless by default.
    • Visible debugging: SHOW_UI=1 pnpm -C apps/chrome-extension test:e2e or HEADLESS=0 pnpm -C apps/chrome-extension test:e2e

Troubleshooting

  • โ€œDaemon not reachableโ€:
    • summarize daemon status
    • Confirm the native host is reported as installed and Chrome granted the optional companion permission.
    • If the daemon uses a non-default port, confirm Options โ†’ Runtime โ†’ Daemon โ†’ Port matches the daemon configuration.
    • If the native host was just reinstalled for an unpacked extension ID, reload the extension and reopen the side panel.
    • Logs: ~/.summarize/logs/daemon.err.log
  • Windows install:
    • summarize daemon install registers a Scheduled Task via schtasks /Create /XML, which requires an elevated shell. Run it from an Administrator PowerShell/cmd; otherwise you'll see schtasks create failed: ERROR: Access is denied.
    • The task launches wscript.exe //B //Nologo %USERPROFILE%\.summarize\daemon-launch.vbs. If install completes but /health is unreachable, run cscript //nologo %USERPROFILE%\.summarize\daemon-launch.vbs to surface launcher errors, then schtasks /Query /TN "Summarize Daemon" /V /FO LIST to inspect the task state.
  • macOS launchctl bootstrap errors (Input/output error, Domain does not support specified action):
    • summarize daemon install now tries both launchd domains (gui/ then user/).
    • Install as your normal user (not root) so HOME + launchd domain match.
    • Re-run: summarize daemon install --token <TOKEN>.
  • Windows containers:
    • summarize daemon install --token <TOKEN> starts the daemon for the current container session but does not create a Scheduled Task.
    • Chrome Daemon mode also needs the pending packaged Windows native-host executable. Do not expose the daemon port as a substitute; use Direct and Browser modes.
  • โ€œNeed extension-side tracesโ€:
    • Options โ†’ Logs โ†’ extension.log (panel/background events).
    • Enable โ€œExtended loggingโ€ in Advanced settings for full pipeline traces.
  • Captionless YouTube transcription is slow on first use:
    • Chrome downloads and caches the multilingual Whisper Tiny model on demand.
    • WebGPU is preferred; a slower WASM/CPU path is used when WebGPU is unavailable.
    • Offline model bundling and subscription/download workflows are not currently provided.
  • โ€œStream ended unexpectedlyโ€ / empty chat response:
    • The daemon likely stopped mid-stream. Restart it, then click โ€œTry againโ€.
    • summarize daemon restart
  • Slide strip/gallery missing after a parallel slide run failure:
    • Click the slide notice โ€œTry againโ€ button. If the dedicated slide run never started, the extension now requests a fresh summarize+slides run instead of reusing the summary-only run.
  • Tweet video not transcribing / no progress:
    • Ensure yt-dlp is available on your PATH (or set YT_DLP_PATH) and you have a transcription provider (whisper.cpp installed or GROQ_API_KEY / ASSEMBLYAI_API_KEY / GEMINI_API_KEY / OPENAI_API_KEY / FAL_KEY / DEEPGRAM_API_KEY).
    • Re-run summarize daemon install --token <TOKEN> to refresh the daemon env snapshot (launchd wonโ€™t inherit your shell PATH).
  • โ€œCould not establish connection / Receiving end does not existโ€:
    • The content script wasnโ€™t injected (yet), or Chrome blocked site access.
    • Chrome โ†’ extension details โ†’ โ€œSite accessโ€ โ†’ โ€œOn all sitesโ€ (or allow the domain), then reload the tab.
  • โ€œNative messaging host not foundโ€:
    • Re-run summarize daemon install --token <TOKEN> so the Chrome host manifest and launcher are refreshed.
    • Verify summarize daemon status reports Chrome native messaging host: installed.
    • The host is bound to Web Store ID cejgnmmhbbpdmjnfppjdfkocebngehfg; a normal unpacked build has a different ID unless a development host manifest explicitly allows it.

Architecture

  • Extension (MV3, WXT)
    • Side Panel UI: length + typography controls (font family + size), auto/manual toggle.
    • Background service worker: tab + navigation tracking, content extraction, starts summarize runs.
    • Chrome offscreen page: runs MediaBunny with native WebCodecs for ranged video slides and chunked local media transcription, plus an idle-evictable Whisper runtime.
    • Content script: extract readable article text from the rendered DOM via Readability; also detect SPA URL changes.
    • Direct provider adapters: OpenAI-compatible SSE, Anthropic Messages SSE, and Gemini SSE, normalized to the same summary/chat/tool-call contracts.
    • Panel page reconstructs daemon SSE streams carried through the service worker's native bridge.
  • Daemon (local, autostart service)
    • HTTP server on 127.0.0.1 (default port 8787).
    • Token-authenticated API.
    • Runs the existing summarize pipeline (env/config-based) and streams tokens to client via SSE.
    • Native host com.steipete.summarize proxies only /health and /v1/* to the configured daemon port and preserves SSE and binary responses.
    • Port is configurable: set Options โ†’ Runtime โ†’ Daemon โ†’ Port to match a daemon started with summarize daemon install --port <n>.

Data Flow

  1. User opens side panel (click extension icon).
  2. Panel sends a โ€œreadyโ€ message to the background (plus periodic โ€œpingโ€ heartbeats while open).
  3. On nav/tab change (and auto enabled): background asks the content script to extract { url, title, text } (best-effort).
  4. In Chrome Browser mode, fetchable audio is decoded in bounded chunks through MediaBunny/WebCodecs and transcribed locally. YouTube prefers active-player/watch-page direct audio, then Android VR, buffered direct audio, and captured SABR.
  5. In Direct mode, background uses Gemini Nano for Auto without provider credentials, or calls the selected provider and returns the normalized summary/chat result to the panel.
  6. In Daemon mode, extension contexts send request metadata through the service worker, which checks managed policy and optional permission before connecting to the native host. The host proxies the authenticated /v1/* API and streams response chunks back to the caller.

Auto Mode (URL + Page Text)

The extension always sends the same request shape:

  • Always: url, title
  • When available: extracted text + truncated
  • mode: "auto"

The daemon decides the best pipeline:

  • YouTube / video / podcast / direct media URLs โ†’ prefer URL pipeline (transcripts, yt-dlp, Whisper, readability, โ€ฆ).
  • Normal articles with extracted text โ†’ prefer page pipeline (โ€œwhat you seeโ€).
  • Fallback: if the preferred path fails before output starts, try the other input (when available).

Video Selection (Page vs Video)

When the page contains embedded audio/video, the Summarize button gains a dropdown caret. Click the caret to pick Page vs Video/Audio. Selecting Video/Audio forces URL mode with transcript-first extraction (captions โ†’ yt-dlp/Whisper fallback). Selection is per-run (not persisted).

See docs/media.md for detection and transcript rules.

Slides (Side Panel)

  • The slides toggle lights up on media-friendly URLs (YouTube/watch|shorts, youtu.be, direct media) or when the page reports video/audio. Defaults to Video on those pages.
  • Turning slides on refreshes the current summary and requests slide extraction. Chrome Browser mode uses MediaBunny with native WebCodecs and ranged reads for fetchable videos, then falls back to visible-tab capture. Daemon mode adds yt-dlp, native ffmpeg, and optional tesseract OCR.
  • Active slide mode is slide-first:
    • vertical image/text cards
    • transcript-first text; OCR fallback
    • text can appear before slide images finish extracting
    • the large summary block is hidden while slide cards are active
  • Slides stay off elsewhere and the toggle is disabled on non-media pages.

SPA Navigation

  • Background listens to chrome.webNavigation.onHistoryStateUpdated (SPA route changes) and tabs.onUpdated (page loads).
  • Only triggers summarize when the side panel is open (and auto is enabled).

Markdown Rendering

  • Use markdown-it in the panel.
  • Disable raw HTML: html: false (avoid sanitizing libraries).
  • linkify: true.
  • Render links with target=_blank + rel=noopener noreferrer.
  • When timed transcripts are available, chat context includes [mm:ss] lines.
  • Assistant is prompted to cite timestamps; clicking them seeks the current media (video/audio) while preserving play/pause state.

Model Selection UX

  • Settings:
    • AI connection (Options โ†’ Runtime): Direct | Daemon.
    • Direct provider and credential/base URL (Options โ†’ Runtime). auto uses the configured provider, otherwise Gemini Nano; an explicit provider prefix overrides the selection.
    • Media/slide runtime (Options โ†’ Runtime): Browser | Daemon.
    • Daemon port (Options โ†’ Runtime; default 8787): must match the installed daemon port.
    • Model preset: auto | Gemini Nano | free | custom string (e.g. openai/gpt-5-mini, openrouter/..., github-copilot/...). Explicit Gemini Nano summaries stay on-device in either connection mode.
    • Length: short|medium|long|xl|xxl (or a character target like 20k). Tooltips show target ranges + paragraph guidance (from packages/core/src/prompts/summary-lengths.ts).
    • Language: auto (match source) or a tag like en, de, pt-BR (or free-form like โ€œGermanโ€).
    • Prompt override (advanced): custom instruction prefix (context + content still appended).
    • Auto summarize: on/off.
    • Hover summaries: on/off (side panel drawer, default off).
    • Typography: font family (dropdown + custom), font size (slider).
  • Advanced overrides (Options โ†’ Advanced tab).
    • Leave blank to use daemon config/defaults; set a value to override.
    • Chat: enabled for a configured Direct provider or authenticated daemon.
    • Summary timestamps (advanced): include [mm:ss] links in summaries for media when available (default on).
    • Slides parallel (advanced): show summary first and extract slides in parallel (default on).
    • Slides OCR text (advanced): allow OCR text as a slide text source (default off).
    • Extended logging: send full input/output to daemon logs (requires daemon logging enabled).
    • Hover summary prompt: customize the prompt used for link hover summaries (prefilled; reset to default).
    • Pipeline mode: page|url (default auto).
    • Firecrawl: off|auto|always.
    • Markdown mode: readability|llm|auto|off.
  • Preprocess: off|auto|always.
  • YouTube mode: no-auto|yt-dlp|web|apify (default auto).
  • Timeout (e.g. 90s, 2m), retries, max output tokens (e.g. 2k).
  • Process manager: live list of daemon-spawned tools (ffmpeg, yt-dlp, tesseract, etc.) with logs.
  • Extension includes current settings in request; daemon treats them like CLI flags (--model, --length, --language, --prompt).

Token Pairing / Setup Mode

Problem: daemon must be secured; extension must discover and pair with it.

  • Side panel โ€œSetupโ€ state:
    • Generates token (random, 32+ bytes).
    • Shows:
      • summarize daemon install --token <TOKEN> --port (macOS: LaunchAgent, Linux: systemd user, Windows: Scheduled Task)
      • summarize daemon status
    • โ€œCopy commandโ€ button.
  • Daemon stores paired tokens in ~/.summarize/daemon.json.
  • Extension stores token in chrome.storage.local.
  • Chrome declares nativeMessaging as an optional named permission. It is requested only when the
    user enables the local companion. The registered host is bound to the exact Web Store extension
    ID, accepts only daemon API paths, and rejects a port that does not match daemon.json.
  • If daemon unreachable or 401: show Setup state + troubleshooting.

Daemon Endpoints

  • GET /health
    • 200 JSON: { ok: true, pid }
  • GET /v1/ping
    • Requires auth; returns { ok: true }
  • POST /v1/summarize
    • Headers: Authorization: Bearer <token>
    • Body:
      • url: string (required)
      • title: string | null
      • model?: string (e.g. auto, free, openai/gpt-5-mini, ...)
      • length?: string (e.g. short, xl, 20k)
      • language?: string (e.g. auto, en, de, pt-BR)
      • prompt?: string (custom instruction prefix)
      • mode?: "auto" | "page" | "url" (default: "auto")
      • maxCharacters?: number | null (caps URL-mode extraction before summarization; ignored for extract-only unless explicitly provided)
      • format?: "text" | "markdown" (default: "text")
      • markdownMode?: "readability" | "auto" | "llm" | "off" (only when format: "markdown")
      • preprocess?: "off" | "auto" | "always" (markitdown/HTML preprocess)
      • extractOnly?: boolean (when true, returns extracted content without summarizing; requires mode: "url")
      • text?: string (required for mode: "page"; optional for auto)
      • truncated?: boolean (optional; indicates extracted text was shortened)
    • 200 JSON: { ok: true, id }
  • GET /v1/summarize//slides/events
    • Headers: Authorization: Bearer <token>
    • SSE stream of slide updates (slides, status, done, error) independent of summary stream.
  • POST /v1/agent (SSE by default; JSON via Accept: application/json or ?format=json)
    • Headers: Authorization: Bearer <token>
    • Body:
      • url: string (required)
      • title?: string | null
      • pageContent: string
      • cacheContent?: string (used for cache key; defaults to pageContent)
      • messages: Array<Message> (pi-ai format)
      • model?: string
      • length?: string (e.g. short, xl, 20k)
      • language?: string (e.g. auto, en, de)
      • tools?: string[]
      • automationEnabled?: boolean
    • SSE events:
      • event: chunk data: { text }
      • event: assistant data: { ...assistant }
      • event: done data: {}
      • event: error data: { message }
  • POST /v1/agent/history
    • Headers: Authorization: Bearer <token>
    • Body:
      • url: string (required)
      • pageContent: string
      • cacheContent?: string (used for cache key; defaults to pageContent)
      • model?: string
      • length?: string
      • language?: string
      • automationEnabled?: boolean
    • 200 JSON: { ok: true, messages }
  • GET /v1/summarize/:id/events (SSE)
    • event: chunk data: { text }
    • event: meta data: { model }
    • event: status data: { text } (progress messages before output starts)
    • event: metrics data: { elapsedMs, summary, details, summaryDetailed, detailsDetailed }
    • event: done data: {}
    • event: error data: { message }

Notes:

  • SSE keeps the extension simple + streaming-friendly.
  • Requests keyed by id; daemon keeps a small in-memory map while streaming.

Daemon Autostart

  • CLI commands:
    • summarize daemon install --token <token> [--port ]
      • Writes ~/.summarize/daemon.json
      • Installs platform autostart service; verifies /health
    • summarize daemon uninstall
    • summarize daemon status
    • summarize daemon run (foreground; used by autostart service)
  • Ensure โ€œsingle daemonโ€:
    • Stable service name + predictable unit/task path
    • install reuses the same daemon service and appends new tokens instead of invalidating older paired browsers

Platform details:

  • macOS: LaunchAgent plist in ~/Library/LaunchAgents/<label>.plist
  • Linux: systemd user unit in ~/.config/systemd/user/summarize-daemon.service
  • Windows: Scheduled Task โ€œSummarize Daemonโ€ + ~/.summarize/daemon.cmd

Docs

  • docs/chrome-extension.md (this file): architecture + setup + troubleshooting.
  • Main README.md: link to extension doc and โ€œQuickstart: 2 commands + load unpackedโ€.
  • apps/chrome-extension/README.md: extension-specific dev/build/load-unpacked instructions.

Status

  • Implemented (daemon + CLI + Chrome extension).

File: docs/cli.md


title: "CLI providers"
kicker: "models"
summary: "CLI model providers and config for Claude, Codex, Gemini, Cursor Agent, OpenClaw, OpenCode, GitHub Copilot, Antigravity, and pi."
read_when:

  • "When changing CLI model integration."

CLI models

Summarize can use installed CLIs (Claude, Codex, Gemini, Cursor Agent, OpenClaw, OpenCode, GitHub Copilot, Antigravity, pi) as local model backends.

Model ids

  • cli/claude/<model> (e.g. cli/claude/sonnet)
  • cli/codex/<model> (e.g. cli/codex/gpt-5.5)
  • cli/gemini/<model> (e.g. cli/gemini/flash)
  • cli/agent/<model> (e.g. cli/agent/auto)
  • cli/openclaw/<model> (e.g. cli/openclaw/main)
  • openclaw/<model> (alias for the same OpenClaw CLI path)
  • cli/opencode/<model> (e.g. cli/opencode/openai/gpt-5.4)
  • cli/opencode (use the OpenCode runtime default model)
  • cli/copilot/<model> (e.g. cli/copilot/gpt-5.2)
  • cli/copilot (use the Copilot CLI runtime default model)
  • cli/agy (use the Antigravity CLI active session model)
  • cli/pi/<model> (e.g. cli/pi/sonnet)
  • cli/pi (use pi's configured default model)

Use --cli [provider] (case-insensitive) for the provider default, or --model cli//<model> to pin a model.
Bare cli/codex defaults to GPT-5.5; set cli.codex.model to override it.
Antigravity does not support per-call model selection in print mode, so use cli/agy without a model suffix. pi supports cli/pi/<model> or a configured cli.pi.model.
If --cli is provided without a provider, auto selection is used with CLI enabled.

Codex GPT Fast:

  • --model codex-fast maps to cli/codex/gpt-fast.
  • OpenAI fast mode is not a CLI model. Use --model openai/gpt-5.5 --fast --thinking medium; see docs/openai.md.

Auto mode

Auto mode can prepend CLI attempts in two ways:

  • cli.enabled set in config:
    • Auto always uses this list order.
    • Also acts as an allowlist for explicit --cli / --model cli/....
  • Auto CLI fallback (cli.autoFallback, default enabled):
    • Applies only to implicit auto (when no model is set via flag/env/config).
    • Default behavior: only when no API key is configured.
    • Default order: claude, gemini, codex, agent, openclaw, opencode, copilot.
    • Antigravity and pi are opt-in unless added to cli.autoFallback.order.
    • Remembers + prioritizes the last successful CLI provider (~/.summarize/cli-state.json).

Gemini CLI performance: summarize sets GEMINI_CLI_NO_RELAUNCH=true for Gemini CLI runs to avoid a costly self-relaunch (can be overridden by setting it yourself).

Set explicit CLI allowlist:

json
{
  "cli": { "enabled": ["gemini"] }
}

Configure auto CLI fallback:

json
{
  "cli": {
    "autoFallback": {
      "enabled": true,
      "onlyWhenNoApiKeys": true,
      "order": ["claude", "gemini", "codex", "agent", "openclaw", "opencode", "copilot"]
    }
  }
}

Disable auto CLI fallback:

json
{
  "cli": { "autoFallback": { "enabled": false } }
}

Note: --model auto (explicit) does not trigger auto CLI fallback unless cli.enabled is set.

CLI discovery

Binary lookup:

  • CLAUDE_PATH, CODEX_PATH, GEMINI_PATH (optional overrides)
  • AGENT_PATH (optional override)
  • OPENCLAW_PATH (optional override)
  • OPENCODE_PATH (optional override)
  • COPILOT_PATH (optional override)
  • AGY_PATH (optional override)
  • PI_PATH (optional override)
  • Otherwise uses PATH

Run summarize status to list enabled CLI providers whose executable is currently available.
summarize status --verbose also prints the resolved executable path and configured CLI model.
Missing or disabled providers are omitted.

Attachments (images/files)

When a CLI attempt is used for an image or non-text file, Summarize switches to a
path-based prompt and enables the required tool flags:

  • Claude: --tools Read --dangerously-skip-permissions
  • Gemini: --yolo and --include-directories <dir>
  • Codex: codex exec --output-last-message ... and -i for images
  • Agent: uses built-in file tools in agent --print mode (no extra flags)
  • OpenCode: opencode run --format json ... --file when a file/image path is required
  • Copilot: copilot -p ; passes --model <model> when one is configured
  • Antigravity: agy --print; does not auto-approve tools for attachment prompts
  • pi: pi --print --mode json; passes --system-prompt, sends prompt over stdin, and uses --no-tools for isolated summaries

Remote image and binary-file URLs are limited to native providers and CLI backends that can consume the staged attachment without broad tool approval. Local file inputs keep the existing path-based CLI behavior because they are operator-selected files rather than untrusted remote content.

Config

json
{
  "cli": {
    "enabled": [
      "claude",
      "gemini",
      "codex",
      "agent",
      "openclaw",
      "opencode",
      "copilot",
      "agy",
      "pi"
    ],
    "autoFallback": {
      "enabled": true,
      "onlyWhenNoApiKeys": true,
      "order": ["claude", "gemini", "codex", "agent", "openclaw", "opencode", "copilot"]
    },
    "codex": { "model": "gpt-5.5" },
    "gemini": { "model": "flash", "extraArgs": ["--verbose"] },
    "claude": {
      "model": "sonnet",
      "binary": "/usr/local/bin/claude",
      "extraArgs": ["--verbose"]
    },
    "agent": {
      "model": "auto",
      "binary": "/usr/local/bin/agent"
    },
    "openclaw": {
      "model": "main",
      "binary": "/usr/local/bin/openclaw"
    },
    "opencode": {
      "binary": "/usr/local/bin/opencode"
    },
    "copilot": {
      "binary": "/usr/local/bin/copilot"
    },
    "agy": {
      "binary": "/usr/local/bin/agy"
    },
    "pi": {
      "binary": "/usr/local/bin/pi"
    }
  }
}

Notes:

  • CLI output is treated as text only (no token accounting).
  • If a CLI call fails, auto mode falls back to the next candidate.
  • Cursor Agent CLI uses the agent binary and relies on Cursor CLI auth (login or CURSOR_API_KEY).
  • Antigravity CLI uses the active agy session model; cli.agy.model is ignored by runtime selection.
  • Antigravity normal text summaries run agy --print in a temporary cwd with --sandbox. Because current agy print mode requires the prompt in argv, large prompts are rejected before launch using platform-specific argv limits; use another CLI/provider for large or sensitive extracted content. Attachment prompts keep the caller cwd so agy can inspect the requested path but do not auto-approve tools.
  • pi runs in JSON mode (--print --mode json) with --no-tools, --no-context-files, --no-extensions, --no-skills, --no-session for isolated summarization. It receives the summarize system prompt via --system-prompt, the summarize prompt over stdin, and reports usage/cost via JSONL events. Use PI_PATH to override the binary path.
  • Codex CLI normal text summaries run isolated by default: codex exec --ephemeral --ignore-user-config --ignore-rules -C <temp-dir> ... with a sanitized temporary CODEX_HOME that carries auth only. Set cli.codex.isolated to false only when you intentionally need Codex to inherit local config/rules.
  • Gemini CLI is invoked in headless mode with --prompt for compatibility with current Gemini CLI releases.
  • OpenClaw uses openclaw agent --agent <model> --message --json because current OpenClaw requires -m/--message; very large extracted inputs are rejected before launch to avoid argv limits.
  • OpenCode uses opencode run --format json, streams prompt text over stdin, and uses the runtime default model when none is configured.
  • Copilot uses copilot -p, treats stdout as plain text, and uses the runtime default model when none is configured.

Quick smoke test (all CLI providers)

Use a tiny local text file and run each provider with a longer timeout (Gemini can be slower):

bash
printf "Summarize CLI smoke input.\nOne short paragraph. Reply can be brief.\n" >/tmp/summarize-cli-smoke.txt

summarize --cli codex --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli claude --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli gemini --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli agent --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli openclaw --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli opencode --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli copilot --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli agy --plain --timeout 2m /tmp/summarize-cli-smoke.txt
summarize --cli pi --plain --timeout 2m /tmp/summarize-cli-smoke.txt

If Agent fails with auth, run agent login (interactive) or set CURSOR_API_KEY.

Generate free preset (OpenRouter)

summarize ships with a built-in preset free, backed by OpenRouter :free models.
To regenerate the candidate list (and persist it in your config):

bash
summarize refresh-free

Options:

  • --runs 2 (default): extra timing runs per selected model (total runs = 1 + runs)
  • --smart 3 (default): number of โ€œsmart-firstโ€ picks (rest filled by fastest)
  • --set-default: also sets "model": "free" in ~/.summarize/config.json

File: docs/config.md


title: "Config"
kicker: "get started"
summary: "Config file location, precedence, and schema."
read_when:

  • "When adding config keys or defaults."

Config

summarize supports an optional JSON config file for defaults.

Location

Default path:

  • ~/.summarize/config.json

Status

Inspect the effective model selection and positive provider setup:

bash
summarize status
summarize status --verbose
summarize status --probe
summarize status --json

The command omits missing providers and never prints secret values. API providers appear as
configured when an effective key is present. Enabled CLI providers appear as available when
their executable can be resolved. --probe checks supported OpenAI-compatible/local model-list
endpoints without running inference and marks successful providers as usable.

Precedence

For model:

  1. CLI flag --model
  2. Env SUMMARIZE_MODEL
  3. Config file model
  4. Built-in default (auto)

For output language:

  1. CLI flag --language / --lang
  2. Config file output.language (preferred) or language (legacy)
  3. Built-in default (auto = match source content language)

For output length:

  1. CLI flag --length
  2. Config file output.length
  3. Built-in default (long)

See docs/language.md for supported values.

For prompt:

  1. CLI flag --prompt / --prompt-file
  2. Config file prompt
  3. Built-in default prompt

For environment variables:

  1. Process environment variables
  2. Config file env
  3. Legacy config file apiKeys (mapped to env names)

For UI theme:

  1. CLI flag --theme
  2. Env SUMMARIZE_THEME
  3. Config file ui.theme
  4. Built-in default (aurora)

Format

~/.summarize/config.json:

json
{
  "model": { "id": "google/gemini-3-flash" },
  "env": { "OPENAI_API_KEY": "sk-..." },
  "output": { "language": "auto", "length": "long" },
  "prompt": "Explain like I am five.",
  "ui": { "theme": "ember" }
}

output.length accepts the same values as --length:

  • Presets: short, medium, long, xl, xxl
  • Shorthand: s, m, l
  • Character targets: 1500, 20k, 20000

Shorthand (equivalent):

json
{
  "model": "google/gemini-3-flash"
}

model can also be auto:

json
{
  "model": { "mode": "auto" }
}

Shorthand (equivalent):

json
{
  "model": "auto"
}

Prompt

prompt replaces the built-in summary instructions (same behavior as --prompt).

Example:

json
{
  "prompt": "Explain for a kid. Short sentences. Simple words."
}

Environment defaults

Set any env var in config (process env still wins):

json
{
  "env": {
    "ASSEMBLYAI_API_KEY": "...",
    "OPENAI_API_KEY": "sk-...",
    "OPENROUTER_API_KEY": "sk-or-...",
    "FIRECRAWL_API_KEY": "...",
    "CUSTOM_FLAG": "1"
  }
}

Legacy shortcut (still supported):

json
{
  "apiKeys": {
    "openai": "sk-...",
    "groq": "gsk-...",
    "assemblyai": "...",
    "elevenlabs": "...",
    "anthropic": "sk-ant-...",
    "google": "...",
    "openrouter": "sk-or-...",
    "xai": "...",
    "zai": "...",
    "apify": "...",
    "firecrawl": "...",
    "fal": "..."
  }
}

Cache

Configure the on-disk SQLite cache (extracted content, transcripts, summaries).

json
{
  "cache": {
    "enabled": true,
    "maxMb": 512,
    "ttlDays": 30,
    "path": "~/.summarize/cache.sqlite",
    "media": {
      "enabled": true,
      "maxMb": 2048,
      "ttlDays": 7,
      "path": "~/.summarize/cache/media",
      "verify": "size"
    }
  }
}

Notes:

  • cache.media controls the media file cache (yt-dlp downloads).
  • --no-cache bypasses summary caching only (LLM output); extract/transcript caches still apply. Use --no-media-cache for media.
  • verify: size (default), hash, or none.

UI theme

Set a default CLI theme:

json
{
  "ui": { "theme": "moss" }
}

Slides defaults

Enable slides by default and tune extraction parameters:

json
{
  "slides": {
    "enabled": true,
    "ocr": false,
    "dir": "slides",
    "sceneThreshold": 0.3,
    "max": 20,
    "minDuration": 2
  }
}

Speaker identification

Define reusable names and context for diarized YouTube transcripts:

json
{
  "speakers": {
    "defaultProfile": "modern-wisdom",
    "autoIdentify": true,
    "model": "openai/gpt-5.5",
    "minimumConfidence": 0.85,
    "profiles": {
      "modern-wisdom": {
        "host": "Chris Williamson",
        "knownSpeakers": ["Chris Williamson"],
        "context": "Modern Wisdom podcast. Chris Williamson is the host."
      }
    }
  }
}

Run with --diarize, then use --speaker-profile, repeatable --speaker-at <timestamp=name>, and
--remember-speakers to verify and persist a video's label mapping. autoIdentify enables identity
resolution whenever diarization is requested. --no-identify-speakers disables it for one run.

Remembered mappings live under speakers.sources["youtube:<video-id>"]. They include a transcript hash
and are ignored if provider output changes. Profile-level model, minimumConfidence, and autoIdentify
override the corresponding top-level speaker settings.

Logging (daemon)

Enable JSON log files for the daemon:

json
{
  "logging": {
    "enabled": true,
    "level": "info",
    "format": "json",
    "file": "~/.summarize/logs/daemon.jsonl",
    "maxMb": 10,
    "maxFiles": 3
  }
}

Notes:

  • Default: logging is off.
  • format: json (default) or pretty.
  • maxMb is per file; maxFiles controls rotation (ring).
  • Extension โ€œExtended loggingโ€ sends full input/output to daemon logs (large). Cache hits skip content logging.

Presets

Define presets you can select via --model :

json
{
  "models": {
    "small": { "id": "openai/gpt-5-mini" },
    "or-free": {
      "rules": [
        {
          "candidates": [
            "openrouter/google/gemini-2.0-flash-exp:free",
            "openrouter/meta-llama/llama-3.3-70b-instruct:free"
          ]
        }
      ]
    }
  }
}

Notes:

  • auto is reserved and canโ€™t be defined as a preset.
  • free is built-in (OpenRouter :free candidates). Override it by defining models.free in your config, or regenerate it via summarize refresh-free.
  • gpt-fast and fast remain compatibility aliases for OpenAI GPT-5.5 Fast mode. Prefer model: "openai/gpt-5.5" plus openai.serviceTier / openai.thinking. codex-fast is the explicit Codex CLI fast preset.

OpenAI request options can be set globally:

json
{
  "openai": {
    "serviceTier": "fast",
    "thinking": "medium",
    "textVerbosity": "low"
  }
}

Or per preset:

json
{
  "models": {
    "fast-mini": {
      "id": "openai/gpt-5.4-mini",
      "serviceTier": "fast",
      "thinking": "low"
    }
  }
}

thinking is an alias for reasoningEffort. Supported values: none, low, medium, high, xhigh; shorthand aliases include off, min (low), mid, med, x-high, and extra-high.

serviceTier: "fast" is the summarize/Codex-facing spelling. Direct OpenAI requests map it to service_tier="priority". CLI --service-tier default clears a configured tier for one run.

Use a preset as your default model:

json
{
  "model": "small"
}

Notes:

  • For presets, "mode": "auto" is optional when "rules" is present.

For auto selection with rules:

json
{
  "model": {
    "mode": "auto",
    "rules": [
      {
        "when": ["video"],
        "candidates": ["google/gemini-3-flash"]
      },
      {
        "when": ["website", "youtube"],
        "bands": [
          {
            "token": { "max": 8000 },
            "candidates": ["openai/gpt-5-mini"]
          },
          {
            "candidates": ["xai/grok-4-fast-non-reasoning"]
          }
        ]
      },
      {
        "candidates": ["openai/gpt-5-mini", "openrouter/openai/gpt-5-mini"]
      }
    ]
  },
  "media": {
    "videoMode": "auto",
    "embeddedVideo": "auto"
  }
}

media.embeddedVideo accepts "auto", "off", "prefer", or "both". Automatic mode uses only high-confidence primary YouTube embeds and free captions, combining the transcript with substantial article text.

Notes:

  • Parsed leniently (JSON5), but comments are not allowed.
  • Unknown keys are ignored.
  • model.rules is optional. If omitted, built-in defaults apply.
  • model.rules[].when (optional) must be an array (e.g. ["video","youtube"]).
  • model.rules[] must use either candidates or bands.

Output language

Set a default output language for summaries:

json
{
  "output": { "language": "auto" }
}

Examples:

  • "auto" (default): match the source language.
  • "en", "de": common shorthands.
  • "english", "german": common names.
  • "en-US", "pt-BR": BCP-47-ish tags.

CLI config

json
{
  "cli": {
    "enabled": ["gemini", "agent", "openclaw", "opencode", "copilot", "agy", "pi"],
    "autoFallback": {
      "enabled": true,
      "onlyWhenNoApiKeys": true,
      "order": ["claude", "gemini", "codex", "agent", "openclaw", "opencode", "copilot"]
    },
    "codex": { "model": "gpt-5.5" },
    "claude": { "binary": "/usr/local/bin/claude", "extraArgs": ["--verbose"] },
    "agent": { "binary": "/usr/local/bin/agent", "model": "gpt-5.2" },
    "openclaw": { "binary": "/usr/local/bin/openclaw", "model": "main" },
    "opencode": { "binary": "/usr/local/bin/opencode", "model": "openai/gpt-5.4" },
    "copilot": { "binary": "/usr/local/bin/copilot", "model": "gpt-5.2" },
    "agy": { "binary": "/usr/local/bin/agy" },
    "pi": { "binary": "/usr/local/bin/pi" }
  }
}

Notes:

  • cli.enabled is an allowlist (and order) for auto + explicit CLI model ids.
  • cli.autoFallback controls implicit-auto CLI fallback when cli.enabled is not set.
  • Default auto fallback order: claude, gemini, codex, agent, openclaw, opencode, copilot.
  • Antigravity and pi are opt-in unless added to cli.autoFallback.order.
  • Auto fallback stores the last successful provider in ~/.summarize/cli-state.json and prioritizes it on the next run.
  • cli..binary overrides CLI binary discovery.
  • cli..extraArgs appends extra CLI args.
  • Antigravity CLI uses the active agy session model; cli.agy.model is ignored by runtime selection.
  • pi CLI runs pi --print --mode json, passes extracted content over stdin, and accepts configured or per-call models; use PI_PATH to override binary.
  • cli.codex.isolated defaults to true for normal summaries, adding Codex ephemeral/no-user-config/no-rules flags, a temporary cwd, and a sanitized temporary CODEX_HOME that carries auth only. Set it to false only when local Codex config/rules are intentional.

OpenAI config

json
{
  "openai": {
    "baseUrl": "https://my-openai-proxy.example.com/v1",
    "useChatCompletions": true,
    "whisperUsdPerMinute": 0.006
  }
}

Notes:

  • openai.baseUrl overrides the OpenAI-compatible API endpoint. Use this for proxies, gateways, or OpenAI-compatible APIs. Env OPENAI_BASE_URL takes precedence.
  • openai.whisperUsdPerMinute is only used to estimate transcription cost in the finish-line metrics when Whisper transcription runs via OpenAI.

Provider base URLs

Override API endpoints for any provider to use proxies, gateways, or compatible APIs:

json
{
  "openai": { "baseUrl": "https://my-openai-proxy.example.com/v1" },
  "nvidia": { "baseUrl": "https://integrate.api.nvidia.com/v1" },
  "anthropic": { "baseUrl": "https://my-anthropic-proxy.example.com" },
  "google": { "baseUrl": "https://my-google-proxy.example.com" },
  "xai": { "baseUrl": "https://my-xai-proxy.example.com" },
  "zai": { "baseUrl": "https://api.zhipuai.cn/paas/v4" },
  "minimax": { "baseUrl": "https://api.minimax.io/v1" },
  "ollama": { "baseUrl": "http://localhost:11434/v1" }
}

Or via environment variables (which take precedence over config):

Provider Environment Variable(s)
OpenAI OPENAI_BASE_URL
NVIDIA NVIDIA_BASE_URL
Anthropic ANTHROPIC_BASE_URL
Google GOOGLE_BASE_URL (alias: GEMINI_BASE_URL)
xAI XAI_BASE_URL
Z.AI Z_AI_BASE_URL (alias: ZAI_BASE_URL)
MiniMax MINIMAX_BASE_URL
Ollama OLLAMA_BASE_URL

File: docs/extract-only.md


title: "Extract"
kicker: "modes"
summary: "Extract mode behavior and markdown handling."
read_when:

  • "When changing extract pipeline or flags."

Extract mode

--extract prints the extracted content and exits.

Deprecated alias: --extract-only.

Notes

  • No summarization LLM call happens in this mode.
  • Supported inputs are URLs, YouTube/direct media, Loom share/embed recordings, local audio/video, and local PDF files (- stdin is not supported with --extract).
  • Extraction can still use configured remote transcription, OCR, or Markdown providers for media/PDF inputs. For sensitive files, use local transcription/provider settings for media and --preprocess off when you need to block PDF/Markdown preprocessing.
  • No extraction cap is applied. Use --max-extract-characters <count> to cap output if needed.
  • --format md may still convert HTML to Markdown (depending on --markdown-mode and available tools).
  • --length is intended for summarization guidance; extraction prints full content.
  • --timestamps keeps the plain transcript text but also exposes transcriptSegments and transcriptTimedText (JSON) and prints a timed transcript block when available.
  • YouTube transcript output includes the public view count when available. JSON exposes extracted.sourceMetrics with platform, videoId, viewCount, and observedAt; human output prints YouTube views: <count> before the transcript. Metrics refresh at least hourly even when the transcript remains cached.
  • --slides runs slide detection (YouTube/direct video URLs/local video files). Slide metadata is included in JSON output and written to slides.json in the slide directory.
    • When combined with --extract for videos that have timed transcripts, the CLI interleaves slide images inline at matching timestamps.
    • Scene detection auto-tunes using sampled frame hashes.
  • For non-YouTube URLs with --format md, the CLI uses Readability article HTML as the default Markdown input (--markdown-mode readability).
    • Use --markdown-mode auto to prefer LLM/markitdown conversion without Readability preprocessing.
    • Use --markdown-mode llm to force an LLM conversion.
    • Use --firecrawl always to try Firecrawl first for non-YouTube URLs.
  • For non-YouTube URLs with --format md, --markdown-mode auto can convert HTML to Markdown via an LLM when configured.
    • Force it with --markdown-mode llm.
    • If no LLM is configured, --markdown-mode auto may fall back to uvx markitdown when available.
  • --markdown-mode readability uses Readability to extract article HTML before Markdown conversion.

Daemon note:

  • /v1/summarize supports format: "markdown" + markdownMode for extract-only output (use extractOnly: true).

File: docs/firecrawl.md


title: "Firecrawl"
kicker: "modes"
summary: "Firecrawl fallback modes and API key usage."
read_when:

  • "When changing Firecrawl behavior."

Firecrawl mode

Firecrawl is a fallback for sites that block direct HTML fetching or donโ€™t render meaningful content without JS.

`--firecrawl off|auto|always`

  • off: never use Firecrawl.
  • auto (default): use Firecrawl only when HTML extraction looks blocked/thin.
  • always: try Firecrawl first for non-YouTube URLs (falls back to HTML if Firecrawl is unavailable/empty).
    • YouTube URLs reject --firecrawl always; use --youtube auto|web|yt-dlp|apify instead.

Extract default

When --extract --format md is used for non-YouTube URLs and FIRECRAWL_API_KEY is configured, the CLI defaults to --firecrawl always to return Markdown.

API key

  • FIRECRAWL_API_KEY (required for Firecrawl requests)

2. Official Technical Reference & Guides (steipete/docs)

Mintlify Starter Kit

Use the starter kit to get your docs deployed and ready to customize.

Click the green Use this template button at the top of this repo to copy the Mintlify starter kit. The starter kit contains examples with

  • Guide pages
  • Navigation
  • Customizations
  • API reference pages
  • Use of popular components

Follow the full quickstart guide

Development

Install the Mintlify CLI to preview your documentation changes locally. To install, use the following command:

text
npm i mint

Run the following command at the root of your documentation, where your docs.json is located:

text
mint dev

View your local preview at http://localhost:3000.

Publishing changes

Install our GitHub app from your dashboard to propagate changes from your repo to your deployment. Changes are deployed to production automatically after pushing to the default branch.

Need help?

Troubleshooting

  • If your dev environment isn't running: Run mint update to ensure you have the most recent version of the CLI.
  • If a page loads as a 404: Make sure you are running in a folder with a valid docs.json.

Resources