prism-mcp-server 4.2.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,8 @@
7
7
  * ═══════════════════════════════════════════════════════════════════
8
8
  */
9
9
  import { getStorage } from "../storage/index.js";
10
- import { GOOGLE_API_KEY, PRISM_USER_ID } from "../config.js";
11
- import { GoogleGenerativeAI } from "@google/generative-ai";
10
+ import { PRISM_USER_ID } from "../config.js";
11
+ import { getLLMProvider } from "../utils/llm/factory.js";
12
12
  import { debugLog } from "../utils/logger.js";
13
13
  // ─── Constants ────────────────────────────────────────────────
14
14
  const COMPACTION_CHUNK_SIZE = 10;
@@ -17,27 +17,20 @@ const MAX_ENTRIES_PER_RUN = 100;
17
17
  export function isCompactLedgerArgs(args) {
18
18
  return typeof args === "object" && args !== null;
19
19
  }
20
- // ─── Gemini Summarization ─────────────────────────────────────
20
+ // ─── LLM Summarization ────────────────────────────────────────
21
21
  async function summarizeEntries(entries) {
22
- if (!GOOGLE_API_KEY) {
23
- throw new Error("Cannot compact ledger: GOOGLE_API_KEY required for Gemini summarization");
24
- }
25
- const genAI = new GoogleGenerativeAI(GOOGLE_API_KEY);
26
- const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
22
+ const llm = getLLMProvider(); // throws if no API key configured
27
23
  const entriesText = entries.map((e, i) => `[${i + 1}] ${e.session_date || "unknown date"}: ${e.summary || "no summary"}\n` +
28
24
  (e.decisions?.length ? ` Decisions: ${e.decisions.join("; ")}\n` : "") +
29
25
  (e.files_changed?.length ? ` Files: ${e.files_changed.join(", ")}\n` : "")).join("\n");
30
- const prompt = `You are compressing a session history log. Summarize these ${entries.length} ` +
26
+ const prompt = (`You are compressing a session history log. Summarize these ${entries.length} ` +
31
27
  `work sessions into a single concise paragraph (max 500 words).\n\n` +
32
28
  `PRESERVE: key decisions, important file changes, error resolutions, ` +
33
29
  `architecture changes, and any recurring patterns.\n` +
34
30
  `OMIT: routine operations, intermediate debugging steps, and redundant details.\n\n` +
35
31
  `Sessions to summarize:\n${entriesText}\n\n` +
36
- `Provide ONLY the summary paragraph, no headers or formatting.`;
37
- const truncatedPrompt = prompt.substring(0, 30000);
38
- const result = await model.generateContent(truncatedPrompt);
39
- const response = result.response;
40
- return response.text();
32
+ `Provide ONLY the summary paragraph, no headers or formatting.`).substring(0, 30000);
33
+ return llm.generateText(prompt);
41
34
  }
42
35
  // ─── Main Handler ─────────────────────────────────────────────
43
36
  export async function compactLedgerHandler(args) {
@@ -20,7 +20,7 @@
20
20
  * helps the AI understand how much token budget was saved.
21
21
  */
22
22
  import { performWebSearch, performWebSearchRaw, performLocalSearch, performLocalSearchRaw, performBraveAnswers } from "../utils/braveApi.js";
23
- import { analyzePaperWithGemini } from "../utils/googleAi.js";
23
+ import { getLLMProvider } from "../utils/llm/factory.js";
24
24
  import { isBraveWebSearchArgs, isBraveLocalSearchArgs, isBraveAnswersArgs, isGeminiResearchPaperAnalysisArgs, isBraveWebSearchCodeModeArgs, isBraveLocalSearchCodeModeArgs, isCodeModeTransformArgs } from "./definitions.js";
25
25
  import { runInSandbox } from "../utils/executor.js";
26
26
  import { CODE_MODE_TEMPLATES, getTemplateNames } from "../templates/codeMode.js";
@@ -216,8 +216,31 @@ export async function researchPaperAnalysisHandler(args) {
216
216
  };
217
217
  }
218
218
  try {
219
- debugLog(`Analyzing research paper with Gemini (${analysisType} analysis)...`);
220
- const analysis = await analyzePaperWithGemini(paperContent, analysisType, additionalContext);
219
+ debugLog(`Analyzing research paper (${analysisType} analysis)...`);
220
+ // Build the analysis prompt (mirrors the original analyzePaperWithGemini logic)
221
+ let prompt = `I need you to perform a detailed ${analysisType} analysis of the following research paper.\n\n`;
222
+ if (additionalContext) {
223
+ prompt += `Additional context: ${additionalContext}\n\n`;
224
+ }
225
+ prompt += `Research paper content:\n${paperContent}\n\n`;
226
+ switch (analysisType.toLowerCase()) {
227
+ case "summary":
228
+ prompt += "Provide a comprehensive summary including the research question, methodology, key findings, and conclusions.";
229
+ break;
230
+ case "critique":
231
+ prompt += "Provide a critical evaluation of the research methodology, validity of findings, limitations, and suggestions for improvement.";
232
+ break;
233
+ case "literature review":
234
+ prompt += "Analyze how this paper fits into the broader research landscape, identifying key related works and research gaps.";
235
+ break;
236
+ case "key findings":
237
+ prompt += "Extract and explain the most significant findings and their implications.";
238
+ break;
239
+ default:
240
+ prompt += "Perform a comprehensive analysis including summary, methodology assessment, key findings, limitations, and significance.";
241
+ }
242
+ const llm = getLLMProvider();
243
+ const analysis = await llm.generateText(prompt);
221
244
  return {
222
245
  content: [{ type: "text", text: analysis }],
223
246
  isError: false,
@@ -26,8 +26,8 @@ export { webSearchHandler, braveWebSearchCodeModeHandler, localSearchHandler, br
26
26
  // This file always exports them — server.ts decides whether to include them in the tool list.
27
27
  //
28
28
  // v0.4.0: Added SESSION_COMPACT_LEDGER_TOOL and SESSION_SEARCH_MEMORY_TOOL
29
- export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL, SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL } from "./sessionMemoryDefinitions.js";
30
- export { sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, memoryHistoryHandler, memoryCheckoutHandler, sessionSaveImageHandler, sessionViewImageHandler, sessionHealthCheckHandler, sessionForgetMemoryHandler, knowledgeSetRetentionHandler, sessionSaveExperienceHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler } from "./sessionMemoryHandlers.js";
29
+ export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, SESSION_EXPORT_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL, SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL, KNOWLEDGE_SYNC_RULES_TOOL } from "./sessionMemoryDefinitions.js";
30
+ export { sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, memoryHistoryHandler, memoryCheckoutHandler, sessionSaveImageHandler, sessionViewImageHandler, sessionHealthCheckHandler, sessionForgetMemoryHandler, knowledgeSetRetentionHandler, sessionSaveExperienceHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler, knowledgeSyncRulesHandler, sessionExportMemoryHandler } from "./sessionMemoryHandlers.js";
31
31
  // ── Compaction Handler (v0.4.0 — Enhancement #2) ──
32
32
  // The compaction handler is in a separate file because it's significantly
33
33
  // more complex than the other session memory handlers (chunked Gemini
@@ -605,6 +605,59 @@ export function isSessionForgetMemoryArgs(args) {
605
605
  "memory_id" in args &&
606
606
  typeof args.memory_id === "string");
607
607
  }
608
+ // ─── Phase 2: GDPR Export Tool ─────────────────────────────────────────
609
+ //
610
+ // Complements session_forget_memory (surgical deletion) with a full data
611
+ // portability export. Fulfills GDPR Article 20 (Right to Data Portability).
612
+ // API keys are always redacted from the exported settings object.
613
+ export const SESSION_EXPORT_MEMORY_TOOL = {
614
+ name: "session_export_memory",
615
+ description: "Export all of a project's memory to a local file. " +
616
+ "Fulfills GDPR Article 20 (Right to Data Portability) and the " +
617
+ "'local-first' portability promise.\n\n" +
618
+ "**What is exported:**\n" +
619
+ "- All session ledger entries (summaries, decisions, TODOs, file changes)\n" +
620
+ "- Current handoff state (live project context)\n" +
621
+ "- System settings (API keys are \"**REDACTED**\" for security)\n" +
622
+ "- Visual memory index (descriptions, captions, timestamps; not the raw files)\n\n" +
623
+ "**Formats:**\n" +
624
+ "- `json` — machine-readable, suitable for import into another Prism instance\n" +
625
+ "- `markdown` — human-readable, ideal for Obsidian, Notion, or archiving\n\n" +
626
+ "⚠️ Output directory must exist and be writable. " +
627
+ "Filenames are auto-generated: `prism-export-<project>-<date>.(json|md)`",
628
+ inputSchema: {
629
+ type: "object",
630
+ properties: {
631
+ project: {
632
+ type: "string",
633
+ description: "Project to export. If omitted, exports ALL projects into separate files.",
634
+ },
635
+ format: {
636
+ type: "string",
637
+ enum: ["json", "markdown"],
638
+ description: "Export format: 'json' (machine-readable) or 'markdown' (human-readable). Default: json.",
639
+ default: "json",
640
+ },
641
+ output_dir: {
642
+ type: "string",
643
+ description: "Absolute path to the directory where the export file(s) will be written. " +
644
+ "Must exist and be writable. Example: '/Users/admin/Desktop'.",
645
+ },
646
+ },
647
+ required: ["output_dir"],
648
+ },
649
+ };
650
+ /**
651
+ * Type guard for session_export_memory arguments.
652
+ * output_dir is required (must be an absolute path).
653
+ * project and format are optional.
654
+ */
655
+ export function isSessionExportMemoryArgs(args) {
656
+ return (typeof args === "object" &&
657
+ args !== null &&
658
+ "output_dir" in args &&
659
+ typeof args.output_dir === "string");
660
+ }
608
661
  // ─── v3.1: Knowledge Set Retention (TTL) ─────────────────────
609
662
  export const KNOWLEDGE_SET_RETENTION_TOOL = {
610
663
  name: "knowledge_set_retention",
@@ -743,3 +796,43 @@ export function isKnowledgeVoteArgs(args) {
743
796
  "id" in args &&
744
797
  typeof args.id === "string");
745
798
  }
799
+ // ─── v4.2: Knowledge Sync Rules Tool ─────────────────────────
800
+ export const KNOWLEDGE_SYNC_RULES_TOOL = {
801
+ name: "knowledge_sync_rules",
802
+ description: "Auto-sync graduated insights (importance >= 7) into your project's IDE rules file " +
803
+ "(.cursorrules or .clauderules). This bridges behavioral memory with static IDE context — " +
804
+ "turning dynamic agent learnings into always-on rules.\n\n" +
805
+ "**How it works:**\n" +
806
+ "1. Fetches graduated insights from the ledger\n" +
807
+ "2. Formats them as markdown rules inside sentinel markers\n" +
808
+ "3. Idempotently writes them into the target file at the project's configured repo_path\n\n" +
809
+ "**Requirements:** The project must have a repo_path configured in the dashboard.\n\n" +
810
+ "**Idempotency:** Uses `<!-- PRISM:AUTO-RULES:START -->` / `<!-- PRISM:AUTO-RULES:END -->` " +
811
+ "sentinel markers. Running this tool multiple times produces the same file. " +
812
+ "User-maintained content outside the sentinels is never touched.",
813
+ inputSchema: {
814
+ type: "object",
815
+ properties: {
816
+ project: {
817
+ type: "string",
818
+ description: "Project identifier. Must have a repo_path configured in the dashboard.",
819
+ },
820
+ target_file: {
821
+ type: "string",
822
+ description: "Target rules filename (default: '.cursorrules'). " +
823
+ "Common values: '.cursorrules', '.clauderules'.",
824
+ },
825
+ dry_run: {
826
+ type: "boolean",
827
+ description: "If true, returns a preview of the rules block without writing to disk. Default: false.",
828
+ },
829
+ },
830
+ required: ["project"],
831
+ },
832
+ };
833
+ export function isKnowledgeSyncRulesArgs(args) {
834
+ return (typeof args === "object" &&
835
+ args !== null &&
836
+ "project" in args &&
837
+ typeof args.project === "string");
838
+ }