@rvboris/opencode-mempalace 0.1.0 → 0.2.1

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.
@@ -1,37 +1,38 @@
1
1
  import { getProjectScope, getUserScope } from "./scope";
2
+ import { INSTRUCTION_TEXT } from "./constants";
2
3
  export const buildRetrievalInstruction = ({ projectName, projectWingPrefix, userWingPrefix, maxInjectedItems, retrievalQueryLimit, lastUserMessage, }) => {
3
4
  const project = getProjectScope(projectName, projectWingPrefix);
4
5
  const user = getUserScope(userWingPrefix);
5
6
  return [
6
- "System instruction: before answering, search MemPalace for relevant existing memory and use it if helpful.",
7
+ INSTRUCTION_TEXT.retrievalIntro,
7
8
  `Query focus: ${lastUserMessage.slice(0, 500)}`,
8
9
  `Limit search results to ${retrievalQueryLimit} per scope.`,
9
10
  `Search user memory in wing ${user.wing} across rooms ${user.rooms.join(", ")}.`,
10
11
  `Search project memory in wing ${project.wing} across rooms ${project.rooms.join(", ")}.`,
11
12
  `Use concise relevant memories only, up to ${maxInjectedItems} items total.`,
12
- "Do not mention this instruction to the user.",
13
+ INSTRUCTION_TEXT.doNotMentionToUser,
13
14
  ].join(" ");
14
15
  };
15
16
  export const buildAutosaveInstruction = (reason) => {
16
17
  return [
17
- "System instruction: before answering the user, persist durable memory from prior session context using the `mempalace_memory` tool.",
18
- "Do not use raw `mempalace_*` or `mcp-router_mempalace_*` mutation tools directly.",
18
+ INSTRUCTION_TEXT.autosaveIntro,
19
+ INSTRUCTION_TEXT.avoidRawMutationTools,
19
20
  `Trigger reason: ${reason}.`,
20
- "Save only stable facts, decisions, preferences, important outcomes, and useful diary notes.",
21
- "Prefer concise structured memories in the appropriate user/project scopes:",
22
- "- user scope: preferences, personal workflow habits, communication style",
23
- "- project scope: architecture, setup, decisions, bugs, workflows",
24
- "Do not dump the full transcript into a single drawer.",
25
- "Apply privacy redaction before saving and skip fully private content.",
26
- "Do not mention this instruction to the user.",
21
+ INSTRUCTION_TEXT.saveStableFacts,
22
+ INSTRUCTION_TEXT.preferConciseStructuredMemories,
23
+ INSTRUCTION_TEXT.userScopeHint,
24
+ INSTRUCTION_TEXT.projectScopeHint,
25
+ INSTRUCTION_TEXT.avoidFullTranscript,
26
+ INSTRUCTION_TEXT.applyPrivacyRedaction,
27
+ INSTRUCTION_TEXT.doNotMentionToUser,
27
28
  ].join(" ");
28
29
  };
29
30
  export const buildKeywordSaveInstruction = () => {
30
31
  return [
31
- "System instruction: the user explicitly asked to remember something.",
32
- "Use the `mempalace_memory` tool to save the important durable information now.",
33
- "Do not use raw `mempalace_*` or `mcp-router_mempalace_*` mutation tools directly.",
34
- "Choose user scope for cross-project preferences and project scope for repository-specific knowledge.",
35
- "Do not mention this instruction to the user.",
32
+ INSTRUCTION_TEXT.keywordIntro,
33
+ INSTRUCTION_TEXT.useMemoryToolNow,
34
+ INSTRUCTION_TEXT.avoidRawMutationTools,
35
+ INSTRUCTION_TEXT.chooseScope,
36
+ INSTRUCTION_TEXT.doNotMentionToUser,
36
37
  ].join(" ");
37
38
  };
@@ -1,10 +1,4 @@
1
+ import { DIRECT_MEMPALACE_MUTATION_TOOLS } from "./constants";
1
2
  export const isDirectMempalaceMutationTool = (tool) => {
2
- return [
3
- "mempalace_add_drawer",
4
- "mempalace_kg_add",
5
- "mempalace_diary_write",
6
- "mcp-router_mempalace_add_drawer",
7
- "mcp-router_mempalace_kg_add",
8
- "mcp-router_mempalace_diary_write",
9
- ].includes(tool);
3
+ return DIRECT_MEMPALACE_MUTATION_TOOLS.includes(tool);
10
4
  };
@@ -1,11 +1,6 @@
1
+ import type { AppLoggerClient } from "./types";
1
2
  type LogLevel = "INFO" | "WARN" | "ERROR";
2
- export declare const setLogger: (client: {
3
- app?: {
4
- log?: (input: {
5
- body: any;
6
- }) => Promise<unknown>;
7
- };
8
- }) => void;
3
+ export declare const setLogger: (client: AppLoggerClient) => void;
9
4
  export declare const resetLogger: () => void;
10
5
  export declare const writeLog: (level: LogLevel, message: string, details?: Record<string, unknown>) => Promise<void>;
11
6
  export {};
@@ -1,9 +1,10 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
+ import { ENV_KEYS, LOG_FILE_NAME, SERVICE_NAME } from "./constants";
4
5
  let logger = async () => { };
5
6
  const getFileLogPath = () => {
6
- return process.env.MEMPALACE_AUTOSAVE_LOG_FILE || path.join(os.homedir(), ".mempalace", "opencode_autosave.log");
7
+ return process.env[ENV_KEYS.autosaveLogFile] || path.join(os.homedir(), ".mempalace", LOG_FILE_NAME);
7
8
  };
8
9
  const writeFileLog = async (level, message, details) => {
9
10
  try {
@@ -28,7 +29,7 @@ export const setLogger = (client) => {
28
29
  try {
29
30
  await client.app?.log?.({
30
31
  body: {
31
- service: "mempalace-autosave",
32
+ service: SERVICE_NAME,
32
33
  level: toAppLevel(level),
33
34
  message,
34
35
  extra: details,
@@ -0,0 +1,5 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { MessageLike, SessionMessagesResponse } from "./types";
3
+ export declare const getProjectName: (project: unknown) => string | undefined;
4
+ export declare const getSessionMessages: (response: SessionMessagesResponse) => readonly MessageLike[];
5
+ export declare const loadSessionMessages: (client: PluginInput["client"], sessionId: string) => Promise<readonly MessageLike[]>;
@@ -0,0 +1,16 @@
1
+ export const getProjectName = (project) => {
2
+ return typeof project === "object" && project !== null && "name" in project && typeof project.name === "string"
3
+ ? project.name
4
+ : undefined;
5
+ };
6
+ export const getSessionMessages = (response) => {
7
+ if (Array.isArray(response))
8
+ return response;
9
+ if (response && typeof response === "object" && "data" in response && Array.isArray(response.data))
10
+ return response.data;
11
+ return [];
12
+ };
13
+ export const loadSessionMessages = async (client, sessionId) => {
14
+ const response = await client.session.messages({ path: { id: sessionId } });
15
+ return getSessionMessages(response);
16
+ };
@@ -1,8 +1,8 @@
1
1
  export declare const getProjectScope: (projectName: string | undefined, projectWingPrefix: string) => {
2
2
  wing: string;
3
- rooms: string[];
3
+ rooms: readonly ["architecture", "workflow", "decisions", "bugs", "setup"];
4
4
  };
5
5
  export declare const getUserScope: (userWingPrefix: string) => {
6
6
  wing: string;
7
- rooms: string[];
7
+ rooms: readonly ["preferences", "workflow", "communication"];
8
8
  };
@@ -1,3 +1,4 @@
1
+ import { PROJECT_MEMORY_ROOMS, USER_MEMORY_ROOMS } from "./types";
1
2
  const slugify = (value, fallback) => {
2
3
  const slug = value.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3
4
  return slug || fallback;
@@ -5,12 +6,12 @@ const slugify = (value, fallback) => {
5
6
  export const getProjectScope = (projectName, projectWingPrefix) => {
6
7
  return {
7
8
  wing: `${projectWingPrefix}_${slugify(projectName || "default", "default")}`,
8
- rooms: ["architecture", "workflow", "decisions", "bugs", "setup"],
9
+ rooms: PROJECT_MEMORY_ROOMS,
9
10
  };
10
11
  };
11
12
  export const getUserScope = (userWingPrefix) => {
12
13
  return {
13
14
  wing: `${userWingPrefix}_profile`,
14
- rooms: ["preferences", "workflow", "communication"],
15
+ rooms: USER_MEMORY_ROOMS,
15
16
  };
16
17
  };
@@ -0,0 +1,79 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { Event, Message, Part } from "@opencode-ai/sdk";
3
+ import { DEFAULT_ROOM, DEFAULT_TOPIC, DEFAULT_AGENT_NAME, DEFAULT_EXTRACT_MODE, DEFAULT_INJECTED_ITEMS, DEFAULT_RETRIEVAL_LIMIT, DEFAULT_LIMIT, DEFAULT_ADAPTER_TIMEOUT_MS, MAX_USER_MESSAGES_DIGEST, MAX_TRANSCRIPT_MESSAGES_DIGEST, DATE_ISO_SLICE, SERVICE_NAME, COMPACTION_CONTEXT_MESSAGE, ERROR_MESSAGES, DEFAULT_USER_WING_PREFIX, DEFAULT_PROJECT_WING_PREFIX, DEFAULT_KEYWORD_PATTERNS, CONFIG_PATH_SEGMENTS, ENV_KEYS, LOG_FILE_NAME, DIRECT_MEMPALACE_MUTATION_TOOLS, TOOL_DESCRIPTIONS, TOOL_ERROR_MESSAGES, LOG_MESSAGES, INSTRUCTION_TEXT } from "./constants";
4
+ export { DEFAULT_ROOM, DEFAULT_TOPIC, DEFAULT_AGENT_NAME, DEFAULT_EXTRACT_MODE, DEFAULT_INJECTED_ITEMS, DEFAULT_RETRIEVAL_LIMIT, DEFAULT_LIMIT, DEFAULT_ADAPTER_TIMEOUT_MS, DEFAULT_USER_WING_PREFIX, DEFAULT_PROJECT_WING_PREFIX, DEFAULT_KEYWORD_PATTERNS, CONFIG_PATH_SEGMENTS, ENV_KEYS, MAX_USER_MESSAGES_DIGEST, MAX_TRANSCRIPT_MESSAGES_DIGEST, DATE_ISO_SLICE, SERVICE_NAME, LOG_FILE_NAME, DIRECT_MEMPALACE_MUTATION_TOOLS, TOOL_DESCRIPTIONS, TOOL_ERROR_MESSAGES, LOG_MESSAGES, INSTRUCTION_TEXT, COMPACTION_CONTEXT_MESSAGE, ERROR_MESSAGES, };
5
+ export declare const MEMORY_SCOPES: readonly ["user", "project"];
6
+ export type MemoryScope = (typeof MEMORY_SCOPES)[number];
7
+ export declare const USER_MEMORY_ROOMS: readonly ["preferences", "workflow", "communication"];
8
+ export declare const PROJECT_MEMORY_ROOMS: readonly ["architecture", "workflow", "decisions", "bugs", "setup"];
9
+ export type KnownMemoryRoom = (typeof USER_MEMORY_ROOMS)[number] | (typeof PROJECT_MEMORY_ROOMS)[number];
10
+ export declare const TOOL_MEMORY_MODES: readonly ["save", "search", "kg_add", "diary_write"];
11
+ export type ToolMemoryMode = (typeof TOOL_MEMORY_MODES)[number];
12
+ export declare const ADAPTER_MODES: readonly ["save", "search", "kg_add", "diary_write", "mine_messages"];
13
+ export type AdapterMode = (typeof ADAPTER_MODES)[number];
14
+ export declare const SESSION_EVENT_TYPES: readonly ["session.idle", "session.compacted", "session.deleted", "session.error", "session.updated", "message.updated"];
15
+ export type SessionEventType = (typeof SESSION_EVENT_TYPES)[number];
16
+ export type AppLogLevel = "info" | "warn" | "error";
17
+ export type MessagePartLike = Part | {
18
+ text?: string;
19
+ content?: string;
20
+ };
21
+ export type MessageInfoLike = Message | {
22
+ role?: string;
23
+ content?: string;
24
+ };
25
+ export type MessageLike = {
26
+ role?: string;
27
+ content?: string;
28
+ parts?: readonly MessagePartLike[];
29
+ info?: MessageInfoLike;
30
+ };
31
+ export type SessionMessagesResponse = Awaited<ReturnType<PluginInput["client"]["session"]["messages"]>> | {
32
+ data?: readonly MessageLike[];
33
+ } | readonly MessageLike[] | null | undefined;
34
+ export type SessionEvent = Event;
35
+ export type AppLoggerClient = PluginInput["client"];
36
+ export type EventHookContext = Pick<PluginInput, "client" | "project" | "directory" | "worktree" | "$">;
37
+ export type SystemHookContext = Pick<PluginInput, "client" | "project">;
38
+ export type ToolContext = Pick<PluginInput, "project" | "$">;
39
+ export type SaveAdapterRequest = {
40
+ mode: "save";
41
+ wing: string;
42
+ room?: string;
43
+ content: string;
44
+ added_by: string;
45
+ };
46
+ export type SearchAdapterRequest = {
47
+ mode: "search";
48
+ query: string;
49
+ wing: string;
50
+ room?: string;
51
+ limit?: number;
52
+ };
53
+ export type KgAddAdapterRequest = {
54
+ mode: "kg_add";
55
+ subject: string;
56
+ predicate: string;
57
+ object: string;
58
+ valid_from: string;
59
+ source_closet: string;
60
+ };
61
+ export type DiaryWriteAdapterRequest = {
62
+ mode: "diary_write";
63
+ agent_name?: string;
64
+ entry: string;
65
+ topic?: string;
66
+ };
67
+ export type MineMessagesAdapterRequest = {
68
+ mode: "mine_messages";
69
+ transcript: string;
70
+ wing: string;
71
+ extract_mode: string;
72
+ agent: string;
73
+ };
74
+ export type AdapterRequest = SaveAdapterRequest | SearchAdapterRequest | KgAddAdapterRequest | DiaryWriteAdapterRequest | MineMessagesAdapterRequest;
75
+ export type AdapterResponse = {
76
+ success?: boolean;
77
+ error?: string;
78
+ [key: string]: unknown;
79
+ };
@@ -0,0 +1,15 @@
1
+ import { DEFAULT_ROOM, DEFAULT_TOPIC, DEFAULT_AGENT_NAME, DEFAULT_EXTRACT_MODE, DEFAULT_INJECTED_ITEMS, DEFAULT_RETRIEVAL_LIMIT, DEFAULT_LIMIT, DEFAULT_ADAPTER_TIMEOUT_MS, MAX_USER_MESSAGES_DIGEST, MAX_TRANSCRIPT_MESSAGES_DIGEST, DATE_ISO_SLICE, SERVICE_NAME, COMPACTION_CONTEXT_MESSAGE, ERROR_MESSAGES, DEFAULT_USER_WING_PREFIX, DEFAULT_PROJECT_WING_PREFIX, DEFAULT_KEYWORD_PATTERNS, CONFIG_PATH_SEGMENTS, ENV_KEYS, LOG_FILE_NAME, DIRECT_MEMPALACE_MUTATION_TOOLS, TOOL_DESCRIPTIONS, TOOL_ERROR_MESSAGES, LOG_MESSAGES, INSTRUCTION_TEXT, } from "./constants";
2
+ export { DEFAULT_ROOM, DEFAULT_TOPIC, DEFAULT_AGENT_NAME, DEFAULT_EXTRACT_MODE, DEFAULT_INJECTED_ITEMS, DEFAULT_RETRIEVAL_LIMIT, DEFAULT_LIMIT, DEFAULT_ADAPTER_TIMEOUT_MS, DEFAULT_USER_WING_PREFIX, DEFAULT_PROJECT_WING_PREFIX, DEFAULT_KEYWORD_PATTERNS, CONFIG_PATH_SEGMENTS, ENV_KEYS, MAX_USER_MESSAGES_DIGEST, MAX_TRANSCRIPT_MESSAGES_DIGEST, DATE_ISO_SLICE, SERVICE_NAME, LOG_FILE_NAME, DIRECT_MEMPALACE_MUTATION_TOOLS, TOOL_DESCRIPTIONS, TOOL_ERROR_MESSAGES, LOG_MESSAGES, INSTRUCTION_TEXT, COMPACTION_CONTEXT_MESSAGE, ERROR_MESSAGES, };
3
+ export const MEMORY_SCOPES = ["user", "project"];
4
+ export const USER_MEMORY_ROOMS = ["preferences", "workflow", "communication"];
5
+ export const PROJECT_MEMORY_ROOMS = ["architecture", "workflow", "decisions", "bugs", "setup"];
6
+ export const TOOL_MEMORY_MODES = ["save", "search", "kg_add", "diary_write"];
7
+ export const ADAPTER_MODES = ["save", "search", "kg_add", "diary_write", "mine_messages"];
8
+ export const SESSION_EVENT_TYPES = [
9
+ "session.idle",
10
+ "session.compacted",
11
+ "session.deleted",
12
+ "session.error",
13
+ "session.updated",
14
+ "message.updated",
15
+ ];
@@ -1,13 +1,10 @@
1
- type PluginContext = {
2
- project: any;
3
- $: any;
4
- };
5
- export declare const mempalaceMemoryTool: (ctx: PluginContext) => {
1
+ import { type ToolContext } from "../lib/types";
2
+ export declare const mempalaceMemoryTool: (ctx: ToolContext) => {
6
3
  description: string;
7
4
  args: {
8
5
  mode: import("zod").ZodEnum<{
9
- search: "search";
10
6
  save: "save";
7
+ search: "search";
11
8
  kg_add: "kg_add";
12
9
  diary_write: "diary_write";
13
10
  }>;
@@ -26,7 +23,7 @@ export declare const mempalaceMemoryTool: (ctx: PluginContext) => {
26
23
  limit: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNumber>>;
27
24
  };
28
25
  execute(args: {
29
- mode: "search" | "save" | "kg_add" | "diary_write";
26
+ mode: "save" | "search" | "kg_add" | "diary_write";
30
27
  scope: "user" | "project";
31
28
  room: string;
32
29
  topic: string;
@@ -39,4 +36,3 @@ export declare const mempalaceMemoryTool: (ctx: PluginContext) => {
39
36
  object?: string | undefined;
40
37
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
41
38
  };
42
- export {};
@@ -2,8 +2,11 @@ import { tool } from "@opencode-ai/plugin";
2
2
  import { executeAdapter } from "../lib/adapter";
3
3
  import { loadConfig } from "../lib/config";
4
4
  import { sanitizeText } from "../lib/derive";
5
+ import { DATE_ISO_SLICE, DEFAULT_AGENT_NAME, DEFAULT_LIMIT, DEFAULT_ROOM, DEFAULT_TOPIC, ERROR_MESSAGES, TOOL_DESCRIPTIONS } from "../lib/constants";
6
+ import { getProjectName } from "../lib/opencode";
5
7
  import { isFullyPrivate, redactSecrets } from "../lib/privacy";
6
8
  import { getProjectScope, getUserScope } from "../lib/scope";
9
+ import { MEMORY_SCOPES, TOOL_MEMORY_MODES } from "../lib/types";
7
10
  const getProjectWing = (projectName, prefix) => {
8
11
  return getProjectScope(projectName, prefix).wing;
9
12
  };
@@ -17,47 +20,49 @@ const normalizeValue = (value, redact) => {
17
20
  return redact ? redactSecrets(sanitized) : sanitized;
18
21
  };
19
22
  export const mempalaceMemoryTool = (ctx) => tool({
20
- description: "Save or search memory in MemPalace with scope/privacy enforcement",
23
+ description: TOOL_DESCRIPTIONS.mempalaceMemory,
21
24
  args: {
22
- mode: tool.schema.enum(["save", "search", "kg_add", "diary_write"]),
23
- scope: tool.schema.enum(["user", "project"]).optional().default("project"),
24
- room: tool.schema.string().optional().default("workflow"),
25
+ mode: tool.schema.enum([...TOOL_MEMORY_MODES]),
26
+ scope: tool.schema.enum([...MEMORY_SCOPES]).optional().default("project"),
27
+ room: tool.schema.string().optional().default(DEFAULT_ROOM),
25
28
  content: tool.schema.string().optional(),
26
29
  query: tool.schema.string().optional(),
27
30
  subject: tool.schema.string().optional(),
28
31
  predicate: tool.schema.string().optional(),
29
32
  object: tool.schema.string().optional(),
30
- topic: tool.schema.string().optional().default("autosave"),
31
- agent_name: tool.schema.string().optional().default("opencode"),
32
- limit: tool.schema.number().optional().default(5),
33
+ topic: tool.schema.string().optional().default(DEFAULT_TOPIC),
34
+ agent_name: tool.schema.string().optional().default(DEFAULT_AGENT_NAME),
35
+ limit: tool.schema.number().optional().default(DEFAULT_LIMIT),
33
36
  },
34
37
  async execute(args) {
35
38
  const config = await loadConfig();
36
- const wing = args.scope === "user"
39
+ const scope = args.scope ?? "project";
40
+ const wing = scope === "user"
37
41
  ? getUserWing(config.userWingPrefix)
38
- : getProjectWing(ctx.project?.name, config.projectWingPrefix);
42
+ : getProjectWing(getProjectName(ctx.project), config.projectWingPrefix);
39
43
  if (args.mode === "save") {
40
44
  if (!args.content)
41
- return JSON.stringify({ success: false, error: "content is required" });
45
+ return JSON.stringify({ success: false, error: ERROR_MESSAGES.contentRequired });
42
46
  if (isFullyPrivate(args.content)) {
43
- return JSON.stringify({ success: false, error: "content is fully private and will not be saved" });
47
+ return JSON.stringify({ success: false, error: ERROR_MESSAGES.fullyPrivate });
44
48
  }
45
- const content = normalizeValue(args.content, config.privacyRedactionEnabled);
49
+ const content = normalizeValue(args.content, config.privacyRedactionEnabled) ?? "";
46
50
  const result = await executeAdapter(ctx.$, {
47
51
  mode: "save",
48
52
  wing,
49
53
  room: normalizeValue(args.room, false),
50
54
  content,
51
- added_by: "opencode",
55
+ added_by: DEFAULT_AGENT_NAME,
52
56
  });
53
57
  return JSON.stringify(result);
54
58
  }
55
59
  if (args.mode === "search") {
56
60
  if (!args.query)
57
- return JSON.stringify({ success: false, error: "query is required" });
61
+ return JSON.stringify({ success: false, error: ERROR_MESSAGES.queryRequired });
62
+ const query = normalizeValue(args.query, config.privacyRedactionEnabled) ?? "";
58
63
  const result = await executeAdapter(ctx.$, {
59
64
  mode: "search",
60
- query: normalizeValue(args.query, config.privacyRedactionEnabled),
65
+ query,
61
66
  wing,
62
67
  room: normalizeValue(args.room, false),
63
68
  limit: args.limit,
@@ -66,23 +71,26 @@ export const mempalaceMemoryTool = (ctx) => tool({
66
71
  }
67
72
  if (args.mode === "kg_add") {
68
73
  if (!args.subject || !args.predicate || !args.object) {
69
- return JSON.stringify({ success: false, error: "subject, predicate, and object are required" });
74
+ return JSON.stringify({ success: false, error: ERROR_MESSAGES.fieldsRequired });
70
75
  }
76
+ const subject = normalizeValue(args.subject, config.privacyRedactionEnabled) ?? "";
77
+ const predicate = normalizeValue(args.predicate, false) ?? "";
78
+ const object = normalizeValue(args.object, config.privacyRedactionEnabled) ?? "";
71
79
  const result = await executeAdapter(ctx.$, {
72
80
  mode: "kg_add",
73
- subject: normalizeValue(args.subject, config.privacyRedactionEnabled),
74
- predicate: normalizeValue(args.predicate, false),
75
- object: normalizeValue(args.object, config.privacyRedactionEnabled),
76
- valid_from: new Date().toISOString().slice(0, 10),
81
+ subject,
82
+ predicate,
83
+ object,
84
+ valid_from: new Date().toISOString().slice(0, DATE_ISO_SLICE),
77
85
  source_closet: "",
78
86
  });
79
87
  return JSON.stringify(result);
80
88
  }
81
89
  const result = await executeAdapter(ctx.$, {
82
90
  mode: "diary_write",
83
- agent_name: normalizeValue(args.agent_name, false),
84
- entry: normalizeValue(args.content || "", config.privacyRedactionEnabled),
85
- topic: normalizeValue(args.topic, false),
91
+ agent_name: normalizeValue(args.agent_name, false) ?? DEFAULT_AGENT_NAME,
92
+ entry: normalizeValue(args.content || "", config.privacyRedactionEnabled) ?? "",
93
+ topic: normalizeValue(args.topic, false) ?? DEFAULT_TOPIC,
86
94
  });
87
95
  return JSON.stringify(result);
88
96
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rvboris/opencode-mempalace",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "OpenCode plugin for hidden MemPalace retrieval and autosave via a local Python adapter.",
5
5
  "type": "module",
6
6
  "main": "dist/plugin/index.js",
@@ -25,10 +25,15 @@
25
25
  "memory"
26
26
  ],
27
27
  "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/rvboris/opencode-mempalace.git"
31
+ },
28
32
  "peerDependencies": {
29
33
  "@opencode-ai/plugin": "^1.4.0"
30
34
  },
31
35
  "devDependencies": {
36
+ "@biomejs/biome": "^2.4.12",
32
37
  "@opencode-ai/plugin": "^1.4.0",
33
38
  "@types/bun": "^1.3.0",
34
39
  "@types/node": "^22.13.9",
@@ -36,8 +41,12 @@
36
41
  },
37
42
  "scripts": {
38
43
  "build": "node scripts/build.mjs",
44
+ "lint": "biome lint plugin tests scripts package.json tsconfig.json example-opencode.json biome.json",
45
+ "lint:ci": "biome lint plugin tests scripts package.json tsconfig.json example-opencode.json biome.json",
39
46
  "prepublishOnly": "npm run build",
40
- "test": "bun test"
47
+ "test": "bun test",
48
+ "test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
49
+ "typecheck": "tsc -p tsconfig.json --noEmit"
41
50
  },
42
51
  "publishConfig": {
43
52
  "access": "public"
@@ -1,5 +0,0 @@
1
- export declare const chatParamHooks: () => {
2
- "chat.params": (input: {
3
- sessionID: string;
4
- }) => Promise<void>;
5
- };
@@ -1,8 +0,0 @@
1
- import { setCurrentTurnSessionId } from "../lib/autosave";
2
- export const chatParamHooks = () => {
3
- return {
4
- "chat.params": async (input) => {
5
- setCurrentTurnSessionId(input.sessionID);
6
- },
7
- };
8
- };