mempalace
Local-first memory for Claude Code. It stores conversations verbatim in a private palace you can search before answering about past work, people, projects, or decisions, with optional shared-brain hub setup.
What claude-subconscious runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
Claude Code
> /plugin marketplace add letta-ai/claude-subconscious> /plugin install claude-subconscious@claude-subconscious
Cursor
Cursor also runs Claude Code hooks from .claude/settings.json, so copy these hooks there to use them in Cursor.
What fires, and when
SessionStart
Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.
*node "${CLAUDE_PLUGIN_ROOT:-.}/hooks/silent-npx.cjs" tsx "${CLAUDE_PLUGIN_ROOT:-.}/scripts/session_start.ts"node "${CLAUDE_PLUGIN_ROOT:-.}/hooks/silent-npx.cjs" tsx "${CLAUDE_PLUGIN_ROOT:-.}/scripts/sync_letta_memory.ts"PreToolUse
*node "${CLAUDE_PLUGIN_ROOT:-.}/hooks/silent-npx.cjs" tsx "${CLAUDE_PLUGIN_ROOT:-.}/scripts/pretool_sync.ts"UserPromptSubmit
Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.
*node "${CLAUDE_PLUGIN_ROOT:-.}/hooks/silent-npx.cjs" tsx "${CLAUDE_PLUGIN_ROOT:-.}/scripts/sync_letta_memory.ts"Stop
*node "${CLAUDE_PLUGIN_ROOT:-.}/hooks/silent-npx.cjs" tsx "${CLAUDE_PLUGIN_ROOT:-.}/scripts/send_messages_to_letta.ts"Where it lives
# Build silent-launcher.exe from SilentLauncher.cs
# Requires .NET Framework csc.exe (ships with Windows)
#
# Usage: powershell -ExecutionPolicy Bypass -File hooks/build.ps1
$ErrorActionPreference = 'Stop'
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$cs = Join-Path $scriptDir 'SilentLauncher.cs'
$exe = Join-Path $scriptDir 'silent-launcher.exe'
# Find csc.exe from the .NET Framework directory
$csc = Join-Path $env:WINDIR 'Microsoft.NET\Framework64\v4.0.30319\csc.exe'
if (-not (Test-Path $csc)) {
$csc = Join-Path $env:WINDIR 'Microsoft.NET\Framework\v4.0.30319\csc.exe'
}
if (-not (Test-Path $csc)) {
Write-Error "csc.exe not found. Ensure .NET Framework 4.x is installed."
exit 1
}
Write-Host "Building silent-launcher.exe ..."
& $csc /nologo /out:$exe /platform:anycpu /target:winexe $cs
if ($LASTEXITCODE -eq 0) {
Write-Host "Built: $exe"
} else {
Write-Error "Build failed (exit code $LASTEXITCODE)"
exit $LASTEXITCODE
}
#!/usr/bin/env node
/**
* Cross-platform launcher for Claude Subconscious hooks.
*
* On Windows: delegates to silent-launcher.exe which creates a headless
* PseudoConsole (ConPTY) + CREATE_NO_WINDOW to eliminate console window
* flashes on Windows 11 / Windows Terminal.
*
* On other platforms: runs tsx directly via node — no console issue.
*
* Called from hooks.json as:
* node hooks/silent-npx.cjs tsx scripts/<script>.ts
*/
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const isWindows = process.platform === 'win32';
const args = process.argv.slice(2); // e.g. ['tsx', 'path/to/script.ts']
let child;
if (args[0] === 'tsx') {
let scriptArgs = args.slice(1); // everything after 'tsx'
const pluginRoot = path.resolve(__dirname, '..');
// Fix for #34: If CLAUDE_PLUGIN_ROOT was empty, script paths resolve to
// absolute paths like "/scripts/foo.ts" which don't exist. Re-resolve
// them relative to the plugin root (which we know from __dirname).
scriptArgs = scriptArgs.map(arg => {
if (!fs.existsSync(arg) && arg.includes('/scripts/')) {
const basename = path.basename(arg);
const resolved = path.join(pluginRoot, 'scripts', basename);
if (fs.existsSync(resolved)) return resolved;
}
return arg;
});
const tsxCli = path.join(pluginRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs');
if (isWindows) {
const silentLauncher = path.join(__dirname, 'silent-launcher.exe');
if (fs.existsSync(silentLauncher) && fs.existsSync(tsxCli)) {
// PseudoConsole + CREATE_NO_WINDOW: popup-free execution
child = spawn(silentLauncher, ['node', tsxCli, ...scriptArgs], {
stdio: 'inherit',
windowsHide: true,
});
} else if (fs.existsSync(tsxCli)) {
// Fallback: run tsx CLI directly (may flash on Windows Terminal)
child = spawn(process.execPath, [tsxCli, ...scriptArgs], {
stdio: 'inherit',
windowsHide: true,
});
} else {
// Last resort: npx through shell
child = spawn('npx', args, {
stdio: 'inherit',
shell: true,
windowsHide: true,
});
}
} else {
// Non-Windows: no console window issues
if (fs.existsSync(tsxCli)) {
child = spawn(process.execPath, [tsxCli, ...scriptArgs], {
stdio: 'inherit',
});
} else {
child = spawn('npx', args, {
stdio: 'inherit',
});
}
}
} else {
// Non-tsx command: use npx
child = spawn('npx', args, {
stdio: 'inherit',
shell: isWindows,
windowsHide: isWindows,
});
}
child.on('exit', (code) => {
process.exit(code || 0);
});
child.on('error', (err) => {
console.error('Failed to start subprocess:', err);
process.exit(1);
});
// Preload: delivers stdin from temp file via unshift, captures stdout to temp file.
// Loaded via --require in the node command line.
// Used with PseudoConsole + CREATE_NO_WINDOW where pipe I/O is not available.
'use strict';
const fs = require('fs');
const stdoutFile = process.env.SL_STDOUT_FILE;
const stdinFile = process.env.SL_STDIN_FILE;
// --- STDIN: Read from temp file, unshift onto existing Socket ---
if (stdinFile) {
try {
const data = fs.readFileSync(stdinFile);
if (data.length > 0) {
const sock = process.stdin;
sock.pause();
sock.unshift(data);
process.nextTick(() => sock.push(null));
}
} catch (e) { /* stdin file may not exist */ }
}
// --- STDOUT/STDERR: Capture all writes to temp file ---
if (stdoutFile) {
try {
const fd = fs.openSync(stdoutFile, 'a');
const origWrite = process.stdout.write.bind(process.stdout);
process.stdout.write = function(chunk, encoding, callback) {
try {
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8');
fs.writeSync(fd, buf);
} catch (e) { /* ignore write errors */ }
return origWrite(chunk, encoding, callback);
};
const origErrWrite = process.stderr.write.bind(process.stderr);
process.stderr.write = function(chunk, encoding, callback) {
try {
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8');
fs.writeSync(fd, buf);
} catch (e) { /* ignore write errors */ }
return origErrWrite(chunk, encoding, callback);
};
process.on('exit', () => {
try { fs.closeSync(fd); } catch(e) {}
});
} catch (e) { /* stdout setup error */ }
}
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
A background agent that whispers to Claude Code. A subconscious agent that watches your sessions, reads your files, builds up memory over time, and whispers guidance back.
Local-first memory for Claude Code. It stores conversations verbatim in a private palace you can search before answering about past work, people, projects, or decisions, with optional shared-brain hub setup.
Gives Claude Code a private memory that stays on your machine. It stores past conversations word for word, so you can ask what was decided, what happened before, or who was involved and get the original context back. It can also hand tasks to other agents.
Keeps large tool outputs, logs, and files out of Claude Code's context window by processing them in a sandbox and indexing them for focused search. Includes diagnostics and a knowledge base indexer.
Keeps large command outputs, logs and data files out of your AI assistant's context by running processing in a sandbox and returning only the relevant parts. You also get a searchable index of local files and diagnostics for checking the setup.
Gives coding agents a git-backed wiki for long-term project memory, with skills for retrieval, durable notes, session handoffs between agents, cross-project messaging, and memory consolidation.
A coding agent harness whose agents keep memory and identity across sessions, so they learn and improve over time. You can run it in a terminal, in the browser, or through Telegram, Slack and Discord.
Audits a Claude Code or Codex setup for context-window waste, fixes it with the user's approval, and measures the savings. It also tracks costs, helps resume prior sessions, and offers a usage dashboard.
A self-learning system for Claude Code that captures your corrections during sessions and reminds you to run /reflect to save them to CLAUDE.md as permanent memory. It also discovers recurring workflow patterns.