opencode-fractal-memory 0.4.1 → 0.4.3

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.
package/README.md CHANGED
@@ -60,11 +60,11 @@ Add the plugin name to `~/.config/opencode/opencode.json`:
60
60
 
61
61
  ```json
62
62
  {
63
- "plugins": ["opencode-fractal-memory"]
63
+ "plugin": ["opencode-fractal-memory"]
64
64
  }
65
65
  ```
66
66
 
67
- OpenCode installs it automatically at startup. Model files (~24 MB) download on first plugin load via `ensureModels()` — no manual steps needed.
67
+ OpenCode installs it automatically at startup from npm. Model files (~24 MB) download on first plugin load via `ensureModels()` — no manual steps needed.
68
68
 
69
69
  ### For development / manual install
70
70
 
@@ -317,6 +317,9 @@ Skills are specialized instruction sets stored as memory nodes. When a task matc
317
317
  | `svelte-core-bestpractices` | svelte, component, runes |
318
318
  | `svelte-code-writer` | svelte 5, sveltekit, component |
319
319
  | `customize-opencode` | opencode config, agent, plugin |
320
+ | `context-engineering` | context, prompt, system message |
321
+ | `git-workflow-and-versioning` | git, commit, branch, version, publish |
322
+ | `incremental-implementation` | step by step, increment, gradual |
320
323
 
321
324
  ### Loading a skill
322
325
 
@@ -410,13 +413,20 @@ When OpenCode loads the plugin, `initStorage()` runs automatically:
410
413
  6. **Background embeddings** — after 1s, generates embeddings for nodes that lack them
411
414
  7. **Auto-retrieve hook** — if enabled in config, injects relevant context into prompts
412
415
 
416
+ Every initialization step is logged with timing in `logs/memory-plugin.log`, making it easy to diagnose startup issues.
417
+
413
418
  All of this happens automatically — no manual intervention required.
414
419
 
415
420
  ## Logs
416
421
 
422
+ All plugin logs are consolidated under `~/.config/opencode/logs/`:
423
+
417
424
  | Log | Path | Contents |
418
425
  |-----|------|----------|
419
- | Memory | `~/.config/opencode/memory.log` | Plugin operations, errors, injection events |
426
+ | Plugin | `logs/memory-plugin.log` | Plugin operations, init steps with timing, auto-retrieve, session events |
427
+ | MCP server | `logs/mcp-server.log` | MCP tool calls, resources, errors |
428
+ | Injection debug | `logs/memory-injection.log` | Full auto-retrieve injection payloads (rotated at 1 MB) |
429
+ | Context dump | `logs/context-dump.log` | Full context snapshots for debugging |
420
430
  | OpenCode | `~/.local/share/opencode/log/` | Application lifecycle, tool calls |
421
431
 
422
432
  ## Development
@@ -7,7 +7,13 @@ import { scoreCandidates } from "./scoring";
7
7
  import { detectRelevantSkills, detectRelevantPlaybooks } from "./detection";
8
8
  import { formatPlaybooksAsAvailable, formatSkillsAsAvailable } from "./formatting";
9
9
  import { formatNodeForInjection } from "./content";
10
- const INJECTION_LOG_FILE = path.join(os.homedir(), ".config", "opencode", "memory-injection.log");
10
+ const INJECTION_LOG_DIR = path.join(os.homedir(), ".config", "opencode", "logs");
11
+ const INJECTION_LOG_FILE = path.join(INJECTION_LOG_DIR, "memory-injection.log");
12
+ const INJECTION_LOG_MAX_SIZE = 1024 * 1024;
13
+ try {
14
+ fs.mkdirSync(INJECTION_LOG_DIR, { recursive: true });
15
+ }
16
+ catch { }
11
17
  export function createAutoRetrieveHook(deps) {
12
18
  const { store, config, log } = deps;
13
19
  const autoConfig = config.autoRetrieve;
@@ -136,6 +142,13 @@ ${JSON.stringify(memoriesJson, null, 2)}
136
142
  return;
137
143
  const debugLine = `[${new Date().toISOString()}] Query: ${userText.slice(0, 100)}...\n${fullBlock}\n\n`;
138
144
  try {
145
+ try {
146
+ const stat = fs.statSync(INJECTION_LOG_FILE);
147
+ if (stat.size > INJECTION_LOG_MAX_SIZE) {
148
+ fs.renameSync(INJECTION_LOG_FILE, INJECTION_LOG_FILE + ".old");
149
+ }
150
+ }
151
+ catch { }
139
152
  fs.appendFileSync(INJECTION_LOG_FILE, debugLine);
140
153
  }
141
154
  catch { /* silent fail */ }
package/dist/logging.js CHANGED
@@ -1,9 +1,14 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- const LOG_FILE = path.join(os.homedir(), ".config", "opencode", "memory-plugin.log");
5
- const CONTEXT_DUMP_FILE = path.join(os.homedir(), ".config", "opencode", "context_dump.log");
4
+ const LOG_DIR = path.join(os.homedir(), ".config", "opencode", "logs");
5
+ const LOG_FILE = path.join(LOG_DIR, "memory-plugin.log");
6
+ const CONTEXT_DUMP_FILE = path.join(LOG_DIR, "context-dump.log");
6
7
  const MAX_LOG_SIZE = 5 * 1024 * 1024;
8
+ try {
9
+ fs.mkdirSync(LOG_DIR, { recursive: true });
10
+ }
11
+ catch { }
7
12
  let currentSessionId = null;
8
13
  let logLevel = "info";
9
14
  // Categories to always skip (OpenCode core noise)
@@ -1,8 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import * as os from "node:os";
4
- const MCP_LOG_FILE = path.join(os.homedir(), ".config", "opencode", "fractal-memory-server.log");
4
+ const MCP_LOG_DIR = path.join(os.homedir(), ".config", "opencode", "logs");
5
+ const MCP_LOG_FILE = path.join(MCP_LOG_DIR, "mcp-server.log");
5
6
  const MAX_LOG_SIZE = 1024 * 1024;
7
+ try {
8
+ fs.mkdirSync(MCP_LOG_DIR, { recursive: true });
9
+ }
10
+ catch { }
6
11
  export function mcpLog(level, msg, data) {
7
12
  try {
8
13
  const ts = new Date().toISOString().slice(0, 19).replace("T", " ");
@@ -3,18 +3,13 @@ import { z } from "zod";
3
3
  import { createSqliteMemoryStore } from "../storage/sqlite";
4
4
  import { withMcpLogging, mcpLog } from "./logging";
5
5
  import { nodeToPlain, ensureScope, resourceStats } from "./transform";
6
- import { fileURLToPath } from "node:url";
7
- import * as path from "node:path";
8
- import * as fs from "node:fs";
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
- const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../package.json"), "utf-8"));
6
+ import { VERSION } from "../version";
12
7
  let store;
13
8
  export async function createMemoryMcpServer(projectDir, globalDbPath) {
14
9
  mcpLog("info", "Creating memory store", { projectDir });
15
10
  store = createSqliteMemoryStore(projectDir, globalDbPath);
16
11
  mcpLog("info", "Memory store created");
17
- const server = new McpServer({ name: "opencode-fractal-memory", version: pkg.version }, { capabilities: { tools: {}, resources: {} } });
12
+ const server = new McpServer({ name: "opencode-fractal-memory", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
18
13
  server.registerTool("memory_search", {
19
14
  description: "Search memory nodes by text, embedding similarity, or BM25 score",
20
15
  inputSchema: {
@@ -1,18 +1,14 @@
1
1
  #!/usr/bin/env bun
2
2
  import * as path from "node:path";
3
3
  import * as os from "node:os";
4
- import * as fs from "node:fs";
5
- import { fileURLToPath } from "node:url";
6
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
5
  import { createMemoryMcpServer } from "./mcp/server";
8
6
  import { mcpLog } from "./mcp/logging";
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
- const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"));
7
+ import { VERSION } from "./version";
12
8
  const projectDir = process.env.MGMT_PROJECT_DIR || process.cwd();
13
9
  const globalDbPath = path.join(os.homedir(), ".config", "opencode", "memory.db");
14
10
  async function main() {
15
- mcpLog("info", "MCP server starting", { name: "opencode-fractal-memory", version: pkg.version });
11
+ mcpLog("info", "MCP server starting", { name: "opencode-fractal-memory", version: VERSION });
16
12
  const server = await createMemoryMcpServer(projectDir, globalDbPath);
17
13
  const transport = new StdioServerTransport();
18
14
  await server.connect(transport);
@@ -2,6 +2,21 @@ import { initStorage, loadPluginConfig, seedRuleNodes, backfillData, scheduleBac
2
2
  import { createHookHandlers } from "./hooks";
3
3
  import { createToolMap } from "./tools";
4
4
  import { memLog, perfNow } from "../logging";
5
+ import { VERSION } from "../version";
6
+ function showToast(serverUrl, version) {
7
+ const url = `${serverUrl.origin}/tui/show-toast`;
8
+ fetch(url, {
9
+ method: "POST",
10
+ headers: { "Content-Type": "application/json" },
11
+ body: JSON.stringify({
12
+ title: "Fractal Memory",
13
+ message: `v${version} loaded`,
14
+ variant: "info",
15
+ }),
16
+ }).catch(() => {
17
+ // silently ignore — TUI may not be available in headless mode
18
+ });
19
+ }
5
20
  export const MemoryPlugin = async (ctx) => {
6
21
  const { directory, client } = ctx;
7
22
  const t0 = perfNow();
@@ -35,6 +50,7 @@ export const MemoryPlugin = async (ctx) => {
35
50
  const handlers = createHookHandlers(store, client, memConfig, ruleCache, ruleCacheDirty, sessionInjectionLock, latestUserMessage);
36
51
  const toolMap = createToolMap(store, journalTools, client);
37
52
  memLog("info", "init", "Plugin initialization completed", { totalDurationMs: perfNow() - t0 });
53
+ showToast(ctx.serverUrl, VERSION);
38
54
  return {
39
55
  ...handlers,
40
56
  ...(autoRetrieveHook || {}),
@@ -1,12 +1,6 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
2
  import { wrapWithTracking } from "./shared";
3
- import { fileURLToPath } from "node:url";
4
- import * as path from "node:path";
5
- import * as fs from "node:fs";
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
- const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../package.json"), "utf-8"));
9
- const VERSION = pkg.version;
3
+ import { VERSION } from "../version";
10
4
  export function MemoryVersion(store) {
11
5
  const t = tool({
12
6
  description: "Show the installed version of the Fractal Memory plugin.",
@@ -0,0 +1,7 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import * as path from "node:path";
3
+ import * as fs from "node:fs";
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
6
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"));
7
+ export const VERSION = pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-fractal-memory",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Fractal memory system for OpenCode with semantic search and automatic compression.",
5
5
  "main": "dist/plugin.js",
6
6
  "type": "module",