import assert from "node:assert/strict"; import test from "node:test"; import { TaskState } from "../src/state.ts"; import { registerTaskTools } from "../src/tools.ts"; function registeredTools(state = new TaskState()) { const tools = new Map(); let changes = 0; const pi = { registerTool(tool: any) { tools.set(tool.name, tool); }, }; registerTaskTools(pi as never, state, () => { changes += 1; }); return { tools, state, changes: () => changes }; } async function execute(tool: any, params: unknown) { return tool.execute("call", params, undefined, undefined, {}); } test("registers the five requested tools with minimal top-level parameters", () => { const { tools } = registeredTools(); assert.deepEqual([...tools.keys()], ["add_task", "update_task", "rm_task", "list_task", "get_task"]); assert.deepEqual(Object.keys(tools.get("add_task").parameters.properties), ["tasks"]); assert.deepEqual(Object.keys(tools.get("update_task").parameters.properties), ["tasks"]); assert.deepEqual(Object.keys(tools.get("rm_task").parameters.properties), ["ids"]); assert.deepEqual(Object.keys(tools.get("list_task").parameters.properties), []); assert.deepEqual(Object.keys(tools.get("get_task").parameters.properties), ["ids"]); }); test("mutations return compact content and complete persistence snapshots", async () => { const harness = registeredTools(); const result = await execute(harness.tools.get("add_task"), { tasks: [{ name: "Parent", description: "Parent", children: [{ name: "Child", description: "Child" }] }], }); assert.equal(result.content[0].text, "Added 2 tasks: 1, 2. 0/2 done."); assert.equal(result.details.snapshot.tasks.length, 2); assert.equal(result.details.snapshot.nextId, 3); assert.equal(harness.changes(), 1); }); test("tool validation errors are summarized while preserving atomic state", async () => { const harness = registeredTools(); await execute(harness.tools.get("add_task"), { tasks: [{ name: "Parent", description: "Parent", children: [{ name: "Child", description: "Child" }] }], }); const before = harness.state.snapshot(); await assert.rejects( execute(harness.tools.get("update_task"), { tasks: [{ id: 1, parentId: 2 }] }), /Update failed · 1 validation error\n- task [12]: parentId creates a cycle/, ); assert.deepEqual(harness.state.snapshot(), before); assert.equal(harness.changes(), 1); }); test("list is compact and get returns full JSON for multiple IDs", async () => { const harness = registeredTools(); await execute(harness.tools.get("add_task"), { tasks: [{ name: "Parent", description: "Detailed", children: [{ name: "Child", description: "Child detail" }] }], }); const list = await execute(harness.tools.get("list_task"), {}); assert.equal(list.content[0].text, "1 [pending] Parent\n└─ 2 [pending] Child"); const get = await execute(harness.tools.get("get_task"), { ids: [1, 2] }); const parsed = JSON.parse(get.content[0].text); assert.equal(parsed[0].description, "Detailed"); assert.deepEqual(parsed[0].childrenIds, [2]); assert.equal(parsed[1].parentId, 1); });