Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170import test from "node:test";import assert from "node:assert/strict";import { spawn } from "node:child_process";import path from "node:path";import { fileURLToPath } from "node:url";import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from "node:fs";import { tmpdir } from "node:os";import { apiLookup, loadMap, outline, symbolText, outlineText, referencesText, examples, handle, mcpConfig, TOOLS } from "../src/tools.mjs";
const here = path.dirname(fileURLToPath(import.meta.url));const repo = path.resolve(here, "..", "..");const server = path.join(here, "..", "src", "tools.mjs");
const PIECE = `// smiley, 2026import { thing } from "./lib/thing.mjs";
const RADIUS = 20;
// Draw the face.function paint({ wipe, ink, circle, screen }) { wipe("blue"); ink("yellow").circle(screen.width / 2, screen.height / 2, RADIUS, true);}
function act({ event: e }) { if (e.is("touch")) grow();}
const grow = () => { // nothing yet};
export { paint, act };`;
function workspace() { const root = mkdtempSync(path.join(tmpdir(), "easel-tools-")); mkdirSync(path.join(root, "disks")); writeFileSync(path.join(root, "smiley.mjs"), PIECE); writeFileSync(path.join(root, "other.mjs"), `function paint({ circle }) { circle(1, 2, 3); }\nexport { paint };\n`); return { root, cleanup: () => rmSync(root, { recursive: true, force: true }) };}
test("the API map is built and answers the questions sessions actually asked", () => { const map = loadMap(); assert.ok(map.entries.length > 60, `map has ${map.entries.length} entries`); const circle = apiLookup(map, "circle"); assert.match(circle, /^circle\n\s+circle\(x0, y0, radius, filled/m); assert.match(circle, /e\.g\. disks\//); const synth = apiLookup(map, "synth"); assert.match(synth, /sound\.synth\n\s+sound\.synth\(/); assert.match(synth, /tone.*Hz|tone = 440/); const button = apiLookup(map, "button multitouch"); assert.match(button, /ui\.Button/); assert.match(apiLookup(map, "zzzznotathing"), /Nothing in the API map/); // No query lists everything, one per line. const index = apiLookup(map, "").split("\n"); assert.ok(index.length >= map.entries.length); assert.equal(new Set(index.map(line => line.split(" — ")[0])).size, index.length); assert.ok(index.some(line => line.startsWith("typeface — ")));});
test("outline reads a flat piece as symbols with spans", () => { const { items, lines } = outline(PIECE); assert.equal(lines, PIECE.split("\n").length); const names = items.map((item) => `${item.kind}:${item.name}`); assert.deepEqual(names, [ "import:./lib/thing.mjs", "value:RADIUS", "function:paint", "function:act", "function:grow", "exports:paint, act", ]); const paint = items.find((item) => item.name === "paint"); assert.equal(paint.line, 7); // The span ends at paint's closing brace, not at act's opening line. assert.equal(paint.end, 10);});
test("symbol and outline resolve a piece by bare name inside the workspace", () => { const { root, cleanup } = workspace(); try { const text = symbolText(root, "smiley", "paint"); assert.match(text, /^smiley\.mjs:7-10 {2}function paint/); assert.match(text, /circle\(screen\.width/); assert.match(symbolText(root, "smiley.mjs", "nope"), /No top-level symbol "nope"/); assert.match(outlineText(root, "smiley"), /5 top-level symbols/); assert.throws(() => outlineText(root, "../../etc/passwd"), /no such piece/); const hits = examples(root, "circle"); assert.match(hits, /smiley\.mjs:9/); assert.match(hits, /other\.mjs:1/); } finally { cleanup(); }});
test("outline of a real large piece is a page, not a file", () => { const text = outlineText(repo, "notepat"); const rows = text.split("\n"); assert.match(rows[0], /notepat\.mjs — \d+ lines, \d+ top-level symbols/); assert.ok(rows.length > 20 && rows.length < 400, `${rows.length} rows`);});
test("the JSON-RPC surface: initialize, list, call, unknown", () => { const context = { cwd: repo, map: loadMap() }; const init = handle({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-06-18" } }, context); assert.equal(init.result.protocolVersion, "2025-06-18"); assert.deepEqual(init.result.capabilities, { tools: {} }); assert.equal(handle({ jsonrpc: "2.0", method: "notifications/initialized" }, context), null); const list = handle({ jsonrpc: "2.0", id: 2, method: "tools/list" }, context); assert.deepEqual(list.result.tools.map((tool) => tool.name), ["aesel_settings", "ac_references", "ac_preview", "ac_frame", "ac_api", "ac_examples", "ac_outline", "ac_symbol"]); assert.equal(list.result.tools, TOOLS); const call = handle({ jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "ac_api", arguments: { query: "wipe" } } }, context); assert.match(call.result.content[0].text, /^wipe\n/); const bad = handle({ jsonrpc: "2.0", id: 4, method: "tools/call", params: { name: "ac_symbol", arguments: { file: "missing", name: "x" } } }, context); assert.equal(bad.result.isError, true); const unknown = handle({ jsonrpc: "2.0", id: 5, method: "resources/list" }, context); assert.equal(unknown.error.code, -32601);});
test("the server runs on stdio and the config points the CLI at it", async () => { const config = mcpConfig(repo); assert.equal(config.mcpServers.ac.command, process.execPath); assert.deepEqual(config.mcpServers.ac.args.slice(1), ["--cwd", repo]); const child = spawn(config.mcpServers.ac.command, config.mcpServers.ac.args, { stdio: ["pipe", "pipe", "inherit"] }); const out = []; child.stdout.on("data", (chunk) => out.push(chunk)); child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} })}\n`); child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "ac_outline", arguments: { file: "notepat" } } })}\n`); child.stdin.end(); await new Promise((done) => child.on("close", done)); const replies = Buffer.concat(out).toString().trim().split("\n").map((line) => JSON.parse(line)); assert.equal(replies.length, 2); assert.equal(replies[0].result.serverInfo.name, "easel-ac"); assert.match(replies[1].result.content[0].text, /notepat\.mjs — \d+ lines/);});
test("AST spans handle indentation, multiline declarations and method bodies",()=>{ const result=outline(` export function paint( {ink} ) { const literal="function fake() {}"; } class Game { step() { return 1; } }`); assert.deepEqual(result.items.map(x=>x.name),['paint','Game','Game.step']); assert.equal(result.items[0].end,3); assert.equal(result.items[2].end,5); }); test("installed workspaces can navigate bundled runtime references",()=>{ const {root,cleanup}=workspace();try{ assert.match(outlineText(root,'lib/cam-doll.mjs'),/CamDoll/); assert.match(examples(root,'synth'),/synth/); assert.throws(()=>outlineText(root,'../package.json'),/no such piece/); }finally{cleanup();} });
test("identifier lookup excludes comment and string lookalikes",()=>{ const {root,cleanup}=workspace();try{ writeFileSync(path.join(root,'refs.mjs'),'// target\nconst text="target"; const target=1; console.log(target);'); const result=referencesText(root,'refs.mjs','target'); assert.match(result,/2 syntactic occurrences/); }finally{cleanup();}});
test("exact API lookups return only the requested symbol",()=>{ const result=apiLookup(loadMap(),'circle'); assert.match(result,/^circle\n/);assert.doesNotMatch(result,/\n\ngeo\.Circle/);});