From e6b5e536ca1cc66c4bd07a66c9c9cd39f4fe5ee3 Mon Sep 17 00:00:00 2001 From: 4 Bytes Robby Date: Sun, 7 Jun 2026 15:22:28 +0200 Subject: [PATCH 1/5] refactor: consume logger + cache from @four-bytes/opencode-plugin-lib v0.2.0 #89 --- bun.lock | 2 +- package.json | 2 +- src/cache.ts | 60 ++--------------------------------------------- src/logger.ts | 65 +++++++-------------------------------------------- 4 files changed, 12 insertions(+), 117 deletions(-) diff --git a/bun.lock b/bun.lock index 7206b6f..43f4883 100644 --- a/bun.lock +++ b/bun.lock @@ -81,7 +81,7 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@four-bytes/opencode-plugin-lib": ["@four-bytes/opencode-plugin-lib@github:four-bytes/opencode-plugin-lib#5680406", { "peerDependencies": { "@opencode-ai/plugin": ">=1.16.0" } }, "four-bytes-opencode-plugin-lib-5680406", "sha512-9XIrIbrq52ErspssAdKzRCVkLTmNUXXmIzGZ7SRjpFsqGmPLHJYy7M2pQTfDcnPxbzoBPyb+UyvS47S+Ah+NHw=="], + "@four-bytes/opencode-plugin-lib": ["@four-bytes/opencode-plugin-lib@github:four-bytes/opencode-plugin-lib#af22c91", { "peerDependencies": { "@opencode-ai/plugin": ">=1.16.0" } }, "four-bytes-opencode-plugin-lib-af22c91", "sha512-boNCtMYCGAXm73K9WQqlylQP4kcVO2G+aaGpd7fUdcRw6UdRYdL9xlWUJELTpl0BSZIYh9iXQyug0LJ4hod7HA=="], "@huggingface/jinja": ["@huggingface/jinja@0.5.9", "", {}, "sha512-uWTG+l3VJRsl7EXxYizuL3P+cCPoc3cRqbWWRcQN0FhejRfbdq0RNhCmbY/YDtnTcz9icdLYuLDjsnz4d8JMuw=="], diff --git a/package.json b/package.json index 29625e7..6e3cd5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@four-bytes/four-opencode-brain", - "version": "1.4.1", + "version": "1.5.1", "description": "Unified brain plugin — single SQLite DB for RAG search, memory, and knowledge base with content-hash dedup and per-session LRU cache", "license": "Apache-2.0", "type": "module", diff --git a/src/cache.ts b/src/cache.ts index 353112b..409f19b 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,58 +1,2 @@ -// --------------------------------------------------------------------------- -// Per-session LRU cache using Map (TTL = process lifetime) -// --------------------------------------------------------------------------- - -export class LRUCache { - private map = new Map(); - - constructor(private maxEntries: number = 1000) {} - - get(key: K): V | undefined { - const entry = this.map.get(key); - if (!entry) return undefined; - // Refresh LRU position - this.map.delete(key); - this.map.set(key, entry); - return entry.value; - } - - set(key: K, value: V): void { - if (this.map.has(key)) this.map.delete(key); - else if (this.map.size >= this.maxEntries) { - // Evict oldest (first key in insertion order) - const firstKey = this.map.keys().next().value; - if (firstKey !== undefined) this.map.delete(firstKey); - } - this.map.set(key, { value, ts: Date.now() }); - } - - has(key: K): boolean { - return this.map.has(key); - } - - clear(): void { - this.map.clear(); - } - - get size(): number { - return this.map.size; - } -} - -// --------------------------------------------------------------------------- -// Singleton session cache — reset on plugin init / opencode restart -// --------------------------------------------------------------------------- - -export const sessionCache = { - embeddings: new LRUCache(500), // text hash → embedding vector - search: new LRUCache(200), // query+filters serialized → results - chunks: new LRUCache(500), // chunk ID → chunk data - hashes: new LRUCache(2000), // content → sha256 hex - - reset(): void { - this.embeddings.clear(); - this.search.clear(); - this.chunks.clear(); - this.hashes.clear(); - }, -}; +// Re-exported from @four-bytes/opencode-plugin-lib +export { LRUCache, sessionCache } from "@four-bytes/opencode-plugin-lib"; diff --git a/src/logger.ts b/src/logger.ts index 38638f8..4ccb262 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,59 +1,10 @@ -// --------------------------------------------------------------------------- -// Throttled, rate-limited logger — no ANSI colors, compact single-line output -// --------------------------------------------------------------------------- +// Re-exported from @four-bytes/opencode-plugin-lib +// Kept for backward compatibility — all internal imports use "../logger" etc. +export { createLogger } from "@four-bytes/opencode-plugin-lib"; +export type { LogLevel } from "@four-bytes/opencode-plugin-lib"; +import { createLogger as libCreateLogger } from "@four-bytes/opencode-plugin-lib"; -type LogLevel = "debug" | "info" | "warn" | "error"; +const brainLogger = libCreateLogger("brain"); -interface ThrottleState { - lastCall: number; - count: number; -} - -const throttles = new Map(); -let silent = false; - -function shouldLog(key: string, intervalMs: number = 60000): boolean { - const now = Date.now(); - const state = throttles.get(key); - if (!state || now - state.lastCall > intervalMs) { - throttles.set(key, { lastCall: now, count: 1 }); - return true; - } - state.count++; - return false; -} - -/** - * Suppress all console output. Use after the initial version banner. - * When silent, only memory-based log aggregation happens — no console writes. - * Toggle back with setSilent(false) for debug mode. - */ -export function setSilent(val: boolean): void { - silent = val; -} - -export function log( - level: LogLevel, - key: string, - msg: string, - data?: Record, -): void { - if (level === "debug" && process.env.BRAIN_DEBUG !== "true") return; - - if (level === "warn" || level === "info") { - // Throttle repetitive messages to once per 60s - if (!shouldLog(key, 60000)) return; - } - - // Silent mode: skip all console output (errors still go to stderr) - if (silent && level !== "error") return; - - const timestamp = new Date().toISOString(); - const prefix = `[brain] ${timestamp} ${level.toUpperCase()} ${key}`; - const payload = data ? ` ${JSON.stringify(data)}` : ""; - const line = `${prefix} ${msg}${payload}`; - - if (level === "error") console.error(line); - else if (level === "warn") console.warn(line); - else console.log(line); -} +export const log = brainLogger; +export const setSilent = brainLogger.setSilent; From f17e8677089064bc3027172fc11681304992d723 Mon Sep 17 00:00:00 2001 From: 4 Bytes Robby Date: Sun, 7 Jun 2026 15:38:38 +0200 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20switch=20to=20JSONL=20logger=20?= =?UTF-8?q?from=20lib=20=E2=80=94=20console=20noise=20removed=20#89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lock | 2 +- src/logger.ts | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/bun.lock b/bun.lock index 43f4883..a9d2290 100644 --- a/bun.lock +++ b/bun.lock @@ -81,7 +81,7 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@four-bytes/opencode-plugin-lib": ["@four-bytes/opencode-plugin-lib@github:four-bytes/opencode-plugin-lib#af22c91", { "peerDependencies": { "@opencode-ai/plugin": ">=1.16.0" } }, "four-bytes-opencode-plugin-lib-af22c91", "sha512-boNCtMYCGAXm73K9WQqlylQP4kcVO2G+aaGpd7fUdcRw6UdRYdL9xlWUJELTpl0BSZIYh9iXQyug0LJ4hod7HA=="], + "@four-bytes/opencode-plugin-lib": ["@four-bytes/opencode-plugin-lib@github:four-bytes/opencode-plugin-lib#6b8d915", { "peerDependencies": { "@opencode-ai/plugin": ">=1.16.0" } }, "four-bytes-opencode-plugin-lib-6b8d915", "sha512-bMnpphF9E+R0OuEyNKH5/ScuEXBKBJtgF0M+kigSAVEvypd1F2ZJvN6b97hSCtOqQwumLS0j/UivDnNEB4vIdw=="], "@huggingface/jinja": ["@huggingface/jinja@0.5.9", "", {}, "sha512-uWTG+l3VJRsl7EXxYizuL3P+cCPoc3cRqbWWRcQN0FhejRfbdq0RNhCmbY/YDtnTcz9icdLYuLDjsnz4d8JMuw=="], diff --git a/src/logger.ts b/src/logger.ts index 4ccb262..51e6d26 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,10 +1,20 @@ // Re-exported from @four-bytes/opencode-plugin-lib -// Kept for backward compatibility — all internal imports use "../logger" etc. -export { createLogger } from "@four-bytes/opencode-plugin-lib"; -export type { LogLevel } from "@four-bytes/opencode-plugin-lib"; -import { createLogger as libCreateLogger } from "@four-bytes/opencode-plugin-lib"; +// Console output removed — opencode TUI already captures stdout. +// Structured JSONL logging enabled via CC_DEBUG=true. +import { createJsonlLogger } from "@four-bytes/opencode-plugin-lib"; -const brainLogger = libCreateLogger("brain"); +type LogLevel = "debug" | "info" | "warn" | "error"; -export const log = brainLogger; -export const setSilent = brainLogger.setSilent; +const jsonl = createJsonlLogger("four-opencode-brain"); + +/** No-op — JSONL logger is opt-in via CC_DEBUG. Kept for API compat. */ +export function setSilent(_val: boolean): void {} + +export function log( + level: LogLevel, + key: string, + msg: string, + data?: Record, +): void { + jsonl(`${level}.${key}`, { msg, ...(data ?? {}) }); +} From b7f1a34e12f4c7c2c553b350f3a17b5f404c6216 Mon Sep 17 00:00:00 2001 From: 4 Bytes Robby Date: Sun, 7 Jun 2026 15:39:25 +0200 Subject: [PATCH 3/5] refactor: keep console logger (opencode captures it), lib provides JSONL for debug #89 --- src/logger.ts | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 51e6d26..9ca4ed0 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,14 +1,31 @@ -// Re-exported from @four-bytes/opencode-plugin-lib -// Console output removed — opencode TUI already captures stdout. -// Structured JSONL logging enabled via CC_DEBUG=true. -import { createJsonlLogger } from "@four-bytes/opencode-plugin-lib"; +// --------------------------------------------------------------------------- +// Throttled, rate-limited console logger — captured by opencode session log +// --------------------------------------------------------------------------- type LogLevel = "debug" | "info" | "warn" | "error"; -const jsonl = createJsonlLogger("four-opencode-brain"); +interface ThrottleState { + lastCall: number; + count: number; +} + +const throttles = new Map(); +let silent = false; -/** No-op — JSONL logger is opt-in via CC_DEBUG. Kept for API compat. */ -export function setSilent(_val: boolean): void {} +function shouldLog(key: string, intervalMs: number = 60000): boolean { + const now = Date.now(); + const state = throttles.get(key); + if (!state || now - state.lastCall > intervalMs) { + throttles.set(key, { lastCall: now, count: 1 }); + return true; + } + state.count++; + return false; +} + +export function setSilent(val: boolean): void { + silent = val; +} export function log( level: LogLevel, @@ -16,5 +33,20 @@ export function log( msg: string, data?: Record, ): void { - jsonl(`${level}.${key}`, { msg, ...(data ?? {}) }); + if (level === "debug" && process.env.BRAIN_DEBUG !== "true") return; + + if (level === "warn" || level === "info") { + if (!shouldLog(key, 60000)) return; + } + + if (silent && level !== "error") return; + + const timestamp = new Date().toISOString(); + const prefix = `[brain] ${timestamp} ${level.toUpperCase()} ${key}`; + const payload = data ? ` ${JSON.stringify(data)}` : ""; + const line = `${prefix} ${msg}${payload}`; + + if (level === "error") console.error(line); + else if (level === "warn") console.warn(line); + else console.log(line); } From 4dd21d50d7bc71239f5d03afe5019467a853908e Mon Sep 17 00:00:00 2001 From: 4 Bytes Robby Date: Sun, 7 Jun 2026 15:52:22 +0200 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20route=20debug=20logs=20to=20stderr?= =?UTF-8?q?=20=E2=80=94=20prevent=20stdout=20pollution=20#89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/logger.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/logger.ts b/src/logger.ts index 9ca4ed0..d43d3c7 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -48,5 +48,6 @@ export function log( if (level === "error") console.error(line); else if (level === "warn") console.warn(line); + else if (level === "debug") console.error(line); else console.log(line); } From 2353ba683fde08ecb1d40177174a722d728736cd Mon Sep 17 00:00:00 2001 From: 4 Bytes Robby Date: Sun, 7 Jun 2026 16:01:24 +0200 Subject: [PATCH 5/5] fix: pin @four-bytes/opencode-plugin-lib to v0.2.0 tag #89 --- bun.lock | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index a9d2290..03b28d6 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,7 @@ "@opencode-ai/plugin": "1.16.2", "@opentui/core": "0.3.2", "@opentui/solid": "0.3.2", - "@four-bytes/opencode-plugin-lib": "github:four-bytes/opencode-plugin-lib", + "@four-bytes/opencode-plugin-lib": "github:four-bytes/opencode-plugin-lib#v0.2.0", "node-llama-cpp": "3.18.1", "solid-js": "1.9.13", "tree-sitter-javascript": "0.25.0", diff --git a/package.json b/package.json index 6e3cd5d..b113898 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "four-bytes" ], "dependencies": { - "@four-bytes/opencode-plugin-lib": "github:four-bytes/opencode-plugin-lib", + "@four-bytes/opencode-plugin-lib": "github:four-bytes/opencode-plugin-lib#v0.2.0", "@opencode-ai/plugin": "1.16.2", "@opentui/core": "0.3.2", "@opentui/solid": "0.3.2",