@claude-sessions/core 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,231 +1,8 @@
1
+ import { M as MessagePayload, a as Message, P as Project, T as TodoItem, F as FileChange, b as MoveSessionResult, S as SearchResult, c as SummaryInfo, A as AgentInfo } from './types-C2fzbmg9.js';
2
+ export { i as CleanupPreview, h as ClearSessionsResult, C as ContentItem, D as DeleteSessionResult, k as ProjectTreeData, R as RenameSessionResult, l as ResumeSessionOptions, m as ResumeSessionResult, f as SessionFilesSummary, d as SessionMeta, e as SessionTodos, j as SessionTreeData, g as SplitSessionResult } from './types-C2fzbmg9.js';
1
3
  import * as effect_Cause from 'effect/Cause';
2
4
  import { Effect } from 'effect';
3
5
 
4
- /**
5
- * Core types for Claude Code session management
6
- */
7
- /**
8
- * Base interface for objects with a type discriminator.
9
- * Used as a foundation for message and content types.
10
- */
11
- interface TypedObject {
12
- type: string;
13
- }
14
- /**
15
- * Text content block in a message.
16
- * @see https://docs.anthropic.com/en/api/messages
17
- */
18
- interface TextContent {
19
- type: 'text';
20
- text: string;
21
- }
22
- /**
23
- * Tool result content returned after tool execution.
24
- * Contains the output from a tool_use request.
25
- */
26
- interface ToolResultContent {
27
- type: 'tool_result';
28
- tool_use_id: string;
29
- content: string;
30
- is_error?: boolean;
31
- }
32
- /**
33
- * Tool use request from the assistant.
34
- * Represents a function call with input parameters.
35
- */
36
- interface ToolUseContent {
37
- type: 'tool_use';
38
- id: string;
39
- name: string;
40
- input: unknown;
41
- }
42
- /**
43
- * Union type for all message content blocks.
44
- * Includes known types and a fallback for unknown content types.
45
- */
46
- type ContentItem = TextContent | ToolResultContent | ToolUseContent | {
47
- type: string;
48
- text?: string;
49
- name?: string;
50
- input?: unknown;
51
- content?: string;
52
- };
53
- /**
54
- * Message payload containing role and content.
55
- * Matches Anthropic API message structure.
56
- */
57
- interface MessagePayload {
58
- role?: string;
59
- content?: ContentItem[] | string;
60
- model?: string;
61
- }
62
- /**
63
- * A single message in a Claude Code session.
64
- * Stored as a line in a JSONL session file.
65
- */
66
- interface Message extends TypedObject {
67
- /** Unique identifier for this message */
68
- uuid: string;
69
- /** Parent message UUID for conversation threading */
70
- parentUuid?: string | null;
71
- /** Session ID this message belongs to */
72
- sessionId?: string;
73
- /** Timestamp in ISO format */
74
- timestamp?: string;
75
- /** Message content (nested payload structure) */
76
- message?: MessagePayload;
77
- /** Direct content (alternative to message.content) */
78
- content?: ContentItem[] | string;
79
- /** User-defined custom title for this message */
80
- customTitle?: string;
81
- /** Summary text for summary-type messages */
82
- summary?: string;
83
- /** Flag indicating this is a context continuation summary */
84
- isCompactSummary?: boolean;
85
- /** Tool use result text (for tool_result messages) */
86
- toolUseResult?: string;
87
- }
88
- /** Basic metadata for a session, used in listings and summaries */
89
- interface SessionMeta {
90
- id: string;
91
- projectName: string;
92
- title?: string;
93
- messageCount: number;
94
- createdAt?: string;
95
- updatedAt?: string;
96
- }
97
- /** Project directory information */
98
- interface Project {
99
- name: string;
100
- displayName: string;
101
- path: string;
102
- sessionCount: number;
103
- }
104
- /** A single todo item from TodoWrite tool */
105
- interface TodoItem {
106
- content: string;
107
- status: 'pending' | 'in_progress' | 'completed';
108
- /** Active form text shown during execution */
109
- activeForm?: string;
110
- }
111
- /** Aggregated todos for a session including agent todos */
112
- interface SessionTodos {
113
- sessionId: string;
114
- sessionTodos: TodoItem[];
115
- agentTodos: {
116
- agentId: string;
117
- todos: TodoItem[];
118
- }[];
119
- hasTodos: boolean;
120
- }
121
- /** A file modification recorded during a session */
122
- interface FileChange {
123
- path: string;
124
- action: 'created' | 'modified' | 'deleted';
125
- timestamp?: string;
126
- messageUuid?: string;
127
- }
128
- /** Summary of all file changes in a session */
129
- interface SessionFilesSummary {
130
- sessionId: string;
131
- projectName: string;
132
- files: FileChange[];
133
- totalChanges: number;
134
- }
135
- /** Result of deleting a session */
136
- interface DeleteSessionResult {
137
- success: boolean;
138
- backupPath?: string;
139
- deletedAgents: number;
140
- deletedTodos?: number;
141
- }
142
- /** Result of renaming a session */
143
- interface RenameSessionResult {
144
- success: boolean;
145
- error?: string;
146
- }
147
- /** Result of splitting a session at a message */
148
- interface SplitSessionResult {
149
- success: boolean;
150
- newSessionId?: string;
151
- newSessionPath?: string;
152
- movedMessageCount?: number;
153
- duplicatedSummary?: boolean;
154
- error?: string;
155
- }
156
- /** Result of moving a session to another project */
157
- interface MoveSessionResult {
158
- success: boolean;
159
- error?: string;
160
- }
161
- /** Result of clearing empty/invalid sessions */
162
- interface ClearSessionsResult {
163
- success: boolean;
164
- deletedCount: number;
165
- removedMessageCount?: number;
166
- deletedOrphanAgentCount?: number;
167
- deletedOrphanTodoCount?: number;
168
- }
169
- /** Preview of sessions that would be cleaned up */
170
- interface CleanupPreview {
171
- project: string;
172
- emptySessions: SessionMeta[];
173
- invalidSessions: SessionMeta[];
174
- emptyWithTodosCount?: number;
175
- orphanAgentCount?: number;
176
- orphanTodoCount?: number;
177
- }
178
- /** A search result matching a session or message */
179
- interface SearchResult {
180
- sessionId: string;
181
- projectName: string;
182
- title: string;
183
- matchType: 'title' | 'content';
184
- snippet?: string;
185
- messageUuid?: string;
186
- timestamp?: string;
187
- }
188
- /** Summary extracted from a session for display */
189
- interface SummaryInfo {
190
- summary: string;
191
- leafUuid?: string;
192
- timestamp?: string;
193
- }
194
- /** Agent information for tree display */
195
- interface AgentInfo {
196
- id: string;
197
- name?: string;
198
- messageCount: number;
199
- }
200
- /** Full session data for tree view rendering */
201
- interface SessionTreeData {
202
- id: string;
203
- projectName: string;
204
- /** First user message title */
205
- title: string;
206
- /** User-set custom title */
207
- customTitle?: string;
208
- /** Current (first) summary text for display/tooltip */
209
- currentSummary?: string;
210
- messageCount: number;
211
- createdAt?: string;
212
- updatedAt?: string;
213
- /** All summaries in reverse order (newest first) */
214
- summaries: SummaryInfo[];
215
- agents: AgentInfo[];
216
- todos: SessionTodos;
217
- /** UUID of last compact_boundary message */
218
- lastCompactBoundaryUuid?: string;
219
- }
220
- /** Project with all sessions for tree view */
221
- interface ProjectTreeData {
222
- name: string;
223
- displayName: string;
224
- path: string;
225
- sessionCount: number;
226
- sessions: SessionTreeData[];
227
- }
228
-
229
6
  interface Logger$1 {
230
7
  debug: (msg: string) => void;
231
8
  warn: (msg: string) => void;
@@ -266,6 +43,18 @@ declare const getRealPathFromSession: (folderName: string, sessionsDir?: string,
266
43
  * If path is under home directory, show relative (~/...)
267
44
  */
268
45
  declare const folderNameToPath: (folderName: string) => string;
46
+ /**
47
+ * Find project folder name that matches the given workspace path
48
+ * Searches through all projects and checks if any session's cwd matches
49
+ *
50
+ * @param workspacePath - Absolute path of current workspace
51
+ * @param projectNames - List of project folder names to search
52
+ * @param sessionsDir - Optional sessions directory for testing
53
+ * @param fileSystem - Optional FileSystem for testing
54
+ * @param logger - Optional Logger for testing
55
+ * @returns Matching project folder name or null
56
+ */
57
+ declare const findProjectByWorkspacePath: (workspacePath: string, projectNames: string[], sessionsDir?: string, fileSystem?: FileSystem, logger?: Logger$1) => string | null;
269
58
 
270
59
  /**
271
60
  * Utility functions for message processing
@@ -280,6 +69,22 @@ declare const isContinuationSummary: (msg: Message) => boolean;
280
69
  * Priority: customTitle > currentSummary (truncated) > title > fallback
281
70
  */
282
71
  declare const getDisplayTitle: (customTitle: string | undefined, currentSummary: string | undefined, title: string | undefined, maxLength?: number, fallback?: string) => string;
72
+ /**
73
+ * Mask home directory path with ~
74
+ * Only masks the specified homeDir, not other users' paths
75
+ */
76
+ declare const maskHomePath: (text: string, homeDir: string) => string;
77
+ /**
78
+ * Sort projects with priority:
79
+ * 1. Current project (if specified)
80
+ * 2. Current user's home directory subpaths
81
+ * 3. Others (alphabetically by displayName)
82
+ */
83
+ declare const sortProjects: (projects: Project[], options?: {
84
+ currentProjectName?: string | null;
85
+ homeDir?: string;
86
+ filterEmpty?: boolean;
87
+ }) => Project[];
283
88
 
284
89
  declare const findLinkedAgents: (projectName: string, sessionId: string) => Effect.Effect<string[], effect_Cause.UnknownException, never>;
285
90
  declare const findOrphanAgents: (projectName: string) => Effect.Effect<{
@@ -507,4 +312,4 @@ declare const getLogger: () => Logger;
507
312
  */
508
313
  declare const createLogger: (namespace: string) => Logger;
509
314
 
510
- export { type AgentInfo, type CleanupPreview, type ClearSessionsResult, type ContentItem, type DeleteSessionResult, type FileChange, type Logger, type Message, type MessagePayload, type MoveSessionResult, type Project, type ProjectTreeData, type RenameSessionResult, type SearchResult, type SessionFilesSummary, type SessionMeta, type SessionTodos, type SessionTreeData, type SplitSessionResult, type SummaryInfo, type TodoItem, clearSessions, createLogger, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, folderNameToDisplayPath, folderNameToPath, getDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, restoreMessage, searchSessions, sessionHasTodos, setLogger, splitSession, updateSessionSummary };
315
+ export { AgentInfo, FileChange, type Logger, Message, MessagePayload, MoveSessionResult, Project, SearchResult, SummaryInfo, TodoItem, clearSessions, createLogger, deleteLinkedTodos, deleteMessage, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, getDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionsDir, getTodosDir, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, maskHomePath, moveSession, pathToFolderName, previewCleanup, readSession, renameSession, restoreMessage, searchSessions, sessionHasTodos, setLogger, sortProjects, splitSession, updateSessionSummary };
package/dist/index.js CHANGED
@@ -135,6 +135,30 @@ var folderNameToPath = (folderName) => {
135
135
  const absolutePath = folderNameToDisplayPath(folderName);
136
136
  return toRelativePath(absolutePath, homeDir);
137
137
  };
138
+ var findProjectByWorkspacePath = (workspacePath, projectNames, sessionsDir = getSessionsDir(), fileSystem = fs, logger2 = log) => {
139
+ const directMatch = pathToFolderName(workspacePath);
140
+ if (projectNames.includes(directMatch)) {
141
+ return directMatch;
142
+ }
143
+ for (const projectName of projectNames) {
144
+ const projectDir = path.join(sessionsDir, projectName);
145
+ try {
146
+ const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
147
+ for (const f of files) {
148
+ const cwd = tryGetCwdFromFile(path.join(projectDir, f), fileSystem, logger2);
149
+ if (cwd === workspacePath) {
150
+ logger2.debug(
151
+ `findProjectByWorkspacePath: ${workspacePath} -> found in ${projectName} (moved session)`
152
+ );
153
+ return projectName;
154
+ }
155
+ }
156
+ } catch {
157
+ }
158
+ }
159
+ logger2.debug(`findProjectByWorkspacePath: ${workspacePath} -> no matching project found`);
160
+ return null;
161
+ };
138
162
 
139
163
  // src/utils.ts
140
164
  var logger = createLogger("utils");
@@ -186,22 +210,56 @@ var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallb
186
210
  if (title && title !== "Untitled") return title;
187
211
  return fallback;
188
212
  };
213
+ var replaceMessageContent = (msg, text) => ({
214
+ ...msg,
215
+ message: {
216
+ ...msg.message,
217
+ content: [{ type: "text", text }]
218
+ },
219
+ toolUseResult: void 0
220
+ });
189
221
  var cleanupSplitFirstMessage = (msg) => {
190
222
  const toolUseResult = msg.toolUseResult;
191
223
  if (!toolUseResult) return msg;
192
- const rejectionMarker = "The user provided the following reason for the rejection:";
193
- const rejectionIndex = toolUseResult.indexOf(rejectionMarker);
194
- if (rejectionIndex === -1) return msg;
195
- const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim();
196
- if (!text) return msg;
197
- return {
198
- ...msg,
199
- message: {
200
- ...msg.message,
201
- content: [{ type: "text", text }]
202
- },
203
- toolUseResult: void 0
204
- };
224
+ if (typeof toolUseResult === "object" && "answers" in toolUseResult) {
225
+ const answers = toolUseResult.answers;
226
+ const qaText = Object.entries(answers).map(([q, a]) => `Q: ${q}
227
+ A: ${a}`).join("\n\n");
228
+ return replaceMessageContent(msg, qaText);
229
+ }
230
+ if (typeof toolUseResult === "string") {
231
+ const rejectionMarker = "The user provided the following reason for the rejection:";
232
+ const rejectionIndex = toolUseResult.indexOf(rejectionMarker);
233
+ if (rejectionIndex === -1) return msg;
234
+ const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim();
235
+ if (!text) return msg;
236
+ return replaceMessageContent(msg, text);
237
+ }
238
+ return msg;
239
+ };
240
+ var maskHomePath = (text, homeDir) => {
241
+ if (!homeDir) return text;
242
+ const escapedHome = homeDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
243
+ const regex = new RegExp(`${escapedHome}(?=[/\\\\]|$)`, "g");
244
+ return text.replace(regex, "~");
245
+ };
246
+ var sortProjects = (projects, options = {}) => {
247
+ const { currentProjectName, homeDir, filterEmpty = true } = options;
248
+ const filtered = filterEmpty ? projects.filter((p) => p.sessionCount > 0) : projects;
249
+ const homeDirPrefix = homeDir ? pathToFolderName(homeDir) : null;
250
+ return filtered.sort((a, b) => {
251
+ if (currentProjectName) {
252
+ if (a.name === currentProjectName) return -1;
253
+ if (b.name === currentProjectName) return 1;
254
+ }
255
+ if (homeDirPrefix) {
256
+ const aIsUserHome = a.name.startsWith(homeDirPrefix);
257
+ const bIsUserHome = b.name.startsWith(homeDirPrefix);
258
+ if (aIsUserHome && !bIsUserHome) return -1;
259
+ if (!aIsUserHome && bIsUserHome) return 1;
260
+ }
261
+ return a.displayName.localeCompare(b.displayName);
262
+ });
205
263
  };
206
264
 
207
265
  // src/agents.ts
@@ -847,36 +905,40 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect3.gen(f
847
905
  const summaryMessage = summaryMessages.length > 0 ? summaryMessages[summaryMessages.length - 1] : null;
848
906
  const splitMessage = allMessages[splitIndex];
849
907
  const shouldDuplicate = isContinuationSummary(splitMessage);
850
- let remainingMessages;
851
- const movedMessages = allMessages.slice(splitIndex);
908
+ let keptMessages = allMessages.slice(splitIndex);
909
+ let movedMessages;
852
910
  if (shouldDuplicate) {
853
911
  const duplicatedMessage = {
854
912
  ...splitMessage,
855
913
  uuid: crypto.randomUUID(),
856
- sessionId
857
- // Keep original session ID
914
+ sessionId: newSessionId
858
915
  };
859
- remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage];
916
+ movedMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage];
860
917
  } else {
861
- remainingMessages = allMessages.slice(0, splitIndex);
918
+ movedMessages = allMessages.slice(0, splitIndex);
862
919
  }
863
- const updatedMovedMessages = movedMessages.map((msg, index) => {
864
- let updated = { ...msg, sessionId: newSessionId };
920
+ keptMessages = keptMessages.map((msg, index) => {
921
+ let updated = { ...msg };
865
922
  if (index === 0) {
866
923
  updated.parentUuid = null;
867
924
  updated = cleanupSplitFirstMessage(updated);
868
925
  }
869
926
  return updated;
870
927
  });
928
+ const updatedMovedMessages = movedMessages.map((msg) => ({
929
+ ...msg,
930
+ sessionId: newSessionId
931
+ }));
871
932
  if (summaryMessage) {
872
933
  const clonedSummary = {
873
934
  ...summaryMessage,
935
+ sessionId: newSessionId,
874
936
  leafUuid: updatedMovedMessages[0]?.uuid ?? null
875
937
  };
876
938
  updatedMovedMessages.unshift(clonedSummary);
877
939
  }
878
- const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
879
- yield* Effect3.tryPromise(() => fs4.writeFile(filePath, remainingContent, "utf-8"));
940
+ const keptContent = keptMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
941
+ yield* Effect3.tryPromise(() => fs4.writeFile(filePath, keptContent, "utf-8"));
880
942
  const newFilePath = path4.join(projectPath, `${newSessionId}.jsonl`);
881
943
  const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
882
944
  yield* Effect3.tryPromise(() => fs4.writeFile(newFilePath, newContent, "utf-8"));
@@ -1367,6 +1429,7 @@ export {
1367
1429
  findLinkedTodos,
1368
1430
  findOrphanAgents,
1369
1431
  findOrphanTodos,
1432
+ findProjectByWorkspacePath,
1370
1433
  folderNameToDisplayPath,
1371
1434
  folderNameToPath,
1372
1435
  getDisplayTitle,
@@ -1382,6 +1445,7 @@ export {
1382
1445
  loadAgentMessages,
1383
1446
  loadProjectTreeData,
1384
1447
  loadSessionTreeData,
1448
+ maskHomePath,
1385
1449
  moveSession,
1386
1450
  pathToFolderName,
1387
1451
  previewCleanup,
@@ -1391,6 +1455,7 @@ export {
1391
1455
  searchSessions,
1392
1456
  sessionHasTodos,
1393
1457
  setLogger,
1458
+ sortProjects,
1394
1459
  splitSession,
1395
1460
  updateSessionSummary
1396
1461
  };