opencode-memoir 1.1.4 → 1.1.6

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/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/store.ts", "../src/debug.ts", "../src/utils.ts", "../src/tools.ts", "../src/memoir-ops.ts", "../src/recall-gate.ts", "../src/commands.ts", "../src/capture.ts", "../src/capture-hooks.ts", "../src/session-context.ts", "../src/index.ts"],
4
- "sourcesContent": ["import { execFile, execFileSync } from 'node:child_process';\nimport { access, mkdir, rm } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { homedir, tmpdir } from 'node:os';\nimport { promisify } from 'node:util';\nimport { debugLog } from './debug.js';\nimport { errorMessage } from './utils.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport const MEMOIR_PACKAGE = 'memoir-ai';\n\n/**\n * Pinned memoir-ai version for uvx/uv fallbacks.\n * Mirrors plugins/claude-code/scripts/resolve-memoir-cli.sh.\n * Bump deliberately after verifying the new release works with this plugin.\n */\nexport const MEMOIR_AI_PIN = '0.2.2';\n\n/** Timeout (ms) for synchronous git subprocess calls. */\nexport const GIT_TIMEOUT_MS = 3_000;\n\n/** Timeout (ms) for async memoir CLI calls. */\nexport const MEMOIR_TIMEOUT_MS = 15_000;\n\n/** Max stdout/stderr buffer (bytes) for async memoir CLI calls. */\nexport const MEMOIR_MAX_BUFFER = 1_024 * 1_024;\n\nexport type SpawnSpec = { command: string; args: string[]; label: string };\n\nexport type MemoirResult =\n | { ok: true; stdout: string }\n | { ok: false; error: string; resolver?: string };\n\n/** Max entries in _noGitCache before eviction. */\nconst NO_GIT_CACHE_MAX = 500;\n\n/**\n * Cache of cwds confirmed to be outside a git repo.\n * Avoids redundant ~200ms failing `git rev-parse` calls on non-git directories.\n */\nexport const _noGitCache = new Set<string>();\n\n/**\n * Resolve the main worktree root path for a git repository.\n * Mirrors _main_worktree_root from plugins/claude-code/scripts/derive-store-path.sh.\n *\n * @returns The main worktree path, or empty string if not in a git repo.\n */\nexport function _main_worktree_root(cwd: string): string {\n if (_noGitCache.has(cwd)) return '';\n try {\n // Fast path: check if --git-dir and --git-common-dir resolve to the same path\n const gitDir = execFileSync('git', ['rev-parse', '--git-dir'], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS }).trim();\n const gitCommonDir = execFileSync('git', ['rev-parse', '--git-common-dir'], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS }).trim();\n\n // Resolve to absolute paths\n const resolvePath = (path: string): string => {\n if (path.startsWith('/')) return path;\n return join(cwd, path);\n };\n const gitDirAbs = resolvePath(gitDir);\n const gitCommonDirAbs = resolvePath(gitCommonDir);\n\n if (gitDirAbs === gitCommonDirAbs) {\n // Main worktree or non-worktree repo\n return execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS }).trim();\n }\n\n // Slow path: parse `git worktree list --porcelain` for the main worktree\n const worktreeList = execFileSync('git', ['worktree', 'list', '--porcelain'], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS });\n const firstLine = worktreeList.split('\\n')[0];\n if (firstLine.startsWith('worktree ')) {\n const mainWorktree = firstLine.substring('worktree '.length).trim();\n if (mainWorktree && mainWorktree !== '(bare)') {\n return mainWorktree;\n }\n }\n\n // Fallback: try --show-toplevel (bare repo or older git)\n return execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT_MS }).trim();\n } catch (e) {\n debugLog('_main_worktree_root: not a git repo or git error:', errorMessage(e));\n _noGitCache.add(cwd);\n if (_noGitCache.size > NO_GIT_CACHE_MAX) _noGitCache.clear();\n return '';\n }\n}\n\n/**\n * Derive `~/.memoir/<slug>` from cwd.\n * Override via `store` plugin option or `MEMOIR_STORE` env var.\n *\n * Resolution order (mirrors plugins/claude-code/scripts/derive-store-path.sh):\n * 1. Plugin option override\n * 2. MEMOIR_STORE env var\n * 3. Git root (realpath) \u2014 all worktrees of a repo share one store\n * 4. `path.resolve()` of cwd \u2014 normalized deterministic slug for non-git folders\n * 5. Raw cwd fallback\n */\nexport function deriveStorePath(cwd: string = process.cwd()): string {\n if (pluginStoreOverride) return pluginStoreOverride;\n const configured = process.env.MEMOIR_STORE;\n if (configured) return configured;\n\n // Prefer git root so all subdirectories and worktrees share one store\n let projectDir: string;\n try {\n const gitRoot = _main_worktree_root(cwd);\n if (gitRoot) {\n projectDir = gitRoot;\n } else {\n // Not in git \u2014 normalize to deterministic absolute path\n projectDir = resolve(cwd);\n }\n } catch (e) {\n debugLog('deriveStorePath: unexpected error:', errorMessage(e));\n // Fallback to raw cwd if everything else fails\n projectDir = cwd;\n }\n\n // Slug = absolute path with '/' and '.' replaced by '-'\n // Matches Claude Code's own ~/.claude/projects/ naming convention\n const slug = projectDir.replace(/[/.]/g, '-');\n return join(homedir(), '.memoir', slug);\n}\n\n/** Set by plugin options (`store` key). */\nexport let pluginStoreOverride: string | undefined;\n\n/** Set `pluginStoreOverride` from plugin init. Exported as a function\n * because ES module imports are read-only bindings (direct assignment\n * from another module is a TS2632 error). */\nexport function setPluginStoreOverride(store: string | undefined): void {\n pluginStoreOverride = store;\n}\n\n/** Max entries in ensuredStores before eviction. */\nconst ENSURED_STORES_MAX = 200;\n\n/** Cache of stores already verified or created \u2014 avoids redundant access() + CLI calls. */\nexport const ensuredStores = new Set<string>();\n\n/**\n * Stores currently being created \u2014 prevents concurrent `memoir new` on\n * the same path (C5 fix).\n */\nconst storeCreations = new Map<string, Promise<void>>();\n\nexport async function ensureStore(store: string): Promise<void> {\n if (ensuredStores.has(store)) return;\n\n // If another caller is already creating this store, await it\n const inFlight = storeCreations.get(store);\n if (inFlight) {\n await inFlight;\n return;\n }\n\n try {\n await access(join(store, '.git'));\n ensuredStores.add(store);\n if (ensuredStores.size > ENSURED_STORES_MAX) ensuredStores.clear();\n return; // already exists\n } catch {\n // ensure parent dir exists (memoir new doesn't create intermediate dirs)\n await mkdir(join(store, '..'), { recursive: true }).catch((e: unknown) => debugLog('ensureStore: mkdir failed:', errorMessage(e)));\n }\n\n // Register this creation so concurrent callers coallesce\n const creationPromise = (async () => {\n const tmpDir = join(tmpdir(), `memoir-scratch-${Date.now()}`);\n try {\n await mkdir(tmpDir, { recursive: true });\n await execFileAsync('git', ['init', '-q', tmpDir], { timeout: 5000 });\n const result = await runMemoir(['new', store, '--taxonomy-builtin'], { cwd: tmpDir });\n if (!result.ok) {\n throw new Error(result.error);\n }\n ensuredStores.add(store);\n if (ensuredStores.size > ENSURED_STORES_MAX) ensuredStores.clear();\n } catch (e: unknown) {\n debugLog('ensureStore: creation failed:', errorMessage(e));\n throw e;\n } finally {\n rm(tmpDir, { recursive: true, force: true }).catch((e) => debugLog('tmp cleanup failed:', errorMessage(e)));\n }\n })();\n storeCreations.set(store, creationPromise);\n try {\n await creationPromise;\n } finally {\n storeCreations.delete(store);\n }\n}\n\n/**\n * Reorder a cached resolver list so that `winner` is first.\n * Returns a new array (does not mutate the input).\n */\nexport function reorderResolver<T>(resolver: T[], winner: T): T[] {\n const idx = resolver.indexOf(winner);\n if (idx <= 0) return [...resolver];\n const next = [...resolver];\n next.splice(idx, 1);\n next.unshift(winner);\n return next;\n}\n\n/** Which CLI launcher successfully resolved memoir. Cached to avoid redundant fallback probing. */\nexport let memoirResolved: string | null = null;\n\nexport async function runMemoir(args: string[], options: { cwd?: string } = {}): Promise<MemoirResult> {\n let specs = memoirSpawnSpecs(args);\n\n // Put cached resolver first so we skip redundant fallback probing\n if (memoirResolved) {\n const winner = specs.find(s => s.label === memoirResolved);\n if (winner) specs = reorderResolver(specs, winner);\n }\n\n let lastError = '';\n let lastResolver: string | undefined;\n for (const spec of specs) {\n try {\n const { stdout } = await execFileAsync(spec.command, spec.args, {\n cwd: options.cwd ?? process.cwd(),\n env: process.env,\n maxBuffer: MEMOIR_MAX_BUFFER,\n timeout: MEMOIR_TIMEOUT_MS, // prevent hang if memoir CLI stalls\n });\n memoirResolved = spec.label;\n return { ok: true, stdout: stdout.trim() };\n } catch (e) {\n lastError = errorMessage(e);\n lastResolver = spec.label;\n debugLog('runMemoir: fallback', spec.label, 'failed:', lastError);\n // Invalidate cache so we don't keep trying a broken resolver on next call\n if (memoirResolved === spec.label) memoirResolved = null;\n }\n }\n\n return { ok: false, error: `Memoir command failed: ${lastError}`, resolver: lastResolver };\n}\n\nexport function memoirSpawnSpecs(args: string[]): SpawnSpec[] {\n return [\n { command: 'memoir', args, label: 'memoir' },\n { command: 'uvx', args: ['--from', `${MEMOIR_PACKAGE}==${MEMOIR_AI_PIN}`, 'memoir', ...args], label: 'uvx' },\n { command: 'uv', args: ['tool', 'run', '--from', `${MEMOIR_PACKAGE}==${MEMOIR_AI_PIN}`, 'memoir', ...args], label: 'uv tool run' },\n ];\n}\n\n/** Read the current memoir branch name for use in storage keys. */\nexport async function getCurrentBranch(store: string): Promise<string> {\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn(['--json', '-s', store, 'status'], { cwd: store });\n if (!result.ok) return 'unknown';\n const data = JSON.parse(result.stdout);\n return data.branch || 'unknown';\n } catch (e) {\n debugLog('getCurrentBranch: failed:', errorMessage(e));\n return 'unknown';\n }\n}\n\n/**\n * Return the current git branch of the user's project (from cwd).\n * Empty string if not in a git repo or detached HEAD.\n * Mirrors code_git_branch from plugins/claude-code/hooks/common.sh.\n */\nexport async function codeGitBranch(): Promise<string> {\n try {\n const execFn = _storeTestOverrides.execFileAsyncFn ?? execFileAsync;\n const { stdout } = await execFn('git', ['branch', '--show-current'], { encoding: 'utf8', timeout: GIT_TIMEOUT_MS });\n return stdout.trim();\n } catch (e) {\n debugLog('codeGitBranch: failed:', errorMessage(e));\n return '';\n }\n}\n\n/**\n * Check whether a branch exists in the memoir store.\n * Mirrors branch_exists_in_memoir from plugins/claude-code/hooks/common.sh.\n */\nexport async function branchExistsInMemoir(store: string, name: string): Promise<boolean> {\n if (!name) return false;\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn(['--json', '-s', store, 'branch'], { cwd: store });\n if (!result.ok) return false;\n const data = JSON.parse(result.stdout);\n const branches: string[] = data?.branches ?? [];\n return branches.includes(name);\n } catch (e) {\n debugLog('branchExistsInMemoir: failed:', errorMessage(e));\n return false;\n }\n}\n\n/**\n * Auto-match memoir branch to the current code git branch.\n * If the code branch differs from the memoir branch, creates the memoir branch\n * (forked from main) if needed and checks it out.\n * Mirrors auto_match_memoir_branch from plugins/claude-code/hooks/common.sh.\n */\nexport async function autoMatchMemoirBranch(store: string): Promise<string> {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const codeBranch = await codeGitBranch();\n if (!codeBranch) {\n return getCurrentBranch(store); // detached or non-git \u2014 just report current\n }\n\n const current = await getCurrentBranch(store);\n if (current === codeBranch) return current; // already matched\n\n // Create the branch from main if it doesn't exist yet\n if (!(await branchExistsInMemoir(store, codeBranch))) {\n const result = await runFn(['-s', store, 'branch', codeBranch, '--from', 'main'], { cwd: store });\n if (!result.ok) {\n debugLog('autoMatchMemoirBranch: create branch failed:', result.error);\n return getCurrentBranch(store);\n }\n }\n // Checkout the branch\n const result = await runFn(['-s', store, 'checkout', codeBranch], { cwd: store });\n if (!result.ok) {\n debugLog('autoMatchMemoirBranch: checkout failed:', result.error);\n return getCurrentBranch(store);\n }\n return codeBranch;\n}\n\n/**\n * Read the content of a single memoir key (first item found).\n * Returns empty string if key doesn't exist or on error.\n */\nexport async function readMemoirValue(store: string, key: string, namespace: string = 'default'): Promise<string> {\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn(['--json', '-s', store, 'get', key, '-n', namespace], { cwd: store });\n if (!result.ok) return '';\n const parsed = JSON.parse(result.stdout);\n const items = parsed?.items ?? [];\n const value = items[0]?.value?.content;\n return typeof value === 'string' ? value : '';\n } catch (e) {\n debugLog('readMemoirValue: failed:', errorMessage(e));\n return '';\n }\n}\n\n// --- Test seams (only for unit tests) ---\ntype StoreTestOverrides = {\n runMemoirFn?: (args: string[], options: { cwd?: string }) => Promise<MemoirResult>;\n execFileAsyncFn?: (command: string, args: string[], options?: Record<string, unknown>) => Promise<{ stdout: string }>;\n};\nlet _storeTestOverrides: StoreTestOverrides = {};\nexport function setStoreTestOverrides(overrides: StoreTestOverrides): void {\n _storeTestOverrides = overrides;\n}\n", "/**\n * Debug logger writing to stderr. Prefixes with [memoir] for easy filtering.\n * Only writes when MEMOIR_DEBUG=1 is set.\n */\nexport function debugLog(...args: unknown[]): void {\n if (process.env.MEMOIR_DEBUG !== '1') return;\n const ts = new Date().toISOString();\n process.stderr.write(`[memoir ${ts}] ${args.map(a => String(a)).join(' ')}\\n`);\n}\n", "/** Shared helpers for the Memoir OpenCode plugin.\n *\n * IMPORTANT: these must NOT be exported from index.ts \u2014 OpenCode's plugin\n * loader treats every module export as a plugin and throws\n * \"Plugin export is not a function\" on non-function exports.\n */\n\nexport function coercePaths(path: string | string[] | undefined): string[] {\n if (!path) return [];\n return Array.isArray(path) ? path.filter(Boolean) : [path];\n}\n\n/** Format JSON string with 2-space indentation. Passes non-JSON through unchanged. */\nexport function tryPrettyJson(text: string): string {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\n/** Max keys memoir_get accepts to avoid hitting OS arg-length limits. */\nexport const MEMOIR_GET_MAX_KEYS = 20;\n\nexport const SECRET_PATTERN = new RegExp(\n [\n // Generic secret assignments\n \"(?:password|passwd|secret|api[_-]?key|token|auth[_-]?token|access[_-]?key|private[_-]?key)\\\\s*[=:]\\\\s*['\\\"]?[^\\\\s'\\\"]{8,}\",\n // AWS secret keys\n \"(?:aws[_-]?secret[_-]?access[_-]?key|aws_secret)\\\\s*[=:]\\\\s*['\\\"]?[A-Za-z0-9/+=]{40}\",\n // Connection strings with embedded credentials\n \"(?:postgres|mysql|mongodb|redis|amqp)://[^\\\\s:]+:[^\\\\s@]+@[^\\\\s]+\",\n // Generic connection strings with passwords\n \"://[^\\\\s:]+:[^\\\\s@]+@\",\n // PEM private keys\n \"-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----\",\n // Generic high-entropy secret assignments\n \"(?:secret|credential|key|password)\\\\s*[=:]\\\\s*['\\\"][A-Za-z0-9+/=_-]{20,}['\\\"]\",\n ].join(\"|\"),\n);\n\n/** Safely extract a display string from an unknown thrown value. */\nexport function errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n", "import { tool } from '@opencode-ai/plugin';\nimport { ensureStoreOrError, statusJson } from './memoir-ops.js';\nimport { deriveStorePath, runMemoir } from './store.js';\nimport { DEFAULT_RECALL_NAMESPACES, isSecretSanitizationEnabled } from './recall-gate.js';\nimport { coercePaths, MEMOIR_GET_MAX_KEYS, tryPrettyJson, SECRET_PATTERN } from './utils.js';\n\ntype MemoirRememberArgs = {\n content: string;\n path?: string | string[];\n namespace?: string;\n replace?: boolean;\n};\n\ntype MemoirRecallArgs = {\n query?: string;\n namespace?: string;\n namespaces?: string[];\n includeMetrics?: boolean;\n};\n\ntype MemoirGetArgs = {\n keys: string[];\n namespace?: string;\n};\n\nconst memoirStatus = tool({\n description: 'Show Memoir status for the current OpenCode project store.',\n args: {},\n execute: async () => statusJson(deriveStorePath()),\n});\n\nconst memoirRemember = tool({\n description: 'Explicitly save a durable memory to Memoir at one or more semantic taxonomy paths.',\n args: {\n content: tool.schema.string().describe('Memory content to save. Do not include secrets.'),\n path: tool.schema.string().optional().describe('Semantic taxonomy path, e.g. preferences.coding.style.'),\n namespace: tool.schema.string().optional().describe('Memoir namespace. Defaults to default.'),\n replace: tool.schema.boolean().optional().describe('Replace existing value at the path.'),\n },\n execute: async (args: MemoirRememberArgs) => {\n const content = args.content?.trim();\n if (!content) return 'Memoir memory was not saved: content is empty.';\n if (isSecretSanitizationEnabled() && SECRET_PATTERN.test(content)) {\n return 'Memoir memory was not saved: the content looks like a secret or credential. Save a redacted rule instead.';\n }\n const paths = coercePaths(args.path);\n if (paths.length === 0) {\n return 'Memoir memory was not saved: provide a semantic path, e.g. preferences.coding.style.';\n }\n\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const cliArgs = ['--json', '-s', store, 'remember', content];\n for (const p of paths) cliArgs.push('-p', p);\n cliArgs.push('-n', args.namespace ?? 'default');\n if (args.replace) cliArgs.push('--replace');\n const rememberResult = await runMemoir(cliArgs, { cwd: store });\n if (!rememberResult.ok) return rememberResult.error;\n return tryPrettyJson(rememberResult.stdout);\n },\n});\n\nconst memoirRecall = tool({\n description: 'List Memoir memory keys across relevant namespaces for relevance picking. Never calls legacy memoir recall.',\n args: {\n query: tool.schema.string().optional().describe('User query or topic to recall for.'),\n namespace: tool.schema.string().optional().describe('Single Memoir namespace to inspect. If omitted, checks default + onboard namespaces.'),\n namespaces: tool.schema.array(tool.schema.string()).optional().describe('Namespaces to inspect. Defaults to default, project:onboard, codebase:onboard.'),\n includeMetrics: tool.schema.boolean().optional().describe('Include metrics.* memories in the listing.'),\n },\n execute: async (args: MemoirRecallArgs) => {\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const namespaces = args.namespace\n ? [args.namespace]\n : (args.namespaces && args.namespaces.length > 0 ? args.namespaces : DEFAULT_RECALL_NAMESPACES);\n const sections: string[] = [];\n for (const namespace of namespaces) {\n const summarizeResult = await runMemoir(['--json', '-s', store, 'summarize', '--depth', '3', '-n', namespace], { cwd: store });\n if (!summarizeResult.ok) return summarizeResult.error;\n const output = tryPrettyJson(summarizeResult.stdout);\n sections.push(`## namespace: ${namespace}\\n${output}`);\n }\n const note = args.includeMetrics\n ? 'Metrics were included by request.'\n : 'Ignore metrics.* and taxonomy:v1:* entries unless explicitly needed. If default is empty or only metrics, inspect project:onboard/codebase:onboard before concluding there is no memory.';\n const query = args.query ? `Query: ${args.query}\\n` : '';\n return `${query}${note}\\nPick at most 5-7 relevant exact keys across namespaces, then call memoir_get with the matching namespace if values are needed.\\n${sections.join('\\n\\n')}`;\n },\n});\n\nconst memoirGet = tool({\n description: 'Fetch exact Memoir memory keys after selecting them from memoir_recall output.',\n args: {\n keys: tool.schema.array(tool.schema.string()).describe('Exact memory keys to fetch.'),\n namespace: tool.schema.string().optional().describe('Memoir namespace. Defaults to default.'),\n },\n execute: async (args: MemoirGetArgs) => {\n const keys = args.keys?.map((key) => key.trim()).filter(Boolean) ?? [];\n if (keys.length === 0) return 'No Memoir keys were provided.';\n if (keys.length > MEMOIR_GET_MAX_KEYS) {\n return `Error: Too many keys requested (max ${MEMOIR_GET_MAX_KEYS}, got ${keys.length}). Narrow your selection from memoir_recall output.`;\n }\n\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const getResult = await runMemoir(['--json', '-s', store, 'get', ...keys, '-n', args.namespace ?? 'default'], { cwd: store });\n if (!getResult.ok) return getResult.error;\n return tryPrettyJson(getResult.stdout);\n },\n});\n\nexport const memoirTools = {\n memoir_status: memoirStatus,\n memoir_remember: memoirRemember,\n memoir_recall: memoirRecall,\n memoir_get: memoirGet,\n};\n", "import { spawn } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { ensureStore, runMemoir, codeGitBranch, memoirSpawnSpecs, memoirResolved, reorderResolver } from './store.js';\nimport { errorMessage } from './utils.js';\nimport { debugLog } from './debug.js';\n\n/** Timeout (ms) for UI URL detection polling. */\nconst UI_URL_DEADLINE_MS = 5_000;\n\n/** Polling interval (ms) for UI URL detection. */\nconst UI_URL_POLL_MS = 100;\n\n/** Ensure the memoir store exists, returning an error message on failure. */\nexport async function ensureStoreOrError(store: string): Promise<string | null> {\n try {\n await ensureStore(store);\n return null;\n } catch (error) {\n return errorMessage(error);\n }\n}\n\nexport async function statusJson(store: string): Promise<string> {\n const storeErr = await ensureStoreOrError(store);\n if (storeErr) return `Memoir command failed: ${storeErr}`;\n const result = await runMemoir(['--json', '-s', store, 'status'], { cwd: store });\n if (!result.ok) return result.error;\n try {\n const data = JSON.parse(result.stdout);\n const branch = await codeGitBranch();\n data.opencode = { store, project_git_root: process.cwd(), project_git_branch: branch };\n return JSON.stringify(data, null, 2);\n } catch (e) {\n debugLog('statusJson: failed to parse JSON:', errorMessage(e));\n return result.stdout;\n }\n}\n\nexport async function launchUi(store: string): Promise<string> {\n await ensureStore(store);\n const pidDir = join(homedir(), '.memoir', 'ui-servers');\n await mkdir(pidDir, { recursive: true });\n const hash = createHash('sha256').update(store).digest('hex').slice(0, 8);\n const pidfile = join(pidDir, `${hash}.json`);\n\n try {\n const existing = JSON.parse(await readFile(pidfile, 'utf8'));\n if (existing?.pid && existing?.url) {\n try {\n process.kill(Number(existing.pid), 0); // check if alive\n return JSON.stringify({ ...existing, reused: true }, null, 2);\n } catch (e) {\n debugLog('launchUi: process dead, relaunching:', errorMessage(e));\n // process is dead \u2014 fall through to relaunch\n }\n }\n } catch (e) {\n debugLog('launchUi: failed to read pidfile:', errorMessage(e));\n await rm(pidfile, { force: true }).catch((e) => debugLog('launchUi: failed to remove stale pidfile', errorMessage(e)));\n }\n\n let lastError = '';\n let uiSpecs = memoirSpawnSpecs(['ui', store]);\n // Reorder: put cached memoir resolver first, same as runMemoir\n if (memoirResolved) {\n const winner = uiSpecs.find(s => s.label === memoirResolved);\n if (winner) uiSpecs = reorderResolver(uiSpecs, winner);\n }\n for (const spec of uiSpecs) {\n const child = spawn(spec.command, spec.args, {\n detached: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: process.env,\n });\n\n let output = '';\n child.stdout?.on('data', chunk => { output += String(chunk); });\n child.stderr?.on('data', chunk => { output += String(chunk); });\n\n const spawnFailed = new Promise<string | null>(resolve => {\n child.once('error', error => resolve(errorMessage(error)));\n child.once('spawn', () => resolve(null));\n });\n const error = await spawnFailed;\n if (error) {\n lastError = `${spec.label}: ${error}`;\n continue;\n }\n\n child.unref();\n\n const urlPattern = /https?:\\/\\/(?:localhost|127\\.0\\.0\\.1):\\d+\\S*/;\n const deadline = Date.now() + UI_URL_DEADLINE_MS;\n while (Date.now() < deadline) {\n const match = output.match(urlPattern);\n if (match) {\n const url = match[0];\n const data = { pid: child.pid, url, store, command: spec.label, started: new Date().toISOString(), reused: false };\n await writeFile(pidfile, JSON.stringify(data, null, 2));\n return JSON.stringify(data, null, 2);\n }\n await new Promise(resolve => setTimeout(resolve, UI_URL_POLL_MS));\n }\n\n return `Memoir UI started with ${spec.label} (pid ${child.pid ?? 'unknown'}), but URL was not detected yet.\\n${output.trim()}`.trim();\n }\n\n return `Memoir UI failed to start: ${lastError || 'no launcher succeeded'}`;\n}\n\n/** List memoir branches with unmerged changes relative to main. */\nexport async function unmergedBranchesText(store: string): Promise<string> {\n await ensureStore(store);\n const branchResult = await runMemoir(['--json', '-s', store, 'branch'], { cwd: store });\n if (!branchResult.ok) {\n return branchResult.error;\n }\n const data = JSON.parse(branchResult.stdout);\n const branches: string[] = data?.branches ?? [];\n const unmerged: string[] = [];\n for (const branch of branches) {\n if (branch === 'main') continue;\n const diffResult = await runMemoir(['-s', store, 'diff', branch, 'main', '--stat'], { cwd: store });\n if (!diffResult.ok) {\n debugLog('command.execute.before: memoir diff failed', diffResult.error);\n continue;\n }\n if (diffResult.stdout.trim()) {\n unmerged.push(branch);\n }\n }\n return unmerged.length > 0\n ? `Unmerged branches:\\n${unmerged.join('\\n')}`\n : 'All branches are merged into main.';\n}\n", "export { SECRET_PATTERN } from './utils.js';\n\n/** Whether secret sanitization is enabled. Default on. Set MEMOIR_SANITIZE_SECRETS=0 to disable. */\nexport function isSecretSanitizationEnabled(): boolean {\n return process.env.MEMOIR_SANITIZE_SECRETS !== '0';\n}\n\n/**\n * Sessions waiting for a recall instruction (one-shot, consumed by\n * experimental.chat.system.transform on the next LLM call).\n */\nexport const pendingRecall = new Set<string>();\n\n/** Short acknowledgements that never trigger recall. */\nconst ACK_PATTERN = /^(ok|thanks|thank you|sounds good|got it|\uD83D\uDC4D|\uD83D\uDE4F|perfect|great|cool|nice|awesome|understood|makes sense|agree|right|sure|yes|no|done|nvm|never mind|lgtm|looks good|proceed|continue|good|fine)\\b/i;\n\n/** Explicit memoir commands always trigger recall regardless of length. */\nconst EXPLICIT_RECALL_PATTERN = /\\b(memoir:recall|memoir:remember|memoir-recall|memoir-remember)\\b|(\\/recall|\\/remember)\\b/i;\n\n/** Positive-list patterns \u2014 identical to the Claude Code UserPromptSubmit gate. */\nconst RECALL_TRIGGER_PATTERNS = [\n // Action verbs and domain nouns\n /\\b(add|build|implement|refactor|redesign|design|create|write|set( |-)up|wire( |-)up|integrate|migrate|rewrite|extract|extend|plumb|hook( |-)up|ship|scaffold|optimize|fix|debug|review|architect|model|schema|API|service|feature|module|system|pipeline|workflow|make|move|replace|convert|swap|remove|clean( |-)up|transform|investigate|explore|figure( |-)out|plan|decide|choose|pick|compare|walk( |-)?me( |-)?through|take( |-)?a( |-)?stab|help( |-)?me|harness|hook|prompt|test)\\b/i,\n // Question starts (how/why/what/where/when/should/can/could/would/is it/are we/do I/does it)\n /^(how|why|what|where|when|should|can|could|would|is it|are we|do I|does it)\\b.*\\?/im,\n // Code blocks (triple backticks)\n /```/,\n // Code definitions\n /\\b(def|function|class|import|export)\\s+/,\n // Memoir/recall keywords\n /\\b(memoir|recall|remember|memory)\\b/i,\n // File extensions\n /\\b\\w+\\.(py|js|ts|tsx|scala|java|go|rs|rb|md|json|yaml|yml|toml|sh|bash|css|html|kt|swift|c|cpp|h|hpp)\\b/i,\n // File paths (slash-containing tokens)\n /\\w+\\/\\w+/,\n];\n\nexport function isAcknowledgement(text: string): boolean {\n const trimmed = text.trim().toLowerCase();\n const words = trimmed.split(/\\s+/);\n return words.length <= 5 && ACK_PATTERN.test(trimmed);\n}\n\n/**\n * Decide whether a user message should trigger a recall instruction.\n * Identical gate logic to the Claude Code UserPromptSubmit hook:\n *\n * 1. Empty text \u2192 skip\n * 2. Explicit `/recall` / `memoir:recall` commands \u2192 always fire\n * 3. < 10 chars \u2192 skip (empty or noise)\n * 4. Acknowledgements (ok/thanks/\u2026) \u2192 skip\n * 5. < 40 chars without explicit command \u2192 skip (too short for intent)\n * 6. \u2265 40 chars + any trigger pattern \u2192 fire\n */\nexport function shouldTriggerRecall(text: string): boolean {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Explicit memoir commands fire regardless of length\n if (EXPLICIT_RECALL_PATTERN.test(trimmed)) return true;\n if (trimmed.length < 10) return false;\n if (isAcknowledgement(trimmed)) return false;\n // Gate: \u2265 40 chars + any trigger\n if (trimmed.length >= 40 && RECALL_TRIGGER_PATTERNS.some(p => p.test(trimmed))) return true;\n return false;\n}\n\nexport const DEFAULT_RECALL_NAMESPACES = ['default', 'project:onboard', 'codebase:onboard'];\n", "import { type Config } from '@opencode-ai/plugin';\nimport { errorMessage } from './utils.js';\nimport { statusJson, launchUi, unmergedBranchesText } from './memoir-ops.js';\n\nexport type CommandOutput = {\n parts: unknown[];\n};\n\nexport type OpenCodeConfig = Config & {\n command?: Config['command'];\n};\n\nfunction pushText(output: CommandOutput, text: string): void {\n output.parts.length = 0;\n output.parts.push({ type: 'text', text });\n}\n\nexport function registerCommands(config: OpenCodeConfig): void {\n config.command = config.command ?? {};\n\n config.command['memoir:status'] = {\n description: 'Show Memoir status for the current OpenCode project',\n template: 'Show Memoir status for this OpenCode project.',\n };\n\n config.command['memoir:ui'] = {\n description: 'Launch or reopen the Memoir web UI for this project store',\n template: 'Launch or reopen the Memoir UI for this project.',\n };\n\n config.command['memoir:remember'] = {\n description: 'Save a durable fact, preference, rule, or decision to Memoir',\n template: `Use Memoir to save this durable memory now.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nExtract the memory content, choose a semantic path if none is supplied, then call the memoir_remember tool. Never save secrets.`,\n };\n\n config.command['memoir:recall'] = {\n description: 'Recall relevant facts from Memoir before answering',\n template: `Recall relevant Memoir memories for this request.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nUse memoir_recall first. It checks default plus onboard namespaces unless a namespace is specified. Then call memoir_get with the matching namespace for exact values before answering.`,\n };\n\n config.command['memoir:onboard'] = {\n description: 'Populate or refresh Memoir onboarding for this project',\n template: `Populate or refresh Memoir onboarding for the CURRENT OpenCode project only.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nWorkflow:\\n- Stay inside the current project/worktree. Do not inspect parent directories.\\n- First obtain a project file tree to understand structure.\\n- Start studying from project documentation.\\n- Continue only based on what the tree and documentation show.\\n\\nMemory rules:\\n- Record only verified facts from files/docs/code or explicit user statements.\\n- Do not write inferred user thoughts, intentions, preferences, or opinions.\\n- Do not use preferences.* paths unless the user explicitly stated a preference.\\n- If a fact is your interpretation, do not save it; report it as uncertain instead.\\n\\nThen call memoir_remember with replace=true for durable onboarding facts. Use namespace codebase:onboard in git repositories and project:onboard outside git. Do not install or invoke separate skills/scripts.`,\n };\n\n config.command['memoir:unmerged'] = {\n description: 'List memoir branches with changes not yet merged into main',\n template: 'List memoir branches that have diverged from main.',\n };\n}\n\nexport async function handleCommandExecuteBefore(storeRoot: string, input: { command?: string }, output: CommandOutput): Promise<void> {\n try {\n if (input.command === 'memoir:status') {\n pushText(output, await statusJson(storeRoot));\n }\n if (input.command === 'memoir:ui') {\n pushText(output, await launchUi(storeRoot));\n }\n if (input.command === 'memoir:unmerged') {\n pushText(output, await unmergedBranchesText(storeRoot));\n }\n } catch (error) {\n pushText(output, `Memoir command failed: ${errorMessage(error)}`);\n }\n}\n", "import { type MemoirResult, deriveStorePath, ensureStore, getCurrentBranch, readMemoirValue, runMemoir } from './store.js';\nimport { debugLog } from './debug.js';\nimport { errorMessage } from './utils.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Test-only override for CLI functions used by flushCapture. When null (production),\n * the real implementations from ./store.js are used. */\nexport type MemoirCliOverrides = {\n runMemoir?: (args: string[], options?: { cwd?: string }) => Promise<MemoirResult>;\n readMemoirValue?: (store: string, key: string, namespace?: string) => Promise<string>;\n getCurrentBranch?: (store: string) => Promise<string>;\n};\n\nlet _cliOverrides: MemoirCliOverrides | null = null;\n\n/** Set test-only CLI overrides for flushCapture. Pass null to restore defaults. */\nexport function setCliOverrides(overrides: MemoirCliOverrides | null): void {\n _cliOverrides = overrides;\n}\n\nexport const EDIT_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'apply_patch', 'ApplyPatch', 'MultiFileEdit']);\n\nexport interface EditRecord {\n tool: string;\n filePath: string;\n snippet: string;\n timestamp: number;\n}\n\nexport interface ToolMetrics {\n calls: number;\n errors: number;\n}\n\ninterface MetricsEntry {\n timestamp: number;\n summary: string;\n files: string[];\n}\n\n/** Evict the oldest entry from a Map when it exceeds maxSize. */\nfunction evictOldest<K, V>(map: Map<K, V>, maxSize: number): void {\n if (map.size <= maxSize) return;\n const firstKey = map.keys().next().value;\n if (firstKey !== undefined) map.delete(firstKey);\n}\n\n/**\n * Maximum entries retained in metrics.code.<branch>.\n * Mirrors MEMOIR_METRICS_CODE_MAX from plugins/claude-code/hooks/stop.sh.\n */\nexport const METRICS_CODE_MAX = 1000;\n\n// ---------------------------------------------------------------------------\n// Per-session state\n// ---------------------------------------------------------------------------\n\nconst pendingEditsBySession = new Map<string, EditRecord[]>();\nconst toolMetricsBySession = new Map<string, Map<string, ToolMetrics>>();\nconst cachedBranchBySession = new Map<string, string>();\n\nexport function getCachedBranch(sessionID: string): string {\n return cachedBranchBySession.get(sessionID) ?? 'unknown';\n}\n\nexport function setCachedBranch(sessionID: string, branch: string): void {\n cachedBranchBySession.set(sessionID, branch);\n evictOldest(cachedBranchBySession, 200);\n}\n\n/** Record an edit for a specific session. */\nexport function recordEdit(sessionID: string, edit: EditRecord): void {\n let edits = pendingEditsBySession.get(sessionID);\n if (!edits) { edits = []; pendingEditsBySession.set(sessionID, edits); evictOldest(pendingEditsBySession, 500); }\n edits.push(edit);\n}\n\n/** Accumulate tool metrics for a specific session (adds to previous values). */\nexport function recordToolMetrics(sessionID: string, tool: string, metrics: ToolMetrics): void {\n let sessionMetrics = toolMetricsBySession.get(sessionID);\n if (!sessionMetrics) { sessionMetrics = new Map(); toolMetricsBySession.set(sessionID, sessionMetrics); evictOldest(toolMetricsBySession, 500); }\n const prev = sessionMetrics.get(tool) ?? { calls: 0, errors: 0 };\n sessionMetrics.set(tool, {\n calls: prev.calls + metrics.calls,\n errors: prev.errors + metrics.errors,\n });\n}\n\n/** Peek at a session's pending edits (test helper, read-only). */\nexport function getPendingEdits(sessionID: string): readonly EditRecord[] {\n return pendingEditsBySession.get(sessionID) ?? [];\n}\n\n/** Peek at a session's tool metrics (test helper, read-only). */\nexport function getToolMetrics(sessionID: string): ReadonlyMap<string, ToolMetrics> {\n return toolMetricsBySession.get(sessionID) ?? new Map();\n}\n\n/** Clear all state for a session (test helper). */\nexport function clearSession(sessionID: string): void {\n pendingEditsBySession.delete(sessionID);\n toolMetricsBySession.delete(sessionID);\n cachedBranchBySession.delete(sessionID);\n}\n\n// ---------------------------------------------------------------------------\n// parse / serialize\n// ---------------------------------------------------------------------------\n\nexport function parseTurnMetrics(text: string): Map<string, ToolMetrics> {\n const result = new Map<string, ToolMetrics>();\n if (!text) return result;\n const parts = text.split('|');\n for (const part of parts) {\n const trimmed = part.trim();\n if (!trimmed) continue;\n const [tool, callsStr, errorsStr] = trimmed.split(':');\n if (!tool) continue;\n result.set(tool, {\n calls: parseInt(callsStr, 10) || 0,\n errors: parseInt(errorsStr, 10) || 0,\n });\n }\n return result;\n}\n\nexport function serializeTurnMetrics(map: Map<string, ToolMetrics>): string {\n return [...map.entries()]\n .map(([tool, m]) => `${tool}:${m.calls}:${m.errors}`)\n .join(' | ');\n}\n\n// ---------------------------------------------------------------------------\n// Per-store mutex\n// ---------------------------------------------------------------------------\n\nconst flushQueues = new Map<string, Promise<void>>();\n\nasync function acquireFlushLock(store: string): Promise<() => void> {\n const prev = flushQueues.get(store) ?? Promise.resolve();\n let release: () => void;\n const next = new Promise<void>(resolve => { release = resolve; });\n flushQueues.set(store, next);\n await prev;\n return () => {\n release();\n if (flushQueues.get(store) === next) flushQueues.delete(store);\n };\n}\n\n// ---------------------------------------------------------------------------\n// ---------------------------------------------------------------------------\n// flushCapture \u2014 flushes one session, or all if no sessionID\n// ---------------------------------------------------------------------------\n\nexport async function flushCapture(store?: string, branch?: string, sessionID?: string): Promise<void> {\n // Determine which sessions to flush\n const targets: string[] = sessionID\n ? [sessionID]\n : [...pendingEditsBySession.keys()]; // dispose: flush all sessions\n if (targets.length === 0) return;\n\n try {\n store = store ?? deriveStorePath();\n if (!store) return;\n await ensureStore(store);\n\n // Acquire store-level mutex before touching per-session state.\n // Prevents C1 (cross-session interleave) and C2-RACE (data arriving\n // between snapshot and drain).\n const releaseLock = await acquireFlushLock(store);\n try {\n // Atomic swap: grab current data and replace with empty containers.\n // Any recordEdit/recordToolMetrics call during the subsequent I/O\n // goes into the fresh empty containers and is safe.\n interface SessionData { edits: EditRecord[]; metrics: Map<string, ToolMetrics>; sessionBranch: string; }\n const perSession: SessionData[] = [];\n const swappedSids = new Map<string, number>(); // sid \u2192 index in perSession for rollback\n for (const sid of targets) {\n const idx = perSession.length;\n const edits = pendingEditsBySession.get(sid) ?? [];\n pendingEditsBySession.set(sid, []);\n const metrics = toolMetricsBySession.get(sid) ?? new Map();\n toolMetricsBySession.set(sid, new Map());\n const sb = branch ?? cachedBranchBySession.get(sid);\n perSession.push({ edits, metrics, sessionBranch: sb ?? 'unknown' });\n swappedSids.set(sid, idx);\n }\n\n // Skip if nothing to flush\n if (!perSession.some(p => p.edits.length > 0 || p.metrics.size > 0)) return;\n\n // Wrap I/O in inner try \u2014 if it fails, restore swapped data before\n // propagating the error so the next flush retries it.\n try {\n // Resolve 'unknown' branches (read-only I/O, safe inside lock \u2014\n // the lock serializes per-store CLI access)\n for (const p of perSession) {\n if (p.sessionBranch === 'unknown' || !p.sessionBranch) {\n p.sessionBranch = await (_cliOverrides?.getCurrentBranch ?? getCurrentBranch)(store);\n }\n }\n\n // For each session, read-merge-write sequentially.\n for (const p of perSession) {\n const { edits, metrics, sessionBranch: branchKey } = p;\n if (edits.length === 0 && metrics.size === 0) continue;\n\n const [prevCodeRaw, prevTurnRaw] = await Promise.all([\n edits.length > 0 ? (_cliOverrides?.readMemoirValue ?? readMemoirValue)(store, `metrics.code.${branchKey}`) : Promise.resolve(''),\n metrics.size > 0 ? (_cliOverrides?.readMemoirValue ?? readMemoirValue)(store, `metrics.turn.${branchKey}`) : Promise.resolve(''),\n ]);\n\n let codeWrite: Promise<MemoirResult> | undefined;\n if (edits.length > 0) {\n const files = [...new Set(edits.map(e => e.filePath))];\n const entry = {\n timestamp: Date.now() / 1000,\n summary: `Changed ${edits.length} block(s) across ${files.length} file(s): ${files.join(', ')}`,\n files,\n };\n\n let acc: { schema_version: number; entries: MetricsEntry[] } = {\n schema_version: 2, entries: [],\n };\n if (prevCodeRaw) {\n try {\n const parsed = JSON.parse(prevCodeRaw);\n if (parsed?.entries && Array.isArray(parsed.entries)) {\n acc = parsed;\n if (acc.schema_version < 2) acc.schema_version = 2;\n }\n } catch (e) {\n debugLog('flushCapture: failed to parse existing code metrics, starting fresh:', errorMessage(e));\n }\n }\n acc.entries.push(entry);\n if (acc.entries.length > METRICS_CODE_MAX) acc.entries = acc.entries.slice(-METRICS_CODE_MAX);\n codeWrite = (_cliOverrides?.runMemoir ?? runMemoir)(['-s', store, 'remember', '--replace', JSON.stringify(acc), '-p', `metrics.code.${branchKey}`], { cwd: store });\n }\n\n let turnWrite: Promise<MemoirResult> | undefined;\n if (metrics.size > 0) {\n const existing = parseTurnMetrics(prevTurnRaw);\n for (const [tool, current] of metrics) {\n const prev = existing.get(tool) ?? { calls: 0, errors: 0 };\n existing.set(tool, { calls: prev.calls + current.calls, errors: prev.errors + current.errors });\n }\n turnWrite = (_cliOverrides?.runMemoir ?? runMemoir)(['-s', store, 'remember', '--replace', serializeTurnMetrics(existing), '-p', `metrics.turn.${branchKey}`], { cwd: store });\n }\n\n // Check writes for this session's pair\n const writes: Promise<MemoirResult>[] = [];\n if (codeWrite) writes.push(codeWrite);\n if (turnWrite) writes.push(turnWrite);\n const results = await Promise.all(writes);\n for (const result of results) {\n if (!result.ok) {\n throw new Error(`Memoir write failed: ${result.error}`);\n }\n }\n }\n } catch (e) {\n // Restore swapped data into pending maps so the next flush retries it.\n // Entries accumulated during I/O by concurrent recordEdit/recordToolMetrics\n // are preserved: we prepend the original entries, so ordering is maintained.\n for (const [sid, idx] of swappedSids) {\n const p = perSession[idx];\n const currentEdits = pendingEditsBySession.get(sid) ?? [];\n pendingEditsBySession.set(sid, [...p.edits, ...currentEdits]);\n\n const merged = toolMetricsBySession.get(sid) ?? new Map();\n for (const [tool, m] of p.metrics) {\n const prev = merged.get(tool) ?? { calls: 0, errors: 0 };\n merged.set(tool, { calls: prev.calls + m.calls, errors: prev.errors + m.errors });\n }\n toolMetricsBySession.set(sid, merged);\n }\n // Re-throw so the outer catch logs it\n throw e;\n }\n } finally {\n releaseLock();\n }\n } catch (e) {\n debugLog('flushCapture: failed:', errorMessage(e));\n }\n}\n", "import { EDIT_TOOLS, flushCapture, getCachedBranch, recordEdit, recordToolMetrics, setCachedBranch } from './capture.js';\nimport { autoMatchMemoirBranch } from './store.js';\nimport { pendingRecall, shouldTriggerRecall } from './recall-gate.js';\nimport { errorMessage } from './utils.js';\nimport { debugLog } from './debug.js';\n\nexport async function handleShellEnv(storeRoot: string, _input: unknown, output: { env: Record<string, string> }): Promise<void> {\n try {\n output.env.MEMOIR_STORE = storeRoot;\n } catch (e) {\n debugLog('shell.env: failed:', errorMessage(e));\n }\n}\n\nexport async function handleToolExecuteAfter(input: unknown, output: unknown): Promise<void> {\n try {\n const typedInput = input as { tool?: string; sessionID?: string; callID?: string; args?: Record<string, unknown> } | undefined;\n const typedOutput = output as { metadata?: { error?: string }; output?: string } | undefined;\n // Per-session state key\n const sid = typedInput?.sessionID ?? 'default';\n\n // Accumulate per-tool metrics (cf. collect-metrics.sh).\n // Skipped when MEMOIR_NO_CAPTURE or MEMOIR_NO_METRICS is set.\n if (process.env.MEMOIR_NO_CAPTURE !== '1' && process.env.MEMOIR_NO_METRICS !== '1') {\n const calls = 1;\n const errors = (typedOutput?.metadata?.error || typedOutput?.output?.startsWith('Error:')) ? 1 : 0;\n recordToolMetrics(sid, typedInput?.tool ?? '', { calls, errors });\n }\n\n // Track file edits (cf. collect-edits.sh).\n // Skipped when MEMOIR_NO_CAPTURE or MEMOIR_NO_CODE_SUMMARY is set.\n if (process.env.MEMOIR_NO_CAPTURE !== '1' && process.env.MEMOIR_NO_CODE_SUMMARY !== '1' && EDIT_TOOLS.has(typedInput?.tool ?? '')) {\n const args = typedInput?.args ?? {};\n const filePath =\n typeof args.filePath === 'string' ? args.filePath\n : typeof args.path === 'string' ? args.path\n : '';\n if (filePath) {\n recordEdit(sid, { tool: typedInput?.tool ?? '', filePath, snippet: '', timestamp: Date.now() });\n }\n }\n } catch (e) {\n debugLog('tool.execute.after: failed:', errorMessage(e));\n }\n}\n\nexport async function handleChatMessage(storeRoot: string, input: unknown, output: unknown): Promise<void> {\n try {\n const typedInput = input as { sessionID?: string } | undefined;\n const typedOutput = output as { parts?: Array<{ type: string; text: string }> } | undefined;\n const sid = typedInput?.sessionID ?? 'default';\n\n // C4: run recall gate BEFORE any await \u2014 otherwise system.transform\n // could fire before pendingRecall is set.\n const text = (typedOutput?.parts ?? [])\n .filter((p): p is { type: 'text'; text: string } => p.type === 'text')\n .map(p => p.text)\n .join(' ');\n if (shouldTriggerRecall(text)) {\n pendingRecall.add(sid);\n }\n\n // Snapshot previous branch BEFORE switching \u2014 edits from the last turn\n // belong to the OLD branch. (C2 fix: prevents misattribution when the\n // user switches git branch between turns.)\n const _cachedBranch = getCachedBranch(sid);\n const prevBranch = _cachedBranch === 'unknown' ? undefined : _cachedBranch;\n setCachedBranch(sid, await autoMatchMemoirBranch(storeRoot));\n\n // Skip capture flush when MEMOIR_NO_CAPTURE is set.\n // Flush under the PREVIOUS branch (the one edits were made on).\n if (process.env.MEMOIR_NO_CAPTURE !== '1') {\n await flushCapture(storeRoot, prevBranch, sid);\n }\n } catch (e) {\n debugLog('chat.message: failed:', errorMessage(e));\n }\n}\n\nexport async function flushAllCapture(storeRoot: string): Promise<void> {\n await flushCapture(storeRoot);\n}\n", "import { runMemoir } from './store.js';\nimport { pendingRecall } from './recall-gate.js';\nimport { errorMessage, tryPrettyJson } from './utils.js';\nimport { debugLog } from './debug.js';\n\n/** Cached session init context (taxonomy overview). Fetched once, injected per-session. */\nlet initContext: string | null = null;\nlet initContextFetched = false;\n/**\n * Tracks sessions that have received initial context injection.\n * Uses a Map with timestamp so stale entries can be evicted.\n * Cleaned on dispose. Sessions older than 1 hour are considered stale.\n */\nconst SESSION_CONTEXT_TTL_MS = 60 * 60 * 1000;\nconst sessionsWithContext = new Map<string, number>();\n\n/** Check if a session has received context (and prune stale entries opportunistically). */\nfunction hasSessionContext(sessionID: string): boolean {\n const ts = sessionsWithContext.get(sessionID);\n if (ts === undefined) return false;\n if (Date.now() - ts > SESSION_CONTEXT_TTL_MS) {\n sessionsWithContext.delete(sessionID);\n return false;\n }\n return true;\n}\n\n/** Mark a session as having received context. */\nfunction markSessionContext(sessionID: string): void {\n sessionsWithContext.set(sessionID, Date.now());\n}\n\n/** Prune all stale session entries. */\nfunction pruneStaleSessions(): void {\n const now = Date.now();\n for (const [id, ts] of sessionsWithContext) {\n if (now - ts > SESSION_CONTEXT_TTL_MS) sessionsWithContext.delete(id);\n }\n}\n\nexport async function handleEvent(storeRoot: string, input: { event: { type: string } }): Promise<void> {\n if (input.event.type === 'session.created' && !initContextFetched) {\n initContextFetched = true;\n (async () => {\n try {\n const taxonomyResult = await runMemoir(\n ['--json', '-s', storeRoot, 'summarize', '--depth', '3', '-n', 'default'],\n { cwd: storeRoot }\n );\n if (!taxonomyResult.ok) {\n throw new Error(taxonomyResult.error);\n }\n const pretty = tryPrettyJson(taxonomyResult.stdout);\n initContext = `[memoir] Available taxonomy paths:\\n${pretty}`;\n } catch (e: unknown) {\n debugLog('event: taxonomy fetch failed, will retry on next session:', errorMessage(e));\n // Allow a later session to retry \u2014 the store may not have been ready yet.\n initContextFetched = false;\n }\n })().catch(() => { initContextFetched = false; });\n }\n}\n\nexport async function handleSystemTransform(input: unknown, output: unknown): Promise<void> {\n try {\n const typedInput = input as { sessionID?: string } | undefined;\n const typedOutput = output as { system?: string[] } | undefined;\n\n // Inject initial context on the first LLM call of each session\n if (typedInput?.sessionID && !hasSessionContext(typedInput.sessionID)) {\n markSessionContext(typedInput.sessionID);\n if (initContext) {\n typedOutput?.system?.unshift(initContext);\n }\n }\n\n // Inject recall instruction (one-shot per trigger)\n if (typedInput?.sessionID && pendingRecall.has(typedInput.sessionID)) {\n pendingRecall.delete(typedInput.sessionID);\n typedOutput?.system?.push(\n '\\n[memoir] The user may have relevant context in Memoir. Run memoir_recall to list available memories across default and onboard namespaces, then memoir_get with the matching namespace to fetch exact values.'\n );\n }\n } catch (e) {\n debugLog('system.transform: failed:', errorMessage(e));\n }\n}\n\nexport function clearSessionContext(): void {\n pruneStaleSessions();\n sessionsWithContext.clear();\n}\n", "import { type Plugin, type PluginModule } from '@opencode-ai/plugin';\nimport { deriveStorePath, setPluginStoreOverride } from './store.js';\nimport { memoirTools } from './tools.js';\nimport { registerCommands, handleCommandExecuteBefore } from './commands.js';\nimport { handleShellEnv, handleToolExecuteAfter, handleChatMessage, flushAllCapture } from './capture-hooks.js';\nimport { handleEvent, handleSystemTransform, clearSessionContext } from './session-context.js';\n\nconst MemoirOpenCode: Plugin = async (_input, rawOptions) => {\n const opts = (rawOptions ?? {}) as { store?: string };\n if (opts.store) setPluginStoreOverride(opts.store);\n\n // Resolve store path once at init so shell.env doesn't call execFileSync\n // on every shell command (mirrors Claude Code's MEMOIR_STORE_PATH caching).\n const storeRoot = deriveStorePath();\n\n return ({\n name: 'memoir',\n tool: { ...memoirTools },\n config: async (opencodeConfig: Record<string, unknown>) => {\n registerCommands(opencodeConfig as Parameters<typeof registerCommands>[0]);\n },\n 'command.execute.before': async (input: { command?: string }, output: { parts: unknown[] }) => {\n await handleCommandExecuteBefore(storeRoot, input, output);\n },\n\n /**\n * Inject MEMOIR_STORE into every shell command's environment so any memoir\n * invocation automatically targets the right store without manual -s flags.\n * Uses the cached value resolved at init time (avoiding execFileSync overhead\n * on every shell command).\n */\n 'shell.env': async (_input, output) => {\n await handleShellEnv(storeRoot, _input, output as { env: Record<string, string> });\n },\n\n /**\n * Observe every tool execution for metrics and code-change tracking.\n * Mirrors the observation phase of Claude Code's Stop hook.\n * Never modifies the tool output.\n */\n 'tool.execute.after': async (input, output) => {\n await handleToolExecuteAfter(input, output);\n },\n\n /**\n * Fires on every incoming user message.\n *\n * 1. Auto-match memoir branch to current git branch\n * (cf. UserPromptSubmit's auto_match_memoir_branch in Claude Code plugin).\n * 2. Flush pending edits from the previous assistant turn\n * (cf. Stop hook code change audit, run after each turn).\n * 3. Run the recall gate (cf. UserPromptSubmit).\n *\n * Steps 1\u20132 are skipped when MEMOIR_NO_CAPTURE=1.\n */\n 'chat.message': async (input, output) => {\n await handleChatMessage(storeRoot, input, output);\n },\n\n /**\n * Fires on SDK events. On session.created, kick off a background fetch of\n * the taxonomy overview so it's ready before the first LLM call.\n * Mirrors Claude Code's SessionStart context injection.\n */\n event: async (input: { event: { type: string } }) => {\n await handleEvent(storeRoot, input);\n },\n\n /**\n * Fires before every LLM call.\n *\n * 1. Injects the memoir taxonomy context for this session (once per session).\n * 2. If a recall is pending for this session, injects a brief instruction\n * telling the model to use memoir tools (one-shot per trigger).\n */\n 'experimental.chat.system.transform': async (input, output) => {\n await handleSystemTransform(input, output);\n },\n\n /**\n * Fires when the plugin is shut down. Flushes any remaining code changes\n * and metrics (cf. SessionEnd heartbeat cleanup + final metrics flush).\n */\n dispose: async () => {\n try {\n // No sessionID \u2192 flushCapture flushes ALL sessions\n await flushAllCapture(storeRoot);\n clearSessionContext();\n } catch (e: unknown) {\n // Errors are already logged inside flushAllCapture\n }\n },\n});\n};\n\n/**\n * V1 plugin module format: OpenCode's loader recognizes `{ id, server }` and\n * ignores any other module exports. The legacy format (default-exported\n * function) made the loader treat EVERY export as a plugin and fail on\n * non-function exports (\"Plugin export is not a function\").\n */\nconst plugin: PluginModule = {\n id: 'opencode-memoir',\n server: MemoirOpenCode,\n};\n\nexport default plugin;\n"],
5
- "mappings": ";AAAA,SAAS,UAAU,oBAAoB;AACvC,SAAS,QAAQ,OAAO,UAAU;AAClC,SAAS,MAAM,eAAe;AAC9B,SAAS,SAAS,cAAc;AAChC,SAAS,iBAAiB;;;ACAnB,SAAS,YAAY,MAAuB;AACjD,MAAI,QAAQ,IAAI,iBAAiB,IAAK;AACtC,QAAM,MAAK,oBAAI,KAAK,GAAE,YAAY;AAClC,UAAQ,OAAO,MAAM,WAAW,EAAE,KAAK,KAAK,IAAI,OAAK,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,CAAI;AAC/E;;;ACDO,SAAS,YAAY,MAA+C;AACzE,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,SAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,OAAO,OAAO,IAAI,CAAC,IAAI;AAC3D;AAGO,SAAS,cAAc,MAAsB;AAClD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK,MAAM,IAAI,GAAG,MAAM,CAAC;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,IAAM,sBAAsB;AAE5B,IAAM,iBAAiB,IAAI;AAAA,EAChC;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF,EAAE,KAAK,GAAG;AACZ;AAGO,SAAS,aAAa,GAAoB;AAC/C,SAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAClD;;;AFpCA,IAAM,gBAAgB,UAAU,QAAQ;AAEjC,IAAM,iBAAiB;AAOvB,IAAM,gBAAgB;AAGtB,IAAM,iBAAiB;AAGvB,IAAM,oBAAoB;AAG1B,IAAM,oBAAoB,OAAQ;AASzC,IAAM,mBAAmB;AAMlB,IAAM,cAAc,oBAAI,IAAY;AAQpC,SAAS,oBAAoB,KAAqB;AACvD,MAAI,YAAY,IAAI,GAAG,EAAG,QAAO;AACjC,MAAI;AAEF,UAAM,SAAS,aAAa,OAAO,CAAC,aAAa,WAAW,GAAG,EAAE,KAAK,UAAU,QAAQ,SAAS,eAAe,CAAC,EAAE,KAAK;AACxH,UAAM,eAAe,aAAa,OAAO,CAAC,aAAa,kBAAkB,GAAG,EAAE,KAAK,UAAU,QAAQ,SAAS,eAAe,CAAC,EAAE,KAAK;AAGrI,UAAM,cAAc,CAAC,SAAyB;AAC5C,UAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AACjC,aAAO,KAAK,KAAK,IAAI;AAAA,IACvB;AACA,UAAM,YAAY,YAAY,MAAM;AACpC,UAAM,kBAAkB,YAAY,YAAY;AAEhD,QAAI,cAAc,iBAAiB;AAEjC,aAAO,aAAa,OAAO,CAAC,aAAa,iBAAiB,GAAG,EAAE,KAAK,UAAU,QAAQ,SAAS,eAAe,CAAC,EAAE,KAAK;AAAA,IACxH;AAGA,UAAM,eAAe,aAAa,OAAO,CAAC,YAAY,QAAQ,aAAa,GAAG,EAAE,KAAK,UAAU,QAAQ,SAAS,eAAe,CAAC;AAChI,UAAM,YAAY,aAAa,MAAM,IAAI,EAAE,CAAC;AAC5C,QAAI,UAAU,WAAW,WAAW,GAAG;AACrC,YAAM,eAAe,UAAU,UAAU,YAAY,MAAM,EAAE,KAAK;AAClE,UAAI,gBAAgB,iBAAiB,UAAU;AAC7C,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,aAAa,OAAO,CAAC,aAAa,iBAAiB,GAAG,EAAE,KAAK,UAAU,QAAQ,SAAS,eAAe,CAAC,EAAE,KAAK;AAAA,EACxH,SAAS,GAAG;AACV,aAAS,qDAAqD,aAAa,CAAC,CAAC;AAC7E,gBAAY,IAAI,GAAG;AACnB,QAAI,YAAY,OAAO,iBAAkB,aAAY,MAAM;AAC3D,WAAO;AAAA,EACT;AACF;AAaO,SAAS,gBAAgB,MAAc,QAAQ,IAAI,GAAW;AACnE,MAAI,oBAAqB,QAAO;AAChC,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,WAAY,QAAO;AAGvB,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,oBAAoB,GAAG;AACvC,QAAI,SAAS;AACX,mBAAa;AAAA,IACf,OAAO;AAEL,mBAAa,QAAQ,GAAG;AAAA,IAC1B;AAAA,EACF,SAAS,GAAG;AACV,aAAS,sCAAsC,aAAa,CAAC,CAAC;AAE9D,iBAAa;AAAA,EACf;AAIA,QAAM,OAAO,WAAW,QAAQ,SAAS,GAAG;AAC5C,SAAO,KAAK,QAAQ,GAAG,WAAW,IAAI;AACxC;AAGO,IAAI;AAKJ,SAAS,uBAAuB,OAAiC;AACtE,wBAAsB;AACxB;AAGA,IAAM,qBAAqB;AAGpB,IAAM,gBAAgB,oBAAI,IAAY;AAM7C,IAAM,iBAAiB,oBAAI,IAA2B;AAEtD,eAAsB,YAAY,OAA8B;AAC9D,MAAI,cAAc,IAAI,KAAK,EAAG;AAG9B,QAAM,WAAW,eAAe,IAAI,KAAK;AACzC,MAAI,UAAU;AACZ,UAAM;AACN;AAAA,EACF;AAEA,MAAI;AACF,UAAM,OAAO,KAAK,OAAO,MAAM,CAAC;AAChC,kBAAc,IAAI,KAAK;AACvB,QAAI,cAAc,OAAO,mBAAoB,eAAc,MAAM;AACjE;AAAA,EACF,QAAQ;AAEN,UAAM,MAAM,KAAK,OAAO,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC,EAAE,MAAM,CAAC,MAAe,SAAS,8BAA8B,aAAa,CAAC,CAAC,CAAC;AAAA,EACnI;AAGA,QAAM,mBAAmB,YAAY;AACnC,UAAM,SAAS,KAAK,OAAO,GAAG,kBAAkB,KAAK,IAAI,CAAC,EAAE;AAC5D,QAAI;AACF,YAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,YAAM,cAAc,OAAO,CAAC,QAAQ,MAAM,MAAM,GAAG,EAAE,SAAS,IAAK,CAAC;AACpE,YAAM,SAAS,MAAM,UAAU,CAAC,OAAO,OAAO,oBAAoB,GAAG,EAAE,KAAK,OAAO,CAAC;AACpF,UAAI,CAAC,OAAO,IAAI;AACd,cAAM,IAAI,MAAM,OAAO,KAAK;AAAA,MAC9B;AACA,oBAAc,IAAI,KAAK;AACvB,UAAI,cAAc,OAAO,mBAAoB,eAAc,MAAM;AAAA,IACnE,SAAS,GAAY;AACnB,eAAS,iCAAiC,aAAa,CAAC,CAAC;AACzD,YAAM;AAAA,IACR,UAAE;AACA,SAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,SAAS,uBAAuB,aAAa,CAAC,CAAC,CAAC;AAAA,IAC5G;AAAA,EACF,GAAG;AACH,iBAAe,IAAI,OAAO,eAAe;AACzC,MAAI;AACF,UAAM;AAAA,EACR,UAAE;AACA,mBAAe,OAAO,KAAK;AAAA,EAC7B;AACF;AAMO,SAAS,gBAAmB,UAAe,QAAgB;AAChE,QAAM,MAAM,SAAS,QAAQ,MAAM;AACnC,MAAI,OAAO,EAAG,QAAO,CAAC,GAAG,QAAQ;AACjC,QAAM,OAAO,CAAC,GAAG,QAAQ;AACzB,OAAK,OAAO,KAAK,CAAC;AAClB,OAAK,QAAQ,MAAM;AACnB,SAAO;AACT;AAGO,IAAI,iBAAgC;AAE3C,eAAsB,UAAU,MAAgB,UAA4B,CAAC,GAA0B;AACrG,MAAI,QAAQ,iBAAiB,IAAI;AAGjC,MAAI,gBAAgB;AAClB,UAAM,SAAS,MAAM,KAAK,OAAK,EAAE,UAAU,cAAc;AACzD,QAAI,OAAQ,SAAQ,gBAAgB,OAAO,MAAM;AAAA,EACnD;AAEA,MAAI,YAAY;AAChB,MAAI;AACJ,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM,cAAc,KAAK,SAAS,KAAK,MAAM;AAAA,QAC9D,KAAK,QAAQ,OAAO,QAAQ,IAAI;AAAA,QAChC,KAAK,QAAQ;AAAA,QACb,WAAW;AAAA,QACX,SAAS;AAAA;AAAA,MACX,CAAC;AACD,uBAAiB,KAAK;AACtB,aAAO,EAAE,IAAI,MAAM,QAAQ,OAAO,KAAK,EAAE;AAAA,IAC3C,SAAS,GAAG;AACV,kBAAY,aAAa,CAAC;AAC1B,qBAAe,KAAK;AACpB,eAAS,uBAAuB,KAAK,OAAO,WAAW,SAAS;AAEhE,UAAI,mBAAmB,KAAK,MAAO,kBAAiB;AAAA,IACtD;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,OAAO,OAAO,0BAA0B,SAAS,IAAI,UAAU,aAAa;AAC3F;AAEO,SAAS,iBAAiB,MAA6B;AAC5D,SAAO;AAAA,IACL,EAAE,SAAS,UAAU,MAAM,OAAO,SAAS;AAAA,IAC3C,EAAE,SAAS,OAAO,MAAM,CAAC,UAAU,GAAG,cAAc,KAAK,aAAa,IAAI,UAAU,GAAG,IAAI,GAAG,OAAO,MAAM;AAAA,IAC3G,EAAE,SAAS,MAAM,MAAM,CAAC,QAAQ,OAAO,UAAU,GAAG,cAAc,KAAK,aAAa,IAAI,UAAU,GAAG,IAAI,GAAG,OAAO,cAAc;AAAA,EACnI;AACF;AAGA,eAAsB,iBAAiB,OAAgC;AACrE,MAAI;AACF,UAAM,QAAQ,oBAAoB,eAAe;AACjD,UAAM,SAAS,MAAM,MAAM,CAAC,UAAU,MAAM,OAAO,QAAQ,GAAG,EAAE,KAAK,MAAM,CAAC;AAC5E,QAAI,CAAC,OAAO,GAAI,QAAO;AACvB,UAAM,OAAO,KAAK,MAAM,OAAO,MAAM;AACrC,WAAO,KAAK,UAAU;AAAA,EACxB,SAAS,GAAG;AACV,aAAS,6BAA6B,aAAa,CAAC,CAAC;AACrD,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,gBAAiC;AACrD,MAAI;AACF,UAAM,SAAS,oBAAoB,mBAAmB;AACtD,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,OAAO,CAAC,UAAU,gBAAgB,GAAG,EAAE,UAAU,QAAQ,SAAS,eAAe,CAAC;AAClH,WAAO,OAAO,KAAK;AAAA,EACrB,SAAS,GAAG;AACV,aAAS,0BAA0B,aAAa,CAAC,CAAC;AAClD,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,qBAAqB,OAAe,MAAgC;AACxF,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,UAAM,QAAQ,oBAAoB,eAAe;AACjD,UAAM,SAAS,MAAM,MAAM,CAAC,UAAU,MAAM,OAAO,QAAQ,GAAG,EAAE,KAAK,MAAM,CAAC;AAC5E,QAAI,CAAC,OAAO,GAAI,QAAO;AACvB,UAAM,OAAO,KAAK,MAAM,OAAO,MAAM;AACrC,UAAM,WAAqB,MAAM,YAAY,CAAC;AAC9C,WAAO,SAAS,SAAS,IAAI;AAAA,EAC/B,SAAS,GAAG;AACV,aAAS,iCAAiC,aAAa,CAAC,CAAC;AACzD,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,sBAAsB,OAAgC;AAC1E,QAAM,QAAQ,oBAAoB,eAAe;AACjD,QAAM,aAAa,MAAM,cAAc;AACvC,MAAI,CAAC,YAAY;AACf,WAAO,iBAAiB,KAAK;AAAA,EAC/B;AAEA,QAAM,UAAU,MAAM,iBAAiB,KAAK;AAC5C,MAAI,YAAY,WAAY,QAAO;AAGnC,MAAI,CAAE,MAAM,qBAAqB,OAAO,UAAU,GAAI;AACpD,UAAMA,UAAS,MAAM,MAAM,CAAC,MAAM,OAAO,UAAU,YAAY,UAAU,MAAM,GAAG,EAAE,KAAK,MAAM,CAAC;AAChG,QAAI,CAACA,QAAO,IAAI;AACd,eAAS,gDAAgDA,QAAO,KAAK;AACrE,aAAO,iBAAiB,KAAK;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,MAAM,CAAC,MAAM,OAAO,YAAY,UAAU,GAAG,EAAE,KAAK,MAAM,CAAC;AAChF,MAAI,CAAC,OAAO,IAAI;AACd,aAAS,2CAA2C,OAAO,KAAK;AAChE,WAAO,iBAAiB,KAAK;AAAA,EAC/B;AACA,SAAO;AACT;AAMA,eAAsB,gBAAgB,OAAe,KAAa,YAAoB,WAA4B;AAChH,MAAI;AACF,UAAM,QAAQ,oBAAoB,eAAe;AACjD,UAAM,SAAS,MAAM,MAAM,CAAC,UAAU,MAAM,OAAO,OAAO,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,CAAC;AAC/F,QAAI,CAAC,OAAO,GAAI,QAAO;AACvB,UAAM,SAAS,KAAK,MAAM,OAAO,MAAM;AACvC,UAAM,QAAQ,QAAQ,SAAS,CAAC;AAChC,UAAM,QAAQ,MAAM,CAAC,GAAG,OAAO;AAC/B,WAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C,SAAS,GAAG;AACV,aAAS,4BAA4B,aAAa,CAAC,CAAC;AACpD,WAAO;AAAA,EACT;AACF;AAOA,IAAI,sBAA0C,CAAC;;;AGvW/C,SAAS,YAAY;;;ACArB,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,SAAAC,QAAO,UAAU,MAAAC,KAAI,iBAAiB;AAC/C,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAMxB,IAAM,qBAAqB;AAG3B,IAAM,iBAAiB;AAGvB,eAAsB,mBAAmB,OAAuC;AAC9E,MAAI;AACF,UAAM,YAAY,KAAK;AACvB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,aAAa,KAAK;AAAA,EAC3B;AACF;AAEA,eAAsB,WAAW,OAAgC;AAC/D,QAAM,WAAW,MAAM,mBAAmB,KAAK;AAC/C,MAAI,SAAU,QAAO,0BAA0B,QAAQ;AACvD,QAAM,SAAS,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,QAAQ,GAAG,EAAE,KAAK,MAAM,CAAC;AAChF,MAAI,CAAC,OAAO,GAAI,QAAO,OAAO;AAC9B,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,OAAO,MAAM;AACrC,UAAM,SAAS,MAAM,cAAc;AACnC,SAAK,WAAW,EAAE,OAAO,kBAAkB,QAAQ,IAAI,GAAG,oBAAoB,OAAO;AACrF,WAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,EACrC,SAAS,GAAG;AACV,aAAS,qCAAqC,aAAa,CAAC,CAAC;AAC7D,WAAO,OAAO;AAAA,EAChB;AACF;AAEA,eAAsB,SAAS,OAAgC;AAC7D,QAAM,YAAY,KAAK;AACvB,QAAM,SAASC,MAAKC,SAAQ,GAAG,WAAW,YAAY;AACtD,QAAMC,OAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,QAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AACxE,QAAM,UAAUF,MAAK,QAAQ,GAAG,IAAI,OAAO;AAE3C,MAAI;AACF,UAAM,WAAW,KAAK,MAAM,MAAM,SAAS,SAAS,MAAM,CAAC;AAC3D,QAAI,UAAU,OAAO,UAAU,KAAK;AAClC,UAAI;AACF,gBAAQ,KAAK,OAAO,SAAS,GAAG,GAAG,CAAC;AACpC,eAAO,KAAK,UAAU,EAAE,GAAG,UAAU,QAAQ,KAAK,GAAG,MAAM,CAAC;AAAA,MAC9D,SAAS,GAAG;AACV,iBAAS,wCAAwC,aAAa,CAAC,CAAC;AAAA,MAElE;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,aAAS,qCAAqC,aAAa,CAAC,CAAC;AAC7D,UAAMG,IAAG,SAAS,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,CAACC,OAAM,SAAS,4CAA4C,aAAaA,EAAC,CAAC,CAAC;AAAA,EACvH;AAEA,MAAI,YAAY;AAChB,MAAI,UAAU,iBAAiB,CAAC,MAAM,KAAK,CAAC;AAE5C,MAAI,gBAAgB;AAClB,UAAM,SAAS,QAAQ,KAAK,OAAK,EAAE,UAAU,cAAc;AAC3D,QAAI,OAAQ,WAAU,gBAAgB,SAAS,MAAM;AAAA,EACvD;AACA,aAAW,QAAQ,SAAS;AAC1B,UAAM,QAAQ,MAAM,KAAK,SAAS,KAAK,MAAM;AAAA,MAC3C,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAChC,KAAK,QAAQ;AAAA,IACf,CAAC;AAED,QAAI,SAAS;AACb,UAAM,QAAQ,GAAG,QAAQ,WAAS;AAAE,gBAAU,OAAO,KAAK;AAAA,IAAG,CAAC;AAC9D,UAAM,QAAQ,GAAG,QAAQ,WAAS;AAAE,gBAAU,OAAO,KAAK;AAAA,IAAG,CAAC;AAE9D,UAAM,cAAc,IAAI,QAAuB,CAAAC,aAAW;AACxD,YAAM,KAAK,SAAS,CAAAC,WAASD,SAAQ,aAAaC,MAAK,CAAC,CAAC;AACzD,YAAM,KAAK,SAAS,MAAMD,SAAQ,IAAI,CAAC;AAAA,IACzC,CAAC;AACD,UAAM,QAAQ,MAAM;AACpB,QAAI,OAAO;AACT,kBAAY,GAAG,KAAK,KAAK,KAAK,KAAK;AACnC;AAAA,IACF;AAEA,UAAM,MAAM;AAEZ,UAAM,aAAa;AACnB,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,QAAQ,OAAO,MAAM,UAAU;AACrC,UAAI,OAAO;AACT,cAAM,MAAM,MAAM,CAAC;AACnB,cAAM,OAAO,EAAE,KAAK,MAAM,KAAK,KAAK,OAAO,SAAS,KAAK,OAAO,UAAS,oBAAI,KAAK,GAAE,YAAY,GAAG,QAAQ,MAAM;AACjH,cAAM,UAAU,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACtD,eAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,MACrC;AACA,YAAM,IAAI,QAAQ,CAAAA,aAAW,WAAWA,UAAS,cAAc,CAAC;AAAA,IAClE;AAEA,WAAO,0BAA0B,KAAK,KAAK,SAAS,MAAM,OAAO,SAAS;AAAA,EAAqC,OAAO,KAAK,CAAC,GAAG,KAAK;AAAA,EACtI;AAEA,SAAO,8BAA8B,aAAa,uBAAuB;AAC3E;AAGA,eAAsB,qBAAqB,OAAgC;AACzE,QAAM,YAAY,KAAK;AACvB,QAAM,eAAe,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,QAAQ,GAAG,EAAE,KAAK,MAAM,CAAC;AACtF,MAAI,CAAC,aAAa,IAAI;AACpB,WAAO,aAAa;AAAA,EACtB;AACA,QAAM,OAAO,KAAK,MAAM,aAAa,MAAM;AAC3C,QAAM,WAAqB,MAAM,YAAY,CAAC;AAC9C,QAAM,WAAqB,CAAC;AAC5B,aAAW,UAAU,UAAU;AAC7B,QAAI,WAAW,OAAQ;AACvB,UAAM,aAAa,MAAM,UAAU,CAAC,MAAM,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,EAAE,KAAK,MAAM,CAAC;AAClG,QAAI,CAAC,WAAW,IAAI;AAClB,eAAS,8CAA8C,WAAW,KAAK;AACvE;AAAA,IACF;AACA,QAAI,WAAW,OAAO,KAAK,GAAG;AAC5B,eAAS,KAAK,MAAM;AAAA,IACtB;AAAA,EACF;AACA,SAAO,SAAS,SAAS,IACrB;AAAA,EAAuB,SAAS,KAAK,IAAI,CAAC,KAC1C;AACN;;;ACtIO,SAAS,8BAAuC;AACrD,SAAO,QAAQ,IAAI,4BAA4B;AACjD;AAMO,IAAM,gBAAgB,oBAAI,IAAY;AAG7C,IAAM,cAAc;AAGpB,IAAM,0BAA0B;AAGhC,IAAM,0BAA0B;AAAA;AAAA,EAE9B;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAEO,SAAS,kBAAkB,MAAuB;AACvD,QAAM,UAAU,KAAK,KAAK,EAAE,YAAY;AACxC,QAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,SAAO,MAAM,UAAU,KAAK,YAAY,KAAK,OAAO;AACtD;AAaO,SAAS,oBAAoB,MAAuB;AACzD,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,wBAAwB,KAAK,OAAO,EAAG,QAAO;AAClD,MAAI,QAAQ,SAAS,GAAI,QAAO;AAChC,MAAI,kBAAkB,OAAO,EAAG,QAAO;AAEvC,MAAI,QAAQ,UAAU,MAAM,wBAAwB,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,EAAG,QAAO;AACvF,SAAO;AACT;AAEO,IAAM,4BAA4B,CAAC,WAAW,mBAAmB,kBAAkB;;;AFzC1F,IAAM,eAAe,KAAK;AAAA,EACxB,aAAa;AAAA,EACb,MAAM,CAAC;AAAA,EACP,SAAS,YAAY,WAAW,gBAAgB,CAAC;AACnD,CAAC;AAED,IAAM,iBAAiB,KAAK;AAAA,EAC1B,aAAa;AAAA,EACb,MAAM;AAAA,IACJ,SAAS,KAAK,OAAO,OAAO,EAAE,SAAS,iDAAiD;AAAA,IACxF,MAAM,KAAK,OAAO,OAAO,EAAE,SAAS,EAAE,SAAS,wDAAwD;AAAA,IACvG,WAAW,KAAK,OAAO,OAAO,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,IAC5F,SAAS,KAAK,OAAO,QAAQ,EAAE,SAAS,EAAE,SAAS,qCAAqC;AAAA,EAC1F;AAAA,EACA,SAAS,OAAO,SAA6B;AAC3C,UAAM,UAAU,KAAK,SAAS,KAAK;AACnC,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,4BAA4B,KAAK,eAAe,KAAK,OAAO,GAAG;AACjE,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,YAAY,KAAK,IAAI;AACnC,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,gBAAgB;AAC9B,UAAM,aAAa,MAAM,mBAAmB,KAAK;AACjD,QAAI,WAAY,QAAO;AAEvB,UAAM,UAAU,CAAC,UAAU,MAAM,OAAO,YAAY,OAAO;AAC3D,eAAW,KAAK,MAAO,SAAQ,KAAK,MAAM,CAAC;AAC3C,YAAQ,KAAK,MAAM,KAAK,aAAa,SAAS;AAC9C,QAAI,KAAK,QAAS,SAAQ,KAAK,WAAW;AAC1C,UAAM,iBAAiB,MAAM,UAAU,SAAS,EAAE,KAAK,MAAM,CAAC;AAC9D,QAAI,CAAC,eAAe,GAAI,QAAO,eAAe;AAC9C,WAAO,cAAc,eAAe,MAAM;AAAA,EAC5C;AACF,CAAC;AAED,IAAM,eAAe,KAAK;AAAA,EACxB,aAAa;AAAA,EACb,MAAM;AAAA,IACJ,OAAO,KAAK,OAAO,OAAO,EAAE,SAAS,EAAE,SAAS,oCAAoC;AAAA,IACpF,WAAW,KAAK,OAAO,OAAO,EAAE,SAAS,EAAE,SAAS,sFAAsF;AAAA,IAC1I,YAAY,KAAK,OAAO,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,gFAAgF;AAAA,IACxJ,gBAAgB,KAAK,OAAO,QAAQ,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,EACxG;AAAA,EACA,SAAS,OAAO,SAA2B;AACzC,UAAM,QAAQ,gBAAgB;AAC9B,UAAM,aAAa,MAAM,mBAAmB,KAAK;AACjD,QAAI,WAAY,QAAO;AAEvB,UAAM,aAAa,KAAK,YACpB,CAAC,KAAK,SAAS,IACd,KAAK,cAAc,KAAK,WAAW,SAAS,IAAI,KAAK,aAAa;AACvE,UAAM,WAAqB,CAAC;AAC5B,eAAW,aAAa,YAAY;AAClC,YAAM,kBAAkB,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,aAAa,WAAW,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,CAAC;AAC7H,UAAI,CAAC,gBAAgB,GAAI,QAAO,gBAAgB;AAChD,YAAM,SAAS,cAAc,gBAAgB,MAAM;AACnD,eAAS,KAAK,iBAAiB,SAAS;AAAA,EAAK,MAAM,EAAE;AAAA,IACvD;AACA,UAAM,OAAO,KAAK,iBACd,sCACA;AACJ,UAAM,QAAQ,KAAK,QAAQ,UAAU,KAAK,KAAK;AAAA,IAAO;AACtD,WAAO,GAAG,KAAK,GAAG,IAAI;AAAA;AAAA,EAAqI,SAAS,KAAK,MAAM,CAAC;AAAA,EAClL;AACF,CAAC;AAED,IAAM,YAAY,KAAK;AAAA,EACrB,aAAa;AAAA,EACb,MAAM;AAAA,IACJ,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,SAAS,6BAA6B;AAAA,IACpF,WAAW,KAAK,OAAO,OAAO,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,EAC9F;AAAA,EACA,SAAS,OAAO,SAAwB;AACtC,UAAM,OAAO,KAAK,MAAM,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,OAAO,OAAO,KAAK,CAAC;AACrE,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAI,KAAK,SAAS,qBAAqB;AACrC,aAAO,uCAAuC,mBAAmB,SAAS,KAAK,MAAM;AAAA,IACvF;AAEA,UAAM,QAAQ,gBAAgB;AAC9B,UAAM,aAAa,MAAM,mBAAmB,KAAK;AACjD,QAAI,WAAY,QAAO;AAEvB,UAAM,YAAY,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,OAAO,GAAG,MAAM,MAAM,KAAK,aAAa,SAAS,GAAG,EAAE,KAAK,MAAM,CAAC;AAC5H,QAAI,CAAC,UAAU,GAAI,QAAO,UAAU;AACpC,WAAO,cAAc,UAAU,MAAM;AAAA,EACvC;AACF,CAAC;AAEM,IAAM,cAAc;AAAA,EACzB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,YAAY;AACd;;;AG/GA,SAAS,SAAS,QAAuB,MAAoB;AAC3D,SAAO,MAAM,SAAS;AACtB,SAAO,MAAM,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC;AAC1C;AAEO,SAAS,iBAAiB,QAA8B;AAC7D,SAAO,UAAU,OAAO,WAAW,CAAC;AAEpC,SAAO,QAAQ,eAAe,IAAI;AAAA,IAChC,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAEA,SAAO,QAAQ,WAAW,IAAI;AAAA,IAC5B,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAEA,SAAO,QAAQ,iBAAiB,IAAI;AAAA,IAClC,aAAa;AAAA,IACb,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACZ;AAEA,SAAO,QAAQ,eAAe,IAAI;AAAA,IAChC,aAAa;AAAA,IACb,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACZ;AAEA,SAAO,QAAQ,gBAAgB,IAAI;AAAA,IACjC,aAAa;AAAA,IACb,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACZ;AAEA,SAAO,QAAQ,iBAAiB,IAAI;AAAA,IAClC,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,eAAsB,2BAA2B,WAAmB,OAA6B,QAAsC;AACrI,MAAI;AACF,QAAI,MAAM,YAAY,iBAAiB;AACrC,eAAS,QAAQ,MAAM,WAAW,SAAS,CAAC;AAAA,IAC9C;AACA,QAAI,MAAM,YAAY,aAAa;AACjC,eAAS,QAAQ,MAAM,SAAS,SAAS,CAAC;AAAA,IAC5C;AACA,QAAI,MAAM,YAAY,mBAAmB;AACvC,eAAS,QAAQ,MAAM,qBAAqB,SAAS,CAAC;AAAA,IACxD;AAAA,EACF,SAAS,OAAO;AACd,aAAS,QAAQ,0BAA0B,aAAa,KAAK,CAAC,EAAE;AAAA,EAClE;AACF;;;ACjDA,IAAI,gBAA2C;AAOxC,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,SAAS,aAAa,gBAAgB,eAAe,cAAc,eAAe,CAAC;AAqB9H,SAAS,YAAkB,KAAgB,SAAuB;AAChE,MAAI,IAAI,QAAQ,QAAS;AACzB,QAAM,WAAW,IAAI,KAAK,EAAE,KAAK,EAAE;AACnC,MAAI,aAAa,OAAW,KAAI,OAAO,QAAQ;AACjD;AAMO,IAAM,mBAAmB;AAMhC,IAAM,wBAAwB,oBAAI,IAA0B;AAC5D,IAAM,uBAAuB,oBAAI,IAAsC;AACvE,IAAM,wBAAwB,oBAAI,IAAoB;AAE/C,SAAS,gBAAgB,WAA2B;AACzD,SAAO,sBAAsB,IAAI,SAAS,KAAK;AACjD;AAEO,SAAS,gBAAgB,WAAmB,QAAsB;AACvE,wBAAsB,IAAI,WAAW,MAAM;AAC3C,cAAY,uBAAuB,GAAG;AACxC;AAGO,SAAS,WAAW,WAAmB,MAAwB;AACpE,MAAI,QAAQ,sBAAsB,IAAI,SAAS;AAC/C,MAAI,CAAC,OAAO;AAAE,YAAQ,CAAC;AAAG,0BAAsB,IAAI,WAAW,KAAK;AAAG,gBAAY,uBAAuB,GAAG;AAAA,EAAG;AAChH,QAAM,KAAK,IAAI;AACjB;AAGO,SAAS,kBAAkB,WAAmBE,OAAc,SAA4B;AAC7F,MAAI,iBAAiB,qBAAqB,IAAI,SAAS;AACvD,MAAI,CAAC,gBAAgB;AAAE,qBAAiB,oBAAI,IAAI;AAAG,yBAAqB,IAAI,WAAW,cAAc;AAAG,gBAAY,sBAAsB,GAAG;AAAA,EAAG;AAChJ,QAAM,OAAO,eAAe,IAAIA,KAAI,KAAK,EAAE,OAAO,GAAG,QAAQ,EAAE;AAC/D,iBAAe,IAAIA,OAAM;AAAA,IACvB,OAAO,KAAK,QAAQ,QAAQ;AAAA,IAC5B,QAAQ,KAAK,SAAS,QAAQ;AAAA,EAChC,CAAC;AACH;AAuBO,SAAS,iBAAiB,MAAwC;AACvE,QAAM,SAAS,oBAAI,IAAyB;AAC5C,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AACd,UAAM,CAACC,OAAM,UAAU,SAAS,IAAI,QAAQ,MAAM,GAAG;AACrD,QAAI,CAACA,MAAM;AACX,WAAO,IAAIA,OAAM;AAAA,MACf,OAAO,SAAS,UAAU,EAAE,KAAK;AAAA,MACjC,QAAQ,SAAS,WAAW,EAAE,KAAK;AAAA,IACrC,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,SAAS,qBAAqB,KAAuC;AAC1E,SAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,EACrB,IAAI,CAAC,CAACA,OAAM,CAAC,MAAM,GAAGA,KAAI,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,EAAE,EACnD,KAAK,KAAK;AACf;AAMA,IAAM,cAAc,oBAAI,IAA2B;AAEnD,eAAe,iBAAiB,OAAoC;AAClE,QAAM,OAAO,YAAY,IAAI,KAAK,KAAK,QAAQ,QAAQ;AACvD,MAAI;AACJ,QAAM,OAAO,IAAI,QAAc,CAAAC,aAAW;AAAE,cAAUA;AAAA,EAAS,CAAC;AAChE,cAAY,IAAI,OAAO,IAAI;AAC3B,QAAM;AACN,SAAO,MAAM;AACX,YAAQ;AACR,QAAI,YAAY,IAAI,KAAK,MAAM,KAAM,aAAY,OAAO,KAAK;AAAA,EAC/D;AACF;AAOA,eAAsB,aAAa,OAAgB,QAAiB,WAAmC;AAErG,QAAM,UAAoB,YACtB,CAAC,SAAS,IACV,CAAC,GAAG,sBAAsB,KAAK,CAAC;AACpC,MAAI,QAAQ,WAAW,EAAG;AAE1B,MAAI;AACF,YAAQ,SAAS,gBAAgB;AACjC,QAAI,CAAC,MAAO;AACZ,UAAM,YAAY,KAAK;AAKvB,UAAM,cAAc,MAAM,iBAAiB,KAAK;AAChD,QAAI;AAKF,YAAM,aAA4B,CAAC;AACnC,YAAM,cAAc,oBAAI,IAAoB;AAC5C,iBAAW,OAAO,SAAS;AACzB,cAAM,MAAM,WAAW;AACvB,cAAM,QAAQ,sBAAsB,IAAI,GAAG,KAAK,CAAC;AACjD,8BAAsB,IAAI,KAAK,CAAC,CAAC;AACjC,cAAM,UAAU,qBAAqB,IAAI,GAAG,KAAK,oBAAI,IAAI;AACzD,6BAAqB,IAAI,KAAK,oBAAI,IAAI,CAAC;AACvC,cAAM,KAAK,UAAU,sBAAsB,IAAI,GAAG;AAClD,mBAAW,KAAK,EAAE,OAAO,SAAS,eAAe,MAAM,UAAU,CAAC;AAClE,oBAAY,IAAI,KAAK,GAAG;AAAA,MAC1B;AAGA,UAAI,CAAC,WAAW,KAAK,OAAK,EAAE,MAAM,SAAS,KAAK,EAAE,QAAQ,OAAO,CAAC,EAAG;AAIrE,UAAI;AAGF,mBAAW,KAAK,YAAY;AAC1B,cAAI,EAAE,kBAAkB,aAAa,CAAC,EAAE,eAAe;AACrD,cAAE,gBAAgB,OAAO,eAAe,oBAAoB,kBAAkB,KAAK;AAAA,UACrF;AAAA,QACF;AAGA,mBAAW,KAAK,YAAY;AAC1B,gBAAM,EAAE,OAAO,SAAS,eAAe,UAAU,IAAI;AACrD,cAAI,MAAM,WAAW,KAAK,QAAQ,SAAS,EAAG;AAE9C,gBAAM,CAAC,aAAa,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,YACnD,MAAM,SAAS,KAAK,eAAe,mBAAmB,iBAAiB,OAAO,gBAAgB,SAAS,EAAE,IAAI,QAAQ,QAAQ,EAAE;AAAA,YAC/H,QAAQ,OAAO,KAAK,eAAe,mBAAmB,iBAAiB,OAAO,gBAAgB,SAAS,EAAE,IAAI,QAAQ,QAAQ,EAAE;AAAA,UACjI,CAAC;AAED,cAAI;AACJ,cAAI,MAAM,SAAS,GAAG;AACpB,kBAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,QAAQ,CAAC,CAAC;AACrD,kBAAM,QAAQ;AAAA,cACZ,WAAW,KAAK,IAAI,IAAI;AAAA,cACxB,SAAS,WAAW,MAAM,MAAM,oBAAoB,MAAM,MAAM,aAAa,MAAM,KAAK,IAAI,CAAC;AAAA,cAC7F;AAAA,YACF;AAEA,gBAAI,MAA2D;AAAA,cAC7D,gBAAgB;AAAA,cAAG,SAAS,CAAC;AAAA,YAC/B;AACA,gBAAI,aAAa;AACf,kBAAI;AACF,sBAAM,SAAS,KAAK,MAAM,WAAW;AACrC,oBAAI,QAAQ,WAAW,MAAM,QAAQ,OAAO,OAAO,GAAG;AACpD,wBAAM;AACN,sBAAI,IAAI,iBAAiB,EAAG,KAAI,iBAAiB;AAAA,gBACnD;AAAA,cACF,SAAS,GAAG;AACV,yBAAS,wEAAwE,aAAa,CAAC,CAAC;AAAA,cAClG;AAAA,YACF;AACA,gBAAI,QAAQ,KAAK,KAAK;AACtB,gBAAI,IAAI,QAAQ,SAAS,iBAAkB,KAAI,UAAU,IAAI,QAAQ,MAAM,CAAC,gBAAgB;AAC5F,yBAAa,eAAe,aAAa,WAAW,CAAC,MAAM,OAAO,YAAY,aAAa,KAAK,UAAU,GAAG,GAAG,MAAM,gBAAgB,SAAS,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC;AAAA,UACpK;AAEA,cAAI;AACJ,cAAI,QAAQ,OAAO,GAAG;AACpB,kBAAM,WAAW,iBAAiB,WAAW;AAC7C,uBAAW,CAACD,OAAM,OAAO,KAAK,SAAS;AACrC,oBAAM,OAAO,SAAS,IAAIA,KAAI,KAAK,EAAE,OAAO,GAAG,QAAQ,EAAE;AACzD,uBAAS,IAAIA,OAAM,EAAE,OAAO,KAAK,QAAQ,QAAQ,OAAO,QAAQ,KAAK,SAAS,QAAQ,OAAO,CAAC;AAAA,YAChG;AACA,yBAAa,eAAe,aAAa,WAAW,CAAC,MAAM,OAAO,YAAY,aAAa,qBAAqB,QAAQ,GAAG,MAAM,gBAAgB,SAAS,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC;AAAA,UAC/K;AAGA,gBAAM,SAAkC,CAAC;AACzC,cAAI,UAAW,QAAO,KAAK,SAAS;AACpC,cAAI,UAAW,QAAO,KAAK,SAAS;AACpC,gBAAM,UAAU,MAAM,QAAQ,IAAI,MAAM;AACxC,qBAAW,UAAU,SAAS;AAC5B,gBAAI,CAAC,OAAO,IAAI;AACd,oBAAM,IAAI,MAAM,wBAAwB,OAAO,KAAK,EAAE;AAAA,YACxD;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AAIV,mBAAW,CAAC,KAAK,GAAG,KAAK,aAAa;AACpC,gBAAM,IAAI,WAAW,GAAG;AACxB,gBAAM,eAAe,sBAAsB,IAAI,GAAG,KAAK,CAAC;AACxD,gCAAsB,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAAC;AAE5D,gBAAM,SAAS,qBAAqB,IAAI,GAAG,KAAK,oBAAI,IAAI;AACxD,qBAAW,CAACA,OAAM,CAAC,KAAK,EAAE,SAAS;AACjC,kBAAM,OAAO,OAAO,IAAIA,KAAI,KAAK,EAAE,OAAO,GAAG,QAAQ,EAAE;AACvD,mBAAO,IAAIA,OAAM,EAAE,OAAO,KAAK,QAAQ,EAAE,OAAO,QAAQ,KAAK,SAAS,EAAE,OAAO,CAAC;AAAA,UAClF;AACA,+BAAqB,IAAI,KAAK,MAAM;AAAA,QACtC;AAEA,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,kBAAY;AAAA,IACd;AAAA,EACF,SAAS,GAAG;AACV,aAAS,yBAAyB,aAAa,CAAC,CAAC;AAAA,EACnD;AACF;;;AC5RA,eAAsB,eAAe,WAAmB,QAAiB,QAAwD;AAC/H,MAAI;AACF,WAAO,IAAI,eAAe;AAAA,EAC5B,SAAS,GAAG;AACV,aAAS,sBAAsB,aAAa,CAAC,CAAC;AAAA,EAChD;AACF;AAEA,eAAsB,uBAAuB,OAAgB,QAAgC;AAC3F,MAAI;AACF,UAAM,aAAa;AACnB,UAAM,cAAc;AAEpB,UAAM,MAAM,YAAY,aAAa;AAIrC,QAAI,QAAQ,IAAI,sBAAsB,OAAO,QAAQ,IAAI,sBAAsB,KAAK;AAClF,YAAM,QAAQ;AACd,YAAM,SAAU,aAAa,UAAU,SAAS,aAAa,QAAQ,WAAW,QAAQ,IAAK,IAAI;AACjG,wBAAkB,KAAK,YAAY,QAAQ,IAAI,EAAE,OAAO,OAAO,CAAC;AAAA,IAClE;AAIA,QAAI,QAAQ,IAAI,sBAAsB,OAAO,QAAQ,IAAI,2BAA2B,OAAO,WAAW,IAAI,YAAY,QAAQ,EAAE,GAAG;AACjI,YAAM,OAAO,YAAY,QAAQ,CAAC;AAClC,YAAM,WACJ,OAAO,KAAK,aAAa,WAAW,KAAK,WACvC,OAAO,KAAK,SAAS,WAAW,KAAK,OACrC;AACJ,UAAI,UAAU;AACZ,mBAAW,KAAK,EAAE,MAAM,YAAY,QAAQ,IAAI,UAAU,SAAS,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,aAAS,+BAA+B,aAAa,CAAC,CAAC;AAAA,EACzD;AACF;AAEA,eAAsB,kBAAkB,WAAmB,OAAgB,QAAgC;AACzG,MAAI;AACF,UAAM,aAAa;AACnB,UAAM,cAAc;AACpB,UAAM,MAAM,YAAY,aAAa;AAIrC,UAAM,QAAQ,aAAa,SAAS,CAAC,GAClC,OAAO,CAAC,MAA2C,EAAE,SAAS,MAAM,EACpE,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,GAAG;AACX,QAAI,oBAAoB,IAAI,GAAG;AAC7B,oBAAc,IAAI,GAAG;AAAA,IACvB;AAKA,UAAM,gBAAgB,gBAAgB,GAAG;AACzC,UAAM,aAAa,kBAAkB,YAAY,SAAY;AAC7D,oBAAgB,KAAK,MAAM,sBAAsB,SAAS,CAAC;AAI3D,QAAI,QAAQ,IAAI,sBAAsB,KAAK;AACzC,YAAM,aAAa,WAAW,YAAY,GAAG;AAAA,IAC/C;AAAA,EACF,SAAS,GAAG;AACV,aAAS,yBAAyB,aAAa,CAAC,CAAC;AAAA,EACnD;AACF;AAEA,eAAsB,gBAAgB,WAAkC;AACtE,QAAM,aAAa,SAAS;AAC9B;;;AC3EA,IAAI,cAA6B;AACjC,IAAI,qBAAqB;AAMzB,IAAM,yBAAyB,KAAK,KAAK;AACzC,IAAM,sBAAsB,oBAAI,IAAoB;AAGpD,SAAS,kBAAkB,WAA4B;AACrD,QAAM,KAAK,oBAAoB,IAAI,SAAS;AAC5C,MAAI,OAAO,OAAW,QAAO;AAC7B,MAAI,KAAK,IAAI,IAAI,KAAK,wBAAwB;AAC5C,wBAAoB,OAAO,SAAS;AACpC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,mBAAmB,WAAyB;AACnD,sBAAoB,IAAI,WAAW,KAAK,IAAI,CAAC;AAC/C;AAGA,SAAS,qBAA2B;AAClC,QAAM,MAAM,KAAK,IAAI;AACrB,aAAW,CAAC,IAAI,EAAE,KAAK,qBAAqB;AAC1C,QAAI,MAAM,KAAK,uBAAwB,qBAAoB,OAAO,EAAE;AAAA,EACtE;AACF;AAEA,eAAsB,YAAY,WAAmB,OAAmD;AACtG,MAAI,MAAM,MAAM,SAAS,qBAAqB,CAAC,oBAAoB;AACjE,yBAAqB;AACrB,KAAC,YAAY;AACX,UAAI;AACF,cAAM,iBAAiB,MAAM;AAAA,UAC3B,CAAC,UAAU,MAAM,WAAW,aAAa,WAAW,KAAK,MAAM,SAAS;AAAA,UACxE,EAAE,KAAK,UAAU;AAAA,QACnB;AACA,YAAI,CAAC,eAAe,IAAI;AACtB,gBAAM,IAAI,MAAM,eAAe,KAAK;AAAA,QACtC;AACA,cAAM,SAAS,cAAc,eAAe,MAAM;AAClD,sBAAc;AAAA,EAAuC,MAAM;AAAA,MAC7D,SAAS,GAAY;AACnB,iBAAS,6DAA6D,aAAa,CAAC,CAAC;AAErF,6BAAqB;AAAA,MACvB;AAAA,IACF,GAAG,EAAE,MAAM,MAAM;AAAE,2BAAqB;AAAA,IAAO,CAAC;AAAA,EAClD;AACF;AAEA,eAAsB,sBAAsB,OAAgB,QAAgC;AAC1F,MAAI;AACF,UAAM,aAAa;AACnB,UAAM,cAAc;AAGpB,QAAI,YAAY,aAAa,CAAC,kBAAkB,WAAW,SAAS,GAAG;AACrE,yBAAmB,WAAW,SAAS;AACvC,UAAI,aAAa;AACf,qBAAa,QAAQ,QAAQ,WAAW;AAAA,MAC1C;AAAA,IACF;AAGA,QAAI,YAAY,aAAa,cAAc,IAAI,WAAW,SAAS,GAAG;AACpE,oBAAc,OAAO,WAAW,SAAS;AACzC,mBAAa,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,aAAS,6BAA6B,aAAa,CAAC,CAAC;AAAA,EACvD;AACF;AAEO,SAAS,sBAA4B;AAC1C,qBAAmB;AACnB,sBAAoB,MAAM;AAC5B;;;ACpFA,IAAM,iBAAyB,OAAO,QAAQ,eAAe;AAC3D,QAAM,OAAQ,cAAc,CAAC;AAC7B,MAAI,KAAK,MAAO,wBAAuB,KAAK,KAAK;AAIjD,QAAM,YAAY,gBAAgB;AAElC,SAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM,EAAE,GAAG,YAAY;AAAA,IACvB,QAAQ,OAAO,mBAA4C;AACzD,uBAAiB,cAAwD;AAAA,IAC3E;AAAA,IACA,0BAA0B,OAAO,OAA6B,WAAiC;AAC7F,YAAM,2BAA2B,WAAW,OAAO,MAAM;AAAA,IAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,aAAa,OAAOE,SAAQ,WAAW;AACrC,YAAM,eAAe,WAAWA,SAAQ,MAAyC;AAAA,IACnF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,sBAAsB,OAAO,OAAO,WAAW;AAC7C,YAAM,uBAAuB,OAAO,MAAM;AAAA,IAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaA,gBAAgB,OAAO,OAAO,WAAW;AACvC,YAAM,kBAAkB,WAAW,OAAO,MAAM;AAAA,IAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,OAAO,OAAO,UAAuC;AACnD,YAAM,YAAY,WAAW,KAAK;AAAA,IACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,sCAAsC,OAAO,OAAO,WAAW;AAC7D,YAAM,sBAAsB,OAAO,MAAM;AAAA,IAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,SAAS,YAAY;AACnB,UAAI;AAEF,cAAM,gBAAgB,SAAS;AAC/B,4BAAoB;AAAA,MACtB,SAAS,GAAY;AAAA,MAErB;AAAA,IACF;AAAA,EACF;AACA;AAQA,IAAM,SAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,QAAQ;AACV;AAEA,IAAO,gBAAQ;",
6
- "names": ["result", "mkdir", "rm", "join", "homedir", "join", "homedir", "mkdir", "rm", "e", "resolve", "error", "tool", "tool", "resolve", "_input"]
3
+ "sources": ["../src/debug.ts", "../src/store.ts", "../src/utils.ts", "../src/capture.ts", "../src/recall-gate.ts", "../src/capture-hooks.ts", "../src/memoir-ops.ts", "../src/commands.ts", "../src/session-context.ts", "../src/tools.ts", "../src/index.ts"],
4
+ "sourcesContent": ["/**\n * Debug logger writing to stderr. Prefixes with [memoir] for easy filtering.\n * Only writes when MEMOIR_DEBUG=1 is set.\n */\nexport function debugLog(...args: unknown[]): void {\n if (process.env.MEMOIR_DEBUG !== \"1\") return;\n const ts = new Date().toISOString();\n process.stderr.write(`[memoir ${ts}] ${args.map((a) => String(a)).join(\" \")}\\n`);\n}\n", "import { execFile, execFileSync } from \"node:child_process\";\nimport { access, mkdir, rm } from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { debugLog } from \"./debug.js\";\nimport { errorMessage } from \"./utils.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport const MEMOIR_PACKAGE = \"memoir-ai\";\n\n/**\n * Pinned memoir-ai version for uvx/uv fallbacks.\n * Mirrors plugins/claude-code/scripts/resolve-memoir-cli.sh.\n * Bump deliberately after verifying the new release works with this plugin.\n */\nexport const MEMOIR_AI_PIN = \"0.2.2\";\n\n/** Timeout (ms) for synchronous git subprocess calls. */\nexport const GIT_TIMEOUT_MS = 3_000;\n\n/** Timeout (ms) for async memoir CLI calls. */\nexport const MEMOIR_TIMEOUT_MS = 15_000;\n\n/** Max stdout/stderr buffer (bytes) for async memoir CLI calls. */\nexport const MEMOIR_MAX_BUFFER = 1_024 * 1_024;\n\nexport type SpawnSpec = { command: string; args: string[]; label: string };\n\nexport type MemoirResult =\n | { ok: true; stdout: string }\n | { ok: false; error: string; resolver?: string };\n\n/** Max entries in _noGitCache before eviction. */\nconst NO_GIT_CACHE_MAX = 500;\n\n/** TTL for non-git cache entries (5 minutes). */\nconst NO_GIT_CACHE_TTL = 5 * 60 * 1_000;\n\n/** Evict the oldest entry (first inserted) from the noGitCache. */\nfunction evictOldestNoGit(): void {\n const firstKey = _noGitCache.keys().next().value;\n if (firstKey !== undefined) _noGitCache.delete(firstKey);\n}\n\n/** Sweep expired entries from noGitCache. */\nfunction sweepNoGitCache(): void {\n const now = Date.now();\n for (const [key, ts] of _noGitCache) {\n if (now - ts >= NO_GIT_CACHE_TTL) _noGitCache.delete(key);\n }\n}\n\n/**\n * Cache of cwds confirmed to be outside a git repo, keyed by path with\n * timestamp of when the entry was added.\n * Avoids redundant ~200ms failing `git rev-parse` calls on non-git directories,\n * while still allowing a directory that becomes a git repo (e.g. `git init`)\n * to be retried after the TTL expires.\n */\nexport const _noGitCache = new Map<string, number>();\n\n/**\n * Check whether a cwd is cached as non-git and still within TTL.\n * If the entry exists but is expired, it is removed from the cache.\n */\nexport function _checkNoGit(cwd: string): boolean {\n const ts = _noGitCache.get(cwd);\n if (ts === undefined) return false;\n const age = Date.now() - ts;\n if (age < NO_GIT_CACHE_TTL) return true;\n // Expired \u2014 remove so the directory can be re-checked\n _noGitCache.delete(cwd);\n return false;\n}\n\n/**\n * Resolve the main worktree root path for a git repository.\n * Mirrors _main_worktree_root from plugins/claude-code/scripts/derive-store-path.sh.\n *\n * @returns The main worktree path, or empty string if not in a git repo.\n */\nexport function _main_worktree_root(cwd: string): string {\n if (_checkNoGit(cwd)) return \"\";\n try {\n // Fast path: check if --git-dir and --git-common-dir resolve to the same path\n const gitDir = execFileSync(\"git\", [\"rev-parse\", \"--git-dir\"], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n }).trim();\n const gitCommonDir = execFileSync(\"git\", [\"rev-parse\", \"--git-common-dir\"], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n }).trim();\n\n // Resolve to absolute paths\n const resolvePath = (path: string): string => {\n if (path.startsWith(\"/\")) return path;\n return join(cwd, path);\n };\n const gitDirAbs = resolvePath(gitDir);\n const gitCommonDirAbs = resolvePath(gitCommonDir);\n\n if (gitDirAbs === gitCommonDirAbs) {\n // Main worktree or non-worktree repo\n return execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n }).trim();\n }\n\n // Slow path: parse `git worktree list --porcelain` for the main worktree\n const worktreeList = execFileSync(\"git\", [\"worktree\", \"list\", \"--porcelain\"], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n });\n const firstLine = worktreeList.split(\"\\n\")[0];\n if (firstLine.startsWith(\"worktree \")) {\n const mainWorktree = firstLine.substring(\"worktree \".length).trim();\n if (mainWorktree && mainWorktree !== \"(bare)\") {\n return mainWorktree;\n }\n }\n\n // Fallback: try --show-toplevel (bare repo or older git)\n return execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n }).trim();\n } catch (e) {\n debugLog(\"_main_worktree_root: not a git repo or git error:\", errorMessage(e));\n sweepNoGitCache();\n _noGitCache.set(cwd, Date.now());\n if (_noGitCache.size > NO_GIT_CACHE_MAX) evictOldestNoGit();\n return \"\";\n }\n}\n\n/**\n * Derive `~/.memoir/<slug>` from cwd.\n * Override via `store` plugin option or `MEMOIR_STORE` env var.\n *\n * Resolution order (mirrors plugins/claude-code/scripts/derive-store-path.sh):\n * 1. Plugin option override\n * 2. MEMOIR_STORE env var\n * 3. Git root (realpath) \u2014 all worktrees of a repo share one store\n * 4. `path.resolve()` of cwd \u2014 normalized deterministic slug for non-git folders\n * 5. Raw cwd fallback\n */\nexport function deriveStorePath(cwd: string = process.cwd()): string {\n if (pluginStoreOverride) return pluginStoreOverride;\n const configured = process.env.MEMOIR_STORE;\n if (configured) return configured;\n\n // Prefer git root so all subdirectories and worktrees share one store\n let projectDir: string;\n try {\n const gitRoot = _main_worktree_root(cwd);\n if (gitRoot) {\n projectDir = gitRoot;\n } else {\n // Not in git \u2014 normalize to deterministic absolute path\n projectDir = resolve(cwd);\n }\n } catch (e) {\n debugLog(\"deriveStorePath: unexpected error:\", errorMessage(e));\n // Fallback to raw cwd if everything else fails\n projectDir = cwd;\n }\n\n // Slug = absolute path with '/' and '.' replaced by '-'\n // Matches Claude Code's own ~/.claude/projects/ naming convention\n const slug = projectDir.replace(/[/.]/g, \"-\");\n return join(homedir(), \".memoir\", slug);\n}\n\n/** Set by plugin options (`store` key). */\nexport let pluginStoreOverride: string | undefined;\n\n/** Set `pluginStoreOverride` from plugin init. Exported as a function\n * because ES module imports are read-only bindings (direct assignment\n * from another module is a TS2632 error). */\nexport function setPluginStoreOverride(store: string | undefined): void {\n pluginStoreOverride = store;\n}\n\n/** Max entries in ensuredStores before eviction. */\nconst ENSURED_STORES_MAX = 200;\n\n/** Cache of stores already verified or created \u2014 avoids redundant access() + CLI calls. */\nexport const ensuredStores = new Set<string>();\n\n/**\n * Stores currently being created \u2014 prevents concurrent `memoir new` on\n * the same path (C5 fix).\n */\nconst storeCreations = new Map<string, Promise<void>>();\n\nexport async function ensureStore(store: string): Promise<void> {\n if (ensuredStores.has(store)) return;\n\n // If another caller is already creating this store, await it\n const inFlight = storeCreations.get(store);\n if (inFlight) {\n await inFlight;\n return;\n }\n\n try {\n await access(join(store, \".git\"));\n ensuredStores.add(store);\n if (ensuredStores.size > ENSURED_STORES_MAX) ensuredStores.clear();\n return; // already exists\n } catch {\n // ensure parent dir exists (memoir new doesn't create intermediate dirs)\n await mkdir(join(store, \"..\"), { recursive: true }).catch((e: unknown) =>\n debugLog(\"ensureStore: mkdir failed:\", errorMessage(e)),\n );\n }\n\n // Register this creation so concurrent callers coallesce\n const creationPromise = (async () => {\n const tmpDir = join(tmpdir(), `memoir-scratch-${Date.now()}`);\n try {\n await mkdir(tmpDir, { recursive: true });\n await execFileAsync(\"git\", [\"init\", \"-q\", tmpDir], { timeout: 5000 });\n const result = await runMemoir([\"new\", store, \"--taxonomy-builtin\"], { cwd: tmpDir });\n if (!result.ok) {\n throw new Error(result.error);\n }\n ensuredStores.add(store);\n if (ensuredStores.size > ENSURED_STORES_MAX) ensuredStores.clear();\n } catch (e: unknown) {\n debugLog(\"ensureStore: creation failed:\", errorMessage(e));\n throw e;\n } finally {\n rm(tmpDir, { recursive: true, force: true }).catch((e) =>\n debugLog(\"tmp cleanup failed:\", errorMessage(e)),\n );\n }\n })();\n storeCreations.set(store, creationPromise);\n try {\n await creationPromise;\n } finally {\n storeCreations.delete(store);\n }\n}\n\n/**\n * Reorder a cached resolver list so that `winner` is first.\n * Returns a new array (does not mutate the input).\n */\nexport function reorderResolver<T>(resolver: T[], winner: T): T[] {\n const idx = resolver.indexOf(winner);\n if (idx <= 0) return [...resolver];\n const next = [...resolver];\n next.splice(idx, 1);\n next.unshift(winner);\n return next;\n}\n\n/** Which CLI launcher successfully resolved memoir. Cached to avoid redundant fallback probing. */\nexport let memoirResolved: string | null = null;\n\nexport async function runMemoir(\n args: string[],\n options: { cwd?: string } = {},\n): Promise<MemoirResult> {\n let specs = memoirSpawnSpecs(args);\n\n // Put cached resolver first so we skip redundant fallback probing\n if (memoirResolved) {\n const winner = specs.find((s) => s.label === memoirResolved);\n if (winner) specs = reorderResolver(specs, winner);\n }\n\n let lastError = \"\";\n let lastResolver: string | undefined;\n for (const spec of specs) {\n try {\n const { stdout } = await execFileAsync(spec.command, spec.args, {\n cwd: options.cwd ?? process.cwd(),\n env: process.env,\n maxBuffer: MEMOIR_MAX_BUFFER,\n timeout: MEMOIR_TIMEOUT_MS, // prevent hang if memoir CLI stalls\n });\n memoirResolved = spec.label;\n return { ok: true, stdout: stdout.trim() };\n } catch (e) {\n lastError = errorMessage(e);\n lastResolver = spec.label;\n debugLog(\"runMemoir: fallback\", spec.label, \"failed:\", lastError);\n // Invalidate cache so we don't keep trying a broken resolver on next call\n if (memoirResolved === spec.label) memoirResolved = null;\n }\n }\n\n return { ok: false, error: `Memoir command failed: ${lastError}`, resolver: lastResolver };\n}\n\nexport function memoirSpawnSpecs(args: string[]): SpawnSpec[] {\n return [\n { command: \"memoir\", args, label: \"memoir\" },\n {\n command: \"uvx\",\n args: [\"--from\", `${MEMOIR_PACKAGE}==${MEMOIR_AI_PIN}`, \"memoir\", ...args],\n label: \"uvx\",\n },\n {\n command: \"uv\",\n args: [\"tool\", \"run\", \"--from\", `${MEMOIR_PACKAGE}==${MEMOIR_AI_PIN}`, \"memoir\", ...args],\n label: \"uv tool run\",\n },\n ];\n}\n\n/** Read the current memoir branch name for use in storage keys. */\nexport async function getCurrentBranch(store: string): Promise<string> {\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn([\"--json\", \"-s\", store, \"status\"], { cwd: store });\n if (!result.ok) return \"unknown\";\n const data = JSON.parse(result.stdout);\n return data.branch || \"unknown\";\n } catch (e) {\n debugLog(\"getCurrentBranch: failed:\", errorMessage(e));\n return \"unknown\";\n }\n}\n\n/**\n * Return the current git branch of the user's project (from cwd).\n * Empty string if not in a git repo or detached HEAD.\n * Mirrors code_git_branch from plugins/claude-code/hooks/common.sh.\n */\nexport async function codeGitBranch(): Promise<string> {\n try {\n const execFn = _storeTestOverrides.execFileAsyncFn ?? execFileAsync;\n const { stdout } = await execFn(\"git\", [\"branch\", \"--show-current\"], {\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n });\n return stdout.trim();\n } catch (e) {\n debugLog(\"codeGitBranch: failed:\", errorMessage(e));\n return \"\";\n }\n}\n\n/**\n * Check whether a branch exists in the memoir store.\n * Mirrors branch_exists_in_memoir from plugins/claude-code/hooks/common.sh.\n */\nexport async function branchExistsInMemoir(store: string, name: string): Promise<boolean> {\n if (!name) return false;\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn([\"--json\", \"-s\", store, \"branch\"], { cwd: store });\n if (!result.ok) return false;\n const data = JSON.parse(result.stdout);\n const branches: string[] = data?.branches ?? [];\n return branches.includes(name);\n } catch (e) {\n debugLog(\"branchExistsInMemoir: failed:\", errorMessage(e));\n return false;\n }\n}\n\n/**\n * Auto-match memoir branch to the current code git branch.\n * If the code branch differs from the memoir branch, creates the memoir branch\n * (forked from main) if needed and checks it out.\n * Mirrors auto_match_memoir_branch from plugins/claude-code/hooks/common.sh.\n */\nexport async function autoMatchMemoirBranch(store: string): Promise<string> {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const codeBranch = await codeGitBranch();\n if (!codeBranch) {\n return getCurrentBranch(store); // detached or non-git \u2014 just report current\n }\n\n const current = await getCurrentBranch(store);\n if (current === codeBranch) return current; // already matched\n\n // Create the branch from main if it doesn't exist yet\n if (!(await branchExistsInMemoir(store, codeBranch))) {\n const result = await runFn([\"-s\", store, \"branch\", codeBranch, \"--from\", \"main\"], {\n cwd: store,\n });\n if (!result.ok) {\n debugLog(\"autoMatchMemoirBranch: create branch failed:\", result.error);\n return getCurrentBranch(store);\n }\n }\n // Checkout the branch\n const result = await runFn([\"-s\", store, \"checkout\", codeBranch], { cwd: store });\n if (!result.ok) {\n debugLog(\"autoMatchMemoirBranch: checkout failed:\", result.error);\n return getCurrentBranch(store);\n }\n return codeBranch;\n}\n\n/**\n * Read the content of a single memoir key (first item found).\n * Returns empty string if key doesn't exist or on error.\n */\nexport async function readMemoirValue(\n store: string,\n key: string,\n namespace: string = \"default\",\n): Promise<string> {\n try {\n const runFn = _storeTestOverrides.runMemoirFn ?? runMemoir;\n const result = await runFn([\"--json\", \"-s\", store, \"get\", key, \"-n\", namespace], {\n cwd: store,\n });\n if (!result.ok) return \"\";\n const parsed = JSON.parse(result.stdout);\n const items = parsed?.items ?? [];\n const value = items[0]?.value?.content;\n return typeof value === \"string\" ? value : \"\";\n } catch (e) {\n debugLog(\"readMemoirValue: failed:\", errorMessage(e));\n return \"\";\n }\n}\n\n// --- Test seams (only for unit tests) ---\ntype StoreTestOverrides = {\n runMemoirFn?: (args: string[], options: { cwd?: string }) => Promise<MemoirResult>;\n execFileAsyncFn?: (\n command: string,\n args: string[],\n options?: Record<string, unknown>,\n ) => Promise<{ stdout: string }>;\n};\nlet _storeTestOverrides: StoreTestOverrides = {};\nexport function setStoreTestOverrides(overrides: StoreTestOverrides): void {\n _storeTestOverrides = overrides;\n}\n", "/** Shared helpers for the Memoir OpenCode plugin.\n *\n * IMPORTANT: these must NOT be exported from index.ts \u2014 OpenCode's plugin\n * loader treats every module export as a plugin and throws\n * \"Plugin export is not a function\" on non-function exports.\n */\n\nexport function coercePaths(path: string | string[] | undefined): string[] {\n if (!path) return [];\n return Array.isArray(path) ? path.filter(Boolean) : [path];\n}\n\n/** Format JSON string with 2-space indentation. Passes non-JSON through unchanged. */\nexport function tryPrettyJson(text: string): string {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\n/** Max keys memoir_get accepts to avoid hitting OS arg-length limits. */\nexport const MEMOIR_GET_MAX_KEYS = 20;\n\nexport const SECRET_PATTERN = new RegExp(\n [\n // Generic secret assignments\n \"(?:password|passwd|secret|api[_-]?key|token|auth[_-]?token|access[_-]?key|private[_-]?key)\\\\s*[=:]\\\\s*['\\\"]?[^\\\\s'\\\"]{8,}\",\n // AWS secret keys\n \"(?:aws[_-]?secret[_-]?access[_-]?key|aws_secret)\\\\s*[=:]\\\\s*['\\\"]?[A-Za-z0-9/+=]{40}\",\n // Connection strings with embedded credentials\n \"(?:postgres|mysql|mongodb|redis|amqp)://[^\\\\s:]+:[^\\\\s@]+@[^\\\\s]+\",\n // Generic connection strings with passwords\n \"://[^\\\\s:]+:[^\\\\s@]+@\",\n // PEM private keys\n \"-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----\",\n // Generic high-entropy secret assignments\n \"(?:secret|credential|key|password)\\\\s*[=:]\\\\s*['\\\"][A-Za-z0-9+/=_-]{20,}['\\\"]\",\n ].join(\"|\"),\n);\n\n/** Safely extract a display string from an unknown thrown value. */\nexport function errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n", "import { debugLog } from \"./debug.js\";\nimport {\n deriveStorePath,\n ensureStore,\n getCurrentBranch,\n type MemoirResult,\n readMemoirValue,\n runMemoir,\n} from \"./store.js\";\nimport { errorMessage } from \"./utils.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Test-only override for CLI functions used by flushCapture. When null (production),\n * the real implementations from ./store.js are used. */\nexport type MemoirCliOverrides = {\n runMemoir?: (args: string[], options?: { cwd?: string }) => Promise<MemoirResult>;\n readMemoirValue?: (store: string, key: string, namespace?: string) => Promise<string>;\n getCurrentBranch?: (store: string) => Promise<string>;\n};\n\nlet _cliOverrides: MemoirCliOverrides | null = null;\n\n/** Set test-only CLI overrides for flushCapture. Pass null to restore defaults. */\nexport function setCliOverrides(overrides: MemoirCliOverrides | null): void {\n _cliOverrides = overrides;\n}\n\nexport const EDIT_TOOLS = new Set([\n \"Edit\",\n \"Write\",\n \"MultiEdit\",\n \"NotebookEdit\",\n \"apply_patch\",\n \"ApplyPatch\",\n \"MultiFileEdit\",\n]);\n\nexport interface EditRecord {\n tool: string;\n filePath: string;\n snippet: string;\n timestamp: number;\n}\n\nexport interface ToolMetrics {\n calls: number;\n errors: number;\n}\n\ninterface MetricsEntry {\n timestamp: number;\n summary: string;\n files: string[];\n}\n\n/** Evict the oldest entry from a Map when it exceeds maxSize. */\nfunction evictOldest<K, V>(map: Map<K, V>, maxSize: number): void {\n if (map.size <= maxSize) return;\n const firstKey = map.keys().next().value;\n if (firstKey !== undefined) map.delete(firstKey);\n}\n\n/**\n * Maximum entries retained in metrics.code.<branch>.\n * Mirrors MEMOIR_METRICS_CODE_MAX from plugins/claude-code/hooks/stop.sh.\n */\nexport const METRICS_CODE_MAX = 1000;\n\n// ---------------------------------------------------------------------------\n// Per-session state\n// ---------------------------------------------------------------------------\n\nconst pendingEditsBySession = new Map<string, EditRecord[]>();\nconst toolMetricsBySession = new Map<string, Map<string, ToolMetrics>>();\nconst cachedBranchBySession = new Map<string, string>();\n\nexport function getCachedBranch(sessionID: string): string {\n return cachedBranchBySession.get(sessionID) ?? \"unknown\";\n}\n\nexport function setCachedBranch(sessionID: string, branch: string): void {\n cachedBranchBySession.set(sessionID, branch);\n evictOldest(cachedBranchBySession, 200);\n}\n\n/** Record an edit for a specific session. */\nexport function recordEdit(sessionID: string, edit: EditRecord): void {\n let edits = pendingEditsBySession.get(sessionID);\n if (!edits) {\n edits = [];\n pendingEditsBySession.set(sessionID, edits);\n evictOldest(pendingEditsBySession, 500);\n }\n edits.push(edit);\n}\n\n/** Accumulate tool metrics for a specific session (adds to previous values). */\nexport function recordToolMetrics(sessionID: string, tool: string, metrics: ToolMetrics): void {\n let sessionMetrics = toolMetricsBySession.get(sessionID);\n if (!sessionMetrics) {\n sessionMetrics = new Map();\n toolMetricsBySession.set(sessionID, sessionMetrics);\n evictOldest(toolMetricsBySession, 500);\n }\n const prev = sessionMetrics.get(tool) ?? { calls: 0, errors: 0 };\n sessionMetrics.set(tool, {\n calls: prev.calls + metrics.calls,\n errors: prev.errors + metrics.errors,\n });\n}\n\n/** Peek at a session's pending edits (test helper, read-only). */\nexport function getPendingEdits(sessionID: string): readonly EditRecord[] {\n return pendingEditsBySession.get(sessionID) ?? [];\n}\n\n/** Peek at a session's tool metrics (test helper, read-only). */\nexport function getToolMetrics(sessionID: string): ReadonlyMap<string, ToolMetrics> {\n return toolMetricsBySession.get(sessionID) ?? new Map();\n}\n\n/** Clear all state for a session (test helper). */\nexport function clearSession(sessionID: string): void {\n pendingEditsBySession.delete(sessionID);\n toolMetricsBySession.delete(sessionID);\n cachedBranchBySession.delete(sessionID);\n}\n\n// ---------------------------------------------------------------------------\n// parse / serialize\n// ---------------------------------------------------------------------------\n\nexport function parseTurnMetrics(text: string): Map<string, ToolMetrics> {\n const result = new Map<string, ToolMetrics>();\n if (!text) return result;\n const parts = text.split(\"|\");\n for (const part of parts) {\n const trimmed = part.trim();\n if (!trimmed) continue;\n const [tool, callsStr, errorsStr] = trimmed.split(\":\");\n if (!tool) continue;\n result.set(tool, {\n calls: parseInt(callsStr, 10) || 0,\n errors: parseInt(errorsStr, 10) || 0,\n });\n }\n return result;\n}\n\nexport function serializeTurnMetrics(map: Map<string, ToolMetrics>): string {\n return [...map.entries()].map(([tool, m]) => `${tool}:${m.calls}:${m.errors}`).join(\" | \");\n}\n\n// ---------------------------------------------------------------------------\n// Per-store mutex\n// ---------------------------------------------------------------------------\n\nconst flushQueues = new Map<string, Promise<void>>();\n\nasync function acquireFlushLock(store: string): Promise<() => void> {\n const prev = flushQueues.get(store) ?? Promise.resolve();\n let release: () => void;\n const next = new Promise<void>((resolve) => {\n release = resolve;\n });\n flushQueues.set(store, next);\n await prev;\n return () => {\n release();\n if (flushQueues.get(store) === next) flushQueues.delete(store);\n };\n}\n\n// ---------------------------------------------------------------------------\n// ---------------------------------------------------------------------------\n// flushCapture \u2014 flushes one session, or all if no sessionID\n// ---------------------------------------------------------------------------\n\nexport async function flushCapture(\n store?: string,\n branch?: string,\n sessionID?: string,\n): Promise<void> {\n // Determine which sessions to flush\n const targets: string[] = sessionID ? [sessionID] : [...pendingEditsBySession.keys()]; // dispose: flush all sessions\n if (targets.length === 0) return;\n\n try {\n store = store ?? deriveStorePath();\n if (!store) return;\n await ensureStore(store);\n\n // Acquire store-level mutex before touching per-session state.\n // Prevents C1 (cross-session interleave) and C2-RACE (data arriving\n // between snapshot and drain).\n const releaseLock = await acquireFlushLock(store);\n try {\n // Atomic swap: grab current data and replace with empty containers.\n // Any recordEdit/recordToolMetrics call during the subsequent I/O\n // goes into the fresh empty containers and is safe.\n interface SessionData {\n edits: EditRecord[];\n metrics: Map<string, ToolMetrics>;\n sessionBranch: string;\n }\n const perSession: SessionData[] = [];\n const swappedSids = new Map<string, number>(); // sid \u2192 index in perSession for rollback\n for (const sid of targets) {\n const idx = perSession.length;\n const edits = pendingEditsBySession.get(sid) ?? [];\n pendingEditsBySession.set(sid, []);\n const metrics = toolMetricsBySession.get(sid) ?? new Map();\n toolMetricsBySession.set(sid, new Map());\n const sb = branch ?? cachedBranchBySession.get(sid);\n perSession.push({ edits, metrics, sessionBranch: sb ?? \"unknown\" });\n swappedSids.set(sid, idx);\n }\n\n // Skip if nothing to flush\n if (!perSession.some((p) => p.edits.length > 0 || p.metrics.size > 0)) return;\n\n // Wrap I/O in inner try \u2014 if it fails, restore swapped data before\n // propagating the error so the next flush retries it.\n try {\n // Resolve 'unknown' branches (read-only I/O, safe inside lock \u2014\n // the lock serializes per-store CLI access)\n for (const p of perSession) {\n if (p.sessionBranch === \"unknown\" || !p.sessionBranch) {\n p.sessionBranch = await (_cliOverrides?.getCurrentBranch ?? getCurrentBranch)(store);\n }\n }\n\n // For each session, read-merge-write sequentially.\n for (const p of perSession) {\n const { edits, metrics, sessionBranch: branchKey } = p;\n if (edits.length === 0 && metrics.size === 0) continue;\n\n const [prevCodeRaw, prevTurnRaw] = await Promise.all([\n edits.length > 0\n ? (_cliOverrides?.readMemoirValue ?? readMemoirValue)(\n store,\n `metrics.code.${branchKey}`,\n )\n : Promise.resolve(\"\"),\n metrics.size > 0\n ? (_cliOverrides?.readMemoirValue ?? readMemoirValue)(\n store,\n `metrics.turn.${branchKey}`,\n )\n : Promise.resolve(\"\"),\n ]);\n\n let codeWrite: Promise<MemoirResult> | undefined;\n if (edits.length > 0) {\n const files = [...new Set(edits.map((e) => e.filePath))];\n const entry = {\n timestamp: Date.now() / 1000,\n summary: `Changed ${edits.length} block(s) across ${files.length} file(s): ${files.join(\", \")}`,\n files,\n };\n\n let acc: { schema_version: number; entries: MetricsEntry[] } = {\n schema_version: 2,\n entries: [],\n };\n if (prevCodeRaw) {\n try {\n const parsed = JSON.parse(prevCodeRaw);\n if (parsed?.entries && Array.isArray(parsed.entries)) {\n acc = parsed;\n if (acc.schema_version < 2) acc.schema_version = 2;\n }\n } catch (e) {\n debugLog(\n \"flushCapture: failed to parse existing code metrics, starting fresh:\",\n errorMessage(e),\n );\n }\n }\n acc.entries.push(entry);\n if (acc.entries.length > METRICS_CODE_MAX)\n acc.entries = acc.entries.slice(-METRICS_CODE_MAX);\n codeWrite = (_cliOverrides?.runMemoir ?? runMemoir)(\n [\n \"-s\",\n store,\n \"remember\",\n \"--replace\",\n JSON.stringify(acc),\n \"-p\",\n `metrics.code.${branchKey}`,\n ],\n { cwd: store },\n );\n }\n\n let turnWrite: Promise<MemoirResult> | undefined;\n if (metrics.size > 0) {\n const existing = parseTurnMetrics(prevTurnRaw);\n for (const [tool, current] of metrics) {\n const prev = existing.get(tool) ?? { calls: 0, errors: 0 };\n existing.set(tool, {\n calls: prev.calls + current.calls,\n errors: prev.errors + current.errors,\n });\n }\n turnWrite = (_cliOverrides?.runMemoir ?? runMemoir)(\n [\n \"-s\",\n store,\n \"remember\",\n \"--replace\",\n serializeTurnMetrics(existing),\n \"-p\",\n `metrics.turn.${branchKey}`,\n ],\n { cwd: store },\n );\n }\n\n // Check writes for this session's pair\n const writes: Promise<MemoirResult>[] = [];\n if (codeWrite) writes.push(codeWrite);\n if (turnWrite) writes.push(turnWrite);\n const results = await Promise.all(writes);\n for (const result of results) {\n if (!result.ok) {\n throw new Error(`Memoir write failed: ${result.error}`);\n }\n }\n }\n } catch (e) {\n // Restore swapped data into pending maps so the next flush retries it.\n // Entries accumulated during I/O by concurrent recordEdit/recordToolMetrics\n // are preserved: we prepend the original entries, so ordering is maintained.\n for (const [sid, idx] of swappedSids) {\n const p = perSession[idx];\n const currentEdits = pendingEditsBySession.get(sid) ?? [];\n pendingEditsBySession.set(sid, [...p.edits, ...currentEdits]);\n\n const merged = toolMetricsBySession.get(sid) ?? new Map();\n for (const [tool, m] of p.metrics) {\n const prev = merged.get(tool) ?? { calls: 0, errors: 0 };\n merged.set(tool, { calls: prev.calls + m.calls, errors: prev.errors + m.errors });\n }\n toolMetricsBySession.set(sid, merged);\n }\n // Re-throw so the outer catch logs it\n throw e;\n }\n } finally {\n releaseLock();\n }\n } catch (e) {\n debugLog(\"flushCapture: failed:\", errorMessage(e));\n }\n}\n", "/**\n * Whether secret sanitization is enabled. Default on. Set MEMOIR_SANITIZE_SECRETS=0 to disable.\n *\n * NOTE: Do not re-export SECRET_PATTERN from this module \u2014 callers should import\n * it directly from utils.ts to keep the dependency chain flat.\n */\nexport function isSecretSanitizationEnabled(): boolean {\n return process.env.MEMOIR_SANITIZE_SECRETS !== \"0\";\n}\n\n/**\n * Sessions waiting for a recall instruction (one-shot, consumed by\n * experimental.chat.system.transform on the next LLM call).\n */\nexport const pendingRecall = new Set<string>();\n\n/** Short acknowledgements that never trigger recall. */\nconst ACK_PATTERN =\n /^(ok|thanks|thank you|sounds good|got it|\uD83D\uDC4D|\uD83D\uDE4F|perfect|great|cool|nice|awesome|understood|makes sense|agree|right|sure|yes|no|done|nvm|never mind|lgtm|looks good|proceed|continue|good|fine)\\b/i;\n\n/** Explicit memoir commands always trigger recall regardless of length. */\nconst EXPLICIT_RECALL_PATTERN =\n /\\b(memoir:recall|memoir:remember|memoir-recall|memoir-remember)\\b|(\\/recall|\\/remember)\\b/i;\n\n/** Positive-list patterns \u2014 identical to the Claude Code UserPromptSubmit gate. */\nconst RECALL_TRIGGER_PATTERNS = [\n // Action verbs and domain nouns\n /\\b(add|build|implement|refactor|redesign|design|create|write|set( |-)up|wire( |-)up|integrate|migrate|rewrite|extract|extend|plumb|hook( |-)up|ship|scaffold|optimize|fix|debug|review|architect|model|schema|API|service|feature|module|system|pipeline|workflow|make|move|replace|convert|swap|remove|clean( |-)up|transform|investigate|explore|figure( |-)out|plan|decide|choose|pick|compare|walk( |-)?me( |-)?through|take( |-)?a( |-)?stab|help( |-)?me|harness|hook|prompt|test)\\b/i,\n // Question starts (how/why/what/where/when/should/can/could/would/is it/are we/do I/does it)\n /^(how|why|what|where|when|should|can|could|would|is it|are we|do I|does it)\\b.*\\?/im,\n // Code blocks (triple backticks)\n /```/,\n // Code definitions\n /\\b(def|function|class|import|export)\\s+/,\n // Memoir/recall keywords\n /\\b(memoir|recall|remember|memory)\\b/i,\n // File extensions\n /\\b\\w+\\.(py|js|ts|tsx|scala|java|go|rs|rb|md|json|yaml|yml|toml|sh|bash|css|html|kt|swift|c|cpp|h|hpp)\\b/i,\n // File paths (slash-containing tokens)\n /\\w+\\/\\w+/,\n];\n\nexport function isAcknowledgement(text: string): boolean {\n const trimmed = text.trim().toLowerCase();\n const words = trimmed.split(/\\s+/);\n return words.length <= 5 && ACK_PATTERN.test(trimmed);\n}\n\n/**\n * Decide whether a user message should trigger a recall instruction.\n * Identical gate logic to the Claude Code UserPromptSubmit hook:\n *\n * 1. Empty text \u2192 skip\n * 2. Explicit `/recall` / `memoir:recall` commands \u2192 always fire\n * 3. < 10 chars \u2192 skip (empty or noise)\n * 4. Acknowledgements (ok/thanks/\u2026) \u2192 skip\n * 5. < 40 chars without explicit command \u2192 skip (too short for intent)\n * 6. \u2265 40 chars + any trigger pattern \u2192 fire\n */\nexport function shouldTriggerRecall(text: string): boolean {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Explicit memoir commands fire regardless of length\n if (EXPLICIT_RECALL_PATTERN.test(trimmed)) return true;\n if (trimmed.length < 10) return false;\n if (isAcknowledgement(trimmed)) return false;\n // Gate: \u2265 40 chars + any trigger\n if (trimmed.length >= 40 && RECALL_TRIGGER_PATTERNS.some((p) => p.test(trimmed))) return true;\n return false;\n}\n\nexport const DEFAULT_RECALL_NAMESPACES = [\"default\", \"project:onboard\", \"codebase:onboard\"];\n", "import {\n EDIT_TOOLS,\n flushCapture,\n getCachedBranch,\n recordEdit,\n recordToolMetrics,\n setCachedBranch,\n} from \"./capture.js\";\nimport { debugLog } from \"./debug.js\";\nimport { pendingRecall, shouldTriggerRecall } from \"./recall-gate.js\";\nimport { autoMatchMemoirBranch } from \"./store.js\";\nimport { errorMessage } from \"./utils.js\";\n\nexport async function handleShellEnv(\n storeRoot: string,\n _input: unknown,\n output: { env: Record<string, string> },\n): Promise<void> {\n try {\n output.env.MEMOIR_STORE = storeRoot;\n } catch (e) {\n debugLog(\"shell.env: failed:\", errorMessage(e));\n }\n}\n\nexport async function handleToolExecuteAfter(input: unknown, output: unknown): Promise<void> {\n try {\n const typedInput = input as\n | { tool?: string; sessionID?: string; callID?: string; args?: Record<string, unknown> }\n | undefined;\n const typedOutput = output as { metadata?: { error?: string }; output?: string } | undefined;\n // Per-session state key\n const sid = typedInput?.sessionID ?? \"default\";\n\n // Accumulate per-tool metrics (cf. collect-metrics.sh).\n // Skipped when MEMOIR_NO_CAPTURE or MEMOIR_NO_METRICS is set.\n if (process.env.MEMOIR_NO_CAPTURE !== \"1\" && process.env.MEMOIR_NO_METRICS !== \"1\") {\n const calls = 1;\n const errors =\n typedOutput?.metadata?.error || typedOutput?.output?.startsWith(\"Error:\") ? 1 : 0;\n recordToolMetrics(sid, typedInput?.tool ?? \"\", { calls, errors });\n }\n\n // Track file edits (cf. collect-edits.sh).\n // Skipped when MEMOIR_NO_CAPTURE or MEMOIR_NO_CODE_SUMMARY is set.\n if (\n process.env.MEMOIR_NO_CAPTURE !== \"1\" &&\n process.env.MEMOIR_NO_CODE_SUMMARY !== \"1\" &&\n EDIT_TOOLS.has(typedInput?.tool ?? \"\")\n ) {\n const args = typedInput?.args ?? {};\n const filePath =\n typeof args.filePath === \"string\"\n ? args.filePath\n : typeof args.path === \"string\"\n ? args.path\n : \"\";\n if (filePath) {\n recordEdit(sid, {\n tool: typedInput?.tool ?? \"\",\n filePath,\n snippet: \"\",\n timestamp: Date.now(),\n });\n }\n }\n } catch (e) {\n debugLog(\"tool.execute.after: failed:\", errorMessage(e));\n }\n}\n\nexport async function handleChatMessage(\n storeRoot: string,\n input: unknown,\n output: unknown,\n): Promise<void> {\n try {\n const typedInput = input as { sessionID?: string } | undefined;\n const typedOutput = output as { parts?: Array<{ type: string; text: string }> } | undefined;\n const sid = typedInput?.sessionID ?? \"default\";\n\n // C4: run recall gate BEFORE any await \u2014 otherwise system.transform\n // could fire before pendingRecall is set.\n const text = (typedOutput?.parts ?? [])\n .filter((p): p is { type: \"text\"; text: string } => p.type === \"text\")\n .map((p) => p.text)\n .join(\" \");\n if (shouldTriggerRecall(text)) {\n pendingRecall.add(sid);\n }\n\n // Snapshot previous branch BEFORE switching \u2014 edits from the last turn\n // belong to the OLD branch. (C2 fix: prevents misattribution when the\n // user switches git branch between turns.)\n const _cachedBranch = getCachedBranch(sid);\n const prevBranch = _cachedBranch === \"unknown\" ? undefined : _cachedBranch;\n setCachedBranch(sid, await autoMatchMemoirBranch(storeRoot));\n\n // Skip capture flush when MEMOIR_NO_CAPTURE is set.\n // Flush under the PREVIOUS branch (the one edits were made on).\n if (process.env.MEMOIR_NO_CAPTURE !== \"1\") {\n await flushCapture(storeRoot, prevBranch, sid);\n }\n } catch (e) {\n debugLog(\"chat.message: failed:\", errorMessage(e));\n }\n}\n\nexport async function flushAllCapture(storeRoot: string): Promise<void> {\n await flushCapture(storeRoot);\n}\n", "import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { debugLog } from \"./debug.js\";\nimport {\n codeGitBranch,\n ensureStore,\n memoirResolved,\n memoirSpawnSpecs,\n reorderResolver,\n runMemoir,\n} from \"./store.js\";\nimport { errorMessage } from \"./utils.js\";\n\n/** Timeout (ms) for UI URL detection polling. */\nconst UI_URL_DEADLINE_MS = 5_000;\n\n/** Polling interval (ms) for UI URL detection. */\nconst UI_URL_POLL_MS = 100;\n\n/** Ensure the memoir store exists, returning an error message on failure. */\nexport async function ensureStoreOrError(store: string): Promise<string | null> {\n try {\n await ensureStore(store);\n return null;\n } catch (error) {\n return errorMessage(error);\n }\n}\n\nexport async function statusJson(store: string): Promise<string> {\n const storeErr = await ensureStoreOrError(store);\n if (storeErr) return `Memoir command failed: ${storeErr}`;\n const result = await runMemoir([\"--json\", \"-s\", store, \"status\"], { cwd: store });\n if (!result.ok) return result.error;\n try {\n const data = JSON.parse(result.stdout);\n const branch = await codeGitBranch();\n data.opencode = { store, project_git_root: process.cwd(), project_git_branch: branch };\n return JSON.stringify(data, null, 2);\n } catch (e) {\n debugLog(\"statusJson: failed to parse JSON:\", errorMessage(e));\n return result.stdout;\n }\n}\n\nexport async function launchUi(store: string): Promise<string> {\n await ensureStore(store);\n const pidDir = join(homedir(), \".memoir\", \"ui-servers\");\n await mkdir(pidDir, { recursive: true });\n const hash = createHash(\"sha256\").update(store).digest(\"hex\").slice(0, 8);\n const pidfile = join(pidDir, `${hash}.json`);\n\n try {\n const existing = JSON.parse(await readFile(pidfile, \"utf8\"));\n if (existing?.pid && existing?.url) {\n try {\n process.kill(Number(existing.pid), 0); // check if alive\n return JSON.stringify({ ...existing, reused: true }, null, 2);\n } catch (e) {\n debugLog(\"launchUi: process dead, relaunching:\", errorMessage(e));\n // process is dead \u2014 fall through to relaunch\n }\n }\n } catch (e) {\n debugLog(\"launchUi: failed to read pidfile:\", errorMessage(e));\n await rm(pidfile, { force: true }).catch((e) =>\n debugLog(\"launchUi: failed to remove stale pidfile\", errorMessage(e)),\n );\n }\n\n let lastError = \"\";\n let uiSpecs = memoirSpawnSpecs([\"ui\", store]);\n // Reorder: put cached memoir resolver first, same as runMemoir\n if (memoirResolved) {\n const winner = uiSpecs.find((s) => s.label === memoirResolved);\n if (winner) uiSpecs = reorderResolver(uiSpecs, winner);\n }\n for (const spec of uiSpecs) {\n const child = spawn(spec.command, spec.args, {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // Scrub process.env \u2014 only forward whitelisted vars to avoid leaking secrets\n env: Object.fromEntries(\n Object.entries({\n PATH: process.env.PATH,\n MEMOIR_STORE: process.env.MEMOIR_STORE,\n MEMOIR_DEBUG: process.env.MEMOIR_DEBUG,\n }).filter(([, v]) => v != null),\n ) as Record<string, string>,\n });\n\n let output = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n output += String(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n output += String(chunk);\n });\n\n const spawnFailed = new Promise<string | null>((resolve) => {\n child.once(\"error\", (error) => resolve(errorMessage(error)));\n child.once(\"spawn\", () => resolve(null));\n });\n const error = await spawnFailed;\n if (error) {\n lastError = `${spec.label}: ${error}`;\n continue;\n }\n\n child.unref();\n\n const urlPattern = /https?:\\/\\/(?:localhost|127\\.0\\.0\\.1):\\d+\\S*/;\n const deadline = Date.now() + UI_URL_DEADLINE_MS;\n while (Date.now() < deadline) {\n const match = output.match(urlPattern);\n if (match) {\n const url = match[0];\n const data = {\n pid: child.pid,\n url,\n store,\n command: spec.label,\n started: new Date().toISOString(),\n reused: false,\n };\n await writeFile(pidfile, JSON.stringify(data, null, 2));\n return JSON.stringify(data, null, 2);\n }\n await new Promise((resolve) => setTimeout(resolve, UI_URL_POLL_MS));\n }\n\n return `Memoir UI started with ${spec.label} (pid ${child.pid ?? \"unknown\"}), but URL was not detected yet.\\n${output.trim()}`.trim();\n }\n\n return `Memoir UI failed to start: ${lastError || \"no launcher succeeded\"}`;\n}\n\n/** List memoir branches with unmerged changes relative to main. */\nexport async function unmergedBranchesText(store: string): Promise<string> {\n await ensureStore(store);\n const branchResult = await runMemoir([\"--json\", \"-s\", store, \"branch\"], { cwd: store });\n if (!branchResult.ok) {\n return branchResult.error;\n }\n const data = JSON.parse(branchResult.stdout);\n const branches: string[] = data?.branches ?? [];\n const unmerged: string[] = [];\n for (const branch of branches) {\n if (branch === \"main\") continue;\n const diffResult = await runMemoir([\"-s\", store, \"diff\", branch, \"main\", \"--stat\"], {\n cwd: store,\n });\n if (!diffResult.ok) {\n debugLog(\"command.execute.before: memoir diff failed\", diffResult.error);\n continue;\n }\n if (diffResult.stdout.trim()) {\n unmerged.push(branch);\n }\n }\n return unmerged.length > 0\n ? `Unmerged branches:\\n${unmerged.join(\"\\n\")}`\n : \"All branches are merged into main.\";\n}\n", "import type { Config } from \"@opencode-ai/plugin\";\nimport { launchUi, statusJson, unmergedBranchesText } from \"./memoir-ops.js\";\nimport { isSecretSanitizationEnabled } from \"./recall-gate.js\";\nimport { errorMessage, SECRET_PATTERN } from \"./utils.js\";\n\nexport type CommandOutput = {\n parts: unknown[];\n};\n\nexport type OpenCodeConfig = Config & {\n command?: Config[\"command\"];\n};\n\nfunction pushText(output: CommandOutput, text: string): void {\n output.parts.length = 0;\n output.parts.push({ type: \"text\", text });\n}\n\nexport function registerCommands(config: OpenCodeConfig): void {\n config.command = config.command ?? {};\n\n config.command[\"memoir:status\"] = {\n description: \"Show Memoir status for the current OpenCode project\",\n template: \"Show Memoir status for this OpenCode project.\",\n };\n\n config.command[\"memoir:ui\"] = {\n description: \"Launch or reopen the Memoir web UI for this project store\",\n template: \"Launch or reopen the Memoir UI for this project.\",\n };\n\n config.command[\"memoir:remember\"] = {\n description: \"Save a durable fact, preference, rule, or decision to Memoir\",\n template: `Use Memoir to save this durable memory now.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nExtract the memory content, choose a semantic path if none is supplied, then call the memoir_remember tool. Never save secrets.`,\n };\n\n config.command[\"memoir:recall\"] = {\n description: \"Recall relevant facts from Memoir before answering\",\n template: `Recall relevant Memoir memories for this request.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nUse memoir_recall first. It checks default plus onboard namespaces unless a namespace is specified. Then call memoir_get with the matching namespace for exact values before answering.`,\n };\n\n config.command[\"memoir:onboard\"] = {\n description: \"Populate or refresh Memoir onboarding for this project\",\n template: `Populate or refresh Memoir onboarding for the CURRENT OpenCode project only.\\n\\nUSER REQUEST:\\n$ARGUMENTS\\n\\nWorkflow:\\n- Stay inside the current project/worktree. Do not inspect parent directories.\\n- First obtain a project file tree to understand structure.\\n- Start studying from project documentation.\\n- Continue only based on what the tree and documentation show.\\n\\nMemory rules:\\n- Record only verified facts from files/docs/code or explicit user statements.\\n- Do not write inferred user thoughts, intentions, preferences, or opinions.\\n- Do not use preferences.* paths unless the user explicitly stated a preference.\\n- If a fact is your interpretation, do not save it; report it as uncertain instead.\\n\\nThen call memoir_remember with replace=true for durable onboarding facts. Use namespace codebase:onboard in git repositories and project:onboard outside git. Do not install or invoke separate skills/scripts.`,\n };\n\n config.command[\"memoir:unmerged\"] = {\n description: \"List memoir branches with changes not yet merged into main\",\n template: \"List memoir branches that have diverged from main.\",\n };\n}\n\nexport async function handleCommandExecuteBefore(\n storeRoot: string,\n input: { command?: string },\n output: CommandOutput,\n): Promise<void> {\n try {\n if (input.command === \"memoir:status\") {\n pushText(output, await statusJson(storeRoot));\n }\n if (input.command === \"memoir:ui\") {\n pushText(output, await launchUi(storeRoot));\n }\n if (input.command === \"memoir:remember\") {\n const args = (input as { arguments?: string }).arguments;\n if (args && isSecretSanitizationEnabled() && SECRET_PATTERN.test(args)) {\n pushText(\n output,\n \"Memoir: cannot remember content that matches secret patterns. Please remove sensitive data and try again.\",\n );\n }\n }\n if (input.command === \"memoir:unmerged\") {\n pushText(output, await unmergedBranchesText(storeRoot));\n }\n } catch (error) {\n pushText(output, `Memoir command failed: ${errorMessage(error)}`);\n }\n}\n", "import { debugLog } from \"./debug.js\";\nimport { pendingRecall } from \"./recall-gate.js\";\nimport { runMemoir } from \"./store.js\";\nimport { errorMessage, tryPrettyJson } from \"./utils.js\";\n\n/** Cached session init context (taxonomy overview). Fetched once, injected per-session. */\nlet initContext: string | null = null;\nlet initContextFetched = false;\nconst MAX_TAXONOMY_RETRIES = 3;\nlet taxonomyFetchRetries = 0;\n/**\n * Tracks sessions that have received initial context injection.\n * Uses a Map with timestamp so stale entries can be evicted.\n * Cleaned on dispose. Sessions older than 1 hour are considered stale.\n */\nconst SESSION_CONTEXT_TTL_MS = 60 * 60 * 1000;\nconst sessionsWithContext = new Map<string, number>();\n\n/** Check if a session has received context (and prune stale entries opportunistically). */\nfunction hasSessionContext(sessionID: string): boolean {\n const ts = sessionsWithContext.get(sessionID);\n if (ts === undefined) return false;\n if (Date.now() - ts > SESSION_CONTEXT_TTL_MS) {\n sessionsWithContext.delete(sessionID);\n return false;\n }\n return true;\n}\n\n/** Mark a session as having received context. */\nfunction markSessionContext(sessionID: string): void {\n sessionsWithContext.set(sessionID, Date.now());\n}\n\n/** Prune all stale session entries. */\nfunction pruneStaleSessions(): void {\n const now = Date.now();\n for (const [id, ts] of sessionsWithContext) {\n if (now - ts > SESSION_CONTEXT_TTL_MS) sessionsWithContext.delete(id);\n }\n}\n\nexport async function handleEvent(\n storeRoot: string,\n input: { event: { type: string } },\n): Promise<void> {\n if (input.event.type === \"session.created\" && !initContextFetched) {\n initContextFetched = true;\n fetchTaxonomy(storeRoot).catch((e: unknown) => {\n debugLog(\"event: taxonomy fetch failed, will retry on next session:\", errorMessage(e));\n taxonomyFetchRetries++;\n if (taxonomyFetchRetries >= MAX_TAXONOMY_RETRIES) {\n debugLog(\"memoir: taxonomy fetch failed after max retries, giving up\");\n return;\n }\n // Allow a later session to retry \u2014 the store may not have been ready yet.\n initContextFetched = false;\n });\n }\n}\n\nasync function fetchTaxonomy(storeRoot: string): Promise<void> {\n const taxonomyResult = await runMemoir(\n [\"--json\", \"-s\", storeRoot, \"summarize\", \"--depth\", \"3\", \"-n\", \"default\"],\n { cwd: storeRoot },\n );\n if (!taxonomyResult.ok) {\n throw new Error(taxonomyResult.error);\n }\n const pretty = tryPrettyJson(taxonomyResult.stdout);\n initContext = `[memoir] Available taxonomy paths:\\n${pretty}`;\n taxonomyFetchRetries = 0;\n}\n\nexport async function handleSystemTransform(input: unknown, output: unknown): Promise<void> {\n try {\n const typedInput = input as { sessionID?: string } | undefined;\n const typedOutput = output as { system?: string[] } | undefined;\n\n // Inject initial context on the first LLM call of each session\n if (typedInput?.sessionID && !hasSessionContext(typedInput.sessionID)) {\n markSessionContext(typedInput.sessionID);\n if (initContext) {\n typedOutput?.system?.unshift(initContext);\n }\n }\n\n // Inject recall instruction (one-shot per trigger)\n if (typedInput?.sessionID && pendingRecall.has(typedInput.sessionID)) {\n pendingRecall.delete(typedInput.sessionID);\n typedOutput?.system?.push(\n \"\\n[memoir] The user may have relevant context in Memoir. Run memoir_recall to list available memories across default and onboard namespaces, then memoir_get with the matching namespace to fetch exact values.\",\n );\n }\n } catch (e) {\n debugLog(\"system.transform: failed:\", errorMessage(e));\n }\n}\n\nexport function clearSessionContext(): void {\n pruneStaleSessions();\n sessionsWithContext.clear();\n}\n", "import { tool } from \"@opencode-ai/plugin\";\nimport { ensureStoreOrError, statusJson } from \"./memoir-ops.js\";\nimport { DEFAULT_RECALL_NAMESPACES, isSecretSanitizationEnabled } from \"./recall-gate.js\";\nimport { deriveStorePath, runMemoir } from \"./store.js\";\nimport { coercePaths, MEMOIR_GET_MAX_KEYS, SECRET_PATTERN, tryPrettyJson } from \"./utils.js\";\n\ntype MemoirRememberArgs = {\n content: string;\n path?: string | string[];\n namespace?: string;\n replace?: boolean;\n};\n\ntype MemoirRecallArgs = {\n query?: string;\n namespace?: string;\n namespaces?: string[];\n includeMetrics?: boolean;\n};\n\ntype MemoirGetArgs = {\n keys: string[];\n namespace?: string;\n};\n\nconst memoirStatus = tool({\n description: \"Show Memoir status for the current OpenCode project store.\",\n args: {},\n execute: async () => statusJson(deriveStorePath()),\n});\n\nconst memoirRemember = tool({\n description: \"Explicitly save a durable memory to Memoir at one or more semantic taxonomy paths.\",\n args: {\n content: tool.schema.string().describe(\"Memory content to save. Do not include secrets.\"),\n path: tool.schema\n .string()\n .optional()\n .describe(\"Semantic taxonomy path, e.g. preferences.coding.style.\"),\n namespace: tool.schema.string().optional().describe(\"Memoir namespace. Defaults to default.\"),\n replace: tool.schema.boolean().optional().describe(\"Replace existing value at the path.\"),\n },\n execute: async (args: MemoirRememberArgs) => {\n const content = args.content?.trim();\n if (!content) return \"Memoir memory was not saved: content is empty.\";\n if (isSecretSanitizationEnabled() && SECRET_PATTERN.test(content)) {\n return \"Memoir memory was not saved: the content looks like a secret or credential. Save a redacted rule instead.\";\n }\n const paths = coercePaths(args.path);\n if (paths.length === 0) {\n return \"Memoir memory was not saved: provide a semantic path, e.g. preferences.coding.style.\";\n }\n\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const cliArgs = [\"--json\", \"-s\", store, \"remember\", content];\n for (const p of paths) cliArgs.push(\"-p\", p);\n cliArgs.push(\"-n\", args.namespace ?? \"default\");\n if (args.replace) cliArgs.push(\"--replace\");\n const rememberResult = await runMemoir(cliArgs, { cwd: store });\n if (!rememberResult.ok) return rememberResult.error;\n return tryPrettyJson(rememberResult.stdout);\n },\n});\n\nconst memoirRecall = tool({\n description:\n \"List Memoir memory keys across relevant namespaces for relevance picking. Never calls legacy memoir recall.\",\n args: {\n query: tool.schema.string().optional().describe(\"User query or topic to recall for.\"),\n namespace: tool.schema\n .string()\n .optional()\n .describe(\n \"Single Memoir namespace to inspect. If omitted, checks default + onboard namespaces.\",\n ),\n namespaces: tool.schema\n .array(tool.schema.string())\n .optional()\n .describe(\"Namespaces to inspect. Defaults to default, project:onboard, codebase:onboard.\"),\n includeMetrics: tool.schema\n .boolean()\n .optional()\n .describe(\"Include metrics.* memories in the listing.\"),\n },\n execute: async (args: MemoirRecallArgs) => {\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const namespaces = args.namespace\n ? [args.namespace]\n : args.namespaces && args.namespaces.length > 0\n ? args.namespaces\n : DEFAULT_RECALL_NAMESPACES;\n const sections: string[] = [];\n for (const namespace of namespaces) {\n const summarizeResult = await runMemoir(\n [\"--json\", \"-s\", store, \"summarize\", \"--depth\", \"3\", \"-n\", namespace],\n { cwd: store },\n );\n if (!summarizeResult.ok) return summarizeResult.error;\n const output = tryPrettyJson(summarizeResult.stdout);\n sections.push(`## namespace: ${namespace}\\n${output}`);\n }\n const note = args.includeMetrics\n ? \"Metrics were included by request.\"\n : \"Ignore metrics.* and taxonomy:v1:* entries unless explicitly needed. If default is empty or only metrics, inspect project:onboard/codebase:onboard before concluding there is no memory.\";\n const query = args.query ? `Query: ${args.query}\\n` : \"\";\n return `${query}${note}\\nPick at most 5-7 relevant exact keys across namespaces, then call memoir_get with the matching namespace if values are needed.\\n${sections.join(\"\\n\\n\")}`;\n },\n});\n\nconst memoirGet = tool({\n description: \"Fetch exact Memoir memory keys after selecting them from memoir_recall output.\",\n args: {\n keys: tool.schema.array(tool.schema.string()).describe(\"Exact memory keys to fetch.\"),\n namespace: tool.schema.string().optional().describe(\"Memoir namespace. Defaults to default.\"),\n },\n execute: async (args: MemoirGetArgs) => {\n const keys = args.keys?.map((key) => key.trim()).filter(Boolean) ?? [];\n if (keys.length === 0) return \"No Memoir keys were provided.\";\n if (keys.length > MEMOIR_GET_MAX_KEYS) {\n return `Error: Too many keys requested (max ${MEMOIR_GET_MAX_KEYS}, got ${keys.length}). Narrow your selection from memoir_recall output.`;\n }\n\n const store = deriveStorePath();\n const storeError = await ensureStoreOrError(store);\n if (storeError) return storeError;\n\n const getResult = await runMemoir(\n [\"--json\", \"-s\", store, \"get\", ...keys, \"-n\", args.namespace ?? \"default\"],\n { cwd: store },\n );\n if (!getResult.ok) return getResult.error;\n return tryPrettyJson(getResult.stdout);\n },\n});\n\nexport const memoirTools = {\n memoir_status: memoirStatus,\n memoir_remember: memoirRemember,\n memoir_recall: memoirRecall,\n memoir_get: memoirGet,\n};\n", "import type { Plugin, PluginModule } from \"@opencode-ai/plugin\";\nimport {\n flushAllCapture,\n handleChatMessage,\n handleShellEnv,\n handleToolExecuteAfter,\n} from \"./capture-hooks.js\";\nimport { handleCommandExecuteBefore, registerCommands } from \"./commands.js\";\nimport { debugLog } from \"./debug.js\";\nimport { clearSessionContext, handleEvent, handleSystemTransform } from \"./session-context.js\";\nimport { deriveStorePath, setPluginStoreOverride } from \"./store.js\";\nimport { memoirTools } from \"./tools.js\";\nimport { errorMessage } from \"./utils.js\";\n\nconst MemoirOpenCode: Plugin = async (_input, rawOptions) => {\n const opts = (rawOptions ?? {}) as { store?: string };\n if (opts.store) setPluginStoreOverride(opts.store);\n\n // Resolve store path once at init so shell.env doesn't call execFileSync\n // on every shell command (mirrors Claude Code's MEMOIR_STORE_PATH caching).\n const storeRoot = deriveStorePath();\n\n return {\n name: \"memoir\",\n tool: { ...memoirTools },\n config: async (opencodeConfig: Record<string, unknown>) => {\n registerCommands(opencodeConfig as Parameters<typeof registerCommands>[0]);\n },\n \"command.execute.before\": async (input: { command?: string }, output: { parts: unknown[] }) => {\n await handleCommandExecuteBefore(storeRoot, input, output);\n },\n\n /**\n * Inject MEMOIR_STORE into every shell command's environment so any memoir\n * invocation automatically targets the right store without manual -s flags.\n * Uses the cached value resolved at init time (avoiding execFileSync overhead\n * on every shell command).\n */\n \"shell.env\": async (_input, output) => {\n await handleShellEnv(storeRoot, _input, output as { env: Record<string, string> });\n },\n\n /**\n * Observe every tool execution for metrics and code-change tracking.\n * Mirrors the observation phase of Claude Code's Stop hook.\n * Never modifies the tool output.\n */\n \"tool.execute.after\": async (input, output) => {\n await handleToolExecuteAfter(input, output);\n },\n\n /**\n * Fires on every incoming user message.\n *\n * 1. Auto-match memoir branch to current git branch\n * (cf. UserPromptSubmit's auto_match_memoir_branch in Claude Code plugin).\n * 2. Flush pending edits from the previous assistant turn\n * (cf. Stop hook code change audit, run after each turn).\n * 3. Run the recall gate (cf. UserPromptSubmit).\n *\n * Steps 1\u20132 are skipped when MEMOIR_NO_CAPTURE=1.\n */\n \"chat.message\": async (input, output) => {\n await handleChatMessage(storeRoot, input, output);\n },\n\n /**\n * Fires on SDK events. On session.created, kick off a background fetch of\n * the taxonomy overview so it's ready before the first LLM call.\n * Mirrors Claude Code's SessionStart context injection.\n */\n event: async (input: { event: { type: string } }) => {\n await handleEvent(storeRoot, input);\n },\n\n /**\n * Fires before every LLM call.\n *\n * 1. Injects the memoir taxonomy context for this session (once per session).\n * 2. If a recall is pending for this session, injects a brief instruction\n * telling the model to use memoir tools (one-shot per trigger).\n */\n \"experimental.chat.system.transform\": async (input, output) => {\n await handleSystemTransform(input, output);\n },\n\n /**\n * Fires when the plugin is shut down. Flushes any remaining code changes\n * and metrics (cf. SessionEnd heartbeat cleanup + final metrics flush).\n */\n dispose: async () => {\n try {\n // No sessionID \u2192 flushCapture flushes ALL sessions\n await flushAllCapture(storeRoot);\n clearSessionContext();\n } catch (e: unknown) {\n const msg = `memoir: dispose error: ${errorMessage(e)}`;\n debugLog(msg);\n console.error(msg);\n }\n },\n };\n};\n\n/**\n * V1 plugin module format: OpenCode's loader recognizes `{ id, server }` and\n * ignores any other module exports. The legacy format (default-exported\n * function) made the loader treat EVERY export as a plugin and fail on\n * non-function exports (\"Plugin export is not a function\").\n */\nconst plugin: PluginModule = {\n id: \"opencode-memoir\",\n server: MemoirOpenCode,\n};\n\nexport default plugin;\n"],
5
+ "mappings": "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",
6
+ "names": ["result", "tool", "tool", "resolve", "mkdir", "rm", "homedir", "join", "join", "homedir", "mkdir", "rm", "e", "resolve", "error", "_input"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"memoir-ops.d.ts","sourceRoot":"","sources":["../src/memoir-ops.ts"],"names":[],"mappings":"AAeA,6EAA6E;AAC7E,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAO9E;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc/D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsE7D;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBzE"}
1
+ {"version":3,"file":"memoir-ops.d.ts","sourceRoot":"","sources":["../src/memoir-ops.ts"],"names":[],"mappings":"AAsBA,6EAA6E;AAC7E,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAO9E;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc/D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0F7D;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzE"}
@@ -1,5 +1,9 @@
1
- export { SECRET_PATTERN } from './utils.js';
2
- /** Whether secret sanitization is enabled. Default on. Set MEMOIR_SANITIZE_SECRETS=0 to disable. */
1
+ /**
2
+ * Whether secret sanitization is enabled. Default on. Set MEMOIR_SANITIZE_SECRETS=0 to disable.
3
+ *
4
+ * NOTE: Do not re-export SECRET_PATTERN from this module — callers should import
5
+ * it directly from utils.ts to keep the dependency chain flat.
6
+ */
3
7
  export declare function isSecretSanitizationEnabled(): boolean;
4
8
  /**
5
9
  * Sessions waiting for a recall instruction (one-shot, consumed by
@@ -1 +1 @@
1
- {"version":3,"file":"recall-gate.d.ts","sourceRoot":"","sources":["../src/recall-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,oGAAoG;AACpG,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,aAAoB,CAAC;AA0B/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUzD;AAED,eAAO,MAAM,yBAAyB,UAAqD,CAAC"}
1
+ {"version":3,"file":"recall-gate.d.ts","sourceRoot":"","sources":["../src/recall-gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,aAAoB,CAAC;AA4B/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUzD;AAED,eAAO,MAAM,yBAAyB,UAAqD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../src/session-context.ts"],"names":[],"mappings":"AAwCA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtG;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB1F;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
1
+ {"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../src/session-context.ts"],"names":[],"mappings":"AA0CA,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAcf;AAeD,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB1F;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
package/dist/store.d.ts CHANGED
@@ -25,10 +25,18 @@ export type MemoirResult = {
25
25
  resolver?: string;
26
26
  };
27
27
  /**
28
- * Cache of cwds confirmed to be outside a git repo.
29
- * Avoids redundant ~200ms failing `git rev-parse` calls on non-git directories.
28
+ * Cache of cwds confirmed to be outside a git repo, keyed by path with
29
+ * timestamp of when the entry was added.
30
+ * Avoids redundant ~200ms failing `git rev-parse` calls on non-git directories,
31
+ * while still allowing a directory that becomes a git repo (e.g. `git init`)
32
+ * to be retried after the TTL expires.
30
33
  */
31
- export declare const _noGitCache: Set<string>;
34
+ export declare const _noGitCache: Map<string, number>;
35
+ /**
36
+ * Check whether a cwd is cached as non-git and still within TTL.
37
+ * If the entry exists but is expired, it is removed from the cache.
38
+ */
39
+ export declare function _checkNoGit(cwd: string): boolean;
32
40
  /**
33
41
  * Resolve the main worktree root path for a git repository.
34
42
  * Mirrors _main_worktree_root from plugins/claude-code/scripts/derive-store-path.sh.
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAsHA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKvB,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AA6IA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-memoir",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Memoir memory plugin for OpenCode — Git-versioned, taxonomy-structured AI memory.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,7 +19,10 @@
19
19
  "build": "tsc -p tsconfig.json --declaration --emitDeclarationOnly && esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:node:* --external:@opencode-ai/plugin --sourcemap",
20
20
  "typecheck": "tsc -p tsconfig.json --noEmit",
21
21
  "test": "tsx --test tests/*.test.ts",
22
- "prepublishOnly": "npm run build && npm test && node -e \"import('./dist/index.js').then(m=>{const d=m.default;if(!d||typeof d!=='object'||typeof d.server!=='function'||d.id!=='opencode-memoir'){console.error('Smoke FAILED: default export is not a V1 plugin module ({ id, server })');process.exit(1)}else{console.log('Smoke OK: id=',d.id,'server=',typeof d.server)}})\""
22
+ "prepublishOnly": "npm run build && npm test && node -e \"import('./dist/index.js').then(m=>{const d=m.default;if(!d||typeof d!=='object'||typeof d.server!=='function'||d.id!=='opencode-memoir'){console.error('Smoke FAILED: default export is not a V1 plugin module ({ id, server })');process.exit(1)}else{console.log('Smoke OK: id=',d.id,'server=',typeof d.server)}})\"",
23
+ "lint": "biome check src/ tests/",
24
+ "lint:fix": "biome check --write src/ tests/",
25
+ "format": "biome format --write src/ tests/"
23
26
  },
24
27
  "keywords": [
25
28
  "opencode",
@@ -43,10 +46,11 @@
43
46
  "@opencode-ai/plugin": "^1.17.3"
44
47
  },
45
48
  "devDependencies": {
46
- "@types/node": "^22.0.0",
49
+ "@biomejs/biome": "^2.5.0",
50
+ "@types/node": "^25.9.3",
47
51
  "esbuild": "^0.28.0",
48
52
  "tsx": "^4.22.4",
49
- "typescript": "^5.7.0"
53
+ "typescript": "^6.0.3"
50
54
  },
51
55
  "engines": {
52
56
  "node": ">=20"