@claude-sessions/web 0.3.7 → 0.4.1-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/build/client/_app/immutable/assets/0.D2FnNtrO.css +1 -0
- package/build/client/_app/immutable/assets/0.D2FnNtrO.css.br +0 -0
- package/build/client/_app/immutable/assets/0.D2FnNtrO.css.gz +0 -0
- package/build/client/_app/immutable/assets/Toast.CQSY_S6M.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{pOdkXMWy.js → B-Z9hXPk.js} +1 -1
- package/build/client/_app/immutable/chunks/B-Z9hXPk.js.br +0 -0
- package/build/client/_app/immutable/chunks/B-Z9hXPk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Ck-YPZB6.js → BAHJNsIv.js} +1 -1
- package/build/client/_app/immutable/chunks/BAHJNsIv.js.br +0 -0
- package/build/client/_app/immutable/chunks/BAHJNsIv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BUqdiQ8V.js +90 -0
- package/build/client/_app/immutable/chunks/BUqdiQ8V.js.br +0 -0
- package/build/client/_app/immutable/chunks/BUqdiQ8V.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dbjp-HdT.js → Bt_HBaBk.js} +1 -1
- package/build/client/_app/immutable/chunks/Bt_HBaBk.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bt_HBaBk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cr0eW1j3.js +1 -0
- package/build/client/_app/immutable/chunks/Cr0eW1j3.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cr0eW1j3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DOXVKi87.js +2 -0
- package/build/client/_app/immutable/chunks/DOXVKi87.js.br +0 -0
- package/build/client/_app/immutable/chunks/DOXVKi87.js.gz +0 -0
- package/build/client/_app/immutable/chunks/YXuXYbOb.js +1 -0
- package/build/client/_app/immutable/chunks/YXuXYbOb.js.br +0 -0
- package/build/client/_app/immutable/chunks/YXuXYbOb.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.B37sNVm4.js +2 -0
- package/build/client/_app/immutable/entry/app.B37sNVm4.js.br +0 -0
- package/build/client/_app/immutable/entry/app.B37sNVm4.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CUKKnspj.js +1 -0
- package/build/client/_app/immutable/entry/start.CUKKnspj.js.br +2 -0
- package/build/client/_app/immutable/entry/start.CUKKnspj.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.D4LDvqQb.js → 0.DD0gH91e.js} +1 -1
- package/build/client/_app/immutable/nodes/0.DD0gH91e.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.DD0gH91e.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.j1rqa9HA.js +1 -0
- package/build/client/_app/immutable/nodes/1.j1rqa9HA.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.j1rqa9HA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.5x79czX-.js +4 -0
- package/build/client/_app/immutable/nodes/2.5x79czX-.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.5x79czX-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js +4 -0
- package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/env.js +51 -2
- package/build/handler.js +46 -4
- package/build/index.js +25 -14
- package/build/server/chunks/0-BhPy9YZY.js +17 -0
- package/build/server/chunks/{0-DkD09WcK.js.map → 0-BhPy9YZY.js.map} +1 -1
- package/build/server/chunks/1-CTTD_wla.js +9 -0
- package/build/server/chunks/{1-CjtVgNYW.js.map → 1-CTTD_wla.js.map} +1 -1
- package/build/server/chunks/2-Bvyspg6i.js +9 -0
- package/build/server/chunks/{2-wstlfOP5.js.map → 2-Bvyspg6i.js.map} +1 -1
- package/build/server/chunks/3-BHD2c0Ph.js +9 -0
- package/build/server/chunks/{3-Cdn9_0OG.js.map → 3-BHD2c0Ph.js.map} +1 -1
- package/build/server/chunks/{InputModal-XaplAR5y.js → InputModal-CbpiMXy3.js} +3 -3
- package/build/server/chunks/InputModal-CbpiMXy3.js.map +1 -0
- package/build/server/chunks/{Toast-ihrVamsT.js → Toast-Dr8M-5Wk.js} +4 -4
- package/build/server/chunks/Toast-Dr8M-5Wk.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-COfbk473.js → _layout.svelte-CkIsNl4s.js} +7 -6
- package/build/server/chunks/_layout.svelte-CkIsNl4s.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CKSi7jlX.js → _page.svelte-BdKbLK1W.js} +8 -7
- package/build/server/chunks/_page.svelte-BdKbLK1W.js.map +1 -0
- package/build/server/chunks/{_page.svelte-C5Xngf9U.js → _page.svelte-DWKQDFXZ.js} +452 -240
- package/build/server/chunks/_page.svelte-DWKQDFXZ.js.map +1 -0
- package/build/server/chunks/{_server.ts-CFQ3_7g1.js → _server.ts-8OBR5eCA.js} +3 -2
- package/build/server/chunks/{_server.ts-CFQ3_7g1.js.map → _server.ts-8OBR5eCA.js.map} +1 -1
- package/build/server/chunks/_server.ts-B2LLvx6I.js +41 -0
- package/build/server/chunks/_server.ts-B2LLvx6I.js.map +1 -0
- package/build/server/chunks/{_server.ts-Bzg6xSYv.js → _server.ts-B4SnxrOX.js} +3 -2
- package/build/server/chunks/{_server.ts-Bzg6xSYv.js.map → _server.ts-B4SnxrOX.js.map} +1 -1
- package/build/server/chunks/{_server.ts-a85289si.js → _server.ts-B9gP-Tri.js} +7 -3
- package/build/server/chunks/_server.ts-B9gP-Tri.js.map +1 -0
- package/build/server/chunks/{_server.ts-IjEZJmtD.js → _server.ts-BQxGBeCg.js} +3 -2
- package/build/server/chunks/{_server.ts-IjEZJmtD.js.map → _server.ts-BQxGBeCg.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CF7wNACi.js → _server.ts-Bcdjougg.js} +3 -2
- package/build/server/chunks/{_server.ts-CF7wNACi.js.map → _server.ts-Bcdjougg.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CBabn3Le.js → _server.ts-BeWDhgJ_.js} +3 -2
- package/build/server/chunks/{_server.ts-CBabn3Le.js.map → _server.ts-BeWDhgJ_.js.map} +1 -1
- package/build/server/chunks/{_server.ts-bj28Awpj.js → _server.ts-CEGd9zNn.js} +3 -2
- package/build/server/chunks/_server.ts-CEGd9zNn.js.map +1 -0
- package/build/server/chunks/{_server.ts-QYg-pRKw.js → _server.ts-CMsFINkl.js} +3 -2
- package/build/server/chunks/{_server.ts-QYg-pRKw.js.map → _server.ts-CMsFINkl.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BNGntgHD.js → _server.ts-CUginV8c.js} +7 -3
- package/build/server/chunks/_server.ts-CUginV8c.js.map +1 -0
- package/build/server/chunks/{_server.ts-DRy1BtKz.js → _server.ts-Crip_Csr.js} +3 -2
- package/build/server/chunks/{_server.ts-DRy1BtKz.js.map → _server.ts-Crip_Csr.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C46vcb_V.js → _server.ts-CuAlKvWH.js} +3 -2
- package/build/server/chunks/{_server.ts-C46vcb_V.js.map → _server.ts-CuAlKvWH.js.map} +1 -1
- package/build/server/chunks/{_server.ts-msjVztXK.js → _server.ts-DfNIRtb5.js} +3 -2
- package/build/server/chunks/{_server.ts-msjVztXK.js.map → _server.ts-DfNIRtb5.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CBCUnjeX.js → _server.ts-DnfpkUoI.js} +3 -2
- package/build/server/chunks/{_server.ts-CBCUnjeX.js.map → _server.ts-DnfpkUoI.js.map} +1 -1
- package/build/server/chunks/_server.ts-cVOhu8ub.js +26 -0
- package/build/server/chunks/_server.ts-cVOhu8ub.js.map +1 -0
- package/build/server/chunks/{_server.ts-Dtvv8PMc.js → _server.ts-dXxEqPlK.js} +3 -2
- package/build/server/chunks/{_server.ts-Dtvv8PMc.js.map → _server.ts-dXxEqPlK.js.map} +1 -1
- package/build/server/chunks/{error.svelte-C43AeaaJ.js → error.svelte-BNCG_dZH.js} +3 -3
- package/build/server/chunks/{error.svelte-C43AeaaJ.js.map → error.svelte-BNCG_dZH.js.map} +1 -1
- package/build/server/chunks/exports-BXvEiaiv.js.map +1 -1
- package/build/server/chunks/{index2-Da0doXJY.js → index-BEaiIYry.js} +388 -4
- package/build/server/chunks/index-BEaiIYry.js.map +1 -0
- package/build/server/chunks/index-CoD1IJuy.js.map +1 -1
- package/build/server/chunks/{index-DzYX9r1_.js → index2-ybZwlzIk.js} +110 -116
- package/build/server/chunks/index2-ybZwlzIk.js.map +1 -0
- package/build/server/chunks/{index3-DkgTDgY2.js → index3-D8wHvUHX.js} +781 -597
- package/build/server/chunks/index3-D8wHvUHX.js.map +1 -0
- package/build/server/chunks/{index4-BUZEWk-I.js → index4-B6vRxXfo.js} +2 -2
- package/build/server/chunks/index4-B6vRxXfo.js.map +1 -0
- package/build/server/chunks/shared-server-DaWdgxVh.js +11 -0
- package/build/server/chunks/shared-server-DaWdgxVh.js.map +1 -0
- package/build/server/index.js +115 -439
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +21 -21
- package/build/server/manifest.js.map +1 -1
- package/dist/cli.js +26 -1
- package/package.json +19 -18
- package/build/client/_app/immutable/assets/0.DYXbBCWs.css +0 -1
- package/build/client/_app/immutable/assets/0.DYXbBCWs.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DYXbBCWs.css.gz +0 -0
- package/build/client/_app/immutable/chunks/CT0aV1AN.js +0 -90
- package/build/client/_app/immutable/chunks/CT0aV1AN.js.br +0 -0
- package/build/client/_app/immutable/chunks/CT0aV1AN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Ck-YPZB6.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ck-YPZB6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dbjp-HdT.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dbjp-HdT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/HaThayHP.js +0 -1
- package/build/client/_app/immutable/chunks/HaThayHP.js.br +0 -0
- package/build/client/_app/immutable/chunks/HaThayHP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/fuY-CvTV.js +0 -1
- package/build/client/_app/immutable/chunks/fuY-CvTV.js.br +0 -0
- package/build/client/_app/immutable/chunks/fuY-CvTV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/nUq5tIUU.js +0 -2
- package/build/client/_app/immutable/chunks/nUq5tIUU.js.br +0 -0
- package/build/client/_app/immutable/chunks/nUq5tIUU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/pOdkXMWy.js.br +0 -0
- package/build/client/_app/immutable/chunks/pOdkXMWy.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DMBaTVWt.js +0 -2
- package/build/client/_app/immutable/entry/app.DMBaTVWt.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DMBaTVWt.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.C3tcesOa.js +0 -1
- package/build/client/_app/immutable/entry/start.C3tcesOa.js.br +0 -2
- package/build/client/_app/immutable/entry/start.C3tcesOa.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.D4LDvqQb.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D4LDvqQb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.5BEtqK67.js +0 -1
- package/build/client/_app/immutable/nodes/1.5BEtqK67.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.5BEtqK67.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.C-xl-nxy.js +0 -4
- package/build/client/_app/immutable/nodes/2.C-xl-nxy.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.C-xl-nxy.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.BqDX5X09.js +0 -4
- package/build/client/_app/immutable/nodes/3.BqDX5X09.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BqDX5X09.js.gz +0 -0
- package/build/server/chunks/0-DkD09WcK.js +0 -17
- package/build/server/chunks/1-CjtVgNYW.js +0 -9
- package/build/server/chunks/2-wstlfOP5.js +0 -9
- package/build/server/chunks/3-Cdn9_0OG.js +0 -9
- package/build/server/chunks/InputModal-XaplAR5y.js.map +0 -1
- package/build/server/chunks/Toast-ihrVamsT.js.map +0 -1
- package/build/server/chunks/_layout.svelte-COfbk473.js.map +0 -1
- package/build/server/chunks/_page.svelte-C5Xngf9U.js.map +0 -1
- package/build/server/chunks/_page.svelte-CKSi7jlX.js.map +0 -1
- package/build/server/chunks/_server.ts-BNGntgHD.js.map +0 -1
- package/build/server/chunks/_server.ts-BbeQOMoJ.js +0 -19
- package/build/server/chunks/_server.ts-BbeQOMoJ.js.map +0 -1
- package/build/server/chunks/_server.ts-CHX8x48n.js +0 -27
- package/build/server/chunks/_server.ts-CHX8x48n.js.map +0 -1
- package/build/server/chunks/_server.ts-a85289si.js.map +0 -1
- package/build/server/chunks/_server.ts-bj28Awpj.js.map +0 -1
- package/build/server/chunks/index-DzYX9r1_.js.map +0 -1
- package/build/server/chunks/index2-Da0doXJY.js.map +0 -1
- package/build/server/chunks/index3-DkgTDgY2.js.map +0 -1
- package/build/server/chunks/index4-BUZEWk-I.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as fs2 from 'fs';
|
|
2
2
|
import * as os from 'os';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path5 from 'path';
|
|
4
4
|
import { Effect, pipe, Array as Array$1, Option } from 'effect';
|
|
5
|
-
import * as
|
|
5
|
+
import * as fs6 from 'fs/promises';
|
|
6
|
+
import * as crypto from 'crypto';
|
|
6
7
|
|
|
7
8
|
var consoleLogger = {
|
|
8
9
|
debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),
|
|
@@ -17,109 +18,6 @@ var createLogger = (namespace) => ({
|
|
|
17
18
|
warn: (msg, ...args) => currentLogger.warn(`[${namespace}] ${msg}`, ...args),
|
|
18
19
|
error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args)
|
|
19
20
|
});
|
|
20
|
-
var log = createLogger("paths");
|
|
21
|
-
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR || path4.join(os.homedir(), ".claude", "projects");
|
|
22
|
-
var getTodosDir = () => path4.join(os.homedir(), ".claude", "todos");
|
|
23
|
-
var extractCwdFromContent = (content) => {
|
|
24
|
-
const lines = content.split("\n").filter((l) => l.trim());
|
|
25
|
-
for (const line of lines) {
|
|
26
|
-
try {
|
|
27
|
-
const parsed = JSON.parse(line);
|
|
28
|
-
if (parsed?.cwd) {
|
|
29
|
-
return parsed.cwd;
|
|
30
|
-
}
|
|
31
|
-
} catch {
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
};
|
|
36
|
-
var isSessionFile = (filename) => filename.endsWith(".jsonl") && !filename.startsWith("agent-");
|
|
37
|
-
var toRelativePath = (absolutePath, homeDir) => {
|
|
38
|
-
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
39
|
-
const normalizedHome = homeDir.replace(/\\/g, "/");
|
|
40
|
-
if (normalizedPath === normalizedHome) {
|
|
41
|
-
return "~";
|
|
42
|
-
}
|
|
43
|
-
if (normalizedPath.startsWith(normalizedHome + "/")) {
|
|
44
|
-
return "~" + normalizedPath.slice(normalizedHome.length);
|
|
45
|
-
}
|
|
46
|
-
return absolutePath;
|
|
47
|
-
};
|
|
48
|
-
var folderNameToDisplayPath = (folderName) => {
|
|
49
|
-
const windowsDriveMatch = folderName.match(/^([A-Za-z])--/);
|
|
50
|
-
if (windowsDriveMatch) {
|
|
51
|
-
const driveLetter = windowsDriveMatch[1];
|
|
52
|
-
const rest = folderName.slice(3);
|
|
53
|
-
return driveLetter + ":\\" + rest.replace(/--/g, "\\.").replace(/-/g, "\\");
|
|
54
|
-
}
|
|
55
|
-
return folderName.replace(/^-/, "/").replace(/--/g, "/.").replace(/-/g, "/");
|
|
56
|
-
};
|
|
57
|
-
var pathToFolderName = (absolutePath) => {
|
|
58
|
-
const convertNonAscii = (str) => [...str].map((char) => char.charCodeAt(0) <= 127 ? char : "-").join("");
|
|
59
|
-
const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\]/);
|
|
60
|
-
if (windowsDriveMatch) {
|
|
61
|
-
const driveLetter = windowsDriveMatch[1].toLowerCase();
|
|
62
|
-
const rest = absolutePath.slice(3);
|
|
63
|
-
return driveLetter + "--" + convertNonAscii(rest).replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-").replace(/\./g, "-");
|
|
64
|
-
}
|
|
65
|
-
return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
|
|
66
|
-
};
|
|
67
|
-
var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
|
|
68
|
-
const basename3 = path4.basename(filePath);
|
|
69
|
-
try {
|
|
70
|
-
const content = fileSystem.readFileSync(filePath, "utf-8");
|
|
71
|
-
const cwd = extractCwdFromContent(content);
|
|
72
|
-
if (cwd === null) {
|
|
73
|
-
const lines = content.split("\n").filter((l) => l.trim());
|
|
74
|
-
if (lines.length === 0) {
|
|
75
|
-
logger2.debug(`tryGetCwdFromFile: ${basename3} -> empty file`);
|
|
76
|
-
} else {
|
|
77
|
-
logger2.debug(`tryGetCwdFromFile: ${basename3} -> no cwd found in ${lines.length} lines`);
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
return cwd;
|
|
82
|
-
} catch (e) {
|
|
83
|
-
logger2.warn(`tryGetCwdFromFile: ${basename3} -> read error: ${e}`);
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs, logger2 = log) => {
|
|
88
|
-
const projectDir = path4.join(sessionsDir, folderName);
|
|
89
|
-
try {
|
|
90
|
-
const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
|
|
91
|
-
const cwdList = [];
|
|
92
|
-
for (const f of files) {
|
|
93
|
-
const cwd = tryGetCwdFromFile(path4.join(projectDir, f), fileSystem, logger2);
|
|
94
|
-
if (cwd !== null) {
|
|
95
|
-
cwdList.push(cwd);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName);
|
|
99
|
-
if (matched) {
|
|
100
|
-
return matched;
|
|
101
|
-
}
|
|
102
|
-
if (cwdList.length > 0) {
|
|
103
|
-
logger2.warn(
|
|
104
|
-
`getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(", ")}`
|
|
105
|
-
);
|
|
106
|
-
} else {
|
|
107
|
-
logger2.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
|
|
108
|
-
}
|
|
109
|
-
return null;
|
|
110
|
-
} catch {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
var folderNameToPath = (folderName) => {
|
|
115
|
-
const homeDir = os.homedir();
|
|
116
|
-
const realPath = getRealPathFromSession(folderName);
|
|
117
|
-
if (realPath) {
|
|
118
|
-
return toRelativePath(realPath, homeDir);
|
|
119
|
-
}
|
|
120
|
-
const absolutePath = folderNameToDisplayPath(folderName);
|
|
121
|
-
return toRelativePath(absolutePath, homeDir);
|
|
122
|
-
};
|
|
123
21
|
var logger = createLogger("utils");
|
|
124
22
|
var extractTextContent = (message) => {
|
|
125
23
|
if (!message) return "";
|
|
@@ -137,14 +35,19 @@ var extractTextContent = (message) => {
|
|
|
137
35
|
}
|
|
138
36
|
return "";
|
|
139
37
|
};
|
|
38
|
+
var parseCommandMessage = (content) => {
|
|
39
|
+
const name = content?.match(/<command-name>([^<]+)<\/command-name>/)?.[1] ?? "";
|
|
40
|
+
const message = content?.match(/<command-message>([^<]+)<\/command-message>/)?.[1] ?? "";
|
|
41
|
+
return { name, message };
|
|
42
|
+
};
|
|
140
43
|
var extractTitle = (text) => {
|
|
141
44
|
if (!text) return "Untitled";
|
|
45
|
+
const { name } = parseCommandMessage(text);
|
|
46
|
+
if (name) return name;
|
|
142
47
|
let cleaned = text.replace(/<ide_[^>]*>[\s\S]*?<\/ide_[^>]*>/g, "").trim();
|
|
143
48
|
if (!cleaned) return "Untitled";
|
|
144
49
|
if (cleaned.includes("\n\n")) {
|
|
145
50
|
cleaned = cleaned.split("\n\n")[0];
|
|
146
|
-
} else if (cleaned.includes("\n")) {
|
|
147
|
-
cleaned = cleaned.split("\n")[0];
|
|
148
51
|
}
|
|
149
52
|
if (cleaned.length > 100) {
|
|
150
53
|
return cleaned.slice(0, 100) + "...";
|
|
@@ -177,7 +80,13 @@ var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallb
|
|
|
177
80
|
if (currentSummary) {
|
|
178
81
|
return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
|
|
179
82
|
}
|
|
180
|
-
if (title && title !== "Untitled")
|
|
83
|
+
if (title && title !== "Untitled") {
|
|
84
|
+
if (title.includes("<command-name>")) {
|
|
85
|
+
const { name } = parseCommandMessage(title);
|
|
86
|
+
if (name) return name;
|
|
87
|
+
}
|
|
88
|
+
return title;
|
|
89
|
+
}
|
|
181
90
|
return fallback;
|
|
182
91
|
};
|
|
183
92
|
var replaceMessageContent = (msg, text) => ({
|
|
@@ -213,6 +122,135 @@ var maskHomePath = (text, homeDir) => {
|
|
|
213
122
|
const regex = new RegExp(`${escapedHome}(?=[/\\\\]|$)`, "g");
|
|
214
123
|
return text.replace(regex, "~");
|
|
215
124
|
};
|
|
125
|
+
var getSessionSortTimestamp = (session) => {
|
|
126
|
+
const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt;
|
|
127
|
+
return timestampStr ? new Date(timestampStr).getTime() : 0;
|
|
128
|
+
};
|
|
129
|
+
var tryParseJsonLine = (line, lineNumber, filePath) => {
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(line);
|
|
132
|
+
} catch {
|
|
133
|
+
if (filePath) {
|
|
134
|
+
console.warn(`Skipping invalid JSON at line ${lineNumber} in ${filePath}`);
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var parseJsonlLines = (lines, filePath) => {
|
|
140
|
+
return lines.map((line, idx) => {
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(line);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
const err = e;
|
|
145
|
+
throw new Error(`Failed to parse line ${idx + 1} in ${filePath}: ${err.message}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
var readJsonlFile = (filePath) => Effect.gen(function* () {
|
|
150
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
151
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
152
|
+
return parseJsonlLines(lines, filePath);
|
|
153
|
+
});
|
|
154
|
+
var log = createLogger("paths");
|
|
155
|
+
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR || path5.join(os.homedir(), ".claude", "projects");
|
|
156
|
+
var getTodosDir = () => path5.join(os.homedir(), ".claude", "todos");
|
|
157
|
+
var extractCwdFromContent = (content, filePath) => {
|
|
158
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
159
|
+
for (let i = 0; i < lines.length; i++) {
|
|
160
|
+
const parsed = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
161
|
+
if (parsed?.cwd) {
|
|
162
|
+
return parsed.cwd;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
167
|
+
var isSessionFile = (filename) => filename.endsWith(".jsonl") && !filename.startsWith("agent-");
|
|
168
|
+
var toRelativePath = (absolutePath, homeDir) => {
|
|
169
|
+
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
170
|
+
const normalizedHome = homeDir.replace(/\\/g, "/");
|
|
171
|
+
if (normalizedPath === normalizedHome) {
|
|
172
|
+
return "~";
|
|
173
|
+
}
|
|
174
|
+
if (normalizedPath.startsWith(normalizedHome + "/")) {
|
|
175
|
+
return "~" + normalizedPath.slice(normalizedHome.length);
|
|
176
|
+
}
|
|
177
|
+
return absolutePath;
|
|
178
|
+
};
|
|
179
|
+
var folderNameToDisplayPath = (folderName) => {
|
|
180
|
+
const windowsDriveMatch = folderName.match(/^([A-Za-z])--/);
|
|
181
|
+
if (windowsDriveMatch) {
|
|
182
|
+
const driveLetter = windowsDriveMatch[1];
|
|
183
|
+
const rest = folderName.slice(3);
|
|
184
|
+
return driveLetter + ":\\" + rest.replace(/--/g, "\\.").replace(/-/g, "\\");
|
|
185
|
+
}
|
|
186
|
+
return folderName.replace(/^-/, "/").replace(/--/g, "/.").replace(/-/g, "/");
|
|
187
|
+
};
|
|
188
|
+
var pathToFolderName = (absolutePath) => {
|
|
189
|
+
const convertNonAscii = (str) => [...str].map((char) => char.charCodeAt(0) <= 127 ? char : "-").join("");
|
|
190
|
+
const windowsDriveMatch = absolutePath.match(/^([A-Za-z]):[/\\]/);
|
|
191
|
+
if (windowsDriveMatch) {
|
|
192
|
+
const driveLetter = windowsDriveMatch[1].toLowerCase();
|
|
193
|
+
const rest = absolutePath.slice(3);
|
|
194
|
+
return driveLetter + "--" + convertNonAscii(rest).replace(/[/\\]\./g, "--").replace(/[/\\]/g, "-").replace(/\./g, "-");
|
|
195
|
+
}
|
|
196
|
+
return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
|
|
197
|
+
};
|
|
198
|
+
var tryGetCwdFromFile = (filePath, fileSystem = fs2, logger2 = log) => {
|
|
199
|
+
const basename3 = path5.basename(filePath);
|
|
200
|
+
try {
|
|
201
|
+
const content = fileSystem.readFileSync(filePath, "utf-8");
|
|
202
|
+
const cwd = extractCwdFromContent(content);
|
|
203
|
+
if (cwd === null) {
|
|
204
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
205
|
+
if (lines.length === 0) {
|
|
206
|
+
logger2.debug(`tryGetCwdFromFile: ${basename3} -> empty file`);
|
|
207
|
+
} else {
|
|
208
|
+
logger2.debug(`tryGetCwdFromFile: ${basename3} -> no cwd found in ${lines.length} lines`);
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
return cwd;
|
|
213
|
+
} catch (e) {
|
|
214
|
+
logger2.warn(`tryGetCwdFromFile: ${basename3} -> read error: ${e}`);
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs2, logger2 = log) => {
|
|
219
|
+
const projectDir = path5.join(sessionsDir, folderName);
|
|
220
|
+
try {
|
|
221
|
+
const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
|
|
222
|
+
const cwdList = [];
|
|
223
|
+
for (const f of files) {
|
|
224
|
+
const cwd = tryGetCwdFromFile(path5.join(projectDir, f), fileSystem, logger2);
|
|
225
|
+
if (cwd !== null) {
|
|
226
|
+
cwdList.push(cwd);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const matched = cwdList.find((cwd) => pathToFolderName(cwd) === folderName);
|
|
230
|
+
if (matched) {
|
|
231
|
+
return matched;
|
|
232
|
+
}
|
|
233
|
+
if (cwdList.length > 0) {
|
|
234
|
+
logger2.warn(
|
|
235
|
+
`getRealPathFromSession: ${folderName} -> no match, cwds found: ${cwdList.join(", ")}`
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
logger2.warn(`getRealPathFromSession: ${folderName} -> no valid cwd in any session`);
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
var folderNameToPath = (folderName) => {
|
|
246
|
+
const homeDir = os.homedir();
|
|
247
|
+
const realPath = getRealPathFromSession(folderName);
|
|
248
|
+
if (realPath) {
|
|
249
|
+
return toRelativePath(realPath, homeDir);
|
|
250
|
+
}
|
|
251
|
+
const absolutePath = folderNameToDisplayPath(folderName);
|
|
252
|
+
return toRelativePath(absolutePath, homeDir);
|
|
253
|
+
};
|
|
216
254
|
var sortProjects = (projects, options = {}) => {
|
|
217
255
|
const { currentProjectName, homeDir, filterEmpty = true } = options;
|
|
218
256
|
const filtered = filterEmpty ? projects.filter((p) => p.sessionCount > 0) : projects;
|
|
@@ -232,13 +270,13 @@ var sortProjects = (projects, options = {}) => {
|
|
|
232
270
|
});
|
|
233
271
|
};
|
|
234
272
|
var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
235
|
-
const projectPath =
|
|
236
|
-
const files = yield* Effect.tryPromise(() =>
|
|
273
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
274
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
237
275
|
const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
238
276
|
const linkedAgents = [];
|
|
239
277
|
for (const agentFile of agentFiles) {
|
|
240
|
-
const filePath =
|
|
241
|
-
const content = yield* Effect.tryPromise(() =>
|
|
278
|
+
const filePath = path5.join(projectPath, agentFile);
|
|
279
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
242
280
|
const firstLine = content.split("\n")[0];
|
|
243
281
|
if (firstLine) {
|
|
244
282
|
try {
|
|
@@ -253,21 +291,21 @@ var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
253
291
|
return linkedAgents;
|
|
254
292
|
});
|
|
255
293
|
var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
256
|
-
const projectPath =
|
|
257
|
-
const files = yield* Effect.tryPromise(() =>
|
|
294
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
295
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
258
296
|
const sessionIds = new Set(
|
|
259
297
|
files.filter((f) => !f.startsWith("agent-") && f.endsWith(".jsonl")).map((f) => f.replace(".jsonl", ""))
|
|
260
298
|
);
|
|
261
299
|
const orphanAgents = [];
|
|
262
300
|
const checkAgentFile = async (filePath) => {
|
|
263
301
|
try {
|
|
264
|
-
const content = await
|
|
302
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
265
303
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
266
304
|
const firstLine = lines[0];
|
|
267
305
|
if (!firstLine) return null;
|
|
268
306
|
const parsed = JSON.parse(firstLine);
|
|
269
307
|
if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {
|
|
270
|
-
const fileName =
|
|
308
|
+
const fileName = path5.basename(filePath);
|
|
271
309
|
return {
|
|
272
310
|
agentId: fileName.replace(".jsonl", ""),
|
|
273
311
|
sessionId: parsed.sessionId,
|
|
@@ -280,27 +318,27 @@ var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
|
280
318
|
};
|
|
281
319
|
const rootAgentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
282
320
|
for (const agentFile of rootAgentFiles) {
|
|
283
|
-
const filePath =
|
|
321
|
+
const filePath = path5.join(projectPath, agentFile);
|
|
284
322
|
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
285
323
|
if (orphan) {
|
|
286
324
|
orphanAgents.push({ ...orphan, filePath });
|
|
287
325
|
}
|
|
288
326
|
}
|
|
289
327
|
for (const entry of files) {
|
|
290
|
-
const entryPath =
|
|
291
|
-
const
|
|
292
|
-
if (
|
|
293
|
-
const subagentsPath =
|
|
328
|
+
const entryPath = path5.join(projectPath, entry);
|
|
329
|
+
const stat4 = yield* Effect.tryPromise(() => fs6.stat(entryPath).catch(() => null));
|
|
330
|
+
if (stat4?.isDirectory() && !entry.startsWith(".")) {
|
|
331
|
+
const subagentsPath = path5.join(entryPath, "subagents");
|
|
294
332
|
const subagentsExists = yield* Effect.tryPromise(
|
|
295
|
-
() =>
|
|
333
|
+
() => fs6.stat(subagentsPath).then(() => true).catch(() => false)
|
|
296
334
|
);
|
|
297
335
|
if (subagentsExists) {
|
|
298
336
|
const subagentFiles = yield* Effect.tryPromise(
|
|
299
|
-
() =>
|
|
337
|
+
() => fs6.readdir(subagentsPath).catch(() => [])
|
|
300
338
|
);
|
|
301
339
|
for (const subagentFile of subagentFiles) {
|
|
302
340
|
if (subagentFile.startsWith("agent-") && subagentFile.endsWith(".jsonl")) {
|
|
303
|
-
const filePath =
|
|
341
|
+
const filePath = path5.join(subagentsPath, subagentFile);
|
|
304
342
|
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
305
343
|
if (orphan) {
|
|
306
344
|
orphanAgents.push({ ...orphan, filePath });
|
|
@@ -317,7 +355,7 @@ var findOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
317
355
|
return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }));
|
|
318
356
|
});
|
|
319
357
|
var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
320
|
-
const projectPath =
|
|
358
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
321
359
|
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
322
360
|
const deletedAgents = [];
|
|
323
361
|
const backedUpAgents = [];
|
|
@@ -325,40 +363,40 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
325
363
|
let backupDirCreated = false;
|
|
326
364
|
const foldersToCheck = /* @__PURE__ */ new Set();
|
|
327
365
|
for (const orphan of orphans) {
|
|
328
|
-
const parentDir =
|
|
366
|
+
const parentDir = path5.dirname(orphan.filePath);
|
|
329
367
|
if (parentDir.endsWith("/subagents") || parentDir.endsWith("\\subagents")) {
|
|
330
368
|
foldersToCheck.add(parentDir);
|
|
331
369
|
}
|
|
332
370
|
if (orphan.lineCount <= 2) {
|
|
333
|
-
yield* Effect.tryPromise(() =>
|
|
371
|
+
yield* Effect.tryPromise(() => fs6.unlink(orphan.filePath));
|
|
334
372
|
deletedAgents.push(orphan.agentId);
|
|
335
373
|
} else {
|
|
336
374
|
if (!backupDirCreated) {
|
|
337
|
-
const backupDir2 =
|
|
338
|
-
yield* Effect.tryPromise(() =>
|
|
375
|
+
const backupDir2 = path5.join(projectPath, ".bak");
|
|
376
|
+
yield* Effect.tryPromise(() => fs6.mkdir(backupDir2, { recursive: true }));
|
|
339
377
|
backupDirCreated = true;
|
|
340
378
|
}
|
|
341
|
-
const backupDir =
|
|
342
|
-
const agentBackupPath =
|
|
343
|
-
yield* Effect.tryPromise(() =>
|
|
379
|
+
const backupDir = path5.join(projectPath, ".bak");
|
|
380
|
+
const agentBackupPath = path5.join(backupDir, `${orphan.agentId}.jsonl`);
|
|
381
|
+
yield* Effect.tryPromise(() => fs6.rename(orphan.filePath, agentBackupPath));
|
|
344
382
|
backedUpAgents.push(orphan.agentId);
|
|
345
383
|
}
|
|
346
384
|
}
|
|
347
385
|
for (const subagentsDir of foldersToCheck) {
|
|
348
386
|
const isEmpty = yield* Effect.tryPromise(async () => {
|
|
349
|
-
const files = await
|
|
387
|
+
const files = await fs6.readdir(subagentsDir);
|
|
350
388
|
return files.length === 0;
|
|
351
389
|
});
|
|
352
390
|
if (isEmpty) {
|
|
353
|
-
yield* Effect.tryPromise(() =>
|
|
391
|
+
yield* Effect.tryPromise(() => fs6.rmdir(subagentsDir));
|
|
354
392
|
cleanedFolders.push(subagentsDir);
|
|
355
|
-
const sessionDir =
|
|
393
|
+
const sessionDir = path5.dirname(subagentsDir);
|
|
356
394
|
const sessionDirEmpty = yield* Effect.tryPromise(async () => {
|
|
357
|
-
const files = await
|
|
395
|
+
const files = await fs6.readdir(sessionDir);
|
|
358
396
|
return files.length === 0;
|
|
359
397
|
});
|
|
360
398
|
if (sessionDirEmpty) {
|
|
361
|
-
yield* Effect.tryPromise(() =>
|
|
399
|
+
yield* Effect.tryPromise(() => fs6.rmdir(sessionDir));
|
|
362
400
|
cleanedFolders.push(sessionDir);
|
|
363
401
|
}
|
|
364
402
|
}
|
|
@@ -375,27 +413,25 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
375
413
|
};
|
|
376
414
|
});
|
|
377
415
|
var loadAgentMessages = (projectName, _sessionId, agentId) => Effect.gen(function* () {
|
|
378
|
-
const projectPath =
|
|
379
|
-
const agentFilePath =
|
|
380
|
-
const content = yield* Effect.tryPromise(() =>
|
|
416
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
417
|
+
const agentFilePath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
418
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(agentFilePath, "utf-8"));
|
|
381
419
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
382
420
|
const messages = [];
|
|
383
|
-
for (
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
messages.push(parsed);
|
|
390
|
-
} catch {
|
|
421
|
+
for (let i = 0; i < lines.length; i++) {
|
|
422
|
+
const parsed = tryParseJsonLine(lines[i], i + 1, agentFilePath);
|
|
423
|
+
if (!parsed) continue;
|
|
424
|
+
if ("sessionId" in parsed && !("type" in parsed)) {
|
|
425
|
+
continue;
|
|
391
426
|
}
|
|
427
|
+
messages.push(parsed);
|
|
392
428
|
}
|
|
393
429
|
return messages;
|
|
394
430
|
});
|
|
395
431
|
var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
396
432
|
const todosDir = getTodosDir();
|
|
397
433
|
const exists = yield* Effect.tryPromise(
|
|
398
|
-
() =>
|
|
434
|
+
() => fs6.access(todosDir).then(() => true).catch(() => false)
|
|
399
435
|
);
|
|
400
436
|
if (!exists) {
|
|
401
437
|
return {
|
|
@@ -405,19 +441,19 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
405
441
|
hasTodos: false
|
|
406
442
|
};
|
|
407
443
|
}
|
|
408
|
-
const sessionTodoPath =
|
|
444
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
409
445
|
let sessionTodos = [];
|
|
410
446
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
411
|
-
() =>
|
|
447
|
+
() => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
412
448
|
);
|
|
413
449
|
if (sessionTodoExists) {
|
|
414
|
-
const content = yield* Effect.tryPromise(() =>
|
|
450
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(sessionTodoPath, "utf-8"));
|
|
415
451
|
try {
|
|
416
452
|
sessionTodos = JSON.parse(content);
|
|
417
453
|
} catch {
|
|
418
454
|
}
|
|
419
455
|
}
|
|
420
|
-
const allFiles = yield* Effect.tryPromise(() =>
|
|
456
|
+
const allFiles = yield* Effect.tryPromise(() => fs6.readdir(todosDir));
|
|
421
457
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
422
458
|
const discoveredAgentIds = new Set(agentIds);
|
|
423
459
|
for (const file of allFiles) {
|
|
@@ -429,12 +465,12 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
429
465
|
const agentTodos = [];
|
|
430
466
|
for (const agentId of discoveredAgentIds) {
|
|
431
467
|
const shortAgentId = agentId.replace("agent-", "");
|
|
432
|
-
const agentTodoPath =
|
|
468
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
433
469
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
434
|
-
() =>
|
|
470
|
+
() => fs6.access(agentTodoPath).then(() => true).catch(() => false)
|
|
435
471
|
);
|
|
436
472
|
if (agentTodoExists) {
|
|
437
|
-
const content = yield* Effect.tryPromise(() =>
|
|
473
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(agentTodoPath, "utf-8"));
|
|
438
474
|
try {
|
|
439
475
|
const todos = JSON.parse(content);
|
|
440
476
|
if (todos.length > 0) {
|
|
@@ -455,22 +491,22 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
455
491
|
var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
456
492
|
const todosDir = getTodosDir();
|
|
457
493
|
const exists = yield* Effect.tryPromise(
|
|
458
|
-
() =>
|
|
494
|
+
() => fs6.access(todosDir).then(() => true).catch(() => false)
|
|
459
495
|
);
|
|
460
496
|
if (!exists) return false;
|
|
461
|
-
const sessionTodoPath =
|
|
497
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
462
498
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
463
|
-
() =>
|
|
499
|
+
() => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
464
500
|
);
|
|
465
501
|
if (sessionTodoExists) {
|
|
466
|
-
const content = yield* Effect.tryPromise(() =>
|
|
502
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(sessionTodoPath, "utf-8"));
|
|
467
503
|
try {
|
|
468
504
|
const todos = JSON.parse(content);
|
|
469
505
|
if (todos.length > 0) return true;
|
|
470
506
|
} catch {
|
|
471
507
|
}
|
|
472
508
|
}
|
|
473
|
-
const allFiles = yield* Effect.tryPromise(() =>
|
|
509
|
+
const allFiles = yield* Effect.tryPromise(() => fs6.readdir(todosDir));
|
|
474
510
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
475
511
|
const discoveredAgentIds = new Set(agentIds);
|
|
476
512
|
for (const file of allFiles) {
|
|
@@ -481,12 +517,12 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
481
517
|
}
|
|
482
518
|
for (const agentId of discoveredAgentIds) {
|
|
483
519
|
const shortAgentId = agentId.replace("agent-", "");
|
|
484
|
-
const agentTodoPath =
|
|
520
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
485
521
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
486
|
-
() =>
|
|
522
|
+
() => fs6.access(agentTodoPath).then(() => true).catch(() => false)
|
|
487
523
|
);
|
|
488
524
|
if (agentTodoExists) {
|
|
489
|
-
const content = yield* Effect.tryPromise(() =>
|
|
525
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(agentTodoPath, "utf-8"));
|
|
490
526
|
try {
|
|
491
527
|
const todos = JSON.parse(content);
|
|
492
528
|
if (todos.length > 0) return true;
|
|
@@ -499,30 +535,30 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
499
535
|
var deleteLinkedTodos = (sessionId, agentIds) => Effect.gen(function* () {
|
|
500
536
|
const todosDir = getTodosDir();
|
|
501
537
|
const exists = yield* Effect.tryPromise(
|
|
502
|
-
() =>
|
|
538
|
+
() => fs6.access(todosDir).then(() => true).catch(() => false)
|
|
503
539
|
);
|
|
504
540
|
if (!exists) return { deletedCount: 0 };
|
|
505
|
-
const backupDir =
|
|
506
|
-
yield* Effect.tryPromise(() =>
|
|
541
|
+
const backupDir = path5.join(todosDir, ".bak");
|
|
542
|
+
yield* Effect.tryPromise(() => fs6.mkdir(backupDir, { recursive: true }));
|
|
507
543
|
let deletedCount = 0;
|
|
508
|
-
const sessionTodoPath =
|
|
544
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
509
545
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
510
|
-
() =>
|
|
546
|
+
() => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
511
547
|
);
|
|
512
548
|
if (sessionTodoExists) {
|
|
513
|
-
const backupPath =
|
|
514
|
-
yield* Effect.tryPromise(() =>
|
|
549
|
+
const backupPath = path5.join(backupDir, `${sessionId}.json`);
|
|
550
|
+
yield* Effect.tryPromise(() => fs6.rename(sessionTodoPath, backupPath));
|
|
515
551
|
deletedCount++;
|
|
516
552
|
}
|
|
517
553
|
for (const agentId of agentIds) {
|
|
518
554
|
const shortAgentId = agentId.replace("agent-", "");
|
|
519
|
-
const agentTodoPath =
|
|
555
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
520
556
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
521
|
-
() =>
|
|
557
|
+
() => fs6.access(agentTodoPath).then(() => true).catch(() => false)
|
|
522
558
|
);
|
|
523
559
|
if (agentTodoExists) {
|
|
524
|
-
const backupPath =
|
|
525
|
-
yield* Effect.tryPromise(() =>
|
|
560
|
+
const backupPath = path5.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
561
|
+
yield* Effect.tryPromise(() => fs6.rename(agentTodoPath, backupPath));
|
|
526
562
|
deletedCount++;
|
|
527
563
|
}
|
|
528
564
|
}
|
|
@@ -533,23 +569,23 @@ var findOrphanTodos = () => Effect.gen(function* () {
|
|
|
533
569
|
const sessionsDir = getSessionsDir();
|
|
534
570
|
const [todosExists, sessionsExists] = yield* Effect.all([
|
|
535
571
|
Effect.tryPromise(
|
|
536
|
-
() =>
|
|
572
|
+
() => fs6.access(todosDir).then(() => true).catch(() => false)
|
|
537
573
|
),
|
|
538
574
|
Effect.tryPromise(
|
|
539
|
-
() =>
|
|
575
|
+
() => fs6.access(sessionsDir).then(() => true).catch(() => false)
|
|
540
576
|
)
|
|
541
577
|
]);
|
|
542
578
|
if (!todosExists || !sessionsExists) return [];
|
|
543
|
-
const todoFiles = yield* Effect.tryPromise(() =>
|
|
579
|
+
const todoFiles = yield* Effect.tryPromise(() => fs6.readdir(todosDir));
|
|
544
580
|
const jsonFiles = todoFiles.filter((f) => f.endsWith(".json"));
|
|
545
581
|
const validSessionIds = /* @__PURE__ */ new Set();
|
|
546
582
|
const projectEntries = yield* Effect.tryPromise(
|
|
547
|
-
() =>
|
|
583
|
+
() => fs6.readdir(sessionsDir, { withFileTypes: true })
|
|
548
584
|
);
|
|
549
585
|
for (const entry of projectEntries) {
|
|
550
586
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
551
|
-
const projectPath =
|
|
552
|
-
const files = yield* Effect.tryPromise(() =>
|
|
587
|
+
const projectPath = path5.join(sessionsDir, entry.name);
|
|
588
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
553
589
|
for (const f of files) {
|
|
554
590
|
if (f.endsWith(".jsonl") && !f.startsWith("agent-")) {
|
|
555
591
|
validSessionIds.add(f.replace(".jsonl", ""));
|
|
@@ -572,13 +608,13 @@ var deleteOrphanTodos = () => Effect.gen(function* () {
|
|
|
572
608
|
const todosDir = getTodosDir();
|
|
573
609
|
const orphans = yield* findOrphanTodos();
|
|
574
610
|
if (orphans.length === 0) return { success: true, deletedCount: 0 };
|
|
575
|
-
const backupDir =
|
|
576
|
-
yield* Effect.tryPromise(() =>
|
|
611
|
+
const backupDir = path5.join(todosDir, ".bak");
|
|
612
|
+
yield* Effect.tryPromise(() => fs6.mkdir(backupDir, { recursive: true }));
|
|
577
613
|
let deletedCount = 0;
|
|
578
614
|
for (const orphan of orphans) {
|
|
579
|
-
const filePath =
|
|
580
|
-
const backupPath =
|
|
581
|
-
yield* Effect.tryPromise(() =>
|
|
615
|
+
const filePath = path5.join(todosDir, orphan);
|
|
616
|
+
const backupPath = path5.join(backupDir, orphan);
|
|
617
|
+
yield* Effect.tryPromise(() => fs6.rename(filePath, backupPath));
|
|
582
618
|
deletedCount++;
|
|
583
619
|
}
|
|
584
620
|
return { success: true, deletedCount };
|
|
@@ -586,17 +622,17 @@ var deleteOrphanTodos = () => Effect.gen(function* () {
|
|
|
586
622
|
var listProjects = Effect.gen(function* () {
|
|
587
623
|
const sessionsDir = getSessionsDir();
|
|
588
624
|
const exists = yield* Effect.tryPromise(
|
|
589
|
-
() =>
|
|
625
|
+
() => fs6.access(sessionsDir).then(() => true).catch(() => false)
|
|
590
626
|
);
|
|
591
627
|
if (!exists) {
|
|
592
628
|
return [];
|
|
593
629
|
}
|
|
594
|
-
const entries = yield* Effect.tryPromise(() =>
|
|
630
|
+
const entries = yield* Effect.tryPromise(() => fs6.readdir(sessionsDir, { withFileTypes: true }));
|
|
595
631
|
const projects = yield* Effect.all(
|
|
596
632
|
entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map(
|
|
597
633
|
(entry) => Effect.gen(function* () {
|
|
598
|
-
const projectPath =
|
|
599
|
-
const files = yield* Effect.tryPromise(() =>
|
|
634
|
+
const projectPath = path5.join(sessionsDir, entry.name);
|
|
635
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
600
636
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
601
637
|
return {
|
|
602
638
|
name: entry.name,
|
|
@@ -610,17 +646,90 @@ var listProjects = Effect.gen(function* () {
|
|
|
610
646
|
);
|
|
611
647
|
return projects;
|
|
612
648
|
});
|
|
649
|
+
function deleteMessageWithChainRepair(messages, targetId, targetType) {
|
|
650
|
+
let targetIndex = -1;
|
|
651
|
+
if (targetType === "file-history-snapshot") {
|
|
652
|
+
targetIndex = messages.findIndex(
|
|
653
|
+
(m) => m.type === "file-history-snapshot" && m.messageId === targetId
|
|
654
|
+
);
|
|
655
|
+
} else if (targetType === "summary") {
|
|
656
|
+
targetIndex = messages.findIndex(
|
|
657
|
+
(m) => m.leafUuid === targetId
|
|
658
|
+
);
|
|
659
|
+
} else {
|
|
660
|
+
targetIndex = messages.findIndex((m) => m.uuid === targetId);
|
|
661
|
+
if (targetIndex === -1) {
|
|
662
|
+
targetIndex = messages.findIndex(
|
|
663
|
+
(m) => m.leafUuid === targetId
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
if (targetIndex === -1) {
|
|
667
|
+
targetIndex = messages.findIndex(
|
|
668
|
+
(m) => m.type === "file-history-snapshot" && m.messageId === targetId
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (targetIndex === -1) {
|
|
673
|
+
return { deleted: null, alsoDeleted: [] };
|
|
674
|
+
}
|
|
675
|
+
const deletedMsg = messages[targetIndex];
|
|
676
|
+
const toolUseIds = [];
|
|
677
|
+
if (deletedMsg.type === "assistant") {
|
|
678
|
+
const content = deletedMsg.message?.content;
|
|
679
|
+
if (Array.isArray(content)) {
|
|
680
|
+
for (const item of content) {
|
|
681
|
+
if (item.type === "tool_use" && item.id) {
|
|
682
|
+
toolUseIds.push(item.id);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const toolResultIndices = [];
|
|
688
|
+
if (toolUseIds.length > 0) {
|
|
689
|
+
for (let i = 0; i < messages.length; i++) {
|
|
690
|
+
const msg = messages[i];
|
|
691
|
+
if (msg.type === "user") {
|
|
692
|
+
const content = msg.message?.content;
|
|
693
|
+
if (Array.isArray(content)) {
|
|
694
|
+
for (const item of content) {
|
|
695
|
+
if (item.type === "tool_result" && item.tool_use_id && toolUseIds.includes(item.tool_use_id)) {
|
|
696
|
+
toolResultIndices.push(i);
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const indicesToDelete = [targetIndex, ...toolResultIndices].sort((a, b) => b - a);
|
|
705
|
+
for (const idx of indicesToDelete) {
|
|
706
|
+
const msg = messages[idx];
|
|
707
|
+
const isInParentChain = msg.type !== "file-history-snapshot" && msg.uuid;
|
|
708
|
+
if (isInParentChain) {
|
|
709
|
+
const deletedUuid = msg.uuid;
|
|
710
|
+
const parentUuid = msg.parentUuid;
|
|
711
|
+
for (const m of messages) {
|
|
712
|
+
if (m.parentUuid === deletedUuid) {
|
|
713
|
+
m.parentUuid = parentUuid;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const alsoDeleted = toolResultIndices.map((i) => messages[i]);
|
|
719
|
+
for (const idx of indicesToDelete) {
|
|
720
|
+
messages.splice(idx, 1);
|
|
721
|
+
}
|
|
722
|
+
return { deleted: deletedMsg, alsoDeleted };
|
|
723
|
+
}
|
|
613
724
|
var listSessions = (projectName) => Effect.gen(function* () {
|
|
614
|
-
const projectPath =
|
|
615
|
-
const files = yield* Effect.tryPromise(() =>
|
|
725
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
726
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
616
727
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
617
728
|
const sessions = yield* Effect.all(
|
|
618
729
|
sessionFiles.map(
|
|
619
730
|
(file) => Effect.gen(function* () {
|
|
620
|
-
const filePath =
|
|
621
|
-
const
|
|
622
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
623
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
731
|
+
const filePath = path5.join(projectPath, file);
|
|
732
|
+
const messages = yield* readJsonlFile(filePath);
|
|
624
733
|
const sessionId = file.replace(".jsonl", "");
|
|
625
734
|
const userAssistantMessages = messages.filter(
|
|
626
735
|
(m) => m.type === "user" || m.type === "assistant"
|
|
@@ -637,10 +746,25 @@ var listSessions = (projectName) => Effect.gen(function* () {
|
|
|
637
746
|
}),
|
|
638
747
|
Option.getOrElse(() => hasSummary ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`)
|
|
639
748
|
);
|
|
749
|
+
const currentSummary = pipe(
|
|
750
|
+
messages,
|
|
751
|
+
Array$1.findFirst((m) => m.type === "summary"),
|
|
752
|
+
Option.map((m) => m.summary),
|
|
753
|
+
Option.getOrUndefined
|
|
754
|
+
);
|
|
755
|
+
const customTitle = pipe(
|
|
756
|
+
messages,
|
|
757
|
+
Array$1.findFirst((m) => m.type === "custom-title"),
|
|
758
|
+
Option.map((m) => m.customTitle),
|
|
759
|
+
Option.flatMap(Option.fromNullable),
|
|
760
|
+
Option.getOrUndefined
|
|
761
|
+
);
|
|
640
762
|
return {
|
|
641
763
|
id: sessionId,
|
|
642
764
|
projectName,
|
|
643
765
|
title,
|
|
766
|
+
customTitle,
|
|
767
|
+
currentSummary,
|
|
644
768
|
// If session has summary but no user/assistant messages, count as 1
|
|
645
769
|
messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,
|
|
646
770
|
createdAt: firstMessage?.timestamp,
|
|
@@ -657,40 +781,23 @@ var listSessions = (projectName) => Effect.gen(function* () {
|
|
|
657
781
|
});
|
|
658
782
|
});
|
|
659
783
|
var readSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
660
|
-
const filePath =
|
|
661
|
-
|
|
662
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
663
|
-
return lines.map((line) => JSON.parse(line));
|
|
784
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
785
|
+
return yield* readJsonlFile(filePath);
|
|
664
786
|
});
|
|
665
|
-
var deleteMessage = (projectName, sessionId, messageUuid) => Effect.gen(function* () {
|
|
666
|
-
const filePath =
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
const targetIndex = messages.findIndex(
|
|
671
|
-
(m) => m.uuid === messageUuid || m.messageId === messageUuid || m.leafUuid === messageUuid
|
|
672
|
-
);
|
|
673
|
-
if (targetIndex === -1) {
|
|
787
|
+
var deleteMessage = (projectName, sessionId, messageUuid, targetType) => Effect.gen(function* () {
|
|
788
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
789
|
+
const messages = yield* readJsonlFile(filePath);
|
|
790
|
+
const result = deleteMessageWithChainRepair(messages, messageUuid, targetType);
|
|
791
|
+
if (!result.deleted) {
|
|
674
792
|
return { success: false, error: "Message not found" };
|
|
675
793
|
}
|
|
676
|
-
const deletedMsg = messages[targetIndex];
|
|
677
|
-
const deletedUuid = deletedMsg?.uuid ?? deletedMsg?.messageId;
|
|
678
|
-
const parentUuid = deletedMsg?.parentUuid;
|
|
679
|
-
for (const msg of messages) {
|
|
680
|
-
if (msg.parentUuid === deletedUuid) {
|
|
681
|
-
msg.parentUuid = parentUuid;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
messages.splice(targetIndex, 1);
|
|
685
794
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
686
|
-
yield* Effect.tryPromise(() =>
|
|
687
|
-
return { success: true, deletedMessage:
|
|
795
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
796
|
+
return { success: true, deletedMessage: result.deleted };
|
|
688
797
|
});
|
|
689
798
|
var restoreMessage = (projectName, sessionId, message, index) => Effect.gen(function* () {
|
|
690
|
-
const filePath =
|
|
691
|
-
const
|
|
692
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
693
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
799
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
800
|
+
const messages = yield* readJsonlFile(filePath);
|
|
694
801
|
const msgUuid = message.uuid ?? message.messageId;
|
|
695
802
|
if (!msgUuid) {
|
|
696
803
|
return { success: false, error: "Message has no uuid or messageId" };
|
|
@@ -705,41 +812,41 @@ var restoreMessage = (projectName, sessionId, message, index) => Effect.gen(func
|
|
|
705
812
|
const insertIndex = Math.min(index, messages.length);
|
|
706
813
|
messages.splice(insertIndex, 0, message);
|
|
707
814
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
708
|
-
yield* Effect.tryPromise(() =>
|
|
815
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
709
816
|
return { success: true };
|
|
710
817
|
});
|
|
711
818
|
var deleteSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
712
819
|
const sessionsDir = getSessionsDir();
|
|
713
|
-
const projectPath =
|
|
714
|
-
const filePath =
|
|
820
|
+
const projectPath = path5.join(sessionsDir, projectName);
|
|
821
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
715
822
|
const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
718
|
-
yield* Effect.tryPromise(() =>
|
|
719
|
-
const agentBackupDir2 =
|
|
720
|
-
yield* Effect.tryPromise(() =>
|
|
823
|
+
const stat4 = yield* Effect.tryPromise(() => fs6.stat(filePath));
|
|
824
|
+
if (stat4.size === 0) {
|
|
825
|
+
yield* Effect.tryPromise(() => fs6.unlink(filePath));
|
|
826
|
+
const agentBackupDir2 = path5.join(projectPath, ".bak");
|
|
827
|
+
yield* Effect.tryPromise(() => fs6.mkdir(agentBackupDir2, { recursive: true }));
|
|
721
828
|
for (const agentId of linkedAgents) {
|
|
722
|
-
const agentPath =
|
|
723
|
-
const agentBackupPath =
|
|
724
|
-
yield* Effect.tryPromise(() =>
|
|
829
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
830
|
+
const agentBackupPath = path5.join(agentBackupDir2, `${agentId}.jsonl`);
|
|
831
|
+
yield* Effect.tryPromise(() => fs6.rename(agentPath, agentBackupPath).catch(() => {
|
|
725
832
|
}));
|
|
726
833
|
}
|
|
727
834
|
yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
728
835
|
return { success: true, deletedAgents: linkedAgents.length };
|
|
729
836
|
}
|
|
730
|
-
const backupDir =
|
|
731
|
-
yield* Effect.tryPromise(() =>
|
|
732
|
-
const agentBackupDir =
|
|
733
|
-
yield* Effect.tryPromise(() =>
|
|
837
|
+
const backupDir = path5.join(sessionsDir, ".bak");
|
|
838
|
+
yield* Effect.tryPromise(() => fs6.mkdir(backupDir, { recursive: true }));
|
|
839
|
+
const agentBackupDir = path5.join(projectPath, ".bak");
|
|
840
|
+
yield* Effect.tryPromise(() => fs6.mkdir(agentBackupDir, { recursive: true }));
|
|
734
841
|
for (const agentId of linkedAgents) {
|
|
735
|
-
const agentPath =
|
|
736
|
-
const agentBackupPath =
|
|
737
|
-
yield* Effect.tryPromise(() =>
|
|
842
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
843
|
+
const agentBackupPath = path5.join(agentBackupDir, `${agentId}.jsonl`);
|
|
844
|
+
yield* Effect.tryPromise(() => fs6.rename(agentPath, agentBackupPath).catch(() => {
|
|
738
845
|
}));
|
|
739
846
|
}
|
|
740
847
|
const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
741
|
-
const backupPath =
|
|
742
|
-
yield* Effect.tryPromise(() =>
|
|
848
|
+
const backupPath = path5.join(backupDir, `${projectName}_${sessionId}.jsonl`);
|
|
849
|
+
yield* Effect.tryPromise(() => fs6.rename(filePath, backupPath));
|
|
743
850
|
return {
|
|
744
851
|
success: true,
|
|
745
852
|
backupPath,
|
|
@@ -748,14 +855,14 @@ var deleteSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
748
855
|
};
|
|
749
856
|
});
|
|
750
857
|
var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* () {
|
|
751
|
-
const projectPath =
|
|
752
|
-
const filePath =
|
|
753
|
-
const content = yield* Effect.tryPromise(() =>
|
|
858
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
859
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
860
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
754
861
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
755
862
|
if (lines.length === 0) {
|
|
756
863
|
return { success: false, error: "Empty session" };
|
|
757
864
|
}
|
|
758
|
-
const messages = lines
|
|
865
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
759
866
|
const sessionUuids = /* @__PURE__ */ new Set();
|
|
760
867
|
for (const msg of messages) {
|
|
761
868
|
if (msg.uuid && typeof msg.uuid === "string") {
|
|
@@ -774,16 +881,14 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
774
881
|
messages.unshift(customTitleRecord);
|
|
775
882
|
}
|
|
776
883
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
777
|
-
yield* Effect.tryPromise(() =>
|
|
778
|
-
const projectFiles = yield* Effect.tryPromise(() =>
|
|
884
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
885
|
+
const projectFiles = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
779
886
|
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
780
887
|
const summariesTargetingThis = [];
|
|
781
888
|
for (const file of allJsonlFiles) {
|
|
782
|
-
const otherFilePath =
|
|
889
|
+
const otherFilePath = path5.join(projectPath, file);
|
|
783
890
|
try {
|
|
784
|
-
const
|
|
785
|
-
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
786
|
-
const otherMessages = otherLines.map((l) => JSON.parse(l));
|
|
891
|
+
const otherMessages = yield* readJsonlFile(otherFilePath);
|
|
787
892
|
for (let i = 0; i < otherMessages.length; i++) {
|
|
788
893
|
const msg = otherMessages[i];
|
|
789
894
|
if (msg.type === "summary" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
@@ -801,20 +906,16 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
801
906
|
if (summariesTargetingThis.length > 0) {
|
|
802
907
|
summariesTargetingThis.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
|
|
803
908
|
const firstSummary = summariesTargetingThis[0];
|
|
804
|
-
const summaryFilePath =
|
|
805
|
-
const
|
|
806
|
-
const summaryLines = summaryContent.trim().split("\n").filter(Boolean);
|
|
807
|
-
const summaryMessages = summaryLines.map((l) => JSON.parse(l));
|
|
909
|
+
const summaryFilePath = path5.join(projectPath, firstSummary.file);
|
|
910
|
+
const summaryMessages = yield* readJsonlFile(summaryFilePath);
|
|
808
911
|
summaryMessages[firstSummary.idx] = {
|
|
809
912
|
...summaryMessages[firstSummary.idx],
|
|
810
913
|
summary: newTitle
|
|
811
914
|
};
|
|
812
915
|
const newSummaryContent = summaryMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
813
|
-
yield* Effect.tryPromise(() =>
|
|
916
|
+
yield* Effect.tryPromise(() => fs6.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
|
|
814
917
|
} else {
|
|
815
|
-
const
|
|
816
|
-
const currentLines = currentContent.trim().split("\n").filter(Boolean);
|
|
817
|
-
const currentMessages = currentLines.map((l) => JSON.parse(l));
|
|
918
|
+
const currentMessages = yield* readJsonlFile(filePath);
|
|
818
919
|
const firstUserIdx = currentMessages.findIndex((m) => m.type === "user");
|
|
819
920
|
if (firstUserIdx >= 0) {
|
|
820
921
|
const firstMsg = currentMessages[firstUserIdx];
|
|
@@ -831,104 +932,50 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
831
932
|
|
|
832
933
|
${cleanedText}`;
|
|
833
934
|
const updatedContent = currentMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
834
|
-
yield* Effect.tryPromise(() =>
|
|
935
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, updatedContent, "utf-8"));
|
|
835
936
|
}
|
|
836
937
|
}
|
|
837
938
|
}
|
|
838
939
|
}
|
|
839
940
|
return { success: true };
|
|
840
941
|
});
|
|
841
|
-
var getSessionFiles = (projectName, sessionId) => Effect.gen(function* () {
|
|
842
|
-
const messages = yield* readSession(projectName, sessionId);
|
|
843
|
-
const fileChanges = [];
|
|
844
|
-
const seenFiles = /* @__PURE__ */ new Set();
|
|
845
|
-
for (const msg of messages) {
|
|
846
|
-
if (msg.type === "file-history-snapshot") {
|
|
847
|
-
const snapshot = msg;
|
|
848
|
-
const backups = snapshot.snapshot?.trackedFileBackups;
|
|
849
|
-
if (backups && typeof backups === "object") {
|
|
850
|
-
for (const filePath of Object.keys(backups)) {
|
|
851
|
-
if (!seenFiles.has(filePath)) {
|
|
852
|
-
seenFiles.add(filePath);
|
|
853
|
-
fileChanges.push({
|
|
854
|
-
path: filePath,
|
|
855
|
-
action: "modified",
|
|
856
|
-
timestamp: snapshot.snapshot?.timestamp,
|
|
857
|
-
messageUuid: snapshot.messageId ?? msg.uuid
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
if (msg.type === "assistant" && msg.message?.content) {
|
|
864
|
-
const content = msg.message.content;
|
|
865
|
-
if (Array.isArray(content)) {
|
|
866
|
-
for (const item of content) {
|
|
867
|
-
if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
|
|
868
|
-
const toolUse = item;
|
|
869
|
-
if ((toolUse.name === "Write" || toolUse.name === "Edit") && toolUse.input?.file_path) {
|
|
870
|
-
const filePath = toolUse.input.file_path;
|
|
871
|
-
if (!seenFiles.has(filePath)) {
|
|
872
|
-
seenFiles.add(filePath);
|
|
873
|
-
fileChanges.push({
|
|
874
|
-
path: filePath,
|
|
875
|
-
action: toolUse.name === "Write" ? "created" : "modified",
|
|
876
|
-
timestamp: msg.timestamp,
|
|
877
|
-
messageUuid: msg.uuid
|
|
878
|
-
});
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
return {
|
|
887
|
-
sessionId,
|
|
888
|
-
projectName,
|
|
889
|
-
files: fileChanges,
|
|
890
|
-
totalChanges: fileChanges.length
|
|
891
|
-
};
|
|
892
|
-
});
|
|
893
942
|
var moveSession = (sourceProject, sessionId, targetProject) => Effect.gen(function* () {
|
|
894
943
|
const sessionsDir = getSessionsDir();
|
|
895
|
-
const sourcePath =
|
|
896
|
-
const targetPath =
|
|
897
|
-
const sourceFile =
|
|
898
|
-
const targetFile =
|
|
944
|
+
const sourcePath = path5.join(sessionsDir, sourceProject);
|
|
945
|
+
const targetPath = path5.join(sessionsDir, targetProject);
|
|
946
|
+
const sourceFile = path5.join(sourcePath, `${sessionId}.jsonl`);
|
|
947
|
+
const targetFile = path5.join(targetPath, `${sessionId}.jsonl`);
|
|
899
948
|
const sourceExists = yield* Effect.tryPromise(
|
|
900
|
-
() =>
|
|
949
|
+
() => fs6.access(sourceFile).then(() => true).catch(() => false)
|
|
901
950
|
);
|
|
902
951
|
if (!sourceExists) {
|
|
903
952
|
return { success: false, error: "Source session not found" };
|
|
904
953
|
}
|
|
905
954
|
const targetExists = yield* Effect.tryPromise(
|
|
906
|
-
() =>
|
|
955
|
+
() => fs6.access(targetFile).then(() => true).catch(() => false)
|
|
907
956
|
);
|
|
908
957
|
if (targetExists) {
|
|
909
958
|
return { success: false, error: "Session already exists in target project" };
|
|
910
959
|
}
|
|
911
|
-
yield* Effect.tryPromise(() =>
|
|
960
|
+
yield* Effect.tryPromise(() => fs6.mkdir(targetPath, { recursive: true }));
|
|
912
961
|
const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId);
|
|
913
|
-
yield* Effect.tryPromise(() =>
|
|
962
|
+
yield* Effect.tryPromise(() => fs6.rename(sourceFile, targetFile));
|
|
914
963
|
for (const agentId of linkedAgents) {
|
|
915
|
-
const sourceAgentFile =
|
|
916
|
-
const targetAgentFile =
|
|
964
|
+
const sourceAgentFile = path5.join(sourcePath, `${agentId}.jsonl`);
|
|
965
|
+
const targetAgentFile = path5.join(targetPath, `${agentId}.jsonl`);
|
|
917
966
|
const agentExists = yield* Effect.tryPromise(
|
|
918
|
-
() =>
|
|
967
|
+
() => fs6.access(sourceAgentFile).then(() => true).catch(() => false)
|
|
919
968
|
);
|
|
920
969
|
if (agentExists) {
|
|
921
|
-
yield* Effect.tryPromise(() =>
|
|
970
|
+
yield* Effect.tryPromise(() => fs6.rename(sourceAgentFile, targetAgentFile));
|
|
922
971
|
}
|
|
923
972
|
}
|
|
924
973
|
return { success: true };
|
|
925
974
|
});
|
|
926
975
|
var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(function* () {
|
|
927
|
-
const projectPath =
|
|
928
|
-
const filePath =
|
|
929
|
-
const
|
|
930
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
931
|
-
const allMessages = lines.map((line) => JSON.parse(line));
|
|
976
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
977
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
978
|
+
const allMessages = yield* readJsonlFile(filePath);
|
|
932
979
|
const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid);
|
|
933
980
|
if (splitIndex === -1) {
|
|
934
981
|
return { success: false, error: "Message not found" };
|
|
@@ -974,30 +1021,30 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(fu
|
|
|
974
1021
|
updatedMovedMessages.unshift(clonedSummary);
|
|
975
1022
|
}
|
|
976
1023
|
const keptContent = keptMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
977
|
-
yield* Effect.tryPromise(() =>
|
|
978
|
-
const newFilePath =
|
|
1024
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, keptContent, "utf-8"));
|
|
1025
|
+
const newFilePath = path5.join(projectPath, `${newSessionId}.jsonl`);
|
|
979
1026
|
const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
980
|
-
yield* Effect.tryPromise(() =>
|
|
981
|
-
const agentFiles = yield* Effect.tryPromise(() =>
|
|
1027
|
+
yield* Effect.tryPromise(() => fs6.writeFile(newFilePath, newContent, "utf-8"));
|
|
1028
|
+
const agentFiles = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
982
1029
|
const agentJsonlFiles = agentFiles.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
983
1030
|
for (const agentFile of agentJsonlFiles) {
|
|
984
|
-
const agentPath =
|
|
985
|
-
const agentContent = yield* Effect.tryPromise(() =>
|
|
1031
|
+
const agentPath = path5.join(projectPath, agentFile);
|
|
1032
|
+
const agentContent = yield* Effect.tryPromise(() => fs6.readFile(agentPath, "utf-8"));
|
|
986
1033
|
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
987
1034
|
if (agentLines.length === 0) continue;
|
|
988
|
-
const
|
|
1035
|
+
const agentMessages = parseJsonlLines(agentLines, agentPath);
|
|
1036
|
+
const firstAgentMsg = agentMessages[0];
|
|
989
1037
|
if (firstAgentMsg.sessionId === sessionId) {
|
|
990
1038
|
const agentId = agentFile.replace("agent-", "").replace(".jsonl", "");
|
|
991
1039
|
const isRelatedToMoved = movedMessages.some(
|
|
992
1040
|
(msg) => msg.agentId === agentId
|
|
993
1041
|
);
|
|
994
1042
|
if (isRelatedToMoved) {
|
|
995
|
-
const updatedAgentMessages =
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
});
|
|
1043
|
+
const updatedAgentMessages = agentMessages.map(
|
|
1044
|
+
(msg) => JSON.stringify({ ...msg, sessionId: newSessionId })
|
|
1045
|
+
);
|
|
999
1046
|
const updatedAgentContent = updatedAgentMessages.join("\n") + "\n";
|
|
1000
|
-
yield* Effect.tryPromise(() =>
|
|
1047
|
+
yield* Effect.tryPromise(() => fs6.writeFile(agentPath, updatedAgentContent, "utf-8"));
|
|
1001
1048
|
}
|
|
1002
1049
|
}
|
|
1003
1050
|
}
|
|
@@ -1009,12 +1056,276 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(fu
|
|
|
1009
1056
|
duplicatedSummary: shouldDuplicate
|
|
1010
1057
|
};
|
|
1011
1058
|
});
|
|
1059
|
+
var sortSessions = (sessions, sort) => {
|
|
1060
|
+
return sessions.sort((a, b) => {
|
|
1061
|
+
let comparison = 0;
|
|
1062
|
+
switch (sort.field) {
|
|
1063
|
+
case "summary": {
|
|
1064
|
+
comparison = a.sortTimestamp - b.sortTimestamp;
|
|
1065
|
+
break;
|
|
1066
|
+
}
|
|
1067
|
+
case "modified": {
|
|
1068
|
+
comparison = (a.fileMtime ?? 0) - (b.fileMtime ?? 0);
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
case "created": {
|
|
1072
|
+
const createdA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
1073
|
+
const createdB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
1074
|
+
comparison = createdA - createdB;
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
case "updated": {
|
|
1078
|
+
const updatedA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
1079
|
+
const updatedB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
1080
|
+
comparison = updatedA - updatedB;
|
|
1081
|
+
break;
|
|
1082
|
+
}
|
|
1083
|
+
case "messageCount": {
|
|
1084
|
+
comparison = a.messageCount - b.messageCount;
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
case "title": {
|
|
1088
|
+
const titleA = a.customTitle ?? a.currentSummary ?? a.title;
|
|
1089
|
+
const titleB = b.customTitle ?? b.currentSummary ?? b.title;
|
|
1090
|
+
comparison = titleA.localeCompare(titleB);
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return sort.order === "desc" ? -comparison : comparison;
|
|
1095
|
+
});
|
|
1096
|
+
};
|
|
1097
|
+
var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSession, fileMtime) => Effect.gen(function* () {
|
|
1098
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1099
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
1100
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
1101
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1102
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1103
|
+
let summaries;
|
|
1104
|
+
if (summariesByTargetSession) {
|
|
1105
|
+
summaries = [...summariesByTargetSession.get(sessionId) ?? []].sort((a, b) => {
|
|
1106
|
+
const timestampCmp = (a.timestamp ?? "").localeCompare(b.timestamp ?? "");
|
|
1107
|
+
if (timestampCmp !== 0) return timestampCmp;
|
|
1108
|
+
return (b.sourceFile ?? "").localeCompare(a.sourceFile ?? "");
|
|
1109
|
+
});
|
|
1110
|
+
} else {
|
|
1111
|
+
summaries = [];
|
|
1112
|
+
const sessionUuids = /* @__PURE__ */ new Set();
|
|
1113
|
+
for (const msg of messages) {
|
|
1114
|
+
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1115
|
+
sessionUuids.add(msg.uuid);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
const projectFiles = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
1119
|
+
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1120
|
+
for (const file of allJsonlFiles) {
|
|
1121
|
+
try {
|
|
1122
|
+
const otherFilePath = path5.join(projectPath, file);
|
|
1123
|
+
const otherContent = yield* Effect.tryPromise(() => fs6.readFile(otherFilePath, "utf-8"));
|
|
1124
|
+
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
1125
|
+
for (let i = 0; i < otherLines.length; i++) {
|
|
1126
|
+
const msg = tryParseJsonLine(otherLines[i], i + 1, otherFilePath);
|
|
1127
|
+
if (!msg) continue;
|
|
1128
|
+
if (msg.type === "summary" && typeof msg.summary === "string" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
1129
|
+
const targetMsg = messages.find((m) => m.uuid === msg.leafUuid);
|
|
1130
|
+
summaries.push({
|
|
1131
|
+
summary: msg.summary,
|
|
1132
|
+
leafUuid: msg.leafUuid,
|
|
1133
|
+
timestamp: targetMsg?.timestamp ?? msg.timestamp,
|
|
1134
|
+
sourceFile: file
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
} catch {
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
summaries.sort((a, b) => {
|
|
1143
|
+
const timestampCmp = (a.timestamp ?? "").localeCompare(b.timestamp ?? "");
|
|
1144
|
+
if (timestampCmp !== 0) return timestampCmp;
|
|
1145
|
+
return (b.sourceFile ?? "").localeCompare(a.sourceFile ?? "");
|
|
1146
|
+
});
|
|
1147
|
+
let lastCompactBoundaryUuid;
|
|
1148
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1149
|
+
const msg = messages[i];
|
|
1150
|
+
if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
1151
|
+
lastCompactBoundaryUuid = msg.uuid;
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
const firstUserMsg = messages.find((m) => m.type === "user");
|
|
1156
|
+
const customTitleMsg = messages.find((m) => m.type === "custom-title");
|
|
1157
|
+
const customTitle = customTitleMsg?.customTitle;
|
|
1158
|
+
const title = firstUserMsg ? extractTitle(extractTextContent(firstUserMsg.message)) : summaries.length > 0 ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`;
|
|
1159
|
+
const userAssistantMessages = messages.filter(
|
|
1160
|
+
(m) => m.type === "user" || m.type === "assistant"
|
|
1161
|
+
);
|
|
1162
|
+
const firstMessage = userAssistantMessages[0];
|
|
1163
|
+
const lastMessage = userAssistantMessages[userAssistantMessages.length - 1];
|
|
1164
|
+
const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId);
|
|
1165
|
+
const agents = [];
|
|
1166
|
+
for (const agentId of linkedAgentIds) {
|
|
1167
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
1168
|
+
try {
|
|
1169
|
+
const agentContent = yield* Effect.tryPromise(() => fs6.readFile(agentPath, "utf-8"));
|
|
1170
|
+
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
1171
|
+
const agentMsgs = agentLines.map((l) => JSON.parse(l));
|
|
1172
|
+
const agentUserAssistant = agentMsgs.filter(
|
|
1173
|
+
(m) => m.type === "user" || m.type === "assistant"
|
|
1174
|
+
);
|
|
1175
|
+
let agentName;
|
|
1176
|
+
const firstAgentMsg = agentMsgs.find((m) => m.type === "user");
|
|
1177
|
+
if (firstAgentMsg) {
|
|
1178
|
+
const text = extractTextContent(firstAgentMsg.message);
|
|
1179
|
+
if (text) {
|
|
1180
|
+
agentName = extractTitle(text);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
agents.push({
|
|
1184
|
+
id: agentId,
|
|
1185
|
+
name: agentName,
|
|
1186
|
+
messageCount: agentUserAssistant.length
|
|
1187
|
+
});
|
|
1188
|
+
} catch {
|
|
1189
|
+
agents.push({
|
|
1190
|
+
id: agentId,
|
|
1191
|
+
messageCount: 0
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const todos = yield* findLinkedTodos(sessionId, linkedAgentIds);
|
|
1196
|
+
const createdAt = firstMessage?.timestamp ?? void 0;
|
|
1197
|
+
const sortTimestamp = getSessionSortTimestamp({ summaries, createdAt });
|
|
1198
|
+
return {
|
|
1199
|
+
id: sessionId,
|
|
1200
|
+
projectName,
|
|
1201
|
+
title,
|
|
1202
|
+
customTitle,
|
|
1203
|
+
currentSummary: summaries[0]?.summary,
|
|
1204
|
+
messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : summaries.length > 0 ? 1 : 0,
|
|
1205
|
+
createdAt,
|
|
1206
|
+
updatedAt: lastMessage?.timestamp ?? void 0,
|
|
1207
|
+
fileMtime,
|
|
1208
|
+
sortTimestamp,
|
|
1209
|
+
summaries,
|
|
1210
|
+
agents,
|
|
1211
|
+
todos,
|
|
1212
|
+
lastCompactBoundaryUuid
|
|
1213
|
+
};
|
|
1214
|
+
});
|
|
1215
|
+
var loadSessionTreeData = (projectName, sessionId) => loadSessionTreeDataInternal(projectName, sessionId, void 0);
|
|
1216
|
+
var DEFAULT_SORT = { field: "summary", order: "desc" };
|
|
1217
|
+
var loadProjectTreeData = (projectName, sortOptions) => Effect.gen(function* () {
|
|
1218
|
+
const project = (yield* listProjects).find((p) => p.name === projectName);
|
|
1219
|
+
if (!project) {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
const sort = sortOptions ?? DEFAULT_SORT;
|
|
1223
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1224
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
1225
|
+
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1226
|
+
const fileMtimes = /* @__PURE__ */ new Map();
|
|
1227
|
+
yield* Effect.all(
|
|
1228
|
+
sessionFiles.map(
|
|
1229
|
+
(file) => Effect.gen(function* () {
|
|
1230
|
+
const filePath = path5.join(projectPath, file);
|
|
1231
|
+
try {
|
|
1232
|
+
const stat4 = yield* Effect.tryPromise(() => fs6.stat(filePath));
|
|
1233
|
+
fileMtimes.set(file.replace(".jsonl", ""), stat4.mtimeMs);
|
|
1234
|
+
} catch {
|
|
1235
|
+
}
|
|
1236
|
+
})
|
|
1237
|
+
),
|
|
1238
|
+
{ concurrency: 20 }
|
|
1239
|
+
);
|
|
1240
|
+
const globalUuidMap = /* @__PURE__ */ new Map();
|
|
1241
|
+
const allSummaries = [];
|
|
1242
|
+
const allJsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
1243
|
+
yield* Effect.all(
|
|
1244
|
+
allJsonlFiles.map(
|
|
1245
|
+
(file) => Effect.gen(function* () {
|
|
1246
|
+
const filePath = path5.join(projectPath, file);
|
|
1247
|
+
const fileSessionId = file.replace(".jsonl", "");
|
|
1248
|
+
try {
|
|
1249
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
1250
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1251
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1252
|
+
const msg = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
1253
|
+
if (!msg) continue;
|
|
1254
|
+
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1255
|
+
globalUuidMap.set(msg.uuid, {
|
|
1256
|
+
sessionId: fileSessionId,
|
|
1257
|
+
timestamp: msg.timestamp
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
if (msg.messageId && typeof msg.messageId === "string") {
|
|
1261
|
+
globalUuidMap.set(msg.messageId, {
|
|
1262
|
+
sessionId: fileSessionId,
|
|
1263
|
+
timestamp: msg.snapshot?.timestamp
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
if (msg.type === "summary" && typeof msg.summary === "string") {
|
|
1267
|
+
allSummaries.push({
|
|
1268
|
+
summary: msg.summary,
|
|
1269
|
+
leafUuid: msg.leafUuid,
|
|
1270
|
+
timestamp: msg.timestamp,
|
|
1271
|
+
sourceFile: file
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
} catch {
|
|
1276
|
+
}
|
|
1277
|
+
})
|
|
1278
|
+
),
|
|
1279
|
+
{ concurrency: 20 }
|
|
1280
|
+
);
|
|
1281
|
+
const summariesByTargetSession = /* @__PURE__ */ new Map();
|
|
1282
|
+
for (const summaryData of allSummaries) {
|
|
1283
|
+
if (summaryData.leafUuid) {
|
|
1284
|
+
const targetInfo = globalUuidMap.get(summaryData.leafUuid);
|
|
1285
|
+
if (targetInfo) {
|
|
1286
|
+
const targetSessionId = targetInfo.sessionId;
|
|
1287
|
+
if (!summariesByTargetSession.has(targetSessionId)) {
|
|
1288
|
+
summariesByTargetSession.set(targetSessionId, []);
|
|
1289
|
+
}
|
|
1290
|
+
summariesByTargetSession.get(targetSessionId).push({
|
|
1291
|
+
summary: summaryData.summary,
|
|
1292
|
+
leafUuid: summaryData.leafUuid,
|
|
1293
|
+
// Use summary's own timestamp for sorting, not the target message's timestamp
|
|
1294
|
+
timestamp: summaryData.timestamp ?? targetInfo.timestamp,
|
|
1295
|
+
sourceFile: summaryData.sourceFile
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
const sessions = yield* Effect.all(
|
|
1301
|
+
sessionFiles.map((file) => {
|
|
1302
|
+
const sessionId = file.replace(".jsonl", "");
|
|
1303
|
+
const mtime = fileMtimes.get(sessionId);
|
|
1304
|
+
return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime);
|
|
1305
|
+
}),
|
|
1306
|
+
{ concurrency: 10 }
|
|
1307
|
+
);
|
|
1308
|
+
const sortedSessions = sortSessions(sessions, sort);
|
|
1309
|
+
const filteredSessions = sortedSessions.filter((s) => {
|
|
1310
|
+
if (isErrorSessionTitle(s.title)) return false;
|
|
1311
|
+
if (isErrorSessionTitle(s.customTitle)) return false;
|
|
1312
|
+
if (isErrorSessionTitle(s.currentSummary)) return false;
|
|
1313
|
+
return true;
|
|
1314
|
+
});
|
|
1315
|
+
return {
|
|
1316
|
+
name: project.name,
|
|
1317
|
+
displayName: project.displayName,
|
|
1318
|
+
path: project.path,
|
|
1319
|
+
sessionCount: filteredSessions.length,
|
|
1320
|
+
sessions: filteredSessions
|
|
1321
|
+
};
|
|
1322
|
+
});
|
|
1012
1323
|
var cleanInvalidMessages = (projectName, sessionId) => Effect.gen(function* () {
|
|
1013
|
-
const filePath =
|
|
1014
|
-
const content = yield* Effect.tryPromise(() =>
|
|
1324
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
1325
|
+
const content = yield* Effect.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
1015
1326
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1016
1327
|
if (lines.length === 0) return { removedCount: 0, remainingCount: 0 };
|
|
1017
|
-
const messages = lines
|
|
1328
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1018
1329
|
const invalidIndices = [];
|
|
1019
1330
|
messages.forEach((msg, idx) => {
|
|
1020
1331
|
if (isInvalidApiKeyMessage(msg)) {
|
|
@@ -1043,7 +1354,7 @@ var cleanInvalidMessages = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
1043
1354
|
lastValidUuid = msg.uuid;
|
|
1044
1355
|
}
|
|
1045
1356
|
const newContent = filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join("\n") + "\n" : "";
|
|
1046
|
-
yield* Effect.tryPromise(() =>
|
|
1357
|
+
yield* Effect.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
1047
1358
|
const remainingUserAssistant = filtered.filter(
|
|
1048
1359
|
(m) => m.type === "user" || m.type === "assistant"
|
|
1049
1360
|
).length;
|
|
@@ -1109,8 +1420,8 @@ var clearSessions = (options) => Effect.gen(function* () {
|
|
|
1109
1420
|
const sessionsToDelete = [];
|
|
1110
1421
|
if (clearInvalid) {
|
|
1111
1422
|
for (const project of targetProjects) {
|
|
1112
|
-
const projectPath =
|
|
1113
|
-
const files = yield* Effect.tryPromise(() =>
|
|
1423
|
+
const projectPath = path5.join(getSessionsDir(), project.name);
|
|
1424
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
1114
1425
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1115
1426
|
for (const file of sessionFiles) {
|
|
1116
1427
|
const sessionId = file.replace(".jsonl", "");
|
|
@@ -1164,277 +1475,150 @@ var clearSessions = (options) => Effect.gen(function* () {
|
|
|
1164
1475
|
deletedOrphanTodoCount
|
|
1165
1476
|
};
|
|
1166
1477
|
});
|
|
1478
|
+
var extractSnippet = (text, matchIndex, queryLength) => {
|
|
1479
|
+
const start = Math.max(0, matchIndex - 50);
|
|
1480
|
+
const end = Math.min(text.length, matchIndex + queryLength + 50);
|
|
1481
|
+
return (start > 0 ? "..." : "") + text.slice(start, end).trim() + (end < text.length ? "..." : "");
|
|
1482
|
+
};
|
|
1483
|
+
var findContentMatch = (lines, queryLower, filePath) => {
|
|
1484
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1485
|
+
const msg = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
1486
|
+
if (!msg) continue;
|
|
1487
|
+
if (msg.type !== "user" && msg.type !== "assistant") continue;
|
|
1488
|
+
const text = extractTextContent(msg.message);
|
|
1489
|
+
const textLower = text.toLowerCase();
|
|
1490
|
+
const matchIndex = textLower.indexOf(queryLower);
|
|
1491
|
+
if (matchIndex !== -1) {
|
|
1492
|
+
return {
|
|
1493
|
+
msg,
|
|
1494
|
+
snippet: extractSnippet(text, matchIndex, queryLower.length)
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return null;
|
|
1499
|
+
};
|
|
1500
|
+
var searchSessionContent = (projectName, sessionId, filePath, queryLower) => pipe(
|
|
1501
|
+
Effect.tryPromise(() => fs6.readFile(filePath, "utf-8")),
|
|
1502
|
+
Effect.map((content) => {
|
|
1503
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1504
|
+
const match = findContentMatch(lines, queryLower, filePath);
|
|
1505
|
+
if (!match) return null;
|
|
1506
|
+
return {
|
|
1507
|
+
sessionId,
|
|
1508
|
+
projectName,
|
|
1509
|
+
title: extractTitle(extractTextContent(match.msg.message)) || `Session ${sessionId.slice(0, 8)}`,
|
|
1510
|
+
matchType: "content",
|
|
1511
|
+
snippet: match.snippet,
|
|
1512
|
+
messageUuid: match.msg.uuid,
|
|
1513
|
+
timestamp: match.msg.timestamp
|
|
1514
|
+
};
|
|
1515
|
+
}),
|
|
1516
|
+
Effect.catchAll(() => Effect.succeed(null))
|
|
1517
|
+
);
|
|
1518
|
+
var searchProjectContent = (project, queryLower, alreadyFoundIds) => Effect.gen(function* () {
|
|
1519
|
+
const projectPath = path5.join(getSessionsDir(), project.name);
|
|
1520
|
+
const files = yield* Effect.tryPromise(() => fs6.readdir(projectPath));
|
|
1521
|
+
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1522
|
+
const searchEffects = sessionFiles.map((file) => ({
|
|
1523
|
+
sessionId: file.replace(".jsonl", ""),
|
|
1524
|
+
filePath: path5.join(projectPath, file)
|
|
1525
|
+
})).filter(({ sessionId }) => !alreadyFoundIds.has(`${project.name}:${sessionId}`)).map(
|
|
1526
|
+
({ sessionId, filePath }) => searchSessionContent(project.name, sessionId, filePath, queryLower)
|
|
1527
|
+
);
|
|
1528
|
+
const results = yield* Effect.all(searchEffects, { concurrency: 10 });
|
|
1529
|
+
return results.filter((r) => r !== null);
|
|
1530
|
+
});
|
|
1167
1531
|
var searchSessions = (query, options = {}) => Effect.gen(function* () {
|
|
1168
1532
|
const { projectName, searchContent = false } = options;
|
|
1169
|
-
const results = [];
|
|
1170
1533
|
const queryLower = query.toLowerCase();
|
|
1171
1534
|
const projects = yield* listProjects;
|
|
1172
1535
|
const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1536
|
+
const titleSearchEffects = targetProjects.map(
|
|
1537
|
+
(project) => pipe(
|
|
1538
|
+
listSessions(project.name),
|
|
1539
|
+
Effect.map(
|
|
1540
|
+
(sessions) => sessions.filter((session) => (session.title ?? "").toLowerCase().includes(queryLower)).map(
|
|
1541
|
+
(session) => ({
|
|
1542
|
+
sessionId: session.id,
|
|
1543
|
+
projectName: project.name,
|
|
1544
|
+
title: session.title ?? "Untitled",
|
|
1545
|
+
matchType: "title",
|
|
1546
|
+
timestamp: session.updatedAt
|
|
1547
|
+
})
|
|
1548
|
+
)
|
|
1549
|
+
)
|
|
1550
|
+
)
|
|
1551
|
+
);
|
|
1552
|
+
const titleResultsNested = yield* Effect.all(titleSearchEffects, { concurrency: 10 });
|
|
1553
|
+
const titleResults = titleResultsNested.flat();
|
|
1554
|
+
let contentResults = [];
|
|
1188
1555
|
if (searchContent) {
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {
|
|
1196
|
-
continue;
|
|
1197
|
-
}
|
|
1198
|
-
const filePath = path4.join(projectPath, file);
|
|
1199
|
-
const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
|
|
1200
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
1201
|
-
for (const line of lines) {
|
|
1202
|
-
try {
|
|
1203
|
-
const msg = JSON.parse(line);
|
|
1204
|
-
if (msg.type !== "user" && msg.type !== "assistant") continue;
|
|
1205
|
-
const text = extractTextContent(msg.message);
|
|
1206
|
-
const textLower = text.toLowerCase();
|
|
1207
|
-
if (textLower.includes(queryLower)) {
|
|
1208
|
-
const matchIndex = textLower.indexOf(queryLower);
|
|
1209
|
-
const start = Math.max(0, matchIndex - 50);
|
|
1210
|
-
const end = Math.min(text.length, matchIndex + query.length + 50);
|
|
1211
|
-
const snippet = (start > 0 ? "..." : "") + text.slice(start, end).trim() + (end < text.length ? "..." : "");
|
|
1212
|
-
results.push({
|
|
1213
|
-
sessionId,
|
|
1214
|
-
projectName: project.name,
|
|
1215
|
-
title: extractTitle(extractTextContent(msg.message)) || `Session ${sessionId.slice(0, 8)}`,
|
|
1216
|
-
matchType: "content",
|
|
1217
|
-
snippet,
|
|
1218
|
-
messageUuid: msg.uuid,
|
|
1219
|
-
timestamp: msg.timestamp
|
|
1220
|
-
});
|
|
1221
|
-
break;
|
|
1222
|
-
}
|
|
1223
|
-
} catch {
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1556
|
+
const alreadyFoundIds = new Set(titleResults.map((r) => `${r.projectName}:${r.sessionId}`));
|
|
1557
|
+
const contentSearchEffects = targetProjects.map(
|
|
1558
|
+
(project) => searchProjectContent(project, queryLower, alreadyFoundIds)
|
|
1559
|
+
);
|
|
1560
|
+
const contentResultsNested = yield* Effect.all(contentSearchEffects, { concurrency: 5 });
|
|
1561
|
+
contentResults = contentResultsNested.flat();
|
|
1228
1562
|
}
|
|
1229
|
-
|
|
1563
|
+
const allResults = [...titleResults, ...contentResults];
|
|
1564
|
+
return allResults.sort((a, b) => {
|
|
1230
1565
|
const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
|
|
1231
1566
|
const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
|
|
1232
1567
|
return dateB - dateA;
|
|
1233
1568
|
});
|
|
1234
1569
|
});
|
|
1235
|
-
var
|
|
1236
|
-
const
|
|
1237
|
-
const
|
|
1238
|
-
const
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
}
|
|
1254
|
-
const projectFiles = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
|
|
1255
|
-
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1256
|
-
for (const file of allJsonlFiles) {
|
|
1257
|
-
try {
|
|
1258
|
-
const otherFilePath = path4.join(projectPath, file);
|
|
1259
|
-
const otherContent = yield* Effect.tryPromise(() => fs4.readFile(otherFilePath, "utf-8"));
|
|
1260
|
-
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
1261
|
-
for (const line of otherLines) {
|
|
1262
|
-
try {
|
|
1263
|
-
const msg = JSON.parse(line);
|
|
1264
|
-
if (msg.type === "summary" && typeof msg.summary === "string" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
1265
|
-
const targetMsg = messages.find((m) => m.uuid === msg.leafUuid);
|
|
1266
|
-
summaries.push({
|
|
1267
|
-
summary: msg.summary,
|
|
1268
|
-
leafUuid: msg.leafUuid,
|
|
1269
|
-
timestamp: targetMsg?.timestamp ?? msg.timestamp
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
} catch {
|
|
1570
|
+
var getSessionFiles = (projectName, sessionId) => Effect.gen(function* () {
|
|
1571
|
+
const messages = yield* readSession(projectName, sessionId);
|
|
1572
|
+
const fileChanges = [];
|
|
1573
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
1574
|
+
for (const msg of messages) {
|
|
1575
|
+
if (msg.type === "file-history-snapshot") {
|
|
1576
|
+
const snapshot = msg;
|
|
1577
|
+
const backups = snapshot.snapshot?.trackedFileBackups;
|
|
1578
|
+
if (backups && typeof backups === "object") {
|
|
1579
|
+
for (const filePath of Object.keys(backups)) {
|
|
1580
|
+
if (!seenFiles.has(filePath)) {
|
|
1581
|
+
seenFiles.add(filePath);
|
|
1582
|
+
fileChanges.push({
|
|
1583
|
+
path: filePath,
|
|
1584
|
+
action: "modified",
|
|
1585
|
+
timestamp: snapshot.snapshot?.timestamp,
|
|
1586
|
+
messageUuid: snapshot.messageId ?? msg.uuid
|
|
1587
|
+
});
|
|
1273
1588
|
}
|
|
1274
1589
|
}
|
|
1275
|
-
} catch {
|
|
1276
1590
|
}
|
|
1277
1591
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
(m) => m.type === "user" || m.type === "assistant"
|
|
1294
|
-
);
|
|
1295
|
-
const firstMessage = userAssistantMessages[0];
|
|
1296
|
-
const lastMessage = userAssistantMessages[userAssistantMessages.length - 1];
|
|
1297
|
-
const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId);
|
|
1298
|
-
const agents = [];
|
|
1299
|
-
for (const agentId of linkedAgentIds) {
|
|
1300
|
-
const agentPath = path4.join(projectPath, `${agentId}.jsonl`);
|
|
1301
|
-
try {
|
|
1302
|
-
const agentContent = yield* Effect.tryPromise(() => fs4.readFile(agentPath, "utf-8"));
|
|
1303
|
-
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
1304
|
-
const agentMsgs = agentLines.map((l) => JSON.parse(l));
|
|
1305
|
-
const agentUserAssistant = agentMsgs.filter(
|
|
1306
|
-
(m) => m.type === "user" || m.type === "assistant"
|
|
1307
|
-
);
|
|
1308
|
-
let agentName;
|
|
1309
|
-
const firstAgentMsg = agentMsgs.find((m) => m.type === "user");
|
|
1310
|
-
if (firstAgentMsg) {
|
|
1311
|
-
const text = extractTextContent(firstAgentMsg.message);
|
|
1312
|
-
if (text) {
|
|
1313
|
-
agentName = extractTitle(text);
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
agents.push({
|
|
1317
|
-
id: agentId,
|
|
1318
|
-
name: agentName,
|
|
1319
|
-
messageCount: agentUserAssistant.length
|
|
1320
|
-
});
|
|
1321
|
-
} catch {
|
|
1322
|
-
agents.push({
|
|
1323
|
-
id: agentId,
|
|
1324
|
-
messageCount: 0
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
const todos = yield* findLinkedTodos(sessionId, linkedAgentIds);
|
|
1329
|
-
return {
|
|
1330
|
-
id: sessionId,
|
|
1331
|
-
projectName,
|
|
1332
|
-
title,
|
|
1333
|
-
customTitle,
|
|
1334
|
-
currentSummary: summaries[0]?.summary,
|
|
1335
|
-
messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : summaries.length > 0 ? 1 : 0,
|
|
1336
|
-
createdAt: firstMessage?.timestamp ?? void 0,
|
|
1337
|
-
updatedAt: lastMessage?.timestamp ?? void 0,
|
|
1338
|
-
summaries,
|
|
1339
|
-
agents,
|
|
1340
|
-
todos,
|
|
1341
|
-
lastCompactBoundaryUuid
|
|
1342
|
-
};
|
|
1343
|
-
});
|
|
1344
|
-
var loadSessionTreeData = (projectName, sessionId) => loadSessionTreeDataInternal(projectName, sessionId, void 0);
|
|
1345
|
-
var loadProjectTreeData = (projectName) => Effect.gen(function* () {
|
|
1346
|
-
const project = (yield* listProjects).find((p) => p.name === projectName);
|
|
1347
|
-
if (!project) {
|
|
1348
|
-
return null;
|
|
1349
|
-
}
|
|
1350
|
-
const projectPath = path4.join(getSessionsDir(), projectName);
|
|
1351
|
-
const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
|
|
1352
|
-
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1353
|
-
const globalUuidMap = /* @__PURE__ */ new Map();
|
|
1354
|
-
const allSummaries = [];
|
|
1355
|
-
const allJsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
1356
|
-
yield* Effect.all(
|
|
1357
|
-
allJsonlFiles.map(
|
|
1358
|
-
(file) => Effect.gen(function* () {
|
|
1359
|
-
const filePath = path4.join(projectPath, file);
|
|
1360
|
-
const fileSessionId = file.replace(".jsonl", "");
|
|
1361
|
-
try {
|
|
1362
|
-
const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
|
|
1363
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
1364
|
-
for (const line of lines) {
|
|
1365
|
-
try {
|
|
1366
|
-
const msg = JSON.parse(line);
|
|
1367
|
-
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1368
|
-
globalUuidMap.set(msg.uuid, {
|
|
1369
|
-
sessionId: fileSessionId,
|
|
1370
|
-
timestamp: msg.timestamp
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
if (msg.messageId && typeof msg.messageId === "string") {
|
|
1374
|
-
globalUuidMap.set(msg.messageId, {
|
|
1375
|
-
sessionId: fileSessionId,
|
|
1376
|
-
timestamp: msg.snapshot?.timestamp
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
if (msg.type === "summary" && typeof msg.summary === "string") {
|
|
1380
|
-
allSummaries.push({
|
|
1381
|
-
summary: msg.summary,
|
|
1382
|
-
leafUuid: msg.leafUuid,
|
|
1383
|
-
timestamp: msg.timestamp
|
|
1592
|
+
if (msg.type === "assistant" && msg.message?.content) {
|
|
1593
|
+
const content = msg.message.content;
|
|
1594
|
+
if (Array.isArray(content)) {
|
|
1595
|
+
for (const item of content) {
|
|
1596
|
+
if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
|
|
1597
|
+
const toolUse = item;
|
|
1598
|
+
if ((toolUse.name === "Write" || toolUse.name === "Edit") && toolUse.input?.file_path) {
|
|
1599
|
+
const filePath = toolUse.input.file_path;
|
|
1600
|
+
if (!seenFiles.has(filePath)) {
|
|
1601
|
+
seenFiles.add(filePath);
|
|
1602
|
+
fileChanges.push({
|
|
1603
|
+
path: filePath,
|
|
1604
|
+
action: toolUse.name === "Write" ? "created" : "modified",
|
|
1605
|
+
timestamp: msg.timestamp,
|
|
1606
|
+
messageUuid: msg.uuid
|
|
1384
1607
|
});
|
|
1385
1608
|
}
|
|
1386
|
-
} catch {
|
|
1387
1609
|
}
|
|
1388
1610
|
}
|
|
1389
|
-
} catch {
|
|
1390
|
-
}
|
|
1391
|
-
})
|
|
1392
|
-
),
|
|
1393
|
-
{ concurrency: 20 }
|
|
1394
|
-
);
|
|
1395
|
-
const summariesByTargetSession = /* @__PURE__ */ new Map();
|
|
1396
|
-
for (const summaryData of allSummaries) {
|
|
1397
|
-
if (summaryData.leafUuid) {
|
|
1398
|
-
const targetInfo = globalUuidMap.get(summaryData.leafUuid);
|
|
1399
|
-
if (targetInfo) {
|
|
1400
|
-
const targetSessionId = targetInfo.sessionId;
|
|
1401
|
-
if (!summariesByTargetSession.has(targetSessionId)) {
|
|
1402
|
-
summariesByTargetSession.set(targetSessionId, []);
|
|
1403
1611
|
}
|
|
1404
|
-
summariesByTargetSession.get(targetSessionId).push({
|
|
1405
|
-
summary: summaryData.summary,
|
|
1406
|
-
leafUuid: summaryData.leafUuid,
|
|
1407
|
-
timestamp: targetInfo.timestamp ?? summaryData.timestamp
|
|
1408
|
-
});
|
|
1409
1612
|
}
|
|
1410
1613
|
}
|
|
1411
1614
|
}
|
|
1412
|
-
const sessions = yield* Effect.all(
|
|
1413
|
-
sessionFiles.map((file) => {
|
|
1414
|
-
const sessionId = file.replace(".jsonl", "");
|
|
1415
|
-
return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession);
|
|
1416
|
-
}),
|
|
1417
|
-
{ concurrency: 10 }
|
|
1418
|
-
);
|
|
1419
|
-
const sortedSessions = sessions.sort((a, b) => {
|
|
1420
|
-
const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
1421
|
-
const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
1422
|
-
return dateB - dateA;
|
|
1423
|
-
});
|
|
1424
|
-
const filteredSessions = sortedSessions.filter((s) => {
|
|
1425
|
-
if (isErrorSessionTitle(s.title)) return false;
|
|
1426
|
-
if (isErrorSessionTitle(s.customTitle)) return false;
|
|
1427
|
-
if (isErrorSessionTitle(s.currentSummary)) return false;
|
|
1428
|
-
return true;
|
|
1429
|
-
});
|
|
1430
1615
|
return {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
sessions: filteredSessions
|
|
1616
|
+
sessionId,
|
|
1617
|
+
projectName,
|
|
1618
|
+
files: fileChanges,
|
|
1619
|
+
totalChanges: fileChanges.length
|
|
1436
1620
|
};
|
|
1437
1621
|
});
|
|
1438
1622
|
|
|
1439
|
-
export { listProjects as a, loadProjectTreeData as b, clearSessions as c, deleteMessage as d, listSessions as e, deleteSession as f, getSessionsDir as g, readSession as h, getSessionFiles as i, renameSession as j, folderNameToPath as k, loadAgentMessages as l, moveSession as m, splitSession as n, loadSessionTreeData as o, previewCleanup as p, pathToFolderName as q, restoreMessage as r, searchSessions as s, sortProjects as t, getDisplayTitle as u,
|
|
1440
|
-
//# sourceMappingURL=index3-
|
|
1623
|
+
export { listProjects as a, loadProjectTreeData as b, clearSessions as c, deleteMessage as d, listSessions as e, deleteSession as f, getSessionsDir as g, readSession as h, getSessionFiles as i, renameSession as j, folderNameToPath as k, loadAgentMessages as l, moveSession as m, splitSession as n, loadSessionTreeData as o, previewCleanup as p, pathToFolderName as q, restoreMessage as r, searchSessions as s, sortProjects as t, getDisplayTitle as u, parseCommandMessage as v, deleteMessageWithChainRepair as w, maskHomePath as x };
|
|
1624
|
+
//# sourceMappingURL=index3-D8wHvUHX.js.map
|