@claude-sessions/core 0.4.4 → 0.4.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -40
- package/dist/index.d.ts +7 -10
- package/dist/index.js +40 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
57
|
-
|
|
58
|
-
| `listProjects()`
|
|
59
|
-
| `listSessions(projectName)`
|
|
60
|
-
| `readSession(projectName, sessionId)`
|
|
61
|
-
| `searchSessions(query, options?)`
|
|
62
|
-
| `renameSession(projectName, sessionId, title)` | Rename a session
|
|
63
|
-
| `deleteSession(projectName, sessionId)`
|
|
64
|
-
| `deleteMessage(projectName, sessionId, uuid)`
|
|
65
|
-
| `splitSession(projectName, sessionId, uuid)`
|
|
66
|
-
| `moveSession(source, sessionId, target)`
|
|
67
|
-
| `getSessionFiles(projectName, sessionId)`
|
|
68
|
-
| `previewCleanup(projectName?)`
|
|
69
|
-
| `clearSessions(options)`
|
|
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
|
|
74
|
-
|
|
75
|
-
| `getSessionsDir()`
|
|
76
|
-
| `getTodosDir()`
|
|
77
|
-
| `folderNameToDisplayPath(name)`
|
|
78
|
-
| `
|
|
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
|
|
83
|
-
|
|
84
|
-
| `extractTextContent(message)`
|
|
85
|
-
| `extractTitle(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)`
|
|
84
|
+
| `isContinuationSummary(message)` | Check if message is a continuation summary |
|
|
88
85
|
|
|
89
86
|
### Agent & Todo Management
|
|
90
87
|
|
|
91
|
-
| Function
|
|
92
|
-
|
|
88
|
+
| Function | Description |
|
|
89
|
+
| ------------------------------------------ | ----------------------------- |
|
|
93
90
|
| `findLinkedAgents(projectName, sessionId)` | Find agents linked to session |
|
|
94
|
-
| `findOrphanAgents(projectName?)`
|
|
95
|
-
| `deleteOrphanAgents(projectName?)`
|
|
96
|
-
| `findLinkedTodos(projectName, sessionId)`
|
|
97
|
-
| `sessionHasTodos(projectName, sessionId)`
|
|
98
|
-
| `findOrphanTodos(projectName?)`
|
|
99
|
-
| `deleteOrphanTodos(projectName?)`
|
|
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
|
|
@@ -24,16 +26,10 @@ declare const getTodosDir: () => string;
|
|
|
24
26
|
* Handle dot-prefixed folders: --claude -> /.claude
|
|
25
27
|
*/
|
|
26
28
|
declare const folderNameToDisplayPath: (folderName: string) => string;
|
|
27
|
-
/**
|
|
28
|
-
* Convert display path to folder name (reverse of folderNameToDisplayPath)
|
|
29
|
-
* @deprecated Use pathToFolderName for absolute paths. This function exists
|
|
30
|
-
* for roundtrip testing and does not handle non-ASCII or normalize drive case.
|
|
31
|
-
*/
|
|
32
|
-
declare const displayPathToFolderName: (displayPath: string) => string;
|
|
33
29
|
/**
|
|
34
30
|
* Convert absolute path to project folder name
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* All non-alphanumeric characters are converted to '-'
|
|
32
|
+
* Matches official Claude Code: A.replace(/[^a-zA-Z0-9]/g, '-')
|
|
37
33
|
*/
|
|
38
34
|
declare const pathToFolderName: (absolutePath: string) => string;
|
|
39
35
|
/**
|
|
@@ -48,7 +44,7 @@ declare const getRealPathFromSession: (folderName: string, sessionsDir?: string,
|
|
|
48
44
|
* Convert folder name to relative or absolute path for display
|
|
49
45
|
* If path is under home directory, show relative (~/...)
|
|
50
46
|
*/
|
|
51
|
-
declare const folderNameToPath: (folderName: string) => string
|
|
47
|
+
declare const folderNameToPath: (folderName: string) => Promise<string>;
|
|
52
48
|
/**
|
|
53
49
|
* Find project folder name that matches the given workspace path
|
|
54
50
|
* Searches through all projects and checks if any session's cwd matches
|
|
@@ -535,6 +531,7 @@ interface ProgressError {
|
|
|
535
531
|
line: number;
|
|
536
532
|
hookEvent?: string;
|
|
537
533
|
hookName?: string;
|
|
534
|
+
messageType?: string;
|
|
538
535
|
}
|
|
539
536
|
interface ValidationResult {
|
|
540
537
|
valid: boolean;
|
|
@@ -662,4 +659,4 @@ declare const getLogger: () => Logger;
|
|
|
662
659
|
*/
|
|
663
660
|
declare const createLogger: (namespace: string) => Logger;
|
|
664
661
|
|
|
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,
|
|
662
|
+
export { AgentInfo, type ChainError, CompressSessionOptions, ConversationLine, FileChange, type GenericMessage, type Logger, Message, MessagePayload$1 as MessagePayload, MoveSessionResult, type ProgressError, Project, ProjectTreeData, SearchResult, SessionIndexEntry, SessionSortOptions, SessionTodos, SessionsIndex, SummarizeSessionOptions, SummaryInfo, TREE_ICONS, type ToolUseResultError, type TreeItemType, type ValidationResult, analyzeSession, autoRepairChain, canMoveSession, clearSessions, compressSession, createLogger, deleteLinkedTodos, deleteMessage, deleteMessageWithChainRepair, deleteOrphanAgents, deleteOrphanTodos, deleteSession, expandHomePath, extractProjectKnowledge, extractTextContent, extractTitle, findLinkedAgents, findLinkedTodos, findOrphanAgents, findOrphanTodos, findProjectByWorkspacePath, folderNameToDisplayPath, folderNameToPath, formatRelativeTime, generateTreeNodeId, 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
|
|
|
@@ -229,18 +230,11 @@ var WINDOWS_PATTERNS = {
|
|
|
229
230
|
/** Matches Windows folder name format: C-- */
|
|
230
231
|
folderName: /^([A-Za-z])--/
|
|
231
232
|
};
|
|
232
|
-
var parseWindowsAbsPath = (p) => {
|
|
233
|
-
const match = p.match(WINDOWS_PATTERNS.absolutePath);
|
|
234
|
-
return match ? { isWindows: true, drive: match[1], rest: p.slice(3) } : { isWindows: false };
|
|
235
|
-
};
|
|
236
233
|
var parseWindowsFolderName = (name) => {
|
|
237
234
|
const match = name.match(WINDOWS_PATTERNS.folderName);
|
|
238
235
|
return match ? { isWindows: true, drive: match[1], rest: name.slice(3) } : { isWindows: false };
|
|
239
236
|
};
|
|
240
237
|
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
|
-
var separatorsToFolderName = (str) => str.replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-");
|
|
244
238
|
var extractCwdFromContent = (content, filePath) => {
|
|
245
239
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
246
240
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -252,6 +246,11 @@ var extractCwdFromContent = (content, filePath) => {
|
|
|
252
246
|
return null;
|
|
253
247
|
};
|
|
254
248
|
var isSessionFile = (filename) => filename.endsWith(".jsonl") && !filename.startsWith("agent-");
|
|
249
|
+
var expandHomePath = (tildePath, homeDir) => {
|
|
250
|
+
if (!tildePath.startsWith("~")) return tildePath;
|
|
251
|
+
const relativePart = tildePath.slice(1);
|
|
252
|
+
return path.join(homeDir, ...relativePart.split("/").filter(Boolean));
|
|
253
|
+
};
|
|
255
254
|
var toRelativePath = (absolutePath, homeDir) => {
|
|
256
255
|
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
257
256
|
const normalizedHome = homeDir.replace(/\\/g, "/");
|
|
@@ -263,11 +262,10 @@ var toRelativePath = (absolutePath, homeDir) => {
|
|
|
263
262
|
}
|
|
264
263
|
const startsWithHome = isWin ? pathLower.startsWith(homeLower + "/") : normalizedPath.startsWith(normalizedHome + "/");
|
|
265
264
|
if (startsWithHome) {
|
|
266
|
-
const sep = isWin ? "\\" : "/";
|
|
267
265
|
const relativePart = absolutePath.slice(homeDir.length);
|
|
268
|
-
return "~" + relativePart.replace(
|
|
266
|
+
return "~" + relativePart.replace(/\\/g, "/");
|
|
269
267
|
}
|
|
270
|
-
return absolutePath;
|
|
268
|
+
return absolutePath.replace(/\\/g, "/");
|
|
271
269
|
};
|
|
272
270
|
var folderNameToDisplayPath = (folderName) => {
|
|
273
271
|
const parsed = parseWindowsFolderName(folderName);
|
|
@@ -276,20 +274,7 @@ var folderNameToDisplayPath = (folderName) => {
|
|
|
276
274
|
}
|
|
277
275
|
return folderName.replace(/^-/, "/").replace(/--/g, "/.").replace(/-/g, "/");
|
|
278
276
|
};
|
|
279
|
-
var
|
|
280
|
-
const parsed = parseWindowsAbsPath(displayPath);
|
|
281
|
-
if (parsed.isWindows) {
|
|
282
|
-
return parsed.drive + "--" + separatorsToFolderName(parsed.rest);
|
|
283
|
-
}
|
|
284
|
-
return displayPath.replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-");
|
|
285
|
-
};
|
|
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
|
-
};
|
|
277
|
+
var pathToFolderName = (absolutePath) => absolutePath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
293
278
|
var tryGetCwdFromFile = (filePath, fileSystem = fs2, logger2 = log) => {
|
|
294
279
|
const basename3 = path.basename(filePath);
|
|
295
280
|
try {
|
|
@@ -323,20 +308,39 @@ var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSy
|
|
|
323
308
|
logger2.warn(
|
|
324
309
|
`getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(", ")}`
|
|
325
310
|
);
|
|
326
|
-
} else {
|
|
327
|
-
logger2.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
|
|
328
311
|
}
|
|
329
312
|
return null;
|
|
330
313
|
} catch {
|
|
331
314
|
return null;
|
|
332
315
|
}
|
|
333
316
|
};
|
|
334
|
-
var
|
|
317
|
+
var getClaudeConfigPath = () => path.join(os.homedir(), ".claude.json");
|
|
318
|
+
var resolvePathFromClaudeConfig = async (folderName, fileSystem = fsp) => {
|
|
319
|
+
try {
|
|
320
|
+
const configPath = getClaudeConfigPath();
|
|
321
|
+
const content = await fileSystem.readFile(configPath, "utf-8");
|
|
322
|
+
const config = JSON.parse(content);
|
|
323
|
+
if (!config.projects) return null;
|
|
324
|
+
for (const projectPath of Object.keys(config.projects)) {
|
|
325
|
+
if (pathToFolderName(projectPath) === folderName) {
|
|
326
|
+
return projectPath;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var folderNameToPath = async (folderName) => {
|
|
335
335
|
const homeDir = os.homedir();
|
|
336
336
|
const realPath = getRealPathFromSession(folderName);
|
|
337
337
|
if (realPath) {
|
|
338
338
|
return toRelativePath(realPath, homeDir);
|
|
339
339
|
}
|
|
340
|
+
const configPath = await resolvePathFromClaudeConfig(folderName);
|
|
341
|
+
if (configPath) {
|
|
342
|
+
return toRelativePath(configPath, homeDir);
|
|
343
|
+
}
|
|
340
344
|
const absolutePath = folderNameToDisplayPath(folderName);
|
|
341
345
|
return toRelativePath(absolutePath, homeDir);
|
|
342
346
|
};
|
|
@@ -808,9 +812,10 @@ var listProjects = Effect4.gen(function* () {
|
|
|
808
812
|
const projectPath = path4.join(sessionsDir, entry.name);
|
|
809
813
|
const files = yield* Effect4.tryPromise(() => fs5.readdir(projectPath));
|
|
810
814
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
815
|
+
const displayName = yield* Effect4.tryPromise(() => folderNameToPath(entry.name));
|
|
811
816
|
return {
|
|
812
817
|
name: entry.name,
|
|
813
|
-
displayName
|
|
818
|
+
displayName,
|
|
814
819
|
path: projectPath,
|
|
815
820
|
sessionCount: sessionFiles.length
|
|
816
821
|
};
|
|
@@ -899,6 +904,12 @@ function validateProgressMessages(messages) {
|
|
|
899
904
|
hookName
|
|
900
905
|
});
|
|
901
906
|
}
|
|
907
|
+
} else if (msg.type === "saved_hook_context") {
|
|
908
|
+
errors.push({
|
|
909
|
+
type: "unwanted_progress",
|
|
910
|
+
line: i + 1,
|
|
911
|
+
messageType: "saved_hook_context"
|
|
912
|
+
});
|
|
902
913
|
}
|
|
903
914
|
}
|
|
904
915
|
return {
|
|
@@ -2342,7 +2353,7 @@ export {
|
|
|
2342
2353
|
deleteOrphanAgents,
|
|
2343
2354
|
deleteOrphanTodos,
|
|
2344
2355
|
deleteSession,
|
|
2345
|
-
|
|
2356
|
+
expandHomePath,
|
|
2346
2357
|
extractProjectKnowledge,
|
|
2347
2358
|
extractTextContent,
|
|
2348
2359
|
extractTitle,
|