@claude-sessions/core 0.4.7 → 0.4.8-beta.1
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/index.d.ts +50 -12
- package/dist/index.js +124 -35
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/{types-BMQErZZE.d.ts → types-CoLRUgPk.d.ts} +11 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Project, M as MessagePayload$1, a as Message, S as SummaryInfo, b as
|
|
2
|
-
export {
|
|
1
|
+
import { P as Project, M as MessagePayload$1, a as Message, T as TitleDisplayMode, S as SummaryInfo, b as SessionSortField, c as SessionTodos, d as MoveSessionResult, A as AgentInfo, e as SessionSortOptions, f as ProjectTreeData, C as CompressSessionOptions, g as SummarizeSessionOptions, h as ConversationLine, i as SearchResult, F as FileChange, j as SessionsIndex, k as SessionIndexEntry } from './types-CoLRUgPk.js';
|
|
2
|
+
export { r as CleanupPreview, q as ClearSessionsResult, x as CompressSessionResult, l as ContentItem, D as DeleteSessionResult, y as ProjectKnowledge, R as RenameSessionResult, t as ResumeSessionOptions, u as ResumeSessionResult, w as SessionAnalysis, o as SessionFilesSummary, m as SessionMeta, B as SessionSortOrder, s as SessionTreeData, p as SplitSessionResult, z as SummarizeSessionResult, n as TodoItem, v as ToolUsageStats } from './types-CoLRUgPk.js';
|
|
3
3
|
import * as effect_Cause from 'effect/Cause';
|
|
4
4
|
import { Effect } from 'effect';
|
|
5
5
|
|
|
@@ -71,7 +71,8 @@ declare const findProjectByWorkspacePath: (workspacePath: string, projectNames:
|
|
|
71
71
|
* Sort projects with priority:
|
|
72
72
|
* 1. Current project (if specified)
|
|
73
73
|
* 2. Current user's home directory subpaths
|
|
74
|
-
* 3.
|
|
74
|
+
* 3. Most recently modified first (by newest session file mtime)
|
|
75
|
+
* 4. Alphabetically by displayName as tiebreaker
|
|
75
76
|
*/
|
|
76
77
|
declare const sortProjects: (projects: Project[], options?: {
|
|
77
78
|
currentProjectName?: string | null;
|
|
@@ -94,25 +95,60 @@ declare const parseCommandMessage: (content?: string) => {
|
|
|
94
95
|
declare const extractTitle: (input: MessagePayload$1 | string | undefined) => string;
|
|
95
96
|
declare const isInvalidApiKeyMessage: (msg: Message) => boolean;
|
|
96
97
|
declare const isContinuationSummary: (msg: Message) => boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Options for getDisplayTitle when using the options-based signature
|
|
100
|
+
*/
|
|
101
|
+
interface DisplayTitleOptions {
|
|
102
|
+
customTitle?: string;
|
|
103
|
+
currentSummary?: string;
|
|
104
|
+
title?: string;
|
|
105
|
+
createdAt?: string;
|
|
106
|
+
maxLength?: number;
|
|
107
|
+
fallback?: string;
|
|
108
|
+
/** 'message' = first user message (default), 'datetime' = relative date/time */
|
|
109
|
+
mode?: TitleDisplayMode;
|
|
110
|
+
/** Locale for date formatting (e.g. 'de-DE', 'en-GB'). Defaults to system locale. */
|
|
111
|
+
locale?: string;
|
|
112
|
+
}
|
|
97
113
|
/**
|
|
98
114
|
* Get display title with fallback logic
|
|
99
|
-
* Priority: customTitle > currentSummary (truncated) > title > fallback
|
|
115
|
+
* Priority: customTitle > currentSummary (truncated) > title/datetime > fallback
|
|
100
116
|
* Also handles slash command format in title
|
|
117
|
+
*
|
|
118
|
+
* Supports two call signatures:
|
|
119
|
+
* - Legacy: getDisplayTitle(customTitle, currentSummary, title, maxLength?, fallback?)
|
|
120
|
+
* - Options: getDisplayTitle(options)
|
|
101
121
|
*/
|
|
102
|
-
declare
|
|
122
|
+
declare function getDisplayTitle(options: DisplayTitleOptions): string;
|
|
123
|
+
declare function getDisplayTitle(customTitle: string | undefined, currentSummary: string | undefined, title: string | undefined, maxLength?: number, fallback?: string): string;
|
|
103
124
|
/**
|
|
104
125
|
* Mask home directory path with ~
|
|
105
126
|
* Only masks the specified homeDir, not other users' paths
|
|
106
127
|
*/
|
|
107
128
|
declare const maskHomePath: (text: string, homeDir: string) => string;
|
|
108
129
|
/**
|
|
109
|
-
* Get sort timestamp for session (Unix timestamp ms)
|
|
110
|
-
* Priority: summaries[0].timestamp > createdAt > 0
|
|
130
|
+
* Get summary-based sort timestamp for session (Unix timestamp ms)
|
|
131
|
+
* Used for 'summary' sort field. Priority: summaries[0].timestamp > createdAt > 0
|
|
111
132
|
*/
|
|
133
|
+
declare const getSummarySortTimestamp: (session: {
|
|
134
|
+
summaries?: SummaryInfo[];
|
|
135
|
+
createdAt?: string;
|
|
136
|
+
}) => number;
|
|
137
|
+
/** @deprecated Use getSummarySortTimestamp instead */
|
|
112
138
|
declare const getSessionSortTimestamp: (session: {
|
|
113
139
|
summaries?: SummaryInfo[];
|
|
114
140
|
createdAt?: string;
|
|
115
141
|
}) => number;
|
|
142
|
+
/**
|
|
143
|
+
* Get display-ready sort timestamp based on active sort field.
|
|
144
|
+
* Returns the timestamp matching what the user sees as the sort order.
|
|
145
|
+
*/
|
|
146
|
+
declare const getDisplaySortTimestamp: (session: {
|
|
147
|
+
summaries?: SummaryInfo[];
|
|
148
|
+
createdAt?: string;
|
|
149
|
+
updatedAt?: string;
|
|
150
|
+
fileMtime?: number;
|
|
151
|
+
}, sortField: SessionSortField) => number;
|
|
116
152
|
/**
|
|
117
153
|
* Try to parse a single JSON line, returning null on failure with optional warning log
|
|
118
154
|
* Use this when you want to skip invalid lines instead of throwing
|
|
@@ -139,7 +175,7 @@ declare const readJsonlFile: <T = Record<string, unknown>>(filePath: string, opt
|
|
|
139
175
|
* @param timestamp - Unix timestamp (ms) or ISO date string
|
|
140
176
|
* @returns Relative time string
|
|
141
177
|
*/
|
|
142
|
-
declare const formatRelativeTime: (timestamp: number | string) => string;
|
|
178
|
+
declare const formatRelativeTime: (timestamp: number | string, locale?: string) => string;
|
|
143
179
|
/**
|
|
144
180
|
* Calculate total todo count from session todos
|
|
145
181
|
* Includes both session-level and agent-level todos
|
|
@@ -483,6 +519,7 @@ declare const previewCleanup: (projectName?: string) => Effect.Effect<{
|
|
|
483
519
|
emptyWithTodosCount: number;
|
|
484
520
|
orphanAgentCount: number;
|
|
485
521
|
orphanTodoCount: number;
|
|
522
|
+
isStale: boolean;
|
|
486
523
|
}[], effect_Cause.UnknownException, never>;
|
|
487
524
|
declare const clearSessions: (options: {
|
|
488
525
|
projectName?: string;
|
|
@@ -491,12 +528,15 @@ declare const clearSessions: (options: {
|
|
|
491
528
|
skipWithTodos?: boolean;
|
|
492
529
|
clearOrphanAgents?: boolean;
|
|
493
530
|
clearOrphanTodos?: boolean;
|
|
531
|
+
clearStale?: boolean;
|
|
532
|
+
staleProjects?: string[];
|
|
494
533
|
}) => Effect.Effect<{
|
|
495
534
|
success: true;
|
|
496
535
|
deletedCount: number;
|
|
497
536
|
removedMessageCount: number;
|
|
498
537
|
deletedOrphanAgentCount: number;
|
|
499
538
|
deletedOrphanTodoCount: number;
|
|
539
|
+
deletedStaleProjectCount: number;
|
|
500
540
|
}, effect_Cause.UnknownException, never>;
|
|
501
541
|
|
|
502
542
|
declare const searchSessions: (query: string, options?: {
|
|
@@ -573,9 +613,7 @@ declare function validateChain(messages: readonly GenericMessage[]): ValidationR
|
|
|
573
613
|
errors: ChainError[];
|
|
574
614
|
};
|
|
575
615
|
/**
|
|
576
|
-
* Validate for unwanted
|
|
577
|
-
*
|
|
578
|
-
* Only 'Stop' hookEvent is treated as an error (should be removed)
|
|
616
|
+
* Validate for unwanted cleanup artifacts that should not remain in sessions.
|
|
579
617
|
*/
|
|
580
618
|
declare function validateProgressMessages(messages: readonly GenericMessage[]): ValidationResult & {
|
|
581
619
|
errors: ProgressError[];
|
|
@@ -670,4 +708,4 @@ declare const getLogger: () => Logger;
|
|
|
670
708
|
*/
|
|
671
709
|
declare const createLogger: (namespace: string) => Logger;
|
|
672
710
|
|
|
673
|
-
export { AgentInfo, type ChainError, CompressSessionOptions, ConversationLine, FileChange, type GenericMessage, type Logger, Message, MessagePayload$1 as MessagePayload, MoveSessionResult, type ProgressError, Project, ProjectTreeData, SearchResult, SessionIndexEntry, SessionSortOptions, SessionTodos, SessionsIndex, SummarizeSessionOptions, SummaryInfo, TREE_ICONS, type ToolUseResultError, type TreeItemType, type ValidationResult, analyzeSession, autoRepairChain, canMoveSession, clearSessions, compressSession, createLogger, deleteLinkedTodos, deleteMessage, deleteMessageWithChainRepair, deleteOrphanAgents, deleteOrphanTodos, deleteSession, expandHomePath, extractProjectKnowledge, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, formatRelativeTime, generateTreeNodeId, getCachePath, getDisplayTitle, getIndexEntryDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionSortTimestamp, getSessionTooltip, getSessionsDir, getTodoIcon, getTodosDir, getTotalTodoCount, hasSessionsIndex, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, loadSessionsIndex, maskHomePath, moveSession, parseCommandMessage, parseJsonlLines, parseTreeNodeId, pathToFolderName, previewCleanup, readJsonlFile, readSession, renameSession, repairChain, repairParentUuidChain, restoreMessage, searchSessions, sessionHasSubItems, sessionHasTodos, setLogger, sortIndexEntriesByModified, sortProjects, splitSession, summarizeSession, tryParseJsonLine, updateSessionSummary, validateChain, validateProgressMessages, validateToolUseResult };
|
|
711
|
+
export { AgentInfo, type ChainError, CompressSessionOptions, ConversationLine, type DisplayTitleOptions, FileChange, type GenericMessage, type Logger, Message, MessagePayload$1 as MessagePayload, MoveSessionResult, type ProgressError, Project, ProjectTreeData, SearchResult, SessionIndexEntry, SessionSortField, SessionSortOptions, SessionTodos, SessionsIndex, SummarizeSessionOptions, SummaryInfo, TREE_ICONS, TitleDisplayMode, type ToolUseResultError, type TreeItemType, type ValidationResult, analyzeSession, autoRepairChain, canMoveSession, clearSessions, compressSession, createLogger, deleteLinkedTodos, deleteMessage, deleteMessageWithChainRepair, deleteOrphanAgents, deleteOrphanTodos, deleteSession, expandHomePath, extractProjectKnowledge, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, formatRelativeTime, generateTreeNodeId, getCachePath, getDisplaySortTimestamp, getDisplayTitle, getIndexEntryDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionSortTimestamp, getSessionTooltip, getSessionsDir, getSummarySortTimestamp, getTodoIcon, getTodosDir, getTotalTodoCount, hasSessionsIndex, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, loadSessionsIndex, maskHomePath, moveSession, parseCommandMessage, parseJsonlLines, parseTreeNodeId, pathToFolderName, previewCleanup, readJsonlFile, readSession, renameSession, repairChain, repairParentUuidChain, restoreMessage, searchSessions, sessionHasSubItems, sessionHasTodos, setLogger, sortIndexEntriesByModified, sortProjects, splitSession, summarizeSession, tryParseJsonLine, updateSessionSummary, validateChain, validateProgressMessages, validateToolUseResult };
|
package/dist/index.js
CHANGED
|
@@ -95,11 +95,31 @@ var isContinuationSummary = (msg) => {
|
|
|
95
95
|
const text = extractTextContent(msg.message);
|
|
96
96
|
return text.startsWith("This session is being continued from");
|
|
97
97
|
};
|
|
98
|
-
|
|
98
|
+
function getDisplayTitle(customTitleOrOptions, currentSummary, title, maxLength = 60, fallback = "Untitled") {
|
|
99
|
+
let mode = "message";
|
|
100
|
+
let createdAt;
|
|
101
|
+
let customTitle;
|
|
102
|
+
let locale;
|
|
103
|
+
if (typeof customTitleOrOptions === "object" && customTitleOrOptions !== null) {
|
|
104
|
+
const opts = customTitleOrOptions;
|
|
105
|
+
customTitle = opts.customTitle;
|
|
106
|
+
currentSummary = opts.currentSummary;
|
|
107
|
+
title = opts.title;
|
|
108
|
+
createdAt = opts.createdAt;
|
|
109
|
+
maxLength = opts.maxLength ?? 60;
|
|
110
|
+
fallback = opts.fallback ?? "Untitled";
|
|
111
|
+
mode = opts.mode ?? "message";
|
|
112
|
+
locale = opts.locale;
|
|
113
|
+
} else {
|
|
114
|
+
customTitle = customTitleOrOptions;
|
|
115
|
+
}
|
|
99
116
|
if (customTitle) return customTitle;
|
|
100
117
|
if (currentSummary) {
|
|
101
118
|
return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
|
|
102
119
|
}
|
|
120
|
+
if (mode === "datetime" && createdAt) {
|
|
121
|
+
return formatRelativeTime(createdAt, locale);
|
|
122
|
+
}
|
|
103
123
|
if (title && title !== "Untitled") {
|
|
104
124
|
const firstParagraph = title.includes("\n\n") ? title.split("\n\n")[0] : title;
|
|
105
125
|
if (firstParagraph.includes("<command-name>")) {
|
|
@@ -109,7 +129,7 @@ var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallb
|
|
|
109
129
|
return firstParagraph;
|
|
110
130
|
}
|
|
111
131
|
return fallback;
|
|
112
|
-
}
|
|
132
|
+
}
|
|
113
133
|
var replaceMessageContent = (msg, text) => ({
|
|
114
134
|
...msg,
|
|
115
135
|
message: {
|
|
@@ -143,10 +163,27 @@ var maskHomePath = (text, homeDir) => {
|
|
|
143
163
|
const regex = new RegExp(`${escapedHome}(?=[/\\\\]|$)`, "g");
|
|
144
164
|
return text.replace(regex, "~");
|
|
145
165
|
};
|
|
146
|
-
var
|
|
166
|
+
var getSummarySortTimestamp = (session) => {
|
|
147
167
|
const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt;
|
|
148
168
|
return timestampStr ? new Date(timestampStr).getTime() : 0;
|
|
149
169
|
};
|
|
170
|
+
var getSessionSortTimestamp = getSummarySortTimestamp;
|
|
171
|
+
var getDisplaySortTimestamp = (session, sortField) => {
|
|
172
|
+
switch (sortField) {
|
|
173
|
+
case "updated": {
|
|
174
|
+
if (session.updatedAt) return new Date(session.updatedAt).getTime();
|
|
175
|
+
return getSummarySortTimestamp(session);
|
|
176
|
+
}
|
|
177
|
+
case "created": {
|
|
178
|
+
if (session.createdAt) return new Date(session.createdAt).getTime();
|
|
179
|
+
return getSummarySortTimestamp(session);
|
|
180
|
+
}
|
|
181
|
+
case "modified":
|
|
182
|
+
return session.fileMtime ?? getSummarySortTimestamp(session);
|
|
183
|
+
default:
|
|
184
|
+
return getSummarySortTimestamp(session);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
150
187
|
var tryParseJsonLine = (line, lineNumber, filePath) => {
|
|
151
188
|
try {
|
|
152
189
|
return JSON.parse(line);
|
|
@@ -179,7 +216,7 @@ var readJsonlFile = (filePath, options) => Effect.gen(function* () {
|
|
|
179
216
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
180
217
|
return parseJsonlLines(lines, filePath, options);
|
|
181
218
|
});
|
|
182
|
-
var formatRelativeTime = (timestamp) => {
|
|
219
|
+
var formatRelativeTime = (timestamp, locale) => {
|
|
183
220
|
const date = typeof timestamp === "string" ? new Date(timestamp) : new Date(timestamp);
|
|
184
221
|
const now = /* @__PURE__ */ new Date();
|
|
185
222
|
const diff = now.getTime() - date.getTime();
|
|
@@ -190,7 +227,7 @@ var formatRelativeTime = (timestamp) => {
|
|
|
190
227
|
if (minutes < 60) return `${minutes}m ago`;
|
|
191
228
|
if (hours < 24) return `${hours}h ago`;
|
|
192
229
|
if (days < 7) return `${days}d ago`;
|
|
193
|
-
return date.toLocaleDateString();
|
|
230
|
+
return date.toLocaleDateString(locale, { day: "numeric", month: "short", year: "numeric" });
|
|
194
231
|
};
|
|
195
232
|
var getTotalTodoCount = (todos) => {
|
|
196
233
|
return todos.sessionTodos.length + todos.agentTodos.reduce((sum, a) => sum + a.todos.length, 0);
|
|
@@ -390,6 +427,9 @@ var sortProjects = (projects, options = {}) => {
|
|
|
390
427
|
if (aIsUserHome && !bIsUserHome) return -1;
|
|
391
428
|
if (!aIsUserHome && bIsUserHome) return 1;
|
|
392
429
|
}
|
|
430
|
+
const aMtime = a.lastModified ?? 0;
|
|
431
|
+
const bMtime = b.lastModified ?? 0;
|
|
432
|
+
if (aMtime !== bMtime) return bMtime - aMtime;
|
|
393
433
|
return a.displayName.localeCompare(b.displayName);
|
|
394
434
|
});
|
|
395
435
|
};
|
|
@@ -499,8 +539,8 @@ var findOrphanAgentsWithPaths = (projectName) => Effect2.gen(function* () {
|
|
|
499
539
|
}
|
|
500
540
|
for (const entry of files) {
|
|
501
541
|
const entryPath = path2.join(projectPath, entry);
|
|
502
|
-
const
|
|
503
|
-
if (
|
|
542
|
+
const stat6 = yield* Effect2.tryPromise(() => fs3.stat(entryPath).catch(() => null));
|
|
543
|
+
if (stat6?.isDirectory() && !entry.startsWith(".")) {
|
|
504
544
|
const subagentsPath = path2.join(entryPath, "subagents");
|
|
505
545
|
const subagentsExists = yield* Effect2.tryPromise(
|
|
506
546
|
() => fs3.stat(subagentsPath).then(() => true).catch(() => false)
|
|
@@ -818,11 +858,24 @@ var listProjects = Effect4.gen(function* () {
|
|
|
818
858
|
const files = yield* Effect4.tryPromise(() => fs5.readdir(projectPath));
|
|
819
859
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
820
860
|
const displayName = yield* Effect4.tryPromise(() => folderNameToPath(entry.name));
|
|
861
|
+
let lastModified = 0;
|
|
862
|
+
if (sessionFiles.length > 0) {
|
|
863
|
+
const stats = yield* Effect4.all(
|
|
864
|
+
sessionFiles.map(
|
|
865
|
+
(f) => Effect4.tryPromise(
|
|
866
|
+
() => fs5.stat(path4.join(projectPath, f)).then((s) => s.mtimeMs)
|
|
867
|
+
).pipe(Effect4.orElseSucceed(() => 0))
|
|
868
|
+
),
|
|
869
|
+
{ concurrency: 20 }
|
|
870
|
+
);
|
|
871
|
+
lastModified = stats.reduce((max, value) => value > max ? value : max, 0);
|
|
872
|
+
}
|
|
821
873
|
return {
|
|
822
874
|
name: entry.name,
|
|
823
875
|
displayName,
|
|
824
876
|
path: projectPath,
|
|
825
|
-
sessionCount: sessionFiles.length
|
|
877
|
+
sessionCount: sessionFiles.length,
|
|
878
|
+
lastModified
|
|
826
879
|
};
|
|
827
880
|
})
|
|
828
881
|
),
|
|
@@ -897,19 +950,7 @@ function validateProgressMessages(messages) {
|
|
|
897
950
|
const errors = [];
|
|
898
951
|
for (let i = 0; i < messages.length; i++) {
|
|
899
952
|
const msg = messages[i];
|
|
900
|
-
if (msg.type === "
|
|
901
|
-
const progressMsg = msg;
|
|
902
|
-
const hookEvent = progressMsg.hookEvent ?? progressMsg.data?.hookEvent;
|
|
903
|
-
const hookName = progressMsg.hookName ?? progressMsg.data?.hookName;
|
|
904
|
-
if (hookEvent === "Stop") {
|
|
905
|
-
errors.push({
|
|
906
|
-
type: "unwanted_progress",
|
|
907
|
-
line: i + 1,
|
|
908
|
-
hookEvent,
|
|
909
|
-
hookName
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
} else if (msg.type === "saved_hook_context") {
|
|
953
|
+
if (msg.type === "saved_hook_context") {
|
|
913
954
|
errors.push({
|
|
914
955
|
type: "unwanted_progress",
|
|
915
956
|
line: i + 1,
|
|
@@ -1199,8 +1240,8 @@ var deleteSession = (projectName, sessionId) => Effect5.gen(function* () {
|
|
|
1199
1240
|
const projectPath = path5.join(sessionsDir, projectName);
|
|
1200
1241
|
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
1201
1242
|
const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
|
|
1202
|
-
const
|
|
1203
|
-
if (
|
|
1243
|
+
const stat6 = yield* Effect5.tryPromise(() => fs6.stat(filePath));
|
|
1244
|
+
if (stat6.size === 0) {
|
|
1204
1245
|
yield* Effect5.tryPromise(() => fs6.unlink(filePath));
|
|
1205
1246
|
const agentBackupDir2 = path5.join(projectPath, ".bak");
|
|
1206
1247
|
yield* Effect5.tryPromise(() => fs6.mkdir(agentBackupDir2, { recursive: true }));
|
|
@@ -1590,7 +1631,7 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
|
|
|
1590
1631
|
}
|
|
1591
1632
|
const todos = yield* findLinkedTodos(sessionId, linkedAgentIds);
|
|
1592
1633
|
const createdAt = firstMessage?.timestamp ?? void 0;
|
|
1593
|
-
const sortTimestamp =
|
|
1634
|
+
const sortTimestamp = getSummarySortTimestamp({ summaries, createdAt });
|
|
1594
1635
|
return {
|
|
1595
1636
|
id: sessionId,
|
|
1596
1637
|
projectName,
|
|
@@ -1689,12 +1730,16 @@ var buildProjectTreeResult = (project, sessions, sort) => {
|
|
|
1689
1730
|
if (isErrorSessionTitle(s.currentSummary)) return false;
|
|
1690
1731
|
return true;
|
|
1691
1732
|
});
|
|
1733
|
+
const displaySessions = filteredSessions.map((s) => ({
|
|
1734
|
+
...s,
|
|
1735
|
+
sortTimestamp: getDisplaySortTimestamp(s, sort.field)
|
|
1736
|
+
}));
|
|
1692
1737
|
return {
|
|
1693
1738
|
name: project.name,
|
|
1694
1739
|
displayName: project.displayName,
|
|
1695
1740
|
path: project.path,
|
|
1696
|
-
sessionCount:
|
|
1697
|
-
sessions:
|
|
1741
|
+
sessionCount: displaySessions.length,
|
|
1742
|
+
sessions: displaySessions
|
|
1698
1743
|
};
|
|
1699
1744
|
};
|
|
1700
1745
|
var buildTreeCache = (globalUuidMap, allSummaries, sessions, fileMtimes) => {
|
|
@@ -1721,7 +1766,7 @@ var updateSessionSummaries = (cached, summariesByTargetSession) => {
|
|
|
1721
1766
|
const oldJson = JSON.stringify(cached.summaries);
|
|
1722
1767
|
const newJson = JSON.stringify(newSummaries);
|
|
1723
1768
|
if (oldJson === newJson) return cached;
|
|
1724
|
-
const newSortTimestamp =
|
|
1769
|
+
const newSortTimestamp = getSummarySortTimestamp({
|
|
1725
1770
|
summaries: newSummaries,
|
|
1726
1771
|
createdAt: cached.createdAt
|
|
1727
1772
|
});
|
|
@@ -1750,8 +1795,8 @@ var loadProjectTreeData = (projectName, sortOptions) => Effect6.gen(function* ()
|
|
|
1750
1795
|
(file) => Effect6.gen(function* () {
|
|
1751
1796
|
const filePath = path7.join(projectPath, file);
|
|
1752
1797
|
try {
|
|
1753
|
-
const
|
|
1754
|
-
fileMtimes.set(file.replace(".jsonl", ""),
|
|
1798
|
+
const stat6 = yield* Effect6.tryPromise(() => fs8.stat(filePath));
|
|
1799
|
+
fileMtimes.set(file.replace(".jsonl", ""), stat6.mtimeMs);
|
|
1755
1800
|
} catch {
|
|
1756
1801
|
}
|
|
1757
1802
|
})
|
|
@@ -2007,9 +2052,12 @@ var compressSession = (projectName, sessionId, options = {}) => Effect7.gen(func
|
|
|
2007
2052
|
const messagesToRemove = [];
|
|
2008
2053
|
const filteredMessages = messages.filter((msg, idx) => {
|
|
2009
2054
|
if (msg.type === "progress") {
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2055
|
+
const hookEvent = (typeof msg.hookEvent === "string" ? msg.hookEvent : void 0) ?? (typeof msg.data === "object" && msg.data !== null && "hookEvent" in msg.data ? msg.data.hookEvent : void 0);
|
|
2056
|
+
if (hookEvent !== "Stop") {
|
|
2057
|
+
removedProgress++;
|
|
2058
|
+
messagesToRemove.push(msg);
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2013
2061
|
}
|
|
2014
2062
|
if (msg.type === "custom-title") {
|
|
2015
2063
|
if (customTitleIndices.length > 1 && idx !== customTitleIndices[customTitleIndices.length - 1]) {
|
|
@@ -2196,6 +2244,7 @@ var summarizeSession = (projectName, sessionId, options = {}) => Effect7.gen(fun
|
|
|
2196
2244
|
import { Effect as Effect8 } from "effect";
|
|
2197
2245
|
import * as fs10 from "fs/promises";
|
|
2198
2246
|
import * as path9 from "path";
|
|
2247
|
+
import * as os2 from "os";
|
|
2199
2248
|
var cleanInvalidMessages = (projectName, sessionId) => Effect8.gen(function* () {
|
|
2200
2249
|
const filePath = path9.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
2201
2250
|
const content = yield* Effect8.tryPromise(() => fs10.readFile(filePath, "utf-8"));
|
|
@@ -2243,9 +2292,32 @@ var previewCleanup = (projectName) => Effect8.gen(function* () {
|
|
|
2243
2292
|
const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
|
|
2244
2293
|
const orphanTodos = yield* findOrphanTodos();
|
|
2245
2294
|
const orphanTodoCount = orphanTodos.length;
|
|
2295
|
+
const homeDir = os2.homedir();
|
|
2296
|
+
const staleSet = /* @__PURE__ */ new Set();
|
|
2297
|
+
yield* Effect8.all(
|
|
2298
|
+
targetProjects.map(
|
|
2299
|
+
(project) => Effect8.tryPromise(async () => {
|
|
2300
|
+
const displayPath = await folderNameToPath(project.name);
|
|
2301
|
+
const absPath = expandHomePath(displayPath, homeDir);
|
|
2302
|
+
try {
|
|
2303
|
+
const stats = await fs10.stat(absPath);
|
|
2304
|
+
if (!stats.isDirectory()) {
|
|
2305
|
+
staleSet.add(project.name);
|
|
2306
|
+
}
|
|
2307
|
+
} catch (error) {
|
|
2308
|
+
const err = error;
|
|
2309
|
+
if (err.code === "ENOENT") {
|
|
2310
|
+
staleSet.add(project.name);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
})
|
|
2314
|
+
),
|
|
2315
|
+
{ concurrency: 10 }
|
|
2316
|
+
);
|
|
2246
2317
|
const results = yield* Effect8.all(
|
|
2247
2318
|
targetProjects.map(
|
|
2248
2319
|
(project) => Effect8.gen(function* () {
|
|
2320
|
+
const isStale = staleSet.has(project.name);
|
|
2249
2321
|
const sessions = yield* listSessions(project.name);
|
|
2250
2322
|
const emptySessions = sessions.filter((s) => s.messageCount === 0);
|
|
2251
2323
|
const invalidSessions = sessions.filter(
|
|
@@ -2266,8 +2338,9 @@ var previewCleanup = (projectName) => Effect8.gen(function* () {
|
|
|
2266
2338
|
invalidSessions,
|
|
2267
2339
|
emptyWithTodosCount,
|
|
2268
2340
|
orphanAgentCount: orphanAgents.length,
|
|
2269
|
-
orphanTodoCount: 0
|
|
2341
|
+
orphanTodoCount: 0,
|
|
2270
2342
|
// Will set for first project only
|
|
2343
|
+
isStale
|
|
2271
2344
|
};
|
|
2272
2345
|
})
|
|
2273
2346
|
),
|
|
@@ -2285,7 +2358,9 @@ var clearSessions = (options) => Effect8.gen(function* () {
|
|
|
2285
2358
|
clearInvalid = true,
|
|
2286
2359
|
skipWithTodos = true,
|
|
2287
2360
|
clearOrphanAgents = true,
|
|
2288
|
-
clearOrphanTodos = false
|
|
2361
|
+
clearOrphanTodos = false,
|
|
2362
|
+
clearStale = false,
|
|
2363
|
+
staleProjects = []
|
|
2289
2364
|
} = options;
|
|
2290
2365
|
const projects = yield* listProjects;
|
|
2291
2366
|
const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
|
|
@@ -2343,12 +2418,24 @@ var clearSessions = (options) => Effect8.gen(function* () {
|
|
|
2343
2418
|
const result = yield* deleteOrphanTodos();
|
|
2344
2419
|
deletedOrphanTodoCount = result.deletedCount;
|
|
2345
2420
|
}
|
|
2421
|
+
let deletedStaleProjectCount = 0;
|
|
2422
|
+
if (clearStale && staleProjects.length > 0) {
|
|
2423
|
+
const sessionsDir = getSessionsDir();
|
|
2424
|
+
for (const staleProjectName of staleProjects) {
|
|
2425
|
+
const projectSessionsPath = path9.join(sessionsDir, staleProjectName);
|
|
2426
|
+
const deleted = yield* Effect8.tryPromise(
|
|
2427
|
+
() => fs10.rm(projectSessionsPath, { recursive: true, force: true }).then(() => true)
|
|
2428
|
+
).pipe(Effect8.orElse(() => Effect8.succeed(false)));
|
|
2429
|
+
if (deleted) deletedStaleProjectCount++;
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2346
2432
|
return {
|
|
2347
2433
|
success: true,
|
|
2348
2434
|
deletedCount: deletedSessionCount,
|
|
2349
2435
|
removedMessageCount,
|
|
2350
2436
|
deletedOrphanAgentCount,
|
|
2351
|
-
deletedOrphanTodoCount
|
|
2437
|
+
deletedOrphanTodoCount,
|
|
2438
|
+
deletedStaleProjectCount
|
|
2352
2439
|
};
|
|
2353
2440
|
});
|
|
2354
2441
|
|
|
@@ -2575,6 +2662,7 @@ export {
|
|
|
2575
2662
|
formatRelativeTime,
|
|
2576
2663
|
generateTreeNodeId,
|
|
2577
2664
|
getCachePath,
|
|
2665
|
+
getDisplaySortTimestamp,
|
|
2578
2666
|
getDisplayTitle,
|
|
2579
2667
|
getIndexEntryDisplayTitle,
|
|
2580
2668
|
getLogger,
|
|
@@ -2583,6 +2671,7 @@ export {
|
|
|
2583
2671
|
getSessionSortTimestamp,
|
|
2584
2672
|
getSessionTooltip,
|
|
2585
2673
|
getSessionsDir,
|
|
2674
|
+
getSummarySortTimestamp,
|
|
2586
2675
|
getTodoIcon,
|
|
2587
2676
|
getTodosDir,
|
|
2588
2677
|
getTotalTodoCount,
|