@scitrera/memorylayer-opencode-plugin 0.1.22

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.
Files changed (58) hide show
  1. package/README.md +178 -0
  2. package/dist/src/hooks/event.d.ts +15 -0
  3. package/dist/src/hooks/event.d.ts.map +1 -0
  4. package/dist/src/hooks/event.js +53 -0
  5. package/dist/src/hooks/event.js.map +1 -0
  6. package/dist/src/hooks/message.d.ts +20 -0
  7. package/dist/src/hooks/message.d.ts.map +1 -0
  8. package/dist/src/hooks/message.js +128 -0
  9. package/dist/src/hooks/message.js.map +1 -0
  10. package/dist/src/hooks/session.d.ts +20 -0
  11. package/dist/src/hooks/session.d.ts.map +1 -0
  12. package/dist/src/hooks/session.js +121 -0
  13. package/dist/src/hooks/session.js.map +1 -0
  14. package/dist/src/hooks/tool.d.ts +23 -0
  15. package/dist/src/hooks/tool.d.ts.map +1 -0
  16. package/dist/src/hooks/tool.js +168 -0
  17. package/dist/src/hooks/tool.js.map +1 -0
  18. package/dist/src/index.d.ts +31 -0
  19. package/dist/src/index.d.ts.map +1 -0
  20. package/dist/src/index.js +176 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/shared/client.d.ts +27 -0
  23. package/dist/src/shared/client.d.ts.map +1 -0
  24. package/dist/src/shared/client.js +69 -0
  25. package/dist/src/shared/client.js.map +1 -0
  26. package/dist/src/shared/formatters.d.ts +33 -0
  27. package/dist/src/shared/formatters.d.ts.map +1 -0
  28. package/dist/src/shared/formatters.js +187 -0
  29. package/dist/src/shared/formatters.js.map +1 -0
  30. package/dist/src/shared/observation.d.ts +59 -0
  31. package/dist/src/shared/observation.d.ts.map +1 -0
  32. package/dist/src/shared/observation.js +406 -0
  33. package/dist/src/shared/observation.js.map +1 -0
  34. package/dist/src/shared/state.d.ts +66 -0
  35. package/dist/src/shared/state.d.ts.map +1 -0
  36. package/dist/src/shared/state.js +144 -0
  37. package/dist/src/shared/state.js.map +1 -0
  38. package/dist/src/shared/types.d.ts +141 -0
  39. package/dist/src/shared/types.d.ts.map +1 -0
  40. package/dist/src/shared/types.js +9 -0
  41. package/dist/src/shared/types.js.map +1 -0
  42. package/opencode.example.json +15 -0
  43. package/package.json +52 -0
  44. package/src/commands/recall.md +55 -0
  45. package/src/commands/remember.md +37 -0
  46. package/src/commands/setup.md +47 -0
  47. package/src/commands/status.md +59 -0
  48. package/src/hooks/event.ts +63 -0
  49. package/src/hooks/message.ts +147 -0
  50. package/src/hooks/session.ts +127 -0
  51. package/src/hooks/tool.ts +207 -0
  52. package/src/index.ts +197 -0
  53. package/src/shared/client.ts +73 -0
  54. package/src/shared/formatters.ts +237 -0
  55. package/src/shared/observation.ts +447 -0
  56. package/src/shared/state.ts +159 -0
  57. package/src/shared/types.ts +144 -0
  58. package/tsconfig.json +24 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Tool execution hooks for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Before-tool: Injects relevant memory context for write/delegation tools.
5
+ * After-tool: Silently captures tool observations as working memory.
6
+ */
7
+ import { getClient, checkHealth } from "../shared/client.js";
8
+ import { formatRecallResult, formatStorageGuidance } from "../shared/formatters.js";
9
+ import { shouldSkipTool, buildObservation, } from "../shared/observation.js";
10
+ import { wasQueryRecalledThisTurn, markRecallDone, getCurrentTopic, getCurrentPrompt, } from "../shared/state.js";
11
+ // ---------------------------------------------------------------------------
12
+ // Before-tool hook
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Handle pre-tool execution for task/delegation tools.
16
+ * Returns context text to prepend to tool description, or null.
17
+ */
18
+ async function handleTaskTool(toolArgs) {
19
+ const taskPrompt = (toolArgs.prompt || toolArgs.description || "");
20
+ if (!taskPrompt) {
21
+ return "RECALL-FIRST RULE: Consider using `memory_recall` before delegating to subagent. Subagents cannot access MemoryLayer.";
22
+ }
23
+ const query = taskPrompt.substring(0, 100);
24
+ if (wasQueryRecalledThisTurn(query)) {
25
+ return "Recall already done for this topic. Include relevant memories in subagent prompt.";
26
+ }
27
+ const healthy = await checkHealth();
28
+ if (!healthy)
29
+ return null;
30
+ try {
31
+ const client = getClient();
32
+ const result = await client.recall({ query, limit: 5 });
33
+ markRecallDone(query);
34
+ if (result.memories.length === 0) {
35
+ return "No relevant memories found for this task. Proceeding with delegation.";
36
+ }
37
+ const recallOutput = formatRecallResult(result, query);
38
+ return `INCLUDE IN SUBAGENT PROMPT - Relevant context from memory:\n\n${recallOutput}`;
39
+ }
40
+ catch {
41
+ return "Memory recall failed. Consider manual recall before delegation.";
42
+ }
43
+ }
44
+ /**
45
+ * Handle pre-tool execution for edit/write tools.
46
+ * Returns context text, or null.
47
+ */
48
+ async function handleEditWriteTool(toolArgs) {
49
+ const filePath = (toolArgs.file_path || toolArgs.path || toolArgs.file || "");
50
+ if (!filePath)
51
+ return null;
52
+ const filename = filePath.split("/").pop() || filePath;
53
+ const topic = getCurrentTopic();
54
+ const query = topic ? `${filename} ${topic}` : `${filename} patterns solutions`;
55
+ if (wasQueryRecalledThisTurn(query))
56
+ return null;
57
+ const healthy = await checkHealth();
58
+ if (!healthy)
59
+ return null;
60
+ try {
61
+ const client = getClient();
62
+ const result = await client.recall({ query, limit: 3 });
63
+ if (result.memories.length === 0)
64
+ return null;
65
+ markRecallDone(query);
66
+ return `Relevant context for ${filename}:\n\n${formatRecallResult(result, filename)}`;
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Handle before-tool execution.
74
+ *
75
+ * In OpenCode's hook system, this is called via "tool.execute.before" with
76
+ * (input, output) where we can mutate output.args. We use the return value
77
+ * pattern here and let the entry point handle injection.
78
+ *
79
+ * Returns additional context text, or null.
80
+ */
81
+ export async function handleToolBefore(toolName, toolArgs) {
82
+ switch (toolName) {
83
+ case "task":
84
+ return handleTaskTool(toolArgs);
85
+ case "edit":
86
+ case "write":
87
+ case "multiedit":
88
+ case "apply_patch":
89
+ return handleEditWriteTool(toolArgs);
90
+ default:
91
+ return null;
92
+ }
93
+ }
94
+ // ---------------------------------------------------------------------------
95
+ // After-tool hook
96
+ // ---------------------------------------------------------------------------
97
+ /**
98
+ * Store observation asynchronously (fire-and-forget)
99
+ */
100
+ async function storeObservationAsync(obs) {
101
+ const client = getClient();
102
+ const sessionId = client.getSessionId();
103
+ if (!sessionId)
104
+ return;
105
+ const controller = new AbortController();
106
+ const timeout = setTimeout(() => controller.abort(), 3000);
107
+ try {
108
+ await client.setWorkingMemory(sessionId, `obs_${obs.contentHash}`, {
109
+ type: obs.type,
110
+ title: obs.title,
111
+ tool: obs.toolName,
112
+ files_read: obs.filesRead,
113
+ files_modified: obs.filesModified,
114
+ facts: obs.facts,
115
+ concepts: obs.concepts,
116
+ intent: obs.intent,
117
+ summary: obs.summary,
118
+ captured_at: new Date().toISOString(),
119
+ });
120
+ }
121
+ catch {
122
+ // Silent failure — never block tool execution
123
+ }
124
+ finally {
125
+ clearTimeout(timeout);
126
+ }
127
+ }
128
+ /**
129
+ * Check if bash output indicates a significant action
130
+ */
131
+ function isSignificantBashOutput(toolArgs, toolOutput) {
132
+ const command = (toolArgs.command || "");
133
+ // Git commits
134
+ if (/git\s+commit/i.test(command))
135
+ return true;
136
+ // Build commands with errors
137
+ if (/npm\s+run\s+build|cargo\s+build|make\b|tsc\b|bun\s+build/i.test(command)) {
138
+ return /error|fail/i.test(toolOutput);
139
+ }
140
+ return false;
141
+ }
142
+ /**
143
+ * Handle after-tool execution — capture observations as working memory.
144
+ *
145
+ * Returns additional context guidance for significant events, or null.
146
+ */
147
+ export async function handleToolAfter(toolName, toolArgs, toolOutput) {
148
+ if (shouldSkipTool(toolName))
149
+ return null;
150
+ const currentPrompt = getCurrentPrompt();
151
+ const obs = buildObservation(toolName, toolArgs, toolOutput, currentPrompt);
152
+ if (!obs)
153
+ return null;
154
+ // Fire-and-forget storage
155
+ storeObservationAsync(obs).catch(() => { });
156
+ // Return guidance for significant events
157
+ if (toolName === "bash" && isSignificantBashOutput(toolArgs, toolOutput)) {
158
+ const command = (toolArgs.command || "");
159
+ if (/git\s+commit/i.test(command)) {
160
+ return formatStorageGuidance("bash", true);
161
+ }
162
+ if (/build|tsc|make|bun\s+build/i.test(command)) {
163
+ return "Build had errors. Consider storing the issue with `memory_remember` (subtype: error) for future reference.";
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/hooks/tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MemoryLayer OpenCode Plugin
3
+ *
4
+ * Provides persistent memory for OpenCode sessions via hooks that:
5
+ * - Inject workspace briefing and directives at session start
6
+ * - Recall relevant memories when users ask questions
7
+ * - Capture tool observations as working memory
8
+ * - Commit working memory before context compaction
9
+ * - Clean up sessions on exit
10
+ *
11
+ * The plugin works alongside the MemoryLayer MCP server which provides
12
+ * the full suite of 21+ memory tools to the LLM.
13
+ *
14
+ * @module @scitrera/memorylayer-opencode-plugin
15
+ */
16
+ import type { MemoryLayerHooks, PluginInput, PluginOptions } from "./shared/types.js";
17
+ export type { MemoryLayerHooks, PluginInput, PluginOptions, Part, Model, HookState } from "./shared/types.js";
18
+ /**
19
+ * MemoryLayer plugin for OpenCode.
20
+ *
21
+ * Usage in opencode.json:
22
+ * ```json
23
+ * {
24
+ * "plugin": ["@scitrera/memorylayer-opencode-plugin"]
25
+ * }
26
+ * ```
27
+ */
28
+ export default function memorylayerPlugin(ctx: PluginInput, _options?: PluginOptions): Promise<MemoryLayerHooks>;
29
+ export declare const id = "memorylayer";
30
+ export declare const server: typeof memorylayerPlugin;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAQtF,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAK9G;;;;;;;;;GASG;AACH,wBAA8B,iBAAiB,CAC7C,GAAG,EAAE,WAAW,EAChB,QAAQ,CAAC,EAAE,aAAa,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAsJ3B;AAGD,eAAO,MAAM,EAAE,gBAAgB,CAAC;AAChC,eAAO,MAAM,MAAM,0BAAoB,CAAC"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * MemoryLayer OpenCode Plugin
3
+ *
4
+ * Provides persistent memory for OpenCode sessions via hooks that:
5
+ * - Inject workspace briefing and directives at session start
6
+ * - Recall relevant memories when users ask questions
7
+ * - Capture tool observations as working memory
8
+ * - Commit working memory before context compaction
9
+ * - Clean up sessions on exit
10
+ *
11
+ * The plugin works alongside the MemoryLayer MCP server which provides
12
+ * the full suite of 21+ memory tools to the LLM.
13
+ *
14
+ * @module @scitrera/memorylayer-opencode-plugin
15
+ */
16
+ import { setPluginDirectory } from "./shared/client.js";
17
+ import { initializeSession, finalizeSession } from "./hooks/session.js";
18
+ import { handleUserMessage, extractMessageText } from "./hooks/message.js";
19
+ import { handleToolBefore, handleToolAfter } from "./hooks/tool.js";
20
+ import { handleCompacting } from "./hooks/event.js";
21
+ /** Track whether session has been initialized */
22
+ let sessionInitialized = false;
23
+ /**
24
+ * MemoryLayer plugin for OpenCode.
25
+ *
26
+ * Usage in opencode.json:
27
+ * ```json
28
+ * {
29
+ * "plugin": ["@scitrera/memorylayer-opencode-plugin"]
30
+ * }
31
+ * ```
32
+ */
33
+ export default async function memorylayerPlugin(ctx, _options) {
34
+ // Initialize client with workspace detection from plugin context
35
+ setPluginDirectory(ctx.worktree || ctx.directory);
36
+ const hooks = {
37
+ /**
38
+ * System prompt transform — inject MemoryLayer context on first interaction.
39
+ *
40
+ * This hook modifies the system prompt to include workspace briefing,
41
+ * directives, and session guidance. It runs once per session (on first call)
42
+ * and injects the formatted context into the system prompt array.
43
+ */
44
+ "experimental.chat.system.transform": async (_input, output) => {
45
+ if (!sessionInitialized) {
46
+ sessionInitialized = true;
47
+ const context = await initializeSession();
48
+ if (context) {
49
+ output.system.push(context);
50
+ }
51
+ }
52
+ },
53
+ /**
54
+ * User message hook — detect patterns and recall relevant memories.
55
+ *
56
+ * When a user sends a message matching known patterns (preference questions,
57
+ * recall requests, implementation tasks, error reports), this hook performs
58
+ * a targeted recall and injects the results as additional message parts.
59
+ */
60
+ "chat.message": async (_input, output) => {
61
+ const messageText = extractMessageText(output.parts);
62
+ if (!messageText)
63
+ return;
64
+ const context = await handleUserMessage(messageText);
65
+ if (context) {
66
+ output.parts.push({
67
+ type: "text",
68
+ text: `\n\n<memory-context>\n${context}\n</memory-context>`,
69
+ });
70
+ }
71
+ },
72
+ /**
73
+ * Pre-tool hook — inject relevant context before tool execution.
74
+ *
75
+ * For write/edit tools: recalls context relevant to the file being modified.
76
+ * For task/delegation tools: recalls and suggests including context in subagent prompts.
77
+ */
78
+ "tool.execute.before": async (input, output) => {
79
+ const context = await handleToolBefore(input.tool, output.args);
80
+ if (context) {
81
+ // Inject context as metadata that the LLM can see
82
+ // OpenCode passes args to the tool — we add a _memorylayer_context field
83
+ // that tools can optionally use, and it appears in the tool call metadata
84
+ output.args._memorylayer_context = context;
85
+ }
86
+ },
87
+ /**
88
+ * Post-tool hook — capture tool observations as working memory.
89
+ *
90
+ * Silently captures structured observations (files read/modified, facts,
91
+ * concepts, intent) and stores them as working memory. Fire-and-forget
92
+ * to avoid blocking tool execution.
93
+ *
94
+ * For significant events (git commits, build errors), injects guidance
95
+ * suggesting the user store important information.
96
+ */
97
+ "tool.execute.after": async (input, output) => {
98
+ const guidance = await handleToolAfter(input.tool, input.args, output.output);
99
+ if (guidance) {
100
+ // Append guidance to tool output so the LLM sees it
101
+ output.output = output.output + `\n\n${guidance}`;
102
+ }
103
+ },
104
+ /**
105
+ * Compaction hook — preserve memory state before context window is trimmed.
106
+ *
107
+ * Commits working memory to long-term storage and checkpoints the
108
+ * server-side sandbox so state survives context compaction.
109
+ */
110
+ "experimental.session.compacting": async (input, output) => {
111
+ const context = await handleCompacting(input.sessionID);
112
+ output.context.push(...context);
113
+ },
114
+ /**
115
+ * Shell environment hook — inject MemoryLayer env vars into shell commands.
116
+ */
117
+ "shell.env": async (_input, output) => {
118
+ if (process.env.MEMORYLAYER_URL) {
119
+ output.env.MEMORYLAYER_URL = process.env.MEMORYLAYER_URL;
120
+ }
121
+ if (process.env.MEMORYLAYER_API_KEY) {
122
+ output.env.MEMORYLAYER_API_KEY = process.env.MEMORYLAYER_API_KEY;
123
+ }
124
+ },
125
+ /**
126
+ * Command hook — handle memorylayer slash commands.
127
+ */
128
+ "command.execute.before": async (input, output) => {
129
+ const cmd = input.command;
130
+ if (cmd === "memorylayer-remember") {
131
+ output.parts.push({
132
+ type: "text",
133
+ text: `Use the \`memory_remember\` tool to store the following: ${input.arguments}\n\nAuto-detect appropriate type, subtype, importance, and tags from the content.`,
134
+ });
135
+ }
136
+ else if (cmd === "memorylayer-recall") {
137
+ output.parts.push({
138
+ type: "text",
139
+ text: `Use the \`memory_recall\` tool to search for: ${input.arguments}\n\nDisplay results with relevance scores and key metadata.`,
140
+ });
141
+ }
142
+ else if (cmd === "memorylayer-status") {
143
+ output.parts.push({
144
+ type: "text",
145
+ text: "Check MemoryLayer connection status: use `memory_briefing` to verify the MCP server is connected, then report server URL, workspace, memory statistics, and active session info.",
146
+ });
147
+ }
148
+ else if (cmd === "memorylayer-setup") {
149
+ output.parts.push({
150
+ type: "text",
151
+ text: [
152
+ "Run MemoryLayer setup verification:",
153
+ "1. Check server health (curl http://localhost:61001/health)",
154
+ "2. Verify MCP tools are connected (call memory_briefing)",
155
+ "3. Smoke test: store a test memory, recall it, then forget it",
156
+ "4. Report: server URL, workspace, tool count, connection status",
157
+ "",
158
+ "If server is not running, suggest: pip install memorylayer-server && memorylayer serve",
159
+ ].join("\n"),
160
+ });
161
+ }
162
+ },
163
+ };
164
+ // Register cleanup on process exit
165
+ const cleanup = () => {
166
+ finalizeSession().catch(() => { });
167
+ };
168
+ process.on("beforeExit", cleanup);
169
+ process.on("SIGTERM", cleanup);
170
+ process.on("SIGINT", cleanup);
171
+ return hooks;
172
+ }
173
+ // Also export the plugin as a PluginModule shape
174
+ export const id = "memorylayer";
175
+ export const server = memorylayerPlugin;
176
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Hook client - provides access to the MemoryLayerClient for hook operations.
3
+ *
4
+ * Hooks use the exact same MemoryLayerClient as MCP tools, just with a shorter
5
+ * timeout since hooks need to respond quickly.
6
+ */
7
+ import { MemoryLayerClient } from "@scitrera/memorylayer-mcp-server";
8
+ /**
9
+ * Set the plugin directory for workspace auto-detection.
10
+ * Called once during plugin initialization. Sets CWD so that
11
+ * detectWorkspaceId() (which reads git config) resolves correctly.
12
+ */
13
+ export declare function setPluginDirectory(directory: string): void;
14
+ /**
15
+ * Get or create the singleton MemoryLayerClient instance.
16
+ * This is the same client class used by MCP tools.
17
+ *
18
+ * On each call, syncs the session ID via resolveSessionId() so that
19
+ * hooks running after session start send the X-Session-ID header for
20
+ * correct workspace resolution on the server.
21
+ */
22
+ export declare function getClient(): MemoryLayerClient;
23
+ /**
24
+ * Check if the MemoryLayer server is reachable
25
+ */
26
+ export declare function checkHealth(): Promise<boolean>;
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/shared/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAqB,MAAM,kCAAkC,CAAC;AAMxF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAM1D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,iBAAiB,CA0B7C;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAOpD"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Hook client - provides access to the MemoryLayerClient for hook operations.
3
+ *
4
+ * Hooks use the exact same MemoryLayerClient as MCP tools, just with a shorter
5
+ * timeout since hooks need to respond quickly.
6
+ */
7
+ import { MemoryLayerClient, detectWorkspaceId } from "@scitrera/memorylayer-mcp-server";
8
+ import { resolveSessionId } from "./state.js";
9
+ /** Singleton client instance for hooks */
10
+ let clientInstance = null;
11
+ /**
12
+ * Set the plugin directory for workspace auto-detection.
13
+ * Called once during plugin initialization. Sets CWD so that
14
+ * detectWorkspaceId() (which reads git config) resolves correctly.
15
+ */
16
+ export function setPluginDirectory(directory) {
17
+ try {
18
+ process.chdir(directory);
19
+ }
20
+ catch {
21
+ // Ignore if directory doesn't exist
22
+ }
23
+ }
24
+ /**
25
+ * Get or create the singleton MemoryLayerClient instance.
26
+ * This is the same client class used by MCP tools.
27
+ *
28
+ * On each call, syncs the session ID via resolveSessionId() so that
29
+ * hooks running after session start send the X-Session-ID header for
30
+ * correct workspace resolution on the server.
31
+ */
32
+ export function getClient() {
33
+ if (!clientInstance) {
34
+ let workspaceId = process.env.MEMORYLAYER_WORKSPACE_ID;
35
+ if (!workspaceId) {
36
+ try {
37
+ workspaceId = detectWorkspaceId();
38
+ }
39
+ catch {
40
+ workspaceId = "_default";
41
+ }
42
+ }
43
+ clientInstance = new MemoryLayerClient({
44
+ baseUrl: process.env.MEMORYLAYER_URL,
45
+ apiKey: process.env.MEMORYLAYER_API_KEY,
46
+ workspaceId,
47
+ timeout: 5000, // Shorter timeout for hooks
48
+ });
49
+ }
50
+ // Sync session ID on every call
51
+ const sessionId = resolveSessionId("client");
52
+ if (sessionId && clientInstance.getSessionId() !== sessionId) {
53
+ clientInstance.setSessionId(sessionId);
54
+ }
55
+ return clientInstance;
56
+ }
57
+ /**
58
+ * Check if the MemoryLayer server is reachable
59
+ */
60
+ export async function checkHealth() {
61
+ try {
62
+ await getClient().getBriefing({ limit: 1, includeMemories: false });
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/shared/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,0CAA0C;AAC1C,IAAI,cAAc,GAA6B,IAAI,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,IAAI,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,IAAI,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;QACvD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,WAAW,GAAG,iBAAiB,EAAE,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW,GAAG,UAAU,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,cAAc,GAAG,IAAI,iBAAiB,CAAC;YACrC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;YACpC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;YACvC,WAAW;YACX,OAAO,EAAE,IAAI,EAAE,4BAA4B;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,SAAS,IAAI,cAAc,CAAC,YAAY,EAAE,KAAK,SAAS,EAAE,CAAC;QAC7D,cAAc,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Format API responses for injection into OpenCode's context.
3
+ *
4
+ * Uses the same types returned by MemoryLayerClient (the MCP adapter),
5
+ * so hooks and MCP tools share the same data contracts.
6
+ */
7
+ import type { Memory, RecallResult, ToolResponse } from "@scitrera/memorylayer-mcp-server";
8
+ /**
9
+ * Format recall results for context injection
10
+ */
11
+ export declare function formatRecallResult(result: RecallResult, query: string): string;
12
+ /**
13
+ * Format briefing for context injection.
14
+ * Accepts the ToolResponse from MemoryLayerClient.getBriefing().
15
+ */
16
+ export declare function formatBriefing(briefing: ToolResponse): string;
17
+ /**
18
+ * Format directive memories specially (high importance user instructions)
19
+ */
20
+ export declare function formatDirectives(memories: Memory[]): string;
21
+ /**
22
+ * Format sandbox state for context injection (post-compaction recovery)
23
+ */
24
+ export declare function formatSandboxState(inspectResult: Record<string, unknown>): string;
25
+ /**
26
+ * Format combined session start output
27
+ */
28
+ export declare function formatSessionStart(briefing: ToolResponse | null, directives: Memory[], topicRecall: RecallResult | null, topic?: string, sandboxState?: Record<string, unknown> | null): string;
29
+ /**
30
+ * Format guidance for storing memories after tool use
31
+ */
32
+ export declare function formatStorageGuidance(toolName: string, isSignificant: boolean): string;
33
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../../src/shared/formatters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AA0B3F;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAmB9E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA6C7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAmB3D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAoBjF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAC7B,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,YAAY,GAAG,IAAI,EAChC,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC5C,MAAM,CAoDR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,MAAM,CAatF"}