mirror of
https://github.com/obra/episodic-memory.git
synced 2026-09-14 13:43:14 +08:00
30916f8cad
Adds Cursor (live transcripts + opt-in state.vscdb backfill) and opencode (DB->JSONL export) as harnesses, following the existing Codex pattern. opencode summarization is fixed to route via transcript text instead of a doomed claude --resume, and hasConversationContent recognizes opencode_message lines. Harness union is 'claude'|'codex'|'cursor'|'opencode'. Also pins conversation timestamps to en-US/UTC in every renderer (#130) and restores valid YAML in the search agent frontmatter (#153). Co-authored-by: vicnaum <vicnaum@users.noreply.github.com> Co-authored-by: slandau3 <slandau3@users.noreply.github.com> Co-authored-by: arimu1 <arimu1@users.noreply.github.com> Claude-Session: https://claude.ai/code/session_0112vdwZphiWzfCYfaXMes4C
118 lines
3.1 KiB
JavaScript
Executable File
118 lines
3.1 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
import { spawn } from 'child_process';
|
|
import { realpathSync } from 'fs';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(realpathSync(__filename));
|
|
|
|
const command = process.argv[2];
|
|
const args = process.argv.slice(3);
|
|
|
|
function runScript(scriptPath, args) {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
stdio: 'inherit'
|
|
});
|
|
|
|
child.on('exit', (code) => {
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`Command failed with exit code ${code}`));
|
|
}
|
|
});
|
|
|
|
child.on('error', (err) => {
|
|
reject(new Error(`Failed to run command: ${err.message}`));
|
|
});
|
|
});
|
|
}
|
|
|
|
function showHelp() {
|
|
console.log(`episodic-memory - Manage and search Claude Code, Codex, and Cursor conversations
|
|
|
|
USAGE:
|
|
episodic-memory <command> [options]
|
|
|
|
COMMANDS:
|
|
sync Sync conversations from Claude Code, Codex, and Cursor and index them
|
|
index Index conversations for search
|
|
search Search indexed conversations
|
|
show Display a conversation in readable format
|
|
stats Show index statistics
|
|
doctor Diagnose Claude Code or Codex integration issues
|
|
import-cursor-history Export legacy Cursor conversations from state.vscdb for indexing
|
|
|
|
Run 'episodic-memory <command> --help' for command-specific help.
|
|
|
|
EXAMPLES:
|
|
# Index all conversations
|
|
episodic-memory index --cleanup
|
|
|
|
# Search for something
|
|
episodic-memory search "React Router auth"
|
|
|
|
# Display a conversation
|
|
episodic-memory show path/to/conversation.jsonl
|
|
|
|
# Generate HTML output
|
|
episodic-memory show --format html conversation.jsonl > output.html`);
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const distDir = join(__dirname, '../dist');
|
|
|
|
switch (command) {
|
|
case 'index':
|
|
await runScript(join(__dirname, 'index-conversations.js'), args);
|
|
break;
|
|
|
|
case 'search':
|
|
await runScript(join(distDir, 'search-cli.js'), args);
|
|
break;
|
|
|
|
case 'show':
|
|
await runScript(join(distDir, 'show-cli.js'), args);
|
|
break;
|
|
|
|
case 'stats':
|
|
await runScript(join(distDir, 'stats-cli.js'), args);
|
|
break;
|
|
|
|
case 'doctor':
|
|
await runScript(join(distDir, 'doctor-cli.js'), args);
|
|
break;
|
|
|
|
case 'sync':
|
|
await runScript(join(distDir, 'sync-cli.js'), args);
|
|
break;
|
|
|
|
case 'import-cursor-history':
|
|
await runScript(join(distDir, 'cursor-import-cli.js'), args);
|
|
break;
|
|
|
|
case '--help':
|
|
case '-h':
|
|
case undefined:
|
|
showHelp();
|
|
break;
|
|
|
|
default:
|
|
console.error(`Unknown command: ${command}`);
|
|
console.error('Try: episodic-memory --help');
|
|
process.exit(1);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error: ${error.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(`Unexpected error: ${error.message}`);
|
|
process.exit(1);
|
|
});
|