Skip to content

About

Reusable TypeScript library for proposing polished English versions of user prompts used with AI assistants

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

English Prompt Polisher

Reusable TypeScript library for proposing polished English versions of user prompts used with AI assistants.

The package does not include an adapter for any concrete AI agent. Integrations provide their own hook, model call, dedupe, scheduling, and logging through dependency injection.

Features

  • Proposes English versions for non-English prompts.
  • Polishes grammatically incorrect or unclear English prompts.
  • Keeps already clear English prompts unchanged.
  • Skips internal checker prompts to avoid recursion.
  • Writes proposals to ~/english-prompt-polisher.md by default.
  • Optionally creates one Notion database entry per proposal.
  • Can call an adapter-provided rewrite callback while still writing proposal outputs.

Install

npm install english-prompt-polisher

Integration Example

Use handlePromptProposal inside your AI-agent plugin or message hook. The integration is responsible for adapting the concrete agent's message shape to ChatMessageInput and ChatMessageOutput, then providing a checker implementation.

import {
	buildCheckerPrompt,
	handlePromptProposal,
	parseCheckerResult,
	type ChatMessageInput,
	type ChatMessageOutput,
	type Options,
	type PolisherResult,
} from 'english-prompt-polisher';

const options: Options = {
	outputFile: '~/english-prompt-polisher.md',
	checker: { model: 'provider/model' },
	notion: {
		enabled: true,
		token: process.env.ENGLISH_PROMPT_POLISHER_NOTION_TOKEN,
		databaseId: process.env.ENGLISH_PROMPT_POLISHER_NOTION_DATABASE_ID,
	},
};

async function onUserPrompt(event: AgentMessageEvent): Promise<void> {
	const input: ChatMessageInput = {
		conversationId: event.conversationId,
		messageId: event.messageId,
		model: event.model,
	};

	const output: ChatMessageOutput = {
		parts: event.parts,
	};

	await handlePromptProposal(input, output, options, {
		checkPrompt: async (_input, checkerOptions, prompt): Promise<PolisherResult> => {
			const text: string = await event.ai.generateText({
				model: checkerOptions.checker?.model,
				prompt: buildCheckerPrompt(prompt),
			});

			return parseCheckerResult([{ type: 'text', text }]);
		},
		log: async (level, message, extra): Promise<void> => {
			event.logger[level](message, extra);
		},
	});
}

AgentMessageEvent is intentionally not provided by this package. It represents your integration's concrete message event type.

Create a Plugin for Your AI Agent

Use this package as the prompt-polishing engine for your own AI-agent plugin. Wire it into your agent's message hook, pass the concrete message data into handlePromptProposal, and keep the agent-specific code focused on transport, dedupe, scheduling, model calls, and logging.

If you maintain an AI assistant, coding agent, chat client, or automation tool, create a small adapter around this library instead of rebuilding prompt detection, proposal formatting, file output, and Notion output from scratch.

Start with the integration example above, replace AgentMessageEvent with your agent's real event type, and publish the adapter as a plugin for that concrete agent.

Rewrite Mode

With handlePromptProposal, use mode: 'rewrite' to invoke your adapter's rewritePrompt callback. This flow still writes proposals to the configured Markdown file and, when enabled, Notion.

const options: Options = {
	mode: 'rewrite',
	outputFile: '~/english-prompt-polisher.md',
	notion: {
		enabled: true,
		token: process.env.ENGLISH_PROMPT_POLISHER_NOTION_TOKEN,
		databaseId: process.env.ENGLISH_PROMPT_POLISHER_NOTION_DATABASE_ID,
	},
};

await handlePromptProposal(input, output, options, {
	checkPrompt,
	rewritePrompt: async (record): Promise<void> => {
		await event.rewritePrompt(record.proposedPrompt);
	},
});

The library decides when a rewrite is needed and passes the full proposal record to rewritePrompt. The concrete adapter still owns the agent-specific mutation because each agent exposes different hook and message APIs. Notion is optional and uses the same output flow as file proposals.

Rewrite Without Proposal Outputs

Use handlePromptRewrite to check a prompt and return a rewrite decision without modifying messages or writing Markdown or Notion outputs. It does not require mode: 'rewrite'; your adapter applies the returned prompt.

import { handlePromptRewrite } from 'english-prompt-polisher';

const result = await handlePromptRewrite(input, prompt, options, {
	checkPrompt,
});

if (result.status === 'rewritten') {
	await event.rewritePrompt(result.proposedPrompt);
}

Returns { status: 'skipped' } when the prompt is ineligible or no usable change is proposed.

Options

Option Default Description
mode proposal With handlePromptProposal, use rewrite to call the adapter rewrite callback.
outputFile ~/english-prompt-polisher.md Markdown file that receives proposals.
maxChars unset Skip prompts exceeding this many Unicode code points.
includeOriginal true Include the original prompt in the Markdown output.
checker.model unset Optional model hint for the injected checker.
notion.enabled false Create Notion database entries.
notion.token unset Direct Notion integration token.
notion.databaseId unset Direct Notion database ID.
notion.titleProperty auto-detect Notion title property name.

maxChars accepts only positive safe integers. If omitted or invalid, no length limit is applied unless a valid maxChars default is supplied to normalizeOptions.

Public API

  • handlePromptProposal: high-level orchestration for extracting, checking, recording, and writing a proposal.
  • handlePromptRewrite: checks a supplied prompt and returns a rewrite decision without modifying messages or writing proposal outputs.
  • buildCheckerPrompt: builds the checker prompt your integration can send to its model.
  • parseCheckerResult: parses model output into a PolisherResult.
  • formatProposalMarkdown: renders a proposal record as Markdown.
  • writeProposal: writes a proposal to configured sinks.
  • normalizeOptions: validates raw adapter options and normalizes shared option fields.
  • resolveCheckerModel: resolves checker.model against the current input model.
  • formatUnknownError: formats unknown thrown values for adapter logs.

The root export and english-prompt-polisher/core expose the same reusable core API. english-prompt-polisher/notion exposes Notion-specific helpers for integrations that want direct control over Notion writes.

Adapter lifecycle code such as message dedupe, background scheduling, and the concrete in-place prompt mutation belongs in the concrete AI-agent plugin because those behaviors depend on the agent's hook semantics and message model.

Notion Setup

  1. Create a Notion integration and copy its token.
  2. Share the target database with the integration.
  3. Export environment variables before starting your AI-agent integration:
export ENGLISH_PROMPT_POLISHER_NOTION_TOKEN="secret_..."
export ENGLISH_PROMPT_POLISHER_NOTION_DATABASE_ID="..."

The database must have a title property. These optional properties are filled only when they exist with matching Notion types:

  • Detected Language: rich text
  • Conversation ID: rich text
  • Created At: date

The full reason, original prompt, and proposed English prompt are stored in the Notion page body.

Output Example

## 2026-06-13T12:00:00.000Z

- Conversation ID: conversation_123
- Message ID: message_456
- Detected Language: Spanish
- Reason: Prompt is written in Spanish and should be translated to English.

### Original Prompt

```text
Crea una herramienta CLI.
```

### Proposed English Prompt

```text
Create a CLI tool.
```

Notes

  • Prompt mutation is the adapter's responsibility, through the rewritePrompt callback or by applying a handlePromptRewrite result.
  • handlePromptProposal writes supporting proposal outputs; handlePromptRewrite does not.
  • Checker failures are propagated to your integration.
  • Default proposal output sinks do not throw on write failure. They return { status: 'failed' } entries in PromptProposalResult.output and log warnings when a logger is provided.
  • If you inject a custom writeProposal dependency and it rejects, that error is propagated.

Handling Output Failures

const result: PromptProposalResult = await handlePromptProposal(input, output, options, dependencies);

if (result.status === 'created') {
	for (const sink of result.output) {
		if (sink.status === 'failed') {
			// Decide whether your integration should warn, retry, or fail.
		}
	}
}

About

Reusable TypeScript library for proposing polished English versions of user prompts used with AI assistants

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages