@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.
Files changed (177) hide show
  1. package/build/client/_app/immutable/assets/0.D2FnNtrO.css +1 -0
  2. package/build/client/_app/immutable/assets/0.D2FnNtrO.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.D2FnNtrO.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/Toast.CQSY_S6M.css.gz +0 -0
  5. package/build/client/_app/immutable/chunks/{pOdkXMWy.js → B-Z9hXPk.js} +1 -1
  6. package/build/client/_app/immutable/chunks/B-Z9hXPk.js.br +0 -0
  7. package/build/client/_app/immutable/chunks/B-Z9hXPk.js.gz +0 -0
  8. package/build/client/_app/immutable/chunks/{Ck-YPZB6.js → BAHJNsIv.js} +1 -1
  9. package/build/client/_app/immutable/chunks/BAHJNsIv.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/BAHJNsIv.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/BUqdiQ8V.js +90 -0
  12. package/build/client/_app/immutable/chunks/BUqdiQ8V.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/BUqdiQ8V.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/{Dbjp-HdT.js → Bt_HBaBk.js} +1 -1
  15. package/build/client/_app/immutable/chunks/Bt_HBaBk.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/Bt_HBaBk.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/Cr0eW1j3.js +1 -0
  18. package/build/client/_app/immutable/chunks/Cr0eW1j3.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/Cr0eW1j3.js.gz +0 -0
  20. package/build/client/_app/immutable/chunks/DOXVKi87.js +2 -0
  21. package/build/client/_app/immutable/chunks/DOXVKi87.js.br +0 -0
  22. package/build/client/_app/immutable/chunks/DOXVKi87.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/YXuXYbOb.js +1 -0
  24. package/build/client/_app/immutable/chunks/YXuXYbOb.js.br +0 -0
  25. package/build/client/_app/immutable/chunks/YXuXYbOb.js.gz +0 -0
  26. package/build/client/_app/immutable/entry/app.B37sNVm4.js +2 -0
  27. package/build/client/_app/immutable/entry/app.B37sNVm4.js.br +0 -0
  28. package/build/client/_app/immutable/entry/app.B37sNVm4.js.gz +0 -0
  29. package/build/client/_app/immutable/entry/start.CUKKnspj.js +1 -0
  30. package/build/client/_app/immutable/entry/start.CUKKnspj.js.br +2 -0
  31. package/build/client/_app/immutable/entry/start.CUKKnspj.js.gz +0 -0
  32. package/build/client/_app/immutable/nodes/{0.D4LDvqQb.js → 0.DD0gH91e.js} +1 -1
  33. package/build/client/_app/immutable/nodes/0.DD0gH91e.js.br +0 -0
  34. package/build/client/_app/immutable/nodes/0.DD0gH91e.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/1.j1rqa9HA.js +1 -0
  36. package/build/client/_app/immutable/nodes/1.j1rqa9HA.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/1.j1rqa9HA.js.gz +0 -0
  38. package/build/client/_app/immutable/nodes/2.5x79czX-.js +4 -0
  39. package/build/client/_app/immutable/nodes/2.5x79czX-.js.br +0 -0
  40. package/build/client/_app/immutable/nodes/2.5x79czX-.js.gz +0 -0
  41. package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js +4 -0
  42. package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js.br +0 -0
  43. package/build/client/_app/immutable/nodes/3.CkQ0QnVZ.js.gz +0 -0
  44. package/build/client/_app/version.json +1 -1
  45. package/build/client/_app/version.json.br +0 -0
  46. package/build/client/_app/version.json.gz +0 -0
  47. package/build/env.js +51 -2
  48. package/build/handler.js +46 -4
  49. package/build/index.js +25 -14
  50. package/build/server/chunks/0-BhPy9YZY.js +17 -0
  51. package/build/server/chunks/{0-DkD09WcK.js.map → 0-BhPy9YZY.js.map} +1 -1
  52. package/build/server/chunks/1-CTTD_wla.js +9 -0
  53. package/build/server/chunks/{1-CjtVgNYW.js.map → 1-CTTD_wla.js.map} +1 -1
  54. package/build/server/chunks/2-Bvyspg6i.js +9 -0
  55. package/build/server/chunks/{2-wstlfOP5.js.map → 2-Bvyspg6i.js.map} +1 -1
  56. package/build/server/chunks/3-BHD2c0Ph.js +9 -0
  57. package/build/server/chunks/{3-Cdn9_0OG.js.map → 3-BHD2c0Ph.js.map} +1 -1
  58. package/build/server/chunks/{InputModal-XaplAR5y.js → InputModal-CbpiMXy3.js} +3 -3
  59. package/build/server/chunks/InputModal-CbpiMXy3.js.map +1 -0
  60. package/build/server/chunks/{Toast-ihrVamsT.js → Toast-Dr8M-5Wk.js} +4 -4
  61. package/build/server/chunks/Toast-Dr8M-5Wk.js.map +1 -0
  62. package/build/server/chunks/{_layout.svelte-COfbk473.js → _layout.svelte-CkIsNl4s.js} +7 -6
  63. package/build/server/chunks/_layout.svelte-CkIsNl4s.js.map +1 -0
  64. package/build/server/chunks/{_page.svelte-CKSi7jlX.js → _page.svelte-BdKbLK1W.js} +8 -7
  65. package/build/server/chunks/_page.svelte-BdKbLK1W.js.map +1 -0
  66. package/build/server/chunks/{_page.svelte-C5Xngf9U.js → _page.svelte-DWKQDFXZ.js} +452 -240
  67. package/build/server/chunks/_page.svelte-DWKQDFXZ.js.map +1 -0
  68. package/build/server/chunks/{_server.ts-CFQ3_7g1.js → _server.ts-8OBR5eCA.js} +3 -2
  69. package/build/server/chunks/{_server.ts-CFQ3_7g1.js.map → _server.ts-8OBR5eCA.js.map} +1 -1
  70. package/build/server/chunks/_server.ts-B2LLvx6I.js +41 -0
  71. package/build/server/chunks/_server.ts-B2LLvx6I.js.map +1 -0
  72. package/build/server/chunks/{_server.ts-Bzg6xSYv.js → _server.ts-B4SnxrOX.js} +3 -2
  73. package/build/server/chunks/{_server.ts-Bzg6xSYv.js.map → _server.ts-B4SnxrOX.js.map} +1 -1
  74. package/build/server/chunks/{_server.ts-a85289si.js → _server.ts-B9gP-Tri.js} +7 -3
  75. package/build/server/chunks/_server.ts-B9gP-Tri.js.map +1 -0
  76. package/build/server/chunks/{_server.ts-IjEZJmtD.js → _server.ts-BQxGBeCg.js} +3 -2
  77. package/build/server/chunks/{_server.ts-IjEZJmtD.js.map → _server.ts-BQxGBeCg.js.map} +1 -1
  78. package/build/server/chunks/{_server.ts-CF7wNACi.js → _server.ts-Bcdjougg.js} +3 -2
  79. package/build/server/chunks/{_server.ts-CF7wNACi.js.map → _server.ts-Bcdjougg.js.map} +1 -1
  80. package/build/server/chunks/{_server.ts-CBabn3Le.js → _server.ts-BeWDhgJ_.js} +3 -2
  81. package/build/server/chunks/{_server.ts-CBabn3Le.js.map → _server.ts-BeWDhgJ_.js.map} +1 -1
  82. package/build/server/chunks/{_server.ts-bj28Awpj.js → _server.ts-CEGd9zNn.js} +3 -2
  83. package/build/server/chunks/_server.ts-CEGd9zNn.js.map +1 -0
  84. package/build/server/chunks/{_server.ts-QYg-pRKw.js → _server.ts-CMsFINkl.js} +3 -2
  85. package/build/server/chunks/{_server.ts-QYg-pRKw.js.map → _server.ts-CMsFINkl.js.map} +1 -1
  86. package/build/server/chunks/{_server.ts-BNGntgHD.js → _server.ts-CUginV8c.js} +7 -3
  87. package/build/server/chunks/_server.ts-CUginV8c.js.map +1 -0
  88. package/build/server/chunks/{_server.ts-DRy1BtKz.js → _server.ts-Crip_Csr.js} +3 -2
  89. package/build/server/chunks/{_server.ts-DRy1BtKz.js.map → _server.ts-Crip_Csr.js.map} +1 -1
  90. package/build/server/chunks/{_server.ts-C46vcb_V.js → _server.ts-CuAlKvWH.js} +3 -2
  91. package/build/server/chunks/{_server.ts-C46vcb_V.js.map → _server.ts-CuAlKvWH.js.map} +1 -1
  92. package/build/server/chunks/{_server.ts-msjVztXK.js → _server.ts-DfNIRtb5.js} +3 -2
  93. package/build/server/chunks/{_server.ts-msjVztXK.js.map → _server.ts-DfNIRtb5.js.map} +1 -1
  94. package/build/server/chunks/{_server.ts-CBCUnjeX.js → _server.ts-DnfpkUoI.js} +3 -2
  95. package/build/server/chunks/{_server.ts-CBCUnjeX.js.map → _server.ts-DnfpkUoI.js.map} +1 -1
  96. package/build/server/chunks/_server.ts-cVOhu8ub.js +26 -0
  97. package/build/server/chunks/_server.ts-cVOhu8ub.js.map +1 -0
  98. package/build/server/chunks/{_server.ts-Dtvv8PMc.js → _server.ts-dXxEqPlK.js} +3 -2
  99. package/build/server/chunks/{_server.ts-Dtvv8PMc.js.map → _server.ts-dXxEqPlK.js.map} +1 -1
  100. package/build/server/chunks/{error.svelte-C43AeaaJ.js → error.svelte-BNCG_dZH.js} +3 -3
  101. package/build/server/chunks/{error.svelte-C43AeaaJ.js.map → error.svelte-BNCG_dZH.js.map} +1 -1
  102. package/build/server/chunks/exports-BXvEiaiv.js.map +1 -1
  103. package/build/server/chunks/{index2-Da0doXJY.js → index-BEaiIYry.js} +388 -4
  104. package/build/server/chunks/index-BEaiIYry.js.map +1 -0
  105. package/build/server/chunks/index-CoD1IJuy.js.map +1 -1
  106. package/build/server/chunks/{index-DzYX9r1_.js → index2-ybZwlzIk.js} +110 -116
  107. package/build/server/chunks/index2-ybZwlzIk.js.map +1 -0
  108. package/build/server/chunks/{index3-DkgTDgY2.js → index3-D8wHvUHX.js} +781 -597
  109. package/build/server/chunks/index3-D8wHvUHX.js.map +1 -0
  110. package/build/server/chunks/{index4-BUZEWk-I.js → index4-B6vRxXfo.js} +2 -2
  111. package/build/server/chunks/index4-B6vRxXfo.js.map +1 -0
  112. package/build/server/chunks/shared-server-DaWdgxVh.js +11 -0
  113. package/build/server/chunks/shared-server-DaWdgxVh.js.map +1 -0
  114. package/build/server/index.js +115 -439
  115. package/build/server/index.js.map +1 -1
  116. package/build/server/manifest.js +21 -21
  117. package/build/server/manifest.js.map +1 -1
  118. package/dist/cli.js +26 -1
  119. package/package.json +19 -18
  120. package/build/client/_app/immutable/assets/0.DYXbBCWs.css +0 -1
  121. package/build/client/_app/immutable/assets/0.DYXbBCWs.css.br +0 -0
  122. package/build/client/_app/immutable/assets/0.DYXbBCWs.css.gz +0 -0
  123. package/build/client/_app/immutable/chunks/CT0aV1AN.js +0 -90
  124. package/build/client/_app/immutable/chunks/CT0aV1AN.js.br +0 -0
  125. package/build/client/_app/immutable/chunks/CT0aV1AN.js.gz +0 -0
  126. package/build/client/_app/immutable/chunks/Ck-YPZB6.js.br +0 -0
  127. package/build/client/_app/immutable/chunks/Ck-YPZB6.js.gz +0 -0
  128. package/build/client/_app/immutable/chunks/Dbjp-HdT.js.br +0 -0
  129. package/build/client/_app/immutable/chunks/Dbjp-HdT.js.gz +0 -0
  130. package/build/client/_app/immutable/chunks/HaThayHP.js +0 -1
  131. package/build/client/_app/immutable/chunks/HaThayHP.js.br +0 -0
  132. package/build/client/_app/immutable/chunks/HaThayHP.js.gz +0 -0
  133. package/build/client/_app/immutable/chunks/fuY-CvTV.js +0 -1
  134. package/build/client/_app/immutable/chunks/fuY-CvTV.js.br +0 -0
  135. package/build/client/_app/immutable/chunks/fuY-CvTV.js.gz +0 -0
  136. package/build/client/_app/immutable/chunks/nUq5tIUU.js +0 -2
  137. package/build/client/_app/immutable/chunks/nUq5tIUU.js.br +0 -0
  138. package/build/client/_app/immutable/chunks/nUq5tIUU.js.gz +0 -0
  139. package/build/client/_app/immutable/chunks/pOdkXMWy.js.br +0 -0
  140. package/build/client/_app/immutable/chunks/pOdkXMWy.js.gz +0 -0
  141. package/build/client/_app/immutable/entry/app.DMBaTVWt.js +0 -2
  142. package/build/client/_app/immutable/entry/app.DMBaTVWt.js.br +0 -0
  143. package/build/client/_app/immutable/entry/app.DMBaTVWt.js.gz +0 -0
  144. package/build/client/_app/immutable/entry/start.C3tcesOa.js +0 -1
  145. package/build/client/_app/immutable/entry/start.C3tcesOa.js.br +0 -2
  146. package/build/client/_app/immutable/entry/start.C3tcesOa.js.gz +0 -0
  147. package/build/client/_app/immutable/nodes/0.D4LDvqQb.js.br +0 -0
  148. package/build/client/_app/immutable/nodes/0.D4LDvqQb.js.gz +0 -0
  149. package/build/client/_app/immutable/nodes/1.5BEtqK67.js +0 -1
  150. package/build/client/_app/immutable/nodes/1.5BEtqK67.js.br +0 -0
  151. package/build/client/_app/immutable/nodes/1.5BEtqK67.js.gz +0 -0
  152. package/build/client/_app/immutable/nodes/2.C-xl-nxy.js +0 -4
  153. package/build/client/_app/immutable/nodes/2.C-xl-nxy.js.br +0 -0
  154. package/build/client/_app/immutable/nodes/2.C-xl-nxy.js.gz +0 -0
  155. package/build/client/_app/immutable/nodes/3.BqDX5X09.js +0 -4
  156. package/build/client/_app/immutable/nodes/3.BqDX5X09.js.br +0 -0
  157. package/build/client/_app/immutable/nodes/3.BqDX5X09.js.gz +0 -0
  158. package/build/server/chunks/0-DkD09WcK.js +0 -17
  159. package/build/server/chunks/1-CjtVgNYW.js +0 -9
  160. package/build/server/chunks/2-wstlfOP5.js +0 -9
  161. package/build/server/chunks/3-Cdn9_0OG.js +0 -9
  162. package/build/server/chunks/InputModal-XaplAR5y.js.map +0 -1
  163. package/build/server/chunks/Toast-ihrVamsT.js.map +0 -1
  164. package/build/server/chunks/_layout.svelte-COfbk473.js.map +0 -1
  165. package/build/server/chunks/_page.svelte-C5Xngf9U.js.map +0 -1
  166. package/build/server/chunks/_page.svelte-CKSi7jlX.js.map +0 -1
  167. package/build/server/chunks/_server.ts-BNGntgHD.js.map +0 -1
  168. package/build/server/chunks/_server.ts-BbeQOMoJ.js +0 -19
  169. package/build/server/chunks/_server.ts-BbeQOMoJ.js.map +0 -1
  170. package/build/server/chunks/_server.ts-CHX8x48n.js +0 -27
  171. package/build/server/chunks/_server.ts-CHX8x48n.js.map +0 -1
  172. package/build/server/chunks/_server.ts-a85289si.js.map +0 -1
  173. package/build/server/chunks/_server.ts-bj28Awpj.js.map +0 -1
  174. package/build/server/chunks/index-DzYX9r1_.js.map +0 -1
  175. package/build/server/chunks/index2-Da0doXJY.js.map +0 -1
  176. package/build/server/chunks/index3-DkgTDgY2.js.map +0 -1
  177. package/build/server/chunks/index4-BUZEWk-I.js.map +0 -1
@@ -1,8 +1,9 @@
1
- import * as fs from 'fs';
1
+ import * as fs2 from 'fs';
2
2
  import * as os from 'os';
3
- import * as path4 from 'path';
3
+ import * as path5 from 'path';
4
4
  import { Effect, pipe, Array as Array$1, Option } from 'effect';
5
- import * as fs4 from 'fs/promises';
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") return title;
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 = path4.join(getSessionsDir(), projectName);
236
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(projectPath, agentFile);
241
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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 = path4.join(getSessionsDir(), projectName);
257
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 fs4.readFile(filePath, "utf-8");
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 = path4.basename(filePath);
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 = path4.join(projectPath, agentFile);
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 = path4.join(projectPath, entry);
291
- const stat3 = yield* Effect.tryPromise(() => fs4.stat(entryPath).catch(() => null));
292
- if (stat3?.isDirectory() && !entry.startsWith(".")) {
293
- const subagentsPath = path4.join(entryPath, "subagents");
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
- () => fs4.stat(subagentsPath).then(() => true).catch(() => false)
333
+ () => fs6.stat(subagentsPath).then(() => true).catch(() => false)
296
334
  );
297
335
  if (subagentsExists) {
298
336
  const subagentFiles = yield* Effect.tryPromise(
299
- () => fs4.readdir(subagentsPath).catch(() => [])
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 = path4.join(subagentsPath, subagentFile);
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 = path4.join(getSessionsDir(), projectName);
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 = path4.dirname(orphan.filePath);
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(() => fs4.unlink(orphan.filePath));
371
+ yield* Effect.tryPromise(() => fs6.unlink(orphan.filePath));
334
372
  deletedAgents.push(orphan.agentId);
335
373
  } else {
336
374
  if (!backupDirCreated) {
337
- const backupDir2 = path4.join(projectPath, ".bak");
338
- yield* Effect.tryPromise(() => fs4.mkdir(backupDir2, { recursive: true }));
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 = path4.join(projectPath, ".bak");
342
- const agentBackupPath = path4.join(backupDir, `${orphan.agentId}.jsonl`);
343
- yield* Effect.tryPromise(() => fs4.rename(orphan.filePath, agentBackupPath));
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 fs4.readdir(subagentsDir);
387
+ const files = await fs6.readdir(subagentsDir);
350
388
  return files.length === 0;
351
389
  });
352
390
  if (isEmpty) {
353
- yield* Effect.tryPromise(() => fs4.rmdir(subagentsDir));
391
+ yield* Effect.tryPromise(() => fs6.rmdir(subagentsDir));
354
392
  cleanedFolders.push(subagentsDir);
355
- const sessionDir = path4.dirname(subagentsDir);
393
+ const sessionDir = path5.dirname(subagentsDir);
356
394
  const sessionDirEmpty = yield* Effect.tryPromise(async () => {
357
- const files = await fs4.readdir(sessionDir);
395
+ const files = await fs6.readdir(sessionDir);
358
396
  return files.length === 0;
359
397
  });
360
398
  if (sessionDirEmpty) {
361
- yield* Effect.tryPromise(() => fs4.rmdir(sessionDir));
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 = path4.join(getSessionsDir(), projectName);
379
- const agentFilePath = path4.join(projectPath, `${agentId}.jsonl`);
380
- const content = yield* Effect.tryPromise(() => fs4.readFile(agentFilePath, "utf-8"));
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 (const line of lines) {
384
- try {
385
- const parsed = JSON.parse(line);
386
- if ("sessionId" in parsed && !("type" in parsed)) {
387
- continue;
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
- () => fs4.access(todosDir).then(() => true).catch(() => false)
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 = path4.join(todosDir, `${sessionId}.json`);
444
+ const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
409
445
  let sessionTodos = [];
410
446
  const sessionTodoExists = yield* Effect.tryPromise(
411
- () => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
447
+ () => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
412
448
  );
413
449
  if (sessionTodoExists) {
414
- const content = yield* Effect.tryPromise(() => fs4.readFile(sessionTodoPath, "utf-8"));
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(() => fs4.readdir(todosDir));
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 = path4.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
468
+ const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
433
469
  const agentTodoExists = yield* Effect.tryPromise(
434
- () => fs4.access(agentTodoPath).then(() => true).catch(() => false)
470
+ () => fs6.access(agentTodoPath).then(() => true).catch(() => false)
435
471
  );
436
472
  if (agentTodoExists) {
437
- const content = yield* Effect.tryPromise(() => fs4.readFile(agentTodoPath, "utf-8"));
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
- () => fs4.access(todosDir).then(() => true).catch(() => false)
494
+ () => fs6.access(todosDir).then(() => true).catch(() => false)
459
495
  );
460
496
  if (!exists) return false;
461
- const sessionTodoPath = path4.join(todosDir, `${sessionId}.json`);
497
+ const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
462
498
  const sessionTodoExists = yield* Effect.tryPromise(
463
- () => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
499
+ () => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
464
500
  );
465
501
  if (sessionTodoExists) {
466
- const content = yield* Effect.tryPromise(() => fs4.readFile(sessionTodoPath, "utf-8"));
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(() => fs4.readdir(todosDir));
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 = path4.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
520
+ const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
485
521
  const agentTodoExists = yield* Effect.tryPromise(
486
- () => fs4.access(agentTodoPath).then(() => true).catch(() => false)
522
+ () => fs6.access(agentTodoPath).then(() => true).catch(() => false)
487
523
  );
488
524
  if (agentTodoExists) {
489
- const content = yield* Effect.tryPromise(() => fs4.readFile(agentTodoPath, "utf-8"));
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
- () => fs4.access(todosDir).then(() => true).catch(() => false)
538
+ () => fs6.access(todosDir).then(() => true).catch(() => false)
503
539
  );
504
540
  if (!exists) return { deletedCount: 0 };
505
- const backupDir = path4.join(todosDir, ".bak");
506
- yield* Effect.tryPromise(() => fs4.mkdir(backupDir, { recursive: true }));
541
+ const backupDir = path5.join(todosDir, ".bak");
542
+ yield* Effect.tryPromise(() => fs6.mkdir(backupDir, { recursive: true }));
507
543
  let deletedCount = 0;
508
- const sessionTodoPath = path4.join(todosDir, `${sessionId}.json`);
544
+ const sessionTodoPath = path5.join(todosDir, `${sessionId}.json`);
509
545
  const sessionTodoExists = yield* Effect.tryPromise(
510
- () => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
546
+ () => fs6.access(sessionTodoPath).then(() => true).catch(() => false)
511
547
  );
512
548
  if (sessionTodoExists) {
513
- const backupPath = path4.join(backupDir, `${sessionId}.json`);
514
- yield* Effect.tryPromise(() => fs4.rename(sessionTodoPath, backupPath));
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 = path4.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
555
+ const agentTodoPath = path5.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
520
556
  const agentTodoExists = yield* Effect.tryPromise(
521
- () => fs4.access(agentTodoPath).then(() => true).catch(() => false)
557
+ () => fs6.access(agentTodoPath).then(() => true).catch(() => false)
522
558
  );
523
559
  if (agentTodoExists) {
524
- const backupPath = path4.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`);
525
- yield* Effect.tryPromise(() => fs4.rename(agentTodoPath, backupPath));
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
- () => fs4.access(todosDir).then(() => true).catch(() => false)
572
+ () => fs6.access(todosDir).then(() => true).catch(() => false)
537
573
  ),
538
574
  Effect.tryPromise(
539
- () => fs4.access(sessionsDir).then(() => true).catch(() => false)
575
+ () => fs6.access(sessionsDir).then(() => true).catch(() => false)
540
576
  )
541
577
  ]);
542
578
  if (!todosExists || !sessionsExists) return [];
543
- const todoFiles = yield* Effect.tryPromise(() => fs4.readdir(todosDir));
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
- () => fs4.readdir(sessionsDir, { withFileTypes: true })
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 = path4.join(sessionsDir, entry.name);
552
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(todosDir, ".bak");
576
- yield* Effect.tryPromise(() => fs4.mkdir(backupDir, { recursive: true }));
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 = path4.join(todosDir, orphan);
580
- const backupPath = path4.join(backupDir, orphan);
581
- yield* Effect.tryPromise(() => fs4.rename(filePath, backupPath));
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
- () => fs4.access(sessionsDir).then(() => true).catch(() => false)
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(() => fs4.readdir(sessionsDir, { withFileTypes: true }));
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 = path4.join(sessionsDir, entry.name);
599
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(getSessionsDir(), projectName);
615
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(projectPath, file);
621
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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 = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
661
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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 = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
667
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
668
- const lines = content.trim().split("\n").filter(Boolean);
669
- const messages = lines.map((line) => JSON.parse(line));
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(() => fs4.writeFile(filePath, newContent, "utf-8"));
687
- return { success: true, deletedMessage: deletedMsg };
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 = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
691
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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(() => fs4.writeFile(filePath, newContent, "utf-8"));
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 = path4.join(sessionsDir, projectName);
714
- const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
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 stat3 = yield* Effect.tryPromise(() => fs4.stat(filePath));
717
- if (stat3.size === 0) {
718
- yield* Effect.tryPromise(() => fs4.unlink(filePath));
719
- const agentBackupDir2 = path4.join(projectPath, ".bak");
720
- yield* Effect.tryPromise(() => fs4.mkdir(agentBackupDir2, { recursive: true }));
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 = path4.join(projectPath, `${agentId}.jsonl`);
723
- const agentBackupPath = path4.join(agentBackupDir2, `${agentId}.jsonl`);
724
- yield* Effect.tryPromise(() => fs4.rename(agentPath, agentBackupPath).catch(() => {
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 = path4.join(sessionsDir, ".bak");
731
- yield* Effect.tryPromise(() => fs4.mkdir(backupDir, { recursive: true }));
732
- const agentBackupDir = path4.join(projectPath, ".bak");
733
- yield* Effect.tryPromise(() => fs4.mkdir(agentBackupDir, { recursive: true }));
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 = path4.join(projectPath, `${agentId}.jsonl`);
736
- const agentBackupPath = path4.join(agentBackupDir, `${agentId}.jsonl`);
737
- yield* Effect.tryPromise(() => fs4.rename(agentPath, agentBackupPath).catch(() => {
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 = path4.join(backupDir, `${projectName}_${sessionId}.jsonl`);
742
- yield* Effect.tryPromise(() => fs4.rename(filePath, backupPath));
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 = path4.join(getSessionsDir(), projectName);
752
- const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
753
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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.map((line) => JSON.parse(line));
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(() => fs4.writeFile(filePath, newContent, "utf-8"));
778
- const projectFiles = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(projectPath, file);
889
+ const otherFilePath = path5.join(projectPath, file);
783
890
  try {
784
- const otherContent = yield* Effect.tryPromise(() => fs4.readFile(otherFilePath, "utf-8"));
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 = path4.join(projectPath, firstSummary.file);
805
- const summaryContent = yield* Effect.tryPromise(() => fs4.readFile(summaryFilePath, "utf-8"));
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(() => fs4.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
916
+ yield* Effect.tryPromise(() => fs6.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
814
917
  } else {
815
- const currentContent = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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(() => fs4.writeFile(filePath, updatedContent, "utf-8"));
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 = path4.join(sessionsDir, sourceProject);
896
- const targetPath = path4.join(sessionsDir, targetProject);
897
- const sourceFile = path4.join(sourcePath, `${sessionId}.jsonl`);
898
- const targetFile = path4.join(targetPath, `${sessionId}.jsonl`);
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
- () => fs4.access(sourceFile).then(() => true).catch(() => false)
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
- () => fs4.access(targetFile).then(() => true).catch(() => false)
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(() => fs4.mkdir(targetPath, { recursive: true }));
960
+ yield* Effect.tryPromise(() => fs6.mkdir(targetPath, { recursive: true }));
912
961
  const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId);
913
- yield* Effect.tryPromise(() => fs4.rename(sourceFile, targetFile));
962
+ yield* Effect.tryPromise(() => fs6.rename(sourceFile, targetFile));
914
963
  for (const agentId of linkedAgents) {
915
- const sourceAgentFile = path4.join(sourcePath, `${agentId}.jsonl`);
916
- const targetAgentFile = path4.join(targetPath, `${agentId}.jsonl`);
964
+ const sourceAgentFile = path5.join(sourcePath, `${agentId}.jsonl`);
965
+ const targetAgentFile = path5.join(targetPath, `${agentId}.jsonl`);
917
966
  const agentExists = yield* Effect.tryPromise(
918
- () => fs4.access(sourceAgentFile).then(() => true).catch(() => false)
967
+ () => fs6.access(sourceAgentFile).then(() => true).catch(() => false)
919
968
  );
920
969
  if (agentExists) {
921
- yield* Effect.tryPromise(() => fs4.rename(sourceAgentFile, targetAgentFile));
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 = path4.join(getSessionsDir(), projectName);
928
- const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
929
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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(() => fs4.writeFile(filePath, keptContent, "utf-8"));
978
- const newFilePath = path4.join(projectPath, `${newSessionId}.jsonl`);
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(() => fs4.writeFile(newFilePath, newContent, "utf-8"));
981
- const agentFiles = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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 = path4.join(projectPath, agentFile);
985
- const agentContent = yield* Effect.tryPromise(() => fs4.readFile(agentPath, "utf-8"));
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 firstAgentMsg = JSON.parse(agentLines[0]);
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 = agentLines.map((line) => {
996
- const msg = JSON.parse(line);
997
- return JSON.stringify({ ...msg, sessionId: newSessionId });
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(() => fs4.writeFile(agentPath, updatedAgentContent, "utf-8"));
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 = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
1014
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
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.map((line) => JSON.parse(line));
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(() => fs4.writeFile(filePath, newContent, "utf-8"));
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 = path4.join(getSessionsDir(), project.name);
1113
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
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
- for (const project of targetProjects) {
1174
- const sessions = yield* listSessions(project.name);
1175
- for (const session of sessions) {
1176
- const titleLower = (session.title ?? "").toLowerCase();
1177
- if (titleLower.includes(queryLower)) {
1178
- results.push({
1179
- sessionId: session.id,
1180
- projectName: project.name,
1181
- title: session.title ?? "Untitled",
1182
- matchType: "title",
1183
- timestamp: session.updatedAt
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
- for (const project of targetProjects) {
1190
- const projectPath = path4.join(getSessionsDir(), project.name);
1191
- const files = yield* Effect.tryPromise(() => fs4.readdir(projectPath));
1192
- const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
1193
- for (const file of sessionFiles) {
1194
- const sessionId = file.replace(".jsonl", "");
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
- return results.sort((a, b) => {
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 loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSession) => Effect.gen(function* () {
1236
- const projectPath = path4.join(getSessionsDir(), projectName);
1237
- const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
1238
- const content = yield* Effect.tryPromise(() => fs4.readFile(filePath, "utf-8"));
1239
- const lines = content.trim().split("\n").filter(Boolean);
1240
- const messages = lines.map((line) => JSON.parse(line));
1241
- let summaries;
1242
- if (summariesByTargetSession) {
1243
- summaries = [...summariesByTargetSession.get(sessionId) ?? []].sort(
1244
- (a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? "")
1245
- );
1246
- } else {
1247
- summaries = [];
1248
- const sessionUuids = /* @__PURE__ */ new Set();
1249
- for (const msg of messages) {
1250
- if (msg.uuid && typeof msg.uuid === "string") {
1251
- sessionUuids.add(msg.uuid);
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
- 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
- }
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
- name: project.name,
1432
- displayName: project.displayName,
1433
- path: project.path,
1434
- sessionCount: filteredSessions.length,
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, maskHomePath as v };
1440
- //# sourceMappingURL=index3-DkgTDgY2.js.map
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