Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.
v2.0 is a breaking release. Six of the ten destinations in 1.x were sending parameters that no longer worked. If you are upgrading, read What changed in 2.0 first.

The problem

Every AI vendor accepts a prompt in a URL. None of them document it, none of them promise it will keep working, and they change it without notice.
So most published "Ask AI" buttons are quietly broken. The tab still opens — it just opens empty, and your user blames your product. Six of the ten destinations this package itself shipped in 1.x were wrong:
DestinationWhat was wrong
GeminiTakes prompts over an HTTP header the browser sets, not a URL parameter. A link can't set headers, so ?q= was always inert.
Microsoft CopilotPrefill regressed in late 2025 and was never restored.
ChatGPT?q= only reaches the composer via a fallback redirect. ?prompt= is stable.
Google AI StudioUses ?prompt=, not ?q=.
PerplexityNeeds /search/new, not /search.
ClaudeReal cap is ~14,000 characters, not the 100,000 claimed.

Install

npm i @raptrx/askai
  1. Import the component and the stylesheet
    import { AskAI } from '@raptrx/askai/react'; import '@raptrx/askai/styles.css';
    The stylesheet import goes anywhere in your app, once. Skip it and the control renders unstyled.
  2. Drop it next to your content
    <AskAI goal="Explain this function" content={code} />
    content is the point — pass the actual code, error, article or record the user is looking at, not a static string.
  3. Check what it produces
    npx @raptrx/askai list
    Prints every destination with its parameter, cap, tier, and whether it runs the prompt or just fills the composer.

Why copy is the primary action

The control is a split button: Copy prompt on the left, destinations behind the caret. That ordering is deliberate.
Deep links are bounded by URL length — Claude cuts at ~14,000 characters, Grok at ~7,500, Cursor at ~5,000 of usable prose. Sending an AI half a function and letting it answer confidently about the wrong code is worse than sending nothing. Copying has no ceiling, so the primary action always works.
When content has to be shortened to fit a link, the result says so instead of hiding it:
const result = buildPrompt('Review this', longFile, 'claude'); result.truncated; // true result.droppedChars; // 36114 result.autoSubmit; // false — Claude fills the composer, user presses Enter
Never write copy that promises one-click. Most destinations fill the composer and wait for Enter. Every documented change in this space has removed auto-submit, never prefill — a link on someone else's page must not make a logged-in assistant execute a prompt. Use result.autoSubmit to label honestly; the built-in menu already does.

Components

AskAI
The split button. Copy plus a menu of destinations, with full keyboard support.
AskAILink
One destination as a plain anchor. No state, no client boundary — works in a server component.
useAskAI
The behaviour with no markup. Bring your own design system.
buildPrompt
No React at all. Returns the URL plus truncation and submit metadata.

AskAI

<AskAI goal="Explain this function" content={code} services={['chatgpt', 'claude', 'perplexity']} />
goalstringrequired
What you want the AI to do.
contentstring | { text, language } | objectrequired
What to send. Objects are serialised to fenced JSON; a language hint forces a code fence.
servicesstring[] | 'default' | 'all'
'default' is a short curated set. 'all' is every verified destination.
labelstring
Primary button label.
theme'light' | 'dark' | 'auto'
Follows the system unless forced.
unstyledboolean
Drops every built-in class. See Matching your design system.
classNamesAskAIClassNames
Per-part class overrides: root, group, copyButton, trigger, menu, item, itemLabel, itemHint, icon, divider, caret, separator, footer.
iconsRecord<string, Component>
Custom icon per service. See Logos.
onCopy / onOpenfunction
Analytics hooks. onOpen receives the full result including autoSubmit and truncated.
<AskAILink service="claude" goal="Review this" content={code}> Ask Claude </AskAILink>
Renders a plain <a>. Because it is a real link, middle-click, Cmd-click, "open in new tab" and "copy link address" all behave — none of which work when a button calls window.open. It has no state or effects, so it needs no 'use client' boundary.

Matching your design system

The button should look like your product, not like this library. Four levels, in increasing order of control.
1. Retheme with CSS custom properties
Everything is driven by custom properties, so you can retheme without overriding a single rule.
.askai { --askai-bg: var(--my-surface); --askai-fg: var(--my-text); --askai-border: var(--my-border); --askai-focus: var(--my-ring); --askai-radius: 4px; --askai-duration: 100ms; }
2. Override individual parts
<AskAI goal="Explain this" content={code} classNames={{ copyButton: 'my-btn my-btn--ghost', menu: 'my-popover', item: 'my-menu-item', }} />
Your classes are merged after the built-ins, so they win.
3. Go fully unstyled
<AskAI goal="Explain this" content={code} unstyled classNames={{ root: 'relative inline-flex', group: 'inline-flex rounded-md border border-zinc-200 dark:border-zinc-800', copyButton: 'px-3 py-1.5 text-sm font-medium hover:bg-zinc-50', trigger: 'px-2 border-l border-zinc-200 hover:bg-zinc-50', menu: 'absolute right-0 top-full mt-1 min-w-48 rounded-md border bg-white p-1 shadow-lg', item: 'flex items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-zinc-100', }} />
No askai-* class is emitted and you do not need the stylesheet. The ARIA and keyboard behaviour stay.
4. Render it yourself with useAskAI
When the component can't bend far enough, take the behaviour and write your own markup. You keep prompt assembly, clipboard handling and the full menu-button keyboard pattern.
import { useAskAI } from '@raptrx/askai/react'; function MyAskAI({ code }) { const ai = useAskAI({ goal: 'Explain this', content: code }); return ( <div {...ai.getRootProps()}> <MyButton {...ai.getCopyButtonProps()}> {ai.copied ? 'Copied' : 'Copy'} </MyButton> <MyButton {...ai.getTriggerProps()}>▾</MyButton> {ai.isOpen && ( <MyMenu {...ai.getMenuProps()}> {ai.destinations.map((d, i) => ( <MyMenuItem {...ai.getItemProps(i)}> {d.name} <small>{d.autoSubmit ? 'Runs' : 'Ready'}</small> </MyMenuItem> ))} </MyMenu> )} </div> ); }

Destinations

Eighteen verified, two deprecated. Run npx @raptrx/askai list for the live table.
DestinationParameterCapOn arrival
ChatGPTprompt16,000fills
Claudeq14,000 documentedfills
Perplexityq12,000runs
Grokq7,500 measuredruns
Kagi Assistantq8,000 measuredruns
DeepSeekq12,000fills
Le Chatq10,000fills
T3 Chatq12,000runs
HuggingChatq8,000 measuredruns
Duck.aiq12,000 measuredruns
Z.aiq12,000runs
Kimiprefill_prompt12,000fills
Qwen Chattext12,000fills
Cursortext5,000fills
Google AI Studioprompt12,000fills
GitHub Copilotprompt12,000fills
v0q12,000runs
Sciraq12,000runs
Caps are labelled by provenance. documented means the vendor publishes it, measured means we observed it, and an unlabelled figure is a conservative floor we chose because no evidence exists. The registry never presents a guess as a fact.
Gemini and Microsoft Copilot are deprecated and will throw. Gemini accepts prompts only over an HTTP header the browser sets from its own address bar — a hyperlink cannot set headers, so ?q= opens an empty chat. Microsoft Copilot's prefill regressed in late 2025 and was never restored. Both are kept in the registry so you get a clear error instead of a silently empty tab. For a Google model, use aistudio.

Bring your own AI

Most companies with an assistant have their own. That's a first-class case:
import { createRegistry } from '@raptrx/askai'; const registry = createRegistry(); registry.add('acme', { name: 'Acme AI', url: 'https://ai.acme.internal/chat', param: 'prompt', });
Use createRegistry() rather than the global addService() anywhere requests share a process. The global registry is process-wide, so one tenant's destination would leak into another's request.

Logos

Neutral glyphs by default. Brand marks are one import away:
import { logos } from '@raptrx/askai/logos'; <AskAI icons={logos} goal="Explain this" content={code} />
Seven vendors ship their real mark — OpenAI, Claude, Grok, Perplexity, Mistral, Qwen and Z.ai — reproduced exactly as supplied, in their own colours. The rest use a monogram in their brand colour, because an approximated logo is worse than none: visibly wrong, and an altered mark is what vendors most clearly prohibit.
Logos sit behind a subpath rather than the package root on purpose. Using a third party's trademark is your product's decision, not a default you inherit. Most vendors require the mark be unaltered and several require written permission — check their brand guidelines before shipping.

CLI

Prefer to own the code, shadcn-style?
npx @raptrx/askai init
Generates core.ts, AskAiButton.tsx and AskAiLink.tsx containing only the destinations you pick, with verified parameters baked in. The generated output is compiled under strict TypeScript by this repo's test suite on every commit.

What changed in 2.0

Destinations
  • gemini and copilot now throw instead of opening an empty chat.
  • chatgpt and aistudio moved to ?prompt=.
  • perplexity moved to /search/new.
  • claude cap corrected to 14,000.
  • Ten destinations added; every entry now carries a tier and a verification date.
API
  • buildPrompt() returns truncation and auto-submit metadata. Truncation is never silent.
  • createRegistry() replaces global mutation for server use.
  • suggestService() removed — its heuristics were arbitrary.
  • options.model removed; Claude and Gemini never supported it.
  • The 1.x ServiceConfig shape is still accepted by addService().
React
  • AiButton / AiButtonBar → AskAI / AskAILink.
  • Navigation uses real anchors, not window.open.
  • 'use client' now survives bundling — the 1.x React entry threw on first render under the Next.js App Router.
  • Styles moved to @raptrx/askai/styles.css.
  • Vendor logos moved to @raptrx/askai/logos.

For coding agents

Paste this into Cursor, Claude Code or any coding agent. It covers installation, which surface to use, the failure modes that produce a button opening an empty chat, styling, and how to verify the integration actually works.
npx @raptrx/askai list` rather than guessed, which is * the same rule the package gives agents. */ export const ASKAI_AGENT_PROMPT = `# askai — integration instructions for a coding agent Add an "Ask AI" control to this project using @raptrx/askai. Follow these rules exactly. Several are counter-intuitive, and getting them wrong produces a button that opens an empty chat — which looks like the product is broken. ## 1. Install npm i @raptrx/askai Prefer the package: one dependency, and updates arrive when vendors change their parameters. Only run `npx @raptrx/askai init` (shadcn-style codegen) if this project vendors its UI or cannot take a dependency — it is interactive and needs a TTY. If you cannot drive a TTY, install the package rather than hand-writing the files. ## 2. Minimum correct integration import { AskAI } from '@raptrx/askai/react'; import '@raptrx/askai/styles.css'; // once, anywhere — do not skip this <AskAI goal="Explain this code" content={code} /> `content` is the entire point: pass the actual code, error, article or record the user is looking at, never a static string. Objects are serialised to fenced JSON automatically; `{ text, language }` forces a code fence. ## 3. Pick the right surface <AskAI> user picks the destination — split button, copy + menu <AskAILink> one fixed destination; a real <a>, so it works in a server component and supports middle-click and Cmd-click useAskAI() the same behaviour with your own markup buildPrompt() no React at all; returns { url, truncated, droppedChars, autoSubmit } ## 4. Rules that make or break the integration - NEVER promise one-click. Label it "Ask AI" or "Open in ChatGPT" — never "Run in ChatGPT" or "Get an answer". Most destinations only fill the composer and wait for the user to press Enter. `result.autoSubmit` tells you which ones actually run; the built-in menu already labels this correctly. - NEVER add `gemini` or Microsoft `copilot`. Gemini has never supported URL prefill — it takes the prompt from an HTTP header the browser sets from its own address bar, and a hyperlink cannot set headers. Microsoft Copilot's prefill was removed in 2025. Both are kept in the registry only so they throw a clear error instead of opening an empty tab. For a Google model use `aistudio`. Note GitHub Copilot is a different product and IS supported. - NEVER hand-write the URLs. `chatgpt.com/?q=` looks correct and is the fragile path; `?prompt=` is the stable one. The registry exists precisely so nobody has to remember this. - Destinations cap between roughly 5,000 and 16,000 characters. Do not pass huge content and assume it arrives. For anything long, let the user copy instead — that is what the primary button is for. Check `buildPrompt(...).truncated` if you need to know programmatically. - Logos are opt-in, not default: import { logos } from '@raptrx/askai/logos'; <AskAI icons={logos} ... /> Never hand-draw a mark to fill a gap. Destinations without a real logo use a monogram on purpose; an approximated mark is visibly wrong and is the thing vendors most clearly prohibit. ## 5. Service ids Confirmed ids: chatgpt, claude, perplexity, grok, deepseek, mistral (Le Chat), kagi (Kagi Assistant), t3chat, aistudio (Google AI Studio). Do not invent ids. Run `npx @raptrx/askai list` for the authoritative set — it prints every destination with its parameter, cap, tier, and whether it runs the prompt or only fills the composer. ## 6. Styling Retheme with CSS custom properties, no rule overrides needed: .askai { --askai-bg: var(--my-surface); --askai-fg: var(--my-text); --askai-border: var(--my-border); --askai-focus: var(--my-ring); --askai-radius: 4px; --askai-duration: 100ms; } Past that: per-part `classNames` (merged after the built-ins, so yours win), `unstyled` (no askai-* class is emitted and the stylesheet becomes unnecessary — the ARIA and keyboard behaviour stay), or `useAskAI` to render it yourself. ## 7. Server and multi-tenant Use `createRegistry()` rather than the global `addService()` anywhere requests share a process. The global registry is process-wide, so one tenant's destination would leak into another's request. import { createRegistry } from '@raptrx/askai'; const registry = createRegistry(); registry.add('acme', { name: 'Acme AI', url: 'https://ai.acme.internal/chat', param: 'prompt', }); ## 8. Verify before you call it done npx @raptrx/askai list Then click the control and confirm the prompt actually lands in the destination's composer. If it opens empty, the parameter changed — open an issue at https://github.com/aliarain/askai rather than patching around it, so the fix reaches everyone. Docs: https://docs.aliarain.com/askai Source: https://github.com/aliarain/askai
The repository also ships an AGENTS.md you can point an agent at directly, and every page on this site is available to LLMs as raw markdown via the docs' llms.txt index.