@colbymchenry/cmem 0.2.20 → 0.2.28
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.
Potentially problematic release.
This version of @colbymchenry/cmem might be problematic. Click here for more details.
- package/dist/cli.js +555 -73
- package/dist/cli.js.map +1 -1
- package/package.json +15 -6
- package/scripts/postinstall.js +46 -0
package/dist/cli.js
CHANGED
|
@@ -202,7 +202,7 @@ import { spawn as spawn2 } from "child_process";
|
|
|
202
202
|
|
|
203
203
|
// src/ui/App.tsx
|
|
204
204
|
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
205
|
-
import { Box as
|
|
205
|
+
import { Box as Box6, Text as Text6, useInput, useApp } from "ink";
|
|
206
206
|
import TextInput2 from "ink-text-input";
|
|
207
207
|
import Spinner from "ink-spinner";
|
|
208
208
|
import { basename as basename4, dirname as dirname2 } from "path";
|
|
@@ -361,21 +361,23 @@ var SessionList = ({
|
|
|
361
361
|
};
|
|
362
362
|
var SessionItem = ({ session, isSelected }) => {
|
|
363
363
|
const hasCustomTitle = !!session.customTitle;
|
|
364
|
-
const displayTitle = truncate(session.customTitle || session.title,
|
|
365
|
-
const folderName = session.projectPath ? truncate(basename2(session.projectPath),
|
|
364
|
+
const displayTitle = truncate(session.customTitle || session.title, 38);
|
|
365
|
+
const folderName = session.projectPath ? truncate(basename2(session.projectPath), 38) : "";
|
|
366
366
|
const msgs = String(session.messageCount).padStart(3);
|
|
367
367
|
const updated = formatTimeAgo(session.updatedAt);
|
|
368
368
|
const getTitleColor = () => {
|
|
369
369
|
if (isSelected) return "cyan";
|
|
370
|
+
if (session.isFavorite) return "yellow";
|
|
370
371
|
if (hasCustomTitle) return "magenta";
|
|
371
372
|
return void 0;
|
|
372
373
|
};
|
|
373
374
|
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
374
375
|
/* @__PURE__ */ jsx3(Text3, { color: isSelected ? "cyan" : void 0, children: isSelected ? "\u25B8 " : " " }),
|
|
375
|
-
/* @__PURE__ */ jsx3(Text3, {
|
|
376
|
+
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: session.isFavorite ? "\u2B50" : " " }),
|
|
377
|
+
/* @__PURE__ */ jsx3(Text3, { bold: isSelected, color: getTitleColor(), wrap: "truncate", children: displayTitle.padEnd(38) }),
|
|
376
378
|
/* @__PURE__ */ jsxs3(Text3, { color: "blue", children: [
|
|
377
379
|
" ",
|
|
378
|
-
folderName.padEnd(
|
|
380
|
+
folderName.padEnd(38)
|
|
379
381
|
] }),
|
|
380
382
|
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
381
383
|
" ",
|
|
@@ -386,16 +388,108 @@ var SessionItem = ({ session, isSelected }) => {
|
|
|
386
388
|
] });
|
|
387
389
|
};
|
|
388
390
|
|
|
389
|
-
// src/ui/components/
|
|
391
|
+
// src/ui/components/ProjectList.tsx
|
|
390
392
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
391
393
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
394
|
+
var ProjectList = ({
|
|
395
|
+
projects,
|
|
396
|
+
selectedIndex
|
|
397
|
+
}) => {
|
|
398
|
+
if (projects.length === 0) {
|
|
399
|
+
return /* @__PURE__ */ jsxs4(
|
|
400
|
+
Box4,
|
|
401
|
+
{
|
|
402
|
+
flexDirection: "column",
|
|
403
|
+
borderStyle: "round",
|
|
404
|
+
borderColor: "gray",
|
|
405
|
+
paddingX: 1,
|
|
406
|
+
paddingY: 0,
|
|
407
|
+
children: [
|
|
408
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: "Projects" }),
|
|
409
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No projects found" }),
|
|
410
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Start using Claude Code in a project directory" })
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
const visibleCount = 8;
|
|
416
|
+
let startIndex = Math.max(0, selectedIndex - Math.floor(visibleCount / 2));
|
|
417
|
+
const endIndex = Math.min(projects.length, startIndex + visibleCount);
|
|
418
|
+
if (endIndex - startIndex < visibleCount) {
|
|
419
|
+
startIndex = Math.max(0, endIndex - visibleCount);
|
|
420
|
+
}
|
|
421
|
+
const visibleProjects = projects.slice(startIndex, endIndex);
|
|
422
|
+
return /* @__PURE__ */ jsxs4(
|
|
423
|
+
Box4,
|
|
424
|
+
{
|
|
425
|
+
flexDirection: "column",
|
|
426
|
+
borderStyle: "round",
|
|
427
|
+
borderColor: "gray",
|
|
428
|
+
paddingX: 1,
|
|
429
|
+
paddingY: 0,
|
|
430
|
+
children: [
|
|
431
|
+
/* @__PURE__ */ jsxs4(Text4, { bold: true, children: [
|
|
432
|
+
"Projects (",
|
|
433
|
+
projects.length,
|
|
434
|
+
")"
|
|
435
|
+
] }),
|
|
436
|
+
visibleProjects.map((project, i) => {
|
|
437
|
+
const actualIndex = startIndex + i;
|
|
438
|
+
const isSelected = actualIndex === selectedIndex;
|
|
439
|
+
return /* @__PURE__ */ jsx4(
|
|
440
|
+
ProjectItem,
|
|
441
|
+
{
|
|
442
|
+
project,
|
|
443
|
+
isSelected
|
|
444
|
+
},
|
|
445
|
+
project.path
|
|
446
|
+
);
|
|
447
|
+
}),
|
|
448
|
+
projects.length > visibleCount && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
449
|
+
startIndex > 0 ? "\u2191 more above" : "",
|
|
450
|
+
startIndex > 0 && endIndex < projects.length ? " | " : "",
|
|
451
|
+
endIndex < projects.length ? "\u2193 more below" : ""
|
|
452
|
+
] })
|
|
453
|
+
]
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
};
|
|
457
|
+
var ProjectItem = ({ project, isSelected }) => {
|
|
458
|
+
const displayName = truncate(project.name, 40);
|
|
459
|
+
const sessions = `${project.sessionCount} session${project.sessionCount !== 1 ? "s" : ""}`;
|
|
460
|
+
const messages = `${project.totalMessages} msgs`;
|
|
461
|
+
const updated = formatTimeAgo(project.lastUpdated);
|
|
462
|
+
const orderBadge = project.sortOrder !== null ? `${project.sortOrder + 1}.` : " ";
|
|
463
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
464
|
+
/* @__PURE__ */ jsx4(Text4, { color: isSelected ? "cyan" : void 0, children: isSelected ? "\u25B8 " : " " }),
|
|
465
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
466
|
+
orderBadge.padStart(3),
|
|
467
|
+
" "
|
|
468
|
+
] }),
|
|
469
|
+
/* @__PURE__ */ jsx4(Text4, { color: "blue", children: "\u{1F4C1} " }),
|
|
470
|
+
/* @__PURE__ */ jsx4(Text4, { bold: isSelected, color: isSelected ? "cyan" : void 0, wrap: "truncate", children: displayName.padEnd(35) }),
|
|
471
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
472
|
+
" ",
|
|
473
|
+
sessions.padEnd(12)
|
|
474
|
+
] }),
|
|
475
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
476
|
+
" ",
|
|
477
|
+
messages.padEnd(10)
|
|
478
|
+
] }),
|
|
479
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: updated.padStart(10) })
|
|
480
|
+
] });
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// src/ui/components/Preview.tsx
|
|
484
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
485
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
392
486
|
var Preview = ({ session }) => {
|
|
393
487
|
if (!session) {
|
|
394
488
|
return null;
|
|
395
489
|
}
|
|
396
490
|
const summary = session.summary ? truncate(session.summary, 200) : "No summary available";
|
|
397
|
-
return /* @__PURE__ */
|
|
398
|
-
|
|
491
|
+
return /* @__PURE__ */ jsxs5(
|
|
492
|
+
Box5,
|
|
399
493
|
{
|
|
400
494
|
flexDirection: "column",
|
|
401
495
|
borderStyle: "round",
|
|
@@ -404,15 +498,16 @@ var Preview = ({ session }) => {
|
|
|
404
498
|
paddingY: 0,
|
|
405
499
|
marginTop: 1,
|
|
406
500
|
children: [
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
/* @__PURE__ */
|
|
409
|
-
session.
|
|
501
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
502
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Preview" }),
|
|
503
|
+
session.isFavorite && /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: " \u2B50" }),
|
|
504
|
+
session.projectPath && /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "blue", children: [
|
|
410
505
|
" \u{1F4C1} ",
|
|
411
506
|
session.projectPath
|
|
412
507
|
] })
|
|
413
508
|
] }),
|
|
414
|
-
/* @__PURE__ */
|
|
415
|
-
/* @__PURE__ */
|
|
509
|
+
/* @__PURE__ */ jsx5(Text5, { wrap: "wrap", children: summary }),
|
|
510
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
416
511
|
"Messages: ",
|
|
417
512
|
session.messageCount
|
|
418
513
|
] })
|
|
@@ -459,6 +554,24 @@ function ensureModelsDir() {
|
|
|
459
554
|
// src/parser/index.ts
|
|
460
555
|
import { readFileSync, readdirSync, existsSync as existsSync2, statSync } from "fs";
|
|
461
556
|
import { join as join2 } from "path";
|
|
557
|
+
var AUTOMATED_TITLE_PATTERNS = [
|
|
558
|
+
/^<[a-z-]+>/i,
|
|
559
|
+
// XML-like tags: <project-instructions>, <command-message>, etc.
|
|
560
|
+
/^<[A-Z_]+>/,
|
|
561
|
+
// Uppercase tags: <SYSTEM>, <TOOL_USE>, etc.
|
|
562
|
+
/^\[system\]/i,
|
|
563
|
+
// [system] prefix
|
|
564
|
+
/^\/[a-z]+$/i
|
|
565
|
+
// Slash commands: /init, /help, etc.
|
|
566
|
+
];
|
|
567
|
+
function isAutomatedByContent(title) {
|
|
568
|
+
for (const pattern of AUTOMATED_TITLE_PATTERNS) {
|
|
569
|
+
if (pattern.test(title.trim())) {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
462
575
|
function extractSessionMetadata(filepath) {
|
|
463
576
|
const content = readFileSync(filepath, "utf-8");
|
|
464
577
|
const metadata = {
|
|
@@ -702,6 +815,22 @@ function initSchema(database) {
|
|
|
702
815
|
embedding FLOAT[${EMBEDDING_DIMENSIONS}]
|
|
703
816
|
);
|
|
704
817
|
`);
|
|
818
|
+
database.exec(`
|
|
819
|
+
CREATE TABLE IF NOT EXISTS favorites (
|
|
820
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
821
|
+
type TEXT NOT NULL CHECK (type IN ('session', 'folder')),
|
|
822
|
+
value TEXT NOT NULL,
|
|
823
|
+
created_at TEXT NOT NULL,
|
|
824
|
+
UNIQUE(type, value)
|
|
825
|
+
);
|
|
826
|
+
`);
|
|
827
|
+
database.exec(`
|
|
828
|
+
CREATE TABLE IF NOT EXISTS project_order (
|
|
829
|
+
path TEXT PRIMARY KEY,
|
|
830
|
+
sort_order INTEGER NOT NULL,
|
|
831
|
+
updated_at TEXT NOT NULL
|
|
832
|
+
);
|
|
833
|
+
`);
|
|
705
834
|
database.exec(`
|
|
706
835
|
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
|
|
707
836
|
`);
|
|
@@ -711,6 +840,9 @@ function initSchema(database) {
|
|
|
711
840
|
database.exec(`
|
|
712
841
|
CREATE INDEX IF NOT EXISTS idx_sessions_source ON sessions(source_file);
|
|
713
842
|
`);
|
|
843
|
+
database.exec(`
|
|
844
|
+
CREATE INDEX IF NOT EXISTS idx_favorites_type ON favorites(type);
|
|
845
|
+
`);
|
|
714
846
|
runMigrations(database);
|
|
715
847
|
}
|
|
716
848
|
function runMigrations(database) {
|
|
@@ -810,6 +942,10 @@ function updateSession(id, input) {
|
|
|
810
942
|
updates.push("is_automated = ?");
|
|
811
943
|
values.push(input.isAutomated ? 1 : 0);
|
|
812
944
|
}
|
|
945
|
+
if (input.projectPath !== void 0) {
|
|
946
|
+
updates.push("project_path = ?");
|
|
947
|
+
values.push(input.projectPath);
|
|
948
|
+
}
|
|
813
949
|
values.push(id);
|
|
814
950
|
const transaction = db2.transaction(() => {
|
|
815
951
|
db2.prepare(`UPDATE sessions SET ${updates.join(", ")} WHERE id = ?`).run(...values);
|
|
@@ -979,6 +1115,96 @@ function needsReembedding(sessionId, currentContentLength, threshold = 500) {
|
|
|
979
1115
|
if (!state) return true;
|
|
980
1116
|
return currentContentLength - state.contentLength >= threshold;
|
|
981
1117
|
}
|
|
1118
|
+
function addFavorite(type, value) {
|
|
1119
|
+
const db2 = getDatabase();
|
|
1120
|
+
try {
|
|
1121
|
+
db2.prepare(`
|
|
1122
|
+
INSERT OR IGNORE INTO favorites (type, value, created_at)
|
|
1123
|
+
VALUES (?, ?, ?)
|
|
1124
|
+
`).run(type, value, (/* @__PURE__ */ new Date()).toISOString());
|
|
1125
|
+
return true;
|
|
1126
|
+
} catch {
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
function removeFavorite(type, value) {
|
|
1131
|
+
const db2 = getDatabase();
|
|
1132
|
+
const result = db2.prepare(`
|
|
1133
|
+
DELETE FROM favorites WHERE type = ? AND value = ?
|
|
1134
|
+
`).run(type, value);
|
|
1135
|
+
return result.changes > 0;
|
|
1136
|
+
}
|
|
1137
|
+
function toggleFavorite(type, value) {
|
|
1138
|
+
if (isFavorite(type, value)) {
|
|
1139
|
+
removeFavorite(type, value);
|
|
1140
|
+
return false;
|
|
1141
|
+
} else {
|
|
1142
|
+
addFavorite(type, value);
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function isFavorite(type, value) {
|
|
1147
|
+
const db2 = getDatabase();
|
|
1148
|
+
const row = db2.prepare(`
|
|
1149
|
+
SELECT 1 FROM favorites WHERE type = ? AND value = ?
|
|
1150
|
+
`).get(type, value);
|
|
1151
|
+
return !!row;
|
|
1152
|
+
}
|
|
1153
|
+
function getFavorites(type) {
|
|
1154
|
+
const db2 = getDatabase();
|
|
1155
|
+
const rows = db2.prepare(`
|
|
1156
|
+
SELECT id, type, value, created_at as createdAt
|
|
1157
|
+
FROM favorites
|
|
1158
|
+
WHERE type = ?
|
|
1159
|
+
ORDER BY created_at DESC
|
|
1160
|
+
`).all(type);
|
|
1161
|
+
return rows;
|
|
1162
|
+
}
|
|
1163
|
+
function getFavoriteSessionIds() {
|
|
1164
|
+
const favorites = getFavorites("session");
|
|
1165
|
+
return new Set(favorites.map((f) => f.value));
|
|
1166
|
+
}
|
|
1167
|
+
function hasFavoriteSessions() {
|
|
1168
|
+
const db2 = getDatabase();
|
|
1169
|
+
const row = db2.prepare(`
|
|
1170
|
+
SELECT 1 FROM favorites WHERE type = 'session' LIMIT 1
|
|
1171
|
+
`).get();
|
|
1172
|
+
return !!row;
|
|
1173
|
+
}
|
|
1174
|
+
function getProjectOrders() {
|
|
1175
|
+
const db2 = getDatabase();
|
|
1176
|
+
const rows = db2.prepare(`
|
|
1177
|
+
SELECT path, sort_order as sortOrder
|
|
1178
|
+
FROM project_order
|
|
1179
|
+
ORDER BY sort_order ASC
|
|
1180
|
+
`).all();
|
|
1181
|
+
const map = /* @__PURE__ */ new Map();
|
|
1182
|
+
for (const row of rows) {
|
|
1183
|
+
map.set(row.path, row.sortOrder);
|
|
1184
|
+
}
|
|
1185
|
+
return map;
|
|
1186
|
+
}
|
|
1187
|
+
function updateProjectOrders(orders) {
|
|
1188
|
+
const db2 = getDatabase();
|
|
1189
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1190
|
+
const stmt = db2.prepare(`
|
|
1191
|
+
INSERT OR REPLACE INTO project_order (path, sort_order, updated_at)
|
|
1192
|
+
VALUES (?, ?, ?)
|
|
1193
|
+
`);
|
|
1194
|
+
const transaction = db2.transaction(() => {
|
|
1195
|
+
for (const order of orders) {
|
|
1196
|
+
stmt.run(order.path, order.sortOrder, now);
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
transaction();
|
|
1200
|
+
}
|
|
1201
|
+
function hasCustomProjectOrder() {
|
|
1202
|
+
const db2 = getDatabase();
|
|
1203
|
+
const row = db2.prepare(`
|
|
1204
|
+
SELECT 1 FROM project_order LIMIT 1
|
|
1205
|
+
`).get();
|
|
1206
|
+
return !!row;
|
|
1207
|
+
}
|
|
982
1208
|
|
|
983
1209
|
// src/db/vectors.ts
|
|
984
1210
|
function storeEmbedding(sessionId, embedding) {
|
|
@@ -1090,18 +1316,81 @@ function useSessions(options = {}) {
|
|
|
1090
1316
|
const [embeddingsReady, setEmbeddingsReady] = useState(false);
|
|
1091
1317
|
const [isSearching, setIsSearching] = useState(false);
|
|
1092
1318
|
const [projectFilter, setProjectFilter] = useState(options.projectFilter ?? null);
|
|
1319
|
+
const [favoriteSessionIds, setFavoriteSessionIds] = useState(/* @__PURE__ */ new Set());
|
|
1320
|
+
const [hasFavSessions, setHasFavSessions] = useState(false);
|
|
1321
|
+
const [projectOrderMap, setProjectOrderMap] = useState(/* @__PURE__ */ new Map());
|
|
1322
|
+
const [hasCustomOrder, setHasCustomOrder] = useState(false);
|
|
1323
|
+
const smartSort = useCallback((sessionList, favIds) => {
|
|
1324
|
+
const withFavorites = sessionList.map((s) => ({
|
|
1325
|
+
...s,
|
|
1326
|
+
isFavorite: favIds.has(s.id)
|
|
1327
|
+
}));
|
|
1328
|
+
return withFavorites.sort((a, b) => {
|
|
1329
|
+
const aHasCustom = !!a.customTitle;
|
|
1330
|
+
const bHasCustom = !!b.customTitle;
|
|
1331
|
+
const aTier = a.isFavorite ? 0 : aHasCustom ? 1 : 2;
|
|
1332
|
+
const bTier = b.isFavorite ? 0 : bHasCustom ? 1 : 2;
|
|
1333
|
+
if (aTier !== bTier) return aTier - bTier;
|
|
1334
|
+
if (a.messageCount !== b.messageCount) return b.messageCount - a.messageCount;
|
|
1335
|
+
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
|
|
1336
|
+
});
|
|
1337
|
+
}, []);
|
|
1338
|
+
const [projects, setProjects] = useState([]);
|
|
1339
|
+
const calculateProjects = useCallback((sessionList, orderMap) => {
|
|
1340
|
+
const projectMap = /* @__PURE__ */ new Map();
|
|
1341
|
+
for (const session of sessionList) {
|
|
1342
|
+
if (!session.projectPath) continue;
|
|
1343
|
+
const existing = projectMap.get(session.projectPath);
|
|
1344
|
+
if (existing) {
|
|
1345
|
+
existing.sessionCount++;
|
|
1346
|
+
existing.totalMessages += session.messageCount;
|
|
1347
|
+
if (new Date(session.updatedAt) > new Date(existing.lastUpdated)) {
|
|
1348
|
+
existing.lastUpdated = session.updatedAt;
|
|
1349
|
+
}
|
|
1350
|
+
} else {
|
|
1351
|
+
const pathParts = session.projectPath.split("/");
|
|
1352
|
+
projectMap.set(session.projectPath, {
|
|
1353
|
+
path: session.projectPath,
|
|
1354
|
+
name: pathParts[pathParts.length - 1] || session.projectPath,
|
|
1355
|
+
sessionCount: 1,
|
|
1356
|
+
totalMessages: session.messageCount,
|
|
1357
|
+
sortOrder: orderMap.get(session.projectPath) ?? null,
|
|
1358
|
+
lastUpdated: session.updatedAt
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return Array.from(projectMap.values()).sort((a, b) => {
|
|
1363
|
+
if (a.sortOrder !== null && b.sortOrder !== null) {
|
|
1364
|
+
return a.sortOrder - b.sortOrder;
|
|
1365
|
+
}
|
|
1366
|
+
if (a.sortOrder !== null && b.sortOrder === null) return -1;
|
|
1367
|
+
if (a.sortOrder === null && b.sortOrder !== null) return 1;
|
|
1368
|
+
if (a.totalMessages !== b.totalMessages) return b.totalMessages - a.totalMessages;
|
|
1369
|
+
return new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime();
|
|
1370
|
+
});
|
|
1371
|
+
}, []);
|
|
1093
1372
|
const loadSessions = useCallback(() => {
|
|
1094
1373
|
try {
|
|
1095
1374
|
const loaded = projectFilter ? listHumanSessionsByProject(projectFilter) : listHumanSessions();
|
|
1096
|
-
|
|
1097
|
-
|
|
1375
|
+
const favIds = getFavoriteSessionIds();
|
|
1376
|
+
const orderMap = getProjectOrders();
|
|
1377
|
+
setFavoriteSessionIds(favIds);
|
|
1378
|
+
setProjectOrderMap(orderMap);
|
|
1379
|
+
setHasFavSessions(hasFavoriteSessions());
|
|
1380
|
+
setHasCustomOrder(hasCustomProjectOrder());
|
|
1381
|
+
const sorted = smartSort(loaded, favIds);
|
|
1382
|
+
setAllSessions(sorted);
|
|
1383
|
+
setSessions(sorted);
|
|
1384
|
+
if (!projectFilter) {
|
|
1385
|
+
setProjects(calculateProjects(sorted, orderMap));
|
|
1386
|
+
}
|
|
1098
1387
|
setError(null);
|
|
1099
1388
|
} catch (err) {
|
|
1100
1389
|
setError(String(err));
|
|
1101
1390
|
} finally {
|
|
1102
1391
|
setLoading(false);
|
|
1103
1392
|
}
|
|
1104
|
-
}, [projectFilter]);
|
|
1393
|
+
}, [projectFilter, smartSort, calculateProjects]);
|
|
1105
1394
|
const initEmbeddings = useCallback(async () => {
|
|
1106
1395
|
if (isReady()) {
|
|
1107
1396
|
setEmbeddingsReady(true);
|
|
@@ -1131,7 +1420,7 @@ function useSessions(options = {}) {
|
|
|
1131
1420
|
const filtered = allSessions.filter(
|
|
1132
1421
|
(s) => s.title.toLowerCase().includes(query.toLowerCase()) || s.summary && s.summary.toLowerCase().includes(query.toLowerCase())
|
|
1133
1422
|
);
|
|
1134
|
-
setSessions(filtered);
|
|
1423
|
+
setSessions(smartSort(filtered, favoriteSessionIds));
|
|
1135
1424
|
setIsSearching(true);
|
|
1136
1425
|
return;
|
|
1137
1426
|
}
|
|
@@ -1139,18 +1428,18 @@ function useSessions(options = {}) {
|
|
|
1139
1428
|
setLoading(true);
|
|
1140
1429
|
const queryEmbedding = await getEmbedding(query);
|
|
1141
1430
|
const results = searchSessions(queryEmbedding, 20);
|
|
1142
|
-
setSessions(results);
|
|
1431
|
+
setSessions(smartSort(results, favoriteSessionIds));
|
|
1143
1432
|
setIsSearching(true);
|
|
1144
1433
|
} catch (err) {
|
|
1145
1434
|
setError(String(err));
|
|
1146
1435
|
const filtered = allSessions.filter(
|
|
1147
1436
|
(s) => s.title.toLowerCase().includes(query.toLowerCase()) || s.summary && s.summary.toLowerCase().includes(query.toLowerCase())
|
|
1148
1437
|
);
|
|
1149
|
-
setSessions(filtered);
|
|
1438
|
+
setSessions(smartSort(filtered, favoriteSessionIds));
|
|
1150
1439
|
} finally {
|
|
1151
1440
|
setLoading(false);
|
|
1152
1441
|
}
|
|
1153
|
-
}, [allSessions, embeddingsReady]);
|
|
1442
|
+
}, [allSessions, embeddingsReady, favoriteSessionIds, smartSort]);
|
|
1154
1443
|
const clearSearch = useCallback(() => {
|
|
1155
1444
|
setSessions(allSessions);
|
|
1156
1445
|
setIsSearching(false);
|
|
@@ -1165,8 +1454,43 @@ function useSessions(options = {}) {
|
|
|
1165
1454
|
const getSessionById = useCallback((id) => {
|
|
1166
1455
|
return getSession(id);
|
|
1167
1456
|
}, []);
|
|
1457
|
+
const toggleFavoriteHandler = useCallback((sessionId) => {
|
|
1458
|
+
const isNowFavorite = toggleFavorite("session", sessionId);
|
|
1459
|
+
const newFavIds = new Set(favoriteSessionIds);
|
|
1460
|
+
if (isNowFavorite) {
|
|
1461
|
+
newFavIds.add(sessionId);
|
|
1462
|
+
} else {
|
|
1463
|
+
newFavIds.delete(sessionId);
|
|
1464
|
+
}
|
|
1465
|
+
setFavoriteSessionIds(newFavIds);
|
|
1466
|
+
setHasFavSessions(newFavIds.size > 0);
|
|
1467
|
+
setAllSessions((prev) => smartSort(prev, newFavIds));
|
|
1468
|
+
setSessions((prev) => smartSort(prev, newFavIds));
|
|
1469
|
+
return isNowFavorite;
|
|
1470
|
+
}, [favoriteSessionIds, smartSort]);
|
|
1471
|
+
const moveProject = useCallback((fromIndex, toIndex) => {
|
|
1472
|
+
if (fromIndex === toIndex) return;
|
|
1473
|
+
if (fromIndex < 0 || toIndex < 0) return;
|
|
1474
|
+
if (fromIndex >= projects.length || toIndex >= projects.length) return;
|
|
1475
|
+
const newProjects = [...projects];
|
|
1476
|
+
const [movedProject] = newProjects.splice(fromIndex, 1);
|
|
1477
|
+
newProjects.splice(toIndex, 0, movedProject);
|
|
1478
|
+
const orders = newProjects.map((project, index) => ({
|
|
1479
|
+
path: project.path,
|
|
1480
|
+
sortOrder: index
|
|
1481
|
+
}));
|
|
1482
|
+
updateProjectOrders(orders);
|
|
1483
|
+
const newOrderMap = /* @__PURE__ */ new Map();
|
|
1484
|
+
for (const order of orders) {
|
|
1485
|
+
newOrderMap.set(order.path, order.sortOrder);
|
|
1486
|
+
}
|
|
1487
|
+
setProjectOrderMap(newOrderMap);
|
|
1488
|
+
setHasCustomOrder(true);
|
|
1489
|
+
setProjects(newProjects.map((p, i) => ({ ...p, sortOrder: i })));
|
|
1490
|
+
}, [projects]);
|
|
1168
1491
|
return {
|
|
1169
1492
|
sessions,
|
|
1493
|
+
projects,
|
|
1170
1494
|
loading,
|
|
1171
1495
|
error,
|
|
1172
1496
|
embeddingsReady,
|
|
@@ -1176,16 +1500,22 @@ function useSessions(options = {}) {
|
|
|
1176
1500
|
search,
|
|
1177
1501
|
clearSearch,
|
|
1178
1502
|
deleteSession: deleteSessionHandler,
|
|
1179
|
-
getSessionById
|
|
1503
|
+
getSessionById,
|
|
1504
|
+
toggleFavorite: toggleFavoriteHandler,
|
|
1505
|
+
favoriteSessionIds,
|
|
1506
|
+
hasFavoriteSessions: hasFavSessions,
|
|
1507
|
+
moveProject,
|
|
1508
|
+
hasCustomProjectOrder: hasCustomOrder
|
|
1180
1509
|
};
|
|
1181
1510
|
}
|
|
1182
1511
|
|
|
1183
1512
|
// src/ui/App.tsx
|
|
1184
|
-
import { jsx as
|
|
1513
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1185
1514
|
var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
1186
1515
|
const { exit } = useApp();
|
|
1187
1516
|
const {
|
|
1188
1517
|
sessions,
|
|
1518
|
+
projects,
|
|
1189
1519
|
loading,
|
|
1190
1520
|
embeddingsReady,
|
|
1191
1521
|
projectFilter,
|
|
@@ -1193,18 +1523,34 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1193
1523
|
search,
|
|
1194
1524
|
clearSearch,
|
|
1195
1525
|
deleteSession: deleteSession2,
|
|
1196
|
-
refresh
|
|
1526
|
+
refresh,
|
|
1527
|
+
toggleFavorite: toggleFavorite2,
|
|
1528
|
+
moveProject
|
|
1197
1529
|
} = useSessions({ projectFilter: initialProjectFilter });
|
|
1198
1530
|
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
1199
1531
|
const [searchQuery, setSearchQuery] = useState2("");
|
|
1200
1532
|
const [mode, setMode] = useState2("list");
|
|
1201
1533
|
const [statusMessage, setStatusMessage] = useState2(null);
|
|
1202
1534
|
const [renameValue, setRenameValue] = useState2("");
|
|
1535
|
+
const [currentTab, setCurrentTab] = useState2("global");
|
|
1536
|
+
const [selectedProjectPath, setSelectedProjectPath] = useState2(null);
|
|
1537
|
+
const getCurrentView = useCallback2(() => {
|
|
1538
|
+
if (currentTab === "projects") {
|
|
1539
|
+
return selectedProjectPath ? "project-sessions" : "projects";
|
|
1540
|
+
}
|
|
1541
|
+
return "sessions";
|
|
1542
|
+
}, [currentTab, selectedProjectPath]);
|
|
1543
|
+
const currentView = getCurrentView();
|
|
1544
|
+
const projectSessions = selectedProjectPath ? sessions.filter((s) => s.projectPath === selectedProjectPath) : [];
|
|
1545
|
+
useEffect2(() => {
|
|
1546
|
+
setSelectedIndex(0);
|
|
1547
|
+
}, [currentTab, selectedProjectPath]);
|
|
1203
1548
|
useEffect2(() => {
|
|
1204
|
-
|
|
1205
|
-
|
|
1549
|
+
const maxIndex = currentView === "projects" ? projects.length - 1 : currentView === "project-sessions" ? projectSessions.length - 1 : sessions.length - 1;
|
|
1550
|
+
if (selectedIndex > maxIndex) {
|
|
1551
|
+
setSelectedIndex(Math.max(0, maxIndex));
|
|
1206
1552
|
}
|
|
1207
|
-
}, [sessions, selectedIndex]);
|
|
1553
|
+
}, [currentView, projects.length, projectSessions.length, sessions.length, selectedIndex]);
|
|
1208
1554
|
useEffect2(() => {
|
|
1209
1555
|
if (mode === "search" && searchQuery.length > 2) {
|
|
1210
1556
|
const timer = setTimeout(() => {
|
|
@@ -1221,8 +1567,15 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1221
1567
|
return () => clearTimeout(timer);
|
|
1222
1568
|
}
|
|
1223
1569
|
}, [statusMessage]);
|
|
1570
|
+
const getCurrentSessions = useCallback2(() => {
|
|
1571
|
+
if (currentView === "project-sessions") {
|
|
1572
|
+
return projectSessions;
|
|
1573
|
+
}
|
|
1574
|
+
return sessions;
|
|
1575
|
+
}, [currentView, projectSessions, sessions]);
|
|
1224
1576
|
const handleRestore = useCallback2(() => {
|
|
1225
|
-
const
|
|
1577
|
+
const currentSessions2 = getCurrentSessions();
|
|
1578
|
+
const session = currentSessions2[selectedIndex];
|
|
1226
1579
|
if (!session) return;
|
|
1227
1580
|
if (session.sourceFile) {
|
|
1228
1581
|
const filename = basename4(session.sourceFile);
|
|
@@ -1261,17 +1614,30 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1261
1614
|
} else {
|
|
1262
1615
|
setStatusMessage("No source file - cannot resume this session");
|
|
1263
1616
|
}
|
|
1264
|
-
}, [
|
|
1617
|
+
}, [getCurrentSessions, selectedIndex, onResume, exit]);
|
|
1618
|
+
const handleEnterProject = useCallback2(() => {
|
|
1619
|
+
const project = projects[selectedIndex];
|
|
1620
|
+
if (project) {
|
|
1621
|
+
setSelectedProjectPath(project.path);
|
|
1622
|
+
setSelectedIndex(0);
|
|
1623
|
+
}
|
|
1624
|
+
}, [projects, selectedIndex]);
|
|
1625
|
+
const handleBackToProjects = useCallback2(() => {
|
|
1626
|
+
setSelectedProjectPath(null);
|
|
1627
|
+
setSelectedIndex(0);
|
|
1628
|
+
}, []);
|
|
1265
1629
|
const handleDelete = useCallback2(() => {
|
|
1266
|
-
const
|
|
1630
|
+
const currentSessions2 = getCurrentSessions();
|
|
1631
|
+
const session = currentSessions2[selectedIndex];
|
|
1267
1632
|
if (session) {
|
|
1268
1633
|
deleteSession2(session.id);
|
|
1269
1634
|
setStatusMessage(`Deleted: ${session.customTitle || session.title}`);
|
|
1270
1635
|
setMode("list");
|
|
1271
1636
|
}
|
|
1272
|
-
}, [
|
|
1637
|
+
}, [getCurrentSessions, selectedIndex, deleteSession2]);
|
|
1273
1638
|
const handleRename = useCallback2(() => {
|
|
1274
|
-
const
|
|
1639
|
+
const currentSessions2 = getCurrentSessions();
|
|
1640
|
+
const session = currentSessions2[selectedIndex];
|
|
1275
1641
|
if (session && renameValue.trim()) {
|
|
1276
1642
|
renameSession(session.id, renameValue.trim());
|
|
1277
1643
|
setStatusMessage(`Renamed to: ${renameValue.trim()}`);
|
|
@@ -1279,18 +1645,19 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1279
1645
|
}
|
|
1280
1646
|
setMode("list");
|
|
1281
1647
|
setRenameValue("");
|
|
1282
|
-
}, [
|
|
1648
|
+
}, [getCurrentSessions, selectedIndex, renameValue, refresh]);
|
|
1283
1649
|
const handleClearRename = useCallback2(() => {
|
|
1284
|
-
const
|
|
1650
|
+
const currentSessions2 = getCurrentSessions();
|
|
1651
|
+
const session = currentSessions2[selectedIndex];
|
|
1285
1652
|
if (session && session.customTitle) {
|
|
1286
1653
|
renameSession(session.id, null);
|
|
1287
1654
|
setStatusMessage(`Cleared custom name`);
|
|
1288
1655
|
refresh();
|
|
1289
1656
|
}
|
|
1290
1657
|
setMode("list");
|
|
1291
|
-
}, [
|
|
1658
|
+
}, [getCurrentSessions, selectedIndex, refresh]);
|
|
1292
1659
|
useInput((input, key) => {
|
|
1293
|
-
if (input === "q" && mode !== "search" && mode !== "rename") {
|
|
1660
|
+
if (input === "q" && mode !== "search" && mode !== "rename" && mode !== "sort-project") {
|
|
1294
1661
|
exit();
|
|
1295
1662
|
return;
|
|
1296
1663
|
}
|
|
@@ -1327,54 +1694,99 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1327
1694
|
}
|
|
1328
1695
|
return;
|
|
1329
1696
|
}
|
|
1697
|
+
if (mode === "sort-project") {
|
|
1698
|
+
if (key.escape || key.return) {
|
|
1699
|
+
setMode("list");
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
if (key.upArrow || input === "k") {
|
|
1703
|
+
if (selectedIndex > 0) {
|
|
1704
|
+
moveProject(selectedIndex, selectedIndex - 1);
|
|
1705
|
+
setSelectedIndex(selectedIndex - 1);
|
|
1706
|
+
}
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
if (key.downArrow || input === "j") {
|
|
1710
|
+
if (selectedIndex < projects.length - 1) {
|
|
1711
|
+
moveProject(selectedIndex, selectedIndex + 1);
|
|
1712
|
+
setSelectedIndex(selectedIndex + 1);
|
|
1713
|
+
}
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1330
1718
|
if (input === "/") {
|
|
1331
1719
|
setMode("search");
|
|
1332
1720
|
return;
|
|
1333
1721
|
}
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
1336
|
-
|
|
1722
|
+
if ((key.escape || key.backspace || key.delete) && currentView === "project-sessions") {
|
|
1723
|
+
handleBackToProjects();
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
if (input === "r" && currentView !== "projects") {
|
|
1727
|
+
const currentSessions2 = getCurrentSessions();
|
|
1728
|
+
if (currentSessions2.length > 0) {
|
|
1729
|
+
const session = currentSessions2[selectedIndex];
|
|
1337
1730
|
setRenameValue(session?.customTitle || "");
|
|
1338
1731
|
setMode("rename");
|
|
1339
1732
|
}
|
|
1340
1733
|
return;
|
|
1341
1734
|
}
|
|
1342
|
-
if (input === "R") {
|
|
1735
|
+
if (input === "R" && currentView !== "projects") {
|
|
1343
1736
|
handleClearRename();
|
|
1344
1737
|
return;
|
|
1345
1738
|
}
|
|
1739
|
+
if (key.leftArrow || input === "h") {
|
|
1740
|
+
if (currentTab === "projects") {
|
|
1741
|
+
setCurrentTab("global");
|
|
1742
|
+
setSelectedProjectPath(null);
|
|
1743
|
+
}
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
if (key.rightArrow || input === "l") {
|
|
1747
|
+
if (currentTab === "global") {
|
|
1748
|
+
setCurrentTab("projects");
|
|
1749
|
+
}
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1346
1752
|
if (key.upArrow || input === "k") {
|
|
1347
1753
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1348
1754
|
return;
|
|
1349
1755
|
}
|
|
1350
1756
|
if (key.downArrow || input === "j") {
|
|
1351
|
-
|
|
1757
|
+
const maxIndex = currentView === "projects" ? projects.length - 1 : currentView === "project-sessions" ? projectSessions.length - 1 : sessions.length - 1;
|
|
1758
|
+
setSelectedIndex((prev) => Math.min(maxIndex, prev + 1));
|
|
1352
1759
|
return;
|
|
1353
1760
|
}
|
|
1354
1761
|
if (key.return) {
|
|
1355
|
-
|
|
1762
|
+
if (currentView === "projects") {
|
|
1763
|
+
handleEnterProject();
|
|
1764
|
+
} else {
|
|
1765
|
+
handleRestore();
|
|
1766
|
+
}
|
|
1356
1767
|
return;
|
|
1357
1768
|
}
|
|
1358
|
-
if (input === "d") {
|
|
1359
|
-
|
|
1769
|
+
if (input === "d" && currentView !== "projects") {
|
|
1770
|
+
const currentSessions2 = getCurrentSessions();
|
|
1771
|
+
if (currentSessions2.length > 0) {
|
|
1360
1772
|
setMode("confirm-delete");
|
|
1361
1773
|
}
|
|
1362
1774
|
return;
|
|
1363
1775
|
}
|
|
1364
|
-
if (input === "
|
|
1365
|
-
if (
|
|
1366
|
-
|
|
1367
|
-
|
|
1776
|
+
if (input === "s") {
|
|
1777
|
+
if (currentView === "projects") {
|
|
1778
|
+
if (projects.length > 0) {
|
|
1779
|
+
setMode("sort-project");
|
|
1780
|
+
setStatusMessage("Sort mode: use \u2191\u2193 to move, Enter to confirm");
|
|
1781
|
+
}
|
|
1368
1782
|
} else {
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
setStatusMessage("No folder for this session");
|
|
1783
|
+
const currentSessions2 = getCurrentSessions();
|
|
1784
|
+
const session = currentSessions2[selectedIndex];
|
|
1785
|
+
if (session) {
|
|
1786
|
+
const isNowFavorite = toggleFavorite2(session.id);
|
|
1787
|
+
setStatusMessage(isNowFavorite ? "\u2B50 Added to favorites" : "Removed from favorites");
|
|
1375
1788
|
}
|
|
1376
1789
|
}
|
|
1377
|
-
setSelectedIndex(0);
|
|
1378
1790
|
return;
|
|
1379
1791
|
}
|
|
1380
1792
|
});
|
|
@@ -1383,15 +1795,43 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1383
1795
|
setSelectedIndex(0);
|
|
1384
1796
|
}, []);
|
|
1385
1797
|
if (loading && sessions.length === 0) {
|
|
1386
|
-
return /* @__PURE__ */
|
|
1387
|
-
/* @__PURE__ */
|
|
1388
|
-
/* @__PURE__ */
|
|
1798
|
+
return /* @__PURE__ */ jsxs6(Box6, { padding: 1, children: [
|
|
1799
|
+
/* @__PURE__ */ jsx6(Spinner, { type: "dots" }),
|
|
1800
|
+
/* @__PURE__ */ jsx6(Text6, { children: " Loading sessions..." })
|
|
1389
1801
|
] });
|
|
1390
1802
|
}
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1803
|
+
const currentSessions = getCurrentSessions();
|
|
1804
|
+
const selectedSession = currentView !== "projects" ? currentSessions[selectedIndex] || null : null;
|
|
1805
|
+
const selectedProject = currentView === "projects" ? projects[selectedIndex] || null : null;
|
|
1806
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
|
|
1807
|
+
/* @__PURE__ */ jsx6(Header, { embeddingsReady, projectFilter: selectedProjectPath }),
|
|
1808
|
+
/* @__PURE__ */ jsxs6(Box6, { marginBottom: 0, children: [
|
|
1809
|
+
/* @__PURE__ */ jsx6(
|
|
1810
|
+
Text6,
|
|
1811
|
+
{
|
|
1812
|
+
color: currentTab === "global" ? "cyan" : void 0,
|
|
1813
|
+
bold: currentTab === "global",
|
|
1814
|
+
dimColor: currentTab !== "global",
|
|
1815
|
+
children: "Global"
|
|
1816
|
+
}
|
|
1817
|
+
),
|
|
1818
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " | " }),
|
|
1819
|
+
/* @__PURE__ */ jsx6(
|
|
1820
|
+
Text6,
|
|
1821
|
+
{
|
|
1822
|
+
color: currentTab === "projects" ? "cyan" : void 0,
|
|
1823
|
+
bold: currentTab === "projects",
|
|
1824
|
+
dimColor: currentTab !== "projects",
|
|
1825
|
+
children: "Projects"
|
|
1826
|
+
}
|
|
1827
|
+
),
|
|
1828
|
+
currentView === "project-sessions" && selectedProjectPath && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
1829
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " \u2192 " }),
|
|
1830
|
+
/* @__PURE__ */ jsx6(Text6, { color: "blue", children: basename4(selectedProjectPath) })
|
|
1831
|
+
] }),
|
|
1832
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " [\u2190\u2192] switch tabs" })
|
|
1833
|
+
] }),
|
|
1834
|
+
currentTab === "global" && /* @__PURE__ */ jsx6(
|
|
1395
1835
|
SearchInput,
|
|
1396
1836
|
{
|
|
1397
1837
|
value: searchQuery,
|
|
@@ -1399,22 +1839,62 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1399
1839
|
isFocused: mode === "search"
|
|
1400
1840
|
}
|
|
1401
1841
|
),
|
|
1402
|
-
/* @__PURE__ */
|
|
1842
|
+
currentView === "projects" ? /* @__PURE__ */ jsx6(
|
|
1843
|
+
ProjectList,
|
|
1844
|
+
{
|
|
1845
|
+
projects,
|
|
1846
|
+
selectedIndex,
|
|
1847
|
+
onSelect: setSelectedIndex
|
|
1848
|
+
}
|
|
1849
|
+
) : /* @__PURE__ */ jsx6(
|
|
1403
1850
|
SessionList,
|
|
1404
1851
|
{
|
|
1405
|
-
sessions,
|
|
1852
|
+
sessions: currentSessions,
|
|
1406
1853
|
selectedIndex,
|
|
1407
1854
|
onSelect: setSelectedIndex
|
|
1408
1855
|
}
|
|
1409
1856
|
),
|
|
1410
|
-
/* @__PURE__ */
|
|
1411
|
-
|
|
1857
|
+
selectedSession && /* @__PURE__ */ jsx6(Preview, { session: selectedSession }),
|
|
1858
|
+
selectedProject && /* @__PURE__ */ jsxs6(
|
|
1859
|
+
Box6,
|
|
1860
|
+
{
|
|
1861
|
+
flexDirection: "column",
|
|
1862
|
+
borderStyle: "round",
|
|
1863
|
+
borderColor: "gray",
|
|
1864
|
+
paddingX: 1,
|
|
1865
|
+
paddingY: 0,
|
|
1866
|
+
marginTop: 1,
|
|
1867
|
+
children: [
|
|
1868
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1869
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "Project Preview" }),
|
|
1870
|
+
selectedProject.sortOrder !== null && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1871
|
+
" (#",
|
|
1872
|
+
selectedProject.sortOrder + 1,
|
|
1873
|
+
")"
|
|
1874
|
+
] })
|
|
1875
|
+
] }),
|
|
1876
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "blue", children: [
|
|
1877
|
+
"\u{1F4C1} ",
|
|
1878
|
+
selectedProject.path
|
|
1879
|
+
] }),
|
|
1880
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1881
|
+
selectedProject.sessionCount,
|
|
1882
|
+
" session",
|
|
1883
|
+
selectedProject.sessionCount !== 1 ? "s" : "",
|
|
1884
|
+
" \u2022 ",
|
|
1885
|
+
selectedProject.totalMessages,
|
|
1886
|
+
" messages"
|
|
1887
|
+
] })
|
|
1888
|
+
]
|
|
1889
|
+
}
|
|
1890
|
+
),
|
|
1891
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: mode === "confirm-delete" ? /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
|
|
1412
1892
|
'Delete "',
|
|
1413
1893
|
selectedSession?.customTitle || selectedSession?.title,
|
|
1414
1894
|
'"? [y/n]'
|
|
1415
|
-
] }) : mode === "rename" ? /* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1895
|
+
] }) : mode === "rename" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1896
|
+
/* @__PURE__ */ jsx6(Text6, { color: "magenta", children: "Rename: " }),
|
|
1897
|
+
/* @__PURE__ */ jsx6(
|
|
1418
1898
|
TextInput2,
|
|
1419
1899
|
{
|
|
1420
1900
|
value: renameValue,
|
|
@@ -1422,8 +1902,8 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1422
1902
|
placeholder: "Enter new name..."
|
|
1423
1903
|
}
|
|
1424
1904
|
),
|
|
1425
|
-
/* @__PURE__ */
|
|
1426
|
-
] }) : statusMessage ? /* @__PURE__ */
|
|
1905
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " [Enter] Save [Esc] Cancel" })
|
|
1906
|
+
] }) : mode === "sort-project" ? /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "Sort mode: [\u2191\u2193] Move project [Enter/Esc] Done" }) : statusMessage ? /* @__PURE__ */ jsx6(Text6, { color: "green", children: statusMessage }) : currentView === "projects" ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "[\u2191\u2193] Navigate [Enter] Open [s] Sort [\u2190\u2192] Switch tabs [q] Quit" }) : currentView === "project-sessions" ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "[\u2191\u2193] Navigate [Enter] Resume [s] Star [Esc] Back [r] Rename [d] Delete [q] Quit" }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "[\u2191\u2193] Navigate [Enter] Resume [s] Star [r] Rename [d] Delete [/] Search [q] Quit" }) })
|
|
1427
1907
|
] });
|
|
1428
1908
|
};
|
|
1429
1909
|
|
|
@@ -1943,20 +2423,22 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
|
|
|
1943
2423
|
const rawData = JSON.stringify({ filePath, messages, mtime: fileMtime });
|
|
1944
2424
|
const projectPath = getProjectPathFromIndex(filePath, sessionId_from_file);
|
|
1945
2425
|
const metadata = extractSessionMetadata(filePath);
|
|
1946
|
-
const isAutomated = metadata.isSidechain || metadata.isMeta;
|
|
2426
|
+
const isAutomated = metadata.isSidechain || metadata.isMeta || isAutomatedByContent(title);
|
|
1947
2427
|
let sessionId;
|
|
1948
2428
|
let isNew = false;
|
|
1949
2429
|
if (existingSession) {
|
|
1950
2430
|
sessionId = existingSession.id;
|
|
1951
2431
|
const needsMetadataUpdate = forceMetadataUpdate || !existingSession.isSidechain && !existingSession.isAutomated && isAutomated;
|
|
1952
|
-
|
|
2432
|
+
const needsProjectPathUpdate = !existingSession.projectPath && projectPath;
|
|
2433
|
+
if (existingSession.messageCount !== messages.length || needsMetadataUpdate || needsProjectPathUpdate) {
|
|
1953
2434
|
updateSession(sessionId, {
|
|
1954
2435
|
title,
|
|
1955
2436
|
summary,
|
|
1956
2437
|
rawData,
|
|
1957
2438
|
messages,
|
|
1958
2439
|
isSidechain: metadata.isSidechain,
|
|
1959
|
-
isAutomated
|
|
2440
|
+
isAutomated,
|
|
2441
|
+
projectPath: projectPath || void 0
|
|
1960
2442
|
});
|
|
1961
2443
|
if (verbose) {
|
|
1962
2444
|
const automatedTag = isAutomated ? chalk7.dim(" [auto]") : "";
|