🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@remember-md/remember

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remember-md/remember - npm Package Compare versions

Comparing version
2.0.4
to
2.0.5
+1
-1
index.js

@@ -6,3 +6,3 @@ const fs = require('fs');

const MAX_EVIDENCE_LINES = 0;
const MAX_EVIDENCE_LINES = 20;

@@ -9,0 +9,0 @@ function truncateEvidence(persona) {

@@ -5,3 +5,3 @@ {

"description": "Portable knowledge base — one brain, every AI tool. Extract decisions, people, and insights from your AI sessions into organized local markdown.",
"version": "2.0.1",
"version": "2.0.5",
"repository": "github:remember-md/remember",

@@ -8,0 +8,0 @@ "skills": ["./skills"],

{
"name": "@remember-md/remember",
"version": "2.0.4",
"version": "2.0.5",
"description": "Portable knowledge base — one brain, every AI tool. Extract decisions, people, and insights from your AI sessions into organized local markdown.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -10,3 +10,3 @@ #!/usr/bin/env node

const BRAIN_ROOT = getBrainRoot();
const PROCESSED_FILE = path.join(BRAIN_ROOT, '.processed_sessions');
const STATE_DIR = path.join(process.env.XDG_STATE_HOME || path.join(os.homedir(), '.local', 'state'), 'remember');
const CLAUDE_PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');

@@ -16,7 +16,30 @@ const MAX_ASSISTANT_TEXT_LEN = loadConfig().extract?.max_assistant_text_len || 500;

// --- Source definitions ---
const SOURCES = {
'claude-code': {
processedFile: path.join(STATE_DIR, 'processed-claude-code'),
},
'openclaw': {
processedFile: path.join(STATE_DIR, 'processed-openclaw'),
},
};
function getDefaultSource() {
// Auto-detect: if OPENCLAW env or running inside OpenClaw, default to openclaw
if (process.env.OPENCLAW || process.env.OPENCLAW_WORKSPACE) return 'openclaw';
return 'claude-code';
}
// --- Session tracking ---
function getProcessedSessions() {
function getProcessedFile(source) {
const src = SOURCES[source];
if (!src) throw new Error(`Unknown source: ${source}. Valid: ${Object.keys(SOURCES).join(', ')}`);
return src.processedFile;
}
function getProcessedSessions(source) {
try {
return new Set(fs.readFileSync(PROCESSED_FILE, 'utf-8').trim().split('\n').filter(Boolean));
return new Set(fs.readFileSync(getProcessedFile(source), 'utf-8').trim().split('\n').filter(Boolean));
} catch {

@@ -27,9 +50,94 @@ return new Set();

function markSessionProcessed(sessionId) {
fs.mkdirSync(path.dirname(PROCESSED_FILE), { recursive: true });
fs.appendFileSync(PROCESSED_FILE, sessionId + '\n', 'utf-8');
function markSessionProcessed(sessionId, source) {
const file = getProcessedFile(source);
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.appendFileSync(file, sessionId + '\n', 'utf-8');
}
// --- Transcript discovery ---
// --- OpenClaw memory file discovery ---
function getOpenClawMemoryDir() {
// Check common locations for OpenClaw memory files
const candidates = [
process.env.OPENCLAW_WORKSPACE && path.join(process.env.OPENCLAW_WORKSPACE, 'memory'),
path.join(os.homedir(), '.openclaw', 'workspace', 'memory'),
].filter(Boolean);
for (const dir of candidates) {
if (fs.existsSync(dir)) return dir;
}
return null;
}
function findOpenClawMemoryFiles() {
const memDir = getOpenClawMemoryDir();
if (!memDir) return [];
const files = [];
for (const file of fs.readdirSync(memDir)) {
if (!file.endsWith('.md')) continue;
const filePath = path.join(memDir, file);
let stat;
try { stat = fs.statSync(filePath); } catch { continue; }
if (!stat.isFile()) continue;
files.push(filePath);
}
return files.sort();
}
function getOpenClawSessionId(filePath) {
return path.basename(filePath, '.md');
}
function extractOpenClawSession(filePath) {
const sessionId = getOpenClawSessionId(filePath);
const content = fs.readFileSync(filePath, 'utf-8');
// Extract date from filename (YYYY-MM-DD.md or YYYY-MM-DD-topic.md)
const dateMatch = sessionId.match(/^(\d{4}-\d{2}-\d{2})/);
const dateStr = dateMatch ? dateMatch[1] : '';
const firstTs = dateStr ? `${dateStr}T00:00:00Z` : null;
// Parse the markdown into pseudo-messages (sections become context)
const messages = [];
const lines = content.split('\n');
let currentSection = '';
let sectionContent = [];
for (const line of lines) {
const headingMatch = line.match(/^#{1,3}\s+(.+)/);
if (headingMatch) {
// Flush previous section
if (currentSection && sectionContent.length) {
const text = sectionContent.join('\n').trim();
if (text) {
messages.push({ role: 'user', text: `## ${currentSection}\n${text}`, time: '', timestamp: firstTs || '' });
}
}
currentSection = headingMatch[1];
sectionContent = [];
} else {
sectionContent.push(line);
}
}
// Flush last section
if (currentSection && sectionContent.length) {
const text = sectionContent.join('\n').trim();
if (text) {
messages.push({ role: 'user', text: `## ${currentSection}\n${text}`, time: '', timestamp: firstTs || '' });
}
}
return {
session_id: sessionId,
project: 'openclaw',
cwd: null,
first_ts: firstTs,
last_ts: firstTs,
spans_multiple_days: false,
session_date: firstTs,
messages,
};
}
// --- Claude Code transcript discovery ---
function findAllTranscripts() {

@@ -259,3 +367,3 @@ const transcripts = [];

function cmdExtract(filePath) {
function cmdExtract(filePath, source) {
if (!fs.existsSync(filePath)) {

@@ -265,20 +373,40 @@ process.stderr.write(`Error: file not found: ${filePath}\n`);

}
const session = extractSession(filePath);
const session = source === 'openclaw'
? extractOpenClawSession(filePath)
: extractSession(filePath);
console.log(formatSessionMarkdown(session));
}
function cmdListUnprocessed(projectFilter) {
const processed = getProcessedSessions();
const transcripts = findAllTranscripts();
function cmdListUnprocessed(projectFilter, source) {
// If source specified, only that source. Otherwise, all available sources.
const sourcesToCheck = source ? [source] : Object.keys(SOURCES);
const unprocessed = [];
for (const t of transcripts) {
const sid = getSessionId(t);
if (processed.has(sid)) continue;
const project = getProjectName(t);
if (projectFilter && !project.toLowerCase().includes(projectFilter.toLowerCase())) continue;
let size;
try { size = fs.statSync(t).size; } catch { continue; }
if (size < MIN_SESSION_SIZE) continue;
unprocessed.push({ path: t, project, size });
for (const src of sourcesToCheck) {
const processed = getProcessedSessions(src);
if (src === 'openclaw') {
const memFiles = findOpenClawMemoryFiles();
for (const f of memFiles) {
const sid = getOpenClawSessionId(f);
if (processed.has(sid)) continue;
let size;
try { size = fs.statSync(f).size; } catch { continue; }
if (size < MIN_SESSION_SIZE) continue;
unprocessed.push({ path: f, project: 'openclaw', size, source: src });
}
} else if (src === 'claude-code') {
const transcripts = findAllTranscripts();
for (const t of transcripts) {
const sid = getSessionId(t);
if (processed.has(sid)) continue;
const project = getProjectName(t);
if (projectFilter && !project.toLowerCase().includes(projectFilter.toLowerCase())) continue;
let size;
try { size = fs.statSync(t).size; } catch { continue; }
if (size < MIN_SESSION_SIZE) continue;
unprocessed.push({ path: t, project, size, source: src });
}
}
}

@@ -293,9 +421,11 @@

if (!unprocessed.length) {
console.log('No unprocessed sessions found.');
const label = source || 'all sources';
console.log(`No unprocessed sessions found (${label}).`);
return;
}
console.log(`Found ${unprocessed.length} unprocessed session(s):\n`);
const label = source || 'all sources';
console.log(`Found ${unprocessed.length} unprocessed session(s) [${label}]:\n`);
for (const u of unprocessed) {
const sid = getSessionId(u.path);
const sid = u.source === 'openclaw' ? getOpenClawSessionId(u.path) : getSessionId(u.path);
let mtime;

@@ -310,2 +440,3 @@ try {

console.log(` ${sid}`);
console.log(` Source: ${u.source}`);
console.log(` Project: ${u.project}`);

@@ -319,5 +450,5 @@ console.log(` Modified: ${mtime}`);

function cmdMarkProcessed(sessionId) {
markSessionProcessed(sessionId);
console.log(`Marked as processed: ${sessionId}`);
function cmdMarkProcessed(sessionId, source) {
markSessionProcessed(sessionId, source);
console.log(`Marked as processed: ${sessionId} (source: ${source})`);
}

@@ -327,24 +458,49 @@

function detectSourceFromPath(filePath) {
if (filePath.endsWith('.md')) return 'openclaw';
if (filePath.endsWith('.jsonl')) return 'claude-code';
return getDefaultSource();
}
function main() {
const args = process.argv.slice(2);
if (!args.length) {
// Parse --source flag (can appear anywhere; optional)
const srcIdx = args.indexOf('--source');
const explicitSource = srcIdx !== -1 && args[srcIdx + 1] ? args[srcIdx + 1] : null;
// Remove --source and its value from args
const cleanArgs = srcIdx !== -1
? args.filter((_, i) => i !== srcIdx && i !== srcIdx + 1)
: [...args];
if (!cleanArgs.length) {
console.log('Usage:');
console.log(' node extract.js <transcript.jsonl>');
console.log(' node extract.js --unprocessed [--project <name>]');
console.log(' node extract.js --mark-processed <session_id>');
console.log(' node extract.js [--source claude-code|openclaw] <file>');
console.log(' node extract.js [--source claude-code|openclaw] --unprocessed [--project <name>]');
console.log(' node extract.js [--source claude-code|openclaw] --mark-processed <session_id>');
console.log('');
console.log('Without --source:');
console.log(' --unprocessed scans ALL sources');
console.log(' <file> auto-detects from extension (.md → openclaw, .jsonl → claude-code)');
console.log(' --mark-processed requires --source');
console.log('');
console.log(`Sources: ${Object.keys(SOURCES).join(', ')}`);
console.log(`State dir: ${STATE_DIR}`);
process.exit(1);
}
if (args[0] === '--unprocessed') {
const projIdx = args.indexOf('--project');
const projectFilter = projIdx !== -1 && args[projIdx + 1] ? args[projIdx + 1] : null;
cmdListUnprocessed(projectFilter);
} else if (args[0] === '--mark-processed') {
if (!args[1]) {
process.stderr.write('Usage: extract.js --mark-processed <session-id>\n');
if (cleanArgs[0] === '--unprocessed') {
const projIdx = cleanArgs.indexOf('--project');
const projectFilter = projIdx !== -1 && cleanArgs[projIdx + 1] ? cleanArgs[projIdx + 1] : null;
cmdListUnprocessed(projectFilter, explicitSource);
} else if (cleanArgs[0] === '--mark-processed') {
if (!cleanArgs[1]) {
process.stderr.write('Usage: extract.js --mark-processed <session-id> --source <source>\n');
process.exit(1);
}
cmdMarkProcessed(args[1]);
const source = explicitSource || getDefaultSource();
cmdMarkProcessed(cleanArgs[1], source);
} else {
cmdExtract(args[0]);
const source = explicitSource || detectSourceFromPath(cleanArgs[0]);
cmdExtract(cleanArgs[0], source);
}

@@ -351,0 +507,0 @@ }

@@ -73,29 +73,6 @@ ---

Create `{brain_path}/REMEMBER.md` with starter content:
**Skip if file exists** — user may have already customized it.
```markdown
# REMEMBER.md
Read the template from `assets/templates/remember.md` (relative to plugin root) and write it **verbatim** to `{brain_path}/REMEMBER.md`. Do NOT add examples, explanations, or any other content — the sections must remain empty so the user fills them in.
Instructions for how your Second Brain captures and processes knowledge.
All sections are optional.
---
## Capture Rules
## Processing
## Custom Types
## Connections
## Language
## Templates
## Notes
```
**Skip if file exists** — user may have already customized it.
**Note:** This creates the global REMEMBER.md. Users can also create a project-level `REMEMBER.md` in any project root for project-specific rules that layer on top of global preferences.

@@ -102,0 +79,0 @@

@@ -62,3 +62,5 @@ ---

With project filter:
This scans **all available sources** (Claude Code transcripts + OpenClaw memory files) and lists everything unprocessed. Each entry shows its `Source:` label.
With project filter (Claude Code only):
```bash

@@ -68,2 +70,8 @@ node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js --unprocessed --project <name>

To filter by source:
```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js --source openclaw --unprocessed
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js --source claude-code --unprocessed
```
Show the list. Ask user which to process: **All**, **specific sessions by number**, or **Skip**.

@@ -75,5 +83,7 @@

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js <transcript_path>
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js <file_path>
```
Source is auto-detected from file extension (`.md` → OpenClaw, `.jsonl` → Claude Code).
Note the `**Session date (use for journal/tasks):**` line — use THAT date for everything (journal filenames, frontmatter, last_contact, task dates). Never use today's date.

@@ -134,5 +144,7 @@

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js --mark-processed <session_id>
node ${CLAUDE_PLUGIN_ROOT}/scripts/extract.js --source <source> --mark-processed <session_id>
```
Use `--source openclaw` for OpenClaw memory files, `--source claude-code` for Claude Code transcripts.
Report summary:

@@ -139,0 +151,0 @@ ```