Project Files
dist / index.js
"use strict";
/**
* @file index.ts
* @description Persona Manager plugin — LM Studio plugin entry point.
*
* Registers three tools via toolsProvider:
* - `switch`: Change the active persona
* - `list`: List available persona profiles
* - `get`: Get current persona details and context
*
* Persona profiles are loaded from the `profiles/` directory as JSON files.
* Each profile defines a system prompt, description, and optional metadata.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const sdk_1 = require("@lmstudio/sdk");
const zod_1 = require("zod");
const loader_1 = require("./loader");
const context_1 = require("./context");
const manager_1 = require("./manager");
const node_path_1 = require("node:path");
// ---------------------------------------------------------------------------
// Plugin initialisation
// ---------------------------------------------------------------------------
const _dirname = (0, node_path_1.dirname)(__dirname);
/** Resolved path to the profiles/ directory bundled with the plugin. */
function getProfilesDir() {
// When running from source: plugin root is two levels up from src/
// When running from dist: plugin root is one level up from dist/
const srcPath = (0, node_path_1.resolve)(_dirname, "..", "profiles");
return srcPath;
}
// ---------------------------------------------------------------------------
// Zod schemas for tool parameters
// ---------------------------------------------------------------------------
const SwitchParams = zod_1.z.object({
personaId: zod_1.z
.string()
.min(1)
.describe("Persona ID to activate (kebab-case, e.g. 'socrates', 'einstein'). " +
"Use '__default__' to reset to the default assistant persona. " +
"Use the 'list' tool to see available personas."),
});
const SaveParams = zod_1.z.object({
name: zod_1.z
.string()
.min(1)
.describe("Display name for the persona (e.g. 'Marie Curie')."),
description: zod_1.z.string().min(1).describe("Persona backstory / description."),
systemPrompt: zod_1.z
.string()
.min(1)
.describe("System prompt injected when this persona is active."),
greeting: zod_1.z
.string()
.optional()
.describe("Optional greeting message to start a conversation."),
tags: zod_1.z
.array(zod_1.z.string())
.optional()
.describe("Optional tags for categorising this persona."),
overwrite: zod_1.z
.boolean()
.optional()
.default(false)
.describe("Overwrite an existing profile with the same generated ID."),
});
// ---------------------------------------------------------------------------
// Tools provider
// ---------------------------------------------------------------------------
async function main(context) {
// Initialise loader and context
const profilesDir = getProfilesDir();
(0, loader_1.initLoader)(profilesDir);
(0, context_1.initContext)();
context.withToolsProvider(toolsProvider);
}
async function toolsProvider(_ctl) {
// ---- switch tool ----
const switchTool = (0, sdk_1.tool)({
name: "switch",
description: "Switch the active persona to the specified character profile.\n\n" +
"This changes the system prompt to match the selected persona, " +
"resets the conversation context, and returns the active persona details.\n\n" +
"Use '__default__' as the personaId to return to the default assistant.",
parameters: {
personaId: SwitchParams.shape.personaId,
},
implementation: async ({ personaId }, { warn }) => {
try {
const result = (0, manager_1.switchToPersona)(personaId);
return {
previousPersona: result.previousPersona ?? "__none__",
activePersona: {
id: result.activePersona.id,
name: result.activePersona.name,
description: result.activePersona.description,
systemPrompt: result.activePersona.systemPrompt,
greeting: result.activePersona.greeting,
tags: result.activePersona.tags ?? [],
},
contextReset: result.contextReset,
};
}
catch (err) {
const message = err instanceof Error ? err.message : String(err ?? "unknown error");
warn(`Persona switch error: ${message}`);
return { error: message };
}
},
});
// ---- list tool ----
const listTool = (0, sdk_1.tool)({
name: "list",
description: "List all available persona profiles.\n\n" +
"Returns an array of persona summaries with id, name, description, and tags. " +
"Use the 'switch' tool with a persona ID to activate one.",
parameters: {},
implementation: async (_params) => {
try {
const result = (0, manager_1.listPersonas)();
return {
personas: result.personas.map((p) => ({
id: p.id,
name: p.name,
description: p.description,
tags: p.tags ?? [],
})),
count: result.count,
};
}
catch (err) {
const message = err instanceof Error ? err.message : String(err ?? "unknown error");
return { error: message, personas: [], count: 0 };
}
},
});
// ---- get tool ----
const getTool = (0, sdk_1.tool)({
name: "get",
description: "Get the currently active persona details and conversation context.\n\n" +
"Returns whether a persona is active, the full profile, " +
"and context metadata (systemPrompt, messageCount).",
parameters: {},
implementation: async (_params) => {
try {
const result = (0, manager_1.getCurrentPersona)();
return {
active: result.active,
persona: result.persona
? {
id: result.persona.id,
name: result.persona.name,
description: result.persona.description,
systemPrompt: result.persona.systemPrompt,
greeting: result.persona.greeting,
tags: result.persona.tags ?? [],
}
: null,
context: {
activePersonaId: result.context.activePersonaId,
activePersonaName: result.context.activePersonaName,
systemPrompt: result.context.systemPrompt,
messageCount: result.context.messageCount,
},
};
}
catch (err) {
const message = err instanceof Error ? err.message : String(err ?? "unknown error");
return { error: message };
}
},
});
// ---- save tool ----
const saveTool = (0, sdk_1.tool)({
name: "save",
description: "Create and save a new persona profile to disk.\n\n" +
"Generates a kebab-case ID from the display name, validates the input, " +
"writes the profile as a JSON file to the profiles directory, " +
"and reloads the cache so the new persona is immediately available " +
"for use with the 'switch' tool.\n\n" +
"Use overwrite=true to replace an existing profile with the same ID.",
parameters: {
name: SaveParams.shape.name,
description: SaveParams.shape.description,
systemPrompt: SaveParams.shape.systemPrompt,
greeting: SaveParams.shape.greeting,
tags: SaveParams.shape.tags,
overwrite: SaveParams.shape.overwrite,
},
implementation: async (params, { warn }) => {
try {
const result = (0, manager_1.savePersona)(params);
return {
id: result.id,
name: result.name,
description: result.description,
systemPrompt: result.systemPrompt,
greeting: result.greeting,
tags: result.tags ?? [],
filePath: result.filePath,
success: result.success,
overwritten: result.overwritten,
message: result.message,
};
}
catch (err) {
const message = err instanceof Error ? err.message : String(err ?? "unknown error");
warn(`Persona save error: ${message}`);
return { error: message, success: false };
}
},
});
return [switchTool, listTool, getTool, saveTool];
}
//# sourceMappingURL=index.js.map