ghostfill
v0.3.0
Published
Stop demoing with test123. Realistic, domain-aware form data in one click.
Maintainers
Readme
GhostFill
Stop demoing with test123. Generate realistic, domain-aware form data in one click.
The Problem
Every team has this story. A PM is demoing to a client, and right there on the screen: "Cheeseburger" in the company name field, "asdf" for the email, and "John Doe" — the same John Doe — on every single record. QA tests with garbage. Designers screenshot forms full of "test123". There's no easy way to fill forms with realistic, context-appropriate data without writing seed scripts or maintaining test fixtures.
Before GhostFill:
First Name: test
Last Name: test
Email: [email protected]
Company: asdfasdf
Job Title: fjdksl
Phone: 1234567890
Address: 123 test streetAfter GhostFill:
First Name: Sarah
Last Name: Mitchell
Email: [email protected]
Company: Northwind Traders
Job Title: Senior Account Executive
Phone: +1 (312) 555-0187
Address: 401 N Michigan Ave, Chicago, IL 60611One click. Any form. No seed scripts.
Who It's For
Developers install it — npm install ghostfill -D, one import, done. But the whole team benefits:
- QA Engineers — fill forms with realistic edge cases instead of copy-pasting the same test data
- Product Managers — demo to clients with professional-looking data, not "test123"
- Designers — screenshot real-looking forms for decks and specs
- Solution Architects — show realistic D365, Salesforce, or ERP data during workshops
Not Another Faker
faker.js fills your database. Browser autofill fills your data. GhostFill fills the form you're looking at — visually, with data that fits the context.
| | GhostFill | faker.js | Browser Autofill | |---|---|---|---| | Who uses it | Anyone on the team | Developers only | Individual user | | How it works | Visual in-page UI | Code library | Browser feature | | Data quality | Domain-aware, contextual | Random but typed | Your personal data | | Form detection | Reads labels, selects, custom dropdowns | N/A — you call it in code | Standard inputs only | | Domain presets | D365, Healthcare, E-commerce, etc. | Generic schemas | N/A | | Setup | One import, zero config | Write generation scripts | Already there |
Install
npm install ghostfill -DUsage
Vanilla JS
import { init } from "ghostfill";
init();React / Next.js
// app.tsx or layout.tsx
import { useEffect } from "react";
function GhostFill() {
useEffect(() => {
import("ghostfill").then((m) =>
m.init({
ai: {
endpoint: "/api/ghostfill",
provider: "openai",
},
})
);
}, []);
return null;
}
// In your app:
{process.env.NODE_ENV === "development" && <GhostFill />}Programmatic
import { fill } from "ghostfill";
await fill({ container: document.querySelector("form") });How it works
- A ghost icon appears on the page (starts minimized)
- Click it to enter selection mode — hover and click a form area
- Click the sparkles button to fill all detected fields
- By default, generates random sample data locally (no API needed)
- Optionally enable AI mode in settings for context-aware data generation through your backend
Presets
Save prompt templates for domain-specific data:
- D365 CE — Accounts, Contacts, Opportunities with CRM-realistic values
- Healthcare — Patient intake, insurance, clinical forms
- E-commerce — Products, orders, customer profiles
- Automotive — Vehicle specs, service records, dealer info
- Custom — Write your own prompt template for any domain
Presets are stored locally in the browser and sent as context to the AI provider.
Secure AI Setup
GhostFill no longer accepts provider API keys in the browser. To use OpenAI, xAI, or Moonshot safely, expose a backend route and keep provider credentials server-side.
Install a server-side SDK in your app:
npm install openaiExample Next.js route:
// app/api/ghostfill/route.ts
import OpenAI from "openai";
import {
buildFillMessages,
parseFillDataPayload,
type GhostFillAIRequest,
type Provider,
} from "ghostfill/server";
const clients: Record<Provider, OpenAI> = {
openai: new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
}),
xai: new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: "https://api.x.ai/v1",
}),
moonshot: new OpenAI({
apiKey: process.env.MOONSHOT_API_KEY,
baseURL: "https://api.moonshot.ai/v1",
}),
};
const models: Record<Provider, string> = {
openai: process.env.GHOSTFILL_OPENAI_MODEL || "gpt-4o-mini",
xai: process.env.GHOSTFILL_XAI_MODEL || "grok-4-fast",
moonshot: process.env.GHOSTFILL_MOONSHOT_MODEL || "kimi-k2",
};
function isProvider(value: unknown): value is Provider {
return value === "openai" || value === "xai" || value === "moonshot";
}
export async function POST(req: Request) {
const body = (await req.json()) as Partial<GhostFillAIRequest>;
if (!isProvider(body.provider) || !Array.isArray(body.fields)) {
return Response.json({ error: "Invalid GhostFill request" }, { status: 400 });
}
const completion = await clients[body.provider].chat.completions.create({
model: models[body.provider],
messages: buildFillMessages({
provider: body.provider,
prompt: typeof body.prompt === "string" ? body.prompt : "",
systemPrompt:
typeof body.systemPrompt === "string" ? body.systemPrompt : undefined,
fields: body.fields,
}),
});
const content = completion.choices[0]?.message?.content || "";
return Response.json(parseFillDataPayload(content));
}This route supports all three providers, keeps secrets server-side, and only sends non-secret field metadata to the model. If you expose it outside local development, add your app's auth, rate limits, and request-size validation.
Settings
Click the gear icon to configure:
- Highlight Colour — pick the selection overlay color
- Use AI — toggle AI-powered fills when a secure backend route is configured
- Provider — cycle between OpenAI, xAI, and Moonshot
- Backend — shows the secure route or handler being used for AI fills
- Presets — save prompt templates for domain-specific data (e.g. D365, healthcare); keep them non-secret because they are stored locally
- Dark/Light theme — toggle with the sun/moon icon
Features
- Zero config — works out of the box with random sample data
- Secure by default — AI mode uses a backend route instead of browser-held provider keys
- Shadow DOM — styles don't leak into your app
- Framework-aware — uses native value setters so React/Vue/Angular pick up changes
- Smart detection — labels from
<label>,aria-label, placeholder, preceding siblings - Custom dropdowns — handles Headless UI Listbox, Radix Select, and other
role="listbox"components - Draggable — drag the toolbar or minimized icon anywhere
- Presets — save and reuse prompt templates
- Dark/Light mode — matches your preference
- Keyboard shortcut —
Alt+Gto toggle
API
init(options?)
Initialize GhostFill and add the UI to the page.
init({
ai?: {
endpoint?: string, // Same-origin backend route (default: "/api/ghostfill")
requestFillData?: (request: GhostFillAIRequest) => Promise<FieldFillData[]>,
provider?: "openai" | "xai" | "moonshot"
},
shortcut?: string, // Keyboard shortcut (default: "Alt+G")
systemPrompt?: string // Custom system prompt to prepend
})Returns { destroy: () => void } to remove the UI.
fill(params)
Programmatic fill without the UI.
await fill({
container: HTMLElement, // The element containing form fields
prompt?: string, // Optional prompt for AI mode
ai?: {
endpoint?: string,
requestFillData?: (request: GhostFillAIRequest) => Promise<FieldFillData[]>,
provider?: "openai" | "xai" | "moonshot"
},
provider?: "openai" | "xai" | "moonshot",
systemPrompt?: string
})Returns { filled: number, errors: string[] }.
MCP Server
GhostFill includes an MCP (Model Context Protocol) server so AI agents like Claude Code and Cursor can generate form data directly.
{
"mcpServers": {
"ghostfill": {
"command": "npx",
"args": ["ghostfill-mcp"]
}
}
}Tools:
ghostfill_generate— Generate realistic fake data locally. No API key required.ghostfill_generate_ai— Generate context-aware data via OpenAI, xAI, or Moonshot. Requires an API key.
Requires @modelcontextprotocol/sdk and zod as peer dependencies:
npm install @modelcontextprotocol/sdk zodLicense
PolyForm Shield 1.0.0 — free to use, but you can't use it to build a competing product.
