lovecode-ai 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chatlog-WSYM3DWQ.js +12 -0
- package/dist/chunk-4IPMBDCG.js +75 -0
- package/dist/chunk-OTEQ6CQ7.js +111 -0
- package/dist/index.js +835 -477
- package/dist/session-LTPGPQPI.js +26 -0
- package/package.json +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/memory/chatlog.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
function getChatLogDir(rootDir) {
|
|
5
|
+
const base = rootDir || process.cwd();
|
|
6
|
+
return path.join(base, ".lovecode", "chats");
|
|
7
|
+
}
|
|
8
|
+
function ensureDir(rootDir) {
|
|
9
|
+
const dir = getChatLogDir(rootDir);
|
|
10
|
+
if (!fs.existsSync(dir)) {
|
|
11
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function writeChatLog(sessionId, title, entries, rootDir) {
|
|
15
|
+
ensureDir(rootDir);
|
|
16
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
17
|
+
const safeTitle = title.replace(/[^a-zA-Z0-9 _-]/g, "").slice(0, 40) || "chat";
|
|
18
|
+
const fileName = `${timestamp}_${safeTitle}_${sessionId.slice(0, 8)}.txt`;
|
|
19
|
+
const filePath = path.join(getChatLogDir(rootDir), fileName);
|
|
20
|
+
const lines = [
|
|
21
|
+
`\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`,
|
|
22
|
+
`\u2551 LoveCode AI - Chat Log \u2551`,
|
|
23
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`,
|
|
24
|
+
`Session: ${sessionId}`,
|
|
25
|
+
`Title: ${title}`,
|
|
26
|
+
`Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
27
|
+
`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
|
|
28
|
+
""
|
|
29
|
+
];
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
const role = entry.role === "user" ? "You" : entry.role === "assistant" ? "LoveCode" : "System";
|
|
32
|
+
lines.push(`\u2500\u2500 ${role} \u2500\u2500 [${new Date(entry.timestamp).toISOString()}]`);
|
|
33
|
+
lines.push(entry.content);
|
|
34
|
+
lines.push("");
|
|
35
|
+
}
|
|
36
|
+
lines.push(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
37
|
+
lines.push(`End of chat log \u2014 ${entries.length} messages`);
|
|
38
|
+
fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
39
|
+
return filePath;
|
|
40
|
+
}
|
|
41
|
+
function listChatLogs(rootDir) {
|
|
42
|
+
const dir = getChatLogDir(rootDir);
|
|
43
|
+
if (!fs.existsSync(dir)) return [];
|
|
44
|
+
const logs = [];
|
|
45
|
+
for (const file of fs.readdirSync(dir)) {
|
|
46
|
+
if (!file.endsWith(".txt")) continue;
|
|
47
|
+
const filePath = path.join(dir, file);
|
|
48
|
+
try {
|
|
49
|
+
const stat = fs.statSync(filePath);
|
|
50
|
+
logs.push({ path: filePath, name: file, size: stat.size, modified: stat.mtime });
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
logs.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
55
|
+
return logs;
|
|
56
|
+
}
|
|
57
|
+
function getChatLogCount(rootDir) {
|
|
58
|
+
const dir = getChatLogDir(rootDir);
|
|
59
|
+
if (!fs.existsSync(dir)) return 0;
|
|
60
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith(".txt")).length;
|
|
61
|
+
}
|
|
62
|
+
function deleteChatLog(fileName, rootDir) {
|
|
63
|
+
const dir = getChatLogDir(rootDir);
|
|
64
|
+
const filePath = path.join(dir, fileName);
|
|
65
|
+
if (!fs.existsSync(filePath)) return false;
|
|
66
|
+
fs.unlinkSync(filePath);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
writeChatLog,
|
|
72
|
+
listChatLogs,
|
|
73
|
+
getChatLogCount,
|
|
74
|
+
deleteChatLog
|
|
75
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/memory/session.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
var LOVE_CODE_DIR = ".lovecode";
|
|
5
|
+
var SESSIONS_DIR = "sessions";
|
|
6
|
+
function getSessionsDir(rootDir) {
|
|
7
|
+
const base = rootDir || process.cwd();
|
|
8
|
+
return path.join(base, LOVE_CODE_DIR, SESSIONS_DIR);
|
|
9
|
+
}
|
|
10
|
+
function ensureDirs(rootDir) {
|
|
11
|
+
const base = rootDir || process.cwd();
|
|
12
|
+
const sessionsDir = getSessionsDir(base);
|
|
13
|
+
const chatsDir = path.join(base, LOVE_CODE_DIR, "chats");
|
|
14
|
+
const memoryDir = path.join(base, LOVE_CODE_DIR, "memory");
|
|
15
|
+
for (const dir of [sessionsDir, chatsDir, memoryDir]) {
|
|
16
|
+
if (!fs.existsSync(dir)) {
|
|
17
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function createSession(title, options) {
|
|
22
|
+
ensureDirs(options?.workingDir);
|
|
23
|
+
const session = {
|
|
24
|
+
id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
25
|
+
title,
|
|
26
|
+
created: Date.now(),
|
|
27
|
+
updated: Date.now(),
|
|
28
|
+
model: options?.model || "unknown",
|
|
29
|
+
provider: options?.provider || "unknown",
|
|
30
|
+
workingDir: options?.workingDir || process.cwd(),
|
|
31
|
+
entries: [],
|
|
32
|
+
context: {}
|
|
33
|
+
};
|
|
34
|
+
saveSession(session);
|
|
35
|
+
return session;
|
|
36
|
+
}
|
|
37
|
+
function saveSession(session) {
|
|
38
|
+
const dir = getSessionsDir(session.workingDir);
|
|
39
|
+
if (!fs.existsSync(dir)) {
|
|
40
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
const filePath = path.join(dir, `${session.id}.json`);
|
|
43
|
+
fs.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
44
|
+
}
|
|
45
|
+
function loadSession(id, rootDir) {
|
|
46
|
+
const dir = getSessionsDir(rootDir);
|
|
47
|
+
const filePath = path.join(dir, `${id}.json`);
|
|
48
|
+
if (!fs.existsSync(filePath)) return null;
|
|
49
|
+
try {
|
|
50
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function listSessions(rootDir) {
|
|
57
|
+
const dir = getSessionsDir(rootDir);
|
|
58
|
+
if (!fs.existsSync(dir)) return [];
|
|
59
|
+
const sessions = [];
|
|
60
|
+
for (const file of fs.readdirSync(dir)) {
|
|
61
|
+
if (!file.endsWith(".json")) continue;
|
|
62
|
+
try {
|
|
63
|
+
const raw = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
64
|
+
sessions.push(JSON.parse(raw));
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
sessions.sort((a, b) => b.updated - a.updated);
|
|
69
|
+
return sessions;
|
|
70
|
+
}
|
|
71
|
+
function deleteSession(id, rootDir) {
|
|
72
|
+
const dir = getSessionsDir(rootDir);
|
|
73
|
+
const filePath = path.join(dir, `${id}.json`);
|
|
74
|
+
if (!fs.existsSync(filePath)) return false;
|
|
75
|
+
fs.unlinkSync(filePath);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
function appendToSession(session, role, content) {
|
|
79
|
+
session.entries.push({ role, content, timestamp: Date.now() });
|
|
80
|
+
session.updated = Date.now();
|
|
81
|
+
saveSession(session);
|
|
82
|
+
}
|
|
83
|
+
function updateSessionContext(session, key, value) {
|
|
84
|
+
session.context[key] = value;
|
|
85
|
+
session.updated = Date.now();
|
|
86
|
+
saveSession(session);
|
|
87
|
+
}
|
|
88
|
+
function searchSessions(query, rootDir) {
|
|
89
|
+
const lower = query.toLowerCase();
|
|
90
|
+
return listSessions(rootDir).filter(
|
|
91
|
+
(s) => s.title.toLowerCase().includes(lower) || s.entries.some((e) => e.content.toLowerCase().includes(lower))
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function resumeLastSession(rootDir) {
|
|
95
|
+
const sessions = listSessions(rootDir);
|
|
96
|
+
return sessions.length > 0 ? sessions[0] : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
getSessionsDir,
|
|
101
|
+
ensureDirs,
|
|
102
|
+
createSession,
|
|
103
|
+
saveSession,
|
|
104
|
+
loadSession,
|
|
105
|
+
listSessions,
|
|
106
|
+
deleteSession,
|
|
107
|
+
appendToSession,
|
|
108
|
+
updateSessionContext,
|
|
109
|
+
searchSessions,
|
|
110
|
+
resumeLastSession
|
|
111
|
+
};
|