| Destination | What was wrong |
| Gemini | Takes prompts over an HTTP header the browser sets, not a URL parameter. A link can't set headers, so ?q= was always inert. |
| Microsoft Copilot | Prefill regressed in late 2025 and was never restored. |
| ChatGPT | ?q= only reaches the composer via a fallback redirect. ?prompt= is stable. |
| Google AI Studio | Uses ?prompt=, not ?q=. |
| Perplexity | Needs /search/new, not /search. |
| Claude | Real cap is ~14,000 characters, not the 100,000 claimed. |
1npm i @raptrx/askai
12import { AskAI } from '@raptrx/askai/react'; import '@raptrx/askai/styles.css';
1<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.1npx @raptrx/askai list
12345const result = buildPrompt('Review this', longFile, 'claude'); result.truncated; // true result.droppedChars; // 36114 result.autoSubmit; // false — Claude fills the composer, user presses Enter
result.autoSubmit to label
honestly; the built-in menu already does.12345<AskAI goal="Explain this function" content={code} services={['chatgpt', 'claude', 'perplexity']} />
goalstringrequiredcontentstring | { text, language } | objectrequiredlanguage hint forces
a code fence.servicesstring[] | 'default' | 'all''default' is a short curated set. 'all' is every verified destination.labelstringtheme'light' | 'dark' | 'auto'unstyledbooleanclassNamesAskAIClassNamesroot, group, copyButton, trigger, menu,
item, itemLabel, itemHint, icon, divider, caret, separator,
footer.iconsRecord<string, Component>onCopy / onOpenfunctiononOpen receives the full result including autoSubmit and
truncated.123<AskAILink service="claude" goal="Review this" content={code}> Ask Claude </AskAILink>
<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.12345678.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; }
123456789<AskAI goal="Explain this" content={code} classNames={{ copyButton: 'my-btn my-btn--ghost', menu: 'my-popover', item: 'my-menu-item', }} />
12345678910111213<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', }} />
askai-* class is emitted and you do not need the stylesheet. The ARIA
and keyboard behaviour stay.12345678910111213141516171819202122232425import { 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> ); }
npx @raptrx/askai list for the live
table.| Destination | Parameter | Cap | On arrival |
| ChatGPT | prompt | 16,000 | fills |
| Claude | q | 14,000 documented | fills |
| Perplexity | q | 12,000 | runs |
| Grok | q | 7,500 measured | runs |
| Kagi Assistant | q | 8,000 measured | runs |
| DeepSeek | q | 12,000 | fills |
| Le Chat | q | 10,000 | fills |
| T3 Chat | q | 12,000 | runs |
| HuggingChat | q | 8,000 measured | runs |
| Duck.ai | q | 12,000 measured | runs |
| Z.ai | q | 12,000 | runs |
| Kimi | prefill_prompt | 12,000 | fills |
| Qwen Chat | text | 12,000 | fills |
| Cursor | text | 5,000 | fills |
| Google AI Studio | prompt | 12,000 | fills |
| GitHub Copilot | prompt | 12,000 | fills |
| v0 | q | 12,000 | runs |
| Scira | q | 12,000 | runs |
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.?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.12345678import { createRegistry } from '@raptrx/askai'; const registry = createRegistry(); registry.add('acme', { name: 'Acme AI', url: 'https://ai.acme.internal/chat', param: 'prompt', });
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.123import { logos } from '@raptrx/askai/logos'; <AskAI icons={logos} goal="Explain this" content={code} />
1npx @raptrx/askai init
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.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.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.ServiceConfig shape is still accepted by addService().AiButton / AiButtonBar → AskAI / AskAILink.window.open.'use client' now survives bundling — the 1.x React entry threw on first
render under the Next.js App Router.@raptrx/askai/styles.css.@raptrx/askai/logos.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122npx @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
llms.txt index.