mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-12 04:29:51 +08:00
* feat(mcp-server): add source-reading and clear-canvas hints to the drawing guide Carry the missing web-prompt sentences into the MCP drawing guide: draw from a document, image or web page by reading it yourself first, compare image replications with screenshot_diagram, keep replies short after a successful draw, compose artistic requests from standard shapes, and clear the canvas by sending only the two root cells. Replace the "If the diagram is large" bullet with guidance on choosing create_new_diagram vs edit_diagram. The server INSTRUCTIONS mention reading files yourself and opening .drawio files with load_diagram; the create_new_diagram description documents the clear-canvas call, and its handler reports "Canvas cleared" when the prepared XML holds only the root cells. Add tests for both and the matching README lines in the mcp-server and claude-plugin packages. * feat(mcp-server): append the user's instructions.md to the drawing guide Read <DRAWIO_DATA_DIR>/instructions.md (default ~/.next-ai-drawio) on every call and append its first 5000 characters to the drawing guide under a "## Custom Instructions" heading, for start_session, get_drawing_guide and the diagram-workflow prompt. start_session now tells the model where the file lives. Document the feature in both READMEs and cover it with unit and server-wiring tests. * fix(mcp-server): review fixes for drawing guide text and custom instructions file * fix(mcp-server): Codex review fixes for the guide and custom instructions - Report "Canvas cleared" only for a one-page document; several empty pages get the page summary - Build the instructions.md path with path.join (Windows separators) - Keep the 15000-character guide budget in the wiring test - Plugin README: DRAWIO_DATA_DIR also holds instructions.md
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
/**
|
|
* Tests for the user's instructions.md appended to the drawing guide
|
|
* (src/custom-instructions.ts).
|
|
*/
|
|
|
|
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"
|
|
import { homedir, tmpdir } from "node:os"
|
|
import { join } from "node:path"
|
|
import { afterEach, describe, expect, it } from "vitest"
|
|
import {
|
|
CUSTOM_INSTRUCTIONS_FILE,
|
|
customInstructionsDir,
|
|
guideWithCustomInstructions,
|
|
readCustomInstructions,
|
|
} from "../src/custom-instructions.ts"
|
|
import { DRAWING_GUIDE } from "../src/drawing-guide.ts"
|
|
|
|
const GUIDE = "# Drawing guide\n\nKeep edges orthogonal."
|
|
const tempDir = () => mkdtempSync(join(tmpdir(), "mcp-instructions-"))
|
|
const writeRules = (dir: string, text: string) =>
|
|
writeFileSync(join(dir, CUSTOM_INSTRUCTIONS_FILE), text)
|
|
|
|
describe("readCustomInstructions", () => {
|
|
it("gives an empty string when there is no file", () => {
|
|
const dir = tempDir()
|
|
expect(readCustomInstructions(dir)).toBe("")
|
|
expect(guideWithCustomInstructions(GUIDE, dir)).toBe(GUIDE)
|
|
})
|
|
|
|
it("appends the file's content under its own heading", () => {
|
|
const dir = tempDir()
|
|
writeRules(dir, "\nAlways draw in minimal style.\n\n")
|
|
expect(readCustomInstructions(dir)).toBe(
|
|
"Always draw in minimal style.",
|
|
)
|
|
expect(guideWithCustomInstructions(GUIDE, dir)).toBe(
|
|
`${GUIDE}\n\n## Custom Instructions\nAlways draw in minimal style.`,
|
|
)
|
|
})
|
|
|
|
it("cuts the content at 5000 characters", () => {
|
|
const dir = tempDir()
|
|
writeRules(dir, "x".repeat(6000))
|
|
expect(readCustomInstructions(dir)).toBe("x".repeat(5000))
|
|
})
|
|
|
|
it("gives an empty string when the file cannot be read", () => {
|
|
const dir = tempDir()
|
|
// A folder in place of the file: readFileSync fails with EISDIR
|
|
mkdirSync(join(dir, CUSTOM_INSTRUCTIONS_FILE))
|
|
expect(readCustomInstructions(dir)).toBe("")
|
|
expect(guideWithCustomInstructions(GUIDE, dir)).toBe(GUIDE)
|
|
})
|
|
|
|
it("reads the file again on every call", () => {
|
|
const dir = tempDir()
|
|
writeRules(dir, "First rule.")
|
|
expect(readCustomInstructions(dir)).toBe("First rule.")
|
|
writeRules(dir, "Second rule.")
|
|
expect(readCustomInstructions(dir)).toBe("Second rule.")
|
|
})
|
|
})
|
|
|
|
describe("customInstructionsDir", () => {
|
|
const original = process.env.DRAWIO_DATA_DIR
|
|
afterEach(() => {
|
|
if (original === undefined) delete process.env.DRAWIO_DATA_DIR
|
|
else process.env.DRAWIO_DATA_DIR = original
|
|
})
|
|
|
|
it("honours DRAWIO_DATA_DIR", () => {
|
|
const dir = tempDir()
|
|
writeRules(dir, "Use blue fill #dae8fc.")
|
|
process.env.DRAWIO_DATA_DIR = dir
|
|
expect(customInstructionsDir()).toBe(dir)
|
|
expect(guideWithCustomInstructions(GUIDE)).toBe(
|
|
`${GUIDE}\n\n## Custom Instructions\nUse blue fill #dae8fc.`,
|
|
)
|
|
})
|
|
|
|
it("expands ~, which JSON configs pass on as it is", () => {
|
|
process.env.DRAWIO_DATA_DIR = "~/drawio-saves"
|
|
expect(customInstructionsDir()).toBe(join(homedir(), "drawio-saves"))
|
|
})
|
|
|
|
it("falls back to the home folder when DRAWIO_DATA_DIR is off or unset", () => {
|
|
const home = join(homedir(), ".next-ai-drawio")
|
|
process.env.DRAWIO_DATA_DIR = "off"
|
|
expect(customInstructionsDir()).toBe(home)
|
|
delete process.env.DRAWIO_DATA_DIR
|
|
expect(customInstructionsDir()).toBe(home)
|
|
})
|
|
})
|
|
|
|
describe("DRAWING_GUIDE", () => {
|
|
it("stays within its own budget; instructions.md adds up to 5000 more", () => {
|
|
expect(DRAWING_GUIDE.length).toBeLessThanOrEqual(15000)
|
|
})
|
|
})
|