@claude-sessions/core 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @claude-sessions/core
2
+
3
+ Core library for Claude Code session management.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @claude-sessions/core
9
+ # or
10
+ pnpm add @claude-sessions/core
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { Effect } from 'effect'
17
+ import {
18
+ listProjects,
19
+ listSessions,
20
+ readSession,
21
+ searchSessions,
22
+ deleteSession,
23
+ renameSession,
24
+ } from '@claude-sessions/core'
25
+
26
+ // List all projects
27
+ const projects = await Effect.runPromise(listProjects())
28
+
29
+ // List sessions in a project
30
+ const sessions = await Effect.runPromise(listSessions(projectName))
31
+
32
+ // Read session messages
33
+ const messages = await Effect.runPromise(readSession(projectName, sessionId))
34
+
35
+ // Search sessions (title only)
36
+ const titleResults = await Effect.runPromise(
37
+ searchSessions('query', { searchContent: false })
38
+ )
39
+
40
+ // Search sessions (title + content)
41
+ const allResults = await Effect.runPromise(
42
+ searchSessions('query', { searchContent: true })
43
+ )
44
+
45
+ // Rename a session
46
+ await Effect.runPromise(renameSession(projectName, sessionId, 'New Title'))
47
+
48
+ // Delete a session (moves to backup)
49
+ await Effect.runPromise(deleteSession(projectName, sessionId))
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### Session Operations
55
+
56
+ | Function | Description |
57
+ |----------|-------------|
58
+ | `listProjects()` | List all Claude Code projects |
59
+ | `listSessions(projectName)` | List sessions in a project |
60
+ | `readSession(projectName, sessionId)` | Read all messages in a session |
61
+ | `searchSessions(query, options?)` | Search sessions by title or content |
62
+ | `renameSession(projectName, sessionId, title)` | Rename a session |
63
+ | `deleteSession(projectName, sessionId)` | Delete a session (backup) |
64
+ | `deleteMessage(projectName, sessionId, uuid)` | Delete a message |
65
+ | `splitSession(projectName, sessionId, uuid)` | Split session at a message |
66
+ | `moveSession(source, sessionId, target)` | Move session to another project |
67
+ | `getSessionFiles(projectName, sessionId)` | Get files changed in session |
68
+ | `previewCleanup(projectName?)` | Preview cleanup candidates |
69
+ | `clearSessions(options)` | Clear empty/invalid sessions |
70
+
71
+ ### Path Utilities
72
+
73
+ | Function | Description |
74
+ |----------|-------------|
75
+ | `getSessionsDir()` | Get Claude Code sessions directory |
76
+ | `getTodosDir()` | Get Claude Code todos directory |
77
+ | `folderNameToDisplayPath(name)` | Convert folder name to display path |
78
+ | `displayPathToFolderName(path)` | Convert display path to folder name |
79
+
80
+ ### Message Utilities
81
+
82
+ | Function | Description |
83
+ |----------|-------------|
84
+ | `extractTextContent(message)` | Extract text from message content |
85
+ | `extractTitle(messages)` | Extract session title from messages |
86
+ | `isInvalidApiKeyMessage(message)` | Check if message has invalid API key error |
87
+ | `isContinuationSummary(message)` | Check if message is a continuation summary |
88
+
89
+ ### Agent & Todo Management
90
+
91
+ | Function | Description |
92
+ |----------|-------------|
93
+ | `findLinkedAgents(projectName, sessionId)` | Find agents linked to session |
94
+ | `findOrphanAgents(projectName?)` | Find orphan agent files |
95
+ | `deleteOrphanAgents(projectName?)` | Delete orphan agents |
96
+ | `findLinkedTodos(projectName, sessionId)` | Find todos linked to session |
97
+ | `sessionHasTodos(projectName, sessionId)` | Check if session has todos |
98
+ | `findOrphanTodos(projectName?)` | Find orphan todo files |
99
+ | `deleteOrphanTodos(projectName?)` | Delete orphan todos |
100
+
101
+ ## License
102
+
103
+ MIT
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ interface Message {
24
24
  sessionId?: string;
25
25
  isCompactSummary?: boolean;
26
26
  customTitle?: string;
27
+ summary?: string;
27
28
  }
28
29
  interface SessionMeta {
29
30
  id: string;
@@ -102,6 +103,15 @@ interface CleanupPreview {
102
103
  orphanAgentCount?: number;
103
104
  orphanTodoCount?: number;
104
105
  }
106
+ interface SearchResult {
107
+ sessionId: string;
108
+ projectName: string;
109
+ title: string;
110
+ matchType: 'title' | 'content';
111
+ snippet?: string;
112
+ messageUuid?: string;
113
+ timestamp?: string;
114
+ }
105
115
 
106
116
  declare const getSessionsDir: () => string;
107
117
  declare const getTodosDir: () => string;
@@ -131,7 +141,7 @@ declare const deleteOrphanAgents: (projectName: string) => Effect.Effect<{
131
141
  count: number;
132
142
  }, effect_Cause.UnknownException, never>;
133
143
 
134
- declare const findLinkedTodos: (sessionId: string, agentIds: string[]) => Effect.Effect<{
144
+ declare const findLinkedTodos: (sessionId: string, agentIds?: string[]) => Effect.Effect<{
135
145
  sessionId: string;
136
146
  sessionTodos: TodoItem[];
137
147
  agentTodos: {
@@ -140,7 +150,7 @@ declare const findLinkedTodos: (sessionId: string, agentIds: string[]) => Effect
140
150
  }[];
141
151
  hasTodos: boolean;
142
152
  }, effect_Cause.UnknownException, never>;
143
- declare const sessionHasTodos: (sessionId: string, agentIds: string[]) => Effect.Effect<boolean, effect_Cause.UnknownException, never>;
153
+ declare const sessionHasTodos: (sessionId: string, agentIds?: string[]) => Effect.Effect<boolean, effect_Cause.UnknownException, never>;
144
154
  declare const deleteLinkedTodos: (sessionId: string, agentIds: string[]) => Effect.Effect<{
145
155
  deletedCount: number;
146
156
  }, effect_Cause.UnknownException, never>;
@@ -178,7 +188,7 @@ declare const deleteSession: (projectName: string, sessionId: string) => Effect.
178
188
  deletedAgents: number;
179
189
  deletedTodos: number;
180
190
  }, effect_Cause.UnknownException, never>;
181
- declare const renameSession: (projectName: string, sessionId: string, newTitle: string) => Effect.Effect<{
191
+ declare const renameSession: (projectName: string, sessionId: string, newTitle: string, newSummary?: string) => Effect.Effect<{
182
192
  success: false;
183
193
  error: string;
184
194
  } | {
@@ -243,5 +253,9 @@ declare const clearSessions: (options: {
243
253
  deletedOrphanAgentCount: number;
244
254
  deletedOrphanTodoCount: number;
245
255
  }, effect_Cause.UnknownException, never>;
256
+ declare const searchSessions: (query: string, options?: {
257
+ projectName?: string;
258
+ searchContent?: boolean;
259
+ }) => Effect.Effect<SearchResult[], effect_Cause.UnknownException, never>;
246
260
 
247
- export { type CleanupPreview, type ClearSessionsResult, type ContentItem, type DeleteSessionResult, type FileChange, type Message, type MessagePayload, type MoveSessionResult, type Project, type RenameSessionResult, type SessionFilesSummary, type SessionMeta, type SessionTodos, type SplitSessionResult, type TodoItem, clearSessions, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, folderNameToDisplayPath, folderNameToPath, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, sessionHasTodos, splitSession };
261
+ export { type CleanupPreview, type ClearSessionsResult, type ContentItem, type DeleteSessionResult, type FileChange, type Message, type MessagePayload, type MoveSessionResult, type Project, type RenameSessionResult, type SearchResult, type SessionFilesSummary, type SessionMeta, type SessionTodos, type SplitSessionResult, type TodoItem, clearSessions, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, folderNameToDisplayPath, folderNameToPath, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, searchSessions, sessionHasTodos, splitSession };
package/dist/index.js CHANGED
@@ -5,27 +5,49 @@ import * as path from "path";
5
5
  var getSessionsDir = () => path.join(os.homedir(), ".claude", "projects");
6
6
  var getTodosDir = () => path.join(os.homedir(), ".claude", "todos");
7
7
  var folderNameToDisplayPath = (folderName) => {
8
+ const windowsDriveMatch = folderName.match(/^([A-Za-z])--/);
9
+ if (windowsDriveMatch) {
10
+ const driveLetter = windowsDriveMatch[1];
11
+ const rest = folderName.slice(3);
12
+ return driveLetter + ":\\" + rest.replace(/--/g, "\\.").replace(/-/g, "\\");
13
+ }
8
14
  return folderName.replace(/^-/, "/").replace(/--/g, "/.").replace(/-/g, "/");
9
15
  };
10
16
  var displayPathToFolderName = (displayPath) => {
17
+ const windowsDriveMatch = displayPath.match(/^([A-Za-z]):[/\\]/);
18
+ if (windowsDriveMatch) {
19
+ const driveLetter = windowsDriveMatch[1];
20
+ const rest = displayPath.slice(3);
21
+ return driveLetter + "--" + rest.replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-");
22
+ }
11
23
  return displayPath.replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-");
12
24
  };
13
25
  var pathToFolderName = (absolutePath) => {
26
+ const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\]/);
27
+ if (windowsDriveMatch) {
28
+ const driveLetter = windowsDriveMatch[1];
29
+ const rest = absolutePath.slice(3);
30
+ return driveLetter + "--" + rest.replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-").replace(/\./g, "-");
31
+ }
14
32
  return absolutePath.replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
15
33
  };
16
34
  var folderNameToPath = (folderName) => {
17
35
  const realPath = getRealPathFromSession(folderName);
18
36
  if (realPath) {
19
37
  const home2 = os.homedir();
20
- if (realPath.startsWith(home2)) {
21
- return "~" + realPath.slice(home2.length);
38
+ const normalizedPath2 = realPath.replace(/\\/g, "/");
39
+ const normalizedHome2 = home2.replace(/\\/g, "/");
40
+ if (normalizedPath2.startsWith(normalizedHome2)) {
41
+ return "~" + normalizedPath2.slice(normalizedHome2.length);
22
42
  }
23
43
  return realPath;
24
44
  }
25
45
  const absolutePath = folderNameToDisplayPath(folderName);
26
46
  const home = os.homedir();
27
- if (absolutePath.startsWith(home)) {
28
- return "~" + absolutePath.slice(home.length);
47
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
48
+ const normalizedHome = home.replace(/\\/g, "/");
49
+ if (normalizedPath.startsWith(normalizedHome)) {
50
+ return "~" + normalizedPath.slice(normalizedHome.length);
29
51
  }
30
52
  return absolutePath;
31
53
  };
@@ -164,7 +186,7 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
164
186
  import { Effect as Effect2 } from "effect";
165
187
  import * as fs3 from "fs/promises";
166
188
  import * as path3 from "path";
167
- var findLinkedTodos = (sessionId, agentIds) => Effect2.gen(function* () {
189
+ var findLinkedTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
168
190
  const todosDir = getTodosDir();
169
191
  const exists = yield* Effect2.tryPromise(
170
192
  () => fs3.access(todosDir).then(() => true).catch(() => false)
@@ -189,8 +211,17 @@ var findLinkedTodos = (sessionId, agentIds) => Effect2.gen(function* () {
189
211
  } catch {
190
212
  }
191
213
  }
214
+ const allFiles = yield* Effect2.tryPromise(() => fs3.readdir(todosDir));
215
+ const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
216
+ const discoveredAgentIds = new Set(agentIds);
217
+ for (const file of allFiles) {
218
+ const match = file.match(agentTodoPattern);
219
+ if (match) {
220
+ discoveredAgentIds.add(`agent-${match[1]}`);
221
+ }
222
+ }
192
223
  const agentTodos = [];
193
- for (const agentId of agentIds) {
224
+ for (const agentId of discoveredAgentIds) {
194
225
  const shortAgentId = agentId.replace("agent-", "");
195
226
  const agentTodoPath = path3.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
196
227
  const agentTodoExists = yield* Effect2.tryPromise(
@@ -200,7 +231,9 @@ var findLinkedTodos = (sessionId, agentIds) => Effect2.gen(function* () {
200
231
  const content = yield* Effect2.tryPromise(() => fs3.readFile(agentTodoPath, "utf-8"));
201
232
  try {
202
233
  const todos = JSON.parse(content);
203
- agentTodos.push({ agentId, todos });
234
+ if (todos.length > 0) {
235
+ agentTodos.push({ agentId, todos });
236
+ }
204
237
  } catch {
205
238
  }
206
239
  }
@@ -213,7 +246,7 @@ var findLinkedTodos = (sessionId, agentIds) => Effect2.gen(function* () {
213
246
  hasTodos
214
247
  };
215
248
  });
216
- var sessionHasTodos = (sessionId, agentIds) => Effect2.gen(function* () {
249
+ var sessionHasTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
217
250
  const todosDir = getTodosDir();
218
251
  const exists = yield* Effect2.tryPromise(
219
252
  () => fs3.access(todosDir).then(() => true).catch(() => false)
@@ -231,7 +264,16 @@ var sessionHasTodos = (sessionId, agentIds) => Effect2.gen(function* () {
231
264
  } catch {
232
265
  }
233
266
  }
234
- for (const agentId of agentIds) {
267
+ const allFiles = yield* Effect2.tryPromise(() => fs3.readdir(todosDir));
268
+ const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
269
+ const discoveredAgentIds = new Set(agentIds);
270
+ for (const file of allFiles) {
271
+ const match = file.match(agentTodoPattern);
272
+ if (match) {
273
+ discoveredAgentIds.add(`agent-${match[1]}`);
274
+ }
275
+ }
276
+ for (const agentId of discoveredAgentIds) {
235
277
  const shortAgentId = agentId.replace("agent-", "");
236
278
  const agentTodoPath = path3.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
237
279
  const agentTodoExists = yield* Effect2.tryPromise(
@@ -482,7 +524,7 @@ var deleteSession = (projectName, sessionId) => Effect3.gen(function* () {
482
524
  deletedTodos: todosResult.deletedCount
483
525
  };
484
526
  });
485
- var renameSession = (projectName, sessionId, newTitle) => Effect3.gen(function* () {
527
+ var renameSession = (projectName, sessionId, newTitle, newSummary) => Effect3.gen(function* () {
486
528
  const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
487
529
  const content = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
488
530
  const lines = content.trim().split("\n").filter(Boolean);
@@ -508,6 +550,19 @@ var renameSession = (projectName, sessionId, newTitle) => Effect3.gen(function*
508
550
  ${cleanedText}`;
509
551
  }
510
552
  }
553
+ if (newSummary !== void 0) {
554
+ const summaryIdx = messages.findIndex((m) => m.type === "summary");
555
+ if (summaryIdx >= 0) {
556
+ messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary };
557
+ } else {
558
+ const summaryMsg = {
559
+ type: "summary",
560
+ summary: newSummary,
561
+ leafUuid: firstMsg.uuid
562
+ };
563
+ messages.unshift(summaryMsg);
564
+ }
565
+ }
511
566
  const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
512
567
  yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
513
568
  return { success: true };
@@ -611,6 +666,7 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect3.gen(f
611
666
  return { success: false, error: "Cannot split at first message" };
612
667
  }
613
668
  const newSessionId = crypto.randomUUID();
669
+ const summaryMessage = allMessages.find((m) => m.type === "summary");
614
670
  const splitMessage = allMessages[splitIndex];
615
671
  const shouldDuplicate = isContinuationSummary(splitMessage);
616
672
  let remainingMessages;
@@ -633,6 +689,13 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect3.gen(f
633
689
  }
634
690
  return updated;
635
691
  });
692
+ if (summaryMessage) {
693
+ const clonedSummary = {
694
+ ...summaryMessage,
695
+ leafUuid: updatedMovedMessages[0]?.uuid ?? null
696
+ };
697
+ updatedMovedMessages.unshift(clonedSummary);
698
+ }
636
699
  const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
637
700
  yield* Effect3.tryPromise(() => fs4.writeFile(filePath, remainingContent, "utf-8"));
638
701
  const newFilePath = path4.join(projectPath, `${newSessionId}.jsonl`);
@@ -824,6 +887,74 @@ var clearSessions = (options) => Effect3.gen(function* () {
824
887
  deletedOrphanTodoCount
825
888
  };
826
889
  });
890
+ var searchSessions = (query, options = {}) => Effect3.gen(function* () {
891
+ const { projectName, searchContent = false } = options;
892
+ const results = [];
893
+ const queryLower = query.toLowerCase();
894
+ const projects = yield* listProjects;
895
+ const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
896
+ for (const project of targetProjects) {
897
+ const sessions = yield* listSessions(project.name);
898
+ for (const session of sessions) {
899
+ const titleLower = (session.title ?? "").toLowerCase();
900
+ if (titleLower.includes(queryLower)) {
901
+ results.push({
902
+ sessionId: session.id,
903
+ projectName: project.name,
904
+ title: session.title ?? "Untitled",
905
+ matchType: "title",
906
+ timestamp: session.updatedAt
907
+ });
908
+ }
909
+ }
910
+ }
911
+ if (searchContent) {
912
+ for (const project of targetProjects) {
913
+ const projectPath = path4.join(getSessionsDir(), project.name);
914
+ const files = yield* Effect3.tryPromise(() => fs4.readdir(projectPath));
915
+ const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
916
+ for (const file of sessionFiles) {
917
+ const sessionId = file.replace(".jsonl", "");
918
+ if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {
919
+ continue;
920
+ }
921
+ const filePath = path4.join(projectPath, file);
922
+ const content = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
923
+ const lines = content.trim().split("\n").filter(Boolean);
924
+ for (const line of lines) {
925
+ try {
926
+ const msg = JSON.parse(line);
927
+ if (msg.type !== "user" && msg.type !== "assistant") continue;
928
+ const text = extractTextContent(msg.message);
929
+ const textLower = text.toLowerCase();
930
+ if (textLower.includes(queryLower)) {
931
+ const matchIndex = textLower.indexOf(queryLower);
932
+ const start = Math.max(0, matchIndex - 50);
933
+ const end = Math.min(text.length, matchIndex + query.length + 50);
934
+ const snippet = (start > 0 ? "..." : "") + text.slice(start, end).trim() + (end < text.length ? "..." : "");
935
+ results.push({
936
+ sessionId,
937
+ projectName: project.name,
938
+ title: extractTitle(extractTextContent(msg.message)) || `Session ${sessionId.slice(0, 8)}`,
939
+ matchType: "content",
940
+ snippet,
941
+ messageUuid: msg.uuid,
942
+ timestamp: msg.timestamp
943
+ });
944
+ break;
945
+ }
946
+ } catch {
947
+ }
948
+ }
949
+ }
950
+ }
951
+ }
952
+ return results.sort((a, b) => {
953
+ const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
954
+ const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
955
+ return dateB - dateA;
956
+ });
957
+ });
827
958
  export {
828
959
  clearSessions,
829
960
  deleteLinkedTodos,
@@ -853,6 +984,7 @@ export {
853
984
  previewCleanup,
854
985
  readSession,
855
986
  renameSession,
987
+ searchSessions,
856
988
  sessionHasTodos,
857
989
  splitSession
858
990
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/paths.ts","../src/utils.ts","../src/agents.ts","../src/todos.ts","../src/session.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n */\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\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// Convert project folder name to display path\n// e.g., -home-user-projects -> /home/user/projects\n// Handle dot-prefixed folders: --claude -> /.claude, -projects--vscode -> /projects/.vscode\nexport const folderNameToDisplayPath = (folderName: string): string => {\n return folderName\n .replace(/^-/, '/')\n .replace(/--/g, '/.') // double dash means dot-prefixed folder\n .replace(/-/g, '/')\n}\n\n// Convert display path to folder name (reverse of above)\nexport const displayPathToFolderName = (displayPath: string): string => {\n return displayPath\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/\\//g, '-')\n}\n\n// Convert absolute path to project folder name\n// e.g., /home/user/projects/.vscode -> -home-user-projects--vscode\n// e.g., /home/user/example.com -> -home-user-example-com\nexport const pathToFolderName = (absolutePath: string): string => {\n return absolutePath\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/\\//g, '-')\n .replace(/\\./g, '-') // dots in filenames become single dash\n}\n\n// Convert folder name to relative or absolute path for display\n// If path is under home directory, show relative (~/...)\n// Otherwise show absolute path\nexport const folderNameToPath = (folderName: string): string => {\n // First try to get real path from session cwd\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n const home = os.homedir()\n if (realPath.startsWith(home)) {\n return '~' + realPath.slice(home.length)\n }\n return realPath\n }\n\n // Fallback to pattern-based conversion\n const absolutePath = folderNameToDisplayPath(folderName)\n const home = os.homedir()\n\n if (absolutePath.startsWith(home)) {\n return '~' + absolutePath.slice(home.length)\n }\n return absolutePath\n}\n\n// Try to extract cwd from a single session file\nconst tryGetCwdFromFile = (filePath: string): string | null => {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const firstLine = content.split('\\n')[0]\n if (!firstLine) return null\n const message = JSON.parse(firstLine) as { cwd?: string }\n return message.cwd ?? null\n } catch {\n return null\n }\n}\n\n// Extract real cwd path from session files in a project\n// This handles edge cases like es~kr -> es.kr (tilde in hostname)\n// Iterates through all session files to find one with matching cwd\n// Returns null if no session has cwd matching the folder name (all sessions moved from other projects)\nexport const getRealPathFromSession = (folderName: string): string | null => {\n const projectDir = path.join(getSessionsDir(), folderName)\n\n try {\n const files = fs.readdirSync(projectDir)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const sessionFile of sessionFiles) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, sessionFile))\n // Verify cwd matches the folder name (session wasn't moved from another project)\n if (cwd && pathToFolderName(cwd) === folderName) {\n return cwd\n }\n }\n\n return null\n } catch {\n return null\n }\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\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 // Read agent todo files\n const agentTodos: { agentId: string; todos: TodoItem[] }[] = []\n\n for (const agentId of agentIds) {\n // Agent todo files are named: {sessionId}-{agentId}.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 agentTodos.push({ agentId, todos })\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)\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 // Check 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 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 { extractTextContent, extractTitle, isInvalidApiKeyMessage, isContinuationSummary } from './utils.js'\nimport { findLinkedAgents, findOrphanAgents, deleteOrphanAgents } from './agents.js'\nimport { deleteLinkedTodos, sessionHasTodos, findOrphanTodos, deleteOrphanTodos } 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} 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\nexport const renameSession = (projectName: string, sessionId: string, newTitle: 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) {\n return { success: false, error: 'Empty session' } satisfies RenameSessionResult\n }\n\n const messages = lines.map((line) => JSON.parse(line) as Message)\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]\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 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 // 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 // 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"],"mappings":"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","names":["home","fs","path","Effect","fs","path","Effect","Effect","fs","path","Effect","stat","agentBackupDir","hasSummary","remainingCount"]}
1
+ {"version":3,"sources":["../src/paths.ts","../src/utils.ts","../src/agents.ts","../src/todos.ts","../src/session.ts"],"sourcesContent":["/**\n * Path utilities for Claude Code session management\n */\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\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// 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, -projects--vscode -> /projects/.vscode\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 // Windows path: C--Users-david -> C:\\Users\\david\n const driveLetter = windowsDriveMatch[1]\n const rest = folderName.slice(3) // Remove \"C--\"\n return (\n driveLetter +\n ':\\\\' +\n rest\n .replace(/--/g, '\\\\.') // double dash means dot-prefixed folder\n .replace(/-/g, '\\\\')\n )\n }\n\n // Unix path\n return folderName\n .replace(/^-/, '/')\n .replace(/--/g, '/.') // double dash means dot-prefixed folder\n .replace(/-/g, '/')\n}\n\n// Convert display path to folder name (reverse of above)\nexport const displayPathToFolderName = (displayPath: string): string => {\n // Check if Windows path (contains backslash or starts with drive letter)\n const windowsDriveMatch = displayPath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n // Windows path: C:\\Users\\david -> C--Users-david\n const driveLetter = windowsDriveMatch[1]\n const rest = displayPath.slice(3) // Remove \"C:\\\"\n return (\n driveLetter +\n '--' +\n rest\n .replace(/[/\\\\]\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/[/\\\\]/g, '-')\n )\n }\n\n // Unix path\n return displayPath\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/\\//g, '-')\n}\n\n// Convert absolute path to project folder name\n// Unix: /home/user/projects/.vscode -> -home-user-projects--vscode\n// Unix: /home/user/example.com -> -home-user-example-com\n// Windows: C:\\Users\\david\\.vscode -> C--Users-david--vscode\nexport const pathToFolderName = (absolutePath: string): string => {\n // Check if Windows path\n const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\\\]/)\n if (windowsDriveMatch) {\n // Windows path: C:\\Users\\david -> C--Users-david\n const driveLetter = windowsDriveMatch[1]\n const rest = absolutePath.slice(3) // Remove \"C:\\\"\n return (\n driveLetter +\n '--' +\n rest\n .replace(/[/\\\\]\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/[/\\\\]/g, '-')\n .replace(/\\./g, '-') // dots in filenames become single dash\n )\n }\n\n // Unix path\n return absolutePath\n .replace(/^\\//g, '-')\n .replace(/\\/\\./g, '--') // dot-prefixed folder becomes double dash\n .replace(/\\//g, '-')\n .replace(/\\./g, '-') // dots in filenames become single dash\n}\n\n// Convert folder name to relative or absolute path for display\n// If path is under home directory, show relative (~/...)\n// Otherwise show absolute path\nexport const folderNameToPath = (folderName: string): string => {\n // First try to get real path from session cwd\n const realPath = getRealPathFromSession(folderName)\n if (realPath) {\n const home = os.homedir()\n // Normalize path separators for comparison\n const normalizedPath = realPath.replace(/\\\\/g, '/')\n const normalizedHome = home.replace(/\\\\/g, '/')\n if (normalizedPath.startsWith(normalizedHome)) {\n return '~' + normalizedPath.slice(normalizedHome.length)\n }\n return realPath\n }\n\n // Fallback to pattern-based conversion\n const absolutePath = folderNameToDisplayPath(folderName)\n const home = os.homedir()\n\n // Normalize path separators for comparison\n const normalizedPath = absolutePath.replace(/\\\\/g, '/')\n const normalizedHome = home.replace(/\\\\/g, '/')\n if (normalizedPath.startsWith(normalizedHome)) {\n return '~' + normalizedPath.slice(normalizedHome.length)\n }\n return absolutePath\n}\n\n// Try to extract cwd from a single session file\nconst tryGetCwdFromFile = (filePath: string): string | null => {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const firstLine = content.split('\\n')[0]\n if (!firstLine) return null\n const message = JSON.parse(firstLine) as { cwd?: string }\n return message.cwd ?? null\n } catch {\n return null\n }\n}\n\n// Extract real cwd path from session files in a project\n// This handles edge cases like es~kr -> es.kr (tilde in hostname)\n// Iterates through all session files to find one with matching cwd\n// Returns null if no session has cwd matching the folder name (all sessions moved from other projects)\nexport const getRealPathFromSession = (folderName: string): string | null => {\n const projectDir = path.join(getSessionsDir(), folderName)\n\n try {\n const files = fs.readdirSync(projectDir)\n const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'))\n\n for (const sessionFile of sessionFiles) {\n const cwd = tryGetCwdFromFile(path.join(projectDir, sessionFile))\n // Verify cwd matches the folder name (session wasn't moved from another project)\n if (cwd && pathToFolderName(cwd) === folderName) {\n return cwd\n }\n }\n\n return null\n } catch {\n return null\n }\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 { deleteLinkedTodos, sessionHasTodos, findOrphanTodos, deleteOrphanTodos } 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} 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 summary message to clone to new session\n const summaryMessage = allMessages.find((m) => m.type === 'summary')\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"],"mappings":"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","names":["home","normalizedPath","normalizedHome","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.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Core library for Claude Code session management",
5
5
  "type": "module",
6
6
  "license": "MIT",