test / dev.test.ts
test / dev.test.ts
// Project commands, LM Studio plugin installs and logs, and the smaller helpers for long tasks.
import { test } from "node:test";
import assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
const tmp = (p: string) => fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), p)));
process.env.SIMPLEFILES_DATA = tmp("sf-dev-data-");
process.env.LOCAL_ONLY_DIR = tmp("sf-dev-shared-");
const LMS = tmp("sf-dev-lms-");
process.env.LMSTUDIO_HOME = LMS;
import { pluginLog, summarize } from "../src/dev";
import { trust } from "../src/projects";
import { apply, proposeEdit, proposeInstall, undo } from "../src/edits";
import { findCommand, runCommand, scopeFor, toolsProvider } from "../src/plugin";
import { readFile } from "../src/reading";
import { maybeAutoApply } from "../src/autoapply";
const ESBUILD_MODULES = path.resolve(__dirname, "..", "node_modules");
// A plugin project, and the copy LM Studio has installed of it.
function project(version: string, testScript = 'node -e "process.exit(0)"') {
const root = tmp("sf-dev-proj-");
fs.mkdirSync(path.join(root, "src"));
fs.writeFileSync(path.join(root, "manifest.json"), JSON.stringify({ type: "plugin", runner: "node", owner: "tester", name: "demo" }));
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "demo", version, scripts: { test: testScript } }));
fs.writeFileSync(path.join(root, "src", "index.ts"), `export const VERSION = "${version}";\nexport async function main() { return VERSION; }\n`);
fs.symlinkSync(ESBUILD_MODULES, path.join(root, "node_modules"), "junction");
const installed = path.join(LMS, "extensions", "plugins", "tester", "demo");
fs.mkdirSync(path.join(installed, ".lmstudio"), { recursive: true });
fs.mkdirSync(path.join(installed, "src"), { recursive: true });
fs.writeFileSync(path.join(installed, "src", "index.ts"), 'export const VERSION = "old";\n');
fs.writeFileSync(path.join(installed, ".lmstudio", "production.js"), "// the old build\n");
fs.writeFileSync(
path.join(installed, ".lmstudio", "entry.ts"),
'const id = process.env.LMS_PLUGIN_CLIENT_IDENTIFIER;\nimport("./../src/index.ts").then((m) => m.main());\n'
);
return { root, installed };
}
async function runScriptTool(folders: string) {
return (await tools(folders)).find((x) => x.name === "run_script")!;
}
function tools(folders: string) {
const ctl = { getGlobalPluginConfig: () => ({ get: (k: string) => (k === "folders" ? folders : k === "devTools" ? true : "") }) } as never;
return toolsProvider(ctl) as unknown as Promise<Array<{ name: string; implementation: (a: unknown, c: unknown) => Promise<string> }>>;
}
test("a /files command is found at the start of a message or later in it", () => {
assert.deepEqual(findCommand("/files apply 3"), { command: "/files apply 3", rest: "" });
assert.deepEqual(findCommand("I solved the captcha for you /files apply 23 24 25"), { command: "/files apply 23 24 25", rest: "I solved the captcha for you" });
assert.deepEqual(findCommand("done.\n/files apply all\nthanks"), { command: "/files apply all", rest: "done.\n\nthanks" });
assert.equal(findCommand("the /filesystem is fine"), null);
assert.equal(findCommand("no command here"), null);
});
test("read_file can read just a few lines", () => {
const { root } = project("1.0.0");
const file = path.join(root, "notes.txt");
fs.writeFileSync(file, Array.from({ length: 50 }, (_, i) => `line ${i + 1}`).join("\n"));
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
const out = readFile(scope, file, 21, 3);
assert.match(out, /lines: 21-23 of 50/);
assert.match(out, /21\| line 21/);
assert.doesNotMatch(out, /\| line 24/);
});
test("an edit to text that is only in a waiting proposal says which proposal to apply first", () => {
const { root } = project("1.0.0");
const file = path.join(root, "src", "index.ts");
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
const first = proposeEdit(scope, file, "return VERSION;", "return VERSION + 1;", "a");
const id = Number(/#(\d+)/.exec(first)![1]);
const second = proposeEdit(scope, file, "return VERSION + 1;", "return VERSION + 2;", "b");
assert.match(second, new RegExp(`only in proposal #${id}, which has not been applied yet`));
});
test("LM Studio's own folder is never granted", async () => {
const s = { folders: [], searchRoots: [], excludes: [] };
assert.match(await runCommand(s, `/files grant ${LMS}`), /inside LM Studio's own folder/);
assert.match(await runCommand(s, `/files grant ${path.join(LMS, "extensions")}`), /inside LM Studio's own folder/);
});
test("test output is summarized to the totals and the failures", () => {
const out = ["# Subtest: adds", "ok 1 - adds", "not ok 2 - subtracts", " ---", " error: 'Expected values to be strictly equal'", " expected: 1", " actual: 2", "# tests 2", "# pass 1", "# fail 1"].join("\n");
const s = summarize(out, false);
assert.match(s, /# tests 2/);
assert.match(s, /not ok 2 - subtracts/);
assert.match(s, /expected: 1/);
assert.doesNotMatch(s, /ok 1 - adds/);
});
test("run_script lists a project's commands, asks before the first check, and runs checks once the folder is trusted", async () => {
const good = project("1.0.0");
const bad = project("1.0.0", 'node -e "console.log(\'not ok 1 - breaks\'); console.log(\' error: boom\'); process.exit(1)"');
const run = await runScriptTool(`${good.root}\n${bad.root}`);
const list = await run.implementation({ folder: good.root }, {});
assert.match(list, /test \(package\.json, asks first\): node -e/);
assert.match(list, /lmstudio-install \(LM Studio plugin, needs \/files apply\)/);
assert.match(list, /lmstudio-log \(LM Studio plugin, runs now\)/);
assert.match(await run.implementation({ folder: good.root, script: "test" }, {}), /^status: needs_trust/);
const settings = { folders: [good.root, bad.root], searchRoots: [], excludes: [] };
assert.match(await runCommand(settings, `/files trust ${path.dirname(good.root)}`), /outside the granted folders/);
const { scope } = scopeFor(settings);
assert.match(trust(scope, good.root), /Checks in this project:\n {2}test: node -e/);
trust(scope, bad.root);
assert.match(await run.implementation({ folder: good.root, script: "test" }, {}), /^status: passed/);
const failed = await run.implementation({ folder: bad.root, script: "test" }, {});
assert.match(failed, /^status: failed/);
assert.match(failed, /not ok 1 - breaks/);
assert.match(await run.implementation({ folder: good.root, script: "nope" }, {}), /has no command called nope/);
assert.match(await run.implementation({ folder: path.dirname(good.root) }, {}), /status: error/);
assert.match(await runCommand(settings, `/files untrust ${bad.root}`), /No longer trusted/);
assert.match(await run.implementation({ folder: bad.root, script: "test" }, {}), /^status: needs_trust/);
});
test("a command that changes something is a proposal, run only by /files apply", async () => {
const { root } = project("1.0.0");
const pkgFile = path.join(root, "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
pkg.scripts.build = `node -e "require('fs').writeFileSync('built.txt', 'yes')"`;
fs.writeFileSync(pkgFile, JSON.stringify(pkg));
const run = await runScriptTool(root);
const proposed = await run.implementation({ folder: root, script: "build", reason: "make it" }, {});
assert.match(proposed, /^status: proposed/);
assert.match(proposed, /command: node -e/);
assert.equal(fs.existsSync(path.join(root, "built.txt")), false, "nothing runs before approval");
const id = Number(/#(\d+)/.exec(proposed)![1]);
const { scope } = scopeFor({ folders: [root], searchRoots: [], excludes: [] });
assert.match(await apply(scope, [id]), new RegExp(`#${id} ran "build" in .*: it succeeded`));
assert.equal(fs.readFileSync(path.join(root, "built.txt"), "utf8"), "yes");
// A command changed after it was proposed is refused.
const again = Number(/#(\d+)/.exec(await run.implementation({ folder: root, script: "build" }, {}))![1]);
pkg.scripts.build = 'node -e "process.exit(0)"';
fs.writeFileSync(pkgFile, JSON.stringify(pkg));
assert.match(await apply(scope, [again]), /changed since it was proposed/);
});
test("read_file can read the end of a file and only the lines containing some text", () => {
const { root } = project("1.0.0");
const file = path.join(root, "app.log");
fs.writeFileSync(file, Array.from({ length: 30 }, (_, i) => `${i % 5 === 0 ? "ERROR" : "info"} event ${i + 1}`).join("\n"));
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
assert.match(readFile(scope, file, 1, 3, { fromEnd: true }), /lines: 28-30 of 30/);
const errors = readFile(scope, file, 1, 300, { matching: "error" });
assert.match(errors, /matching lines: 6 of 6 shown/);
assert.match(errors, / 1\| ERROR event 1/);
assert.doesNotMatch(errors, /\| info/);
assert.match(readFile(scope, file, 1, 2, { matching: "error", fromEnd: true }), /2 of 6 shown, the last ones[\s\S]*26\| ERROR event 26/);
});
test("the project tool is only offered when a granted folder holds a project", async () => {
const plain = tmp("sf-dev-plain-");
fs.writeFileSync(path.join(plain, "notes.md"), "# notes\n");
assert.ok(!(await tools(plain)).some((t) => t.name === "run_script"));
const { root } = project("1.0.0");
assert.ok((await tools(root)).some((t) => t.name === "run_script"));
});
test("an install needs a passing test run of the current code, is approved with /files apply, and can be undone", async () => {
const { root, installed } = project("2.0.0");
const settings = { folders: [root], searchRoots: [root], excludes: [] };
const { scope } = scopeFor(settings);
assert.match(proposeInstall(scope, root, "new version"), /has not been tested yet/);
const run = await runScriptTool(root);
trust(scope, root);
assert.match(await run.implementation({ folder: root, script: "test" }, {}), /^status: passed/);
const proposed = await run.implementation({ folder: root, script: "lmstudio-install", reason: "new version" }, {});
assert.match(proposed, /^status: proposed/);
assert.match(proposed, /install tester\/demo into LM Studio/);
const id = Number(/#(\d+)/.exec(proposed)![1]);
assert.equal(fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8"), "// the old build\n", "nothing installed before approval");
// A change after the tests passed blocks the install until the tests run again.
fs.appendFileSync(path.join(root, "src", "index.ts"), "// later\n");
assert.match(await apply(scope, [id]), /not installed: .*changed since its tests last passed/);
await run.implementation({ folder: root, script: "test" }, {});
const again = Number(/#(\d+)/.exec(proposeInstall(scope, root, "new version"))![1]);
const done = await apply(scope, [again]);
assert.match(done, /installed: tester\/demo 2\.0\.0/);
const built = fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8");
assert.match(built, /LMS_PLUGIN_CLIENT_IDENTIFIER/);
assert.match(built, /2\.0\.0/);
assert.equal(fs.existsSync(path.join(installed, "src", "src")), false, "src is replaced, not nested");
assert.match(fs.readFileSync(path.join(installed, "src", "index.ts"), "utf8"), /2\.0\.0/);
assert.match(undo(), /Undid install/);
assert.equal(fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8"), "// the old build\n");
assert.match(fs.readFileSync(path.join(installed, "src", "index.ts"), "utf8"), /old/);
});
test("plugin_log shows only that plugin's entries, compactly", async () => {
const dir = path.join(LMS, "server-logs", "2026-10");
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(
path.join(dir, "2026-10-20.1.log"),
[
"[2026-10-20 01:19:25][DEBUG][LMSAuthenticator][Client=plugin:installed:tester/demo] Client created.",
"[2026-10-20 01:19:26][INFO][Plugin(tester/demo)] stdout: [demo] version 2.0.0",
"[2026-10-20 01:19:26][INFO][Plugin(tester/demo)] stdout: [PromptPreprocessor][Request (/abc)] New preprocess request received.",
"[2026-10-20 01:19:27][INFO][Plugin(other/thing)] stdout: someone else's line",
"[2026-10-20 01:19:28][ERROR][Plugin(tester/demo)] stderr: TypeError: x is not a function",
" at main (production.js:12:3)",
"",
"[2026-10-20 01:19:29][DEBUG] llama slot timing",
"[2026-10-20 01:19:30][DEBUG][LMSAuthenticator][Client=plugin:installed:tester/demo] Client disconnected.",
].join("\n")
);
const out = pluginLog("demo");
assert.match(out, /01:19:25 LM Studio started the plugin/);
assert.match(out, /01:19:26 \[demo\] version 2\.0\.0/);
assert.match(out, /01:19:28 ERROR stderr: TypeError: x is not a function\n {4}at main/);
assert.match(out, /01:19:30 the plugin stopped/);
assert.doesNotMatch(out, /someone else|preprocess request|llama/);
assert.match(pluginLog("nothing-like-this"), /No installed plugin is called/);
});
test("list_proposals shows what is waiting and what was applied", async () => {
const { root } = project("1.0.0");
const t = await tools(root);
const out = await t.find((x) => x.name === "list_proposals")!.implementation({}, {});
assert.match(out, /^status: ok/);
assert.match(out, /waiting for approval:/);
assert.match(out, /applied recently/);
});
test("/files trust takes the folder from the start of the message and ignores words after it", async () => {
const { root } = project("1.0.0");
const settings = { folders: [root], searchRoots: [], excludes: [] };
const out = await runCommand(settings, `/files trust ${root} run it`);
assert.match(out, new RegExp(`^Trusted: ${root.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&")}\n`));
assert.match(out, /"run it", are not part of the command/);
const run = await runScriptTool(root);
assert.match(await run.implementation({ folder: root, script: "test" }, {}), /^status: passed/);
assert.match(await runCommand(settings, `/files trust ${root}-missing`), /is not a folder/);
});
test("write_handoff records judgment, and /files resume reads it back with fresh evidence", async () => {
const { root } = project("1.0.0");
const tls = await tools(root);
const wh = tls.find((t) => t.name === "write_handoff")!;
assert.ok(wh, "write_handoff is offered when a project is present");
assert.match(await wh.implementation({ folder: root, goal: "" }, {}), /needs at least a goal/);
const saved = await wh.implementation(
{ folder: root, goal: "Make the demo build pass.", ruled_out: "Rebuilding dist did not help.", next_step: "Check the esbuild target." },
{}
);
assert.match(saved, /^privacy: local/m);
assert.match(saved, /status: ok/);
assert.match(saved, /Make the demo build pass\./);
const resume = await runCommand({ folders: [root], searchRoots: [], excludes: [] }, `/files resume ${root}`);
assert.match(resume, /# Handoff:/);
assert.match(resume, /## Tried and ruled out\nRebuilding dist did not help\./);
assert.match(resume, /## Next step\nCheck the esbuild target\./);
assert.match(resume, /Verified state \(gathered now/);
assert.match(resume, /Confirm the verified state above with your own tools/);
});
test("/files handoff shows the verified state even with no handoff saved yet", async () => {
const { root } = project("1.0.0");
const out = await runCommand({ folders: [root], searchRoots: [], excludes: [] }, `/files handoff ${root}`);
assert.match(out, /No handoff has been written/);
assert.match(out, /waiting for approval:/);
assert.match(out, /[Cc]all write_handoff/);
});
test("a handoff can sit at a repository root, and handoff and resume work without a folder", async () => {
const repo = tmp("sf-dev-repo-");
fs.mkdirSync(path.join(repo, ".git"));
for (const sub of ["source", "extension"]) {
fs.mkdirSync(path.join(repo, sub, "src"), { recursive: true });
fs.writeFileSync(path.join(repo, sub, "package.json"), JSON.stringify({ name: sub, version: "1.0.0", scripts: { test: "node -e 0" } }));
fs.writeFileSync(path.join(repo, sub, "src", "index.ts"), "export {};\n");
}
const settings = { folders: [repo], searchRoots: [], excludes: [] };
const { scope } = scopeFor(settings);
// The latest change is inside source/src; with no folder named, the handoff goes to the repository root.
proposeEdit(scope, path.join(repo, "source", "src", "index.ts"), "export {};", "export const x = 1;", "demo");
const shown = await runCommand(settings, "/files handoff");
assert.match(shown, new RegExp(`# Handoff: ${repo.replace(/[\\\\^$.*+?()[\\]{}|]/g, "\\\\$&")}\\n`));
assert.match(shown, /last test run \(source\): none recorded/);
assert.match(shown, /last test run \(extension\): none recorded/);
assert.match(shown, /git repository/);
assert.match(shown, /call write_handoff with folder/i);
const wh = (await tools(repo)).find((t) => t.name === "write_handoff")!;
assert.match(await wh.implementation({ goal: "Archive the paused feature.", next_step: "Commit it on a branch." }, {}), /status: ok/);
// A new chat: no folder, or a folder inside the repository, both find the root's handoff.
for (const cmd of ["/files resume", `/files resume ${path.join(repo, "source")}`]) {
const out = await runCommand(settings, cmd);
assert.match(out, /## Goal\nArchive the paused feature\./, cmd);
assert.match(out, /## Next step\nCommit it on a branch\./, cmd);
}
});
test("auto-apply runs a project's build with the edits, but never other commands or installs", async () => {
const { root } = project("1.0.0");
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
pkg.scripts.build = 'node -e "require(\'fs\').writeFileSync(\'built.txt\', \'ok\')"';
pkg.scripts.deploy = 'node -e "process.exit(0)"';
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(pkg));
const settings = { folders: [root], searchRoots: [], excludes: [] };
const { scope } = scopeFor(settings);
await runCommand(settings, `/files autoapply ${root}`);
const run = await runScriptTool(root);
const built = await run.implementation({ folder: root, script: "build", reason: "rebuild" }, {});
assert.match(built, /status: applied\n#\d+ ran "build"/);
assert.equal(fs.readFileSync(path.join(root, "built.txt"), "utf8"), "ok");
assert.match(await run.implementation({ folder: root, script: "deploy", reason: "ship" }, {}), /status: proposed/);
assert.match(await maybeAutoApply(scope, proposeInstall(scope, root, "install")), /^status: (proposed|error)/);
assert.doesNotMatch(await maybeAutoApply(scope, proposeInstall(scope, root, "install")), /^status: applied/);
await runCommand(settings, "/files reject all");
await runCommand(settings, "/files autoapply off");
});
test("copy_files copies exactly, needs apply, never overwrites, and undo removes the copies", async () => {
const { root } = project("1.0.0");
fs.mkdirSync(path.join(root, "src", "nested"), { recursive: true });
fs.writeFileSync(path.join(root, "src", "a.ts"), "export const a = 1;\n");
fs.writeFileSync(path.join(root, "src", "nested", "b.ts"), "export const b = 2;\n");
fs.writeFileSync(path.join(root, "src", "secret.pem"), "KEY\n");
const tls = await tools(root);
const copy = tls.find((t) => t.name === "copy_files")!;
const dest = path.join(root, "archive");
const proposed = await copy.implementation({ sources: [path.join(root, "src")], dest, reason: "archive the source" }, {});
assert.match(proposed, /status: proposed/);
assert.match(proposed, /privacy: local/);
assert.match(proposed, /copy 3 file\(s\)/, "index.ts, a.ts and nested/b.ts copy; the secret .pem is skipped");
assert.equal(fs.existsSync(dest), false, "nothing copied before apply");
const id = Number(/#(\d+) copy/.exec(proposed)![1]);
const { scope } = scopeFor({ folders: [root], searchRoots: [], excludes: [] });
assert.match(await apply(scope, [id]), new RegExp(`#${id} copied: 3 file`));
assert.equal(fs.readFileSync(path.join(dest, "src", "a.ts"), "utf8"), "export const a = 1;\n", "copied byte for byte");
assert.equal(fs.readFileSync(path.join(dest, "src", "nested", "b.ts"), "utf8"), "export const b = 2;\n");
assert.equal(fs.existsSync(path.join(dest, "src", "secret.pem")), false);
// Re-proposing the same copy refuses, since it would overwrite.
const again = await copy.implementation({ sources: [path.join(root, "src")], dest, reason: "again" }, {});
assert.match(again, /already exists/);
assert.match(undo(), /removed 3 copied file/);
assert.equal(fs.existsSync(path.join(dest, "src", "a.ts")), false);
assert.equal(fs.existsSync(path.join(dest, "src")), false, "subfolders the copy created are cleaned up");
});
// Project commands, LM Studio plugin installs and logs, and the smaller helpers for long tasks.
import { test } from "node:test";
import assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
const tmp = (p: string) => fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), p)));
process.env.SIMPLEFILES_DATA = tmp("sf-dev-data-");
process.env.LOCAL_ONLY_DIR = tmp("sf-dev-shared-");
const LMS = tmp("sf-dev-lms-");
process.env.LMSTUDIO_HOME = LMS;
import { pluginLog, summarize } from "../src/dev";
import { trust } from "../src/projects";
import { apply, proposeEdit, proposeInstall, undo } from "../src/edits";
import { findCommand, runCommand, scopeFor, toolsProvider } from "../src/plugin";
import { readFile } from "../src/reading";
import { maybeAutoApply } from "../src/autoapply";
const ESBUILD_MODULES = path.resolve(__dirname, "..", "node_modules");
// A plugin project, and the copy LM Studio has installed of it.
function project(version: string, testScript = 'node -e "process.exit(0)"') {
const root = tmp("sf-dev-proj-");
fs.mkdirSync(path.join(root, "src"));
fs.writeFileSync(path.join(root, "manifest.json"), JSON.stringify({ type: "plugin", runner: "node", owner: "tester", name: "demo" }));
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "demo", version, scripts: { test: testScript } }));
fs.writeFileSync(path.join(root, "src", "index.ts"), `export const VERSION = "${version}";\nexport async function main() { return VERSION; }\n`);
fs.symlinkSync(ESBUILD_MODULES, path.join(root, "node_modules"), "junction");
const installed = path.join(LMS, "extensions", "plugins", "tester", "demo");
fs.mkdirSync(path.join(installed, ".lmstudio"), { recursive: true });
fs.mkdirSync(path.join(installed, "src"), { recursive: true });
fs.writeFileSync(path.join(installed, "src", "index.ts"), 'export const VERSION = "old";\n');
fs.writeFileSync(path.join(installed, ".lmstudio", "production.js"), "// the old build\n");
fs.writeFileSync(
path.join(installed, ".lmstudio", "entry.ts"),
'const id = process.env.LMS_PLUGIN_CLIENT_IDENTIFIER;\nimport("./../src/index.ts").then((m) => m.main());\n'
);
return { root, installed };
}
async function runScriptTool(folders: string) {
return (await tools(folders)).find((x) => x.name === "run_script")!;
}
function tools(folders: string) {
const ctl = { getGlobalPluginConfig: () => ({ get: (k: string) => (k === "folders" ? folders : k === "devTools" ? true : "") }) } as never;
return toolsProvider(ctl) as unknown as Promise<Array<{ name: string; implementation: (a: unknown, c: unknown) => Promise<string> }>>;
}
test("a /files command is found at the start of a message or later in it", () => {
assert.deepEqual(findCommand("/files apply 3"), { command: "/files apply 3", rest: "" });
assert.deepEqual(findCommand("I solved the captcha for you /files apply 23 24 25"), { command: "/files apply 23 24 25", rest: "I solved the captcha for you" });
assert.deepEqual(findCommand("done.\n/files apply all\nthanks"), { command: "/files apply all", rest: "done.\n\nthanks" });
assert.equal(findCommand("the /filesystem is fine"), null);
assert.equal(findCommand("no command here"), null);
});
test("read_file can read just a few lines", () => {
const { root } = project("1.0.0");
const file = path.join(root, "notes.txt");
fs.writeFileSync(file, Array.from({ length: 50 }, (_, i) => `line ${i + 1}`).join("\n"));
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
const out = readFile(scope, file, 21, 3);
assert.match(out, /lines: 21-23 of 50/);
assert.match(out, /21\| line 21/);
assert.doesNotMatch(out, /\| line 24/);
});
test("an edit to text that is only in a waiting proposal says which proposal to apply first", () => {
const { root } = project("1.0.0");
const file = path.join(root, "src", "index.ts");
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
const first = proposeEdit(scope, file, "return VERSION;", "return VERSION + 1;", "a");
const id = Number(/#(\d+)/.exec(first)![1]);
const second = proposeEdit(scope, file, "return VERSION + 1;", "return VERSION + 2;", "b");
assert.match(second, new RegExp(`only in proposal #${id}, which has not been applied yet`));
});
test("LM Studio's own folder is never granted", async () => {
const s = { folders: [], searchRoots: [], excludes: [] };
assert.match(await runCommand(s, `/files grant ${LMS}`), /inside LM Studio's own folder/);
assert.match(await runCommand(s, `/files grant ${path.join(LMS, "extensions")}`), /inside LM Studio's own folder/);
});
test("test output is summarized to the totals and the failures", () => {
const out = ["# Subtest: adds", "ok 1 - adds", "not ok 2 - subtracts", " ---", " error: 'Expected values to be strictly equal'", " expected: 1", " actual: 2", "# tests 2", "# pass 1", "# fail 1"].join("\n");
const s = summarize(out, false);
assert.match(s, /# tests 2/);
assert.match(s, /not ok 2 - subtracts/);
assert.match(s, /expected: 1/);
assert.doesNotMatch(s, /ok 1 - adds/);
});
test("run_script lists a project's commands, asks before the first check, and runs checks once the folder is trusted", async () => {
const good = project("1.0.0");
const bad = project("1.0.0", 'node -e "console.log(\'not ok 1 - breaks\'); console.log(\' error: boom\'); process.exit(1)"');
const run = await runScriptTool(`${good.root}\n${bad.root}`);
const list = await run.implementation({ folder: good.root }, {});
assert.match(list, /test \(package\.json, asks first\): node -e/);
assert.match(list, /lmstudio-install \(LM Studio plugin, needs \/files apply\)/);
assert.match(list, /lmstudio-log \(LM Studio plugin, runs now\)/);
assert.match(await run.implementation({ folder: good.root, script: "test" }, {}), /^status: needs_trust/);
const settings = { folders: [good.root, bad.root], searchRoots: [], excludes: [] };
assert.match(await runCommand(settings, `/files trust ${path.dirname(good.root)}`), /outside the granted folders/);
const { scope } = scopeFor(settings);
assert.match(trust(scope, good.root), /Checks in this project:\n {2}test: node -e/);
trust(scope, bad.root);
assert.match(await run.implementation({ folder: good.root, script: "test" }, {}), /^status: passed/);
const failed = await run.implementation({ folder: bad.root, script: "test" }, {});
assert.match(failed, /^status: failed/);
assert.match(failed, /not ok 1 - breaks/);
assert.match(await run.implementation({ folder: good.root, script: "nope" }, {}), /has no command called nope/);
assert.match(await run.implementation({ folder: path.dirname(good.root) }, {}), /status: error/);
assert.match(await runCommand(settings, `/files untrust ${bad.root}`), /No longer trusted/);
assert.match(await run.implementation({ folder: bad.root, script: "test" }, {}), /^status: needs_trust/);
});
test("a command that changes something is a proposal, run only by /files apply", async () => {
const { root } = project("1.0.0");
const pkgFile = path.join(root, "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
pkg.scripts.build = `node -e "require('fs').writeFileSync('built.txt', 'yes')"`;
fs.writeFileSync(pkgFile, JSON.stringify(pkg));
const run = await runScriptTool(root);
const proposed = await run.implementation({ folder: root, script: "build", reason: "make it" }, {});
assert.match(proposed, /^status: proposed/);
assert.match(proposed, /command: node -e/);
assert.equal(fs.existsSync(path.join(root, "built.txt")), false, "nothing runs before approval");
const id = Number(/#(\d+)/.exec(proposed)![1]);
const { scope } = scopeFor({ folders: [root], searchRoots: [], excludes: [] });
assert.match(await apply(scope, [id]), new RegExp(`#${id} ran "build" in .*: it succeeded`));
assert.equal(fs.readFileSync(path.join(root, "built.txt"), "utf8"), "yes");
// A command changed after it was proposed is refused.
const again = Number(/#(\d+)/.exec(await run.implementation({ folder: root, script: "build" }, {}))![1]);
pkg.scripts.build = 'node -e "process.exit(0)"';
fs.writeFileSync(pkgFile, JSON.stringify(pkg));
assert.match(await apply(scope, [again]), /changed since it was proposed/);
});
test("read_file can read the end of a file and only the lines containing some text", () => {
const { root } = project("1.0.0");
const file = path.join(root, "app.log");
fs.writeFileSync(file, Array.from({ length: 30 }, (_, i) => `${i % 5 === 0 ? "ERROR" : "info"} event ${i + 1}`).join("\n"));
const { scope } = scopeFor({ folders: [root], searchRoots: [root], excludes: [] });
assert.match(readFile(scope, file, 1, 3, { fromEnd: true }), /lines: 28-30 of 30/);
const errors = readFile(scope, file, 1, 300, { matching: "error" });
assert.match(errors, /matching lines: 6 of 6 shown/);
assert.match(errors, / 1\| ERROR event 1/);
assert.doesNotMatch(errors, /\| info/);
assert.match(readFile(scope, file, 1, 2, { matching: "error", fromEnd: true }), /2 of 6 shown, the last ones[\s\S]*26\| ERROR event 26/);
});
test("the project tool is only offered when a granted folder holds a project", async () => {
const plain = tmp("sf-dev-plain-");
fs.writeFileSync(path.join(plain, "notes.md"), "# notes\n");
assert.ok(!(await tools(plain)).some((t) => t.name === "run_script"));
const { root } = project("1.0.0");
assert.ok((await tools(root)).some((t) => t.name === "run_script"));
});
test("an install needs a passing test run of the current code, is approved with /files apply, and can be undone", async () => {
const { root, installed } = project("2.0.0");
const settings = { folders: [root], searchRoots: [root], excludes: [] };
const { scope } = scopeFor(settings);
assert.match(proposeInstall(scope, root, "new version"), /has not been tested yet/);
const run = await runScriptTool(root);
trust(scope, root);
assert.match(await run.implementation({ folder: root, script: "test" }, {}), /^status: passed/);
const proposed = await run.implementation({ folder: root, script: "lmstudio-install", reason: "new version" }, {});
assert.match(proposed, /^status: proposed/);
assert.match(proposed, /install tester\/demo into LM Studio/);
const id = Number(/#(\d+)/.exec(proposed)![1]);
assert.equal(fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8"), "// the old build\n", "nothing installed before approval");
// A change after the tests passed blocks the install until the tests run again.
fs.appendFileSync(path.join(root, "src", "index.ts"), "// later\n");
assert.match(await apply(scope, [id]), /not installed: .*changed since its tests last passed/);
await run.implementation({ folder: root, script: "test" }, {});
const again = Number(/#(\d+)/.exec(proposeInstall(scope, root, "new version"))![1]);
const done = await apply(scope, [again]);
assert.match(done, /installed: tester\/demo 2\.0\.0/);
const built = fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8");
assert.match(built, /LMS_PLUGIN_CLIENT_IDENTIFIER/);
assert.match(built, /2\.0\.0/);
assert.equal(fs.existsSync(path.join(installed, "src", "src")), false, "src is replaced, not nested");
assert.match(fs.readFileSync(path.join(installed, "src", "index.ts"), "utf8"), /2\.0\.0/);
assert.match(undo(), /Undid install/);
assert.equal(fs.readFileSync(path.join(installed, ".lmstudio", "production.js"), "utf8"), "// the old build\n");
assert.match(fs.readFileSync(path.join(installed, "src", "index.ts"), "utf8"), /old/);
});
test("plugin_log shows only that plugin's entries, compactly", async () => {
const dir = path.join(LMS, "server-logs", "2026-10");
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(
path.join(dir, "2026-10-20.1.log"),
[
"[2026-10-20 01:19:25][DEBUG][LMSAuthenticator][Client=plugin:installed:tester/demo] Client created.",
"[2026-10-20 01:19:26][INFO][Plugin(tester/demo)] stdout: [demo] version 2.0.0",
"[2026-10-20 01:19:26][INFO][Plugin(tester/demo)] stdout: [PromptPreprocessor][Request (/abc)] New preprocess request received.",
"[2026-10-20 01:19:27][INFO][Plugin(other/thing)] stdout: someone else's line",
"[2026-10-20 01:19:28][ERROR][Plugin(tester/demo)] stderr: TypeError: x is not a function",
" at main (production.js:12:3)",
"",
"[2026-10-20 01:19:29][DEBUG] llama slot timing",
"[2026-10-20 01:19:30][DEBUG][LMSAuthenticator][Client=plugin:installed:tester/demo] Client disconnected.",
].join("\n")
);
const out = pluginLog("demo");
assert.match(out, /01:19:25 LM Studio started the plugin/);
assert.match(out, /01:19:26 \[demo\] version 2\.0\.0/);
assert.match(out, /01:19:28 ERROR stderr: TypeError: x is not a function\n {4}at main/);
assert.match(out, /01:19:30 the plugin stopped/);
assert.doesNotMatch(out, /someone else|preprocess request|llama/);
assert.match(pluginLog("nothing-like-this"), /No installed plugin is called/);
});
test("list_proposals shows what is waiting and what was applied", async () => {
const { root } = project("1.0.0");
const t = await tools(root);
const out = await t.find((x) => x.name === "list_proposals")!.implementation({}, {});
assert.match(out, /^status: ok/);
assert.match(out, /waiting for approval:/);
assert.match(out, /applied recently/);
});
test("/files trust takes the folder from the start of the message and ignores words after it", async () => {
const { root } = project("1.0.0");
const settings = { folders: [root], searchRoots: [], excludes: [] };
const out = await runCommand(settings, `/files trust ${root} run it`);
assert.match(out, new RegExp(`^Trusted: ${root.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&")}\n`));
assert.match(out, /"run it", are not part of the command/);
const run = await runScriptTool(root);
assert.match(await run.implementation({ folder: root, script: "test" }, {}), /^status: passed/);
assert.match(await runCommand(settings, `/files trust ${root}-missing`), /is not a folder/);
});
test("write_handoff records judgment, and /files resume reads it back with fresh evidence", async () => {
const { root } = project("1.0.0");
const tls = await tools(root);
const wh = tls.find((t) => t.name === "write_handoff")!;
assert.ok(wh, "write_handoff is offered when a project is present");
assert.match(await wh.implementation({ folder: root, goal: "" }, {}), /needs at least a goal/);
const saved = await wh.implementation(
{ folder: root, goal: "Make the demo build pass.", ruled_out: "Rebuilding dist did not help.", next_step: "Check the esbuild target." },
{}
);
assert.match(saved, /^privacy: local/m);
assert.match(saved, /status: ok/);
assert.match(saved, /Make the demo build pass\./);
const resume = await runCommand({ folders: [root], searchRoots: [], excludes: [] }, `/files resume ${root}`);
assert.match(resume, /# Handoff:/);
assert.match(resume, /## Tried and ruled out\nRebuilding dist did not help\./);
assert.match(resume, /## Next step\nCheck the esbuild target\./);
assert.match(resume, /Verified state \(gathered now/);
assert.match(resume, /Confirm the verified state above with your own tools/);
});
test("/files handoff shows the verified state even with no handoff saved yet", async () => {
const { root } = project("1.0.0");
const out = await runCommand({ folders: [root], searchRoots: [], excludes: [] }, `/files handoff ${root}`);
assert.match(out, /No handoff has been written/);
assert.match(out, /waiting for approval:/);
assert.match(out, /[Cc]all write_handoff/);
});
test("a handoff can sit at a repository root, and handoff and resume work without a folder", async () => {
const repo = tmp("sf-dev-repo-");
fs.mkdirSync(path.join(repo, ".git"));
for (const sub of ["source", "extension"]) {
fs.mkdirSync(path.join(repo, sub, "src"), { recursive: true });
fs.writeFileSync(path.join(repo, sub, "package.json"), JSON.stringify({ name: sub, version: "1.0.0", scripts: { test: "node -e 0" } }));
fs.writeFileSync(path.join(repo, sub, "src", "index.ts"), "export {};\n");
}
const settings = { folders: [repo], searchRoots: [], excludes: [] };
const { scope } = scopeFor(settings);
// The latest change is inside source/src; with no folder named, the handoff goes to the repository root.
proposeEdit(scope, path.join(repo, "source", "src", "index.ts"), "export {};", "export const x = 1;", "demo");
const shown = await runCommand(settings, "/files handoff");
assert.match(shown, new RegExp(`# Handoff: ${repo.replace(/[\\\\^$.*+?()[\\]{}|]/g, "\\\\$&")}\\n`));
assert.match(shown, /last test run \(source\): none recorded/);
assert.match(shown, /last test run \(extension\): none recorded/);
assert.match(shown, /git repository/);
assert.match(shown, /call write_handoff with folder/i);
const wh = (await tools(repo)).find((t) => t.name === "write_handoff")!;
assert.match(await wh.implementation({ goal: "Archive the paused feature.", next_step: "Commit it on a branch." }, {}), /status: ok/);
// A new chat: no folder, or a folder inside the repository, both find the root's handoff.
for (const cmd of ["/files resume", `/files resume ${path.join(repo, "source")}`]) {
const out = await runCommand(settings, cmd);
assert.match(out, /## Goal\nArchive the paused feature\./, cmd);
assert.match(out, /## Next step\nCommit it on a branch\./, cmd);
}
});
test("auto-apply runs a project's build with the edits, but never other commands or installs", async () => {
const { root } = project("1.0.0");
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
pkg.scripts.build = 'node -e "require(\'fs\').writeFileSync(\'built.txt\', \'ok\')"';
pkg.scripts.deploy = 'node -e "process.exit(0)"';
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(pkg));
const settings = { folders: [root], searchRoots: [], excludes: [] };
const { scope } = scopeFor(settings);
await runCommand(settings, `/files autoapply ${root}`);
const run = await runScriptTool(root);
const built = await run.implementation({ folder: root, script: "build", reason: "rebuild" }, {});
assert.match(built, /status: applied\n#\d+ ran "build"/);
assert.equal(fs.readFileSync(path.join(root, "built.txt"), "utf8"), "ok");
assert.match(await run.implementation({ folder: root, script: "deploy", reason: "ship" }, {}), /status: proposed/);
assert.match(await maybeAutoApply(scope, proposeInstall(scope, root, "install")), /^status: (proposed|error)/);
assert.doesNotMatch(await maybeAutoApply(scope, proposeInstall(scope, root, "install")), /^status: applied/);
await runCommand(settings, "/files reject all");
await runCommand(settings, "/files autoapply off");
});
test("copy_files copies exactly, needs apply, never overwrites, and undo removes the copies", async () => {
const { root } = project("1.0.0");
fs.mkdirSync(path.join(root, "src", "nested"), { recursive: true });
fs.writeFileSync(path.join(root, "src", "a.ts"), "export const a = 1;\n");
fs.writeFileSync(path.join(root, "src", "nested", "b.ts"), "export const b = 2;\n");
fs.writeFileSync(path.join(root, "src", "secret.pem"), "KEY\n");
const tls = await tools(root);
const copy = tls.find((t) => t.name === "copy_files")!;
const dest = path.join(root, "archive");
const proposed = await copy.implementation({ sources: [path.join(root, "src")], dest, reason: "archive the source" }, {});
assert.match(proposed, /status: proposed/);
assert.match(proposed, /privacy: local/);
assert.match(proposed, /copy 3 file\(s\)/, "index.ts, a.ts and nested/b.ts copy; the secret .pem is skipped");
assert.equal(fs.existsSync(dest), false, "nothing copied before apply");
const id = Number(/#(\d+) copy/.exec(proposed)![1]);
const { scope } = scopeFor({ folders: [root], searchRoots: [], excludes: [] });
assert.match(await apply(scope, [id]), new RegExp(`#${id} copied: 3 file`));
assert.equal(fs.readFileSync(path.join(dest, "src", "a.ts"), "utf8"), "export const a = 1;\n", "copied byte for byte");
assert.equal(fs.readFileSync(path.join(dest, "src", "nested", "b.ts"), "utf8"), "export const b = 2;\n");
assert.equal(fs.existsSync(path.join(dest, "src", "secret.pem")), false);
// Re-proposing the same copy refuses, since it would overwrite.
const again = await copy.implementation({ sources: [path.join(root, "src")], dest, reason: "again" }, {});
assert.match(again, /already exists/);
assert.match(undo(), /removed 3 copied file/);
assert.equal(fs.existsSync(path.join(dest, "src", "a.ts")), false);
assert.equal(fs.existsSync(path.join(dest, "src")), false, "subfolders the copy created are cleaned up");
});