@claude-sessions/core 0.2.1 → 0.3.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 CHANGED
@@ -4,28 +4,88 @@ import { Effect } from 'effect';
4
4
  /**
5
5
  * Core types for Claude Code session management
6
6
  */
7
- interface ContentItem {
7
+ /**
8
+ * Base interface for objects with a type discriminator.
9
+ * Used as a foundation for message and content types.
10
+ */
11
+ interface TypedObject {
12
+ type: string;
13
+ }
14
+ /**
15
+ * Text content block in a message.
16
+ * @see https://docs.anthropic.com/en/api/messages
17
+ */
18
+ interface TextContent {
19
+ type: 'text';
20
+ text: string;
21
+ }
22
+ /**
23
+ * Tool result content returned after tool execution.
24
+ * Contains the output from a tool_use request.
25
+ */
26
+ interface ToolResultContent {
27
+ type: 'tool_result';
28
+ tool_use_id: string;
29
+ content: string;
30
+ is_error?: boolean;
31
+ }
32
+ /**
33
+ * Tool use request from the assistant.
34
+ * Represents a function call with input parameters.
35
+ */
36
+ interface ToolUseContent {
37
+ type: 'tool_use';
38
+ id: string;
39
+ name: string;
40
+ input: unknown;
41
+ }
42
+ /**
43
+ * Union type for all message content blocks.
44
+ * Includes known types and a fallback for unknown content types.
45
+ */
46
+ type ContentItem = TextContent | ToolResultContent | ToolUseContent | {
8
47
  type: string;
9
48
  text?: string;
10
49
  name?: string;
11
50
  input?: unknown;
12
- }
51
+ content?: string;
52
+ };
53
+ /**
54
+ * Message payload containing role and content.
55
+ * Matches Anthropic API message structure.
56
+ */
13
57
  interface MessagePayload {
14
58
  role?: string;
15
59
  content?: ContentItem[] | string;
16
60
  model?: string;
17
61
  }
18
- interface Message {
62
+ /**
63
+ * A single message in a Claude Code session.
64
+ * Stored as a line in a JSONL session file.
65
+ */
66
+ interface Message extends TypedObject {
67
+ /** Unique identifier for this message */
19
68
  uuid: string;
69
+ /** Parent message UUID for conversation threading */
20
70
  parentUuid?: string | null;
21
- type: string;
22
- message?: MessagePayload;
23
- timestamp?: string;
71
+ /** Session ID this message belongs to */
24
72
  sessionId?: string;
25
- isCompactSummary?: boolean;
73
+ /** Timestamp in ISO format */
74
+ timestamp?: string;
75
+ /** Message content (nested payload structure) */
76
+ message?: MessagePayload;
77
+ /** Direct content (alternative to message.content) */
78
+ content?: ContentItem[] | string;
79
+ /** User-defined custom title for this message */
26
80
  customTitle?: string;
81
+ /** Summary text for summary-type messages */
27
82
  summary?: string;
83
+ /** Flag indicating this is a context continuation summary */
84
+ isCompactSummary?: boolean;
85
+ /** Tool use result text (for tool_result messages) */
86
+ toolUseResult?: string;
28
87
  }
88
+ /** Basic metadata for a session, used in listings and summaries */
29
89
  interface SessionMeta {
30
90
  id: string;
31
91
  projectName: string;
@@ -34,17 +94,21 @@ interface SessionMeta {
34
94
  createdAt?: string;
35
95
  updatedAt?: string;
36
96
  }
97
+ /** Project directory information */
37
98
  interface Project {
38
99
  name: string;
39
100
  displayName: string;
40
101
  path: string;
41
102
  sessionCount: number;
42
103
  }
104
+ /** A single todo item from TodoWrite tool */
43
105
  interface TodoItem {
44
106
  content: string;
45
107
  status: 'pending' | 'in_progress' | 'completed';
108
+ /** Active form text shown during execution */
46
109
  activeForm?: string;
47
110
  }
111
+ /** Aggregated todos for a session including agent todos */
48
112
  interface SessionTodos {
49
113
  sessionId: string;
50
114
  sessionTodos: TodoItem[];
@@ -54,28 +118,33 @@ interface SessionTodos {
54
118
  }[];
55
119
  hasTodos: boolean;
56
120
  }
121
+ /** A file modification recorded during a session */
57
122
  interface FileChange {
58
123
  path: string;
59
124
  action: 'created' | 'modified' | 'deleted';
60
125
  timestamp?: string;
61
126
  messageUuid?: string;
62
127
  }
128
+ /** Summary of all file changes in a session */
63
129
  interface SessionFilesSummary {
64
130
  sessionId: string;
65
131
  projectName: string;
66
132
  files: FileChange[];
67
133
  totalChanges: number;
68
134
  }
135
+ /** Result of deleting a session */
69
136
  interface DeleteSessionResult {
70
137
  success: boolean;
71
138
  backupPath?: string;
72
139
  deletedAgents: number;
73
140
  deletedTodos?: number;
74
141
  }
142
+ /** Result of renaming a session */
75
143
  interface RenameSessionResult {
76
144
  success: boolean;
77
145
  error?: string;
78
146
  }
147
+ /** Result of splitting a session at a message */
79
148
  interface SplitSessionResult {
80
149
  success: boolean;
81
150
  newSessionId?: string;
@@ -84,10 +153,12 @@ interface SplitSessionResult {
84
153
  duplicatedSummary?: boolean;
85
154
  error?: string;
86
155
  }
156
+ /** Result of moving a session to another project */
87
157
  interface MoveSessionResult {
88
158
  success: boolean;
89
159
  error?: string;
90
160
  }
161
+ /** Result of clearing empty/invalid sessions */
91
162
  interface ClearSessionsResult {
92
163
  success: boolean;
93
164
  deletedCount: number;
@@ -95,6 +166,7 @@ interface ClearSessionsResult {
95
166
  deletedOrphanAgentCount?: number;
96
167
  deletedOrphanTodoCount?: number;
97
168
  }
169
+ /** Preview of sessions that would be cleaned up */
98
170
  interface CleanupPreview {
99
171
  project: string;
100
172
  emptySessions: SessionMeta[];
@@ -103,6 +175,7 @@ interface CleanupPreview {
103
175
  orphanAgentCount?: number;
104
176
  orphanTodoCount?: number;
105
177
  }
178
+ /** A search result matching a session or message */
106
179
  interface SearchResult {
107
180
  sessionId: string;
108
181
  projectName: string;
@@ -112,30 +185,39 @@ interface SearchResult {
112
185
  messageUuid?: string;
113
186
  timestamp?: string;
114
187
  }
188
+ /** Summary extracted from a session for display */
115
189
  interface SummaryInfo {
116
190
  summary: string;
117
191
  leafUuid?: string;
118
192
  timestamp?: string;
119
193
  }
194
+ /** Agent information for tree display */
120
195
  interface AgentInfo {
121
196
  id: string;
122
197
  name?: string;
123
198
  messageCount: number;
124
199
  }
200
+ /** Full session data for tree view rendering */
125
201
  interface SessionTreeData {
126
202
  id: string;
127
203
  projectName: string;
204
+ /** First user message title */
128
205
  title: string;
206
+ /** User-set custom title */
129
207
  customTitle?: string;
130
- lastSummary?: string;
208
+ /** Current (first) summary text for display/tooltip */
209
+ currentSummary?: string;
131
210
  messageCount: number;
132
211
  createdAt?: string;
133
212
  updatedAt?: string;
213
+ /** All summaries in reverse order (newest first) */
134
214
  summaries: SummaryInfo[];
135
215
  agents: AgentInfo[];
136
216
  todos: SessionTodos;
217
+ /** UUID of last compact_boundary message */
137
218
  lastCompactBoundaryUuid?: string;
138
219
  }
220
+ /** Project with all sessions for tree view */
139
221
  interface ProjectTreeData {
140
222
  name: string;
141
223
  displayName: string;
@@ -192,7 +274,12 @@ declare const folderNameToPath: (folderName: string) => string;
192
274
  declare const extractTextContent: (message: MessagePayload | undefined) => string;
193
275
  declare const extractTitle: (text: string) => string;
194
276
  declare const isInvalidApiKeyMessage: (msg: Message) => boolean;
195
- declare const isContinuationSummary: (msg: Record<string, unknown>) => boolean;
277
+ declare const isContinuationSummary: (msg: Message) => boolean;
278
+ /**
279
+ * Get display title with fallback logic
280
+ * Priority: customTitle > currentSummary (truncated) > title > fallback
281
+ */
282
+ declare const getDisplayTitle: (customTitle: string | undefined, currentSummary: string | undefined, title: string | undefined, maxLength?: number, fallback?: string) => string;
196
283
 
197
284
  declare const findLinkedAgents: (projectName: string, sessionId: string) => Effect.Effect<string[], effect_Cause.UnknownException, never>;
198
285
  declare const findOrphanAgents: (projectName: string) => Effect.Effect<{
@@ -204,6 +291,8 @@ declare const deleteOrphanAgents: (projectName: string) => Effect.Effect<{
204
291
  deletedAgents: string[];
205
292
  count: number;
206
293
  }, effect_Cause.UnknownException, never>;
294
+ declare const loadAgentMessages: (projectName: string, _sessionId: string, // Reserved for future validation
295
+ agentId: string) => Effect.Effect<Message[], effect_Cause.UnknownException, never>;
207
296
 
208
297
  declare const findLinkedTodos: (sessionId: string, agentIds?: string[]) => Effect.Effect<{
209
298
  sessionId: string;
@@ -237,6 +326,15 @@ declare const readSession: (projectName: string, sessionId: string) => Effect.Ef
237
326
  declare const deleteMessage: (projectName: string, sessionId: string, messageUuid: string) => Effect.Effect<{
238
327
  success: boolean;
239
328
  error: string;
329
+ deletedMessage?: undefined;
330
+ } | {
331
+ success: boolean;
332
+ deletedMessage: Record<string, unknown>;
333
+ error?: undefined;
334
+ }, effect_Cause.UnknownException, never>;
335
+ declare const restoreMessage: (projectName: string, sessionId: string, message: Record<string, unknown>, index: number) => Effect.Effect<{
336
+ success: boolean;
337
+ error: string;
240
338
  } | {
241
339
  success: boolean;
242
340
  error?: undefined;
@@ -252,7 +350,7 @@ declare const deleteSession: (projectName: string, sessionId: string) => Effect.
252
350
  deletedAgents: number;
253
351
  deletedTodos: number;
254
352
  }, effect_Cause.UnknownException, never>;
255
- declare const renameSession: (projectName: string, sessionId: string, newTitle: string, newSummary?: string) => Effect.Effect<{
353
+ declare const renameSession: (projectName: string, sessionId: string, newTitle: string) => Effect.Effect<{
256
354
  success: false;
257
355
  error: string;
258
356
  } | {
@@ -325,8 +423,8 @@ declare const loadSessionTreeData: (projectName: string, sessionId: string) => E
325
423
  id: string;
326
424
  projectName: string;
327
425
  title: string;
328
- customTitle: undefined;
329
- lastSummary: string;
426
+ customTitle: string | undefined;
427
+ currentSummary: string;
330
428
  messageCount: number;
331
429
  createdAt: string;
332
430
  updatedAt: string;
@@ -352,8 +450,8 @@ declare const loadProjectTreeData: (projectName: string) => Effect.Effect<{
352
450
  id: string;
353
451
  projectName: string;
354
452
  title: string;
355
- customTitle: undefined;
356
- lastSummary: string;
453
+ customTitle: string | undefined;
454
+ currentSummary: string;
357
455
  messageCount: number;
358
456
  createdAt: string;
359
457
  updatedAt: string;
@@ -409,4 +507,4 @@ declare const getLogger: () => Logger;
409
507
  */
410
508
  declare const createLogger: (namespace: string) => Logger;
411
509
 
412
- export { type AgentInfo, type CleanupPreview, type ClearSessionsResult, type ContentItem, type DeleteSessionResult, type FileChange, type Logger, type Message, type MessagePayload, type MoveSessionResult, type Project, type ProjectTreeData, type RenameSessionResult, type SearchResult, type SessionFilesSummary, type SessionMeta, type SessionTodos, type SessionTreeData, type SplitSessionResult, type SummaryInfo, type TodoItem, clearSessions, createLogger, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, folderNameToDisplayPath, folderNameToPath, getLogger, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadProjectTreeData, loadSessionTreeData, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, searchSessions, sessionHasTodos, setLogger, splitSession, updateSessionSummary };
510
+ export { type AgentInfo, type CleanupPreview, type ClearSessionsResult, type ContentItem, type DeleteSessionResult, type FileChange, type Logger, type Message, type MessagePayload, type MoveSessionResult, type Project, type ProjectTreeData, type RenameSessionResult, type SearchResult, type SessionFilesSummary, type SessionMeta, type SessionTodos, type SessionTreeData, type SplitSessionResult, type SummaryInfo, type TodoItem, clearSessions, createLogger, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, folderNameToDisplayPath, folderNameToPath, getDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, restoreMessage, searchSessions, sessionHasTodos, setLogger, splitSession, updateSessionSummary };
package/dist/index.js CHANGED
@@ -11,8 +11,8 @@ var consoleLogger = {
11
11
  error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args)
12
12
  };
13
13
  var currentLogger = consoleLogger;
14
- var setLogger = (logger) => {
15
- currentLogger = logger;
14
+ var setLogger = (logger2) => {
15
+ currentLogger = logger2;
16
16
  };
17
17
  var getLogger = () => currentLogger;
18
18
  var createLogger = (namespace) => ({
@@ -79,7 +79,7 @@ var pathToFolderName = (absolutePath) => {
79
79
  }
80
80
  return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
81
81
  };
82
- var tryGetCwdFromFile = (filePath, fileSystem = fs, logger = log) => {
82
+ var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
83
83
  const basename2 = path.basename(filePath);
84
84
  try {
85
85
  const content = fileSystem.readFileSync(filePath, "utf-8");
@@ -87,41 +87,39 @@ var tryGetCwdFromFile = (filePath, fileSystem = fs, logger = log) => {
87
87
  if (cwd === null) {
88
88
  const lines = content.split("\n").filter((l) => l.trim());
89
89
  if (lines.length === 0) {
90
- logger.debug(`tryGetCwdFromFile: ${basename2} -> empty file`);
90
+ logger2.debug(`tryGetCwdFromFile: ${basename2} -> empty file`);
91
91
  } else {
92
- logger.debug(`tryGetCwdFromFile: ${basename2} -> no cwd found in ${lines.length} lines`);
92
+ logger2.debug(`tryGetCwdFromFile: ${basename2} -> no cwd found in ${lines.length} lines`);
93
93
  }
94
94
  return null;
95
95
  }
96
- logger.debug(`tryGetCwdFromFile: ${basename2} -> cwd=${cwd}`);
97
96
  return cwd;
98
97
  } catch (e) {
99
- logger.warn(`tryGetCwdFromFile: ${basename2} -> read error: ${e}`);
98
+ logger2.warn(`tryGetCwdFromFile: ${basename2} -> read error: ${e}`);
100
99
  return null;
101
100
  }
102
101
  };
103
- var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs, logger = log) => {
102
+ var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs, logger2 = log) => {
104
103
  const projectDir = path.join(sessionsDir, folderName);
105
104
  try {
106
105
  const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
107
106
  const cwdList = [];
108
107
  for (const f of files) {
109
- const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger);
108
+ const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger2);
110
109
  if (cwd !== null) {
111
110
  cwdList.push(cwd);
112
111
  }
113
112
  }
114
113
  const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName);
115
114
  if (matched) {
116
- logger.debug(`getRealPathFromSession: ${folderName} -> ${matched}`);
117
115
  return matched;
118
116
  }
119
117
  if (cwdList.length > 0) {
120
- logger.warn(
118
+ logger2.warn(
121
119
  `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(", ")}`
122
120
  );
123
121
  } else {
124
- logger.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
122
+ logger2.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
125
123
  }
126
124
  return null;
127
125
  } catch {
@@ -139,13 +137,20 @@ var folderNameToPath = (folderName) => {
139
137
  };
140
138
 
141
139
  // src/utils.ts
140
+ var logger = createLogger("utils");
142
141
  var extractTextContent = (message) => {
143
142
  if (!message) return "";
144
143
  const content = message.content;
145
144
  if (!content) return "";
146
145
  if (typeof content === "string") return content;
147
146
  if (Array.isArray(content)) {
148
- return content.filter((item) => typeof item === "object" && item?.type === "text").map((item) => item.text ?? "").join("");
147
+ return content.filter((item) => typeof item === "object" && item?.type === "text").map((item) => {
148
+ if (item.text == null) {
149
+ logger.warn("TextContent item has undefined or null text property");
150
+ return "";
151
+ }
152
+ return item.text;
153
+ }).join("");
149
154
  }
150
155
  return "";
151
156
  };
@@ -173,6 +178,31 @@ var isContinuationSummary = (msg) => {
173
178
  const text = extractTextContent(msg.message);
174
179
  return text.startsWith("This session is being continued from");
175
180
  };
181
+ var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallback = "Untitled") => {
182
+ if (customTitle) return customTitle;
183
+ if (currentSummary) {
184
+ return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
185
+ }
186
+ if (title && title !== "Untitled") return title;
187
+ return fallback;
188
+ };
189
+ var cleanupSplitFirstMessage = (msg) => {
190
+ const toolUseResult = msg.toolUseResult;
191
+ if (!toolUseResult) return msg;
192
+ const rejectionMarker = "The user provided the following reason for the rejection:";
193
+ const rejectionIndex = toolUseResult.indexOf(rejectionMarker);
194
+ if (rejectionIndex === -1) return msg;
195
+ const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim();
196
+ if (!text) return msg;
197
+ return {
198
+ ...msg,
199
+ message: {
200
+ ...msg.message,
201
+ content: [{ type: "text", text }]
202
+ },
203
+ toolUseResult: void 0
204
+ };
205
+ };
176
206
 
177
207
  // src/agents.ts
178
208
  import { Effect } from "effect";
@@ -240,6 +270,24 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
240
270
  }
241
271
  return { success: true, deletedAgents, count: deletedAgents.length };
242
272
  });
273
+ var loadAgentMessages = (projectName, _sessionId, agentId) => Effect.gen(function* () {
274
+ const projectPath = path2.join(getSessionsDir(), projectName);
275
+ const agentFilePath = path2.join(projectPath, `${agentId}.jsonl`);
276
+ const content = yield* Effect.tryPromise(() => fs2.readFile(agentFilePath, "utf-8"));
277
+ const lines = content.split("\n").filter((line) => line.trim());
278
+ const messages = [];
279
+ for (const line of lines) {
280
+ try {
281
+ const parsed = JSON.parse(line);
282
+ if ("sessionId" in parsed && !("type" in parsed)) {
283
+ continue;
284
+ }
285
+ messages.push(parsed);
286
+ } catch {
287
+ }
288
+ }
289
+ return messages;
290
+ });
243
291
 
244
292
  // src/todos.ts
245
293
  import { Effect as Effect2 } from "effect";
@@ -526,7 +574,7 @@ var deleteMessage = (projectName, sessionId, messageUuid) => Effect3.gen(functio
526
574
  const lines = content.trim().split("\n").filter(Boolean);
527
575
  const messages = lines.map((line) => JSON.parse(line));
528
576
  const targetIndex = messages.findIndex(
529
- (m) => m.uuid === messageUuid || m.messageId === messageUuid
577
+ (m) => m.uuid === messageUuid || m.messageId === messageUuid || m.leafUuid === messageUuid
530
578
  );
531
579
  if (targetIndex === -1) {
532
580
  return { success: false, error: "Message not found" };
@@ -542,6 +590,28 @@ var deleteMessage = (projectName, sessionId, messageUuid) => Effect3.gen(functio
542
590
  messages.splice(targetIndex, 1);
543
591
  const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
544
592
  yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
593
+ return { success: true, deletedMessage: deletedMsg };
594
+ });
595
+ var restoreMessage = (projectName, sessionId, message, index) => Effect3.gen(function* () {
596
+ const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
597
+ const content = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
598
+ const lines = content.trim().split("\n").filter(Boolean);
599
+ const messages = lines.map((line) => JSON.parse(line));
600
+ const msgUuid = message.uuid ?? message.messageId;
601
+ if (!msgUuid) {
602
+ return { success: false, error: "Message has no uuid or messageId" };
603
+ }
604
+ const restoredParentUuid = message.parentUuid;
605
+ for (const msg of messages) {
606
+ if (msg.parentUuid === restoredParentUuid) {
607
+ msg.parentUuid = msgUuid;
608
+ break;
609
+ }
610
+ }
611
+ const insertIndex = Math.min(index, messages.length);
612
+ messages.splice(insertIndex, 0, message);
613
+ const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
614
+ yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
545
615
  return { success: true };
546
616
  });
547
617
  var deleteSession = (projectName, sessionId) => Effect3.gen(function* () {
@@ -583,47 +653,95 @@ var deleteSession = (projectName, sessionId) => Effect3.gen(function* () {
583
653
  deletedTodos: todosResult.deletedCount
584
654
  };
585
655
  });
586
- var renameSession = (projectName, sessionId, newTitle, newSummary) => Effect3.gen(function* () {
587
- const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
656
+ var renameSession = (projectName, sessionId, newTitle) => Effect3.gen(function* () {
657
+ const projectPath = path4.join(getSessionsDir(), projectName);
658
+ const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
588
659
  const content = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
589
660
  const lines = content.trim().split("\n").filter(Boolean);
590
661
  if (lines.length === 0) {
591
662
  return { success: false, error: "Empty session" };
592
663
  }
593
664
  const messages = lines.map((line) => JSON.parse(line));
594
- const firstUserIdx = messages.findIndex((m) => m.type === "user");
595
- if (firstUserIdx === -1) {
596
- return { success: false, error: "No user message found" };
597
- }
598
- const firstMsg = messages[firstUserIdx];
599
- if (firstMsg?.message?.content && Array.isArray(firstMsg.message.content)) {
600
- const textIdx = firstMsg.message.content.findIndex(
601
- (item) => typeof item === "object" && item?.type === "text" && !item.text?.trim().startsWith("<ide_")
602
- );
603
- if (textIdx >= 0) {
604
- const item = firstMsg.message.content[textIdx];
605
- const oldText = item.text ?? "";
606
- const cleanedText = oldText.replace(/^[^\n]+\n\n/, "");
607
- item.text = `${newTitle}
608
-
609
- ${cleanedText}`;
665
+ const sessionUuids = /* @__PURE__ */ new Set();
666
+ for (const msg of messages) {
667
+ if (msg.uuid && typeof msg.uuid === "string") {
668
+ sessionUuids.add(msg.uuid);
610
669
  }
611
670
  }
612
- if (newSummary !== void 0) {
613
- const summaryIdx = messages.findIndex((m) => m.type === "summary");
614
- if (summaryIdx >= 0) {
615
- messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary };
616
- } else {
617
- const summaryMsg = {
618
- type: "summary",
619
- summary: newSummary,
620
- leafUuid: firstMsg.uuid
621
- };
622
- messages.unshift(summaryMsg);
623
- }
671
+ const customTitleIdx = messages.findIndex((m) => m.type === "custom-title");
672
+ const customTitleRecord = {
673
+ type: "custom-title",
674
+ customTitle: newTitle,
675
+ sessionId
676
+ };
677
+ if (customTitleIdx >= 0) {
678
+ messages[customTitleIdx] = customTitleRecord;
679
+ } else {
680
+ messages.unshift(customTitleRecord);
624
681
  }
625
682
  const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
626
683
  yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
684
+ const projectFiles = yield* Effect3.tryPromise(() => fs4.readdir(projectPath));
685
+ const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
686
+ const summariesTargetingThis = [];
687
+ for (const file of allJsonlFiles) {
688
+ const otherFilePath = path4.join(projectPath, file);
689
+ try {
690
+ const otherContent = yield* Effect3.tryPromise(() => fs4.readFile(otherFilePath, "utf-8"));
691
+ const otherLines = otherContent.trim().split("\n").filter(Boolean);
692
+ const otherMessages = otherLines.map((l) => JSON.parse(l));
693
+ for (let i = 0; i < otherMessages.length; i++) {
694
+ const msg = otherMessages[i];
695
+ if (msg.type === "summary" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
696
+ const targetMsg = messages.find((m) => m.uuid === msg.leafUuid);
697
+ summariesTargetingThis.push({
698
+ file,
699
+ idx: i,
700
+ timestamp: targetMsg?.timestamp ?? msg.timestamp
701
+ });
702
+ }
703
+ }
704
+ } catch {
705
+ }
706
+ }
707
+ if (summariesTargetingThis.length > 0) {
708
+ summariesTargetingThis.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
709
+ const firstSummary = summariesTargetingThis[0];
710
+ const summaryFilePath = path4.join(projectPath, firstSummary.file);
711
+ const summaryContent = yield* Effect3.tryPromise(() => fs4.readFile(summaryFilePath, "utf-8"));
712
+ const summaryLines = summaryContent.trim().split("\n").filter(Boolean);
713
+ const summaryMessages = summaryLines.map((l) => JSON.parse(l));
714
+ summaryMessages[firstSummary.idx] = {
715
+ ...summaryMessages[firstSummary.idx],
716
+ summary: newTitle
717
+ };
718
+ const newSummaryContent = summaryMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
719
+ yield* Effect3.tryPromise(() => fs4.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
720
+ } else {
721
+ const currentContent = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
722
+ const currentLines = currentContent.trim().split("\n").filter(Boolean);
723
+ const currentMessages = currentLines.map((l) => JSON.parse(l));
724
+ const firstUserIdx = currentMessages.findIndex((m) => m.type === "user");
725
+ if (firstUserIdx >= 0) {
726
+ const firstMsg = currentMessages[firstUserIdx];
727
+ const msgPayload = firstMsg.message;
728
+ if (msgPayload?.content && Array.isArray(msgPayload.content)) {
729
+ const textIdx = msgPayload.content.findIndex(
730
+ (item) => typeof item === "object" && item?.type === "text" && !item.text?.trim().startsWith("<ide_")
731
+ );
732
+ if (textIdx >= 0) {
733
+ const item = msgPayload.content[textIdx];
734
+ const oldText = item.text ?? "";
735
+ const cleanedText = oldText.replace(/^[^\n]+\n\n/, "");
736
+ item.text = `${newTitle}
737
+
738
+ ${cleanedText}`;
739
+ const updatedContent = currentMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
740
+ yield* Effect3.tryPromise(() => fs4.writeFile(filePath, updatedContent, "utf-8"));
741
+ }
742
+ }
743
+ }
744
+ }
627
745
  return { success: true };
628
746
  });
629
747
  var getSessionFiles = (projectName, sessionId) => Effect3.gen(function* () {
@@ -743,9 +861,10 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect3.gen(f
743
861
  remainingMessages = allMessages.slice(0, splitIndex);
744
862
  }
745
863
  const updatedMovedMessages = movedMessages.map((msg, index) => {
746
- const updated = { ...msg, sessionId: newSessionId };
864
+ let updated = { ...msg, sessionId: newSessionId };
747
865
  if (index === 0) {
748
866
  updated.parentUuid = null;
867
+ updated = cleanupSplitFirstMessage(updated);
749
868
  }
750
869
  return updated;
751
870
  });
@@ -1023,7 +1142,9 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
1023
1142
  const messages = lines.map((line) => JSON.parse(line));
1024
1143
  let summaries;
1025
1144
  if (summariesByTargetSession) {
1026
- summaries = [...summariesByTargetSession.get(sessionId) ?? []];
1145
+ summaries = [...summariesByTargetSession.get(sessionId) ?? []].sort(
1146
+ (a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? "")
1147
+ );
1027
1148
  } else {
1028
1149
  summaries = [];
1029
1150
  const sessionUuids = /* @__PURE__ */ new Set();
@@ -1057,7 +1178,7 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
1057
1178
  }
1058
1179
  }
1059
1180
  }
1060
- summaries.reverse();
1181
+ summaries.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
1061
1182
  let lastCompactBoundaryUuid;
1062
1183
  for (let i = messages.length - 1; i >= 0; i--) {
1063
1184
  const msg = messages[i];
@@ -1066,9 +1187,10 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
1066
1187
  break;
1067
1188
  }
1068
1189
  }
1069
- const customTitle = void 0;
1070
- const firstUserMsgForTitle = messages.find((m) => m.type === "user");
1071
- const title = firstUserMsgForTitle ? extractTitle(extractTextContent(firstUserMsgForTitle.message)) : summaries.length > 0 ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`;
1190
+ const firstUserMsg = messages.find((m) => m.type === "user");
1191
+ const customTitleMsg = messages.find((m) => m.type === "custom-title");
1192
+ const customTitle = customTitleMsg?.customTitle;
1193
+ const title = firstUserMsg ? extractTitle(extractTextContent(firstUserMsg.message)) : summaries.length > 0 ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`;
1072
1194
  const userAssistantMessages = messages.filter(
1073
1195
  (m) => m.type === "user" || m.type === "assistant"
1074
1196
  );
@@ -1111,7 +1233,7 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
1111
1233
  projectName,
1112
1234
  title,
1113
1235
  customTitle,
1114
- lastSummary: summaries[0]?.summary,
1236
+ currentSummary: summaries[0]?.summary,
1115
1237
  messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : summaries.length > 0 ? 1 : 0,
1116
1238
  createdAt: firstMessage?.timestamp ?? void 0,
1117
1239
  updatedAt: lastMessage?.timestamp ?? void 0,
@@ -1247,6 +1369,7 @@ export {
1247
1369
  findOrphanTodos,
1248
1370
  folderNameToDisplayPath,
1249
1371
  folderNameToPath,
1372
+ getDisplayTitle,
1250
1373
  getLogger,
1251
1374
  getRealPathFromSession,
1252
1375
  getSessionFiles,
@@ -1256,6 +1379,7 @@ export {
1256
1379
  isInvalidApiKeyMessage,
1257
1380
  listProjects,
1258
1381
  listSessions,
1382
+ loadAgentMessages,
1259
1383
  loadProjectTreeData,
1260
1384
  loadSessionTreeData,
1261
1385
  moveSession,
@@ -1263,6 +1387,7 @@ export {
1263
1387
  previewCleanup,
1264
1388
  readSession,
1265
1389
  renameSession,
1390
+ restoreMessage,
1266
1391
  searchSessions,
1267
1392
  sessionHasTodos,
1268
1393
  setLogger,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/paths.ts","../src/logger.ts","../src/utils.ts","../src/agents.ts","../src/todos.ts","../src/session.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n *\n * Architecture:\n * - Pure Functions: extractCwdFromContent, isSessionFile, toRelativePath (no I/O)\n * - I/O Functions: tryGetCwdFromFile, getRealPathFromSession (with optional DI for testing)\n */\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\n\nconst log = createLogger('paths')\n\n// ============================================\n// Types (for dependency injection)\n// ============================================\n\nexport interface Logger {\n debug: (msg: string) => void\n warn: (msg: string) => void\n}\n\nexport interface FileSystem {\n readFileSync: (path: string, encoding: 'utf-8') => string\n readdirSync: (path: string) => string[]\n}\n\n// ============================================\n// Directory Paths\n// ============================================\n\n/** Get Claude sessions directory (~/.claude/projects) */\nexport const getSessionsDir = (): string => path.join(os.homedir(), '.claude', 'projects')\n\n/** Get Claude todos directory (~/.claude/todos) */\nexport const getTodosDir = (): string => path.join(os.homedir(), '.claude', 'todos')\n\n// ============================================\n// Pure Functions (No I/O)\n// ============================================\n\n/** Extract cwd from file content - pure function for easy testing */\nexport const extractCwdFromContent = (content: string): string | null => {\n const lines = content.split('\\n').filter((l) => l.trim())\n\n for (const line of lines) {\n try {\n const parsed = JSON.parse(line)\n if (parsed?.cwd) {\n return parsed.cwd\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n\n return null\n}\n\n/** Check if filename is a session file */\nexport const isSessionFile = (filename: string): boolean =>\n filename.endsWith('.jsonl') && !filename.startsWith('agent-')\n\n/** Convert path to relative form if under home directory */\nexport const toRelativePath = (absolutePath: string, homeDir: string): string => {\n const normalizedPath = absolutePath.replace(/\\\\/g, '/')\n const normalizedHome = homeDir.replace(/\\\\/g, '/')\n\n // Check for exact match or path with separator after home dir\n if (normalizedPath === normalizedHome) {\n return '~'\n }\n if (normalizedPath.startsWith(normalizedHome + '/')) {\n return '~' + normalizedPath.slice(normalizedHome.length)\n }\n return absolutePath\n}\n\n// ============================================\n// Path Conversion (Pure)\n// ============================================\n\n/**\n * Convert project folder name to display path\n * Unix: -home-user-projects -> /home/user/projects\n * Windows: C--Users-david -> C:\\Users\\david\n * Handle dot-prefixed folders: --claude -> /.claude\n */\nexport const folderNameToDisplayPath = (folderName: string): string => {\n // Check if Windows path (starts with drive letter pattern like \"C--\")\n const windowsDriveMatch = folderName.match(/^([A-Za-z])--/)\n if (windowsDriveMatch) {\n const driveLetter = windowsDriveMatch[1]\n const rest = folderName.slice(3)\n return driveLetter + ':\\\\' + rest.replace(/--/g, '\\\\.').replace(/-/g, '\\\\')\n }\n\n // Unix path\n return folderName.replace(/^-/, '/').replace(/--/g, '/.').replace(/-/g, '/')\n}\n\n/** Convert display path to folder name (reverse of above) */\nexport const displayPathToFolderName = (displayPath: string): string => {\n const windowsDriveMatch = displayPath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n const driveLetter = windowsDriveMatch[1]\n const rest = displayPath.slice(3)\n return driveLetter + '--' + rest.replace(/[/\\\\]\\./g, '--').replace(/[/\\\\]/g, '-')\n }\n\n return displayPath.replace(/^\\//g, '-').replace(/\\/\\./g, '--').replace(/\\//g, '-')\n}\n\n/**\n * Convert absolute path to project folder name\n * Non-ASCII characters are converted to '-' per character\n * Windows drive letter is normalized to lowercase (C: -> c--)\n */\nexport const pathToFolderName = (absolutePath: string): string => {\n const convertNonAscii = (str: string): string =>\n [...str].map((char) => (char.charCodeAt(0) <= 127 ? char : '-')).join('')\n\n const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n // Normalize drive letter to lowercase (Claude Code uses lowercase)\n const driveLetter = windowsDriveMatch[1].toLowerCase()\n const rest = absolutePath.slice(3)\n return (\n driveLetter +\n '--' +\n convertNonAscii(rest)\n .replace(/[/\\\\]\\./g, '--')\n .replace(/[/\\\\]/g, '-')\n .replace(/\\./g, '-')\n )\n }\n\n return convertNonAscii(absolutePath)\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--')\n .replace(/\\//g, '-')\n .replace(/\\./g, '-')\n}\n\n// ============================================\n// I/O Functions (with optional DI for testing)\n// ============================================\n\n/**\n * Try to extract cwd from a single session file\n * @param filePath - Path to session file\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const tryGetCwdFromFile = (\n filePath: string,\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const basename = path.basename(filePath)\n\n try {\n const content = fileSystem.readFileSync(filePath, 'utf-8')\n const cwd = extractCwdFromContent(content)\n\n if (cwd === null) {\n const lines = content.split('\\n').filter((l) => l.trim())\n if (lines.length === 0) {\n logger.debug(`tryGetCwdFromFile: ${basename} -> empty file`)\n } else {\n logger.debug(`tryGetCwdFromFile: ${basename} -> no cwd found in ${lines.length} lines`)\n }\n return null\n }\n\n logger.debug(`tryGetCwdFromFile: ${basename} -> cwd=${cwd}`)\n return cwd\n } catch (e) {\n logger.warn(`tryGetCwdFromFile: ${basename} -> read error: ${e}`)\n return null\n }\n}\n\n/**\n * Extract real cwd path from session files in a project\n * @param folderName - Project folder name\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const getRealPathFromSession = (\n folderName: string,\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const projectDir = path.join(sessionsDir, folderName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n const cwdList: string[] = []\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd !== null) {\n cwdList.push(cwd)\n }\n }\n\n // Find cwd that matches folder name\n const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName)\n if (matched) {\n logger.debug(`getRealPathFromSession: ${folderName} -> ${matched}`)\n return matched\n }\n\n // Log for debugging\n if (cwdList.length > 0) {\n logger.warn(\n `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(', ')}`\n )\n } else {\n logger.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`)\n }\n return null\n } catch {\n return null\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Convert folder name to relative or absolute path for display\n * If path is under home directory, show relative (~/...)\n */\nexport const folderNameToPath = (folderName: string): string => {\n const homeDir = os.homedir()\n\n // First try to get real path from session cwd\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n return toRelativePath(realPath, homeDir)\n }\n\n // Fallback to pattern-based conversion\n const absolutePath = folderNameToDisplayPath(folderName)\n return toRelativePath(absolutePath, homeDir)\n}\n","/**\n * Simple logger abstraction for Claude Sessions\n * Consumers can provide their own logger implementation\n */\n\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void\n info: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\n// Default console logger\nconst consoleLogger: Logger = {\n debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),\n info: (msg, ...args) => console.info(`[INFO] ${msg}`, ...args),\n warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),\n}\n\n// Global logger instance\nlet currentLogger: Logger = consoleLogger\n\n/**\n * Set custom logger implementation\n * @example\n * // VSCode extension\n * setLogger({\n * debug: (msg) => outputChannel.appendLine(`[DEBUG] ${msg}`),\n * info: (msg) => outputChannel.appendLine(`[INFO] ${msg}`),\n * warn: (msg) => outputChannel.appendLine(`[WARN] ${msg}`),\n * error: (msg) => outputChannel.appendLine(`[ERROR] ${msg}`),\n * })\n */\nexport const setLogger = (logger: Logger): void => {\n currentLogger = logger\n}\n\n/**\n * Get current logger instance\n */\nexport const getLogger = (): Logger => currentLogger\n\n/**\n * Create a namespaced logger\n * @example\n * const log = createLogger('paths')\n * log.debug('Converting folder name') // [DEBUG] [paths] Converting folder name\n */\nexport const createLogger = (namespace: string): Logger => ({\n debug: (msg, ...args) => currentLogger.debug(`[${namespace}] ${msg}`, ...args),\n info: (msg, ...args) => currentLogger.info(`[${namespace}] ${msg}`, ...args),\n warn: (msg, ...args) => currentLogger.warn(`[${namespace}] ${msg}`, ...args),\n error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args),\n})\n","/**\n * Utility functions for message processing\n */\nimport type { ContentItem, Message, MessagePayload } from './types.js'\n\n// Extract text content from message payload\nexport const extractTextContent = (message: MessagePayload | undefined): string => {\n if (!message) return ''\n\n const content = message.content\n if (!content) return ''\n\n // If content is string, return directly\n if (typeof content === 'string') return content\n\n // If content is array, extract text items\n if (Array.isArray(content)) {\n return content\n .filter((item): item is ContentItem => typeof item === 'object' && item?.type === 'text')\n .map((item) => item.text ?? '')\n .join('')\n }\n\n return ''\n}\n\n// Extract title from text content (remove IDE tags, use first line)\nexport const extractTitle = (text: string): string => {\n if (!text) return 'Untitled'\n\n // Remove IDE tags (<ide_opened_file>, <ide_selection>, etc.)\n let cleaned = text.replace(/<ide_[^>]*>[\\s\\S]*?<\\/ide_[^>]*>/g, '').trim()\n\n if (!cleaned) return 'Untitled'\n\n // Use only content before \\n\\n or \\n as title\n if (cleaned.includes('\\n\\n')) {\n cleaned = cleaned.split('\\n\\n')[0]\n } else if (cleaned.includes('\\n')) {\n cleaned = cleaned.split('\\n')[0]\n }\n\n // Limit to 100 characters\n if (cleaned.length > 100) {\n return cleaned.slice(0, 100) + '...'\n }\n\n return cleaned || 'Untitled'\n}\n\n// Check if message contains \"Invalid API key\"\nexport const isInvalidApiKeyMessage = (msg: Message): boolean => {\n const text = extractTextContent(msg.message)\n return text.includes('Invalid API key')\n}\n\n// Check if a message is a continuation summary (from compact)\nexport const isContinuationSummary = (msg: Record<string, unknown>): boolean => {\n // isCompactSummary flag is set by Claude Code for continuation summaries\n if (msg.isCompactSummary === true) return true\n\n // Fallback: check message content\n if (msg.type !== 'user') return false\n const text = extractTextContent(msg.message as MessagePayload | undefined)\n return text.startsWith('This session is being continued from')\n}\n","/**\n * Agent file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from './paths.js'\n\n// Find agent files linked to a session\nexport const findLinkedAgents = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n const linkedAgents: string[] = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId === sessionId) {\n linkedAgents.push(agentFile.replace('.jsonl', ''))\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return linkedAgents\n })\n\n// Find orphan agent files (agents whose parent session no longer exists)\nexport const findOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n\n const sessionIds = new Set(\n files\n .filter((f) => !f.startsWith('agent-') && f.endsWith('.jsonl'))\n .map((f) => f.replace('.jsonl', ''))\n )\n\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n const orphanAgents: Array<{ agentId: string; sessionId: string }> = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {\n orphanAgents.push({\n agentId: agentFile.replace('.jsonl', ''),\n sessionId: parsed.sessionId,\n })\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return orphanAgents\n })\n\n// Delete orphan agent files (move to .bak)\nexport const deleteOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const orphans = yield* findOrphanAgents(projectName)\n\n // Create backup directory\n const backupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n const deletedAgents: string[] = []\n\n for (const orphan of orphans) {\n const agentPath = path.join(projectPath, `${orphan.agentId}.jsonl`)\n const agentBackupPath = path.join(backupDir, `${orphan.agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath))\n deletedAgents.push(orphan.agentId)\n }\n\n return { success: true, deletedAgents, count: deletedAgents.length }\n })\n","/**\n * Todo file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, getTodosDir } from './paths.js'\nimport type { TodoItem, SessionTodos } from './types.js'\n\n// Find linked todo files for a session and its agents\n// Scans todos directory for files matching session pattern\nexport const findLinkedTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return {\n sessionId,\n sessionTodos: [],\n agentTodos: [],\n hasTodos: false,\n } satisfies SessionTodos\n }\n\n // Read session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n let sessionTodos: TodoItem[] = []\n\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n sessionTodos = JSON.parse(content) as TodoItem[]\n } catch {\n // Invalid JSON, treat as empty\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Read agent todo files\n const agentTodos: { agentId: string; todos: TodoItem[] }[] = []\n\n for (const agentId of discoveredAgentIds) {\n // Agent todo files are named: {sessionId}-agent-{shortAgentId}.json\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) {\n agentTodos.push({ agentId, todos })\n }\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n\n const hasTodos = sessionTodos.length > 0 || agentTodos.some((at) => at.todos.length > 0)\n\n return {\n sessionId,\n sessionTodos,\n agentTodos,\n hasTodos,\n } satisfies SessionTodos\n })\n\n// Check if session has any todos (quick check)\n// Scans todos directory for files matching session pattern\nexport const sessionHasTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return false\n\n // Check session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Check agent todo files\n for (const agentId of discoveredAgentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n }\n\n return false\n })\n\n// Delete linked todo files for a session (move to .bak)\nexport const deleteLinkedTodos = (sessionId: string, agentIds: string[]) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return { deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n // Delete session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}.json`)\n yield* Effect.tryPromise(() => fs.rename(sessionTodoPath, backupPath))\n deletedCount++\n }\n\n // Delete agent todo files\n for (const agentId of agentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`)\n yield* Effect.tryPromise(() => fs.rename(agentTodoPath, backupPath))\n deletedCount++\n }\n }\n\n return { deletedCount }\n })\n\n// Find all orphan todo files (session no longer exists)\nexport const findOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const sessionsDir = getSessionsDir()\n\n // Check if directories exist\n const [todosExists, sessionsExists] = yield* Effect.all([\n Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n ),\n Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n ),\n ])\n\n if (!todosExists || !sessionsExists) return []\n\n // Get all todo files\n const todoFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const jsonFiles = todoFiles.filter((f) => f.endsWith('.json'))\n\n // Build set of all valid session IDs across all projects\n const validSessionIds = new Set<string>()\n const projectEntries = yield* Effect.tryPromise(() =>\n fs.readdir(sessionsDir, { withFileTypes: true })\n )\n\n for (const entry of projectEntries) {\n if (!entry.isDirectory() || entry.name.startsWith('.')) continue\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n for (const f of files) {\n if (f.endsWith('.jsonl') && !f.startsWith('agent-')) {\n validSessionIds.add(f.replace('.jsonl', ''))\n }\n }\n }\n\n // Find orphan todo files\n const orphans: string[] = []\n for (const todoFile of jsonFiles) {\n // Parse session ID from todo filename\n // Format: {sessionId}.json or {sessionId}-agent-{agentId}.json\n const match = todoFile.match(/^([a-f0-9-]+)(?:-agent-[a-f0-9]+)?\\.json$/)\n if (match) {\n const sessionId = match[1]\n if (!validSessionIds.has(sessionId)) {\n orphans.push(todoFile)\n }\n }\n }\n\n return orphans\n })\n\n// Delete orphan todo files\nexport const deleteOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const orphans = yield* findOrphanTodos()\n\n if (orphans.length === 0) return { success: true, deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n for (const orphan of orphans) {\n const filePath = path.join(todosDir, orphan)\n const backupPath = path.join(backupDir, orphan)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n deletedCount++\n }\n\n return { success: true, deletedCount }\n })\n","/**\n * Session management operations\n */\nimport { Effect, pipe, Array as A, Option as O } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from './paths.js'\nimport {\n extractTextContent,\n extractTitle,\n isInvalidApiKeyMessage,\n isContinuationSummary,\n} from './utils.js'\nimport { findLinkedAgents, findOrphanAgents, deleteOrphanAgents } from './agents.js'\nimport {\n findLinkedTodos,\n deleteLinkedTodos,\n sessionHasTodos,\n findOrphanTodos,\n deleteOrphanTodos,\n} from './todos.js'\nimport type {\n Message,\n SessionMeta,\n Project,\n FileChange,\n SessionFilesSummary,\n DeleteSessionResult,\n RenameSessionResult,\n SplitSessionResult,\n MoveSessionResult,\n ClearSessionsResult,\n CleanupPreview,\n ContentItem,\n SearchResult,\n SessionTreeData,\n SummaryInfo,\n AgentInfo,\n ProjectTreeData,\n} from './types.js'\n\n// List all project directories\nexport const listProjects = Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return [] as Project[]\n }\n\n const entries = yield* Effect.tryPromise(() => fs.readdir(sessionsDir, { withFileTypes: true }))\n\n const projects = yield* Effect.all(\n entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((entry) =>\n Effect.gen(function* () {\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n return {\n name: entry.name,\n displayName: folderNameToPath(entry.name),\n path: projectPath,\n sessionCount: sessionFiles.length,\n } satisfies Project\n })\n ),\n { concurrency: 10 }\n )\n\n return projects\n})\n\n// List sessions in a project\nexport const listSessions = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n const sessions = yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Message)\n\n const sessionId = file.replace('.jsonl', '')\n\n // Filter only user/assistant messages for counting\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Check if session has summary (for preserved sessions without user/assistant messages)\n const hasSummary = messages.some((m) => m.type === 'summary')\n\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Extract title from first user message\n const title = pipe(\n messages,\n A.findFirst((m) => m.type === 'user'),\n O.map((m) => {\n const text = extractTextContent(m.message)\n return extractTitle(text)\n }),\n O.getOrElse(() => (hasSummary ? '[Summary Only]' : `Session ${sessionId.slice(0, 8)}`))\n )\n\n return {\n id: sessionId,\n projectName,\n title,\n // If session has summary but no user/assistant messages, count as 1\n messageCount:\n userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,\n createdAt: firstMessage?.timestamp,\n updatedAt: lastMessage?.timestamp,\n } satisfies SessionMeta\n })\n ),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n return sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Read session messages\nexport const readSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n return lines.map((line) => JSON.parse(line) as Message)\n })\n\n// Delete a message from session and repair parentUuid chain\nexport const deleteMessage = (projectName: string, sessionId: string, messageUuid: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find by uuid or messageId (for file-history-snapshot type)\n const targetIndex = messages.findIndex(\n (m) => m.uuid === messageUuid || m.messageId === messageUuid\n )\n if (targetIndex === -1) {\n return { success: false, error: 'Message not found' }\n }\n\n // Get the deleted message's uuid and parentUuid\n const deletedMsg = messages[targetIndex]\n const deletedUuid = deletedMsg?.uuid ?? deletedMsg?.messageId\n const parentUuid = deletedMsg?.parentUuid\n\n // Find all messages that reference the deleted message as their parent\n // and update them to point to the deleted message's parent\n for (const msg of messages) {\n if (msg.parentUuid === deletedUuid) {\n msg.parentUuid = parentUuid\n }\n }\n\n // Remove the message\n messages.splice(targetIndex, 1)\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// Delete a session and its linked agent/todo files\nexport const deleteSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectPath = path.join(sessionsDir, projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Find linked agents first (before any deletion)\n const linkedAgents = yield* findLinkedAgents(projectName, sessionId)\n\n // Check file size - if empty (0 bytes), just delete without backup\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n if (stat.size === 0) {\n yield* Effect.tryPromise(() => fs.unlink(filePath))\n // Still delete linked agents and todos for empty sessions\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n yield* deleteLinkedTodos(sessionId, linkedAgents)\n return { success: true, deletedAgents: linkedAgents.length } satisfies DeleteSessionResult\n }\n\n // Create backup directory\n const backupDir = path.join(sessionsDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n // Delete linked agent files (move to .bak in project folder)\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n\n // Delete linked todo files\n const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents)\n\n // Move session to backup (format: project_name_session_id.jsonl)\n const backupPath = path.join(backupDir, `${projectName}_${sessionId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n\n return {\n success: true,\n backupPath,\n deletedAgents: linkedAgents.length,\n deletedTodos: todosResult.deletedCount,\n } satisfies DeleteSessionResult\n })\n\n// Rename session by adding title prefix and optionally updating summary\nexport const renameSession = (\n projectName: string,\n sessionId: string,\n newTitle: string,\n newSummary?: string\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) {\n return { success: false, error: 'Empty session' } satisfies RenameSessionResult\n }\n\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find first user message\n const firstUserIdx = messages.findIndex((m) => m.type === 'user')\n if (firstUserIdx === -1) {\n return { success: false, error: 'No user message found' } satisfies RenameSessionResult\n }\n\n const firstMsg = messages[firstUserIdx] as unknown as Message\n if (firstMsg?.message?.content && Array.isArray(firstMsg.message.content)) {\n // Find first non-IDE text content\n const textIdx = firstMsg.message.content.findIndex(\n (item): item is ContentItem =>\n typeof item === 'object' &&\n item?.type === 'text' &&\n !item.text?.trim().startsWith('<ide_')\n )\n\n if (textIdx >= 0) {\n const item = firstMsg.message.content[textIdx] as ContentItem\n const oldText = item.text ?? ''\n // Remove existing title pattern (first line ending with \\n\\n)\n const cleanedText = oldText.replace(/^[^\\n]+\\n\\n/, '')\n item.text = `${newTitle}\\n\\n${cleanedText}`\n }\n }\n\n // Update or add summary message if newSummary is provided\n if (newSummary !== undefined) {\n const summaryIdx = messages.findIndex((m) => m.type === 'summary')\n if (summaryIdx >= 0) {\n // Update existing summary\n messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary }\n } else {\n // Add new summary message at the beginning\n const summaryMsg = {\n type: 'summary',\n summary: newSummary,\n leafUuid: firstMsg.uuid,\n }\n messages.unshift(summaryMsg)\n }\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true } satisfies RenameSessionResult\n })\n\n// Get files changed in a session (from file-history-snapshot and tool_use)\nexport const getSessionFiles = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n const fileChanges: FileChange[] = []\n const seenFiles = new Set<string>()\n\n for (const msg of messages) {\n // Check file-history-snapshot type\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n type: string\n messageId?: string\n snapshot?: {\n trackedFileBackups?: Record<string, unknown>\n timestamp?: string\n }\n }\n const backups = snapshot.snapshot?.trackedFileBackups\n if (backups && typeof backups === 'object') {\n for (const filePath of Object.keys(backups)) {\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: 'modified',\n timestamp: snapshot.snapshot?.timestamp,\n messageUuid: snapshot.messageId ?? msg.uuid,\n })\n }\n }\n }\n }\n\n // Check tool_use for Write/Edit operations\n if (msg.type === 'assistant' && msg.message?.content) {\n const content = msg.message.content\n if (Array.isArray(content)) {\n for (const item of content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string; input?: { file_path?: string } }\n if (\n (toolUse.name === 'Write' || toolUse.name === 'Edit') &&\n toolUse.input?.file_path\n ) {\n const filePath = toolUse.input.file_path\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: toolUse.name === 'Write' ? 'created' : 'modified',\n timestamp: msg.timestamp,\n messageUuid: msg.uuid,\n })\n }\n }\n }\n }\n }\n }\n }\n\n return {\n sessionId,\n projectName,\n files: fileChanges,\n totalChanges: fileChanges.length,\n } satisfies SessionFilesSummary\n })\n\n// Move session to another project\nexport const moveSession = (\n sourceProject: string,\n sessionId: string,\n targetProject: string\n): Effect.Effect<MoveSessionResult, Error> =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const sourcePath = path.join(sessionsDir, sourceProject)\n const targetPath = path.join(sessionsDir, targetProject)\n\n const sourceFile = path.join(sourcePath, `${sessionId}.jsonl`)\n const targetFile = path.join(targetPath, `${sessionId}.jsonl`)\n\n // Check source file exists\n const sourceExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!sourceExists) {\n return { success: false, error: 'Source session not found' }\n }\n\n // Check target file does not exist\n const targetExists = yield* Effect.tryPromise(() =>\n fs\n .access(targetFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (targetExists) {\n return { success: false, error: 'Session already exists in target project' }\n }\n\n // Create target directory if needed\n yield* Effect.tryPromise(() => fs.mkdir(targetPath, { recursive: true }))\n\n // Find linked agents before moving\n const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId)\n\n // Move session file\n yield* Effect.tryPromise(() => fs.rename(sourceFile, targetFile))\n\n // Move linked agent files\n for (const agentId of linkedAgents) {\n const sourceAgentFile = path.join(sourcePath, `${agentId}.jsonl`)\n const targetAgentFile = path.join(targetPath, `${agentId}.jsonl`)\n\n const agentExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceAgentFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentExists) {\n yield* Effect.tryPromise(() => fs.rename(sourceAgentFile, targetAgentFile))\n }\n }\n\n return { success: true }\n })\n\n// Split session at a specific message\nexport const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n // Parse all messages preserving their full structure\n const allMessages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find the split point\n const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid)\n if (splitIndex === -1) {\n return { success: false, error: 'Message not found' } satisfies SplitSessionResult\n }\n\n if (splitIndex === 0) {\n return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult\n }\n\n // Generate new session ID\n const newSessionId = crypto.randomUUID()\n\n // Find all summary messages and get the last (most recent) one\n // Summaries are typically at the beginning, but we want the most recent one\n const summaryMessages = allMessages.filter((m) => m.type === 'summary')\n const summaryMessage =\n summaryMessages.length > 0 ? summaryMessages[summaryMessages.length - 1] : null\n\n // Check if the split message is a continuation summary\n const splitMessage = allMessages[splitIndex]\n const shouldDuplicate = isContinuationSummary(splitMessage)\n\n // Split messages - if continuation summary, include it in both sessions\n let remainingMessages: Record<string, unknown>[]\n const movedMessages = allMessages.slice(splitIndex)\n\n if (shouldDuplicate) {\n // Create a copy of the continuation message with new UUID for the original session\n const duplicatedMessage: Record<string, unknown> = {\n ...splitMessage,\n uuid: crypto.randomUUID(),\n sessionId: sessionId, // Keep original session ID\n }\n remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage]\n } else {\n remainingMessages = allMessages.slice(0, splitIndex)\n }\n\n // Update moved messages with new sessionId and fix first message's parentUuid\n const updatedMovedMessages = movedMessages.map((msg, index) => {\n const updated: Record<string, unknown> = { ...msg, sessionId: newSessionId }\n if (index === 0) {\n // First message of new session should have no parent\n updated.parentUuid = null\n }\n return updated\n })\n\n // Clone summary message to new session if exists\n if (summaryMessage) {\n const clonedSummary: Record<string, unknown> = {\n ...summaryMessage,\n leafUuid: updatedMovedMessages[0]?.uuid ?? null,\n }\n updatedMovedMessages.unshift(clonedSummary)\n }\n\n // Write remaining messages to original file\n const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, remainingContent, 'utf-8'))\n\n // Write moved messages to new session file\n const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`)\n const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8'))\n\n // Update linked agent files that reference the old sessionId\n const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n for (const agentFile of agentJsonlFiles) {\n const agentPath = path.join(projectPath, agentFile)\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n\n if (agentLines.length === 0) continue\n\n const firstAgentMsg = JSON.parse(agentLines[0]) as { sessionId?: string }\n\n // If this agent belongs to the original session, check if it should be moved\n if (firstAgentMsg.sessionId === sessionId) {\n // Check if any message in moved messages is related to this agent\n const agentId = agentFile.replace('agent-', '').replace('.jsonl', '')\n const isRelatedToMoved = movedMessages.some(\n (msg) => (msg as { agentId?: string }).agentId === agentId\n )\n\n if (isRelatedToMoved) {\n // Update all messages in this agent file to reference new sessionId\n const updatedAgentMessages = agentLines.map((line) => {\n const msg = JSON.parse(line) as Record<string, unknown>\n return JSON.stringify({ ...msg, sessionId: newSessionId })\n })\n const updatedAgentContent = updatedAgentMessages.join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8'))\n }\n }\n }\n\n return {\n success: true,\n newSessionId,\n newSessionPath: newFilePath,\n movedMessageCount: movedMessages.length,\n duplicatedSummary: shouldDuplicate,\n } satisfies SplitSessionResult\n })\n\n// Remove invalid API key messages from a session, returns remaining message count\nconst cleanInvalidMessages = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) return { removedCount: 0, remainingCount: 0 }\n\n const messages = lines.map((line) => JSON.parse(line) as Message)\n const invalidIndices: number[] = []\n\n // Find all invalid API key messages\n messages.forEach((msg, idx) => {\n if (isInvalidApiKeyMessage(msg)) {\n invalidIndices.push(idx)\n }\n })\n\n if (invalidIndices.length === 0) {\n const userAssistantCount = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = messages.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = userAssistantCount > 0 ? userAssistantCount : hasSummary ? 1 : 0\n return { removedCount: 0, remainingCount }\n }\n\n // Remove invalid messages and fix parentUuid chain\n const filtered: Message[] = []\n let lastValidUuid: string | null = null\n\n for (let i = 0; i < messages.length; i++) {\n if (invalidIndices.includes(i)) {\n continue // Skip invalid message\n }\n\n const msg = messages[i]\n // Update parentUuid to point to last valid message\n if (msg.parentUuid && invalidIndices.some((idx) => messages[idx]?.uuid === msg.parentUuid)) {\n msg.parentUuid = lastValidUuid\n }\n filtered.push(msg)\n lastValidUuid = msg.uuid\n }\n\n const newContent =\n filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join('\\n') + '\\n' : ''\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n const remainingUserAssistant = filtered.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = filtered.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0\n return { removedCount: invalidIndices.length, remainingCount }\n })\n\n// Preview cleanup - find empty and invalid sessions\nexport const previewCleanup = (projectName?: string) =>\n Effect.gen(function* () {\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Get orphan todos count (global, not per-project)\n const orphanTodos = yield* findOrphanTodos()\n const orphanTodoCount = orphanTodos.length\n\n const results = yield* Effect.all(\n targetProjects.map((project) =>\n Effect.gen(function* () {\n const sessions = yield* listSessions(project.name)\n const emptySessions = sessions.filter((s) => s.messageCount === 0)\n const invalidSessions = sessions.filter(\n (s) => s.title?.includes('Invalid API key') || s.title?.includes('API key')\n )\n\n // Count empty sessions that have todos\n let emptyWithTodosCount = 0\n for (const session of emptySessions) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) {\n emptyWithTodosCount++\n }\n }\n\n // Count orphan agents\n const orphanAgents = yield* findOrphanAgents(project.name)\n\n return {\n project: project.name,\n emptySessions,\n invalidSessions,\n emptyWithTodosCount,\n orphanAgentCount: orphanAgents.length,\n orphanTodoCount: 0, // Will set for first project only\n } satisfies CleanupPreview\n })\n ),\n { concurrency: 5 }\n )\n\n // Add orphanTodoCount only to the first result to avoid double counting\n if (results.length > 0) {\n results[0] = { ...results[0], orphanTodoCount }\n }\n\n return results\n })\n\n// Clear sessions\nexport const clearSessions = (options: {\n projectName?: string\n clearEmpty?: boolean\n clearInvalid?: boolean\n skipWithTodos?: boolean\n clearOrphanAgents?: boolean\n clearOrphanTodos?: boolean\n}) =>\n Effect.gen(function* () {\n const {\n projectName,\n clearEmpty = true,\n clearInvalid = true,\n skipWithTodos = true,\n clearOrphanAgents = false,\n clearOrphanTodos = false,\n } = options\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n let deletedSessionCount = 0\n let removedMessageCount = 0\n let deletedOrphanAgentCount = 0\n let deletedOrphanTodoCount = 0\n const sessionsToDelete: { project: string; sessionId: string }[] = []\n\n // Step 1: Clean invalid API key messages from all sessions (if clearInvalid)\n if (clearInvalid) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const result = yield* cleanInvalidMessages(project.name, sessionId)\n removedMessageCount += result.removedCount\n\n // Mark for deletion if now empty\n if (result.remainingCount === 0) {\n sessionsToDelete.push({ project: project.name, sessionId })\n }\n }\n }\n }\n\n // Step 2: Also find originally empty sessions (if clearEmpty is true)\n if (clearEmpty) {\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n for (const session of sessions) {\n if (session.messageCount === 0) {\n const alreadyMarked = sessionsToDelete.some(\n (s) => s.project === project.name && s.sessionId === session.id\n )\n if (!alreadyMarked) {\n // Skip sessions with todos if skipWithTodos is true\n if (skipWithTodos) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) continue\n }\n sessionsToDelete.push({ project: project.name, sessionId: session.id })\n }\n }\n }\n }\n }\n\n // Step 3: Delete all empty sessions (this also deletes linked agents and todos)\n for (const { project, sessionId } of sessionsToDelete) {\n yield* deleteSession(project, sessionId)\n deletedSessionCount++\n }\n\n // Step 4: Delete orphan agents if requested\n if (clearOrphanAgents) {\n for (const project of targetProjects) {\n const result = yield* deleteOrphanAgents(project.name)\n deletedOrphanAgentCount += result.count\n }\n }\n\n // Step 5: Delete orphan todos if requested (global, not per-project)\n if (clearOrphanTodos) {\n const result = yield* deleteOrphanTodos()\n deletedOrphanTodoCount = result.deletedCount\n }\n\n return {\n success: true,\n deletedCount: deletedSessionCount,\n removedMessageCount,\n deletedOrphanAgentCount,\n deletedOrphanTodoCount,\n } satisfies ClearSessionsResult\n })\n\n// Search sessions - two-phase: title search (fast) then content search (slow)\nexport const searchSessions = (\n query: string,\n options: { projectName?: string; searchContent?: boolean } = {}\n) =>\n Effect.gen(function* () {\n const { projectName, searchContent = false } = options\n const results: SearchResult[] = []\n const queryLower = query.toLowerCase()\n\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Phase 1: Title search (fast)\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n\n for (const session of sessions) {\n const titleLower = (session.title ?? '').toLowerCase()\n if (titleLower.includes(queryLower)) {\n results.push({\n sessionId: session.id,\n projectName: project.name,\n title: session.title ?? 'Untitled',\n matchType: 'title',\n timestamp: session.updatedAt,\n })\n }\n }\n }\n\n // Phase 2: Content search (slow, optional)\n if (searchContent) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n\n // Skip if already found in title search\n if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {\n continue\n }\n\n const filePath = path.join(projectPath, file)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n try {\n const msg = JSON.parse(line) as Message\n if (msg.type !== 'user' && msg.type !== 'assistant') continue\n\n const text = extractTextContent(msg.message)\n const textLower = text.toLowerCase()\n\n if (textLower.includes(queryLower)) {\n // Extract snippet around match\n const matchIndex = textLower.indexOf(queryLower)\n const start = Math.max(0, matchIndex - 50)\n const end = Math.min(text.length, matchIndex + query.length + 50)\n const snippet =\n (start > 0 ? '...' : '') +\n text.slice(start, end).trim() +\n (end < text.length ? '...' : '')\n\n results.push({\n sessionId,\n projectName: project.name,\n title:\n extractTitle(extractTextContent(msg.message)) ||\n `Session ${sessionId.slice(0, 8)}`,\n matchType: 'content',\n snippet,\n messageUuid: msg.uuid,\n timestamp: msg.timestamp,\n })\n break // One match per session is enough\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n }\n }\n }\n\n // Sort by timestamp (newest first)\n return results.sort((a, b) => {\n const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0\n const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Internal version that accepts summaries targeting this session\nconst loadSessionTreeDataInternal = (\n projectName: string,\n sessionId: string,\n summariesByTargetSession?: Map<string, SummaryInfo[]>\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Get summaries that TARGET this session (by leafUuid pointing to messages in this session)\n let summaries: SummaryInfo[]\n if (summariesByTargetSession) {\n // Project-wide loading: use pre-computed summaries targeting this session\n summaries = [...(summariesByTargetSession.get(sessionId) ?? [])]\n } else {\n // Single session loading: need to search the entire project for summaries targeting this session\n summaries = []\n // Build uuid set for this session's messages\n const sessionUuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid && typeof msg.uuid === 'string') {\n sessionUuids.add(msg.uuid)\n }\n }\n // Search all session files in the project for summaries with leafUuid pointing to this session\n const projectFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const allJsonlFiles = projectFiles.filter((f) => f.endsWith('.jsonl'))\n for (const file of allJsonlFiles) {\n try {\n const otherFilePath = path.join(projectPath, file)\n const otherContent = yield* Effect.tryPromise(() => fs.readFile(otherFilePath, 'utf-8'))\n const otherLines = otherContent.trim().split('\\n').filter(Boolean)\n for (const line of otherLines) {\n try {\n const msg = JSON.parse(line) as Record<string, unknown>\n if (\n msg.type === 'summary' &&\n typeof msg.summary === 'string' &&\n typeof msg.leafUuid === 'string' &&\n sessionUuids.has(msg.leafUuid)\n ) {\n // This summary's leafUuid points to a message in THIS session\n const targetMsg = messages.find((m) => m.uuid === msg.leafUuid)\n summaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid,\n timestamp:\n (targetMsg?.timestamp as string) ?? (msg.timestamp as string | undefined),\n })\n }\n } catch {\n // Skip invalid JSON\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n // Reverse to get newest first\n summaries.reverse()\n\n // Find last compact_boundary\n let lastCompactBoundaryUuid: string | undefined\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'system' && msg.subtype === 'compact_boundary') {\n lastCompactBoundaryUuid = msg.uuid as string\n break\n }\n }\n\n // customTitle is only set when user explicitly edits the title via UI\n // It should be stored separately in session file (not extracted from message)\n // For now, customTitle is undefined until we implement title editing feature\n const customTitle: string | undefined = undefined\n\n // Get title from first user message\n const firstUserMsgForTitle = messages.find((m) => m.type === 'user') as Message | undefined\n const title = firstUserMsgForTitle\n ? extractTitle(extractTextContent(firstUserMsgForTitle.message))\n : summaries.length > 0\n ? '[Summary Only]'\n : `Session ${sessionId.slice(0, 8)}`\n\n // Count user/assistant messages\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Find linked agents\n const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId)\n\n // Load agent info (message counts)\n const agents: AgentInfo[] = []\n for (const agentId of linkedAgentIds) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n try {\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n const agentMsgs = agentLines.map((l) => JSON.parse(l) as Record<string, unknown>)\n const agentUserAssistant = agentMsgs.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Try to extract agent name from first message\n let agentName: string | undefined\n const firstAgentMsg = agentMsgs.find((m) => m.type === 'user')\n if (firstAgentMsg) {\n const text = extractTextContent(firstAgentMsg.message as Message['message'])\n if (text) {\n agentName = extractTitle(text)\n }\n }\n\n agents.push({\n id: agentId,\n name: agentName,\n messageCount: agentUserAssistant.length,\n })\n } catch {\n // Agent file might not exist or be readable\n agents.push({\n id: agentId,\n messageCount: 0,\n })\n }\n }\n\n // Load todos\n const todos = yield* findLinkedTodos(sessionId, linkedAgentIds)\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n lastSummary: summaries[0]?.summary,\n messageCount:\n userAssistantMessages.length > 0\n ? userAssistantMessages.length\n : summaries.length > 0\n ? 1\n : 0,\n createdAt: (firstMessage?.timestamp as string) ?? undefined,\n updatedAt: (lastMessage?.timestamp as string) ?? undefined,\n summaries,\n agents,\n todos,\n lastCompactBoundaryUuid,\n } satisfies SessionTreeData\n })\n\n// Public wrapper for single session (without global uuid map, leafUuid lookup is limited)\nexport const loadSessionTreeData = (projectName: string, sessionId: string) =>\n loadSessionTreeDataInternal(projectName, sessionId, undefined)\n\n// Load all sessions tree data for a project\nexport const loadProjectTreeData = (projectName: string) =>\n Effect.gen(function* () {\n const project = (yield* listProjects).find((p) => p.name === projectName)\n if (!project) {\n return null\n }\n\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n // Phase 1: Build global uuid map + collect all summaries from ALL sessions\n // This is needed because leafUuid can reference messages in other sessions\n const globalUuidMap = new Map<string, { sessionId: string; timestamp?: string }>()\n const allSummaries: Array<{ summary: string; leafUuid?: string; timestamp?: string }> = []\n\n // Read all .jsonl files (sessions + agents) to build uuid map and collect summaries\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n yield* Effect.all(\n allJsonlFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const fileSessionId = file.replace('.jsonl', '')\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n for (const line of lines) {\n try {\n const msg = JSON.parse(line) as Record<string, unknown>\n if (msg.uuid && typeof msg.uuid === 'string') {\n globalUuidMap.set(msg.uuid, {\n sessionId: fileSessionId,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n // Also check messageId for file-history-snapshot type\n if (msg.messageId && typeof msg.messageId === 'string') {\n globalUuidMap.set(msg.messageId, {\n sessionId: fileSessionId,\n timestamp: (msg.snapshot as Record<string, unknown> | undefined)?.timestamp as\n | string\n | undefined,\n })\n }\n // Collect summaries\n if (msg.type === 'summary' && typeof msg.summary === 'string') {\n allSummaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid as string | undefined,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n ),\n { concurrency: 20 }\n )\n\n // Phase 1.5: Build summariesByTargetSession map\n // Each summary's leafUuid points to a message in some session - that's the TARGET session\n const summariesByTargetSession = new Map<string, SummaryInfo[]>()\n for (const summaryData of allSummaries) {\n if (summaryData.leafUuid) {\n const targetInfo = globalUuidMap.get(summaryData.leafUuid)\n if (targetInfo) {\n const targetSessionId = targetInfo.sessionId\n if (!summariesByTargetSession.has(targetSessionId)) {\n summariesByTargetSession.set(targetSessionId, [])\n }\n summariesByTargetSession.get(targetSessionId)!.push({\n summary: summaryData.summary,\n leafUuid: summaryData.leafUuid,\n timestamp: targetInfo.timestamp ?? summaryData.timestamp,\n })\n }\n }\n }\n\n // Phase 2: Load session tree data with summaries targeting each session\n const sessions = yield* Effect.all(\n sessionFiles.map((file) => {\n const sessionId = file.replace('.jsonl', '')\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession)\n }),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n const sortedSessions = sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n\n return {\n name: project.name,\n displayName: project.displayName,\n path: project.path,\n sessionCount: sessions.length,\n sessions: sortedSessions,\n } satisfies ProjectTreeData\n })\n\n// Update summary message in session\nexport const updateSessionSummary = (projectName: string, sessionId: string, newSummary: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find existing summary message\n const summaryIdx = messages.findIndex((m) => m.type === 'summary')\n\n if (summaryIdx >= 0) {\n // Update existing summary\n messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary }\n } else {\n // Add new summary at the beginning\n const firstUserMsg = messages.find((m) => m.type === 'user')\n const summaryMsg = {\n type: 'summary',\n summary: newSummary,\n leafUuid: (firstUserMsg as Message | undefined)?.uuid ?? null,\n }\n messages.unshift(summaryMsg)\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n"],"mappings":"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","names":["basename","fs","path","Effect","fs","path","Effect","Effect","fs","path","Effect","stat","agentBackupDir","hasSummary","remainingCount"]}
1
+ {"version":3,"sources":["../src/paths.ts","../src/logger.ts","../src/utils.ts","../src/agents.ts","../src/todos.ts","../src/session.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n *\n * Architecture:\n * - Pure Functions: extractCwdFromContent, isSessionFile, toRelativePath (no I/O)\n * - I/O Functions: tryGetCwdFromFile, getRealPathFromSession (with optional DI for testing)\n */\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport { createLogger } from './logger.js'\n\nconst log = createLogger('paths')\n\n// ============================================\n// Types (for dependency injection)\n// ============================================\n\nexport interface Logger {\n debug: (msg: string) => void\n warn: (msg: string) => void\n}\n\nexport interface FileSystem {\n readFileSync: (path: string, encoding: 'utf-8') => string\n readdirSync: (path: string) => string[]\n}\n\n// ============================================\n// Directory Paths\n// ============================================\n\n/** Get Claude sessions directory (~/.claude/projects) */\nexport const getSessionsDir = (): string => path.join(os.homedir(), '.claude', 'projects')\n\n/** Get Claude todos directory (~/.claude/todos) */\nexport const getTodosDir = (): string => path.join(os.homedir(), '.claude', 'todos')\n\n// ============================================\n// Pure Functions (No I/O)\n// ============================================\n\n/** Extract cwd from file content - pure function for easy testing */\nexport const extractCwdFromContent = (content: string): string | null => {\n const lines = content.split('\\n').filter((l) => l.trim())\n\n for (const line of lines) {\n try {\n const parsed = JSON.parse(line)\n if (parsed?.cwd) {\n return parsed.cwd\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n\n return null\n}\n\n/** Check if filename is a session file */\nexport const isSessionFile = (filename: string): boolean =>\n filename.endsWith('.jsonl') && !filename.startsWith('agent-')\n\n/** Convert path to relative form if under home directory */\nexport const toRelativePath = (absolutePath: string, homeDir: string): string => {\n const normalizedPath = absolutePath.replace(/\\\\/g, '/')\n const normalizedHome = homeDir.replace(/\\\\/g, '/')\n\n // Check for exact match or path with separator after home dir\n if (normalizedPath === normalizedHome) {\n return '~'\n }\n if (normalizedPath.startsWith(normalizedHome + '/')) {\n return '~' + normalizedPath.slice(normalizedHome.length)\n }\n return absolutePath\n}\n\n// ============================================\n// Path Conversion (Pure)\n// ============================================\n\n/**\n * Convert project folder name to display path\n * Unix: -home-user-projects -> /home/user/projects\n * Windows: C--Users-david -> C:\\Users\\david\n * Handle dot-prefixed folders: --claude -> /.claude\n */\nexport const folderNameToDisplayPath = (folderName: string): string => {\n // Check if Windows path (starts with drive letter pattern like \"C--\")\n const windowsDriveMatch = folderName.match(/^([A-Za-z])--/)\n if (windowsDriveMatch) {\n const driveLetter = windowsDriveMatch[1]\n const rest = folderName.slice(3)\n return driveLetter + ':\\\\' + rest.replace(/--/g, '\\\\.').replace(/-/g, '\\\\')\n }\n\n // Unix path\n return folderName.replace(/^-/, '/').replace(/--/g, '/.').replace(/-/g, '/')\n}\n\n/** Convert display path to folder name (reverse of above) */\nexport const displayPathToFolderName = (displayPath: string): string => {\n const windowsDriveMatch = displayPath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n const driveLetter = windowsDriveMatch[1]\n const rest = displayPath.slice(3)\n return driveLetter + '--' + rest.replace(/[/\\\\]\\./g, '--').replace(/[/\\\\]/g, '-')\n }\n\n return displayPath.replace(/^\\//g, '-').replace(/\\/\\./g, '--').replace(/\\//g, '-')\n}\n\n/**\n * Convert absolute path to project folder name\n * Non-ASCII characters are converted to '-' per character\n * Windows drive letter is normalized to lowercase (C: -> c--)\n */\nexport const pathToFolderName = (absolutePath: string): string => {\n const convertNonAscii = (str: string): string =>\n [...str].map((char) => (char.charCodeAt(0) <= 127 ? char : '-')).join('')\n\n const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n // Normalize drive letter to lowercase (Claude Code uses lowercase)\n const driveLetter = windowsDriveMatch[1].toLowerCase()\n const rest = absolutePath.slice(3)\n return (\n driveLetter +\n '--' +\n convertNonAscii(rest)\n .replace(/[/\\\\]\\./g, '--')\n .replace(/[/\\\\]/g, '-')\n .replace(/\\./g, '-')\n )\n }\n\n return convertNonAscii(absolutePath)\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--')\n .replace(/\\//g, '-')\n .replace(/\\./g, '-')\n}\n\n// ============================================\n// I/O Functions (with optional DI for testing)\n// ============================================\n\n/**\n * Try to extract cwd from a single session file\n * @param filePath - Path to session file\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const tryGetCwdFromFile = (\n filePath: string,\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const basename = path.basename(filePath)\n\n try {\n const content = fileSystem.readFileSync(filePath, 'utf-8')\n const cwd = extractCwdFromContent(content)\n\n if (cwd === null) {\n const lines = content.split('\\n').filter((l) => l.trim())\n if (lines.length === 0) {\n logger.debug(`tryGetCwdFromFile: ${basename} -> empty file`)\n } else {\n logger.debug(`tryGetCwdFromFile: ${basename} -> no cwd found in ${lines.length} lines`)\n }\n return null\n }\n\n return cwd\n } catch (e) {\n logger.warn(`tryGetCwdFromFile: ${basename} -> read error: ${e}`)\n return null\n }\n}\n\n/**\n * Extract real cwd path from session files in a project\n * @param folderName - Project folder name\n * @param sessionsDir - Optional sessions directory for testing\n * @param fileSystem - Optional FileSystem for testing\n * @param logger - Optional Logger for testing\n */\nexport const getRealPathFromSession = (\n folderName: string,\n sessionsDir: string = getSessionsDir(),\n fileSystem: FileSystem = fs,\n logger: Logger = log\n): string | null => {\n const projectDir = path.join(sessionsDir, folderName)\n\n try {\n const files = fileSystem.readdirSync(projectDir).filter(isSessionFile)\n\n const cwdList: string[] = []\n for (const f of files) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger)\n if (cwd !== null) {\n cwdList.push(cwd)\n }\n }\n\n // Find cwd that matches folder name\n const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName)\n if (matched) {\n return matched\n }\n\n // Log for debugging\n if (cwdList.length > 0) {\n logger.warn(\n `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(', ')}`\n )\n } else {\n logger.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`)\n }\n return null\n } catch {\n return null\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Convert folder name to relative or absolute path for display\n * If path is under home directory, show relative (~/...)\n */\nexport const folderNameToPath = (folderName: string): string => {\n const homeDir = os.homedir()\n\n // First try to get real path from session cwd\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n return toRelativePath(realPath, homeDir)\n }\n\n // Fallback to pattern-based conversion\n const absolutePath = folderNameToDisplayPath(folderName)\n return toRelativePath(absolutePath, homeDir)\n}\n","/**\n * Simple logger abstraction for Claude Sessions\n * Consumers can provide their own logger implementation\n */\n\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void\n info: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\n// Default console logger\nconst consoleLogger: Logger = {\n debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),\n info: (msg, ...args) => console.info(`[INFO] ${msg}`, ...args),\n warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),\n}\n\n// Global logger instance\nlet currentLogger: Logger = consoleLogger\n\n/**\n * Set custom logger implementation\n * @example\n * // VSCode extension\n * setLogger({\n * debug: (msg) => outputChannel.appendLine(`[DEBUG] ${msg}`),\n * info: (msg) => outputChannel.appendLine(`[INFO] ${msg}`),\n * warn: (msg) => outputChannel.appendLine(`[WARN] ${msg}`),\n * error: (msg) => outputChannel.appendLine(`[ERROR] ${msg}`),\n * })\n */\nexport const setLogger = (logger: Logger): void => {\n currentLogger = logger\n}\n\n/**\n * Get current logger instance\n */\nexport const getLogger = (): Logger => currentLogger\n\n/**\n * Create a namespaced logger\n * @example\n * const log = createLogger('paths')\n * log.debug('Converting folder name') // [DEBUG] [paths] Converting folder name\n */\nexport const createLogger = (namespace: string): Logger => ({\n debug: (msg, ...args) => currentLogger.debug(`[${namespace}] ${msg}`, ...args),\n info: (msg, ...args) => currentLogger.info(`[${namespace}] ${msg}`, ...args),\n warn: (msg, ...args) => currentLogger.warn(`[${namespace}] ${msg}`, ...args),\n error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args),\n})\n","/**\n * Utility functions for message processing\n */\nimport type { Message, MessagePayload, TextContent } from './types.js'\nimport { createLogger } from './logger.js'\n\nconst logger = createLogger('utils')\n\n// Extract text content from message payload\nexport const extractTextContent = (message: MessagePayload | undefined): string => {\n if (!message) return ''\n\n const content = message.content\n if (!content) return ''\n\n // If content is string, return directly\n if (typeof content === 'string') return content\n\n // If content is array, extract text items\n if (Array.isArray(content)) {\n return content\n .filter((item): item is TextContent => typeof item === 'object' && item?.type === 'text')\n .map((item) => {\n if (item.text == null) {\n logger.warn('TextContent item has undefined or null text property')\n return ''\n }\n return item.text\n })\n .join('')\n }\n\n return ''\n}\n\n// Extract title from text content (remove IDE tags, use first line)\nexport const extractTitle = (text: string): string => {\n if (!text) return 'Untitled'\n\n // Remove IDE tags (<ide_opened_file>, <ide_selection>, etc.)\n let cleaned = text.replace(/<ide_[^>]*>[\\s\\S]*?<\\/ide_[^>]*>/g, '').trim()\n\n if (!cleaned) return 'Untitled'\n\n // Use only content before \\n\\n or \\n as title\n if (cleaned.includes('\\n\\n')) {\n cleaned = cleaned.split('\\n\\n')[0]\n } else if (cleaned.includes('\\n')) {\n cleaned = cleaned.split('\\n')[0]\n }\n\n // Limit to 100 characters\n if (cleaned.length > 100) {\n return cleaned.slice(0, 100) + '...'\n }\n\n return cleaned || 'Untitled'\n}\n\n// Check if message contains \"Invalid API key\"\nexport const isInvalidApiKeyMessage = (msg: Message): boolean => {\n const text = extractTextContent(msg.message)\n return text.includes('Invalid API key')\n}\n\n// Check if a message is a continuation summary (from compact)\nexport const isContinuationSummary = (msg: Message): boolean => {\n // isCompactSummary flag is set by Claude Code for continuation summaries\n if (msg.isCompactSummary === true) return true\n\n // Fallback: check message content\n if (msg.type !== 'user') return false\n const text = extractTextContent(msg.message as MessagePayload | undefined)\n return text.startsWith('This session is being continued from')\n}\n\n/**\n * Get display title with fallback logic\n * Priority: customTitle > currentSummary (truncated) > title > fallback\n */\nexport const getDisplayTitle = (\n customTitle: string | undefined,\n currentSummary: string | undefined,\n title: string | undefined,\n maxLength = 60,\n fallback = 'Untitled'\n): string => {\n if (customTitle) return customTitle\n if (currentSummary) {\n return currentSummary.length > maxLength\n ? currentSummary.slice(0, maxLength - 3) + '...'\n : currentSummary\n }\n if (title && title !== 'Untitled') return title\n return fallback\n}\n\n// Clean up first message content when splitting session\n// Uses toolUseResult field to extract the actual user message from tool rejection\nexport const cleanupSplitFirstMessage = (msg: Message): Message => {\n const toolUseResult = msg.toolUseResult\n if (!toolUseResult) return msg\n\n // Extract rejection reason from toolUseResult\n const rejectionMarker = 'The user provided the following reason for the rejection:'\n const rejectionIndex = toolUseResult.indexOf(rejectionMarker)\n if (rejectionIndex === -1) return msg\n\n const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim()\n if (!text) return msg\n\n // Replace message content with extracted text\n return {\n ...msg,\n message: {\n ...msg.message,\n content: [{ type: 'text', text } satisfies TextContent],\n },\n toolUseResult: undefined,\n }\n}\n","/**\n * Agent file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir } from './paths.js'\nimport type { Message } from './types.js'\n\n// Find agent files linked to a session\nexport const findLinkedAgents = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n const linkedAgents: string[] = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId === sessionId) {\n linkedAgents.push(agentFile.replace('.jsonl', ''))\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return linkedAgents\n })\n\n// Find orphan agent files (agents whose parent session no longer exists)\nexport const findOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n\n const sessionIds = new Set(\n files\n .filter((f) => !f.startsWith('agent-') && f.endsWith('.jsonl'))\n .map((f) => f.replace('.jsonl', ''))\n )\n\n const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n const orphanAgents: Array<{ agentId: string; sessionId: string }> = []\n\n for (const agentFile of agentFiles) {\n const filePath = path.join(projectPath, agentFile)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const firstLine = content.split('\\n')[0]\n\n if (firstLine) {\n try {\n const parsed = JSON.parse(firstLine) as { sessionId?: string }\n if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {\n orphanAgents.push({\n agentId: agentFile.replace('.jsonl', ''),\n sessionId: parsed.sessionId,\n })\n }\n } catch {\n // Skip invalid JSON\n }\n }\n }\n\n return orphanAgents\n })\n\n// Delete orphan agent files (move to .bak)\nexport const deleteOrphanAgents = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const orphans = yield* findOrphanAgents(projectName)\n\n // Create backup directory\n const backupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n const deletedAgents: string[] = []\n\n for (const orphan of orphans) {\n const agentPath = path.join(projectPath, `${orphan.agentId}.jsonl`)\n const agentBackupPath = path.join(backupDir, `${orphan.agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath))\n deletedAgents.push(orphan.agentId)\n }\n\n return { success: true, deletedAgents, count: deletedAgents.length }\n })\n\n// Load agent messages from agent session file\nexport const loadAgentMessages = (\n projectName: string,\n _sessionId: string, // Reserved for future validation\n agentId: string\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n // Agent files are stored as agent-<agentId>.jsonl in project directory\n const agentFilePath = path.join(projectPath, `${agentId}.jsonl`)\n\n const content = yield* Effect.tryPromise(() => fs.readFile(agentFilePath, 'utf-8'))\n\n const lines = content.split('\\n').filter((line) => line.trim())\n const messages: Message[] = []\n\n for (const line of lines) {\n try {\n const parsed = JSON.parse(line) as Message\n // Skip header line (contains sessionId)\n if ('sessionId' in parsed && !('type' in parsed)) {\n continue\n }\n messages.push(parsed)\n } catch {\n // Skip invalid JSON lines\n }\n }\n\n return messages\n })\n","/**\n * Todo file management utilities\n */\nimport { Effect } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, getTodosDir } from './paths.js'\nimport type { TodoItem, SessionTodos } from './types.js'\n\n// Find linked todo files for a session and its agents\n// Scans todos directory for files matching session pattern\nexport const findLinkedTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return {\n sessionId,\n sessionTodos: [],\n agentTodos: [],\n hasTodos: false,\n } satisfies SessionTodos\n }\n\n // Read session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n let sessionTodos: TodoItem[] = []\n\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n sessionTodos = JSON.parse(content) as TodoItem[]\n } catch {\n // Invalid JSON, treat as empty\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Read agent todo files\n const agentTodos: { agentId: string; todos: TodoItem[] }[] = []\n\n for (const agentId of discoveredAgentIds) {\n // Agent todo files are named: {sessionId}-agent-{shortAgentId}.json\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) {\n agentTodos.push({ agentId, todos })\n }\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n\n const hasTodos = sessionTodos.length > 0 || agentTodos.some((at) => at.todos.length > 0)\n\n return {\n sessionId,\n sessionTodos,\n agentTodos,\n hasTodos,\n } satisfies SessionTodos\n })\n\n// Check if session has any todos (quick check)\n// Scans todos directory for files matching session pattern\nexport const sessionHasTodos = (sessionId: string, agentIds: string[] = []) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return false\n\n // Check session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(sessionTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n\n // Scan todos directory for agent todo files matching this session\n const allFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\\\.json$`)\n\n // Collect agent IDs from both provided list and directory scan\n const discoveredAgentIds = new Set<string>(agentIds)\n for (const file of allFiles) {\n const match = file.match(agentTodoPattern)\n if (match) {\n discoveredAgentIds.add(`agent-${match[1]}`)\n }\n }\n\n // Check agent todo files\n for (const agentId of discoveredAgentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const content = yield* Effect.tryPromise(() => fs.readFile(agentTodoPath, 'utf-8'))\n try {\n const todos = JSON.parse(content) as TodoItem[]\n if (todos.length > 0) return true\n } catch {\n // Invalid JSON, continue\n }\n }\n }\n\n return false\n })\n\n// Delete linked todo files for a session (move to .bak)\nexport const deleteLinkedTodos = (sessionId: string, agentIds: string[]) =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n\n // Check if todos directory exists\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) return { deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n // Delete session's own todo file\n const sessionTodoPath = path.join(todosDir, `${sessionId}.json`)\n const sessionTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (sessionTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}.json`)\n yield* Effect.tryPromise(() => fs.rename(sessionTodoPath, backupPath))\n deletedCount++\n }\n\n // Delete agent todo files\n for (const agentId of agentIds) {\n const shortAgentId = agentId.replace('agent-', '')\n const agentTodoPath = path.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`)\n\n const agentTodoExists = yield* Effect.tryPromise(() =>\n fs\n .access(agentTodoPath)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentTodoExists) {\n const backupPath = path.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`)\n yield* Effect.tryPromise(() => fs.rename(agentTodoPath, backupPath))\n deletedCount++\n }\n }\n\n return { deletedCount }\n })\n\n// Find all orphan todo files (session no longer exists)\nexport const findOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const sessionsDir = getSessionsDir()\n\n // Check if directories exist\n const [todosExists, sessionsExists] = yield* Effect.all([\n Effect.tryPromise(() =>\n fs\n .access(todosDir)\n .then(() => true)\n .catch(() => false)\n ),\n Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n ),\n ])\n\n if (!todosExists || !sessionsExists) return []\n\n // Get all todo files\n const todoFiles = yield* Effect.tryPromise(() => fs.readdir(todosDir))\n const jsonFiles = todoFiles.filter((f) => f.endsWith('.json'))\n\n // Build set of all valid session IDs across all projects\n const validSessionIds = new Set<string>()\n const projectEntries = yield* Effect.tryPromise(() =>\n fs.readdir(sessionsDir, { withFileTypes: true })\n )\n\n for (const entry of projectEntries) {\n if (!entry.isDirectory() || entry.name.startsWith('.')) continue\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n for (const f of files) {\n if (f.endsWith('.jsonl') && !f.startsWith('agent-')) {\n validSessionIds.add(f.replace('.jsonl', ''))\n }\n }\n }\n\n // Find orphan todo files\n const orphans: string[] = []\n for (const todoFile of jsonFiles) {\n // Parse session ID from todo filename\n // Format: {sessionId}.json or {sessionId}-agent-{agentId}.json\n const match = todoFile.match(/^([a-f0-9-]+)(?:-agent-[a-f0-9]+)?\\.json$/)\n if (match) {\n const sessionId = match[1]\n if (!validSessionIds.has(sessionId)) {\n orphans.push(todoFile)\n }\n }\n }\n\n return orphans\n })\n\n// Delete orphan todo files\nexport const deleteOrphanTodos = () =>\n Effect.gen(function* () {\n const todosDir = getTodosDir()\n const orphans = yield* findOrphanTodos()\n\n if (orphans.length === 0) return { success: true, deletedCount: 0 }\n\n // Create backup directory\n const backupDir = path.join(todosDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n let deletedCount = 0\n\n for (const orphan of orphans) {\n const filePath = path.join(todosDir, orphan)\n const backupPath = path.join(backupDir, orphan)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n deletedCount++\n }\n\n return { success: true, deletedCount }\n })\n","/**\n * Session management operations\n */\nimport { Effect, pipe, Array as A, Option as O } from 'effect'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { getSessionsDir, folderNameToPath } from './paths.js'\nimport {\n extractTextContent,\n extractTitle,\n isInvalidApiKeyMessage,\n isContinuationSummary,\n cleanupSplitFirstMessage,\n} from './utils.js'\nimport { findLinkedAgents, findOrphanAgents, deleteOrphanAgents } from './agents.js'\nimport {\n findLinkedTodos,\n deleteLinkedTodos,\n sessionHasTodos,\n findOrphanTodos,\n deleteOrphanTodos,\n} from './todos.js'\nimport type {\n Message,\n SessionMeta,\n Project,\n FileChange,\n SessionFilesSummary,\n DeleteSessionResult,\n RenameSessionResult,\n SplitSessionResult,\n MoveSessionResult,\n ClearSessionsResult,\n CleanupPreview,\n SearchResult,\n SessionTreeData,\n SummaryInfo,\n AgentInfo,\n ProjectTreeData,\n JsonlRecord,\n} from './types.js'\n\n// List all project directories\nexport const listProjects = Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n\n const exists = yield* Effect.tryPromise(() =>\n fs\n .access(sessionsDir)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!exists) {\n return [] as Project[]\n }\n\n const entries = yield* Effect.tryPromise(() => fs.readdir(sessionsDir, { withFileTypes: true }))\n\n const projects = yield* Effect.all(\n entries\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((entry) =>\n Effect.gen(function* () {\n const projectPath = path.join(sessionsDir, entry.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n return {\n name: entry.name,\n displayName: folderNameToPath(entry.name),\n path: projectPath,\n sessionCount: sessionFiles.length,\n } satisfies Project\n })\n ),\n { concurrency: 10 }\n )\n\n return projects\n})\n\n// List sessions in a project\nexport const listSessions = (projectName: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n // Exclude agent- files (subagent logs)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n const sessions = yield* Effect.all(\n sessionFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Message)\n\n const sessionId = file.replace('.jsonl', '')\n\n // Filter only user/assistant messages for counting\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Check if session has summary (for preserved sessions without user/assistant messages)\n const hasSummary = messages.some((m) => m.type === 'summary')\n\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Extract title from first user message\n const title = pipe(\n messages,\n A.findFirst((m) => m.type === 'user'),\n O.map((m) => {\n const text = extractTextContent(m.message)\n return extractTitle(text)\n }),\n O.getOrElse(() => (hasSummary ? '[Summary Only]' : `Session ${sessionId.slice(0, 8)}`))\n )\n\n return {\n id: sessionId,\n projectName,\n title,\n // If session has summary but no user/assistant messages, count as 1\n messageCount:\n userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,\n createdAt: firstMessage?.timestamp,\n updatedAt: lastMessage?.timestamp,\n } satisfies SessionMeta\n })\n ),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n return sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Read session messages\nexport const readSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n return lines.map((line) => JSON.parse(line) as Message)\n })\n\n// Delete a message from session and repair parentUuid chain\nexport const deleteMessage = (projectName: string, sessionId: string, messageUuid: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find by uuid, messageId (for file-history-snapshot type), or leafUuid (for summary type)\n const targetIndex = messages.findIndex(\n (m) => m.uuid === messageUuid || m.messageId === messageUuid || m.leafUuid === messageUuid\n )\n if (targetIndex === -1) {\n return { success: false, error: 'Message not found' }\n }\n\n // Get the deleted message's uuid and parentUuid\n const deletedMsg = messages[targetIndex]\n const deletedUuid = deletedMsg?.uuid ?? deletedMsg?.messageId\n const parentUuid = deletedMsg?.parentUuid\n\n // Find all messages that reference the deleted message as their parent\n // and update them to point to the deleted message's parent\n for (const msg of messages) {\n if (msg.parentUuid === deletedUuid) {\n msg.parentUuid = parentUuid\n }\n }\n\n // Remove the message\n messages.splice(targetIndex, 1)\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true, deletedMessage: deletedMsg }\n })\n\n// Restore a deleted message at a specific index\nexport const restoreMessage = (\n projectName: string,\n sessionId: string,\n message: Record<string, unknown>,\n index: number\n) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n const msgUuid = message.uuid ?? message.messageId\n if (!msgUuid) {\n return { success: false, error: 'Message has no uuid or messageId' }\n }\n\n // Find the message that currently has parentUuid pointing to restored message's parent\n // and update it to point to the restored message instead\n const restoredParentUuid = message.parentUuid as string | undefined\n for (const msg of messages) {\n if (msg.parentUuid === restoredParentUuid) {\n // This message was previously pointing to the deleted message's parent\n // Now it should point to the restored message\n msg.parentUuid = msgUuid\n break // Only one message should be affected\n }\n }\n\n // Insert message at the specified index (or at end if index is out of bounds)\n const insertIndex = Math.min(index, messages.length)\n messages.splice(insertIndex, 0, message)\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n\n// Delete a session and its linked agent/todo files\nexport const deleteSession = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const projectPath = path.join(sessionsDir, projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n\n // Find linked agents first (before any deletion)\n const linkedAgents = yield* findLinkedAgents(projectName, sessionId)\n\n // Check file size - if empty (0 bytes), just delete without backup\n const stat = yield* Effect.tryPromise(() => fs.stat(filePath))\n if (stat.size === 0) {\n yield* Effect.tryPromise(() => fs.unlink(filePath))\n // Still delete linked agents and todos for empty sessions\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n yield* deleteLinkedTodos(sessionId, linkedAgents)\n return { success: true, deletedAgents: linkedAgents.length } satisfies DeleteSessionResult\n }\n\n // Create backup directory\n const backupDir = path.join(sessionsDir, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(backupDir, { recursive: true }))\n\n // Delete linked agent files (move to .bak in project folder)\n const agentBackupDir = path.join(projectPath, '.bak')\n yield* Effect.tryPromise(() => fs.mkdir(agentBackupDir, { recursive: true }))\n for (const agentId of linkedAgents) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n const agentBackupPath = path.join(agentBackupDir, `${agentId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(agentPath, agentBackupPath).catch(() => {}))\n }\n\n // Delete linked todo files\n const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents)\n\n // Move session to backup (format: project_name_session_id.jsonl)\n const backupPath = path.join(backupDir, `${projectName}_${sessionId}.jsonl`)\n yield* Effect.tryPromise(() => fs.rename(filePath, backupPath))\n\n return {\n success: true,\n backupPath,\n deletedAgents: linkedAgents.length,\n deletedTodos: todosResult.deletedCount,\n } satisfies DeleteSessionResult\n })\n\n// Rename session by updating custom-title and first summary\n// custom-title is stored in this session file\n// summary is stored in OTHER session files (where leafUuid points to this session's messages)\nexport const renameSession = (projectName: string, sessionId: string, newTitle: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) {\n return { success: false, error: 'Empty session' } satisfies RenameSessionResult\n }\n\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Build uuid set for this session's messages\n const sessionUuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid && typeof msg.uuid === 'string') {\n sessionUuids.add(msg.uuid)\n }\n }\n\n // Update or add custom-title in this session file\n const customTitleIdx = messages.findIndex((m) => m.type === 'custom-title')\n const customTitleRecord = {\n type: 'custom-title',\n customTitle: newTitle,\n sessionId,\n }\n if (customTitleIdx >= 0) {\n messages[customTitleIdx] = customTitleRecord\n } else {\n messages.unshift(customTitleRecord)\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n // Find and update first summary in OTHER session files\n // Summary's leafUuid points to a message in THIS session\n const projectFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const allJsonlFiles = projectFiles.filter((f) => f.endsWith('.jsonl'))\n\n // Collect all summaries targeting this session with their file info\n const summariesTargetingThis: {\n file: string\n idx: number\n timestamp?: string\n }[] = []\n\n for (const file of allJsonlFiles) {\n const otherFilePath = path.join(projectPath, file)\n try {\n const otherContent = yield* Effect.tryPromise(() => fs.readFile(otherFilePath, 'utf-8'))\n const otherLines = otherContent.trim().split('\\n').filter(Boolean)\n const otherMessages = otherLines.map((l) => JSON.parse(l) as Record<string, unknown>)\n\n for (let i = 0; i < otherMessages.length; i++) {\n const msg = otherMessages[i]\n if (\n msg.type === 'summary' &&\n typeof msg.leafUuid === 'string' &&\n sessionUuids.has(msg.leafUuid)\n ) {\n // Find target message timestamp\n const targetMsg = messages.find((m) => m.uuid === msg.leafUuid)\n summariesTargetingThis.push({\n file,\n idx: i,\n timestamp: (targetMsg?.timestamp as string) ?? (msg.timestamp as string | undefined),\n })\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n if (summariesTargetingThis.length > 0) {\n // Sort by timestamp ascending (oldest first), update the first one\n summariesTargetingThis.sort((a, b) => (a.timestamp ?? '').localeCompare(b.timestamp ?? ''))\n const firstSummary = summariesTargetingThis[0]\n\n const summaryFilePath = path.join(projectPath, firstSummary.file)\n const summaryContent = yield* Effect.tryPromise(() => fs.readFile(summaryFilePath, 'utf-8'))\n const summaryLines = summaryContent.trim().split('\\n').filter(Boolean)\n const summaryMessages = summaryLines.map((l) => JSON.parse(l) as Record<string, unknown>)\n\n summaryMessages[firstSummary.idx] = {\n ...summaryMessages[firstSummary.idx],\n summary: newTitle,\n }\n\n const newSummaryContent = summaryMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(summaryFilePath, newSummaryContent, 'utf-8'))\n } else {\n // No summary exists - use legacy method: add title prefix to first user message content\n // This is recognized by Claude Code extension\n const currentContent = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const currentLines = currentContent.trim().split('\\n').filter(Boolean)\n const currentMessages = currentLines.map((l) => JSON.parse(l) as Record<string, unknown>)\n\n const firstUserIdx = currentMessages.findIndex((m) => m.type === 'user')\n if (firstUserIdx >= 0) {\n const firstMsg = currentMessages[firstUserIdx] as JsonlRecord\n const msgPayload = firstMsg.message as { content?: unknown } | undefined\n if (msgPayload?.content && Array.isArray(msgPayload.content)) {\n // Find first non-IDE text content\n const textIdx = (msgPayload.content as Array<{ type?: string; text?: string }>).findIndex(\n (item) =>\n typeof item === 'object' &&\n item?.type === 'text' &&\n !item.text?.trim().startsWith('<ide_')\n )\n\n if (textIdx >= 0) {\n const item = (msgPayload.content as Array<{ type?: string; text?: string }>)[textIdx]\n const oldText = item.text ?? ''\n // Remove existing title pattern (first line ending with \\n\\n)\n const cleanedText = oldText.replace(/^[^\\n]+\\n\\n/, '')\n item.text = `${newTitle}\\n\\n${cleanedText}`\n\n const updatedContent = currentMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, updatedContent, 'utf-8'))\n }\n }\n }\n }\n\n return { success: true } satisfies RenameSessionResult\n })\n\n// Get files changed in a session (from file-history-snapshot and tool_use)\nexport const getSessionFiles = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const messages = yield* readSession(projectName, sessionId)\n const fileChanges: FileChange[] = []\n const seenFiles = new Set<string>()\n\n for (const msg of messages) {\n // Check file-history-snapshot type\n if (msg.type === 'file-history-snapshot') {\n const snapshot = msg as unknown as {\n type: string\n messageId?: string\n snapshot?: {\n trackedFileBackups?: Record<string, unknown>\n timestamp?: string\n }\n }\n const backups = snapshot.snapshot?.trackedFileBackups\n if (backups && typeof backups === 'object') {\n for (const filePath of Object.keys(backups)) {\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: 'modified',\n timestamp: snapshot.snapshot?.timestamp,\n messageUuid: snapshot.messageId ?? msg.uuid,\n })\n }\n }\n }\n }\n\n // Check tool_use for Write/Edit operations\n if (msg.type === 'assistant' && msg.message?.content) {\n const content = msg.message.content\n if (Array.isArray(content)) {\n for (const item of content) {\n if (item && typeof item === 'object' && 'type' in item && item.type === 'tool_use') {\n const toolUse = item as { name?: string; input?: { file_path?: string } }\n if (\n (toolUse.name === 'Write' || toolUse.name === 'Edit') &&\n toolUse.input?.file_path\n ) {\n const filePath = toolUse.input.file_path\n if (!seenFiles.has(filePath)) {\n seenFiles.add(filePath)\n fileChanges.push({\n path: filePath,\n action: toolUse.name === 'Write' ? 'created' : 'modified',\n timestamp: msg.timestamp,\n messageUuid: msg.uuid,\n })\n }\n }\n }\n }\n }\n }\n }\n\n return {\n sessionId,\n projectName,\n files: fileChanges,\n totalChanges: fileChanges.length,\n } satisfies SessionFilesSummary\n })\n\n// Move session to another project\nexport const moveSession = (\n sourceProject: string,\n sessionId: string,\n targetProject: string\n): Effect.Effect<MoveSessionResult, Error> =>\n Effect.gen(function* () {\n const sessionsDir = getSessionsDir()\n const sourcePath = path.join(sessionsDir, sourceProject)\n const targetPath = path.join(sessionsDir, targetProject)\n\n const sourceFile = path.join(sourcePath, `${sessionId}.jsonl`)\n const targetFile = path.join(targetPath, `${sessionId}.jsonl`)\n\n // Check source file exists\n const sourceExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (!sourceExists) {\n return { success: false, error: 'Source session not found' }\n }\n\n // Check target file does not exist\n const targetExists = yield* Effect.tryPromise(() =>\n fs\n .access(targetFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (targetExists) {\n return { success: false, error: 'Session already exists in target project' }\n }\n\n // Create target directory if needed\n yield* Effect.tryPromise(() => fs.mkdir(targetPath, { recursive: true }))\n\n // Find linked agents before moving\n const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId)\n\n // Move session file\n yield* Effect.tryPromise(() => fs.rename(sourceFile, targetFile))\n\n // Move linked agent files\n for (const agentId of linkedAgents) {\n const sourceAgentFile = path.join(sourcePath, `${agentId}.jsonl`)\n const targetAgentFile = path.join(targetPath, `${agentId}.jsonl`)\n\n const agentExists = yield* Effect.tryPromise(() =>\n fs\n .access(sourceAgentFile)\n .then(() => true)\n .catch(() => false)\n )\n\n if (agentExists) {\n yield* Effect.tryPromise(() => fs.rename(sourceAgentFile, targetAgentFile))\n }\n }\n\n return { success: true }\n })\n\n// Split session at a specific message\nexport const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n // Parse all messages preserving their full structure\n const allMessages = lines.map((line) => JSON.parse(line) as Message)\n\n // Find the split point\n const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid)\n if (splitIndex === -1) {\n return { success: false, error: 'Message not found' } satisfies SplitSessionResult\n }\n\n if (splitIndex === 0) {\n return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult\n }\n\n // Generate new session ID\n const newSessionId = crypto.randomUUID()\n\n // Find all summary messages and get the last (most recent) one\n // Summaries are typically at the beginning, but we want the most recent one\n const summaryMessages = allMessages.filter((m) => m.type === 'summary')\n const summaryMessage =\n summaryMessages.length > 0 ? summaryMessages[summaryMessages.length - 1] : null\n\n // Check if the split message is a continuation summary\n const splitMessage = allMessages[splitIndex]\n const shouldDuplicate = isContinuationSummary(splitMessage)\n\n // Split messages - if continuation summary, include it in both sessions\n let remainingMessages: Message[]\n const movedMessages = allMessages.slice(splitIndex)\n\n if (shouldDuplicate) {\n // Create a copy of the continuation message with new UUID for the original session\n const duplicatedMessage: Message = {\n ...splitMessage,\n uuid: crypto.randomUUID(),\n sessionId: sessionId, // Keep original session ID\n }\n remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage]\n } else {\n remainingMessages = allMessages.slice(0, splitIndex)\n }\n\n // Update moved messages with new sessionId and fix first message's parentUuid\n const updatedMovedMessages = movedMessages.map((msg, index) => {\n let updated: Message = { ...msg, sessionId: newSessionId }\n if (index === 0) {\n // First message of new session should have no parent\n updated.parentUuid = null\n // Clean up first message content if it's a tool_result rejection\n updated = cleanupSplitFirstMessage(updated)\n }\n return updated\n })\n\n // Clone summary message to new session if exists\n if (summaryMessage) {\n const clonedSummary = {\n ...summaryMessage,\n leafUuid: updatedMovedMessages[0]?.uuid ?? null,\n }\n updatedMovedMessages.unshift(clonedSummary)\n }\n\n // Write remaining messages to original file\n const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, remainingContent, 'utf-8'))\n\n // Write moved messages to new session file\n const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`)\n const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8'))\n\n // Update linked agent files that reference the old sessionId\n const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))\n\n for (const agentFile of agentJsonlFiles) {\n const agentPath = path.join(projectPath, agentFile)\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n\n if (agentLines.length === 0) continue\n\n const firstAgentMsg = JSON.parse(agentLines[0]) as { sessionId?: string }\n\n // If this agent belongs to the original session, check if it should be moved\n if (firstAgentMsg.sessionId === sessionId) {\n // Check if any message in moved messages is related to this agent\n const agentId = agentFile.replace('agent-', '').replace('.jsonl', '')\n const isRelatedToMoved = movedMessages.some(\n (msg) => (msg as { agentId?: string }).agentId === agentId\n )\n\n if (isRelatedToMoved) {\n // Update all messages in this agent file to reference new sessionId\n const updatedAgentMessages = agentLines.map((line) => {\n const msg = JSON.parse(line) as Record<string, unknown>\n return JSON.stringify({ ...msg, sessionId: newSessionId })\n })\n const updatedAgentContent = updatedAgentMessages.join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8'))\n }\n }\n }\n\n return {\n success: true,\n newSessionId,\n newSessionPath: newFilePath,\n movedMessageCount: movedMessages.length,\n duplicatedSummary: shouldDuplicate,\n } satisfies SplitSessionResult\n })\n\n// Remove invalid API key messages from a session, returns remaining message count\nconst cleanInvalidMessages = (projectName: string, sessionId: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n if (lines.length === 0) return { removedCount: 0, remainingCount: 0 }\n\n const messages = lines.map((line) => JSON.parse(line) as Message)\n const invalidIndices: number[] = []\n\n // Find all invalid API key messages\n messages.forEach((msg, idx) => {\n if (isInvalidApiKeyMessage(msg)) {\n invalidIndices.push(idx)\n }\n })\n\n if (invalidIndices.length === 0) {\n const userAssistantCount = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = messages.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = userAssistantCount > 0 ? userAssistantCount : hasSummary ? 1 : 0\n return { removedCount: 0, remainingCount }\n }\n\n // Remove invalid messages and fix parentUuid chain\n const filtered: Message[] = []\n let lastValidUuid: string | null = null\n\n for (let i = 0; i < messages.length; i++) {\n if (invalidIndices.includes(i)) {\n continue // Skip invalid message\n }\n\n const msg = messages[i]\n // Update parentUuid to point to last valid message\n if (msg.parentUuid && invalidIndices.some((idx) => messages[idx]?.uuid === msg.parentUuid)) {\n msg.parentUuid = lastValidUuid\n }\n filtered.push(msg)\n lastValidUuid = msg.uuid\n }\n\n const newContent =\n filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join('\\n') + '\\n' : ''\n\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n const remainingUserAssistant = filtered.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n ).length\n const hasSummary = filtered.some((m) => m.type === 'summary')\n // Count summary-only sessions as having 1 message\n const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0\n return { removedCount: invalidIndices.length, remainingCount }\n })\n\n// Preview cleanup - find empty and invalid sessions\nexport const previewCleanup = (projectName?: string) =>\n Effect.gen(function* () {\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Get orphan todos count (global, not per-project)\n const orphanTodos = yield* findOrphanTodos()\n const orphanTodoCount = orphanTodos.length\n\n const results = yield* Effect.all(\n targetProjects.map((project) =>\n Effect.gen(function* () {\n const sessions = yield* listSessions(project.name)\n const emptySessions = sessions.filter((s) => s.messageCount === 0)\n const invalidSessions = sessions.filter(\n (s) => s.title?.includes('Invalid API key') || s.title?.includes('API key')\n )\n\n // Count empty sessions that have todos\n let emptyWithTodosCount = 0\n for (const session of emptySessions) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) {\n emptyWithTodosCount++\n }\n }\n\n // Count orphan agents\n const orphanAgents = yield* findOrphanAgents(project.name)\n\n return {\n project: project.name,\n emptySessions,\n invalidSessions,\n emptyWithTodosCount,\n orphanAgentCount: orphanAgents.length,\n orphanTodoCount: 0, // Will set for first project only\n } satisfies CleanupPreview\n })\n ),\n { concurrency: 5 }\n )\n\n // Add orphanTodoCount only to the first result to avoid double counting\n if (results.length > 0) {\n results[0] = { ...results[0], orphanTodoCount }\n }\n\n return results\n })\n\n// Clear sessions\nexport const clearSessions = (options: {\n projectName?: string\n clearEmpty?: boolean\n clearInvalid?: boolean\n skipWithTodos?: boolean\n clearOrphanAgents?: boolean\n clearOrphanTodos?: boolean\n}) =>\n Effect.gen(function* () {\n const {\n projectName,\n clearEmpty = true,\n clearInvalid = true,\n skipWithTodos = true,\n clearOrphanAgents = false,\n clearOrphanTodos = false,\n } = options\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n let deletedSessionCount = 0\n let removedMessageCount = 0\n let deletedOrphanAgentCount = 0\n let deletedOrphanTodoCount = 0\n const sessionsToDelete: { project: string; sessionId: string }[] = []\n\n // Step 1: Clean invalid API key messages from all sessions (if clearInvalid)\n if (clearInvalid) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n const result = yield* cleanInvalidMessages(project.name, sessionId)\n removedMessageCount += result.removedCount\n\n // Mark for deletion if now empty\n if (result.remainingCount === 0) {\n sessionsToDelete.push({ project: project.name, sessionId })\n }\n }\n }\n }\n\n // Step 2: Also find originally empty sessions (if clearEmpty is true)\n if (clearEmpty) {\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n for (const session of sessions) {\n if (session.messageCount === 0) {\n const alreadyMarked = sessionsToDelete.some(\n (s) => s.project === project.name && s.sessionId === session.id\n )\n if (!alreadyMarked) {\n // Skip sessions with todos if skipWithTodos is true\n if (skipWithTodos) {\n const linkedAgents = yield* findLinkedAgents(project.name, session.id)\n const hasTodos = yield* sessionHasTodos(session.id, linkedAgents)\n if (hasTodos) continue\n }\n sessionsToDelete.push({ project: project.name, sessionId: session.id })\n }\n }\n }\n }\n }\n\n // Step 3: Delete all empty sessions (this also deletes linked agents and todos)\n for (const { project, sessionId } of sessionsToDelete) {\n yield* deleteSession(project, sessionId)\n deletedSessionCount++\n }\n\n // Step 4: Delete orphan agents if requested\n if (clearOrphanAgents) {\n for (const project of targetProjects) {\n const result = yield* deleteOrphanAgents(project.name)\n deletedOrphanAgentCount += result.count\n }\n }\n\n // Step 5: Delete orphan todos if requested (global, not per-project)\n if (clearOrphanTodos) {\n const result = yield* deleteOrphanTodos()\n deletedOrphanTodoCount = result.deletedCount\n }\n\n return {\n success: true,\n deletedCount: deletedSessionCount,\n removedMessageCount,\n deletedOrphanAgentCount,\n deletedOrphanTodoCount,\n } satisfies ClearSessionsResult\n })\n\n// Search sessions - two-phase: title search (fast) then content search (slow)\nexport const searchSessions = (\n query: string,\n options: { projectName?: string; searchContent?: boolean } = {}\n) =>\n Effect.gen(function* () {\n const { projectName, searchContent = false } = options\n const results: SearchResult[] = []\n const queryLower = query.toLowerCase()\n\n const projects = yield* listProjects\n const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects\n\n // Phase 1: Title search (fast)\n for (const project of targetProjects) {\n const sessions = yield* listSessions(project.name)\n\n for (const session of sessions) {\n const titleLower = (session.title ?? '').toLowerCase()\n if (titleLower.includes(queryLower)) {\n results.push({\n sessionId: session.id,\n projectName: project.name,\n title: session.title ?? 'Untitled',\n matchType: 'title',\n timestamp: session.updatedAt,\n })\n }\n }\n }\n\n // Phase 2: Content search (slow, optional)\n if (searchContent) {\n for (const project of targetProjects) {\n const projectPath = path.join(getSessionsDir(), project.name)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const file of sessionFiles) {\n const sessionId = file.replace('.jsonl', '')\n\n // Skip if already found in title search\n if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {\n continue\n }\n\n const filePath = path.join(projectPath, file)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n\n for (const line of lines) {\n try {\n const msg = JSON.parse(line) as Message\n if (msg.type !== 'user' && msg.type !== 'assistant') continue\n\n const text = extractTextContent(msg.message)\n const textLower = text.toLowerCase()\n\n if (textLower.includes(queryLower)) {\n // Extract snippet around match\n const matchIndex = textLower.indexOf(queryLower)\n const start = Math.max(0, matchIndex - 50)\n const end = Math.min(text.length, matchIndex + query.length + 50)\n const snippet =\n (start > 0 ? '...' : '') +\n text.slice(start, end).trim() +\n (end < text.length ? '...' : '')\n\n results.push({\n sessionId,\n projectName: project.name,\n title:\n extractTitle(extractTextContent(msg.message)) ||\n `Session ${sessionId.slice(0, 8)}`,\n matchType: 'content',\n snippet,\n messageUuid: msg.uuid,\n timestamp: msg.timestamp,\n })\n break // One match per session is enough\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n }\n }\n }\n\n // Sort by timestamp (newest first)\n return results.sort((a, b) => {\n const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0\n const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0\n return dateB - dateA\n })\n })\n\n// Internal version that accepts summaries targeting this session\nconst loadSessionTreeDataInternal = (\n projectName: string,\n sessionId: string,\n summariesByTargetSession?: Map<string, SummaryInfo[]>\n) =>\n Effect.gen(function* () {\n const projectPath = path.join(getSessionsDir(), projectName)\n const filePath = path.join(projectPath, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as JsonlRecord)\n\n // Get summaries that TARGET this session (by leafUuid pointing to messages in this session)\n let summaries: SummaryInfo[]\n if (summariesByTargetSession) {\n // Project-wide loading: use pre-computed summaries targeting this session\n // Sort by timestamp ascending (oldest first, current/first summary at index 0)\n summaries = [...(summariesByTargetSession.get(sessionId) ?? [])].sort((a, b) =>\n (a.timestamp ?? '').localeCompare(b.timestamp ?? '')\n )\n } else {\n // Single session loading: need to search the entire project for summaries targeting this session\n summaries = []\n // Build uuid set for this session's messages\n const sessionUuids = new Set<string>()\n for (const msg of messages) {\n if (msg.uuid && typeof msg.uuid === 'string') {\n sessionUuids.add(msg.uuid)\n }\n }\n // Search all session files in the project for summaries with leafUuid pointing to this session\n const projectFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const allJsonlFiles = projectFiles.filter((f) => f.endsWith('.jsonl'))\n for (const file of allJsonlFiles) {\n try {\n const otherFilePath = path.join(projectPath, file)\n const otherContent = yield* Effect.tryPromise(() => fs.readFile(otherFilePath, 'utf-8'))\n const otherLines = otherContent.trim().split('\\n').filter(Boolean)\n for (const line of otherLines) {\n try {\n const msg = JSON.parse(line) as JsonlRecord\n if (\n msg.type === 'summary' &&\n typeof msg.summary === 'string' &&\n typeof msg.leafUuid === 'string' &&\n sessionUuids.has(msg.leafUuid)\n ) {\n // This summary's leafUuid points to a message in THIS session\n const targetMsg = messages.find((m) => m.uuid === msg.leafUuid)\n summaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid,\n timestamp:\n (targetMsg?.timestamp as string) ?? (msg.timestamp as string | undefined),\n })\n }\n } catch {\n // Skip invalid JSON\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n // Sort by timestamp ascending (oldest first, current/first summary at index 0)\n summaries.sort((a, b) => (a.timestamp ?? '').localeCompare(b.timestamp ?? ''))\n\n // Find last compact_boundary\n let lastCompactBoundaryUuid: string | undefined\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'system' && msg.subtype === 'compact_boundary') {\n lastCompactBoundaryUuid = msg.uuid as string\n break\n }\n }\n\n // Get first user message\n const firstUserMsg = messages.find((m) => m.type === 'user') as Message | undefined\n\n // customTitle is stored as separate custom-title type line\n const customTitleMsg = messages.find((m) => m.type === 'custom-title') as\n | { type: 'custom-title'; customTitle?: string }\n | undefined\n const customTitle = customTitleMsg?.customTitle\n\n // Get title from first user message\n const title = firstUserMsg\n ? extractTitle(extractTextContent(firstUserMsg.message))\n : summaries.length > 0\n ? '[Summary Only]'\n : `Session ${sessionId.slice(0, 8)}`\n\n // Count user/assistant messages\n const userAssistantMessages = messages.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n const firstMessage = userAssistantMessages[0]\n const lastMessage = userAssistantMessages[userAssistantMessages.length - 1]\n\n // Find linked agents\n const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId)\n\n // Load agent info (message counts)\n const agents: AgentInfo[] = []\n for (const agentId of linkedAgentIds) {\n const agentPath = path.join(projectPath, `${agentId}.jsonl`)\n try {\n const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))\n const agentLines = agentContent.trim().split('\\n').filter(Boolean)\n const agentMsgs = agentLines.map((l) => JSON.parse(l) as JsonlRecord)\n const agentUserAssistant = agentMsgs.filter(\n (m) => m.type === 'user' || m.type === 'assistant'\n )\n\n // Try to extract agent name from first message\n let agentName: string | undefined\n const firstAgentMsg = agentMsgs.find((m) => m.type === 'user')\n if (firstAgentMsg) {\n const text = extractTextContent(firstAgentMsg.message as Message['message'])\n if (text) {\n agentName = extractTitle(text)\n }\n }\n\n agents.push({\n id: agentId,\n name: agentName,\n messageCount: agentUserAssistant.length,\n })\n } catch {\n // Agent file might not exist or be readable\n agents.push({\n id: agentId,\n messageCount: 0,\n })\n }\n }\n\n // Load todos\n const todos = yield* findLinkedTodos(sessionId, linkedAgentIds)\n\n return {\n id: sessionId,\n projectName,\n title,\n customTitle,\n currentSummary: summaries[0]?.summary,\n messageCount:\n userAssistantMessages.length > 0\n ? userAssistantMessages.length\n : summaries.length > 0\n ? 1\n : 0,\n createdAt: (firstMessage?.timestamp as string) ?? undefined,\n updatedAt: (lastMessage?.timestamp as string) ?? undefined,\n summaries,\n agents,\n todos,\n lastCompactBoundaryUuid,\n } satisfies SessionTreeData\n })\n\n// Public wrapper for single session (without global uuid map, leafUuid lookup is limited)\nexport const loadSessionTreeData = (projectName: string, sessionId: string) =>\n loadSessionTreeDataInternal(projectName, sessionId, undefined)\n\n// Load all sessions tree data for a project\nexport const loadProjectTreeData = (projectName: string) =>\n Effect.gen(function* () {\n const project = (yield* listProjects).find((p) => p.name === projectName)\n if (!project) {\n return null\n }\n\n const projectPath = path.join(getSessionsDir(), projectName)\n const files = yield* Effect.tryPromise(() => fs.readdir(projectPath))\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n // Phase 1: Build global uuid map + collect all summaries from ALL sessions\n // This is needed because leafUuid can reference messages in other sessions\n const globalUuidMap = new Map<string, { sessionId: string; timestamp?: string }>()\n const allSummaries: Array<{ summary: string; leafUuid?: string; timestamp?: string }> = []\n\n // Read all .jsonl files (sessions + agents) to build uuid map and collect summaries\n const allJsonlFiles = files.filter((f) => f.endsWith('.jsonl'))\n yield* Effect.all(\n allJsonlFiles.map((file) =>\n Effect.gen(function* () {\n const filePath = path.join(projectPath, file)\n const fileSessionId = file.replace('.jsonl', '')\n try {\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n for (const line of lines) {\n try {\n const msg = JSON.parse(line) as JsonlRecord\n if (msg.uuid && typeof msg.uuid === 'string') {\n globalUuidMap.set(msg.uuid, {\n sessionId: fileSessionId,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n // Also check messageId for file-history-snapshot type\n if (msg.messageId && typeof msg.messageId === 'string') {\n globalUuidMap.set(msg.messageId, {\n sessionId: fileSessionId,\n timestamp: (msg.snapshot as Record<string, unknown> | undefined)?.timestamp as\n | string\n | undefined,\n })\n }\n // Collect summaries\n if (msg.type === 'summary' && typeof msg.summary === 'string') {\n allSummaries.push({\n summary: msg.summary as string,\n leafUuid: msg.leafUuid as string | undefined,\n timestamp: msg.timestamp as string | undefined,\n })\n }\n } catch {\n // Skip invalid JSON lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n ),\n { concurrency: 20 }\n )\n\n // Phase 1.5: Build summariesByTargetSession map\n // Each summary's leafUuid points to a message in some session - that's the TARGET session\n const summariesByTargetSession = new Map<string, SummaryInfo[]>()\n for (const summaryData of allSummaries) {\n if (summaryData.leafUuid) {\n const targetInfo = globalUuidMap.get(summaryData.leafUuid)\n if (targetInfo) {\n const targetSessionId = targetInfo.sessionId\n if (!summariesByTargetSession.has(targetSessionId)) {\n summariesByTargetSession.set(targetSessionId, [])\n }\n summariesByTargetSession.get(targetSessionId)!.push({\n summary: summaryData.summary,\n leafUuid: summaryData.leafUuid,\n timestamp: targetInfo.timestamp ?? summaryData.timestamp,\n })\n }\n }\n }\n\n // Phase 2: Load session tree data with summaries targeting each session\n const sessions = yield* Effect.all(\n sessionFiles.map((file) => {\n const sessionId = file.replace('.jsonl', '')\n return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession)\n }),\n { concurrency: 10 }\n )\n\n // Sort by newest first\n const sortedSessions = sessions.sort((a, b) => {\n const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0\n const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0\n return dateB - dateA\n })\n\n return {\n name: project.name,\n displayName: project.displayName,\n path: project.path,\n sessionCount: sessions.length,\n sessions: sortedSessions,\n } satisfies ProjectTreeData\n })\n\n// Update summary message in session\nexport const updateSessionSummary = (projectName: string, sessionId: string, newSummary: string) =>\n Effect.gen(function* () {\n const filePath = path.join(getSessionsDir(), projectName, `${sessionId}.jsonl`)\n const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))\n const lines = content.trim().split('\\n').filter(Boolean)\n const messages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)\n\n // Find existing summary message\n const summaryIdx = messages.findIndex((m) => m.type === 'summary')\n\n if (summaryIdx >= 0) {\n // Update existing summary\n messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary }\n } else {\n // Add new summary at the beginning\n const firstUserMsg = messages.find((m) => m.type === 'user')\n const summaryMsg = {\n type: 'summary',\n summary: newSummary,\n leafUuid: (firstUserMsg as Message | undefined)?.uuid ?? null,\n }\n messages.unshift(summaryMsg)\n }\n\n const newContent = messages.map((m) => JSON.stringify(m)).join('\\n') + '\\n'\n yield* Effect.tryPromise(() => fs.writeFile(filePath, newContent, 'utf-8'))\n\n return { success: true }\n })\n"],"mappings":"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","names":["logger","logger","basename","fs","path","Effect","fs","path","Effect","Effect","fs","path","Effect","stat","agentBackupDir","hasSummary","remainingCount"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-sessions/core",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Core library for Claude Code session management",
5
5
  "type": "module",
6
6
  "license": "MIT",