lovecode-ai 0.1.5 → 0.1.6
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 +524 -462
- package/dist/session-LTPGPQPI.js +26 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatConfig,
|
|
3
|
+
getDefaults,
|
|
4
|
+
loadConfig,
|
|
5
|
+
saveConfig
|
|
6
|
+
} from "./chunk-LJ7HTOIK.js";
|
|
7
|
+
import {
|
|
8
|
+
KNOWN_ENV_VARS,
|
|
9
|
+
formatEnvStatus,
|
|
10
|
+
loadEnv,
|
|
11
|
+
saveEnv,
|
|
12
|
+
saveEnvExample
|
|
13
|
+
} from "./chunk-3AHNSXQX.js";
|
|
1
14
|
import {
|
|
2
15
|
getTheme,
|
|
3
16
|
getThemeNames,
|
|
4
17
|
setTheme
|
|
5
18
|
} from "./chunk-FMT77EJQ.js";
|
|
19
|
+
import {
|
|
20
|
+
appendToSession,
|
|
21
|
+
createSession,
|
|
22
|
+
ensureDirs,
|
|
23
|
+
listSessions,
|
|
24
|
+
loadSession,
|
|
25
|
+
searchSessions
|
|
26
|
+
} from "./chunk-OTEQ6CQ7.js";
|
|
27
|
+
import {
|
|
28
|
+
listChatLogs,
|
|
29
|
+
writeChatLog
|
|
30
|
+
} from "./chunk-4IPMBDCG.js";
|
|
6
31
|
import {
|
|
7
32
|
cleanupMergedBranches,
|
|
8
33
|
commit,
|
|
@@ -54,19 +79,6 @@ import {
|
|
|
54
79
|
listPlugins,
|
|
55
80
|
loadBuiltinPlugins
|
|
56
81
|
} from "./chunk-MOZHR2QY.js";
|
|
57
|
-
import {
|
|
58
|
-
formatConfig,
|
|
59
|
-
getDefaults,
|
|
60
|
-
loadConfig,
|
|
61
|
-
saveConfig
|
|
62
|
-
} from "./chunk-LJ7HTOIK.js";
|
|
63
|
-
import {
|
|
64
|
-
KNOWN_ENV_VARS,
|
|
65
|
-
formatEnvStatus,
|
|
66
|
-
loadEnv,
|
|
67
|
-
saveEnv,
|
|
68
|
-
saveEnvExample
|
|
69
|
-
} from "./chunk-3AHNSXQX.js";
|
|
70
82
|
|
|
71
83
|
// src/index.ts
|
|
72
84
|
import { Command as Command17 } from "commander";
|
|
@@ -1090,109 +1102,27 @@ function createSimpleInput(prompt = "") {
|
|
|
1090
1102
|
});
|
|
1091
1103
|
}
|
|
1092
1104
|
|
|
1093
|
-
// src/memory/
|
|
1105
|
+
// src/memory/memory.ts
|
|
1094
1106
|
import * as fs2 from "fs";
|
|
1095
1107
|
import * as path2 from "path";
|
|
1096
|
-
var LOVE_CODE_DIR = ".lovecode";
|
|
1097
|
-
var SESSIONS_DIR = "sessions";
|
|
1098
|
-
function getSessionsDir(rootDir) {
|
|
1099
|
-
const base = rootDir || process.cwd();
|
|
1100
|
-
return path2.join(base, LOVE_CODE_DIR, SESSIONS_DIR);
|
|
1101
|
-
}
|
|
1102
|
-
function ensureDirs(rootDir) {
|
|
1103
|
-
const base = rootDir || process.cwd();
|
|
1104
|
-
const sessionsDir = getSessionsDir(base);
|
|
1105
|
-
const chatsDir = path2.join(base, LOVE_CODE_DIR, "chats");
|
|
1106
|
-
const memoryDir = path2.join(base, LOVE_CODE_DIR, "memory");
|
|
1107
|
-
for (const dir of [sessionsDir, chatsDir, memoryDir]) {
|
|
1108
|
-
if (!fs2.existsSync(dir)) {
|
|
1109
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
function createSession(title, options) {
|
|
1114
|
-
ensureDirs(options?.workingDir);
|
|
1115
|
-
const session = {
|
|
1116
|
-
id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
1117
|
-
title,
|
|
1118
|
-
created: Date.now(),
|
|
1119
|
-
updated: Date.now(),
|
|
1120
|
-
model: options?.model || "unknown",
|
|
1121
|
-
provider: options?.provider || "unknown",
|
|
1122
|
-
workingDir: options?.workingDir || process.cwd(),
|
|
1123
|
-
entries: [],
|
|
1124
|
-
context: {}
|
|
1125
|
-
};
|
|
1126
|
-
saveSession(session);
|
|
1127
|
-
return session;
|
|
1128
|
-
}
|
|
1129
|
-
function saveSession(session) {
|
|
1130
|
-
const dir = getSessionsDir(session.workingDir);
|
|
1131
|
-
if (!fs2.existsSync(dir)) {
|
|
1132
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
1133
|
-
}
|
|
1134
|
-
const filePath = path2.join(dir, `${session.id}.json`);
|
|
1135
|
-
fs2.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
1136
|
-
}
|
|
1137
|
-
function loadSession(id, rootDir) {
|
|
1138
|
-
const dir = getSessionsDir(rootDir);
|
|
1139
|
-
const filePath = path2.join(dir, `${id}.json`);
|
|
1140
|
-
if (!fs2.existsSync(filePath)) return null;
|
|
1141
|
-
try {
|
|
1142
|
-
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
1143
|
-
return JSON.parse(raw);
|
|
1144
|
-
} catch {
|
|
1145
|
-
return null;
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
function listSessions(rootDir) {
|
|
1149
|
-
const dir = getSessionsDir(rootDir);
|
|
1150
|
-
if (!fs2.existsSync(dir)) return [];
|
|
1151
|
-
const sessions = [];
|
|
1152
|
-
for (const file of fs2.readdirSync(dir)) {
|
|
1153
|
-
if (!file.endsWith(".json")) continue;
|
|
1154
|
-
try {
|
|
1155
|
-
const raw = fs2.readFileSync(path2.join(dir, file), "utf-8");
|
|
1156
|
-
sessions.push(JSON.parse(raw));
|
|
1157
|
-
} catch {
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
sessions.sort((a, b) => b.updated - a.updated);
|
|
1161
|
-
return sessions;
|
|
1162
|
-
}
|
|
1163
|
-
function appendToSession(session, role, content) {
|
|
1164
|
-
session.entries.push({ role, content, timestamp: Date.now() });
|
|
1165
|
-
session.updated = Date.now();
|
|
1166
|
-
saveSession(session);
|
|
1167
|
-
}
|
|
1168
|
-
function searchSessions(query, rootDir) {
|
|
1169
|
-
const lower = query.toLowerCase();
|
|
1170
|
-
return listSessions(rootDir).filter(
|
|
1171
|
-
(s) => s.title.toLowerCase().includes(lower) || s.entries.some((e) => e.content.toLowerCase().includes(lower))
|
|
1172
|
-
);
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// src/memory/memory.ts
|
|
1176
|
-
import * as fs3 from "fs";
|
|
1177
|
-
import * as path3 from "path";
|
|
1178
1108
|
function getMemoryDir(rootDir) {
|
|
1179
1109
|
const base = rootDir || process.cwd();
|
|
1180
|
-
return
|
|
1110
|
+
return path2.join(base, ".lovecode", "memory");
|
|
1181
1111
|
}
|
|
1182
1112
|
function getMemoryFilePath(key, rootDir) {
|
|
1183
|
-
return
|
|
1113
|
+
return path2.join(getMemoryDir(rootDir), `${key}.json`);
|
|
1184
1114
|
}
|
|
1185
1115
|
function ensureMemoryDir(rootDir) {
|
|
1186
1116
|
const dir = getMemoryDir(rootDir);
|
|
1187
|
-
if (!
|
|
1188
|
-
|
|
1117
|
+
if (!fs2.existsSync(dir)) {
|
|
1118
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1189
1119
|
}
|
|
1190
1120
|
}
|
|
1191
1121
|
function readMemory(key, fallback, rootDir) {
|
|
1192
1122
|
const filePath = getMemoryFilePath(key, rootDir);
|
|
1193
|
-
if (!
|
|
1123
|
+
if (!fs2.existsSync(filePath)) return fallback;
|
|
1194
1124
|
try {
|
|
1195
|
-
const raw =
|
|
1125
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
1196
1126
|
return JSON.parse(raw);
|
|
1197
1127
|
} catch {
|
|
1198
1128
|
return fallback;
|
|
@@ -1201,7 +1131,7 @@ function readMemory(key, fallback, rootDir) {
|
|
|
1201
1131
|
function writeMemory(key, data, rootDir) {
|
|
1202
1132
|
ensureMemoryDir(rootDir);
|
|
1203
1133
|
const filePath = getMemoryFilePath(key, rootDir);
|
|
1204
|
-
|
|
1134
|
+
fs2.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1205
1135
|
}
|
|
1206
1136
|
var DEFAULT_PREFERENCES = {
|
|
1207
1137
|
indentStyle: "spaces",
|
|
@@ -1274,10 +1204,10 @@ function addRepoNote(note, rootDir) {
|
|
|
1274
1204
|
}
|
|
1275
1205
|
function clearAllMemory(rootDir) {
|
|
1276
1206
|
const dir = getMemoryDir(rootDir);
|
|
1277
|
-
if (
|
|
1278
|
-
for (const file of
|
|
1207
|
+
if (fs2.existsSync(dir)) {
|
|
1208
|
+
for (const file of fs2.readdirSync(dir)) {
|
|
1279
1209
|
if (file.endsWith(".json")) {
|
|
1280
|
-
|
|
1210
|
+
fs2.unlinkSync(path2.join(dir, file));
|
|
1281
1211
|
}
|
|
1282
1212
|
}
|
|
1283
1213
|
}
|
|
@@ -1317,8 +1247,8 @@ function formatRepoMemory(mem) {
|
|
|
1317
1247
|
}
|
|
1318
1248
|
|
|
1319
1249
|
// src/memory/vector.ts
|
|
1320
|
-
import * as
|
|
1321
|
-
import * as
|
|
1250
|
+
import * as fs3 from "fs";
|
|
1251
|
+
import * as path3 from "path";
|
|
1322
1252
|
|
|
1323
1253
|
// src/ai/embeddings.ts
|
|
1324
1254
|
import { execSync } from "child_process";
|
|
@@ -1387,22 +1317,22 @@ function cosineSimilarity(a, b) {
|
|
|
1387
1317
|
// src/memory/vector.ts
|
|
1388
1318
|
function getVectorDir(rootDir) {
|
|
1389
1319
|
const base = rootDir || process.cwd();
|
|
1390
|
-
return
|
|
1320
|
+
return path3.join(base, ".lovecode", "memory");
|
|
1391
1321
|
}
|
|
1392
1322
|
function getVectorFilePath(rootDir) {
|
|
1393
|
-
return
|
|
1323
|
+
return path3.join(getVectorDir(rootDir), "vectors.json");
|
|
1394
1324
|
}
|
|
1395
1325
|
function ensureDir(rootDir) {
|
|
1396
1326
|
const dir = getVectorDir(rootDir);
|
|
1397
|
-
if (!
|
|
1398
|
-
|
|
1327
|
+
if (!fs3.existsSync(dir)) {
|
|
1328
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1399
1329
|
}
|
|
1400
1330
|
}
|
|
1401
1331
|
function loadVectors(rootDir) {
|
|
1402
1332
|
const filePath = getVectorFilePath(rootDir);
|
|
1403
|
-
if (!
|
|
1333
|
+
if (!fs3.existsSync(filePath)) return [];
|
|
1404
1334
|
try {
|
|
1405
|
-
const raw =
|
|
1335
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
1406
1336
|
return JSON.parse(raw);
|
|
1407
1337
|
} catch {
|
|
1408
1338
|
return [];
|
|
@@ -1411,7 +1341,7 @@ function loadVectors(rootDir) {
|
|
|
1411
1341
|
function saveVectors(entries, rootDir) {
|
|
1412
1342
|
ensureDir(rootDir);
|
|
1413
1343
|
const filePath = getVectorFilePath(rootDir);
|
|
1414
|
-
|
|
1344
|
+
fs3.writeFileSync(filePath, JSON.stringify(entries, null, 2), "utf-8");
|
|
1415
1345
|
}
|
|
1416
1346
|
async function storeVector(text, metadata, rootDir) {
|
|
1417
1347
|
const result = await getEmbedding(text);
|
|
@@ -1452,8 +1382,8 @@ async function searchVectors(query, options, rootDir) {
|
|
|
1452
1382
|
}
|
|
1453
1383
|
function clearVectors(rootDir) {
|
|
1454
1384
|
const filePath = getVectorFilePath(rootDir);
|
|
1455
|
-
if (
|
|
1456
|
-
|
|
1385
|
+
if (fs3.existsSync(filePath)) {
|
|
1386
|
+
fs3.unlinkSync(filePath);
|
|
1457
1387
|
}
|
|
1458
1388
|
}
|
|
1459
1389
|
function getVectorCount(rootDir) {
|
|
@@ -1474,63 +1404,6 @@ function chalkDim(s) {
|
|
|
1474
1404
|
return `\x1B[2m${s}\x1B[22m`;
|
|
1475
1405
|
}
|
|
1476
1406
|
|
|
1477
|
-
// src/memory/chatlog.ts
|
|
1478
|
-
import * as fs5 from "fs";
|
|
1479
|
-
import * as path5 from "path";
|
|
1480
|
-
function getChatLogDir(rootDir) {
|
|
1481
|
-
const base = rootDir || process.cwd();
|
|
1482
|
-
return path5.join(base, ".lovecode", "chats");
|
|
1483
|
-
}
|
|
1484
|
-
function ensureDir2(rootDir) {
|
|
1485
|
-
const dir = getChatLogDir(rootDir);
|
|
1486
|
-
if (!fs5.existsSync(dir)) {
|
|
1487
|
-
fs5.mkdirSync(dir, { recursive: true });
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
function writeChatLog(sessionId, title, entries, rootDir) {
|
|
1491
|
-
ensureDir2(rootDir);
|
|
1492
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1493
|
-
const safeTitle = title.replace(/[^a-zA-Z0-9 _-]/g, "").slice(0, 40) || "chat";
|
|
1494
|
-
const fileName = `${timestamp}_${safeTitle}_${sessionId.slice(0, 8)}.txt`;
|
|
1495
|
-
const filePath = path5.join(getChatLogDir(rootDir), fileName);
|
|
1496
|
-
const lines = [
|
|
1497
|
-
`\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`,
|
|
1498
|
-
`\u2551 LoveCode AI - Chat Log \u2551`,
|
|
1499
|
-
`\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`,
|
|
1500
|
-
`Session: ${sessionId}`,
|
|
1501
|
-
`Title: ${title}`,
|
|
1502
|
-
`Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
1503
|
-
`\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`,
|
|
1504
|
-
""
|
|
1505
|
-
];
|
|
1506
|
-
for (const entry of entries) {
|
|
1507
|
-
const role = entry.role === "user" ? "You" : entry.role === "assistant" ? "LoveCode" : "System";
|
|
1508
|
-
lines.push(`\u2500\u2500 ${role} \u2500\u2500 [${new Date(entry.timestamp).toISOString()}]`);
|
|
1509
|
-
lines.push(entry.content);
|
|
1510
|
-
lines.push("");
|
|
1511
|
-
}
|
|
1512
|
-
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`);
|
|
1513
|
-
lines.push(`End of chat log \u2014 ${entries.length} messages`);
|
|
1514
|
-
fs5.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
1515
|
-
return filePath;
|
|
1516
|
-
}
|
|
1517
|
-
function listChatLogs(rootDir) {
|
|
1518
|
-
const dir = getChatLogDir(rootDir);
|
|
1519
|
-
if (!fs5.existsSync(dir)) return [];
|
|
1520
|
-
const logs = [];
|
|
1521
|
-
for (const file of fs5.readdirSync(dir)) {
|
|
1522
|
-
if (!file.endsWith(".txt")) continue;
|
|
1523
|
-
const filePath = path5.join(dir, file);
|
|
1524
|
-
try {
|
|
1525
|
-
const stat = fs5.statSync(filePath);
|
|
1526
|
-
logs.push({ path: filePath, name: file, size: stat.size, modified: stat.mtime });
|
|
1527
|
-
} catch {
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
logs.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
1531
|
-
return logs;
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
1407
|
// src/commands/chat.ts
|
|
1535
1408
|
function renderHeader() {
|
|
1536
1409
|
console.log(chalk4.bold.cyan("\n \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\u2557"));
|
|
@@ -1575,9 +1448,9 @@ var chatCommand = new Command("chat").alias("c").description("Start an interacti
|
|
|
1575
1448
|
const saveLog = () => {
|
|
1576
1449
|
if (!dirty) return;
|
|
1577
1450
|
try {
|
|
1578
|
-
const
|
|
1451
|
+
const path21 = writeChatLog(session.id, session.title, session.entries);
|
|
1579
1452
|
console.log(chalk4.dim(`
|
|
1580
|
-
Chat log saved: ${
|
|
1453
|
+
Chat log saved: ${path21}`));
|
|
1581
1454
|
} catch {
|
|
1582
1455
|
}
|
|
1583
1456
|
};
|
|
@@ -1823,19 +1696,19 @@ async function getApproval(mode, toolName, description, args) {
|
|
|
1823
1696
|
}
|
|
1824
1697
|
|
|
1825
1698
|
// src/core/tools.ts
|
|
1826
|
-
import * as
|
|
1827
|
-
import * as
|
|
1699
|
+
import * as fs15 from "fs";
|
|
1700
|
+
import * as path14 from "path";
|
|
1828
1701
|
import { execSync as execSync3 } from "child_process";
|
|
1829
1702
|
import chalk17 from "chalk";
|
|
1830
1703
|
|
|
1831
1704
|
// src/fs/scanner.ts
|
|
1832
|
-
import * as
|
|
1833
|
-
import * as
|
|
1705
|
+
import * as fs5 from "fs";
|
|
1706
|
+
import * as path5 from "path";
|
|
1834
1707
|
import chalk7 from "chalk";
|
|
1835
1708
|
|
|
1836
1709
|
// src/fs/ignore.ts
|
|
1837
|
-
import * as
|
|
1838
|
-
import * as
|
|
1710
|
+
import * as fs4 from "fs";
|
|
1711
|
+
import * as path4 from "path";
|
|
1839
1712
|
function parseGitignore(content) {
|
|
1840
1713
|
const patterns = [];
|
|
1841
1714
|
const negations = [];
|
|
@@ -1851,9 +1724,9 @@ function parseGitignore(content) {
|
|
|
1851
1724
|
return { patterns, negations };
|
|
1852
1725
|
}
|
|
1853
1726
|
function loadFile(dir, filename) {
|
|
1854
|
-
const filePath =
|
|
1727
|
+
const filePath = path4.join(dir, filename);
|
|
1855
1728
|
try {
|
|
1856
|
-
const content =
|
|
1729
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
1857
1730
|
return parseGitignore(content);
|
|
1858
1731
|
} catch {
|
|
1859
1732
|
return null;
|
|
@@ -1892,7 +1765,7 @@ function matchPattern(filePath, pattern) {
|
|
|
1892
1765
|
return matchSimple(filePath, pattern.slice(1));
|
|
1893
1766
|
}
|
|
1894
1767
|
if (matchSimple(filePath, pattern)) return true;
|
|
1895
|
-
const basename6 =
|
|
1768
|
+
const basename6 = path4.basename(filePath);
|
|
1896
1769
|
if (isGlobless(pattern) && basename6 === pattern) return true;
|
|
1897
1770
|
return false;
|
|
1898
1771
|
}
|
|
@@ -1997,13 +1870,13 @@ function scanDirectory(options) {
|
|
|
1997
1870
|
if (depth > maxDepth || results.length >= maxFiles) return;
|
|
1998
1871
|
let entries;
|
|
1999
1872
|
try {
|
|
2000
|
-
entries =
|
|
1873
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
2001
1874
|
} catch {
|
|
2002
1875
|
return;
|
|
2003
1876
|
}
|
|
2004
1877
|
for (const entry of entries) {
|
|
2005
|
-
const fullPath =
|
|
2006
|
-
const relativePath =
|
|
1878
|
+
const fullPath = path5.join(dir, entry.name);
|
|
1879
|
+
const relativePath = path5.relative(rootDir, fullPath);
|
|
2007
1880
|
if (relativePath.startsWith("..")) continue;
|
|
2008
1881
|
const isHidden = entry.name.startsWith(".") && entry.name !== "." && entry.name !== "..";
|
|
2009
1882
|
if (isHidden && !includeHidden) {
|
|
@@ -2015,8 +1888,8 @@ function scanDirectory(options) {
|
|
|
2015
1888
|
if (entry.isDirectory()) {
|
|
2016
1889
|
walk(fullPath, depth + 1);
|
|
2017
1890
|
} else if (entry.isFile()) {
|
|
2018
|
-
const ext =
|
|
2019
|
-
const stats =
|
|
1891
|
+
const ext = path5.extname(entry.name).toLowerCase();
|
|
1892
|
+
const stats = fs5.statSync(fullPath);
|
|
2020
1893
|
const relative6 = relativePath.replace(/\\/g, "/");
|
|
2021
1894
|
results.push({
|
|
2022
1895
|
path: fullPath,
|
|
@@ -2072,15 +1945,15 @@ function printScanSummary(files) {
|
|
|
2072
1945
|
}
|
|
2073
1946
|
|
|
2074
1947
|
// src/fs/operations.ts
|
|
2075
|
-
import * as
|
|
2076
|
-
import * as
|
|
1948
|
+
import * as fs6 from "fs";
|
|
1949
|
+
import * as path6 from "path";
|
|
2077
1950
|
function renameFile(oldPath, newPath) {
|
|
2078
1951
|
try {
|
|
2079
|
-
const dir =
|
|
2080
|
-
if (!
|
|
2081
|
-
|
|
1952
|
+
const dir = path6.dirname(newPath);
|
|
1953
|
+
if (!fs6.existsSync(dir)) {
|
|
1954
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
2082
1955
|
}
|
|
2083
|
-
|
|
1956
|
+
fs6.renameSync(oldPath, newPath);
|
|
2084
1957
|
return { success: true, message: `Renamed to ${newPath}` };
|
|
2085
1958
|
} catch (err) {
|
|
2086
1959
|
return { success: false, message: "", error: String(err) };
|
|
@@ -2088,11 +1961,11 @@ function renameFile(oldPath, newPath) {
|
|
|
2088
1961
|
}
|
|
2089
1962
|
function duplicateFile(sourcePath, destPath) {
|
|
2090
1963
|
try {
|
|
2091
|
-
const dir =
|
|
2092
|
-
if (!
|
|
2093
|
-
|
|
1964
|
+
const dir = path6.dirname(destPath);
|
|
1965
|
+
if (!fs6.existsSync(dir)) {
|
|
1966
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
2094
1967
|
}
|
|
2095
|
-
|
|
1968
|
+
fs6.copyFileSync(sourcePath, destPath);
|
|
2096
1969
|
return { success: true, message: `Duplicated to ${destPath}` };
|
|
2097
1970
|
} catch (err) {
|
|
2098
1971
|
return { success: false, message: "", error: String(err) };
|
|
@@ -2104,7 +1977,7 @@ function getDirectoryTree(dirPath, prefix = "", maxDepth = 3, currentDepth = 0)
|
|
|
2104
1977
|
let result = "";
|
|
2105
1978
|
let entries;
|
|
2106
1979
|
try {
|
|
2107
|
-
entries =
|
|
1980
|
+
entries = fs6.readdirSync(dirPath, { withFileTypes: true });
|
|
2108
1981
|
} catch {
|
|
2109
1982
|
return `${prefix} (error reading)
|
|
2110
1983
|
`;
|
|
@@ -2124,13 +1997,13 @@ function getDirectoryTree(dirPath, prefix = "", maxDepth = 3, currentDepth = 0)
|
|
|
2124
1997
|
result += `${prefix}${connector}${entry.name}/
|
|
2125
1998
|
`;
|
|
2126
1999
|
result += getDirectoryTree(
|
|
2127
|
-
|
|
2000
|
+
path6.join(dirPath, entry.name),
|
|
2128
2001
|
prefix + nextPrefix,
|
|
2129
2002
|
maxDepth,
|
|
2130
2003
|
currentDepth + 1
|
|
2131
2004
|
);
|
|
2132
2005
|
} else {
|
|
2133
|
-
const stats =
|
|
2006
|
+
const stats = fs6.statSync(path6.join(dirPath, entry.name));
|
|
2134
2007
|
const size = formatSize(stats.size);
|
|
2135
2008
|
result += `${prefix}${connector}${entry.name} ${size}
|
|
2136
2009
|
`;
|
|
@@ -2150,8 +2023,8 @@ function formatSize(bytes) {
|
|
|
2150
2023
|
}
|
|
2151
2024
|
|
|
2152
2025
|
// src/fs/search.ts
|
|
2153
|
-
import * as
|
|
2154
|
-
import * as
|
|
2026
|
+
import * as fs7 from "fs";
|
|
2027
|
+
import * as path7 from "path";
|
|
2155
2028
|
import { execSync as execSync2 } from "child_process";
|
|
2156
2029
|
function normalizeQuery(query) {
|
|
2157
2030
|
return query.toLowerCase().split(/[\s_-]+/).filter((w) => w.length > 0);
|
|
@@ -2180,14 +2053,14 @@ function collectAllFiles(dir, maxFiles = 5e3) {
|
|
|
2180
2053
|
if (results.length >= maxFiles) return;
|
|
2181
2054
|
let entries;
|
|
2182
2055
|
try {
|
|
2183
|
-
entries =
|
|
2056
|
+
entries = fs7.readdirSync(dir2, { withFileTypes: true });
|
|
2184
2057
|
} catch {
|
|
2185
2058
|
return;
|
|
2186
2059
|
}
|
|
2187
2060
|
for (const entry of entries) {
|
|
2188
2061
|
if (results.length >= maxFiles) return;
|
|
2189
2062
|
if (entry.name.startsWith(".") && entry.name !== ".gitignore") continue;
|
|
2190
|
-
const fullPath =
|
|
2063
|
+
const fullPath = path7.join(dir2, entry.name);
|
|
2191
2064
|
if (entry.isDirectory()) {
|
|
2192
2065
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
2193
2066
|
walk(fullPath);
|
|
@@ -2204,8 +2077,8 @@ function findFiles(options) {
|
|
|
2204
2077
|
const allFiles = collectAllFiles(rootDir);
|
|
2205
2078
|
const scored = [];
|
|
2206
2079
|
for (const filePath of allFiles) {
|
|
2207
|
-
const relativePath =
|
|
2208
|
-
const basename6 =
|
|
2080
|
+
const relativePath = path7.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
2081
|
+
const basename6 = path7.basename(filePath);
|
|
2209
2082
|
const score = scoreByName(basename6, query) + scoreByName(relativePath, query) * 0.5;
|
|
2210
2083
|
if (score > 0) {
|
|
2211
2084
|
scored.push({
|
|
@@ -2226,7 +2099,7 @@ function findFiles(options) {
|
|
|
2226
2099
|
}
|
|
2227
2100
|
function searchFileContent(filePath, query) {
|
|
2228
2101
|
try {
|
|
2229
|
-
const content =
|
|
2102
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
2230
2103
|
const lines = content.split("\n");
|
|
2231
2104
|
const matches = [];
|
|
2232
2105
|
const lowerQuery = query.toLowerCase();
|
|
@@ -2247,7 +2120,7 @@ function findWithRipgrep(query, rootDir) {
|
|
|
2247
2120
|
const output = execSync2(cmd, { cwd: rootDir, encoding: "utf-8", maxBuffer: 1024 * 1024 });
|
|
2248
2121
|
const files = output.trim().split("\n").filter(Boolean);
|
|
2249
2122
|
return files.slice(0, 30).map((filePath) => ({
|
|
2250
|
-
filePath:
|
|
2123
|
+
filePath: path7.join(rootDir, filePath),
|
|
2251
2124
|
relativePath: filePath,
|
|
2252
2125
|
score: 50
|
|
2253
2126
|
}));
|
|
@@ -2263,11 +2136,11 @@ function semanticSearch(query, rootDir) {
|
|
|
2263
2136
|
contentResults = findWithRipgrep(query, rootDir);
|
|
2264
2137
|
} catch {
|
|
2265
2138
|
const textFiles = collectAllFiles(rootDir, 2e3).filter((f) => {
|
|
2266
|
-
const ext =
|
|
2139
|
+
const ext = path7.extname(f);
|
|
2267
2140
|
return [".ts", ".js", ".py", ".rs", ".go", ".md", ".json", ".txt", ".yaml", ".yml"].includes(ext);
|
|
2268
2141
|
});
|
|
2269
2142
|
for (const filePath of textFiles.slice(0, 100)) {
|
|
2270
|
-
const relativePath =
|
|
2143
|
+
const relativePath = path7.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
2271
2144
|
if (namePaths.has(relativePath)) continue;
|
|
2272
2145
|
const matches = searchFileContent(filePath, query);
|
|
2273
2146
|
if (matches.length > 0) {
|
|
@@ -2291,7 +2164,7 @@ function semanticSearch(query, rootDir) {
|
|
|
2291
2164
|
}
|
|
2292
2165
|
|
|
2293
2166
|
// src/fs/rank.ts
|
|
2294
|
-
import * as
|
|
2167
|
+
import * as path8 from "path";
|
|
2295
2168
|
import chalk8 from "chalk";
|
|
2296
2169
|
var CATEGORY_WEIGHTS = {
|
|
2297
2170
|
source: 100,
|
|
@@ -2348,7 +2221,7 @@ function rankFiles(files, taskDescription, options = {}) {
|
|
|
2348
2221
|
const results = files.map((file) => {
|
|
2349
2222
|
let score = 0;
|
|
2350
2223
|
const reasons = [];
|
|
2351
|
-
const basename6 =
|
|
2224
|
+
const basename6 = path8.basename(file.path);
|
|
2352
2225
|
score += CATEGORY_WEIGHTS[file.category] || 10;
|
|
2353
2226
|
reasons.push(`category:${file.category}=${CATEGORY_WEIGHTS[file.category] || 10}`);
|
|
2354
2227
|
if (boostSource && file.category === "source") {
|
|
@@ -2419,7 +2292,7 @@ function printRankedFiles(ranked) {
|
|
|
2419
2292
|
}
|
|
2420
2293
|
|
|
2421
2294
|
// src/editor/patch.ts
|
|
2422
|
-
import * as
|
|
2295
|
+
import * as fs8 from "fs";
|
|
2423
2296
|
import chalk9 from "chalk";
|
|
2424
2297
|
function generateDiff(oldLines, newLines) {
|
|
2425
2298
|
const result = [];
|
|
@@ -2445,10 +2318,10 @@ function generateDiff(oldLines, newLines) {
|
|
|
2445
2318
|
}
|
|
2446
2319
|
function applyInlinePatch(filePath, searchBlock, replaceBlock) {
|
|
2447
2320
|
try {
|
|
2448
|
-
if (!
|
|
2321
|
+
if (!fs8.existsSync(filePath)) {
|
|
2449
2322
|
return { success: false, applied: false, output: "", error: `File not found: ${filePath}` };
|
|
2450
2323
|
}
|
|
2451
|
-
const content =
|
|
2324
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
2452
2325
|
const searchNormalized = searchBlock.trim();
|
|
2453
2326
|
const contentNormalized = content;
|
|
2454
2327
|
const idx = contentNormalized.indexOf(searchNormalized);
|
|
@@ -2466,7 +2339,7 @@ function applyInlinePatch(filePath, searchBlock, replaceBlock) {
|
|
|
2466
2339
|
const oldLines = searchBlock.split("\n");
|
|
2467
2340
|
const newLines = replaceBlock.split("\n");
|
|
2468
2341
|
const diff = generateDiff(oldLines, newLines);
|
|
2469
|
-
|
|
2342
|
+
fs8.writeFileSync(filePath, newContent, "utf-8");
|
|
2470
2343
|
return { success: true, applied: true, output: `Patch applied:
|
|
2471
2344
|
${diff}` };
|
|
2472
2345
|
} catch (err) {
|
|
@@ -2594,28 +2467,28 @@ function detectLanguage(filePath) {
|
|
|
2594
2467
|
}
|
|
2595
2468
|
|
|
2596
2469
|
// src/editor/undo.ts
|
|
2597
|
-
import * as
|
|
2598
|
-
import * as
|
|
2470
|
+
import * as fs9 from "fs";
|
|
2471
|
+
import * as path9 from "path";
|
|
2599
2472
|
var UNDO_DIR = ".lovecode/undo";
|
|
2600
2473
|
function getUndoDir(rootDir) {
|
|
2601
|
-
return
|
|
2474
|
+
return path9.join(rootDir, UNDO_DIR);
|
|
2602
2475
|
}
|
|
2603
2476
|
function ensureUndoDir(rootDir) {
|
|
2604
2477
|
const dir = getUndoDir(rootDir);
|
|
2605
|
-
|
|
2478
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
2606
2479
|
return dir;
|
|
2607
2480
|
}
|
|
2608
2481
|
function getUndoFilePath(rootDir, id) {
|
|
2609
|
-
return
|
|
2482
|
+
return path9.join(getUndoDir(rootDir), `${id}.json`);
|
|
2610
2483
|
}
|
|
2611
2484
|
function getIndexPath(rootDir) {
|
|
2612
|
-
return
|
|
2485
|
+
return path9.join(getUndoDir(rootDir), "index.json");
|
|
2613
2486
|
}
|
|
2614
2487
|
function saveUndoPoint(rootDir, filePath, label = "edit") {
|
|
2615
2488
|
try {
|
|
2616
|
-
if (!
|
|
2489
|
+
if (!fs9.existsSync(filePath)) return null;
|
|
2617
2490
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2618
|
-
const content =
|
|
2491
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2619
2492
|
const entry = {
|
|
2620
2493
|
id,
|
|
2621
2494
|
filePath,
|
|
@@ -2625,19 +2498,19 @@ function saveUndoPoint(rootDir, filePath, label = "edit") {
|
|
|
2625
2498
|
};
|
|
2626
2499
|
ensureUndoDir(rootDir);
|
|
2627
2500
|
const entryFile = getUndoFilePath(rootDir, id);
|
|
2628
|
-
|
|
2501
|
+
fs9.writeFileSync(entryFile, JSON.stringify(entry, null, 2), "utf-8");
|
|
2629
2502
|
const indexPath = getIndexPath(rootDir);
|
|
2630
2503
|
let index = [];
|
|
2631
|
-
if (
|
|
2504
|
+
if (fs9.existsSync(indexPath)) {
|
|
2632
2505
|
try {
|
|
2633
|
-
index = JSON.parse(
|
|
2506
|
+
index = JSON.parse(fs9.readFileSync(indexPath, "utf-8"));
|
|
2634
2507
|
} catch {
|
|
2635
2508
|
index = [];
|
|
2636
2509
|
}
|
|
2637
2510
|
}
|
|
2638
2511
|
index.unshift(entry);
|
|
2639
2512
|
if (index.length > 100) index = index.slice(0, 100);
|
|
2640
|
-
|
|
2513
|
+
fs9.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
2641
2514
|
return entry;
|
|
2642
2515
|
} catch {
|
|
2643
2516
|
return null;
|
|
@@ -2646,21 +2519,21 @@ function saveUndoPoint(rootDir, filePath, label = "edit") {
|
|
|
2646
2519
|
function undoLast(rootDir, filePath) {
|
|
2647
2520
|
try {
|
|
2648
2521
|
const indexPath = getIndexPath(rootDir);
|
|
2649
|
-
if (!
|
|
2650
|
-
const index = JSON.parse(
|
|
2522
|
+
if (!fs9.existsSync(indexPath)) return null;
|
|
2523
|
+
const index = JSON.parse(fs9.readFileSync(indexPath, "utf-8"));
|
|
2651
2524
|
const targetIdx = filePath ? index.findIndex((e) => e.filePath === filePath) : 0;
|
|
2652
2525
|
if (targetIdx === -1) return null;
|
|
2653
2526
|
const entry = index[targetIdx];
|
|
2654
|
-
if (!
|
|
2527
|
+
if (!fs9.existsSync(entry.filePath)) {
|
|
2655
2528
|
index.splice(targetIdx, 1);
|
|
2656
|
-
|
|
2529
|
+
fs9.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
2657
2530
|
return null;
|
|
2658
2531
|
}
|
|
2659
|
-
|
|
2532
|
+
fs9.writeFileSync(entry.filePath, entry.originalContent, "utf-8");
|
|
2660
2533
|
index.splice(targetIdx, 1);
|
|
2661
|
-
|
|
2534
|
+
fs9.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
2662
2535
|
const entryFile = getUndoFilePath(rootDir, entry.id);
|
|
2663
|
-
if (
|
|
2536
|
+
if (fs9.existsSync(entryFile)) fs9.unlinkSync(entryFile);
|
|
2664
2537
|
return entry;
|
|
2665
2538
|
} catch {
|
|
2666
2539
|
return null;
|
|
@@ -2669,8 +2542,8 @@ function undoLast(rootDir, filePath) {
|
|
|
2669
2542
|
function getUndoHistory(rootDir) {
|
|
2670
2543
|
try {
|
|
2671
2544
|
const indexPath = getIndexPath(rootDir);
|
|
2672
|
-
if (!
|
|
2673
|
-
return JSON.parse(
|
|
2545
|
+
if (!fs9.existsSync(indexPath)) return [];
|
|
2546
|
+
return JSON.parse(fs9.readFileSync(indexPath, "utf-8"));
|
|
2674
2547
|
} catch {
|
|
2675
2548
|
return [];
|
|
2676
2549
|
}
|
|
@@ -2680,22 +2553,22 @@ function undoForFile(rootDir, filePath) {
|
|
|
2680
2553
|
}
|
|
2681
2554
|
|
|
2682
2555
|
// src/editor/snapshot.ts
|
|
2683
|
-
import * as
|
|
2684
|
-
import * as
|
|
2556
|
+
import * as fs10 from "fs";
|
|
2557
|
+
import * as path10 from "path";
|
|
2685
2558
|
var SNAPSHOT_DIR = ".lovecode/snapshots";
|
|
2686
2559
|
function getSnapshotDir(rootDir) {
|
|
2687
|
-
return
|
|
2560
|
+
return path10.join(rootDir, SNAPSHOT_DIR);
|
|
2688
2561
|
}
|
|
2689
2562
|
function ensureSnapshotDir(rootDir) {
|
|
2690
2563
|
const dir = getSnapshotDir(rootDir);
|
|
2691
|
-
|
|
2564
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
2692
2565
|
return dir;
|
|
2693
2566
|
}
|
|
2694
2567
|
function createSnapshot(rootDir, filePath, label = "") {
|
|
2695
2568
|
try {
|
|
2696
|
-
if (!
|
|
2697
|
-
const content =
|
|
2698
|
-
const relativePath =
|
|
2569
|
+
if (!fs10.existsSync(filePath)) return null;
|
|
2570
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
2571
|
+
const relativePath = path10.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
2699
2572
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2700
2573
|
const snapshotDir = ensureSnapshotDir(rootDir);
|
|
2701
2574
|
const snapshot = {
|
|
@@ -2706,8 +2579,8 @@ function createSnapshot(rootDir, filePath, label = "") {
|
|
|
2706
2579
|
label,
|
|
2707
2580
|
size: content.length
|
|
2708
2581
|
};
|
|
2709
|
-
|
|
2710
|
-
|
|
2582
|
+
fs10.writeFileSync(path10.join(snapshotDir, `${id}.snap`), content, "utf-8");
|
|
2583
|
+
fs10.writeFileSync(path10.join(snapshotDir, `${id}.meta`), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
2711
2584
|
return snapshot;
|
|
2712
2585
|
} catch {
|
|
2713
2586
|
return null;
|
|
@@ -2716,13 +2589,13 @@ function createSnapshot(rootDir, filePath, label = "") {
|
|
|
2716
2589
|
function restoreSnapshot(rootDir, id) {
|
|
2717
2590
|
try {
|
|
2718
2591
|
const snapshotDir = getSnapshotDir(rootDir);
|
|
2719
|
-
const metaPath =
|
|
2720
|
-
const snapPath =
|
|
2721
|
-
if (!
|
|
2722
|
-
const meta = JSON.parse(
|
|
2723
|
-
const content =
|
|
2724
|
-
if (
|
|
2725
|
-
|
|
2592
|
+
const metaPath = path10.join(snapshotDir, `${id}.meta`);
|
|
2593
|
+
const snapPath = path10.join(snapshotDir, `${id}.snap`);
|
|
2594
|
+
if (!fs10.existsSync(metaPath) || !fs10.existsSync(snapPath)) return false;
|
|
2595
|
+
const meta = JSON.parse(fs10.readFileSync(metaPath, "utf-8"));
|
|
2596
|
+
const content = fs10.readFileSync(snapPath, "utf-8");
|
|
2597
|
+
if (fs10.existsSync(meta.filePath)) {
|
|
2598
|
+
fs10.writeFileSync(meta.filePath, content, "utf-8");
|
|
2726
2599
|
}
|
|
2727
2600
|
return true;
|
|
2728
2601
|
} catch {
|
|
@@ -2732,13 +2605,13 @@ function restoreSnapshot(rootDir, id) {
|
|
|
2732
2605
|
function listSnapshots(rootDir) {
|
|
2733
2606
|
try {
|
|
2734
2607
|
const snapshotDir = getSnapshotDir(rootDir);
|
|
2735
|
-
if (!
|
|
2736
|
-
const files =
|
|
2608
|
+
if (!fs10.existsSync(snapshotDir)) return [];
|
|
2609
|
+
const files = fs10.readdirSync(snapshotDir);
|
|
2737
2610
|
const metaFiles = files.filter((f) => f.endsWith(".meta"));
|
|
2738
2611
|
const snapshots = [];
|
|
2739
2612
|
for (const metaFile of metaFiles) {
|
|
2740
2613
|
try {
|
|
2741
|
-
const meta = JSON.parse(
|
|
2614
|
+
const meta = JSON.parse(fs10.readFileSync(path10.join(snapshotDir, metaFile), "utf-8"));
|
|
2742
2615
|
snapshots.push(meta);
|
|
2743
2616
|
} catch {
|
|
2744
2617
|
}
|
|
@@ -2759,7 +2632,7 @@ function createBatchSnapshot(rootDir, filePaths, label = "batch") {
|
|
|
2759
2632
|
}
|
|
2760
2633
|
|
|
2761
2634
|
// src/editor/refactor.ts
|
|
2762
|
-
import * as
|
|
2635
|
+
import * as fs11 from "fs";
|
|
2763
2636
|
import chalk10 from "chalk";
|
|
2764
2637
|
async function executeRefactor(plan) {
|
|
2765
2638
|
const snapshots = [];
|
|
@@ -2773,7 +2646,7 @@ async function executeRefactor(plan) {
|
|
|
2773
2646
|
if (result.success && result.applied) {
|
|
2774
2647
|
if (plan.validateSyntax) {
|
|
2775
2648
|
try {
|
|
2776
|
-
const content =
|
|
2649
|
+
const content = fs11.readFileSync(edit.filePath, "utf-8");
|
|
2777
2650
|
const lang = detectLanguage(edit.filePath);
|
|
2778
2651
|
if (!hasValidSyntax(content, lang)) {
|
|
2779
2652
|
errors.push({
|
|
@@ -3113,8 +2986,8 @@ function printSandboxVerdict(verdict) {
|
|
|
3113
2986
|
}
|
|
3114
2987
|
|
|
3115
2988
|
// src/repo/detect.ts
|
|
3116
|
-
import * as
|
|
3117
|
-
import * as
|
|
2989
|
+
import * as fs12 from "fs";
|
|
2990
|
+
import * as path11 from "path";
|
|
3118
2991
|
import chalk14 from "chalk";
|
|
3119
2992
|
var rules = [
|
|
3120
2993
|
{
|
|
@@ -3123,10 +2996,10 @@ var rules = [
|
|
|
3123
2996
|
language: "TypeScript",
|
|
3124
2997
|
check: (dir) => {
|
|
3125
2998
|
const evidence = [];
|
|
3126
|
-
if (
|
|
2999
|
+
if (fs12.existsSync(path11.join(dir, "next.config.js")) || fs12.existsSync(path11.join(dir, "next.config.mjs"))) {
|
|
3127
3000
|
evidence.push("next.config.{js,mjs}");
|
|
3128
3001
|
}
|
|
3129
|
-
const pkg2 = readJson(
|
|
3002
|
+
const pkg2 = readJson(path11.join(dir, "package.json"));
|
|
3130
3003
|
if (pkg2?.dependencies?.next || pkg2?.devDependencies?.next) {
|
|
3131
3004
|
evidence.push("package.json: next dependency");
|
|
3132
3005
|
}
|
|
@@ -3139,9 +3012,9 @@ var rules = [
|
|
|
3139
3012
|
language: "TypeScript/JavaScript",
|
|
3140
3013
|
check: (dir) => {
|
|
3141
3014
|
const evidence = [];
|
|
3142
|
-
const pkg2 = readJson(
|
|
3015
|
+
const pkg2 = readJson(path11.join(dir, "package.json"));
|
|
3143
3016
|
if (pkg2?.dependencies?.react) evidence.push("package.json: react dependency");
|
|
3144
|
-
if (
|
|
3017
|
+
if (fs12.existsSync(path11.join(dir, "jsconfig.json"))) evidence.push("jsconfig.json");
|
|
3145
3018
|
if (findFiles2(dir, ".jsx", 3).length > 0) evidence.push(".jsx files found");
|
|
3146
3019
|
if (findFiles2(dir, ".tsx", 3).length > 0) evidence.push(".tsx files found");
|
|
3147
3020
|
return { detected: evidence.length > 0, confidence: Math.min(evidence.length * 30, 95), evidence };
|
|
@@ -3153,9 +3026,9 @@ var rules = [
|
|
|
3153
3026
|
language: "JavaScript",
|
|
3154
3027
|
check: (dir) => {
|
|
3155
3028
|
const evidence = [];
|
|
3156
|
-
if (
|
|
3157
|
-
if (
|
|
3158
|
-
const pkg2 = readJson(
|
|
3029
|
+
if (fs12.existsSync(path11.join(dir, "package.json"))) evidence.push("package.json");
|
|
3030
|
+
if (fs12.existsSync(path11.join(dir, "package-lock.json"))) evidence.push("package-lock.json");
|
|
3031
|
+
const pkg2 = readJson(path11.join(dir, "package.json"));
|
|
3159
3032
|
if (pkg2 && !pkg2?.dependencies?.react && !pkg2?.dependencies?.next) {
|
|
3160
3033
|
evidence.push("Node.js package (no React/Next)");
|
|
3161
3034
|
}
|
|
@@ -3168,8 +3041,8 @@ var rules = [
|
|
|
3168
3041
|
language: "Go",
|
|
3169
3042
|
check: (dir) => {
|
|
3170
3043
|
const evidence = [];
|
|
3171
|
-
if (
|
|
3172
|
-
if (
|
|
3044
|
+
if (fs12.existsSync(path11.join(dir, "go.mod"))) evidence.push("go.mod");
|
|
3045
|
+
if (fs12.existsSync(path11.join(dir, "go.sum"))) evidence.push("go.sum");
|
|
3173
3046
|
if (findFiles2(dir, ".go", 3).length > 0) evidence.push(".go files found");
|
|
3174
3047
|
return { detected: evidence.length > 0, confidence: evidence.includes("go.mod") ? 98 : 60, evidence };
|
|
3175
3048
|
}
|
|
@@ -3180,11 +3053,11 @@ var rules = [
|
|
|
3180
3053
|
language: "Python",
|
|
3181
3054
|
check: (dir) => {
|
|
3182
3055
|
const evidence = [];
|
|
3183
|
-
if (
|
|
3056
|
+
if (fs12.existsSync(path11.join(dir, "manage.py"))) evidence.push("manage.py");
|
|
3184
3057
|
if (findFiles2(dir, "settings.py", 5).length > 0) evidence.push("settings.py found");
|
|
3185
|
-
const cfg = readFile(
|
|
3058
|
+
const cfg = readFile(path11.join(dir, "requirements.txt"));
|
|
3186
3059
|
if (cfg?.includes("django")) evidence.push("requirements.txt: django");
|
|
3187
|
-
const pipfile = readFile(
|
|
3060
|
+
const pipfile = readFile(path11.join(dir, "Pipfile"));
|
|
3188
3061
|
if (pipfile?.includes("django")) evidence.push("Pipfile: django");
|
|
3189
3062
|
return { detected: evidence.length > 0, confidence: evidence.includes("manage.py") ? 95 : 50, evidence };
|
|
3190
3063
|
}
|
|
@@ -3195,9 +3068,9 @@ var rules = [
|
|
|
3195
3068
|
language: "Dart",
|
|
3196
3069
|
check: (dir) => {
|
|
3197
3070
|
const evidence = [];
|
|
3198
|
-
if (
|
|
3071
|
+
if (fs12.existsSync(path11.join(dir, "pubspec.yaml"))) evidence.push("pubspec.yaml");
|
|
3199
3072
|
if (findFiles2(dir, ".dart", 3).length > 0) evidence.push(".dart files found");
|
|
3200
|
-
if (
|
|
3073
|
+
if (fs12.existsSync(path11.join(dir, "android")) && fs12.existsSync(path11.join(dir, "ios"))) {
|
|
3201
3074
|
evidence.push("android/ & ios/ directories");
|
|
3202
3075
|
}
|
|
3203
3076
|
return { detected: evidence.length > 0, confidence: evidence.includes("pubspec.yaml") ? 95 : 50, evidence };
|
|
@@ -3209,9 +3082,9 @@ var rules = [
|
|
|
3209
3082
|
language: "Python",
|
|
3210
3083
|
check: (dir) => {
|
|
3211
3084
|
const evidence = [];
|
|
3212
|
-
if (
|
|
3213
|
-
if (
|
|
3214
|
-
if (
|
|
3085
|
+
if (fs12.existsSync(path11.join(dir, "requirements.txt"))) evidence.push("requirements.txt");
|
|
3086
|
+
if (fs12.existsSync(path11.join(dir, "setup.py"))) evidence.push("setup.py");
|
|
3087
|
+
if (fs12.existsSync(path11.join(dir, "pyproject.toml"))) evidence.push("pyproject.toml");
|
|
3215
3088
|
if (findFiles2(dir, ".py", 5).length > 0) evidence.push(".py files found");
|
|
3216
3089
|
return { detected: evidence.length > 0, confidence: Math.min(evidence.length * 25, 90), evidence };
|
|
3217
3090
|
}
|
|
@@ -3222,8 +3095,8 @@ var rules = [
|
|
|
3222
3095
|
language: "Rust",
|
|
3223
3096
|
check: (dir) => {
|
|
3224
3097
|
const evidence = [];
|
|
3225
|
-
if (
|
|
3226
|
-
if (
|
|
3098
|
+
if (fs12.existsSync(path11.join(dir, "Cargo.toml"))) evidence.push("Cargo.toml");
|
|
3099
|
+
if (fs12.existsSync(path11.join(dir, "Cargo.lock"))) evidence.push("Cargo.lock");
|
|
3227
3100
|
if (findFiles2(dir, ".rs", 3).length > 0) evidence.push(".rs files found");
|
|
3228
3101
|
return { detected: evidence.length > 0, confidence: evidence.includes("Cargo.toml") ? 98 : 60, evidence };
|
|
3229
3102
|
}
|
|
@@ -3234,7 +3107,7 @@ var rules = [
|
|
|
3234
3107
|
language: "JavaScript",
|
|
3235
3108
|
check: (dir) => {
|
|
3236
3109
|
const evidence = [];
|
|
3237
|
-
const pkg2 = readJson(
|
|
3110
|
+
const pkg2 = readJson(path11.join(dir, "package.json"));
|
|
3238
3111
|
if (pkg2?.dependencies?.vue) evidence.push("package.json: vue");
|
|
3239
3112
|
if (findFiles2(dir, ".vue", 3).length > 0) evidence.push(".vue files found");
|
|
3240
3113
|
return { detected: evidence.length > 0, confidence: Math.min(evidence.length * 40, 90), evidence };
|
|
@@ -3246,8 +3119,8 @@ var rules = [
|
|
|
3246
3119
|
language: "TypeScript",
|
|
3247
3120
|
check: (dir) => {
|
|
3248
3121
|
const evidence = [];
|
|
3249
|
-
if (
|
|
3250
|
-
const pkg2 = readJson(
|
|
3122
|
+
if (fs12.existsSync(path11.join(dir, "angular.json"))) evidence.push("angular.json");
|
|
3123
|
+
const pkg2 = readJson(path11.join(dir, "package.json"));
|
|
3251
3124
|
if (pkg2?.dependencies?.["@angular/core"]) evidence.push("package.json: @angular/core");
|
|
3252
3125
|
return { detected: evidence.length > 0, confidence: Math.min(evidence.length * 40, 95), evidence };
|
|
3253
3126
|
}
|
|
@@ -3255,7 +3128,7 @@ var rules = [
|
|
|
3255
3128
|
];
|
|
3256
3129
|
function readJson(filePath) {
|
|
3257
3130
|
try {
|
|
3258
|
-
const content =
|
|
3131
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
3259
3132
|
return JSON.parse(content);
|
|
3260
3133
|
} catch {
|
|
3261
3134
|
return null;
|
|
@@ -3263,7 +3136,7 @@ function readJson(filePath) {
|
|
|
3263
3136
|
}
|
|
3264
3137
|
function readFile(filePath) {
|
|
3265
3138
|
try {
|
|
3266
|
-
return
|
|
3139
|
+
return fs12.readFileSync(filePath, "utf-8");
|
|
3267
3140
|
} catch {
|
|
3268
3141
|
return null;
|
|
3269
3142
|
}
|
|
@@ -3271,11 +3144,11 @@ function readFile(filePath) {
|
|
|
3271
3144
|
function findFiles2(dir, ext, max) {
|
|
3272
3145
|
const results = [];
|
|
3273
3146
|
try {
|
|
3274
|
-
const entries =
|
|
3147
|
+
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
3275
3148
|
for (const entry of entries) {
|
|
3276
3149
|
if (results.length >= max) break;
|
|
3277
3150
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
3278
|
-
const fullPath =
|
|
3151
|
+
const fullPath = path11.join(dir, entry.name);
|
|
3279
3152
|
if (entry.isDirectory()) {
|
|
3280
3153
|
results.push(...findFiles2(fullPath, ext, max - results.length));
|
|
3281
3154
|
} else if (entry.name.endsWith(ext)) {
|
|
@@ -3304,12 +3177,12 @@ function detectProject(rootDir) {
|
|
|
3304
3177
|
types.sort((a, b) => b.confidence - a.confidence);
|
|
3305
3178
|
const languages = [...new Set(types.map((t) => t.language))];
|
|
3306
3179
|
const primary = types[0] || null;
|
|
3307
|
-
const pkg2 = readJson(
|
|
3180
|
+
const pkg2 = readJson(path11.join(rootDir, "package.json"));
|
|
3308
3181
|
let packageManager = null;
|
|
3309
|
-
if (
|
|
3310
|
-
else if (
|
|
3311
|
-
else if (
|
|
3312
|
-
else if (
|
|
3182
|
+
if (fs12.existsSync(path11.join(rootDir, "yarn.lock"))) packageManager = "yarn";
|
|
3183
|
+
else if (fs12.existsSync(path11.join(rootDir, "pnpm-lock.yaml"))) packageManager = "pnpm";
|
|
3184
|
+
else if (fs12.existsSync(path11.join(rootDir, "package-lock.json"))) packageManager = "npm";
|
|
3185
|
+
else if (fs12.existsSync(path11.join(rootDir, "bun.lockb"))) packageManager = "bun";
|
|
3313
3186
|
let buildTool = null;
|
|
3314
3187
|
if (pkg2?.scripts) {
|
|
3315
3188
|
const scripts = pkg2.scripts;
|
|
@@ -3321,9 +3194,9 @@ function detectProject(rootDir) {
|
|
|
3321
3194
|
else buildTool = "custom";
|
|
3322
3195
|
}
|
|
3323
3196
|
}
|
|
3324
|
-
if (!buildTool &&
|
|
3325
|
-
if (!buildTool &&
|
|
3326
|
-
if (!buildTool &&
|
|
3197
|
+
if (!buildTool && fs12.existsSync(path11.join(rootDir, "webpack.config.js"))) buildTool = "webpack";
|
|
3198
|
+
if (!buildTool && fs12.existsSync(path11.join(rootDir, "vite.config.ts"))) buildTool = "vite";
|
|
3199
|
+
if (!buildTool && fs12.existsSync(path11.join(rootDir, "tsconfig.json"))) buildTool = "tsc";
|
|
3327
3200
|
return { types, primary, languages, packageManager, buildTool };
|
|
3328
3201
|
}
|
|
3329
3202
|
function printProjectInfo(info) {
|
|
@@ -3358,8 +3231,8 @@ function confidenceBar(pct) {
|
|
|
3358
3231
|
}
|
|
3359
3232
|
|
|
3360
3233
|
// src/repo/deps.ts
|
|
3361
|
-
import * as
|
|
3362
|
-
import * as
|
|
3234
|
+
import * as fs13 from "fs";
|
|
3235
|
+
import * as path12 from "path";
|
|
3363
3236
|
import chalk15 from "chalk";
|
|
3364
3237
|
var IMPORT_PATTERNS = [
|
|
3365
3238
|
{ regex: /import\s+(?:\*\s+as\s+\w+\s+from\s+)?['"]([^'"]+)['"]/g, type: "import" },
|
|
@@ -3386,8 +3259,8 @@ function analyzeDependencies(rootDir) {
|
|
|
3386
3259
|
const edges = [];
|
|
3387
3260
|
const sourceFiles = collectSourceFiles(rootDir);
|
|
3388
3261
|
for (const filePath of sourceFiles) {
|
|
3389
|
-
const relativePath =
|
|
3390
|
-
const ext =
|
|
3262
|
+
const relativePath = path12.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
3263
|
+
const ext = path12.extname(filePath).toLowerCase();
|
|
3391
3264
|
let patterns = IMPORT_PATTERNS;
|
|
3392
3265
|
if (ext === ".py") patterns = PYTHON_PATTERNS;
|
|
3393
3266
|
else if (ext === ".go") patterns = GO_PATTERNS;
|
|
@@ -3402,14 +3275,14 @@ function analyzeDependencies(rootDir) {
|
|
|
3402
3275
|
});
|
|
3403
3276
|
}
|
|
3404
3277
|
try {
|
|
3405
|
-
const content =
|
|
3278
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
3406
3279
|
const imports = extractImports(content, patterns);
|
|
3407
3280
|
for (const imp of imports) {
|
|
3408
3281
|
const resolved = resolveImport(imp, relativePath, rootDir, sourceFiles);
|
|
3409
3282
|
edges.push({ source: relativePath, target: resolved, type: "import" });
|
|
3410
3283
|
nodes.get(relativePath).imports.push(imp);
|
|
3411
3284
|
if (!nodes.has(resolved)) {
|
|
3412
|
-
const isExternal = !sourceFiles.includes(resolved) && !
|
|
3285
|
+
const isExternal = !sourceFiles.includes(resolved) && !fs13.existsSync(resolved);
|
|
3413
3286
|
nodes.set(resolved, {
|
|
3414
3287
|
name: resolved,
|
|
3415
3288
|
filePath: resolved,
|
|
@@ -3432,14 +3305,14 @@ function collectSourceFiles(rootDir) {
|
|
|
3432
3305
|
const skipDirs = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "__pycache__", "target", "vendor"]);
|
|
3433
3306
|
function walk(dir) {
|
|
3434
3307
|
try {
|
|
3435
|
-
const entries =
|
|
3308
|
+
const entries = fs13.readdirSync(dir, { withFileTypes: true });
|
|
3436
3309
|
for (const entry of entries) {
|
|
3437
3310
|
if (entry.name.startsWith(".") || skipDirs.has(entry.name)) continue;
|
|
3438
|
-
const fullPath =
|
|
3311
|
+
const fullPath = path12.join(dir, entry.name);
|
|
3439
3312
|
if (entry.isDirectory()) {
|
|
3440
3313
|
walk(fullPath);
|
|
3441
3314
|
} else {
|
|
3442
|
-
const ext =
|
|
3315
|
+
const ext = path12.extname(entry.name).toLowerCase();
|
|
3443
3316
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", ".rb", ".java"].includes(ext)) {
|
|
3444
3317
|
results.push(fullPath);
|
|
3445
3318
|
}
|
|
@@ -3469,12 +3342,12 @@ function extractImports(content, patterns) {
|
|
|
3469
3342
|
}
|
|
3470
3343
|
function resolveImport(imp, relativeFrom, rootDir, allFiles) {
|
|
3471
3344
|
if (imp.startsWith(".")) {
|
|
3472
|
-
const dir =
|
|
3473
|
-
const resolved =
|
|
3345
|
+
const dir = path12.dirname(path12.join(rootDir, relativeFrom));
|
|
3346
|
+
const resolved = path12.resolve(dir, imp);
|
|
3474
3347
|
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", "/index.ts", "/index.js"];
|
|
3475
3348
|
for (const ext of extensions) {
|
|
3476
3349
|
const candidate = resolved + ext;
|
|
3477
|
-
if (allFiles.includes(candidate)) return
|
|
3350
|
+
if (allFiles.includes(candidate)) return path12.relative(rootDir, candidate).replace(/\\/g, "/");
|
|
3478
3351
|
}
|
|
3479
3352
|
return imp;
|
|
3480
3353
|
}
|
|
@@ -3484,15 +3357,15 @@ function detectEntryPoints(rootDir, files) {
|
|
|
3484
3357
|
const entries = [];
|
|
3485
3358
|
const entryNames = ["index.ts", "index.js", "main.ts", "main.js", "app.ts", "app.js", "server.ts", "server.js"];
|
|
3486
3359
|
for (const name of entryNames) {
|
|
3487
|
-
const fp =
|
|
3360
|
+
const fp = path12.join(rootDir, name);
|
|
3488
3361
|
if (files.includes(fp)) entries.push(name);
|
|
3489
3362
|
}
|
|
3490
|
-
if (
|
|
3363
|
+
if (fs13.existsSync(path12.join(rootDir, "package.json"))) {
|
|
3491
3364
|
try {
|
|
3492
|
-
const pkg2 = JSON.parse(
|
|
3365
|
+
const pkg2 = JSON.parse(fs13.readFileSync(path12.join(rootDir, "package.json"), "utf-8"));
|
|
3493
3366
|
if (pkg2.main) {
|
|
3494
|
-
const mainPath =
|
|
3495
|
-
const relative6 =
|
|
3367
|
+
const mainPath = path12.join(rootDir, pkg2.main);
|
|
3368
|
+
const relative6 = path12.relative(rootDir, mainPath).replace(/\\/g, "/");
|
|
3496
3369
|
if (!entries.includes(relative6)) entries.push(relative6);
|
|
3497
3370
|
}
|
|
3498
3371
|
} catch {
|
|
@@ -3538,7 +3411,7 @@ function findCircularDeps(graph) {
|
|
|
3538
3411
|
const visited = /* @__PURE__ */ new Set();
|
|
3539
3412
|
const recursionStack = /* @__PURE__ */ new Set();
|
|
3540
3413
|
const circles = [];
|
|
3541
|
-
function dfs(node,
|
|
3414
|
+
function dfs(node, path21) {
|
|
3542
3415
|
visited.add(node);
|
|
3543
3416
|
recursionStack.add(node);
|
|
3544
3417
|
const nodeData = graph.nodes.get(node);
|
|
@@ -3550,9 +3423,9 @@ function findCircularDeps(graph) {
|
|
|
3550
3423
|
const target = resolveImportInGraph(imp, node, graph);
|
|
3551
3424
|
if (target && graph.nodes.has(target)) {
|
|
3552
3425
|
if (!visited.has(target)) {
|
|
3553
|
-
dfs(target, [...
|
|
3426
|
+
dfs(target, [...path21, target]);
|
|
3554
3427
|
} else if (recursionStack.has(target)) {
|
|
3555
|
-
const cycle = [...
|
|
3428
|
+
const cycle = [...path21.slice(path21.indexOf(target)), target];
|
|
3556
3429
|
circles.push(cycle);
|
|
3557
3430
|
}
|
|
3558
3431
|
}
|
|
@@ -3568,14 +3441,14 @@ function findCircularDeps(graph) {
|
|
|
3568
3441
|
}
|
|
3569
3442
|
function resolveImportInGraph(imp, from, _graph) {
|
|
3570
3443
|
if (!imp.startsWith(".")) return null;
|
|
3571
|
-
const dir =
|
|
3572
|
-
const resolved =
|
|
3444
|
+
const dir = path12.dirname(from);
|
|
3445
|
+
const resolved = path12.normalize(path12.join(dir, imp)).replace(/\\/g, "/");
|
|
3573
3446
|
return resolved;
|
|
3574
3447
|
}
|
|
3575
3448
|
|
|
3576
3449
|
// src/repo/summary.ts
|
|
3577
|
-
import * as
|
|
3578
|
-
import * as
|
|
3450
|
+
import * as fs14 from "fs";
|
|
3451
|
+
import * as path13 from "path";
|
|
3579
3452
|
import chalk16 from "chalk";
|
|
3580
3453
|
function countDirStats(dir) {
|
|
3581
3454
|
let files = 0;
|
|
@@ -3586,20 +3459,20 @@ function countDirStats(dir) {
|
|
|
3586
3459
|
const skipDirs = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "__pycache__", "target", "vendor", "coverage"]);
|
|
3587
3460
|
function walk(dir2) {
|
|
3588
3461
|
try {
|
|
3589
|
-
const entries =
|
|
3462
|
+
const entries = fs14.readdirSync(dir2, { withFileTypes: true });
|
|
3590
3463
|
for (const entry of entries) {
|
|
3591
3464
|
if (entry.name.startsWith(".") || skipDirs.has(entry.name)) continue;
|
|
3592
|
-
const fullPath =
|
|
3465
|
+
const fullPath = path13.join(dir2, entry.name);
|
|
3593
3466
|
if (entry.isDirectory()) {
|
|
3594
3467
|
dirs++;
|
|
3595
3468
|
walk(fullPath);
|
|
3596
3469
|
} else {
|
|
3597
3470
|
files++;
|
|
3598
|
-
const ext =
|
|
3599
|
-
const stats =
|
|
3471
|
+
const ext = path13.extname(entry.name).toLowerCase() || entry.name;
|
|
3472
|
+
const stats = fs14.statSync(fullPath);
|
|
3600
3473
|
let lineCount = 0;
|
|
3601
3474
|
try {
|
|
3602
|
-
const content =
|
|
3475
|
+
const content = fs14.readFileSync(fullPath, "utf-8");
|
|
3603
3476
|
lineCount = content.split("\n").length;
|
|
3604
3477
|
totalLines += lineCount;
|
|
3605
3478
|
} catch {
|
|
@@ -3624,7 +3497,7 @@ function buildDirectoryTree(rootDir, maxDepth = 4) {
|
|
|
3624
3497
|
function walk(dir, prefix, depth) {
|
|
3625
3498
|
if (depth > maxDepth) return;
|
|
3626
3499
|
try {
|
|
3627
|
-
const entries =
|
|
3500
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
3628
3501
|
const filtered = entries.filter((e) => !e.name.startsWith(".") && !skipDirs.has(e.name));
|
|
3629
3502
|
const sorted = filtered.sort((a, b) => {
|
|
3630
3503
|
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
@@ -3638,9 +3511,9 @@ function buildDirectoryTree(rootDir, maxDepth = 4) {
|
|
|
3638
3511
|
const nextPrefix = isLast ? " " : "\u2502 ";
|
|
3639
3512
|
if (entry.isDirectory()) {
|
|
3640
3513
|
result.push(`${prefix}${connector}${chalk16.cyan(entry.name)}/`);
|
|
3641
|
-
walk(
|
|
3514
|
+
walk(path13.join(dir, entry.name), prefix + nextPrefix, depth + 1);
|
|
3642
3515
|
} else {
|
|
3643
|
-
const stats =
|
|
3516
|
+
const stats = fs14.statSync(path13.join(dir, entry.name));
|
|
3644
3517
|
const size = stats.size > 1024 ? `${(stats.size / 1024).toFixed(1)} KB` : `${stats.size} B`;
|
|
3645
3518
|
result.push(`${prefix}${connector}${entry.name} ${chalk16.dim(size)}`);
|
|
3646
3519
|
}
|
|
@@ -3648,7 +3521,7 @@ function buildDirectoryTree(rootDir, maxDepth = 4) {
|
|
|
3648
3521
|
} catch {
|
|
3649
3522
|
}
|
|
3650
3523
|
}
|
|
3651
|
-
result.push(chalk16.bold(`${
|
|
3524
|
+
result.push(chalk16.bold(`${path13.basename(rootDir)}/`));
|
|
3652
3525
|
walk(rootDir, "", 1);
|
|
3653
3526
|
return result.join("\n");
|
|
3654
3527
|
}
|
|
@@ -3766,11 +3639,11 @@ var fileTools = [
|
|
|
3766
3639
|
usage: "path=<file path>",
|
|
3767
3640
|
execute(workingDir, args) {
|
|
3768
3641
|
try {
|
|
3769
|
-
const filePath =
|
|
3770
|
-
if (!
|
|
3642
|
+
const filePath = path14.resolve(workingDir, args.path || ".");
|
|
3643
|
+
if (!fs15.existsSync(filePath)) {
|
|
3771
3644
|
return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
3772
3645
|
}
|
|
3773
|
-
const content =
|
|
3646
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
3774
3647
|
const lines = content.split("\n");
|
|
3775
3648
|
const numbered = lines.map((l, i) => `${String(i + 1).padStart(4, " ")} | ${l}`).join("\n");
|
|
3776
3649
|
return { success: true, output: numbered };
|
|
@@ -3785,9 +3658,9 @@ var fileTools = [
|
|
|
3785
3658
|
usage: "path=<file path> content=<file content>",
|
|
3786
3659
|
execute(workingDir, args) {
|
|
3787
3660
|
try {
|
|
3788
|
-
const filePath =
|
|
3789
|
-
|
|
3790
|
-
|
|
3661
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3662
|
+
fs15.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
3663
|
+
fs15.writeFileSync(filePath, args.content || "", "utf-8");
|
|
3791
3664
|
return { success: true, output: `Wrote ${filePath}` };
|
|
3792
3665
|
} catch (err) {
|
|
3793
3666
|
return { success: false, output: "", error: String(err) };
|
|
@@ -3800,11 +3673,11 @@ var fileTools = [
|
|
|
3800
3673
|
usage: "path=<file path> oldString=<text to find> newString=<replacement>",
|
|
3801
3674
|
execute(workingDir, args) {
|
|
3802
3675
|
try {
|
|
3803
|
-
const filePath =
|
|
3804
|
-
if (!
|
|
3676
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3677
|
+
if (!fs15.existsSync(filePath)) {
|
|
3805
3678
|
return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
3806
3679
|
}
|
|
3807
|
-
const content =
|
|
3680
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
3808
3681
|
const { oldString, newString } = args;
|
|
3809
3682
|
if (!oldString) {
|
|
3810
3683
|
return { success: false, output: "", error: "oldString is required" };
|
|
@@ -3814,7 +3687,7 @@ var fileTools = [
|
|
|
3814
3687
|
return { success: false, output: "", error: `oldString not found in ${args.path}` };
|
|
3815
3688
|
}
|
|
3816
3689
|
const updated = content.replaceAll(oldString, newString || "");
|
|
3817
|
-
|
|
3690
|
+
fs15.writeFileSync(filePath, updated, "utf-8");
|
|
3818
3691
|
return { success: true, output: `Edited ${filePath} (${count} replacement${count > 1 ? "s" : ""})` };
|
|
3819
3692
|
} catch (err) {
|
|
3820
3693
|
return { success: false, output: "", error: String(err) };
|
|
@@ -3827,12 +3700,12 @@ var fileTools = [
|
|
|
3827
3700
|
usage: "path=<file path>",
|
|
3828
3701
|
execute(workingDir, args) {
|
|
3829
3702
|
try {
|
|
3830
|
-
const filePath =
|
|
3831
|
-
if (
|
|
3703
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3704
|
+
if (fs15.existsSync(filePath)) {
|
|
3832
3705
|
return { success: false, output: "", error: `File already exists: ${args.path}` };
|
|
3833
3706
|
}
|
|
3834
|
-
|
|
3835
|
-
|
|
3707
|
+
fs15.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
3708
|
+
fs15.writeFileSync(filePath, "", "utf-8");
|
|
3836
3709
|
return { success: true, output: `Created ${filePath}` };
|
|
3837
3710
|
} catch (err) {
|
|
3838
3711
|
return { success: false, output: "", error: String(err) };
|
|
@@ -3845,11 +3718,11 @@ var fileTools = [
|
|
|
3845
3718
|
usage: "path=<file path>",
|
|
3846
3719
|
execute(workingDir, args) {
|
|
3847
3720
|
try {
|
|
3848
|
-
const filePath =
|
|
3849
|
-
if (!
|
|
3721
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3722
|
+
if (!fs15.existsSync(filePath)) {
|
|
3850
3723
|
return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
3851
3724
|
}
|
|
3852
|
-
|
|
3725
|
+
fs15.unlinkSync(filePath);
|
|
3853
3726
|
return { success: true, output: `Deleted ${filePath}` };
|
|
3854
3727
|
} catch (err) {
|
|
3855
3728
|
return { success: false, output: "", error: String(err) };
|
|
@@ -3862,9 +3735,9 @@ var fileTools = [
|
|
|
3862
3735
|
usage: "path=<file path> content=<content to append>",
|
|
3863
3736
|
execute(workingDir, args) {
|
|
3864
3737
|
try {
|
|
3865
|
-
const filePath =
|
|
3866
|
-
|
|
3867
|
-
|
|
3738
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3739
|
+
fs15.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
3740
|
+
fs15.appendFileSync(filePath, (args.content || "") + "\n", "utf-8");
|
|
3868
3741
|
return { success: true, output: `Appended to ${filePath}` };
|
|
3869
3742
|
} catch (err) {
|
|
3870
3743
|
return { success: false, output: "", error: String(err) };
|
|
@@ -3911,11 +3784,11 @@ var searchTools = [
|
|
|
3911
3784
|
usage: "path=<directory path>",
|
|
3912
3785
|
execute(workingDir, args) {
|
|
3913
3786
|
try {
|
|
3914
|
-
const dirPath =
|
|
3915
|
-
if (!
|
|
3787
|
+
const dirPath = path14.resolve(workingDir, args.path || ".");
|
|
3788
|
+
if (!fs15.existsSync(dirPath)) {
|
|
3916
3789
|
return { success: false, output: "", error: `Directory not found: ${args.path}` };
|
|
3917
3790
|
}
|
|
3918
|
-
const entries =
|
|
3791
|
+
const entries = fs15.readdirSync(dirPath, { withFileTypes: true });
|
|
3919
3792
|
const output = entries.map((e) => e.isDirectory() ? chalk17.cyan(`${e.name}/`) : e.name).join("\n");
|
|
3920
3793
|
return { success: true, output };
|
|
3921
3794
|
} catch (err) {
|
|
@@ -3939,7 +3812,7 @@ var fsEngineTools = [
|
|
|
3939
3812
|
usage: "[path=<dir>] [maxDepth=<number>] [category=<source|config|doc|script|data>]",
|
|
3940
3813
|
execute(workingDir, args) {
|
|
3941
3814
|
try {
|
|
3942
|
-
const rootDir =
|
|
3815
|
+
const rootDir = path14.resolve(workingDir, args.path || ".");
|
|
3943
3816
|
const maxDepth = parseInt(args.maxDepth || "10", 10);
|
|
3944
3817
|
const category = args.category;
|
|
3945
3818
|
const files = scanDirectory({
|
|
@@ -4017,8 +3890,8 @@ ${lines.join("\n")}` };
|
|
|
4017
3890
|
description: "Rename or move a file",
|
|
4018
3891
|
usage: "oldPath=<current path> newPath=<new path>",
|
|
4019
3892
|
execute(workingDir, args) {
|
|
4020
|
-
const oldPath =
|
|
4021
|
-
const newPath =
|
|
3893
|
+
const oldPath = path14.resolve(workingDir, args.oldPath || "");
|
|
3894
|
+
const newPath = path14.resolve(workingDir, args.newPath || "");
|
|
4022
3895
|
const result = renameFile(oldPath, newPath);
|
|
4023
3896
|
return { success: result.success, output: result.message, error: result.error };
|
|
4024
3897
|
}
|
|
@@ -4028,8 +3901,8 @@ ${lines.join("\n")}` };
|
|
|
4028
3901
|
description: "Copy/duplicate a file",
|
|
4029
3902
|
usage: "source=<source path> dest=<destination path>",
|
|
4030
3903
|
execute(workingDir, args) {
|
|
4031
|
-
const sourcePath =
|
|
4032
|
-
const destPath =
|
|
3904
|
+
const sourcePath = path14.resolve(workingDir, args.source || "");
|
|
3905
|
+
const destPath = path14.resolve(workingDir, args.dest || "");
|
|
4033
3906
|
const result = duplicateFile(sourcePath, destPath);
|
|
4034
3907
|
return { success: result.success, output: result.message, error: result.error };
|
|
4035
3908
|
}
|
|
@@ -4040,7 +3913,7 @@ ${lines.join("\n")}` };
|
|
|
4040
3913
|
usage: "[path=<dir>] [maxDepth=<number>]",
|
|
4041
3914
|
execute(workingDir, args) {
|
|
4042
3915
|
try {
|
|
4043
|
-
const dirPath =
|
|
3916
|
+
const dirPath = path14.resolve(workingDir, args.path || ".");
|
|
4044
3917
|
const maxDepth = parseInt(args.maxDepth || "3", 10);
|
|
4045
3918
|
const tree = getDirectoryTree(dirPath, "", maxDepth);
|
|
4046
3919
|
return { success: true, output: tree };
|
|
@@ -4073,7 +3946,7 @@ var editorTools = [
|
|
|
4073
3946
|
description: "Apply a search/replace patch to a file with context matching",
|
|
4074
3947
|
usage: "path=<file> search=<text to find> replace=<replacement>",
|
|
4075
3948
|
execute(workingDir, args) {
|
|
4076
|
-
const filePath =
|
|
3949
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
4077
3950
|
const result = applyInlinePatch(filePath, args.search || "", args.replace || "");
|
|
4078
3951
|
if (result.success) {
|
|
4079
3952
|
saveUndoPoint(workingDir, filePath, "inline_patch");
|
|
@@ -4087,8 +3960,8 @@ var editorTools = [
|
|
|
4087
3960
|
usage: "path=<file path>",
|
|
4088
3961
|
execute(workingDir, args) {
|
|
4089
3962
|
try {
|
|
4090
|
-
const filePath =
|
|
4091
|
-
const content =
|
|
3963
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
3964
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
4092
3965
|
const result = checkBraceBalance(content, filePath);
|
|
4093
3966
|
if (result.valid) {
|
|
4094
3967
|
return { success: true, output: "Syntax check passed: all braces balanced" };
|
|
@@ -4108,7 +3981,7 @@ ${lines.join("\n")}` };
|
|
|
4108
3981
|
description: "Undo the last file edit",
|
|
4109
3982
|
usage: "[path=<file path>]",
|
|
4110
3983
|
execute(workingDir, args) {
|
|
4111
|
-
const filePath = args.path ?
|
|
3984
|
+
const filePath = args.path ? path14.resolve(workingDir, args.path) : void 0;
|
|
4112
3985
|
const result = filePath ? undoForFile(workingDir, filePath) : undoForFile(workingDir, "");
|
|
4113
3986
|
if (result) {
|
|
4114
3987
|
return { success: true, output: `Undone: ${result.label} on ${result.filePath}` };
|
|
@@ -4135,7 +4008,7 @@ ${lines.join("\n")}` };
|
|
|
4135
4008
|
description: "Create a snapshot of a file",
|
|
4136
4009
|
usage: "path=<file path> [label=<optional label>]",
|
|
4137
4010
|
execute(workingDir, args) {
|
|
4138
|
-
const filePath =
|
|
4011
|
+
const filePath = path14.resolve(workingDir, args.path || "");
|
|
4139
4012
|
const label = args.label || "manual";
|
|
4140
4013
|
const snap = createSnapshot(workingDir, filePath, label);
|
|
4141
4014
|
if (snap) {
|
|
@@ -4526,7 +4399,7 @@ var repoTools = [
|
|
|
4526
4399
|
description: "Detect project type, framework, and stack",
|
|
4527
4400
|
usage: "[dir=<directory>]",
|
|
4528
4401
|
execute(workingDir, args) {
|
|
4529
|
-
const dir = args.dir ?
|
|
4402
|
+
const dir = args.dir ? path14.resolve(workingDir, args.dir) : workingDir;
|
|
4530
4403
|
const info = detectProject(dir);
|
|
4531
4404
|
return { success: true, output: printProjectInfo(info) };
|
|
4532
4405
|
}
|
|
@@ -4536,7 +4409,7 @@ var repoTools = [
|
|
|
4536
4409
|
description: "Analyze dependencies and import graph",
|
|
4537
4410
|
usage: "[dir=<directory>]",
|
|
4538
4411
|
execute(workingDir, args) {
|
|
4539
|
-
const dir = args.dir ?
|
|
4412
|
+
const dir = args.dir ? path14.resolve(workingDir, args.dir) : workingDir;
|
|
4540
4413
|
const graph = analyzeDependencies(dir);
|
|
4541
4414
|
return { success: true, output: printDepGraph(graph) };
|
|
4542
4415
|
}
|
|
@@ -4546,7 +4419,7 @@ var repoTools = [
|
|
|
4546
4419
|
description: "Generate a full repository architecture summary",
|
|
4547
4420
|
usage: "[dir=<directory>]",
|
|
4548
4421
|
execute(workingDir, args) {
|
|
4549
|
-
const dir = args.dir ?
|
|
4422
|
+
const dir = args.dir ? path14.resolve(workingDir, args.dir) : workingDir;
|
|
4550
4423
|
const summary = generateSummary(dir);
|
|
4551
4424
|
return { success: true, output: printSummary(summary) };
|
|
4552
4425
|
}
|
|
@@ -4931,11 +4804,11 @@ var runCommand = new Command2("run").alias("r").description("Run LoveCode AI on
|
|
|
4931
4804
|
// src/commands/init.ts
|
|
4932
4805
|
import { Command as Command3 } from "commander";
|
|
4933
4806
|
import chalk22 from "chalk";
|
|
4934
|
-
import * as
|
|
4935
|
-
import * as
|
|
4807
|
+
import * as fs16 from "fs";
|
|
4808
|
+
import * as path16 from "path";
|
|
4936
4809
|
|
|
4937
4810
|
// src/config/setup.ts
|
|
4938
|
-
import * as
|
|
4811
|
+
import * as path15 from "path";
|
|
4939
4812
|
import chalk21 from "chalk";
|
|
4940
4813
|
var MODELS = ["deepseek", "llama3", "codellama", "mistral", "mixtral", "qwen", "phi", "gpt-4o-mini", "claude-3-haiku"];
|
|
4941
4814
|
var PROVIDERS = ["ollama", "groq", "openrouter", "togetherai", "huggingface"];
|
|
@@ -5010,7 +4883,7 @@ async function runSetup(rootDir) {
|
|
|
5010
4883
|
};
|
|
5011
4884
|
saveConfig(config, dir);
|
|
5012
4885
|
console.log(chalk21.green(`
|
|
5013
|
-
\u2713 Config saved to ${
|
|
4886
|
+
\u2713 Config saved to ${path15.join(dir, ".lovecode/config.yaml")}`));
|
|
5014
4887
|
if (createEnv) {
|
|
5015
4888
|
const envVars = {};
|
|
5016
4889
|
if (apiKey && provider !== "ollama") {
|
|
@@ -5018,7 +4891,7 @@ async function runSetup(rootDir) {
|
|
|
5018
4891
|
if (envKey) envVars[envKey.key] = apiKey;
|
|
5019
4892
|
}
|
|
5020
4893
|
saveEnv(envVars, dir);
|
|
5021
|
-
console.log(chalk21.green(` \u2713 Environment saved to ${
|
|
4894
|
+
console.log(chalk21.green(` \u2713 Environment saved to ${path15.join(dir, ".env")}`));
|
|
5022
4895
|
}
|
|
5023
4896
|
console.log(chalk21.dim("\n Final configuration:"));
|
|
5024
4897
|
console.log(formatConfig(loadConfig(dir)));
|
|
@@ -5028,9 +4901,9 @@ async function runSetup(rootDir) {
|
|
|
5028
4901
|
// src/commands/init.ts
|
|
5029
4902
|
var initCommand = new Command3("init").alias("i").description("Initialize LoveCode AI in the current project").option("-f, --force", "Overwrite existing configuration").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
5030
4903
|
const dir = options.dir || process.cwd();
|
|
5031
|
-
const configPath =
|
|
4904
|
+
const configPath = path16.join(dir, ".lovecode/config.yaml");
|
|
5032
4905
|
const force = options.force;
|
|
5033
|
-
if (
|
|
4906
|
+
if (fs16.existsSync(configPath) && !force) {
|
|
5034
4907
|
console.log(chalk22.yellow(`
|
|
5035
4908
|
Config already exists at ${configPath}`));
|
|
5036
4909
|
console.log(chalk22.dim(' Use --force to overwrite, or run "lovecode setup" for interactive setup.\n'));
|
|
@@ -5041,7 +4914,7 @@ var initCommand = new Command3("init").alias("i").description("Initialize LoveCo
|
|
|
5041
4914
|
saveEnvExample(dir);
|
|
5042
4915
|
console.log(chalk22.bold.cyan("\n LoveCode AI \u26A1 Initialized\n"));
|
|
5043
4916
|
console.log(chalk22.green(` \u2713 ${configPath}`));
|
|
5044
|
-
console.log(chalk22.green(` \u2713 ${
|
|
4917
|
+
console.log(chalk22.green(` \u2713 ${path16.join(dir, ".env.example")}`));
|
|
5045
4918
|
console.log(formatConfig(config));
|
|
5046
4919
|
console.log(chalk22.dim('\n Run "lovecode setup" for interactive configuration,'));
|
|
5047
4920
|
console.log(chalk22.dim(" or edit .lovecode/config.yaml directly.\n"));
|
|
@@ -5188,18 +5061,18 @@ import { Command as Command6 } from "commander";
|
|
|
5188
5061
|
import chalk25 from "chalk";
|
|
5189
5062
|
|
|
5190
5063
|
// src/platform/detect.ts
|
|
5191
|
-
import * as
|
|
5064
|
+
import * as fs17 from "fs";
|
|
5192
5065
|
import * as os from "os";
|
|
5193
5066
|
var _isTermux = null;
|
|
5194
5067
|
var _isCodespaces = null;
|
|
5195
5068
|
function isTermux() {
|
|
5196
5069
|
if (_isTermux !== null) return _isTermux;
|
|
5197
|
-
_isTermux =
|
|
5070
|
+
_isTermux = fs17.existsSync("/data/data/com.termux") || process.env.PREFIX === "/data/data/com.termux/files/usr" || !!process.env.TERMUX_VERSION;
|
|
5198
5071
|
return _isTermux;
|
|
5199
5072
|
}
|
|
5200
5073
|
function isCodespaces() {
|
|
5201
5074
|
if (_isCodespaces !== null) return _isCodespaces;
|
|
5202
|
-
_isCodespaces = process.env.CODESPACES === "true" || !!process.env.CODESPACE_NAME ||
|
|
5075
|
+
_isCodespaces = process.env.CODESPACES === "true" || !!process.env.CODESPACE_NAME || fs17.existsSync("/.codespaces");
|
|
5203
5076
|
return _isCodespaces;
|
|
5204
5077
|
}
|
|
5205
5078
|
function lowRamMode() {
|
|
@@ -5251,8 +5124,8 @@ import { Command as Command7 } from "commander";
|
|
|
5251
5124
|
import chalk27 from "chalk";
|
|
5252
5125
|
|
|
5253
5126
|
// src/telemetry/telemetry.ts
|
|
5254
|
-
import * as
|
|
5255
|
-
import * as
|
|
5127
|
+
import * as fs18 from "fs";
|
|
5128
|
+
import * as path17 from "path";
|
|
5256
5129
|
import * as os2 from "os";
|
|
5257
5130
|
import { createRequire as createRequire2 } from "module";
|
|
5258
5131
|
import chalk26 from "chalk";
|
|
@@ -5260,7 +5133,7 @@ var _require2 = createRequire2(import.meta.url);
|
|
|
5260
5133
|
var TELEMETRY_DIR = ".lovecode/telemetry";
|
|
5261
5134
|
var _enabled = null;
|
|
5262
5135
|
function telemetryDir(rootDir) {
|
|
5263
|
-
return
|
|
5136
|
+
return path17.resolve(rootDir || process.cwd(), TELEMETRY_DIR);
|
|
5264
5137
|
}
|
|
5265
5138
|
function isTelemetryEnabled(rootDir) {
|
|
5266
5139
|
if (_enabled !== null) return _enabled;
|
|
@@ -5272,8 +5145,8 @@ function isTelemetryEnabled(rootDir) {
|
|
|
5272
5145
|
_enabled = false;
|
|
5273
5146
|
return false;
|
|
5274
5147
|
}
|
|
5275
|
-
const configPath =
|
|
5276
|
-
if (
|
|
5148
|
+
const configPath = path17.resolve(rootDir || process.cwd(), ".lovecode/config.yaml");
|
|
5149
|
+
if (fs18.existsSync(configPath)) {
|
|
5277
5150
|
try {
|
|
5278
5151
|
const { loadConfig: loadConfig2 } = _require2("../config/config.js");
|
|
5279
5152
|
const config = loadConfig2(rootDir);
|
|
@@ -5314,10 +5187,10 @@ function getTelemetryData(rootDir) {
|
|
|
5314
5187
|
const dir = telemetryDir(rootDir);
|
|
5315
5188
|
const events = [];
|
|
5316
5189
|
const crashes = [];
|
|
5317
|
-
if (!
|
|
5318
|
-
for (const file of
|
|
5190
|
+
if (!fs18.existsSync(dir)) return { events, crashes };
|
|
5191
|
+
for (const file of fs18.readdirSync(dir)) {
|
|
5319
5192
|
try {
|
|
5320
|
-
const content =
|
|
5193
|
+
const content = fs18.readFileSync(path17.join(dir, file), "utf-8");
|
|
5321
5194
|
const data = JSON.parse(content);
|
|
5322
5195
|
if (file.startsWith("crash-")) {
|
|
5323
5196
|
crashes.push(data);
|
|
@@ -5331,8 +5204,8 @@ function getTelemetryData(rootDir) {
|
|
|
5331
5204
|
}
|
|
5332
5205
|
function clearTelemetryData(rootDir) {
|
|
5333
5206
|
const dir = telemetryDir(rootDir);
|
|
5334
|
-
if (
|
|
5335
|
-
|
|
5207
|
+
if (fs18.existsSync(dir)) {
|
|
5208
|
+
fs18.rmSync(dir, { recursive: true, force: true });
|
|
5336
5209
|
}
|
|
5337
5210
|
}
|
|
5338
5211
|
function formatTelemetryStatus(enabled, data) {
|
|
@@ -5390,8 +5263,8 @@ import { Command as Command8 } from "commander";
|
|
|
5390
5263
|
import chalk29 from "chalk";
|
|
5391
5264
|
|
|
5392
5265
|
// src/installers/install.ts
|
|
5393
|
-
import * as
|
|
5394
|
-
import * as
|
|
5266
|
+
import * as fs19 from "fs";
|
|
5267
|
+
import * as path18 from "path";
|
|
5395
5268
|
import { execSync as execSync4 } from "child_process";
|
|
5396
5269
|
import chalk28 from "chalk";
|
|
5397
5270
|
function detectInstallMethod() {
|
|
@@ -5433,7 +5306,7 @@ function printInstallInstructions() {
|
|
|
5433
5306
|
}
|
|
5434
5307
|
function createInstallScript(rootDir) {
|
|
5435
5308
|
const dir = rootDir || process.cwd();
|
|
5436
|
-
const scriptPath =
|
|
5309
|
+
const scriptPath = path18.join(dir, "install.sh");
|
|
5437
5310
|
const script = `#!/usr/bin/env bash
|
|
5438
5311
|
set -euo pipefail
|
|
5439
5312
|
|
|
@@ -5488,11 +5361,11 @@ echo " Run: lovecode setup Interactive configuration"
|
|
|
5488
5361
|
echo " Run: lovecode Start the AI agent"
|
|
5489
5362
|
echo ""
|
|
5490
5363
|
`;
|
|
5491
|
-
const scriptDir =
|
|
5492
|
-
if (!
|
|
5493
|
-
|
|
5364
|
+
const scriptDir = path18.dirname(scriptPath);
|
|
5365
|
+
if (!fs19.existsSync(scriptDir)) fs19.mkdirSync(scriptDir, { recursive: true });
|
|
5366
|
+
fs19.writeFileSync(scriptPath, script, "utf-8");
|
|
5494
5367
|
try {
|
|
5495
|
-
|
|
5368
|
+
fs19.chmodSync(scriptPath, "755");
|
|
5496
5369
|
} catch {
|
|
5497
5370
|
}
|
|
5498
5371
|
}
|
|
@@ -5603,8 +5476,8 @@ import { Command as Command10 } from "commander";
|
|
|
5603
5476
|
import chalk32 from "chalk";
|
|
5604
5477
|
|
|
5605
5478
|
// src/repo/search.ts
|
|
5606
|
-
import * as
|
|
5607
|
-
import * as
|
|
5479
|
+
import * as fs20 from "fs";
|
|
5480
|
+
import * as path19 from "path";
|
|
5608
5481
|
import chalk31 from "chalk";
|
|
5609
5482
|
function chunkFile(content, maxLines = 50) {
|
|
5610
5483
|
const lines = content.split("\n");
|
|
@@ -5641,7 +5514,7 @@ async function semanticSearch2(rootDir, query, options = { rootDir: "", query: "
|
|
|
5641
5514
|
const queryEmbedding = await getEmbedding(query);
|
|
5642
5515
|
const files = collectTextFiles(rootDir, 200);
|
|
5643
5516
|
for (const filePath of files) {
|
|
5644
|
-
const relativePath =
|
|
5517
|
+
const relativePath = path19.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
5645
5518
|
const content = readFileSafe(filePath);
|
|
5646
5519
|
if (!content) continue;
|
|
5647
5520
|
const chunks = chunkFile(content, 30);
|
|
@@ -5711,14 +5584,14 @@ function collectTextFiles(dir, max) {
|
|
|
5711
5584
|
function walk(dir2) {
|
|
5712
5585
|
if (results.length >= max) return;
|
|
5713
5586
|
try {
|
|
5714
|
-
const entries =
|
|
5587
|
+
const entries = fs20.readdirSync(dir2, { withFileTypes: true });
|
|
5715
5588
|
for (const entry of entries) {
|
|
5716
5589
|
if (results.length >= max) return;
|
|
5717
5590
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".git") continue;
|
|
5718
|
-
const fullPath =
|
|
5591
|
+
const fullPath = path19.join(dir2, entry.name);
|
|
5719
5592
|
if (entry.isDirectory()) {
|
|
5720
5593
|
walk(fullPath);
|
|
5721
|
-
} else if (extSet.has(
|
|
5594
|
+
} else if (extSet.has(path19.extname(entry.name).toLowerCase())) {
|
|
5722
5595
|
results.push(fullPath);
|
|
5723
5596
|
}
|
|
5724
5597
|
}
|
|
@@ -5730,9 +5603,9 @@ function collectTextFiles(dir, max) {
|
|
|
5730
5603
|
}
|
|
5731
5604
|
function readFileSafe(filePath) {
|
|
5732
5605
|
try {
|
|
5733
|
-
const stats =
|
|
5606
|
+
const stats = fs20.statSync(filePath);
|
|
5734
5607
|
if (stats.size > 5e5) return null;
|
|
5735
|
-
return
|
|
5608
|
+
return fs20.readFileSync(filePath, "utf-8");
|
|
5736
5609
|
} catch {
|
|
5737
5610
|
return null;
|
|
5738
5611
|
}
|
|
@@ -6243,8 +6116,8 @@ import React4 from "react";
|
|
|
6243
6116
|
import { render } from "ink";
|
|
6244
6117
|
|
|
6245
6118
|
// src/tui/App.tsx
|
|
6246
|
-
import { useState as useState3, useCallback as useCallback2 } from "react";
|
|
6247
|
-
import { Box as Box3 } from "ink";
|
|
6119
|
+
import { useState as useState3, useCallback as useCallback2, useEffect as useEffect4 } from "react";
|
|
6120
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
6248
6121
|
|
|
6249
6122
|
// src/tui/components.tsx
|
|
6250
6123
|
import React, { useEffect as useEffect2 } from "react";
|
|
@@ -6465,7 +6338,7 @@ function CommandPane({ commands, focused }) {
|
|
|
6465
6338
|
] }, i))
|
|
6466
6339
|
] }) });
|
|
6467
6340
|
}
|
|
6468
|
-
function StatusBar({ mode, theme: themeName, focus, vimMode, messages }) {
|
|
6341
|
+
function StatusBar({ mode, theme: themeName, focus, vimMode, messages, sessionName, provider, model }) {
|
|
6469
6342
|
const theme = getTheme();
|
|
6470
6343
|
return /* @__PURE__ */ jsxs(
|
|
6471
6344
|
Box,
|
|
@@ -6478,11 +6351,27 @@ function StatusBar({ mode, theme: themeName, focus, vimMode, messages }) {
|
|
|
6478
6351
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
6479
6352
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.secondary, children: "LoveCode" }),
|
|
6480
6353
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.textDim, children: " AI " }),
|
|
6354
|
+
sessionName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6355
|
+
/* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: "|" }),
|
|
6356
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.colors.textDim, children: [
|
|
6357
|
+
" ",
|
|
6358
|
+
sessionName,
|
|
6359
|
+
" "
|
|
6360
|
+
] })
|
|
6361
|
+
] }),
|
|
6481
6362
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: "|" }),
|
|
6482
6363
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.text, children: " Mode: " }),
|
|
6483
6364
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.primary, children: mode })
|
|
6484
6365
|
] }),
|
|
6485
6366
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
6367
|
+
provider && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6368
|
+
/* @__PURE__ */ jsx(Text, { color: theme.colors.textDim, children: provider }),
|
|
6369
|
+
/* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: " | " })
|
|
6370
|
+
] }),
|
|
6371
|
+
model && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6372
|
+
/* @__PURE__ */ jsx(Text, { color: theme.colors.textDim, children: model }),
|
|
6373
|
+
/* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: " | " })
|
|
6374
|
+
] }),
|
|
6486
6375
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.textDim, children: "Focus: " }),
|
|
6487
6376
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.accent, children: focus }),
|
|
6488
6377
|
/* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: " | " }),
|
|
@@ -6505,29 +6394,60 @@ function StatusBar({ mode, theme: themeName, focus, vimMode, messages }) {
|
|
|
6505
6394
|
import { useState as useState2, useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
6506
6395
|
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
6507
6396
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
6508
|
-
|
|
6397
|
+
var SLASH_COMMANDS = ["help", "clear", "theme", "connect", "system", "model", "themes", "export", "sessions", "vim", "exit"];
|
|
6398
|
+
function formatTime(ts) {
|
|
6399
|
+
if (!ts) return "";
|
|
6400
|
+
const d = new Date(ts);
|
|
6401
|
+
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
|
|
6402
|
+
}
|
|
6403
|
+
function MessageRow({ msg, isStreaming, streamContent, timestamp }) {
|
|
6509
6404
|
const theme = getTheme();
|
|
6510
6405
|
const displayContent = isStreaming && streamContent !== void 0 ? streamContent : msg.content;
|
|
6511
6406
|
const label = msg.role === "user" ? "You" : msg.role === "assistant" ? "LoveCode" : "System";
|
|
6512
6407
|
const labelColor = msg.role === "user" ? theme.colors.success : msg.role === "assistant" ? theme.colors.primary : theme.colors.warning;
|
|
6513
6408
|
const formatted = msg.role === "assistant" || msg.role === "system" ? renderMarkdown(displayContent) : displayContent;
|
|
6409
|
+
const time = formatTime(timestamp);
|
|
6410
|
+
const lines = formatted.split("\n");
|
|
6514
6411
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
|
|
6515
|
-
/* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: labelColor, children: label }) }),
|
|
6516
6412
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6517
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
6518
|
-
|
|
6519
|
-
] })
|
|
6413
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: labelColor, children: label }),
|
|
6414
|
+
time && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.textDim, children: ` ${time}` })
|
|
6415
|
+
] }),
|
|
6416
|
+
lines.map((line, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
6417
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.colors.text, children: line }),
|
|
6418
|
+
isStreaming && i === lines.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.primary, children: "\u2588" })
|
|
6419
|
+
] }, i))
|
|
6520
6420
|
] });
|
|
6521
6421
|
}
|
|
6522
6422
|
function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeholder = "Type a message..." }) {
|
|
6523
6423
|
const [input, setInput] = useState2("");
|
|
6524
6424
|
const [vimMode, setVimMode] = useState2("insert");
|
|
6425
|
+
const [history, setHistory] = useState2([]);
|
|
6426
|
+
const [historyIdx, setHistoryIdx] = useState2(-1);
|
|
6427
|
+
const [tabIndex, setTabIndex] = useState2(-1);
|
|
6525
6428
|
const scroll = useScroll();
|
|
6526
6429
|
const inputRef = useRef2(input);
|
|
6527
6430
|
inputRef.current = input;
|
|
6528
6431
|
useEffect3(() => {
|
|
6529
6432
|
scroll.scrollToBottom();
|
|
6530
6433
|
}, [messages.length, streamedContent]);
|
|
6434
|
+
function submitInput(val) {
|
|
6435
|
+
if (!val.trim()) return;
|
|
6436
|
+
onSend(val);
|
|
6437
|
+
setHistory((prev) => [val, ...prev].slice(0, 50));
|
|
6438
|
+
setHistoryIdx(-1);
|
|
6439
|
+
setInput("");
|
|
6440
|
+
}
|
|
6441
|
+
function handleTab() {
|
|
6442
|
+
const trimmed = input.trim().toLowerCase();
|
|
6443
|
+
if (!trimmed.startsWith("/")) return;
|
|
6444
|
+
const partial = trimmed.slice(1);
|
|
6445
|
+
const matches = SLASH_COMMANDS.filter((c) => c.startsWith(partial));
|
|
6446
|
+
if (matches.length === 0) return;
|
|
6447
|
+
const next = (tabIndex + 1) % matches.length;
|
|
6448
|
+
setTabIndex(next);
|
|
6449
|
+
setInput("/" + matches[next] + " ");
|
|
6450
|
+
}
|
|
6531
6451
|
useInput2((text, key) => {
|
|
6532
6452
|
if (!focused) return;
|
|
6533
6453
|
if (vimMode === "normal") {
|
|
@@ -6556,27 +6476,42 @@ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeh
|
|
|
6556
6476
|
scroll.scrollToBottom();
|
|
6557
6477
|
return;
|
|
6558
6478
|
}
|
|
6479
|
+
if (text === "0") {
|
|
6480
|
+
setInput("");
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
6559
6483
|
return;
|
|
6560
6484
|
}
|
|
6561
6485
|
if (key.escape) {
|
|
6562
6486
|
setVimMode("normal");
|
|
6563
6487
|
return;
|
|
6564
6488
|
}
|
|
6565
|
-
if (key.return
|
|
6566
|
-
|
|
6567
|
-
setInput("");
|
|
6489
|
+
if (key.return) {
|
|
6490
|
+
submitInput(input);
|
|
6568
6491
|
return;
|
|
6569
6492
|
}
|
|
6570
6493
|
if (key.backspace || key.delete) {
|
|
6571
6494
|
setInput((prev) => prev.slice(0, -1));
|
|
6495
|
+
setTabIndex(-1);
|
|
6572
6496
|
return;
|
|
6573
6497
|
}
|
|
6574
6498
|
if (key.upArrow) {
|
|
6575
|
-
|
|
6499
|
+
if (history.length > 0) {
|
|
6500
|
+
const nextIdx = historyIdx < history.length - 1 ? historyIdx + 1 : historyIdx;
|
|
6501
|
+
setHistoryIdx(nextIdx);
|
|
6502
|
+
setInput(history[nextIdx] || "");
|
|
6503
|
+
}
|
|
6576
6504
|
return;
|
|
6577
6505
|
}
|
|
6578
6506
|
if (key.downArrow) {
|
|
6579
|
-
|
|
6507
|
+
if (historyIdx > 0) {
|
|
6508
|
+
const nextIdx = historyIdx - 1;
|
|
6509
|
+
setHistoryIdx(nextIdx);
|
|
6510
|
+
setInput(history[nextIdx]);
|
|
6511
|
+
} else {
|
|
6512
|
+
setHistoryIdx(-1);
|
|
6513
|
+
setInput("");
|
|
6514
|
+
}
|
|
6580
6515
|
return;
|
|
6581
6516
|
}
|
|
6582
6517
|
if (key.pageUp) {
|
|
@@ -6587,6 +6522,11 @@ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeh
|
|
|
6587
6522
|
for (let i = 0; i < 10; i++) scroll.scrollDown();
|
|
6588
6523
|
return;
|
|
6589
6524
|
}
|
|
6525
|
+
if (text === " ") {
|
|
6526
|
+
handleTab();
|
|
6527
|
+
return;
|
|
6528
|
+
}
|
|
6529
|
+
setTabIndex(-1);
|
|
6590
6530
|
if (text && text.length === 1 && !key.ctrl && !key.meta) {
|
|
6591
6531
|
setInput((prev) => prev + text);
|
|
6592
6532
|
}
|
|
@@ -6594,7 +6534,8 @@ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeh
|
|
|
6594
6534
|
const theme = getTheme();
|
|
6595
6535
|
const cursor = vimMode === "normal" ? "\u258C" : "\u2588";
|
|
6596
6536
|
const modeIndicator = vimMode === "normal" ? " NORMAL " : " INSERT ";
|
|
6597
|
-
const
|
|
6537
|
+
const hasMore = messages.length > 10;
|
|
6538
|
+
const totalMessages = messages.length;
|
|
6598
6539
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: "100%", width: "100%", children: [
|
|
6599
6540
|
/* @__PURE__ */ jsxs2(
|
|
6600
6541
|
Box2,
|
|
@@ -6606,25 +6547,25 @@ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeh
|
|
|
6606
6547
|
paddingX: 1,
|
|
6607
6548
|
paddingY: 0,
|
|
6608
6549
|
children: [
|
|
6609
|
-
messages.length === 0 && /* @__PURE__ */
|
|
6550
|
+
messages.length === 0 && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", alignItems: "center", marginTop: 2, children: [
|
|
6551
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.primary, children: "LoveCode AI" }),
|
|
6552
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.colors.textDim, children: "Type a message or /help to start" })
|
|
6553
|
+
] }),
|
|
6610
6554
|
messages.map((msg, i) => /* @__PURE__ */ jsx2(
|
|
6611
6555
|
MessageRow,
|
|
6612
6556
|
{
|
|
6613
6557
|
msg,
|
|
6614
6558
|
isStreaming: streaming && i === messages.length - 1,
|
|
6615
|
-
streamContent: streaming && i === messages.length - 1 ? streamedContent : void 0
|
|
6559
|
+
streamContent: streaming && i === messages.length - 1 ? streamedContent : void 0,
|
|
6560
|
+
timestamp: msg.timestamp
|
|
6616
6561
|
},
|
|
6617
6562
|
i
|
|
6618
6563
|
)),
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
"
|
|
6623
|
-
|
|
6624
|
-
" of ",
|
|
6625
|
-
messages.length,
|
|
6626
|
-
") --"
|
|
6627
|
-
] })
|
|
6564
|
+
streaming && !streamedContent && /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: theme.colors.warning, children: "Thinking..." }) }),
|
|
6565
|
+
hasMore && /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.textDim, children: [
|
|
6566
|
+
totalMessages,
|
|
6567
|
+
" messages \u2014 j/k to scroll"
|
|
6568
|
+
] }) })
|
|
6628
6569
|
]
|
|
6629
6570
|
}
|
|
6630
6571
|
),
|
|
@@ -6637,8 +6578,8 @@ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeh
|
|
|
6637
6578
|
paddingX: 1,
|
|
6638
6579
|
children: [
|
|
6639
6580
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.warning, children: modeIndicator }),
|
|
6640
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.success, children: "
|
|
6641
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.colors.text, children: input || /* @__PURE__ */ jsx2(Text2, { color: theme.colors.textDim, children: placeholder }) }),
|
|
6581
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.success, children: "> " }),
|
|
6582
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.colors.text, children: input || /* @__PURE__ */ jsx2(Text2, { italic: true, color: theme.colors.textDim, children: placeholder }) }),
|
|
6642
6583
|
focused && vimMode === "insert" && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.primary, children: cursor })
|
|
6643
6584
|
]
|
|
6644
6585
|
}
|
|
@@ -6657,15 +6598,27 @@ function App({
|
|
|
6657
6598
|
framework: _fw,
|
|
6658
6599
|
repoStatus: _rs,
|
|
6659
6600
|
onSendMessage,
|
|
6660
|
-
onRunCommand
|
|
6601
|
+
onRunCommand,
|
|
6602
|
+
sessionId: _sid,
|
|
6603
|
+
sessionName: _sname,
|
|
6604
|
+
provider: _provider,
|
|
6605
|
+
model: _model
|
|
6661
6606
|
}) {
|
|
6662
|
-
const [messages, setMessages] = useState3(
|
|
6607
|
+
const [messages, setMessages] = useState3(
|
|
6608
|
+
initialMessages.map((m) => ({ ...m, timestamp: Date.now() }))
|
|
6609
|
+
);
|
|
6663
6610
|
const [commands, setCommands] = useState3([]);
|
|
6664
6611
|
const [streaming, setStreaming] = useState3(false);
|
|
6665
6612
|
const [streamedContent, setStreamedContent] = useState3("");
|
|
6666
6613
|
const [focus, setFocus] = useFocus();
|
|
6667
6614
|
const [currentTheme, setCurrentThemeState] = useState3("default");
|
|
6615
|
+
const [systemPrompt, setSystemPrompt] = useState3("You are LoveCode AI, a terminal-native coding assistant.");
|
|
6616
|
+
const [splash, setSplash] = useState3(true);
|
|
6668
6617
|
useTerminalSize();
|
|
6618
|
+
useEffect4(() => {
|
|
6619
|
+
const timer = setTimeout(() => setSplash(false), 1500);
|
|
6620
|
+
return () => clearTimeout(timer);
|
|
6621
|
+
}, []);
|
|
6669
6622
|
const setCurrentTheme = useCallback2((name) => {
|
|
6670
6623
|
setTheme(name);
|
|
6671
6624
|
setCurrentThemeState(name);
|
|
@@ -6675,6 +6628,11 @@ function App({
|
|
|
6675
6628
|
setFocus(focus === "chat" ? "command" : focus === "command" ? "repo" : focus === "repo" ? "chat" : "chat");
|
|
6676
6629
|
}
|
|
6677
6630
|
});
|
|
6631
|
+
function addMessage(role, content) {
|
|
6632
|
+
const entry = { role, content, timestamp: Date.now() };
|
|
6633
|
+
setMessages((prev) => [...prev, entry]);
|
|
6634
|
+
return entry;
|
|
6635
|
+
}
|
|
6678
6636
|
async function handleConnect() {
|
|
6679
6637
|
try {
|
|
6680
6638
|
const { getAllProviders } = await import("./registry-ADSIKXA4.js");
|
|
@@ -6693,19 +6651,83 @@ function App({
|
|
|
6693
6651
|
info += "**Available providers:**\n";
|
|
6694
6652
|
for (const p of providers) {
|
|
6695
6653
|
const tag = p.local ? "(local)" : "(cloud)";
|
|
6696
|
-
|
|
6697
|
-
info += ` ${p.name} ${tag}${configured}
|
|
6654
|
+
info += ` ${p.name} ${tag}${config.provider === p.name ? " \u2190 active" : ""}
|
|
6698
6655
|
`;
|
|
6699
6656
|
}
|
|
6700
6657
|
info += "\n**To configure:** use `lovecode init` in your terminal, then restart TUI";
|
|
6701
|
-
|
|
6658
|
+
addMessage("assistant", info);
|
|
6659
|
+
} catch (err) {
|
|
6660
|
+
addMessage("system", `Error loading providers: ${err.message}`);
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
async function handleModel() {
|
|
6664
|
+
try {
|
|
6665
|
+
const { loadConfig: loadConfig2 } = await import("./config-FJNTTKR3.js");
|
|
6666
|
+
const config = loadConfig2();
|
|
6667
|
+
const info = `**Current AI Configuration**
|
|
6668
|
+
|
|
6669
|
+
Provider: \`${config.provider || "not set"}\`
|
|
6670
|
+
Model: \`${config.model || "not set"}\`
|
|
6671
|
+
Theme: \`${currentTheme}\`
|
|
6672
|
+
System prompt: ${systemPrompt}`;
|
|
6673
|
+
addMessage("assistant", info);
|
|
6674
|
+
} catch (err) {
|
|
6675
|
+
addMessage("system", `Error: ${err.message}`);
|
|
6676
|
+
}
|
|
6677
|
+
}
|
|
6678
|
+
function handleThemes() {
|
|
6679
|
+
const names = getThemeNames();
|
|
6680
|
+
let info = "**Available Themes**\n\n";
|
|
6681
|
+
for (const name of names) {
|
|
6682
|
+
info += ` ${currentTheme === name ? "\u25CF" : "\u25CB"} **${name}**${currentTheme === name ? " (active)" : ""}
|
|
6683
|
+
`;
|
|
6684
|
+
}
|
|
6685
|
+
info += "\nUse `/theme <name>` to switch.";
|
|
6686
|
+
addMessage("assistant", info);
|
|
6687
|
+
}
|
|
6688
|
+
function handleSystem(prompt) {
|
|
6689
|
+
if (!prompt) {
|
|
6690
|
+
addMessage("system", `Current system prompt: "${systemPrompt}"
|
|
6691
|
+
Use \`/system <new prompt>\` to change it.`);
|
|
6692
|
+
return;
|
|
6693
|
+
}
|
|
6694
|
+
setSystemPrompt(prompt);
|
|
6695
|
+
addMessage("system", `System prompt updated to: "${prompt}"`);
|
|
6696
|
+
}
|
|
6697
|
+
async function handleExport() {
|
|
6698
|
+
try {
|
|
6699
|
+
const { writeChatLog: writeChatLog2 } = await import("./chatlog-WSYM3DWQ.js");
|
|
6700
|
+
const entries = messages.map((m) => ({ role: m.role, content: m.content, timestamp: m.timestamp }));
|
|
6701
|
+
const path21 = writeChatLog2(_sid || "unknown", _sname || "TUI Chat", entries);
|
|
6702
|
+
addMessage("system", `Chat exported to: ${path21}`);
|
|
6702
6703
|
} catch (err) {
|
|
6703
|
-
|
|
6704
|
+
addMessage("system", `Export failed: ${err.message}`);
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
async function handleSessions() {
|
|
6708
|
+
try {
|
|
6709
|
+
const { listSessions: listSessions2 } = await import("./session-LTPGPQPI.js");
|
|
6710
|
+
const sessions = listSessions2();
|
|
6711
|
+
if (sessions.length === 0) {
|
|
6712
|
+
addMessage("system", "No saved sessions found. Messages are auto-saved during your session.");
|
|
6713
|
+
return;
|
|
6714
|
+
}
|
|
6715
|
+
let info = "**Saved Sessions**\n\n";
|
|
6716
|
+
for (const s of sessions.slice(0, 10)) {
|
|
6717
|
+
const date = new Date(s.updated).toLocaleDateString();
|
|
6718
|
+
info += ` ${_sid === s.id ? "\u25CF" : "\u25CB"} **${s.title}** (${s.entries.length} msgs, ${date})
|
|
6719
|
+
`;
|
|
6720
|
+
}
|
|
6721
|
+
if (sessions.length > 10) info += ` ... and ${sessions.length - 10} more
|
|
6722
|
+
`;
|
|
6723
|
+
info += "\nSessions are auto-saved. Use `lovecode memory list` to manage them.";
|
|
6724
|
+
addMessage("assistant", info);
|
|
6725
|
+
} catch (err) {
|
|
6726
|
+
addMessage("system", `Error: ${err.message}`);
|
|
6704
6727
|
}
|
|
6705
6728
|
}
|
|
6706
6729
|
async function handleSend(text) {
|
|
6707
|
-
|
|
6708
|
-
setMessages((prev) => [...prev, userMsg]);
|
|
6730
|
+
addMessage("user", text);
|
|
6709
6731
|
if (text.startsWith("/")) {
|
|
6710
6732
|
handleCommand(text);
|
|
6711
6733
|
return;
|
|
@@ -6716,20 +6738,43 @@ function App({
|
|
|
6716
6738
|
try {
|
|
6717
6739
|
const response = await onSendMessage(text);
|
|
6718
6740
|
if (response) {
|
|
6719
|
-
|
|
6741
|
+
addMessage("assistant", response);
|
|
6720
6742
|
}
|
|
6721
6743
|
} catch (err) {
|
|
6722
|
-
|
|
6744
|
+
addMessage("assistant", `Error: ${err.message}`);
|
|
6723
6745
|
}
|
|
6724
6746
|
setStreaming(false);
|
|
6725
6747
|
}
|
|
6726
6748
|
}
|
|
6727
6749
|
function handleCommand(text) {
|
|
6728
|
-
const cmd = text.slice(1).toLowerCase();
|
|
6750
|
+
const cmd = text.slice(1).toLowerCase().trim();
|
|
6729
6751
|
if (cmd === "help") {
|
|
6730
|
-
|
|
6752
|
+
addMessage("system", [
|
|
6753
|
+
"**Commands:**",
|
|
6754
|
+
" `/help` \u2014 Show this help",
|
|
6755
|
+
" `/clear` \u2014 Clear chat",
|
|
6756
|
+
" `/theme <name>` \u2014 Change theme",
|
|
6757
|
+
" `/themes` \u2014 List themes",
|
|
6758
|
+
" `/connect` \u2014 Show AI providers",
|
|
6759
|
+
" `/model` \u2014 Show AI config",
|
|
6760
|
+
" `/system <prompt>` \u2014 Set system prompt",
|
|
6761
|
+
" `/export` \u2014 Save chat to file",
|
|
6762
|
+
" `/sessions` \u2014 List sessions",
|
|
6763
|
+
" `/!<cmd>` \u2014 Run shell command",
|
|
6764
|
+
" `/exit` \u2014 Quit TUI",
|
|
6765
|
+
"",
|
|
6766
|
+
"**Keys:** Tab=focus, Esc=normal mode, i=insert, j/k=scroll, \u2191\u2193=history"
|
|
6767
|
+
].join("\n"));
|
|
6731
6768
|
} else if (cmd === "connect") {
|
|
6732
6769
|
handleConnect();
|
|
6770
|
+
} else if (cmd === "model") {
|
|
6771
|
+
handleModel();
|
|
6772
|
+
} else if (cmd === "themes") {
|
|
6773
|
+
handleThemes();
|
|
6774
|
+
} else if (cmd === "export") {
|
|
6775
|
+
handleExport();
|
|
6776
|
+
} else if (cmd === "sessions") {
|
|
6777
|
+
handleSessions();
|
|
6733
6778
|
} else if (cmd === "clear") {
|
|
6734
6779
|
setMessages([]);
|
|
6735
6780
|
} else if (cmd.startsWith("theme ")) {
|
|
@@ -6737,12 +6782,16 @@ function App({
|
|
|
6737
6782
|
const names = getThemeNames();
|
|
6738
6783
|
if (names.includes(name)) {
|
|
6739
6784
|
setCurrentTheme(name);
|
|
6740
|
-
|
|
6785
|
+
addMessage("system", `Theme changed to: ${name}`);
|
|
6741
6786
|
} else {
|
|
6742
|
-
|
|
6787
|
+
addMessage("system", `Unknown theme: ${name}. Available: ${names.join(", ")}`);
|
|
6743
6788
|
}
|
|
6789
|
+
} else if (cmd.startsWith("system ")) {
|
|
6790
|
+
handleSystem(cmd.slice(7));
|
|
6791
|
+
} else if (cmd === "system") {
|
|
6792
|
+
handleSystem("");
|
|
6744
6793
|
} else if (cmd === "vim") {
|
|
6745
|
-
|
|
6794
|
+
addMessage("system", "Vim mode: Esc=normal, i=insert, j/k=scroll");
|
|
6746
6795
|
} else if (cmd === "exit" || cmd === "quit") {
|
|
6747
6796
|
process.exit(0);
|
|
6748
6797
|
} else if (cmd.startsWith("!")) {
|
|
@@ -6771,8 +6820,18 @@ function App({
|
|
|
6771
6820
|
});
|
|
6772
6821
|
});
|
|
6773
6822
|
}
|
|
6823
|
+
} else {
|
|
6824
|
+
addMessage("system", `Unknown command: ${cmd}. Try /help`);
|
|
6774
6825
|
}
|
|
6775
6826
|
}
|
|
6827
|
+
if (splash) {
|
|
6828
|
+
const theme = getTheme();
|
|
6829
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", children: [
|
|
6830
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.colors.primary, children: "LoveCode AI" }),
|
|
6831
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.textDim, children: "Terminal-native autonomous coding agent" }),
|
|
6832
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: "v0.1.5" })
|
|
6833
|
+
] });
|
|
6834
|
+
}
|
|
6776
6835
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
6777
6836
|
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", flexGrow: 1, paddingX: 0, children: /* @__PURE__ */ jsxs3(SplitPane, { direction: "vertical", sizes: [15, 60, 25], children: [
|
|
6778
6837
|
/* @__PURE__ */ jsx3(
|
|
@@ -6813,7 +6872,10 @@ function App({
|
|
|
6813
6872
|
theme: currentTheme,
|
|
6814
6873
|
focus,
|
|
6815
6874
|
vimMode: "INSERT",
|
|
6816
|
-
messages: messages.length
|
|
6875
|
+
messages: messages.length,
|
|
6876
|
+
sessionName: _sname,
|
|
6877
|
+
provider: _provider,
|
|
6878
|
+
model: _model
|
|
6817
6879
|
}
|
|
6818
6880
|
)
|
|
6819
6881
|
] });
|
|
@@ -6980,8 +7042,8 @@ async function cmdActions(actions, options) {
|
|
|
6980
7042
|
const { runActions } = await import("./playwright-N7OAVW2N.js");
|
|
6981
7043
|
let parsedActions;
|
|
6982
7044
|
if (options.file) {
|
|
6983
|
-
const
|
|
6984
|
-
const content =
|
|
7045
|
+
const fs23 = await import("fs");
|
|
7046
|
+
const content = fs23.readFileSync(options.file, "utf-8");
|
|
6985
7047
|
parsedActions = JSON.parse(content);
|
|
6986
7048
|
} else {
|
|
6987
7049
|
parsedActions = actions.map((a) => JSON.parse(a));
|
|
@@ -7146,7 +7208,7 @@ function formatRisk(risk) {
|
|
|
7146
7208
|
}
|
|
7147
7209
|
|
|
7148
7210
|
// src/security/secrets.ts
|
|
7149
|
-
import * as
|
|
7211
|
+
import * as fs21 from "fs";
|
|
7150
7212
|
import chalk39 from "chalk";
|
|
7151
7213
|
var SECRET_RULES = [
|
|
7152
7214
|
{ type: "aws_key", pattern: /(?:AKIA|ASIA)[0-9A-Z]{16}/g, severity: "critical", description: "AWS Access Key ID" },
|
|
@@ -7195,7 +7257,7 @@ function scanText(text, fileName) {
|
|
|
7195
7257
|
}
|
|
7196
7258
|
function scanFile(filePath) {
|
|
7197
7259
|
try {
|
|
7198
|
-
const content =
|
|
7260
|
+
const content = fs21.readFileSync(filePath, "utf-8");
|
|
7199
7261
|
return scanText(content, filePath);
|
|
7200
7262
|
} catch {
|
|
7201
7263
|
return [];
|
|
@@ -7207,7 +7269,7 @@ function scanDirectory2(dirPath, maxFiles = 100) {
|
|
|
7207
7269
|
function walk(dir) {
|
|
7208
7270
|
if (count >= maxFiles) return;
|
|
7209
7271
|
try {
|
|
7210
|
-
const entries =
|
|
7272
|
+
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
7211
7273
|
for (const entry of entries) {
|
|
7212
7274
|
if (count >= maxFiles) return;
|
|
7213
7275
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") continue;
|
|
@@ -7409,8 +7471,8 @@ function formatProfile(profile) {
|
|
|
7409
7471
|
}
|
|
7410
7472
|
|
|
7411
7473
|
// src/security/permissions.ts
|
|
7412
|
-
import * as
|
|
7413
|
-
import * as
|
|
7474
|
+
import * as fs22 from "fs";
|
|
7475
|
+
import * as path20 from "path";
|
|
7414
7476
|
import chalk41 from "chalk";
|
|
7415
7477
|
var PERMISSION_FILE = ".lovecode/permissions.json";
|
|
7416
7478
|
var DEFAULT_PERMISSIONS = {
|
|
@@ -7427,18 +7489,18 @@ var DEFAULT_PERMISSIONS = {
|
|
|
7427
7489
|
};
|
|
7428
7490
|
var cachedPermissions = null;
|
|
7429
7491
|
function getPermPath(rootDir) {
|
|
7430
|
-
return
|
|
7492
|
+
return path20.join(rootDir || process.cwd(), PERMISSION_FILE);
|
|
7431
7493
|
}
|
|
7432
7494
|
function loadPermissions(rootDir) {
|
|
7433
7495
|
if (cachedPermissions) return cachedPermissions;
|
|
7434
7496
|
const filePath = getPermPath(rootDir);
|
|
7435
|
-
if (!
|
|
7497
|
+
if (!fs22.existsSync(filePath)) {
|
|
7436
7498
|
savePermissions(DEFAULT_PERMISSIONS, rootDir);
|
|
7437
7499
|
cachedPermissions = { ...DEFAULT_PERMISSIONS };
|
|
7438
7500
|
return cachedPermissions;
|
|
7439
7501
|
}
|
|
7440
7502
|
try {
|
|
7441
|
-
const raw =
|
|
7503
|
+
const raw = fs22.readFileSync(filePath, "utf-8");
|
|
7442
7504
|
cachedPermissions = JSON.parse(raw);
|
|
7443
7505
|
return cachedPermissions;
|
|
7444
7506
|
} catch {
|
|
@@ -7448,11 +7510,11 @@ function loadPermissions(rootDir) {
|
|
|
7448
7510
|
}
|
|
7449
7511
|
function savePermissions(perms, rootDir) {
|
|
7450
7512
|
const filePath = getPermPath(rootDir);
|
|
7451
|
-
const dir =
|
|
7452
|
-
if (!
|
|
7453
|
-
|
|
7513
|
+
const dir = path20.dirname(filePath);
|
|
7514
|
+
if (!fs22.existsSync(dir)) {
|
|
7515
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
7454
7516
|
}
|
|
7455
|
-
|
|
7517
|
+
fs22.writeFileSync(filePath, JSON.stringify(perms, null, 2), "utf-8");
|
|
7456
7518
|
cachedPermissions = perms;
|
|
7457
7519
|
}
|
|
7458
7520
|
function setDefault(key, value, rootDir) {
|
|
@@ -7518,8 +7580,8 @@ function removeTrustedSource(source, rootDir) {
|
|
|
7518
7580
|
}
|
|
7519
7581
|
function resetPermissions(rootDir) {
|
|
7520
7582
|
const filePath = getPermPath(rootDir);
|
|
7521
|
-
if (
|
|
7522
|
-
|
|
7583
|
+
if (fs22.existsSync(filePath)) {
|
|
7584
|
+
fs22.unlinkSync(filePath);
|
|
7523
7585
|
}
|
|
7524
7586
|
cachedPermissions = null;
|
|
7525
7587
|
}
|