@claude-sessions/core 0.4.4 → 0.4.5-beta.0

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 CHANGED
@@ -33,14 +33,10 @@ const sessions = await Effect.runPromise(listSessions(projectName))
33
33
  const messages = await Effect.runPromise(readSession(projectName, sessionId))
34
34
 
35
35
  // Search sessions (title only)
36
- const titleResults = await Effect.runPromise(
37
- searchSessions('query', { searchContent: false })
38
- )
36
+ const titleResults = await Effect.runPromise(searchSessions('query', { searchContent: false }))
39
37
 
40
38
  // Search sessions (title + content)
41
- const allResults = await Effect.runPromise(
42
- searchSessions('query', { searchContent: true })
43
- )
39
+ const allResults = await Effect.runPromise(searchSessions('query', { searchContent: true }))
44
40
 
45
41
  // Rename a session
46
42
  await Effect.runPromise(renameSession(projectName, sessionId, 'New Title'))
@@ -53,51 +49,52 @@ await Effect.runPromise(deleteSession(projectName, sessionId))
53
49
 
54
50
  ### Session Operations
55
51
 
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 |
52
+ | Function | Description |
53
+ | ---------------------------------------------- | ----------------------------------- |
54
+ | `listProjects()` | List all Claude Code projects |
55
+ | `listSessions(projectName)` | List sessions in a project |
56
+ | `readSession(projectName, sessionId)` | Read all messages in a session |
57
+ | `searchSessions(query, options?)` | Search sessions by title or content |
58
+ | `renameSession(projectName, sessionId, title)` | Rename a session |
59
+ | `deleteSession(projectName, sessionId)` | Delete a session (backup) |
60
+ | `deleteMessage(projectName, sessionId, uuid)` | Delete a message |
61
+ | `splitSession(projectName, sessionId, uuid)` | Split session at a message |
62
+ | `moveSession(source, sessionId, target)` | Move session to another project |
63
+ | `getSessionFiles(projectName, sessionId)` | Get files changed in session |
64
+ | `previewCleanup(projectName?)` | Preview cleanup candidates |
65
+ | `clearSessions(options)` | Clear empty/invalid sessions |
70
66
 
71
67
  ### Path Utilities
72
68
 
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 |
69
+ | Function | Description |
70
+ | ----------------------------------- | ----------------------------------------------------------------- |
71
+ | `getSessionsDir()` | Get Claude Code sessions directory |
72
+ | `getTodosDir()` | Get Claude Code todos directory |
73
+ | `folderNameToDisplayPath(name)` | Convert folder name to display path |
74
+ | `pathToFolderName(path)` | Convert absolute path to folder name (official Claude Code logic) |
75
+ | `resolvePathFromClaudeConfig(name)` | Resolve folder name to path via ~/.claude.json |
79
76
 
80
77
  ### Message Utilities
81
78
 
82
- | Function | Description |
83
- |----------|-------------|
84
- | `extractTextContent(message)` | Extract text from message content |
85
- | `extractTitle(messages)` | Extract session title from messages |
79
+ | Function | Description |
80
+ | --------------------------------- | ------------------------------------------ |
81
+ | `extractTextContent(message)` | Extract text from message content |
82
+ | `extractTitle(messages)` | Extract session title from messages |
86
83
  | `isInvalidApiKeyMessage(message)` | Check if message has invalid API key error |
87
- | `isContinuationSummary(message)` | Check if message is a continuation summary |
84
+ | `isContinuationSummary(message)` | Check if message is a continuation summary |
88
85
 
89
86
  ### Agent & Todo Management
90
87
 
91
- | Function | Description |
92
- |----------|-------------|
88
+ | Function | Description |
89
+ | ------------------------------------------ | ----------------------------- |
93
90
  | `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 |
91
+ | `findOrphanAgents(projectName?)` | Find orphan agent files |
92
+ | `deleteOrphanAgents(projectName?)` | Delete orphan agents |
93
+ | `findLinkedTodos(projectName, sessionId)` | Find todos linked to session |
94
+ | `sessionHasTodos(projectName, sessionId)` | Check if session has todos |
95
+ | `findOrphanTodos(projectName?)` | Find orphan todo files |
96
+ | `deleteOrphanTodos(projectName?)` | Delete orphan todos |
100
97
 
101
98
  ## License
102
99
 
103
- MIT
100
+ MIT
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ interface FileSystem {
17
17
  declare const getSessionsDir: () => string;
18
18
  /** Get Claude todos directory (~/.claude/todos) */
19
19
  declare const getTodosDir: () => string;
20
+ /** Expand ~ path to absolute path with OS-native separators */
21
+ declare const expandHomePath: (tildePath: string, homeDir: string) => string;
20
22
  /**
21
23
  * Convert project folder name to display path
22
24
  * Unix: -home-user-projects -> /home/user/projects
@@ -32,8 +34,8 @@ declare const folderNameToDisplayPath: (folderName: string) => string;
32
34
  declare const displayPathToFolderName: (displayPath: string) => string;
33
35
  /**
34
36
  * Convert absolute path to project folder name
35
- * Non-ASCII characters are converted to '-' per character
36
- * Windows drive letter is normalized to lowercase (C: -> c--)
37
+ * All non-alphanumeric characters are converted to '-'
38
+ * Matches official Claude Code: A.replace(/[^a-zA-Z0-9]/g, '-')
37
39
  */
38
40
  declare const pathToFolderName: (absolutePath: string) => string;
39
41
  /**
@@ -48,7 +50,7 @@ declare const getRealPathFromSession: (folderName: string, sessionsDir?: string,
48
50
  * Convert folder name to relative or absolute path for display
49
51
  * If path is under home directory, show relative (~/...)
50
52
  */
51
- declare const folderNameToPath: (folderName: string) => string;
53
+ declare const folderNameToPath: (folderName: string) => Promise<string>;
52
54
  /**
53
55
  * Find project folder name that matches the given workspace path
54
56
  * Searches through all projects and checks if any session's cwd matches
@@ -662,4 +664,4 @@ declare const getLogger: () => Logger;
662
664
  */
663
665
  declare const createLogger: (namespace: string) => Logger;
664
666
 
665
- export { AgentInfo, type ChainError, CompressSessionOptions, ConversationLine, FileChange, type GenericMessage, type Logger, Message, MessagePayload$1 as MessagePayload, MoveSessionResult, type ProgressError, Project, ProjectTreeData, SearchResult, SessionIndexEntry, SessionSortOptions, SessionTodos, SessionsIndex, SummarizeSessionOptions, SummaryInfo, TREE_ICONS, type ToolUseResultError, type TreeItemType, type ValidationResult, analyzeSession, autoRepairChain, canMoveSession, clearSessions, compressSession, createLogger, deleteLinkedTodos, deleteMessage, deleteMessageWithChainRepair, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, extractProjectKnowledge, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, formatRelativeTime, generateTreeNodeId, getDisplayTitle, getIndexEntryDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionSortTimestamp, getSessionTooltip, getSessionsDir, getTodoIcon, getTodosDir, getTotalTodoCount, hasSessionsIndex, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, loadSessionsIndex, maskHomePath, moveSession, parseCommandMessage, parseJsonlLines, parseTreeNodeId, pathToFolderName, previewCleanup, readJsonlFile, readSession, renameSession, repairChain, repairParentUuidChain, restoreMessage, searchSessions, sessionHasSubItems, sessionHasTodos, setLogger, sortIndexEntriesByModified, sortProjects, splitSession, summarizeSession, tryParseJsonLine, updateSessionSummary, validateChain, validateProgressMessages, validateToolUseResult };
667
+ export { AgentInfo, type ChainError, CompressSessionOptions, ConversationLine, FileChange, type GenericMessage, type Logger, Message, MessagePayload$1 as MessagePayload, MoveSessionResult, type ProgressError, Project, ProjectTreeData, SearchResult, SessionIndexEntry, SessionSortOptions, SessionTodos, SessionsIndex, SummarizeSessionOptions, SummaryInfo, TREE_ICONS, type ToolUseResultError, type TreeItemType, type ValidationResult, analyzeSession, autoRepairChain, canMoveSession, clearSessions, compressSession, createLogger, deleteLinkedTodos, deleteMessage, deleteMessageWithChainRepair, deleteOrphanAgents, deleteOrphanTodos, deleteSession, displayPathToFolderName, expandHomePath, extractProjectKnowledge, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, formatRelativeTime, generateTreeNodeId, getDisplayTitle, getIndexEntryDisplayTitle, getLogger, getRealPathFromSession, getSessionFiles, getSessionSortTimestamp, getSessionTooltip, getSessionsDir, getTodoIcon, getTodosDir, getTotalTodoCount, hasSessionsIndex, isContinuationSummary, isInvalidApiKeyMessage, listProjects, listSessions, loadAgentMessages, loadProjectTreeData, loadSessionTreeData, loadSessionsIndex, maskHomePath, moveSession, parseCommandMessage, parseJsonlLines, parseTreeNodeId, pathToFolderName, previewCleanup, readJsonlFile, readSession, renameSession, repairChain, repairParentUuidChain, restoreMessage, searchSessions, sessionHasSubItems, sessionHasTodos, setLogger, sortIndexEntriesByModified, sortProjects, splitSession, summarizeSession, tryParseJsonLine, updateSessionSummary, validateChain, validateProgressMessages, validateToolUseResult };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/paths.ts
2
2
  import * as fs2 from "fs";
3
+ import * as fsp from "fs/promises";
3
4
  import * as os from "os";
4
5
  import * as path from "path";
5
6
 
@@ -238,8 +239,6 @@ var parseWindowsFolderName = (name) => {
238
239
  return match ? { isWindows: true, drive: match[1], rest: name.slice(3) } : { isWindows: false };
239
240
  };
240
241
  var isWindowsPath = (p) => WINDOWS_PATTERNS.absolutePath.test(p);
241
- var convertNonAscii = (str) => [...str].map((c) => c.charCodeAt(0) <= 127 ? c : "-").join("");
242
- var pathCharsToFolderName = (str) => str.replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-").replace(/\./g, "-");
243
242
  var separatorsToFolderName = (str) => str.replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-");
244
243
  var extractCwdFromContent = (content, filePath) => {
245
244
  const lines = content.split("\n").filter((l) => l.trim());
@@ -252,6 +251,11 @@ var extractCwdFromContent = (content, filePath) => {
252
251
  return null;
253
252
  };
254
253
  var isSessionFile = (filename) => filename.endsWith(".jsonl") && !filename.startsWith("agent-");
254
+ var expandHomePath = (tildePath, homeDir) => {
255
+ if (!tildePath.startsWith("~")) return tildePath;
256
+ const relativePart = tildePath.slice(1);
257
+ return path.join(homeDir, ...relativePart.split("/").filter(Boolean));
258
+ };
255
259
  var toRelativePath = (absolutePath, homeDir) => {
256
260
  const normalizedPath = absolutePath.replace(/\\/g, "/");
257
261
  const normalizedHome = homeDir.replace(/\\/g, "/");
@@ -263,11 +267,10 @@ var toRelativePath = (absolutePath, homeDir) => {
263
267
  }
264
268
  const startsWithHome = isWin ? pathLower.startsWith(homeLower + "/") : normalizedPath.startsWith(normalizedHome + "/");
265
269
  if (startsWithHome) {
266
- const sep = isWin ? "\\" : "/";
267
270
  const relativePart = absolutePath.slice(homeDir.length);
268
- return "~" + relativePart.replace(/[\\/]/g, sep);
271
+ return "~" + relativePart.replace(/\\/g, "/");
269
272
  }
270
- return absolutePath;
273
+ return absolutePath.replace(/\\/g, "/");
271
274
  };
272
275
  var folderNameToDisplayPath = (folderName) => {
273
276
  const parsed = parseWindowsFolderName(folderName);
@@ -283,13 +286,7 @@ var displayPathToFolderName = (displayPath) => {
283
286
  }
284
287
  return displayPath.replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-");
285
288
  };
286
- var pathToFolderName = (absolutePath) => {
287
- const parsed = parseWindowsAbsPath(absolutePath);
288
- if (parsed.isWindows) {
289
- return parsed.drive.toLowerCase() + "--" + pathCharsToFolderName(convertNonAscii(parsed.rest));
290
- }
291
- return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
292
- };
289
+ var pathToFolderName = (absolutePath) => absolutePath.replace(/[^a-zA-Z0-9]/g, "-");
293
290
  var tryGetCwdFromFile = (filePath, fileSystem = fs2, logger2 = log) => {
294
291
  const basename3 = path.basename(filePath);
295
292
  try {
@@ -323,20 +320,39 @@ var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSy
323
320
  logger2.warn(
324
321
  `getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(", ")}`
325
322
  );
326
- } else {
327
- logger2.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
328
323
  }
329
324
  return null;
330
325
  } catch {
331
326
  return null;
332
327
  }
333
328
  };
334
- var folderNameToPath = (folderName) => {
329
+ var getClaudeConfigPath = () => path.join(os.homedir(), ".claude.json");
330
+ var resolvePathFromClaudeConfig = async (folderName, fileSystem = fsp) => {
331
+ try {
332
+ const configPath = getClaudeConfigPath();
333
+ const content = await fileSystem.readFile(configPath, "utf-8");
334
+ const config = JSON.parse(content);
335
+ if (!config.projects) return null;
336
+ for (const projectPath of Object.keys(config.projects)) {
337
+ if (pathToFolderName(projectPath) === folderName) {
338
+ return projectPath;
339
+ }
340
+ }
341
+ return null;
342
+ } catch {
343
+ return null;
344
+ }
345
+ };
346
+ var folderNameToPath = async (folderName) => {
335
347
  const homeDir = os.homedir();
336
348
  const realPath = getRealPathFromSession(folderName);
337
349
  if (realPath) {
338
350
  return toRelativePath(realPath, homeDir);
339
351
  }
352
+ const configPath = await resolvePathFromClaudeConfig(folderName);
353
+ if (configPath) {
354
+ return toRelativePath(configPath, homeDir);
355
+ }
340
356
  const absolutePath = folderNameToDisplayPath(folderName);
341
357
  return toRelativePath(absolutePath, homeDir);
342
358
  };
@@ -808,9 +824,10 @@ var listProjects = Effect4.gen(function* () {
808
824
  const projectPath = path4.join(sessionsDir, entry.name);
809
825
  const files = yield* Effect4.tryPromise(() => fs5.readdir(projectPath));
810
826
  const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
827
+ const displayName = yield* Effect4.tryPromise(() => folderNameToPath(entry.name));
811
828
  return {
812
829
  name: entry.name,
813
- displayName: folderNameToPath(entry.name),
830
+ displayName,
814
831
  path: projectPath,
815
832
  sessionCount: sessionFiles.length
816
833
  };
@@ -2343,6 +2360,7 @@ export {
2343
2360
  deleteOrphanTodos,
2344
2361
  deleteSession,
2345
2362
  displayPathToFolderName,
2363
+ expandHomePath,
2346
2364
  extractProjectKnowledge,
2347
2365
  extractTextContent,
2348
2366
  extractTitle,