Project Files
src / toolsProvider.ts
import { tool, type Tool, type ToolsProviderController } from "@lmstudio/sdk";
import { existsSync } from "fs";
import { z } from "zod";
import { AutoBuilder } from "./orchestrator";
import { MemoryStore } from "./memory/store";
import {
applyPatchToFile,
listWorkspaceFiles,
readWorkspaceFile,
writeWorkspaceFile,
deleteWorkspaceFile,
moveWorkspaceFile,
} from "./workspace";
export async function toolsProvider(ctl: ToolsProviderController) {
const tools: Tool[] = [];
// getWorkspaceRoot() is a helper that defers the call to ctl.getWorkingDirectory()
// until a tool is actually invoked inside an active prediction session.
// Calling it at the top of toolsProvider (outside any implementation) throws:
// "This prediction process is not attached to a working directory."
function getWorkspaceRoot(): string {
return ctl.getWorkingDirectory();
}
tools.push(
tool({
name: "forge_list_files",
description: "List files in the current workspace or a subdirectory.",
parameters: {
dir: z.string().default("."),
max_depth: z.number().int().min(1).max(12).default(8),
},
implementation: async ({ dir, max_depth }) => {
const workspaceRoot = getWorkspaceRoot();
const files = await listWorkspaceFiles(workspaceRoot, dir, max_depth);
return JSON.stringify(files, null, 2);
},
})
);
tools.push(
tool({
name: "forge_read_file",
description: "Read a text file in the current workspace.",
parameters: {
file_path: z.string(),
},
implementation: async ({ file_path }) => {
const workspaceRoot = getWorkspaceRoot();
return await readWorkspaceFile(workspaceRoot, file_path);
},
})
);
tools.push(
tool({
name: "forge_write_file",
description: "Write a text file in the current workspace.",
parameters: {
file_path: z.string(),
content: z.string(),
overwrite: z.boolean().default(true),
},
implementation: async ({ file_path, content, overwrite }) => {
const workspaceRoot = getWorkspaceRoot();
if (!overwrite && existsSync(file_path)) {
return "Error: File already exists.";
}
await writeWorkspaceFile(workspaceRoot, file_path, content);
return JSON.stringify(
{ ok: true, file_path, bytes: content.length },
null,
2
);
},
})
);
tools.push(
tool({
name: "forge_apply_patch",
description: "Apply a diff-style patch to a workspace file.",
parameters: {
file_path: z.string(),
operation: z.enum([
"overwrite",
"replace",
"insert_before",
"insert_after",
"delete",
]),
target: z.string().optional(),
content: z.string().optional(),
},
implementation: async ({ file_path, operation, target, content }) => {
const workspaceRoot = getWorkspaceRoot();
const result = await applyPatchToFile(workspaceRoot, {
file: file_path,
operation,
target,
content,
});
return JSON.stringify(result, null, 2);
},
})
);
tools.push(
tool({
name: "forge_add_memory",
description: "Store a long-term memory entry for future plugin builds.",
parameters: {
kind: z.string(),
content: z.string(),
meta: z.any().optional(),
},
implementation: async ({ kind, content, meta }) => {
const workspaceRoot = getWorkspaceRoot();
const memory = new MemoryStore(workspaceRoot);
const record = await memory.add(kind, content, meta ?? {});
return JSON.stringify(record, null, 2);
},
})
);
tools.push(
tool({
name: "forge_search_memory",
description: "Search the long-term memory store.",
parameters: {
query: z.string(),
limit: z.number().int().min(1).max(20).default(8),
},
implementation: async ({ query, limit }) => {
const workspaceRoot = getWorkspaceRoot();
const memory = new MemoryStore(workspaceRoot);
const items = await memory.search(query, limit);
return JSON.stringify(items, null, 2);
},
})
);
tools.push(
tool({
name: "forge_autobuild",
description:
"Build or repair an LM Studio plugin in an isolated workspace using a multi-agent AutoGPT-style loop.",
parameters: {
spec: z.string(),
target_dir: z.string().optional(),
max_rounds: z.number().int().min(1).max(8).default(4),
},
implementation: async ({ spec, target_dir, max_rounds }) => {
const workspaceRoot = getWorkspaceRoot();
const memory = new MemoryStore(workspaceRoot);
const builder = new AutoBuilder((ctl as any).client, workspaceRoot, memory);
const result = await builder.build(spec, {
targetDir: target_dir,
maxRounds: max_rounds,
});
return JSON.stringify(result, null, 2);
},
})
);
tools.push(
tool({
name: "forge_delete_file",
description: "Delete a file in the current workspace.",
parameters: {
file_path: z.string(),
},
implementation: async ({ file_path }) => {
const workspaceRoot = getWorkspaceRoot();
try {
await deleteWorkspaceFile(workspaceRoot, file_path);
return JSON.stringify({ ok: true, path: file_path }, null, 2);
} catch (error) {
return JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }, null, 2);
}
},
})
);
tools.push(
tool({
name: "forge_move_file",
description: "Move or rename a file in the current workspace.",
parameters: {
old_path: z.string(),
new_path: z.string(),
},
implementation: async ({ old_path, new_path }) => {
const workspaceRoot = getWorkspaceRoot();
try {
await moveWorkspaceFile(workspaceRoot, old_path, new_path);
return JSON.stringify({ ok: true, from: old_path, to: new_path }, null, 2);
} catch (error) {
return JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }, null, 2);
}
},
})
);
return tools;
}