First version

This commit is contained in:
2026-08-11 12:38:02 -03:00
parent 42e875d866
commit 89d1a06204
37 changed files with 5660 additions and 4 deletions
+110
View File
@@ -0,0 +1,110 @@
//
// sessions-store.js — reading Claude Code's own session history.
//
// Claude Code writes one JSONL transcript per session under
// ~/.claude/projects/<slug>/<session-id>.jsonl, where the slug is the project
// directory with path separators and dots flattened to dashes. Listing that
// directory is enough to offer a "resume" picker without shelling out to the
// interactive one.
//
const { log, oneLine } = require("./util.js");
function projectSlug(path) {
return String(path).replace(/[/.]/g, "-");
}
function projectDirectory(workspacePath) {
const home = nova.environment["HOME"];
if (!home || !workspacePath) return null;
return nova.path.join(home, ".claude", "projects", projectSlug(workspacePath));
}
/** First user prompt in a transcript, used as the picker label. */
function firstPrompt(filePath) {
let file = null;
try {
file = nova.fs.open(filePath, "r");
for (let i = 0; i < 40; i++) {
const line = file.readline();
if (!line) break;
let record;
try {
record = JSON.parse(line);
} catch (_) {
continue;
}
if (record.type !== "user" || !record.message) continue;
if (record.isMeta || record.isSynthetic) continue;
const content = record.message.content;
if (typeof content === "string") {
if (content.trim()) return oneLine(content, 70);
} else if (Array.isArray(content)) {
for (const block of content) {
if (block && block.type === "text" && block.text && block.text.trim()) {
return oneLine(block.text, 70);
}
}
}
}
} catch (err) {
log("could not read session transcript:", filePath, err);
} finally {
if (file) {
try {
file.close();
} catch (_) {
/* already closed */
}
}
}
return null;
}
/**
* Recent sessions for a workspace, newest first.
* Returns [{ id, path, modifiedAt, label }].
*/
function recentSessions(workspacePath, limit = 15) {
const directory = projectDirectory(workspacePath);
if (!directory) return [];
let names;
try {
if (!nova.fs.access(directory, nova.fs.R_OK)) return [];
names = nova.fs.listdir(directory);
} catch (err) {
log("no session history:", err);
return [];
}
const sessions = [];
for (const name of names) {
if (!name.endsWith(".jsonl")) continue;
const filePath = nova.path.join(directory, name);
const stats = nova.fs.stat(filePath);
if (!stats || !stats.isFile() || stats.size === 0) continue;
sessions.push({
id: name.slice(0, -".jsonl".length),
path: filePath,
modifiedAt: stats.mtime,
size: stats.size,
});
}
sessions.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
const recent = sessions.slice(0, limit);
for (const session of recent) {
session.label = firstPrompt(session.path) || session.id.slice(0, 8);
}
return recent;
}
module.exports = { recentSessions, projectDirectory, projectSlug };