| import { tool } from "ai"; |
| import { z } from "zod"; |
| import { buildTiptapTools } from "./tiptap-catalog.js"; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const customEditorTools = { |
| replaceSelection: tool({ |
| description: |
| "Replace the currently selected text in the editor with new content. " + |
| "Use ONLY when text is selected and the user asks to rewrite, fix, or rephrase it.", |
| inputSchema: z.object({ |
| newText: z |
| .string() |
| .describe("The replacement text (plain text or markdown)"), |
| }), |
| }), |
|
|
| insertAtCursor: tool({ |
| description: |
| "Insert text at the current cursor position (does not replace anything). " + |
| "Use when the user asks to add or generate new content.", |
| inputSchema: z.object({ |
| text: z.string().describe("The text to insert (plain text or markdown)"), |
| }), |
| }), |
|
|
| applyDiff: tool({ |
| description: |
| "The PRIMARY tool for editing prose. Locates a piece of text in the document using " + |
| "context and replaces it. Provide surrounding context to disambiguate when the " + |
| "same text appears multiple times. For multiple edits, call this tool multiple " + |
| "times in a single response. Each call should be a minimal, surgical edit.", |
| inputSchema: z.object({ |
| contextBefore: z |
| .string() |
| .describe( |
| "A few words of text that appear immediately BEFORE the content to delete. " + |
| "Used to locate the correct position. Can be empty if at the start of the document.", |
| ), |
| contentToDelete: z |
| .string() |
| .describe( |
| "The exact verbatim text to remove from the document. Must match perfectly.", |
| ), |
| contentToInsert: z |
| .string() |
| .describe( |
| "The new text to insert in place of the deleted text. " + |
| "Can be empty to just delete without replacing.", |
| ), |
| contextAfter: z |
| .string() |
| .describe( |
| "A few words of text that appear immediately AFTER the content to delete. " + |
| "Used to confirm the correct position. Can be empty if at the end.", |
| ), |
| }), |
| }), |
| }; |
|
|
| const frontmatterTools = { |
| updateFrontmatter: tool({ |
| description: |
| "Update article metadata (frontmatter). Use when the user asks to change the title, " + |
| "subtitle, authors, affiliations, publication date, DOI, template, or any article setting. " + |
| "Only include the fields you want to change - omitted fields are left unchanged.", |
| inputSchema: z.object({ |
| title: z.string().optional().describe("Article title"), |
| subtitle: z.string().optional().describe("Subtitle"), |
| description: z.string().optional().describe("Short SEO description"), |
| published: z.string().optional().describe('Publication date, e.g. "Apr. 04, 2026"'), |
| template: z.enum(["article", "paper"]).optional().describe("Layout template"), |
| doi: z.string().optional().describe("DOI identifier"), |
| banner: z.string().optional().describe("Banner embed filename"), |
| showPdf: z.boolean().optional().describe("Show PDF download button"), |
| tableOfContentsAutoCollapse: z.boolean().optional().describe("Auto-collapse TOC"), |
| licence: z.string().optional().describe("Licence text (HTML allowed)"), |
| pdfProOnly: z.boolean().optional().describe("Gate PDF behind HF Pro"), |
| seoThumbImage: z.string().optional().describe("Custom OG image URL"), |
| }), |
| }), |
|
|
| addAuthor: tool({ |
| description: |
| "Add an author to the article. Provide the author name and optionally a URL " + |
| "and affiliation indices (1-based). If the affiliation doesn't exist yet, " + |
| "provide newAffiliationName to create it.", |
| inputSchema: z.object({ |
| name: z.string().describe("Author name"), |
| url: z.string().optional().describe("Author URL/homepage"), |
| affiliations: z |
| .array(z.number()) |
| .optional() |
| .describe("1-based affiliation indices"), |
| newAffiliationName: z |
| .string() |
| .optional() |
| .describe("Name of a new affiliation to create (if needed)"), |
| newAffiliationUrl: z |
| .string() |
| .optional() |
| .describe("URL of the new affiliation"), |
| }), |
| }), |
|
|
| removeAuthor: tool({ |
| description: "Remove an author by index (0-based).", |
| inputSchema: z.object({ |
| index: z.number().describe("0-based index of the author to remove"), |
| }), |
| }), |
| }; |
|
|
| export const editorTools = { |
| ...buildTiptapTools(), |
| ...customEditorTools, |
| ...frontmatterTools, |
| }; |
|
|