claude-sesh
Version:
Session explorer for Claude Code - find, search, and resume all your past sessions
147 lines • 4.97 kB
JavaScript
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
class BookmarksService {
dataDir;
filePath;
data;
constructor() {
this.dataDir = join(homedir(), '.claude-sesh');
this.filePath = join(this.dataDir, 'bookmarks.json');
this.data = this.loadData();
}
loadData() {
if (!existsSync(this.dataDir)) {
mkdirSync(this.dataDir, { recursive: true });
}
if (!existsSync(this.filePath)) {
return { version: 1, bookmarks: {} };
}
try {
const content = readFileSync(this.filePath, 'utf-8');
const parsed = JSON.parse(content);
// Convert date strings back to Date objects
for (const bookmark of Object.values(parsed.bookmarks)) {
bookmark.createdAt = new Date(bookmark.createdAt);
bookmark.updatedAt = new Date(bookmark.updatedAt);
}
return parsed;
}
catch (error) {
console.error('Failed to load bookmarks:', error);
return { version: 1, bookmarks: {} };
}
}
saveData() {
try {
writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
}
catch (error) {
console.error('Failed to save bookmarks:', error);
}
}
getOrCreateBookmark(sessionId) {
if (!this.data.bookmarks[sessionId]) {
this.data.bookmarks[sessionId] = {
sessionId,
starred: false,
tags: [],
note: '',
createdAt: new Date(),
updatedAt: new Date(),
};
}
return this.data.bookmarks[sessionId];
}
getBookmark(sessionId) {
return this.data.bookmarks[sessionId] || null;
}
getAllBookmarks() {
return Object.values(this.data.bookmarks);
}
getStarredSessions() {
return Object.values(this.data.bookmarks).filter(b => b.starred);
}
toggleStar(sessionId) {
const bookmark = this.getOrCreateBookmark(sessionId);
bookmark.starred = !bookmark.starred;
bookmark.updatedAt = new Date();
this.saveData();
return bookmark.starred;
}
setStar(sessionId, starred) {
const bookmark = this.getOrCreateBookmark(sessionId);
bookmark.starred = starred;
bookmark.updatedAt = new Date();
this.saveData();
}
addTag(sessionId, tag) {
const bookmark = this.getOrCreateBookmark(sessionId);
const normalizedTag = tag.trim().toLowerCase();
if (normalizedTag && !bookmark.tags.includes(normalizedTag)) {
bookmark.tags.push(normalizedTag);
bookmark.updatedAt = new Date();
this.saveData();
}
return bookmark.tags;
}
removeTag(sessionId, tag) {
const bookmark = this.getOrCreateBookmark(sessionId);
const normalizedTag = tag.trim().toLowerCase();
bookmark.tags = bookmark.tags.filter(t => t !== normalizedTag);
bookmark.updatedAt = new Date();
this.saveData();
return bookmark.tags;
}
setTags(sessionId, tags) {
const bookmark = this.getOrCreateBookmark(sessionId);
bookmark.tags = tags.map(t => t.trim().toLowerCase()).filter(t => t);
bookmark.updatedAt = new Date();
this.saveData();
return bookmark.tags;
}
setNote(sessionId, note) {
const bookmark = this.getOrCreateBookmark(sessionId);
bookmark.note = note.trim();
bookmark.updatedAt = new Date();
this.saveData();
}
getNote(sessionId) {
return this.data.bookmarks[sessionId]?.note || '';
}
getAllTags() {
const allTags = new Set();
for (const bookmark of Object.values(this.data.bookmarks)) {
for (const tag of bookmark.tags) {
allTags.add(tag);
}
}
return Array.from(allTags).sort();
}
getSessionsByTag(tag) {
const normalizedTag = tag.trim().toLowerCase();
return Object.values(this.data.bookmarks)
.filter(b => b.tags.includes(normalizedTag))
.map(b => b.sessionId);
}
deleteBookmark(sessionId) {
delete this.data.bookmarks[sessionId];
this.saveData();
}
// Clean up bookmarks for sessions that no longer exist
cleanup(existingSessionIds) {
let removed = 0;
for (const sessionId of Object.keys(this.data.bookmarks)) {
if (!existingSessionIds.has(sessionId)) {
delete this.data.bookmarks[sessionId];
removed++;
}
}
if (removed > 0) {
this.saveData();
}
return removed;
}
}
export const bookmarksService = new BookmarksService();
//# sourceMappingURL=bookmarks.js.map