Project Files
src / tools / rewrite.ts
/**
* rewrite — apply a surgical text replacement to an existing playbook document.
* Mirrors the replace_string_in_file mechanic: oldText must match exactly once.
*/
import { tool, type Tool, type ToolsProviderController } from "@lmstudio/sdk";
// @ts-ignore — zod/lib re-export chain breaks with NodeNext; runtime is fine
import { z } from "zod";
import path from "node:path";
import fs from "node:fs";
import type { IndexManager } from "../indexManager.js";
import { formatToolMetaBlock } from "../helpers/pluginMeta.js";
export function createRewriteTool(
_ctl: ToolsProviderController,
index: IndexManager
): Tool {
return tool({
name: "rewrite",
description: `Update part of an existing playbook document with a surgical text replacement.
oldText must appear exactly once in the file.
Always call read first to see the current content.
Use this tool when:
- Correcting or extending a specific section of a note
- Updating a value without replacing the whole document
- Applying a targeted change where context around it must be preserved
Returns:
- Confirmation on success
- An error if oldText is not found or matches more than once (make it more specific)
Examples:
- oldText: "Use OAuth1", newText: "Use OAuth2" — updates just that line
- oldText: "## Status\n\nIn progress", newText: "## Status\n\nCompleted" — uses surrounding context for uniqueness
${formatToolMetaBlock()}`,
parameters: {
filename: z
.string()
.describe("Filename of the document to edit (e.g. 'my-note.md')."),
oldText: z
.string()
.describe("The exact text to replace. Must appear exactly once in the file."),
newText: z
.string()
.describe("The text to substitute in place of oldText."),
},
implementation: async (args) => {
const filePath = path.join(index.getPlaybookDirectory(), args.filename);
if (!fs.existsSync(filePath)) {
return `Error: File not found — "${args.filename}". Use recall to find available documents.`;
}
try {
await index.editFile(filePath, args.oldText, args.newText);
return `Edited "${args.filename}" successfully.`;
} catch (err) {
return `Error: ${err instanceof Error ? err.message : String(err)}`;
}
},
});
}