@claude-sessions/web 0.3.7 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/_app/immutable/assets/0.B02QGPuL.css +1 -0
- package/build/client/_app/immutable/assets/0.B02QGPuL.css.br +0 -0
- package/build/client/_app/immutable/assets/0.B02QGPuL.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/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/{Dbjp-HdT.js → D49tNEXH.js} +1 -1
- package/build/client/_app/immutable/chunks/D49tNEXH.js.br +0 -0
- package/build/client/_app/immutable/chunks/D49tNEXH.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/DVh32r5c.js +90 -0
- package/build/client/_app/immutable/chunks/DVh32r5c.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVh32r5c.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dzg_BFdu.js +1 -0
- package/build/client/_app/immutable/chunks/Dzg_BFdu.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dzg_BFdu.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.Btq_bE7X.js +2 -0
- package/build/client/_app/immutable/entry/app.Btq_bE7X.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Btq_bE7X.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DiKqSoAN.js +1 -0
- package/build/client/_app/immutable/entry/start.DiKqSoAN.js.br +2 -0
- package/build/client/_app/immutable/entry/start.DiKqSoAN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.D4LDvqQb.js → 0.CjrUlXhs.js} +1 -1
- package/build/client/_app/immutable/nodes/0.CjrUlXhs.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CjrUlXhs.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.C_mir9rr.js +1 -0
- package/build/client/_app/immutable/nodes/1.C_mir9rr.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.C_mir9rr.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.C1TvKf9x.js +4 -0
- package/build/client/_app/immutable/nodes/2.C1TvKf9x.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.C1TvKf9x.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.BqDX5X09.js → 3.C4KCGMhg.js} +1 -1
- package/build/client/_app/immutable/nodes/3.C4KCGMhg.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C4KCGMhg.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-_4Y5P6Pu.js +17 -0
- package/build/server/chunks/{0-DkD09WcK.js.map → 0-_4Y5P6Pu.js.map} +1 -1
- package/build/server/chunks/1-BFEz-9-0.js +9 -0
- package/build/server/chunks/{1-CjtVgNYW.js.map → 1-BFEz-9-0.js.map} +1 -1
- package/build/server/chunks/2-BJBIDU_y.js +9 -0
- package/build/server/chunks/{2-wstlfOP5.js.map → 2-BJBIDU_y.js.map} +1 -1
- package/build/server/chunks/3-fx1FyCiP.js +9 -0
- package/build/server/chunks/{3-Cdn9_0OG.js.map → 3-fx1FyCiP.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-Crka3UoV.js} +4 -4
- package/build/server/chunks/Toast-Crka3UoV.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-COfbk473.js → _layout.svelte-IUZv8Y-p.js} +7 -6
- package/build/server/chunks/_layout.svelte-IUZv8Y-p.js.map +1 -0
- package/build/server/chunks/{_page.svelte-C5Xngf9U.js → _page.svelte-CVZlXeAH.js} +373 -188
- package/build/server/chunks/_page.svelte-CVZlXeAH.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CKSi7jlX.js → _page.svelte-Hxh7dTyf.js} +8 -7
- package/build/server/chunks/_page.svelte-Hxh7dTyf.js.map +1 -0
- package/build/server/chunks/{_server.ts-QYg-pRKw.js → _server.ts-B1VENOjw.js} +3 -2
- package/build/server/chunks/{_server.ts-QYg-pRKw.js.map → _server.ts-B1VENOjw.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-C46vcb_V.js → _server.ts-B3x7amtu.js} +3 -2
- package/build/server/chunks/{_server.ts-C46vcb_V.js.map → _server.ts-B3x7amtu.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DRy1BtKz.js → _server.ts-B7mAMmzV.js} +3 -2
- package/build/server/chunks/{_server.ts-DRy1BtKz.js.map → _server.ts-B7mAMmzV.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Bzg6xSYv.js → _server.ts-BDo-cBep.js} +3 -2
- package/build/server/chunks/{_server.ts-Bzg6xSYv.js.map → _server.ts-BDo-cBep.js.map} +1 -1
- package/build/server/chunks/{_server.ts-msjVztXK.js → _server.ts-BEMET_M9.js} +3 -2
- package/build/server/chunks/{_server.ts-msjVztXK.js.map → _server.ts-BEMET_M9.js.map} +1 -1
- package/build/server/chunks/{_server.ts-a85289si.js → _server.ts-B_tHvJuE.js} +3 -2
- package/build/server/chunks/_server.ts-B_tHvJuE.js.map +1 -0
- package/build/server/chunks/{_server.ts-CF7wNACi.js → _server.ts-CYy3E-wy.js} +3 -2
- package/build/server/chunks/{_server.ts-CF7wNACi.js.map → _server.ts-CYy3E-wy.js.map} +1 -1
- package/build/server/chunks/_server.ts-Cn3u85fU.js +26 -0
- package/build/server/chunks/_server.ts-Cn3u85fU.js.map +1 -0
- package/build/server/chunks/{_server.ts-bj28Awpj.js → _server.ts-CwCozCTU.js} +3 -2
- package/build/server/chunks/_server.ts-CwCozCTU.js.map +1 -0
- package/build/server/chunks/{_server.ts-IjEZJmtD.js → _server.ts-Cyd5L7R7.js} +3 -2
- package/build/server/chunks/{_server.ts-IjEZJmtD.js.map → _server.ts-Cyd5L7R7.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BNGntgHD.js → _server.ts-D_nsNW3s.js} +7 -3
- package/build/server/chunks/_server.ts-D_nsNW3s.js.map +1 -0
- package/build/server/chunks/{_server.ts-CBabn3Le.js → _server.ts-Dm4cFvT_.js} +3 -2
- package/build/server/chunks/{_server.ts-CBabn3Le.js.map → _server.ts-Dm4cFvT_.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Dtvv8PMc.js → _server.ts-SM5kuocx.js} +3 -2
- package/build/server/chunks/{_server.ts-Dtvv8PMc.js.map → _server.ts-SM5kuocx.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CBCUnjeX.js → _server.ts-X0UsKGpZ.js} +3 -2
- package/build/server/chunks/{_server.ts-CBCUnjeX.js.map → _server.ts-X0UsKGpZ.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CFQ3_7g1.js → _server.ts-kaRz-iNE.js} +3 -2
- package/build/server/chunks/{_server.ts-CFQ3_7g1.js.map → _server.ts-kaRz-iNE.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-CcQtlmu9.js} +568 -421
- package/build/server/chunks/index3-CcQtlmu9.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 +18 -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 +0 -1
- 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.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
1
|
import * as fs 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 fs5 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),
|
|
@@ -18,8 +19,8 @@ var createLogger = (namespace) => ({
|
|
|
18
19
|
error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args)
|
|
19
20
|
});
|
|
20
21
|
var log = createLogger("paths");
|
|
21
|
-
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR ||
|
|
22
|
-
var getTodosDir = () =>
|
|
22
|
+
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR || path5.join(os.homedir(), ".claude", "projects");
|
|
23
|
+
var getTodosDir = () => path5.join(os.homedir(), ".claude", "todos");
|
|
23
24
|
var extractCwdFromContent = (content) => {
|
|
24
25
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
25
26
|
for (const line of lines) {
|
|
@@ -65,7 +66,7 @@ var pathToFolderName = (absolutePath) => {
|
|
|
65
66
|
return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
|
|
66
67
|
};
|
|
67
68
|
var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
|
|
68
|
-
const basename3 =
|
|
69
|
+
const basename3 = path5.basename(filePath);
|
|
69
70
|
try {
|
|
70
71
|
const content = fileSystem.readFileSync(filePath, "utf-8");
|
|
71
72
|
const cwd = extractCwdFromContent(content);
|
|
@@ -85,12 +86,12 @@ var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
|
|
|
85
86
|
}
|
|
86
87
|
};
|
|
87
88
|
var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs, logger2 = log) => {
|
|
88
|
-
const projectDir =
|
|
89
|
+
const projectDir = path5.join(sessionsDir, folderName);
|
|
89
90
|
try {
|
|
90
91
|
const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
|
|
91
92
|
const cwdList = [];
|
|
92
93
|
for (const f of files) {
|
|
93
|
-
const cwd = tryGetCwdFromFile(
|
|
94
|
+
const cwd = tryGetCwdFromFile(path5.join(projectDir, f), fileSystem, logger2);
|
|
94
95
|
if (cwd !== null) {
|
|
95
96
|
cwdList.push(cwd);
|
|
96
97
|
}
|
|
@@ -137,14 +138,19 @@ var extractTextContent = (message) => {
|
|
|
137
138
|
}
|
|
138
139
|
return "";
|
|
139
140
|
};
|
|
141
|
+
var parseCommandMessage = (content) => {
|
|
142
|
+
const name = content?.match(/<command-name>([^<]+)<\/command-name>/)?.[1] ?? "";
|
|
143
|
+
const message = content?.match(/<command-message>([^<]+)<\/command-message>/)?.[1] ?? "";
|
|
144
|
+
return { name, message };
|
|
145
|
+
};
|
|
140
146
|
var extractTitle = (text) => {
|
|
141
147
|
if (!text) return "Untitled";
|
|
148
|
+
const { name } = parseCommandMessage(text);
|
|
149
|
+
if (name) return name;
|
|
142
150
|
let cleaned = text.replace(/<ide_[^>]*>[\s\S]*?<\/ide_[^>]*>/g, "").trim();
|
|
143
151
|
if (!cleaned) return "Untitled";
|
|
144
152
|
if (cleaned.includes("\n\n")) {
|
|
145
153
|
cleaned = cleaned.split("\n\n")[0];
|
|
146
|
-
} else if (cleaned.includes("\n")) {
|
|
147
|
-
cleaned = cleaned.split("\n")[0];
|
|
148
154
|
}
|
|
149
155
|
if (cleaned.length > 100) {
|
|
150
156
|
return cleaned.slice(0, 100) + "...";
|
|
@@ -177,7 +183,13 @@ var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallb
|
|
|
177
183
|
if (currentSummary) {
|
|
178
184
|
return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
|
|
179
185
|
}
|
|
180
|
-
if (title && title !== "Untitled")
|
|
186
|
+
if (title && title !== "Untitled") {
|
|
187
|
+
if (title.includes("<command-name>")) {
|
|
188
|
+
const { name } = parseCommandMessage(title);
|
|
189
|
+
if (name) return name;
|
|
190
|
+
}
|
|
191
|
+
return title;
|
|
192
|
+
}
|
|
181
193
|
return fallback;
|
|
182
194
|
};
|
|
183
195
|
var replaceMessageContent = (msg, text) => ({
|
|
@@ -231,14 +243,18 @@ var sortProjects = (projects, options = {}) => {
|
|
|
231
243
|
return a.displayName.localeCompare(b.displayName);
|
|
232
244
|
});
|
|
233
245
|
};
|
|
246
|
+
var getSessionSortTimestamp = (session) => {
|
|
247
|
+
const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt;
|
|
248
|
+
return timestampStr ? new Date(timestampStr).getTime() : 0;
|
|
249
|
+
};
|
|
234
250
|
var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
235
|
-
const projectPath =
|
|
236
|
-
const files = yield* Effect.tryPromise(() =>
|
|
251
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
252
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
237
253
|
const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
238
254
|
const linkedAgents = [];
|
|
239
255
|
for (const agentFile of agentFiles) {
|
|
240
|
-
const filePath =
|
|
241
|
-
const content = yield* Effect.tryPromise(() =>
|
|
256
|
+
const filePath = path5.join(projectPath, agentFile);
|
|
257
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
242
258
|
const firstLine = content.split("\n")[0];
|
|
243
259
|
if (firstLine) {
|
|
244
260
|
try {
|
|
@@ -253,21 +269,21 @@ var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
253
269
|
return linkedAgents;
|
|
254
270
|
});
|
|
255
271
|
var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
256
|
-
const projectPath =
|
|
257
|
-
const files = yield* Effect.tryPromise(() =>
|
|
272
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
273
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
258
274
|
const sessionIds = new Set(
|
|
259
275
|
files.filter((f) => !f.startsWith("agent-") && f.endsWith(".jsonl")).map((f) => f.replace(".jsonl", ""))
|
|
260
276
|
);
|
|
261
277
|
const orphanAgents = [];
|
|
262
278
|
const checkAgentFile = async (filePath) => {
|
|
263
279
|
try {
|
|
264
|
-
const content = await
|
|
280
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
265
281
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
266
282
|
const firstLine = lines[0];
|
|
267
283
|
if (!firstLine) return null;
|
|
268
284
|
const parsed = JSON.parse(firstLine);
|
|
269
285
|
if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {
|
|
270
|
-
const fileName =
|
|
286
|
+
const fileName = path5.basename(filePath);
|
|
271
287
|
return {
|
|
272
288
|
agentId: fileName.replace(".jsonl", ""),
|
|
273
289
|
sessionId: parsed.sessionId,
|
|
@@ -280,27 +296,27 @@ var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
|
280
296
|
};
|
|
281
297
|
const rootAgentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
282
298
|
for (const agentFile of rootAgentFiles) {
|
|
283
|
-
const filePath =
|
|
299
|
+
const filePath = path5.join(projectPath, agentFile);
|
|
284
300
|
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
285
301
|
if (orphan) {
|
|
286
302
|
orphanAgents.push({ ...orphan, filePath });
|
|
287
303
|
}
|
|
288
304
|
}
|
|
289
305
|
for (const entry of files) {
|
|
290
|
-
const entryPath =
|
|
291
|
-
const
|
|
292
|
-
if (
|
|
293
|
-
const subagentsPath =
|
|
306
|
+
const entryPath = path5.join(projectPath, entry);
|
|
307
|
+
const stat4 = yield* Effect.tryPromise(() => fs5.stat(entryPath).catch(() => null));
|
|
308
|
+
if (stat4?.isDirectory() && !entry.startsWith(".")) {
|
|
309
|
+
const subagentsPath = path5.join(entryPath, "subagents");
|
|
294
310
|
const subagentsExists = yield* Effect.tryPromise(
|
|
295
|
-
() =>
|
|
311
|
+
() => fs5.stat(subagentsPath).then(() => true).catch(() => false)
|
|
296
312
|
);
|
|
297
313
|
if (subagentsExists) {
|
|
298
314
|
const subagentFiles = yield* Effect.tryPromise(
|
|
299
|
-
() =>
|
|
315
|
+
() => fs5.readdir(subagentsPath).catch(() => [])
|
|
300
316
|
);
|
|
301
317
|
for (const subagentFile of subagentFiles) {
|
|
302
318
|
if (subagentFile.startsWith("agent-") && subagentFile.endsWith(".jsonl")) {
|
|
303
|
-
const filePath =
|
|
319
|
+
const filePath = path5.join(subagentsPath, subagentFile);
|
|
304
320
|
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
305
321
|
if (orphan) {
|
|
306
322
|
orphanAgents.push({ ...orphan, filePath });
|
|
@@ -317,7 +333,7 @@ var findOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
317
333
|
return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }));
|
|
318
334
|
});
|
|
319
335
|
var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
320
|
-
const projectPath =
|
|
336
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
321
337
|
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
322
338
|
const deletedAgents = [];
|
|
323
339
|
const backedUpAgents = [];
|
|
@@ -325,40 +341,40 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
325
341
|
let backupDirCreated = false;
|
|
326
342
|
const foldersToCheck = /* @__PURE__ */ new Set();
|
|
327
343
|
for (const orphan of orphans) {
|
|
328
|
-
const parentDir =
|
|
344
|
+
const parentDir = path5.dirname(orphan.filePath);
|
|
329
345
|
if (parentDir.endsWith("/subagents") || parentDir.endsWith("\\subagents")) {
|
|
330
346
|
foldersToCheck.add(parentDir);
|
|
331
347
|
}
|
|
332
348
|
if (orphan.lineCount <= 2) {
|
|
333
|
-
yield* Effect.tryPromise(() =>
|
|
349
|
+
yield* Effect.tryPromise(() => fs5.unlink(orphan.filePath));
|
|
334
350
|
deletedAgents.push(orphan.agentId);
|
|
335
351
|
} else {
|
|
336
352
|
if (!backupDirCreated) {
|
|
337
|
-
const backupDir2 =
|
|
338
|
-
yield* Effect.tryPromise(() =>
|
|
353
|
+
const backupDir2 = path5.join(projectPath, ".bak");
|
|
354
|
+
yield* Effect.tryPromise(() => fs5.mkdir(backupDir2, { recursive: true }));
|
|
339
355
|
backupDirCreated = true;
|
|
340
356
|
}
|
|
341
|
-
const backupDir =
|
|
342
|
-
const agentBackupPath =
|
|
343
|
-
yield* Effect.tryPromise(() =>
|
|
357
|
+
const backupDir = path5.join(projectPath, ".bak");
|
|
358
|
+
const agentBackupPath = path5.join(backupDir, `${orphan.agentId}.jsonl`);
|
|
359
|
+
yield* Effect.tryPromise(() => fs5.rename(orphan.filePath, agentBackupPath));
|
|
344
360
|
backedUpAgents.push(orphan.agentId);
|
|
345
361
|
}
|
|
346
362
|
}
|
|
347
363
|
for (const subagentsDir of foldersToCheck) {
|
|
348
364
|
const isEmpty = yield* Effect.tryPromise(async () => {
|
|
349
|
-
const files = await
|
|
365
|
+
const files = await fs5.readdir(subagentsDir);
|
|
350
366
|
return files.length === 0;
|
|
351
367
|
});
|
|
352
368
|
if (isEmpty) {
|
|
353
|
-
yield* Effect.tryPromise(() =>
|
|
369
|
+
yield* Effect.tryPromise(() => fs5.rmdir(subagentsDir));
|
|
354
370
|
cleanedFolders.push(subagentsDir);
|
|
355
|
-
const sessionDir =
|
|
371
|
+
const sessionDir = path5.dirname(subagentsDir);
|
|
356
372
|
const sessionDirEmpty = yield* Effect.tryPromise(async () => {
|
|
357
|
-
const files = await
|
|
373
|
+
const files = await fs5.readdir(sessionDir);
|
|
358
374
|
return files.length === 0;
|
|
359
375
|
});
|
|
360
376
|
if (sessionDirEmpty) {
|
|
361
|
-
yield* Effect.tryPromise(() =>
|
|
377
|
+
yield* Effect.tryPromise(() => fs5.rmdir(sessionDir));
|
|
362
378
|
cleanedFolders.push(sessionDir);
|
|
363
379
|
}
|
|
364
380
|
}
|
|
@@ -375,9 +391,9 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
375
391
|
};
|
|
376
392
|
});
|
|
377
393
|
var loadAgentMessages = (projectName, _sessionId, agentId) => Effect.gen(function* () {
|
|
378
|
-
const projectPath =
|
|
379
|
-
const agentFilePath =
|
|
380
|
-
const content = yield* Effect.tryPromise(() =>
|
|
394
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
395
|
+
const agentFilePath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
396
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(agentFilePath, "utf-8"));
|
|
381
397
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
382
398
|
const messages = [];
|
|
383
399
|
for (const line of lines) {
|
|
@@ -395,29 +411,24 @@ var loadAgentMessages = (projectName, _sessionId, agentId) => Effect.gen(functio
|
|
|
395
411
|
var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
396
412
|
const todosDir = getTodosDir();
|
|
397
413
|
const exists = yield* Effect.tryPromise(
|
|
398
|
-
() =>
|
|
414
|
+
() => fs5.access(todosDir).then(() => true).catch(() => false)
|
|
399
415
|
);
|
|
400
416
|
if (!exists) {
|
|
401
|
-
return
|
|
402
|
-
sessionId,
|
|
403
|
-
sessionTodos: [],
|
|
404
|
-
agentTodos: [],
|
|
405
|
-
hasTodos: false
|
|
406
|
-
};
|
|
417
|
+
return void 0;
|
|
407
418
|
}
|
|
408
|
-
const sessionTodoPath =
|
|
419
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
409
420
|
let sessionTodos = [];
|
|
410
421
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
411
|
-
() =>
|
|
422
|
+
() => fs5.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
412
423
|
);
|
|
413
424
|
if (sessionTodoExists) {
|
|
414
|
-
const content = yield* Effect.tryPromise(() =>
|
|
425
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(sessionTodoPath, "utf-8"));
|
|
415
426
|
try {
|
|
416
427
|
sessionTodos = JSON.parse(content);
|
|
417
428
|
} catch {
|
|
418
429
|
}
|
|
419
430
|
}
|
|
420
|
-
const allFiles = yield* Effect.tryPromise(() =>
|
|
431
|
+
const allFiles = yield* Effect.tryPromise(() => fs5.readdir(todosDir));
|
|
421
432
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
422
433
|
const discoveredAgentIds = new Set(agentIds);
|
|
423
434
|
for (const file of allFiles) {
|
|
@@ -429,12 +440,12 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
429
440
|
const agentTodos = [];
|
|
430
441
|
for (const agentId of discoveredAgentIds) {
|
|
431
442
|
const shortAgentId = agentId.replace("agent-", "");
|
|
432
|
-
const agentTodoPath =
|
|
443
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
433
444
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
434
|
-
() =>
|
|
445
|
+
() => fs5.access(agentTodoPath).then(() => true).catch(() => false)
|
|
435
446
|
);
|
|
436
447
|
if (agentTodoExists) {
|
|
437
|
-
const content = yield* Effect.tryPromise(() =>
|
|
448
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(agentTodoPath, "utf-8"));
|
|
438
449
|
try {
|
|
439
450
|
const todos = JSON.parse(content);
|
|
440
451
|
if (todos.length > 0) {
|
|
@@ -455,22 +466,22 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
455
466
|
var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
456
467
|
const todosDir = getTodosDir();
|
|
457
468
|
const exists = yield* Effect.tryPromise(
|
|
458
|
-
() =>
|
|
469
|
+
() => fs5.access(todosDir).then(() => true).catch(() => false)
|
|
459
470
|
);
|
|
460
471
|
if (!exists) return false;
|
|
461
|
-
const sessionTodoPath =
|
|
472
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
462
473
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
463
|
-
() =>
|
|
474
|
+
() => fs5.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
464
475
|
);
|
|
465
476
|
if (sessionTodoExists) {
|
|
466
|
-
const content = yield* Effect.tryPromise(() =>
|
|
477
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(sessionTodoPath, "utf-8"));
|
|
467
478
|
try {
|
|
468
479
|
const todos = JSON.parse(content);
|
|
469
480
|
if (todos.length > 0) return true;
|
|
470
481
|
} catch {
|
|
471
482
|
}
|
|
472
483
|
}
|
|
473
|
-
const allFiles = yield* Effect.tryPromise(() =>
|
|
484
|
+
const allFiles = yield* Effect.tryPromise(() => fs5.readdir(todosDir));
|
|
474
485
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
475
486
|
const discoveredAgentIds = new Set(agentIds);
|
|
476
487
|
for (const file of allFiles) {
|
|
@@ -481,12 +492,12 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
481
492
|
}
|
|
482
493
|
for (const agentId of discoveredAgentIds) {
|
|
483
494
|
const shortAgentId = agentId.replace("agent-", "");
|
|
484
|
-
const agentTodoPath =
|
|
495
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
485
496
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
486
|
-
() =>
|
|
497
|
+
() => fs5.access(agentTodoPath).then(() => true).catch(() => false)
|
|
487
498
|
);
|
|
488
499
|
if (agentTodoExists) {
|
|
489
|
-
const content = yield* Effect.tryPromise(() =>
|
|
500
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(agentTodoPath, "utf-8"));
|
|
490
501
|
try {
|
|
491
502
|
const todos = JSON.parse(content);
|
|
492
503
|
if (todos.length > 0) return true;
|
|
@@ -499,30 +510,30 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect.gen(function* () {
|
|
|
499
510
|
var deleteLinkedTodos = (sessionId, agentIds) => Effect.gen(function* () {
|
|
500
511
|
const todosDir = getTodosDir();
|
|
501
512
|
const exists = yield* Effect.tryPromise(
|
|
502
|
-
() =>
|
|
513
|
+
() => fs5.access(todosDir).then(() => true).catch(() => false)
|
|
503
514
|
);
|
|
504
515
|
if (!exists) return { deletedCount: 0 };
|
|
505
|
-
const backupDir =
|
|
506
|
-
yield* Effect.tryPromise(() =>
|
|
516
|
+
const backupDir = path5.join(todosDir, ".bak");
|
|
517
|
+
yield* Effect.tryPromise(() => fs5.mkdir(backupDir, { recursive: true }));
|
|
507
518
|
let deletedCount = 0;
|
|
508
|
-
const sessionTodoPath =
|
|
519
|
+
const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
|
|
509
520
|
const sessionTodoExists = yield* Effect.tryPromise(
|
|
510
|
-
() =>
|
|
521
|
+
() => fs5.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
511
522
|
);
|
|
512
523
|
if (sessionTodoExists) {
|
|
513
|
-
const backupPath =
|
|
514
|
-
yield* Effect.tryPromise(() =>
|
|
524
|
+
const backupPath = path5.join(backupDir, `${sessionId}.json`);
|
|
525
|
+
yield* Effect.tryPromise(() => fs5.rename(sessionTodoPath, backupPath));
|
|
515
526
|
deletedCount++;
|
|
516
527
|
}
|
|
517
528
|
for (const agentId of agentIds) {
|
|
518
529
|
const shortAgentId = agentId.replace("agent-", "");
|
|
519
|
-
const agentTodoPath =
|
|
530
|
+
const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
520
531
|
const agentTodoExists = yield* Effect.tryPromise(
|
|
521
|
-
() =>
|
|
532
|
+
() => fs5.access(agentTodoPath).then(() => true).catch(() => false)
|
|
522
533
|
);
|
|
523
534
|
if (agentTodoExists) {
|
|
524
|
-
const backupPath =
|
|
525
|
-
yield* Effect.tryPromise(() =>
|
|
535
|
+
const backupPath = path5.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
536
|
+
yield* Effect.tryPromise(() => fs5.rename(agentTodoPath, backupPath));
|
|
526
537
|
deletedCount++;
|
|
527
538
|
}
|
|
528
539
|
}
|
|
@@ -533,23 +544,23 @@ var findOrphanTodos = () => Effect.gen(function* () {
|
|
|
533
544
|
const sessionsDir = getSessionsDir();
|
|
534
545
|
const [todosExists, sessionsExists] = yield* Effect.all([
|
|
535
546
|
Effect.tryPromise(
|
|
536
|
-
() =>
|
|
547
|
+
() => fs5.access(todosDir).then(() => true).catch(() => false)
|
|
537
548
|
),
|
|
538
549
|
Effect.tryPromise(
|
|
539
|
-
() =>
|
|
550
|
+
() => fs5.access(sessionsDir).then(() => true).catch(() => false)
|
|
540
551
|
)
|
|
541
552
|
]);
|
|
542
553
|
if (!todosExists || !sessionsExists) return [];
|
|
543
|
-
const todoFiles = yield* Effect.tryPromise(() =>
|
|
554
|
+
const todoFiles = yield* Effect.tryPromise(() => fs5.readdir(todosDir));
|
|
544
555
|
const jsonFiles = todoFiles.filter((f) => f.endsWith(".json"));
|
|
545
556
|
const validSessionIds = /* @__PURE__ */ new Set();
|
|
546
557
|
const projectEntries = yield* Effect.tryPromise(
|
|
547
|
-
() =>
|
|
558
|
+
() => fs5.readdir(sessionsDir, { withFileTypes: true })
|
|
548
559
|
);
|
|
549
560
|
for (const entry of projectEntries) {
|
|
550
561
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
551
|
-
const projectPath =
|
|
552
|
-
const files = yield* Effect.tryPromise(() =>
|
|
562
|
+
const projectPath = path5.join(sessionsDir, entry.name);
|
|
563
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
553
564
|
for (const f of files) {
|
|
554
565
|
if (f.endsWith(".jsonl") && !f.startsWith("agent-")) {
|
|
555
566
|
validSessionIds.add(f.replace(".jsonl", ""));
|
|
@@ -572,13 +583,13 @@ var deleteOrphanTodos = () => Effect.gen(function* () {
|
|
|
572
583
|
const todosDir = getTodosDir();
|
|
573
584
|
const orphans = yield* findOrphanTodos();
|
|
574
585
|
if (orphans.length === 0) return { success: true, deletedCount: 0 };
|
|
575
|
-
const backupDir =
|
|
576
|
-
yield* Effect.tryPromise(() =>
|
|
586
|
+
const backupDir = path5.join(todosDir, ".bak");
|
|
587
|
+
yield* Effect.tryPromise(() => fs5.mkdir(backupDir, { recursive: true }));
|
|
577
588
|
let deletedCount = 0;
|
|
578
589
|
for (const orphan of orphans) {
|
|
579
|
-
const filePath =
|
|
580
|
-
const backupPath =
|
|
581
|
-
yield* Effect.tryPromise(() =>
|
|
590
|
+
const filePath = path5.join(todosDir, orphan);
|
|
591
|
+
const backupPath = path5.join(backupDir, orphan);
|
|
592
|
+
yield* Effect.tryPromise(() => fs5.rename(filePath, backupPath));
|
|
582
593
|
deletedCount++;
|
|
583
594
|
}
|
|
584
595
|
return { success: true, deletedCount };
|
|
@@ -586,17 +597,17 @@ var deleteOrphanTodos = () => Effect.gen(function* () {
|
|
|
586
597
|
var listProjects = Effect.gen(function* () {
|
|
587
598
|
const sessionsDir = getSessionsDir();
|
|
588
599
|
const exists = yield* Effect.tryPromise(
|
|
589
|
-
() =>
|
|
600
|
+
() => fs5.access(sessionsDir).then(() => true).catch(() => false)
|
|
590
601
|
);
|
|
591
602
|
if (!exists) {
|
|
592
603
|
return [];
|
|
593
604
|
}
|
|
594
|
-
const entries = yield* Effect.tryPromise(() =>
|
|
605
|
+
const entries = yield* Effect.tryPromise(() => fs5.readdir(sessionsDir, { withFileTypes: true }));
|
|
595
606
|
const projects = yield* Effect.all(
|
|
596
607
|
entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map(
|
|
597
608
|
(entry) => Effect.gen(function* () {
|
|
598
|
-
const projectPath =
|
|
599
|
-
const files = yield* Effect.tryPromise(() =>
|
|
609
|
+
const projectPath = path5.join(sessionsDir, entry.name);
|
|
610
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
600
611
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
601
612
|
return {
|
|
602
613
|
name: entry.name,
|
|
@@ -610,15 +621,82 @@ var listProjects = Effect.gen(function* () {
|
|
|
610
621
|
);
|
|
611
622
|
return projects;
|
|
612
623
|
});
|
|
624
|
+
function deleteMessageWithChainRepair(messages, targetId, targetType) {
|
|
625
|
+
let targetIndex = -1;
|
|
626
|
+
{
|
|
627
|
+
targetIndex = messages.findIndex((m) => m.uuid === targetId);
|
|
628
|
+
if (targetIndex === -1) {
|
|
629
|
+
targetIndex = messages.findIndex(
|
|
630
|
+
(m) => m.leafUuid === targetId
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
if (targetIndex === -1) {
|
|
634
|
+
targetIndex = messages.findIndex(
|
|
635
|
+
(m) => m.type === "file-history-snapshot" && m.messageId === targetId
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (targetIndex === -1) {
|
|
640
|
+
return { deleted: null, alsoDeleted: [] };
|
|
641
|
+
}
|
|
642
|
+
const deletedMsg = messages[targetIndex];
|
|
643
|
+
const toolUseIds = [];
|
|
644
|
+
if (deletedMsg.type === "assistant") {
|
|
645
|
+
const content = deletedMsg.message?.content;
|
|
646
|
+
if (Array.isArray(content)) {
|
|
647
|
+
for (const item of content) {
|
|
648
|
+
if (item.type === "tool_use" && item.id) {
|
|
649
|
+
toolUseIds.push(item.id);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const toolResultIndices = [];
|
|
655
|
+
if (toolUseIds.length > 0) {
|
|
656
|
+
for (let i = 0; i < messages.length; i++) {
|
|
657
|
+
const msg = messages[i];
|
|
658
|
+
if (msg.type === "user") {
|
|
659
|
+
const content = msg.message?.content;
|
|
660
|
+
if (Array.isArray(content)) {
|
|
661
|
+
for (const item of content) {
|
|
662
|
+
if (item.type === "tool_result" && item.tool_use_id && toolUseIds.includes(item.tool_use_id)) {
|
|
663
|
+
toolResultIndices.push(i);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const indicesToDelete = [targetIndex, ...toolResultIndices].sort((a, b) => b - a);
|
|
672
|
+
for (const idx of indicesToDelete) {
|
|
673
|
+
const msg = messages[idx];
|
|
674
|
+
const isInParentChain = msg.type !== "file-history-snapshot" && msg.uuid;
|
|
675
|
+
if (isInParentChain) {
|
|
676
|
+
const deletedUuid = msg.uuid;
|
|
677
|
+
const parentUuid = msg.parentUuid;
|
|
678
|
+
for (const m of messages) {
|
|
679
|
+
if (m.parentUuid === deletedUuid) {
|
|
680
|
+
m.parentUuid = parentUuid;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const alsoDeleted = toolResultIndices.map((i) => messages[i]);
|
|
686
|
+
for (const idx of indicesToDelete) {
|
|
687
|
+
messages.splice(idx, 1);
|
|
688
|
+
}
|
|
689
|
+
return { deleted: deletedMsg, alsoDeleted };
|
|
690
|
+
}
|
|
613
691
|
var listSessions = (projectName) => Effect.gen(function* () {
|
|
614
|
-
const projectPath =
|
|
615
|
-
const files = yield* Effect.tryPromise(() =>
|
|
692
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
693
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
616
694
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
617
695
|
const sessions = yield* Effect.all(
|
|
618
696
|
sessionFiles.map(
|
|
619
697
|
(file) => Effect.gen(function* () {
|
|
620
|
-
const filePath =
|
|
621
|
-
const content = yield* Effect.tryPromise(() =>
|
|
698
|
+
const filePath = path5.join(projectPath, file);
|
|
699
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
622
700
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
623
701
|
const messages = lines.map((line) => JSON.parse(line));
|
|
624
702
|
const sessionId = file.replace(".jsonl", "");
|
|
@@ -637,10 +715,25 @@ var listSessions = (projectName) => Effect.gen(function* () {
|
|
|
637
715
|
}),
|
|
638
716
|
Option.getOrElse(() => hasSummary ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`)
|
|
639
717
|
);
|
|
718
|
+
const currentSummary = pipe(
|
|
719
|
+
messages,
|
|
720
|
+
Array$1.findFirst((m) => m.type === "summary"),
|
|
721
|
+
Option.map((m) => m.summary),
|
|
722
|
+
Option.getOrUndefined
|
|
723
|
+
);
|
|
724
|
+
const customTitle = pipe(
|
|
725
|
+
messages,
|
|
726
|
+
Array$1.findFirst((m) => m.type === "custom-title"),
|
|
727
|
+
Option.map((m) => m.customTitle),
|
|
728
|
+
Option.flatMap(Option.fromNullable),
|
|
729
|
+
Option.getOrUndefined
|
|
730
|
+
);
|
|
640
731
|
return {
|
|
641
732
|
id: sessionId,
|
|
642
733
|
projectName,
|
|
643
734
|
title,
|
|
735
|
+
customTitle,
|
|
736
|
+
currentSummary,
|
|
644
737
|
// If session has summary but no user/assistant messages, count as 1
|
|
645
738
|
messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,
|
|
646
739
|
createdAt: firstMessage?.timestamp,
|
|
@@ -657,38 +750,27 @@ var listSessions = (projectName) => Effect.gen(function* () {
|
|
|
657
750
|
});
|
|
658
751
|
});
|
|
659
752
|
var readSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
660
|
-
const filePath =
|
|
661
|
-
const content = yield* Effect.tryPromise(() =>
|
|
753
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
754
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
662
755
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
663
756
|
return lines.map((line) => JSON.parse(line));
|
|
664
757
|
});
|
|
665
|
-
var deleteMessage = (projectName, sessionId, messageUuid) => Effect.gen(function* () {
|
|
666
|
-
const filePath =
|
|
667
|
-
const content = yield* Effect.tryPromise(() =>
|
|
758
|
+
var deleteMessage = (projectName, sessionId, messageUuid, targetType) => Effect.gen(function* () {
|
|
759
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
760
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
668
761
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
669
762
|
const messages = lines.map((line) => JSON.parse(line));
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
);
|
|
673
|
-
if (targetIndex === -1) {
|
|
763
|
+
const result = deleteMessageWithChainRepair(messages, messageUuid);
|
|
764
|
+
if (!result.deleted) {
|
|
674
765
|
return { success: false, error: "Message not found" };
|
|
675
766
|
}
|
|
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
767
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
686
|
-
yield* Effect.tryPromise(() =>
|
|
687
|
-
return { success: true, deletedMessage:
|
|
768
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, newContent, "utf-8"));
|
|
769
|
+
return { success: true, deletedMessage: result.deleted };
|
|
688
770
|
});
|
|
689
771
|
var restoreMessage = (projectName, sessionId, message, index) => Effect.gen(function* () {
|
|
690
|
-
const filePath =
|
|
691
|
-
const content = yield* Effect.tryPromise(() =>
|
|
772
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
773
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
692
774
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
693
775
|
const messages = lines.map((line) => JSON.parse(line));
|
|
694
776
|
const msgUuid = message.uuid ?? message.messageId;
|
|
@@ -705,41 +787,41 @@ var restoreMessage = (projectName, sessionId, message, index) => Effect.gen(func
|
|
|
705
787
|
const insertIndex = Math.min(index, messages.length);
|
|
706
788
|
messages.splice(insertIndex, 0, message);
|
|
707
789
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
708
|
-
yield* Effect.tryPromise(() =>
|
|
790
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, newContent, "utf-8"));
|
|
709
791
|
return { success: true };
|
|
710
792
|
});
|
|
711
793
|
var deleteSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
712
794
|
const sessionsDir = getSessionsDir();
|
|
713
|
-
const projectPath =
|
|
714
|
-
const filePath =
|
|
795
|
+
const projectPath = path5.join(sessionsDir, projectName);
|
|
796
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
715
797
|
const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
718
|
-
yield* Effect.tryPromise(() =>
|
|
719
|
-
const agentBackupDir2 =
|
|
720
|
-
yield* Effect.tryPromise(() =>
|
|
798
|
+
const stat4 = yield* Effect.tryPromise(() => fs5.stat(filePath));
|
|
799
|
+
if (stat4.size === 0) {
|
|
800
|
+
yield* Effect.tryPromise(() => fs5.unlink(filePath));
|
|
801
|
+
const agentBackupDir2 = path5.join(projectPath, ".bak");
|
|
802
|
+
yield* Effect.tryPromise(() => fs5.mkdir(agentBackupDir2, { recursive: true }));
|
|
721
803
|
for (const agentId of linkedAgents) {
|
|
722
|
-
const agentPath =
|
|
723
|
-
const agentBackupPath =
|
|
724
|
-
yield* Effect.tryPromise(() =>
|
|
804
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
805
|
+
const agentBackupPath = path5.join(agentBackupDir2, `${agentId}.jsonl`);
|
|
806
|
+
yield* Effect.tryPromise(() => fs5.rename(agentPath, agentBackupPath).catch(() => {
|
|
725
807
|
}));
|
|
726
808
|
}
|
|
727
809
|
yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
728
810
|
return { success: true, deletedAgents: linkedAgents.length };
|
|
729
811
|
}
|
|
730
|
-
const backupDir =
|
|
731
|
-
yield* Effect.tryPromise(() =>
|
|
732
|
-
const agentBackupDir =
|
|
733
|
-
yield* Effect.tryPromise(() =>
|
|
812
|
+
const backupDir = path5.join(sessionsDir, ".bak");
|
|
813
|
+
yield* Effect.tryPromise(() => fs5.mkdir(backupDir, { recursive: true }));
|
|
814
|
+
const agentBackupDir = path5.join(projectPath, ".bak");
|
|
815
|
+
yield* Effect.tryPromise(() => fs5.mkdir(agentBackupDir, { recursive: true }));
|
|
734
816
|
for (const agentId of linkedAgents) {
|
|
735
|
-
const agentPath =
|
|
736
|
-
const agentBackupPath =
|
|
737
|
-
yield* Effect.tryPromise(() =>
|
|
817
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
818
|
+
const agentBackupPath = path5.join(agentBackupDir, `${agentId}.jsonl`);
|
|
819
|
+
yield* Effect.tryPromise(() => fs5.rename(agentPath, agentBackupPath).catch(() => {
|
|
738
820
|
}));
|
|
739
821
|
}
|
|
740
822
|
const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
741
|
-
const backupPath =
|
|
742
|
-
yield* Effect.tryPromise(() =>
|
|
823
|
+
const backupPath = path5.join(backupDir, `${projectName}_${sessionId}.jsonl`);
|
|
824
|
+
yield* Effect.tryPromise(() => fs5.rename(filePath, backupPath));
|
|
743
825
|
return {
|
|
744
826
|
success: true,
|
|
745
827
|
backupPath,
|
|
@@ -748,9 +830,9 @@ var deleteSession = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
748
830
|
};
|
|
749
831
|
});
|
|
750
832
|
var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* () {
|
|
751
|
-
const projectPath =
|
|
752
|
-
const filePath =
|
|
753
|
-
const content = yield* Effect.tryPromise(() =>
|
|
833
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
834
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
835
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
754
836
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
755
837
|
if (lines.length === 0) {
|
|
756
838
|
return { success: false, error: "Empty session" };
|
|
@@ -774,14 +856,14 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
774
856
|
messages.unshift(customTitleRecord);
|
|
775
857
|
}
|
|
776
858
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
777
|
-
yield* Effect.tryPromise(() =>
|
|
778
|
-
const projectFiles = yield* Effect.tryPromise(() =>
|
|
859
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, newContent, "utf-8"));
|
|
860
|
+
const projectFiles = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
779
861
|
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
780
862
|
const summariesTargetingThis = [];
|
|
781
863
|
for (const file of allJsonlFiles) {
|
|
782
|
-
const otherFilePath =
|
|
864
|
+
const otherFilePath = path5.join(projectPath, file);
|
|
783
865
|
try {
|
|
784
|
-
const otherContent = yield* Effect.tryPromise(() =>
|
|
866
|
+
const otherContent = yield* Effect.tryPromise(() => fs5.readFile(otherFilePath, "utf-8"));
|
|
785
867
|
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
786
868
|
const otherMessages = otherLines.map((l) => JSON.parse(l));
|
|
787
869
|
for (let i = 0; i < otherMessages.length; i++) {
|
|
@@ -801,8 +883,8 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
801
883
|
if (summariesTargetingThis.length > 0) {
|
|
802
884
|
summariesTargetingThis.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
|
|
803
885
|
const firstSummary = summariesTargetingThis[0];
|
|
804
|
-
const summaryFilePath =
|
|
805
|
-
const summaryContent = yield* Effect.tryPromise(() =>
|
|
886
|
+
const summaryFilePath = path5.join(projectPath, firstSummary.file);
|
|
887
|
+
const summaryContent = yield* Effect.tryPromise(() => fs5.readFile(summaryFilePath, "utf-8"));
|
|
806
888
|
const summaryLines = summaryContent.trim().split("\n").filter(Boolean);
|
|
807
889
|
const summaryMessages = summaryLines.map((l) => JSON.parse(l));
|
|
808
890
|
summaryMessages[firstSummary.idx] = {
|
|
@@ -810,9 +892,9 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
810
892
|
summary: newTitle
|
|
811
893
|
};
|
|
812
894
|
const newSummaryContent = summaryMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
813
|
-
yield* Effect.tryPromise(() =>
|
|
895
|
+
yield* Effect.tryPromise(() => fs5.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
|
|
814
896
|
} else {
|
|
815
|
-
const currentContent = yield* Effect.tryPromise(() =>
|
|
897
|
+
const currentContent = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
816
898
|
const currentLines = currentContent.trim().split("\n").filter(Boolean);
|
|
817
899
|
const currentMessages = currentLines.map((l) => JSON.parse(l));
|
|
818
900
|
const firstUserIdx = currentMessages.findIndex((m) => m.type === "user");
|
|
@@ -831,102 +913,50 @@ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* (
|
|
|
831
913
|
|
|
832
914
|
${cleanedText}`;
|
|
833
915
|
const updatedContent = currentMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
834
|
-
yield* Effect.tryPromise(() =>
|
|
916
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, updatedContent, "utf-8"));
|
|
835
917
|
}
|
|
836
918
|
}
|
|
837
919
|
}
|
|
838
920
|
}
|
|
839
921
|
return { success: true };
|
|
840
922
|
});
|
|
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
923
|
var moveSession = (sourceProject, sessionId, targetProject) => Effect.gen(function* () {
|
|
894
924
|
const sessionsDir = getSessionsDir();
|
|
895
|
-
const sourcePath =
|
|
896
|
-
const targetPath =
|
|
897
|
-
const sourceFile =
|
|
898
|
-
const targetFile =
|
|
925
|
+
const sourcePath = path5.join(sessionsDir, sourceProject);
|
|
926
|
+
const targetPath = path5.join(sessionsDir, targetProject);
|
|
927
|
+
const sourceFile = path5.join(sourcePath, `${sessionId}.jsonl`);
|
|
928
|
+
const targetFile = path5.join(targetPath, `${sessionId}.jsonl`);
|
|
899
929
|
const sourceExists = yield* Effect.tryPromise(
|
|
900
|
-
() =>
|
|
930
|
+
() => fs5.access(sourceFile).then(() => true).catch(() => false)
|
|
901
931
|
);
|
|
902
932
|
if (!sourceExists) {
|
|
903
933
|
return { success: false, error: "Source session not found" };
|
|
904
934
|
}
|
|
905
935
|
const targetExists = yield* Effect.tryPromise(
|
|
906
|
-
() =>
|
|
936
|
+
() => fs5.access(targetFile).then(() => true).catch(() => false)
|
|
907
937
|
);
|
|
908
938
|
if (targetExists) {
|
|
909
939
|
return { success: false, error: "Session already exists in target project" };
|
|
910
940
|
}
|
|
911
|
-
yield* Effect.tryPromise(() =>
|
|
941
|
+
yield* Effect.tryPromise(() => fs5.mkdir(targetPath, { recursive: true }));
|
|
912
942
|
const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId);
|
|
913
|
-
yield* Effect.tryPromise(() =>
|
|
943
|
+
yield* Effect.tryPromise(() => fs5.rename(sourceFile, targetFile));
|
|
914
944
|
for (const agentId of linkedAgents) {
|
|
915
|
-
const sourceAgentFile =
|
|
916
|
-
const targetAgentFile =
|
|
945
|
+
const sourceAgentFile = path5.join(sourcePath, `${agentId}.jsonl`);
|
|
946
|
+
const targetAgentFile = path5.join(targetPath, `${agentId}.jsonl`);
|
|
917
947
|
const agentExists = yield* Effect.tryPromise(
|
|
918
|
-
() =>
|
|
948
|
+
() => fs5.access(sourceAgentFile).then(() => true).catch(() => false)
|
|
919
949
|
);
|
|
920
950
|
if (agentExists) {
|
|
921
|
-
yield* Effect.tryPromise(() =>
|
|
951
|
+
yield* Effect.tryPromise(() => fs5.rename(sourceAgentFile, targetAgentFile));
|
|
922
952
|
}
|
|
923
953
|
}
|
|
924
954
|
return { success: true };
|
|
925
955
|
});
|
|
926
956
|
var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(function* () {
|
|
927
|
-
const projectPath =
|
|
928
|
-
const filePath =
|
|
929
|
-
const content = yield* Effect.tryPromise(() =>
|
|
957
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
958
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
959
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
930
960
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
931
961
|
const allMessages = lines.map((line) => JSON.parse(line));
|
|
932
962
|
const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid);
|
|
@@ -974,15 +1004,15 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(fu
|
|
|
974
1004
|
updatedMovedMessages.unshift(clonedSummary);
|
|
975
1005
|
}
|
|
976
1006
|
const keptContent = keptMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
977
|
-
yield* Effect.tryPromise(() =>
|
|
978
|
-
const newFilePath =
|
|
1007
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, keptContent, "utf-8"));
|
|
1008
|
+
const newFilePath = path5.join(projectPath, `${newSessionId}.jsonl`);
|
|
979
1009
|
const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
980
|
-
yield* Effect.tryPromise(() =>
|
|
981
|
-
const agentFiles = yield* Effect.tryPromise(() =>
|
|
1010
|
+
yield* Effect.tryPromise(() => fs5.writeFile(newFilePath, newContent, "utf-8"));
|
|
1011
|
+
const agentFiles = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
982
1012
|
const agentJsonlFiles = agentFiles.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
983
1013
|
for (const agentFile of agentJsonlFiles) {
|
|
984
|
-
const agentPath =
|
|
985
|
-
const agentContent = yield* Effect.tryPromise(() =>
|
|
1014
|
+
const agentPath = path5.join(projectPath, agentFile);
|
|
1015
|
+
const agentContent = yield* Effect.tryPromise(() => fs5.readFile(agentPath, "utf-8"));
|
|
986
1016
|
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
987
1017
|
if (agentLines.length === 0) continue;
|
|
988
1018
|
const firstAgentMsg = JSON.parse(agentLines[0]);
|
|
@@ -997,7 +1027,7 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(fu
|
|
|
997
1027
|
return JSON.stringify({ ...msg, sessionId: newSessionId });
|
|
998
1028
|
});
|
|
999
1029
|
const updatedAgentContent = updatedAgentMessages.join("\n") + "\n";
|
|
1000
|
-
yield* Effect.tryPromise(() =>
|
|
1030
|
+
yield* Effect.tryPromise(() => fs5.writeFile(agentPath, updatedAgentContent, "utf-8"));
|
|
1001
1031
|
}
|
|
1002
1032
|
}
|
|
1003
1033
|
}
|
|
@@ -1009,9 +1039,277 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(fu
|
|
|
1009
1039
|
duplicatedSummary: shouldDuplicate
|
|
1010
1040
|
};
|
|
1011
1041
|
});
|
|
1042
|
+
var sortSessions = (sessions, sort) => {
|
|
1043
|
+
return sessions.sort((a, b) => {
|
|
1044
|
+
let comparison = 0;
|
|
1045
|
+
switch (sort.field) {
|
|
1046
|
+
case "summary": {
|
|
1047
|
+
comparison = a.sortTimestamp - b.sortTimestamp;
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
case "modified": {
|
|
1051
|
+
comparison = (a.fileMtime ?? 0) - (b.fileMtime ?? 0);
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
1054
|
+
case "created": {
|
|
1055
|
+
const createdA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
1056
|
+
const createdB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
1057
|
+
comparison = createdA - createdB;
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
case "updated": {
|
|
1061
|
+
const updatedA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
1062
|
+
const updatedB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
1063
|
+
comparison = updatedA - updatedB;
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
case "messageCount": {
|
|
1067
|
+
comparison = a.messageCount - b.messageCount;
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
case "title": {
|
|
1071
|
+
const titleA = a.customTitle ?? a.currentSummary ?? a.title;
|
|
1072
|
+
const titleB = b.customTitle ?? b.currentSummary ?? b.title;
|
|
1073
|
+
comparison = titleA.localeCompare(titleB);
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return sort.order === "desc" ? -comparison : comparison;
|
|
1078
|
+
});
|
|
1079
|
+
};
|
|
1080
|
+
var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSession, fileMtime) => Effect.gen(function* () {
|
|
1081
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1082
|
+
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
1083
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
1084
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1085
|
+
const messages = lines.map((line) => JSON.parse(line));
|
|
1086
|
+
let summaries;
|
|
1087
|
+
if (summariesByTargetSession) {
|
|
1088
|
+
summaries = [...summariesByTargetSession.get(sessionId) ?? []].sort((a, b) => {
|
|
1089
|
+
const timestampCmp = (a.timestamp ?? "").localeCompare(b.timestamp ?? "");
|
|
1090
|
+
if (timestampCmp !== 0) return timestampCmp;
|
|
1091
|
+
return (b.sourceFile ?? "").localeCompare(a.sourceFile ?? "");
|
|
1092
|
+
});
|
|
1093
|
+
} else {
|
|
1094
|
+
summaries = [];
|
|
1095
|
+
const sessionUuids = /* @__PURE__ */ new Set();
|
|
1096
|
+
for (const msg of messages) {
|
|
1097
|
+
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1098
|
+
sessionUuids.add(msg.uuid);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
const projectFiles = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
1102
|
+
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1103
|
+
for (const file of allJsonlFiles) {
|
|
1104
|
+
try {
|
|
1105
|
+
const otherFilePath = path5.join(projectPath, file);
|
|
1106
|
+
const otherContent = yield* Effect.tryPromise(() => fs5.readFile(otherFilePath, "utf-8"));
|
|
1107
|
+
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
1108
|
+
for (const line of otherLines) {
|
|
1109
|
+
try {
|
|
1110
|
+
const msg = JSON.parse(line);
|
|
1111
|
+
if (msg.type === "summary" && typeof msg.summary === "string" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
1112
|
+
const targetMsg = messages.find((m) => m.uuid === msg.leafUuid);
|
|
1113
|
+
summaries.push({
|
|
1114
|
+
summary: msg.summary,
|
|
1115
|
+
leafUuid: msg.leafUuid,
|
|
1116
|
+
timestamp: targetMsg?.timestamp ?? msg.timestamp,
|
|
1117
|
+
sourceFile: file
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
} catch {
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
summaries.sort((a, b) => {
|
|
1128
|
+
const timestampCmp = (a.timestamp ?? "").localeCompare(b.timestamp ?? "");
|
|
1129
|
+
if (timestampCmp !== 0) return timestampCmp;
|
|
1130
|
+
return (b.sourceFile ?? "").localeCompare(a.sourceFile ?? "");
|
|
1131
|
+
});
|
|
1132
|
+
let lastCompactBoundaryUuid;
|
|
1133
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1134
|
+
const msg = messages[i];
|
|
1135
|
+
if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
1136
|
+
lastCompactBoundaryUuid = msg.uuid;
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
const firstUserMsg = messages.find((m) => m.type === "user");
|
|
1141
|
+
const customTitleMsg = messages.find((m) => m.type === "custom-title");
|
|
1142
|
+
const customTitle = customTitleMsg?.customTitle;
|
|
1143
|
+
const title = firstUserMsg ? extractTitle(extractTextContent(firstUserMsg.message)) : summaries.length > 0 ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`;
|
|
1144
|
+
const userAssistantMessages = messages.filter(
|
|
1145
|
+
(m) => m.type === "user" || m.type === "assistant"
|
|
1146
|
+
);
|
|
1147
|
+
const firstMessage = userAssistantMessages[0];
|
|
1148
|
+
const lastMessage = userAssistantMessages[userAssistantMessages.length - 1];
|
|
1149
|
+
const linkedAgentIds = yield* findLinkedAgents(projectName, sessionId);
|
|
1150
|
+
const agents = [];
|
|
1151
|
+
for (const agentId of linkedAgentIds) {
|
|
1152
|
+
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
1153
|
+
try {
|
|
1154
|
+
const agentContent = yield* Effect.tryPromise(() => fs5.readFile(agentPath, "utf-8"));
|
|
1155
|
+
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
1156
|
+
const agentMsgs = agentLines.map((l) => JSON.parse(l));
|
|
1157
|
+
const agentUserAssistant = agentMsgs.filter(
|
|
1158
|
+
(m) => m.type === "user" || m.type === "assistant"
|
|
1159
|
+
);
|
|
1160
|
+
let agentName;
|
|
1161
|
+
const firstAgentMsg = agentMsgs.find((m) => m.type === "user");
|
|
1162
|
+
if (firstAgentMsg) {
|
|
1163
|
+
const text = extractTextContent(firstAgentMsg.message);
|
|
1164
|
+
if (text) {
|
|
1165
|
+
agentName = extractTitle(text);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
agents.push({
|
|
1169
|
+
id: agentId,
|
|
1170
|
+
name: agentName,
|
|
1171
|
+
messageCount: agentUserAssistant.length
|
|
1172
|
+
});
|
|
1173
|
+
} catch {
|
|
1174
|
+
agents.push({
|
|
1175
|
+
id: agentId,
|
|
1176
|
+
messageCount: 0
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const todos = yield* findLinkedTodos(sessionId, linkedAgentIds);
|
|
1181
|
+
const createdAt = firstMessage?.timestamp ?? void 0;
|
|
1182
|
+
const sortTimestamp = getSessionSortTimestamp({ summaries, createdAt });
|
|
1183
|
+
return {
|
|
1184
|
+
id: sessionId,
|
|
1185
|
+
projectName,
|
|
1186
|
+
title,
|
|
1187
|
+
customTitle,
|
|
1188
|
+
currentSummary: summaries[0]?.summary,
|
|
1189
|
+
messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : summaries.length > 0 ? 1 : 0,
|
|
1190
|
+
createdAt,
|
|
1191
|
+
updatedAt: lastMessage?.timestamp ?? void 0,
|
|
1192
|
+
fileMtime,
|
|
1193
|
+
sortTimestamp,
|
|
1194
|
+
summaries,
|
|
1195
|
+
agents,
|
|
1196
|
+
todos,
|
|
1197
|
+
lastCompactBoundaryUuid
|
|
1198
|
+
};
|
|
1199
|
+
});
|
|
1200
|
+
var loadSessionTreeData = (projectName, sessionId) => loadSessionTreeDataInternal(projectName, sessionId, void 0);
|
|
1201
|
+
var DEFAULT_SORT = { field: "summary", order: "desc" };
|
|
1202
|
+
var loadProjectTreeData = (projectName, sortOptions) => Effect.gen(function* () {
|
|
1203
|
+
const project = (yield* listProjects).find((p) => p.name === projectName);
|
|
1204
|
+
if (!project) {
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
const sort = sortOptions ?? DEFAULT_SORT;
|
|
1208
|
+
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1209
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
1210
|
+
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1211
|
+
const fileMtimes = /* @__PURE__ */ new Map();
|
|
1212
|
+
yield* Effect.all(
|
|
1213
|
+
sessionFiles.map(
|
|
1214
|
+
(file) => Effect.gen(function* () {
|
|
1215
|
+
const filePath = path5.join(projectPath, file);
|
|
1216
|
+
try {
|
|
1217
|
+
const stat4 = yield* Effect.tryPromise(() => fs5.stat(filePath));
|
|
1218
|
+
fileMtimes.set(file.replace(".jsonl", ""), stat4.mtimeMs);
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
1221
|
+
})
|
|
1222
|
+
),
|
|
1223
|
+
{ concurrency: 20 }
|
|
1224
|
+
);
|
|
1225
|
+
const globalUuidMap = /* @__PURE__ */ new Map();
|
|
1226
|
+
const allSummaries = [];
|
|
1227
|
+
const allJsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
1228
|
+
yield* Effect.all(
|
|
1229
|
+
allJsonlFiles.map(
|
|
1230
|
+
(file) => Effect.gen(function* () {
|
|
1231
|
+
const filePath = path5.join(projectPath, file);
|
|
1232
|
+
const fileSessionId = file.replace(".jsonl", "");
|
|
1233
|
+
try {
|
|
1234
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
1235
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1236
|
+
for (const line of lines) {
|
|
1237
|
+
try {
|
|
1238
|
+
const msg = JSON.parse(line);
|
|
1239
|
+
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1240
|
+
globalUuidMap.set(msg.uuid, {
|
|
1241
|
+
sessionId: fileSessionId,
|
|
1242
|
+
timestamp: msg.timestamp
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
if (msg.messageId && typeof msg.messageId === "string") {
|
|
1246
|
+
globalUuidMap.set(msg.messageId, {
|
|
1247
|
+
sessionId: fileSessionId,
|
|
1248
|
+
timestamp: msg.snapshot?.timestamp
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
if (msg.type === "summary" && typeof msg.summary === "string") {
|
|
1252
|
+
allSummaries.push({
|
|
1253
|
+
summary: msg.summary,
|
|
1254
|
+
leafUuid: msg.leafUuid,
|
|
1255
|
+
timestamp: msg.timestamp,
|
|
1256
|
+
sourceFile: file
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
} catch {
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
} catch {
|
|
1263
|
+
}
|
|
1264
|
+
})
|
|
1265
|
+
),
|
|
1266
|
+
{ concurrency: 20 }
|
|
1267
|
+
);
|
|
1268
|
+
const summariesByTargetSession = /* @__PURE__ */ new Map();
|
|
1269
|
+
for (const summaryData of allSummaries) {
|
|
1270
|
+
if (summaryData.leafUuid) {
|
|
1271
|
+
const targetInfo = globalUuidMap.get(summaryData.leafUuid);
|
|
1272
|
+
if (targetInfo) {
|
|
1273
|
+
const targetSessionId = targetInfo.sessionId;
|
|
1274
|
+
if (!summariesByTargetSession.has(targetSessionId)) {
|
|
1275
|
+
summariesByTargetSession.set(targetSessionId, []);
|
|
1276
|
+
}
|
|
1277
|
+
summariesByTargetSession.get(targetSessionId).push({
|
|
1278
|
+
summary: summaryData.summary,
|
|
1279
|
+
leafUuid: summaryData.leafUuid,
|
|
1280
|
+
// Use summary's own timestamp for sorting, not the target message's timestamp
|
|
1281
|
+
timestamp: summaryData.timestamp ?? targetInfo.timestamp,
|
|
1282
|
+
sourceFile: summaryData.sourceFile
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
const sessions = yield* Effect.all(
|
|
1288
|
+
sessionFiles.map((file) => {
|
|
1289
|
+
const sessionId = file.replace(".jsonl", "");
|
|
1290
|
+
const mtime = fileMtimes.get(sessionId);
|
|
1291
|
+
return loadSessionTreeDataInternal(projectName, sessionId, summariesByTargetSession, mtime);
|
|
1292
|
+
}),
|
|
1293
|
+
{ concurrency: 10 }
|
|
1294
|
+
);
|
|
1295
|
+
const sortedSessions = sortSessions(sessions, sort);
|
|
1296
|
+
const filteredSessions = sortedSessions.filter((s) => {
|
|
1297
|
+
if (isErrorSessionTitle(s.title)) return false;
|
|
1298
|
+
if (isErrorSessionTitle(s.customTitle)) return false;
|
|
1299
|
+
if (isErrorSessionTitle(s.currentSummary)) return false;
|
|
1300
|
+
return true;
|
|
1301
|
+
});
|
|
1302
|
+
return {
|
|
1303
|
+
name: project.name,
|
|
1304
|
+
displayName: project.displayName,
|
|
1305
|
+
path: project.path,
|
|
1306
|
+
sessionCount: filteredSessions.length,
|
|
1307
|
+
sessions: filteredSessions
|
|
1308
|
+
};
|
|
1309
|
+
});
|
|
1012
1310
|
var cleanInvalidMessages = (projectName, sessionId) => Effect.gen(function* () {
|
|
1013
|
-
const filePath =
|
|
1014
|
-
const content = yield* Effect.tryPromise(() =>
|
|
1311
|
+
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
1312
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
1015
1313
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1016
1314
|
if (lines.length === 0) return { removedCount: 0, remainingCount: 0 };
|
|
1017
1315
|
const messages = lines.map((line) => JSON.parse(line));
|
|
@@ -1043,7 +1341,7 @@ var cleanInvalidMessages = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
1043
1341
|
lastValidUuid = msg.uuid;
|
|
1044
1342
|
}
|
|
1045
1343
|
const newContent = filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join("\n") + "\n" : "";
|
|
1046
|
-
yield* Effect.tryPromise(() =>
|
|
1344
|
+
yield* Effect.tryPromise(() => fs5.writeFile(filePath, newContent, "utf-8"));
|
|
1047
1345
|
const remainingUserAssistant = filtered.filter(
|
|
1048
1346
|
(m) => m.type === "user" || m.type === "assistant"
|
|
1049
1347
|
).length;
|
|
@@ -1109,8 +1407,8 @@ var clearSessions = (options) => Effect.gen(function* () {
|
|
|
1109
1407
|
const sessionsToDelete = [];
|
|
1110
1408
|
if (clearInvalid) {
|
|
1111
1409
|
for (const project of targetProjects) {
|
|
1112
|
-
const projectPath =
|
|
1113
|
-
const files = yield* Effect.tryPromise(() =>
|
|
1410
|
+
const projectPath = path5.join(getSessionsDir(), project.name);
|
|
1411
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
1114
1412
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1115
1413
|
for (const file of sessionFiles) {
|
|
1116
1414
|
const sessionId = file.replace(".jsonl", "");
|
|
@@ -1187,16 +1485,16 @@ var searchSessions = (query, options = {}) => Effect.gen(function* () {
|
|
|
1187
1485
|
}
|
|
1188
1486
|
if (searchContent) {
|
|
1189
1487
|
for (const project of targetProjects) {
|
|
1190
|
-
const projectPath =
|
|
1191
|
-
const files = yield* Effect.tryPromise(() =>
|
|
1488
|
+
const projectPath = path5.join(getSessionsDir(), project.name);
|
|
1489
|
+
const files = yield* Effect.tryPromise(() => fs5.readdir(projectPath));
|
|
1192
1490
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1193
1491
|
for (const file of sessionFiles) {
|
|
1194
1492
|
const sessionId = file.replace(".jsonl", "");
|
|
1195
1493
|
if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {
|
|
1196
1494
|
continue;
|
|
1197
1495
|
}
|
|
1198
|
-
const filePath =
|
|
1199
|
-
const content = yield* Effect.tryPromise(() =>
|
|
1496
|
+
const filePath = path5.join(projectPath, file);
|
|
1497
|
+
const content = yield* Effect.tryPromise(() => fs5.readFile(filePath, "utf-8"));
|
|
1200
1498
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1201
1499
|
for (const line of lines) {
|
|
1202
1500
|
try {
|
|
@@ -1232,209 +1530,58 @@ var searchSessions = (query, options = {}) => Effect.gen(function* () {
|
|
|
1232
1530
|
return dateB - dateA;
|
|
1233
1531
|
});
|
|
1234
1532
|
});
|
|
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 {
|
|
1533
|
+
var getSessionFiles = (projectName, sessionId) => Effect.gen(function* () {
|
|
1534
|
+
const messages = yield* readSession(projectName, sessionId);
|
|
1535
|
+
const fileChanges = [];
|
|
1536
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
1537
|
+
for (const msg of messages) {
|
|
1538
|
+
if (msg.type === "file-history-snapshot") {
|
|
1539
|
+
const snapshot = msg;
|
|
1540
|
+
const backups = snapshot.snapshot?.trackedFileBackups;
|
|
1541
|
+
if (backups && typeof backups === "object") {
|
|
1542
|
+
for (const filePath of Object.keys(backups)) {
|
|
1543
|
+
if (!seenFiles.has(filePath)) {
|
|
1544
|
+
seenFiles.add(filePath);
|
|
1545
|
+
fileChanges.push({
|
|
1546
|
+
path: filePath,
|
|
1547
|
+
action: "modified",
|
|
1548
|
+
timestamp: snapshot.snapshot?.timestamp,
|
|
1549
|
+
messageUuid: snapshot.messageId ?? msg.uuid
|
|
1550
|
+
});
|
|
1273
1551
|
}
|
|
1274
1552
|
}
|
|
1275
|
-
} catch {
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
summaries.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
|
|
1280
|
-
let lastCompactBoundaryUuid;
|
|
1281
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1282
|
-
const msg = messages[i];
|
|
1283
|
-
if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
1284
|
-
lastCompactBoundaryUuid = msg.uuid;
|
|
1285
|
-
break;
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
const firstUserMsg = messages.find((m) => m.type === "user");
|
|
1289
|
-
const customTitleMsg = messages.find((m) => m.type === "custom-title");
|
|
1290
|
-
const customTitle = customTitleMsg?.customTitle;
|
|
1291
|
-
const title = firstUserMsg ? extractTitle(extractTextContent(firstUserMsg.message)) : summaries.length > 0 ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`;
|
|
1292
|
-
const userAssistantMessages = messages.filter(
|
|
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
1553
|
}
|
|
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
1554
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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
|
|
1555
|
+
if (msg.type === "assistant" && msg.message?.content) {
|
|
1556
|
+
const content = msg.message.content;
|
|
1557
|
+
if (Array.isArray(content)) {
|
|
1558
|
+
for (const item of content) {
|
|
1559
|
+
if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
|
|
1560
|
+
const toolUse = item;
|
|
1561
|
+
if ((toolUse.name === "Write" || toolUse.name === "Edit") && toolUse.input?.file_path) {
|
|
1562
|
+
const filePath = toolUse.input.file_path;
|
|
1563
|
+
if (!seenFiles.has(filePath)) {
|
|
1564
|
+
seenFiles.add(filePath);
|
|
1565
|
+
fileChanges.push({
|
|
1566
|
+
path: filePath,
|
|
1567
|
+
action: toolUse.name === "Write" ? "created" : "modified",
|
|
1568
|
+
timestamp: msg.timestamp,
|
|
1569
|
+
messageUuid: msg.uuid
|
|
1384
1570
|
});
|
|
1385
1571
|
}
|
|
1386
|
-
} catch {
|
|
1387
1572
|
}
|
|
1388
1573
|
}
|
|
1389
|
-
} catch {
|
|
1390
1574
|
}
|
|
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
|
-
}
|
|
1404
|
-
summariesByTargetSession.get(targetSessionId).push({
|
|
1405
|
-
summary: summaryData.summary,
|
|
1406
|
-
leafUuid: summaryData.leafUuid,
|
|
1407
|
-
timestamp: targetInfo.timestamp ?? summaryData.timestamp
|
|
1408
|
-
});
|
|
1409
1575
|
}
|
|
1410
1576
|
}
|
|
1411
1577
|
}
|
|
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
1578
|
return {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
sessions: filteredSessions
|
|
1579
|
+
sessionId,
|
|
1580
|
+
projectName,
|
|
1581
|
+
files: fileChanges,
|
|
1582
|
+
totalChanges: fileChanges.length
|
|
1436
1583
|
};
|
|
1437
1584
|
});
|
|
1438
1585
|
|
|
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-
|
|
1586
|
+
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, maskHomePath as w };
|
|
1587
|
+
//# sourceMappingURL=index3-CcQtlmu9.js.map
|