Files
PrismFacet/src/tools.ts
zhi c4a72b13c0 feat: PrismFacet core implementation
- Plugin scaffold: manifest, tsconfig, package.json
- Router loader with dynamic import + cache busting for hot reload
- Rule store: CRUD on rules.json
- Prompt injector: resolve routers → match rules → read files → inject
- MCP tool: prompt_rules (add/remove/list/test/reload-routers/list-routers)
- Built-in routers: agent-id (zero-dep), role, position (ego.json)
- before_prompt_build hook for system prompt injection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 17:12:10 +00:00

119 lines
3.5 KiB
TypeScript

import { addRule, removeRule, listRules } from "./rule-store.js";
import { loadRouters, getRouterNames } from "./router-loader.js";
import { resolveInjection } from "./prompt-injector.js";
import type { PluginApi } from "./types.js";
export function registerTools(
api: PluginApi,
routersDir: string
): void {
api.registerTool({
name: "prompt_rules",
description:
"Manage PrismFacet prompt injection rules. " +
"Actions: add (register a rule), remove (delete a rule), list (show all rules), " +
"test (preview which prompts would be injected for an agent), " +
"reload-routers (hot-reload all router functions), list-routers (show loaded routers).",
parameters: {
type: "object",
properties: {
action: {
type: "string",
enum: [
"add",
"remove",
"list",
"test",
"reload-routers",
"list-routers",
],
description: "The action to perform",
},
router: {
type: "string",
description: "Router name (for add/remove)",
},
key: {
type: "string",
description: "Rule key (for add/remove)",
},
file: {
type: "string",
description: "Prompt file path (for add)",
},
agent: {
type: "string",
description: "Agent ID (for test)",
},
},
required: ["action"],
},
handler: async (params: {
action: string;
router?: string;
key?: string;
file?: string;
agent?: string;
}) => {
switch (params.action) {
case "add": {
if (!params.router || !params.key || !params.file) {
return "Error: add requires --router, --key, and --file";
}
addRule(params.router, params.key, params.file);
return `Rule added: ${params.router}:${params.key}${params.file}`;
}
case "remove": {
if (!params.router || !params.key) {
return "Error: remove requires --router and --key";
}
const removed = removeRule(params.router, params.key);
return removed
? `Rule removed: ${params.router}:${params.key}`
: `Rule not found: ${params.router}:${params.key}`;
}
case "list": {
const rules = listRules();
const entries = Object.entries(rules);
if (entries.length === 0) return "No rules registered.";
return entries
.map(([k, v]) => `${k}${v}`)
.join("\n");
}
case "test": {
if (!params.agent) {
return "Error: test requires --agent";
}
const result = await resolveInjection(
{ agentId: params.agent },
api.logger
);
if (!result.appendSystemContext) {
return `No prompts matched for agent: ${params.agent}`;
}
return `Matched prompts for ${params.agent}:\n\n${result.appendSystemContext}`;
}
case "reload-routers": {
await loadRouters(routersDir, api.logger);
const names = getRouterNames();
return `Routers reloaded: ${names.join(", ") || "(none)"}`;
}
case "list-routers": {
const names = getRouterNames();
return names.length > 0
? `Loaded routers: ${names.join(", ")}`
: "No routers loaded.";
}
default:
return `Unknown action: ${params.action}`;
}
},
});
}