@zzusp/ccsm 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +236 -232
- package/dist/assets/DiskUsage-BY6XwffG.js +2 -0
- package/dist/assets/DiskUsage-BY6XwffG.js.map +1 -0
- package/dist/assets/{ImportPage-b8NORa8b.js → ImportPage-Cwq5bx7G.js} +2 -2
- package/dist/assets/ImportPage-Cwq5bx7G.js.map +1 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js +2 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js.map +1 -0
- package/dist/assets/{ProjectMemory-aSV8UzQ9.js → ProjectMemory-CcE3KbUK.js} +2 -2
- package/dist/assets/ProjectMemory-CcE3KbUK.js.map +1 -0
- package/dist/assets/{charts-A5eNHLjX.js → charts-jxJqXXUr.js} +2 -2
- package/dist/assets/{charts-A5eNHLjX.js.map → charts-jxJqXXUr.js.map} +1 -1
- package/dist/assets/index-CrWxV6sb.css +1 -0
- package/dist/assets/index-DTbWl1jb.js +11 -0
- package/dist/assets/index-DTbWl1jb.js.map +1 -0
- package/dist/assets/markdown-Bag5rX3T.js +30 -0
- package/dist/assets/markdown-Bag5rX3T.js.map +1 -0
- package/dist/assets/{query-C1K1uQRu.js → query-CS7JQ86v.js} +2 -2
- package/dist/assets/{query-C1K1uQRu.js.map → query-CS7JQ86v.js.map} +1 -1
- package/dist/assets/{react-W0jzChlo.js → react-CPkiFScu.js} +10 -10
- package/dist/assets/{react-W0jzChlo.js.map → react-CPkiFScu.js.map} +1 -1
- package/dist/assets/{router-DfbutHY3.js → router-DwaHAh1G.js} +2 -2
- package/dist/assets/{router-DfbutHY3.js.map → router-DwaHAh1G.js.map} +1 -1
- package/dist/assets/vendor-Cs8vYp-N.js +27 -0
- package/dist/assets/vendor-Cs8vYp-N.js.map +1 -0
- package/dist/favicon.svg +7 -7
- package/dist/index.html +30 -30
- package/package.json +24 -11
- package/server/index.ts +4 -0
- package/server/lib/active-sessions.test.ts +119 -0
- package/server/lib/active-sessions.ts +95 -95
- package/server/lib/bundle.test.ts +182 -0
- package/server/lib/bundle.ts +86 -86
- package/server/lib/claude-paths.test.ts +126 -0
- package/server/lib/claude-paths.ts +43 -36
- package/server/lib/cleanup-suggestions.ts +131 -0
- package/server/lib/constants.ts +8 -7
- package/server/lib/delete-project.ts +100 -100
- package/server/lib/delete.test.ts +244 -0
- package/server/lib/delete.ts +192 -203
- package/server/lib/disk-usage.ts +81 -83
- package/server/lib/encode-cwd.ts +24 -24
- package/server/lib/export-bundle.ts +236 -236
- package/server/lib/export-import-bundle.test.ts +337 -0
- package/server/lib/fs-size.ts +38 -38
- package/server/lib/import-bundle.ts +488 -488
- package/server/lib/load-memory.ts +120 -120
- package/server/lib/load-session.ts +209 -209
- package/server/lib/modified-files.test.ts +280 -0
- package/server/lib/modified-files.ts +228 -0
- package/server/lib/open-folder.ts +47 -40
- package/server/lib/parse-jsonl.ts +160 -107
- package/server/lib/port.ts +23 -23
- package/server/lib/safe-id.test.ts +41 -0
- package/server/lib/safe-id.ts +6 -6
- package/server/lib/safe-remove.test.ts +73 -0
- package/server/lib/safe-remove.ts +25 -0
- package/server/lib/scan.ts +289 -183
- package/server/lib/search-all.ts +130 -130
- package/server/lib/search-session.ts +203 -203
- package/server/lib/system-tags.ts +20 -20
- package/server/lib/update.ts +67 -0
- package/server/lib/version.test.ts +39 -0
- package/server/lib/version.ts +117 -0
- package/server/routes/disk-cleanup.ts +54 -0
- package/server/routes/disk.ts +9 -9
- package/server/routes/import.ts +87 -87
- package/server/routes/projects.ts +104 -104
- package/server/routes/search.ts +79 -79
- package/server/routes/sessions.ts +130 -81
- package/server/routes/version.ts +34 -0
- package/server/types.ts +1 -1
- package/shared/constants.ts +7 -2
- package/shared/types.ts +513 -359
- package/dist/assets/DiskUsage-Bq4VaoUA.js +0 -2
- package/dist/assets/DiskUsage-Bq4VaoUA.js.map +0 -1
- package/dist/assets/ImportPage-b8NORa8b.js.map +0 -1
- package/dist/assets/ProjectMemory-aSV8UzQ9.js.map +0 -1
- package/dist/assets/index-DLATR3tZ.js +0 -5
- package/dist/assets/index-DLATR3tZ.js.map +0 -1
- package/dist/assets/index-DLDtbkux.css +0 -1
- package/dist/assets/vendor-CH80ylbS.js +0 -19
- package/dist/assets/vendor-CH80ylbS.js.map +0 -1
package/shared/types.ts
CHANGED
|
@@ -1,359 +1,513 @@
|
|
|
1
|
-
export interface ProjectSummary {
|
|
2
|
-
id: string;
|
|
3
|
-
encodedCwd: string;
|
|
4
|
-
decodedCwd: string;
|
|
5
|
-
cwdResolved: boolean;
|
|
6
|
-
sessionCount: number;
|
|
7
|
-
totalBytes: number;
|
|
8
|
-
lastActiveAt: string | null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface RelatedBytes {
|
|
12
|
-
jsonl: number;
|
|
13
|
-
subdir: number;
|
|
14
|
-
fileHistory: number;
|
|
15
|
-
sessionEnv: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface SessionSummary {
|
|
19
|
-
id: string;
|
|
20
|
-
projectId: string;
|
|
21
|
-
/** Auto-derived: latest `ai-title` record, falling back to first user message. */
|
|
22
|
-
title: string;
|
|
23
|
-
/** User-set name (Claude Code's `custom-title` record); null if never renamed. */
|
|
24
|
-
customTitle: string | null;
|
|
25
|
-
firstAt: string | null;
|
|
26
|
-
/** Last activity: max(latest record timestamp, file mtime) — matches `claude code resume`. */
|
|
27
|
-
lastAt: string | null;
|
|
28
|
-
messageCount: number;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
export
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
export interface
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
1
|
+
export interface ProjectSummary {
|
|
2
|
+
id: string;
|
|
3
|
+
encodedCwd: string;
|
|
4
|
+
decodedCwd: string;
|
|
5
|
+
cwdResolved: boolean;
|
|
6
|
+
sessionCount: number;
|
|
7
|
+
totalBytes: number;
|
|
8
|
+
lastActiveAt: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RelatedBytes {
|
|
12
|
+
jsonl: number;
|
|
13
|
+
subdir: number;
|
|
14
|
+
fileHistory: number;
|
|
15
|
+
sessionEnv: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SessionSummary {
|
|
19
|
+
id: string;
|
|
20
|
+
projectId: string;
|
|
21
|
+
/** Auto-derived: latest `ai-title` record, falling back to first user message. */
|
|
22
|
+
title: string;
|
|
23
|
+
/** User-set name (Claude Code's `custom-title` record); null if never renamed. */
|
|
24
|
+
customTitle: string | null;
|
|
25
|
+
firstAt: string | null;
|
|
26
|
+
/** Last activity: max(latest record timestamp, file mtime) — matches `claude code resume`. */
|
|
27
|
+
lastAt: string | null;
|
|
28
|
+
messageCount: number;
|
|
29
|
+
/** Count of tool_result blocks flagged `is_error` across the whole session. */
|
|
30
|
+
errorCount: number;
|
|
31
|
+
bytes: number;
|
|
32
|
+
relatedBytes: RelatedBytes;
|
|
33
|
+
isLivePid: boolean;
|
|
34
|
+
isRecentlyActive: boolean;
|
|
35
|
+
livePid: number | null;
|
|
36
|
+
/**
|
|
37
|
+
* Claude is *actively processing this turn* right now (a stricter state than
|
|
38
|
+
* `isLivePid`, which only means a Claude Code process is alive). True when the
|
|
39
|
+
* session has a live PID, was touched recently, and its last conversation turn
|
|
40
|
+
* is unfinished — Claude owes a reply (last record is `user`) or is mid-work
|
|
41
|
+
* (last `assistant` record ends on a `tool_use`). An aborted turn (trailing
|
|
42
|
+
* `[Request interrupted by user]`) counts as finished.
|
|
43
|
+
*/
|
|
44
|
+
isWorking: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type Block =
|
|
48
|
+
| { type: 'text'; text: string }
|
|
49
|
+
| { type: 'tool_use'; id: string; name: string; input: unknown }
|
|
50
|
+
| { type: 'tool_result'; toolUseId: string; content: string; isError: boolean }
|
|
51
|
+
| { type: 'thinking'; text: string }
|
|
52
|
+
| { type: 'image'; mediaType: string | null }
|
|
53
|
+
| { type: 'unknown'; raw: unknown };
|
|
54
|
+
|
|
55
|
+
export interface Message {
|
|
56
|
+
uuid: string;
|
|
57
|
+
parentUuid: string | null;
|
|
58
|
+
type: 'user' | 'assistant';
|
|
59
|
+
ts: string | null;
|
|
60
|
+
model: string | null;
|
|
61
|
+
blocks: Block[];
|
|
62
|
+
isMeta: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface SessionMeta {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
projectId: string;
|
|
68
|
+
cwd: string | null;
|
|
69
|
+
gitBranch: string | null;
|
|
70
|
+
version: string | null;
|
|
71
|
+
firstAt: string | null;
|
|
72
|
+
/** Last activity: max(latest record timestamp, file mtime) — matches `claude code resume`. */
|
|
73
|
+
lastAt: string | null;
|
|
74
|
+
messageCount: number;
|
|
75
|
+
bytes: number;
|
|
76
|
+
/** Auto-derived: latest `ai-title` record, falling back to first user message. */
|
|
77
|
+
title: string;
|
|
78
|
+
/** User-set name; null if never renamed. */
|
|
79
|
+
customTitle: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SessionDetail {
|
|
83
|
+
meta: SessionMeta;
|
|
84
|
+
messages: Message[];
|
|
85
|
+
truncated: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface DeleteRequestItem {
|
|
89
|
+
projectId: string;
|
|
90
|
+
sessionId: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface DeletedItem extends DeleteRequestItem {
|
|
94
|
+
freedBytes: number;
|
|
95
|
+
cleaned: string[];
|
|
96
|
+
relatedBytes: RelatedBytes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface SkippedItem extends DeleteRequestItem {
|
|
100
|
+
reason: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface DeleteResult {
|
|
104
|
+
deleted: DeletedItem[];
|
|
105
|
+
skipped: SkippedItem[];
|
|
106
|
+
historyLinesRemoved: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface DeleteProjectResult extends DeleteResult {
|
|
110
|
+
/** True only when the project directory itself was removed (all sessions cleared). */
|
|
111
|
+
projectDirRemoved: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface RevealProjectResult {
|
|
115
|
+
ok: true;
|
|
116
|
+
path: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface DiskUsageProjectRow {
|
|
120
|
+
projectId: string;
|
|
121
|
+
decodedCwd: string;
|
|
122
|
+
totalBytes: number;
|
|
123
|
+
sessionCount: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface DiskUsageMonthRow {
|
|
127
|
+
month: string;
|
|
128
|
+
totalBytes: number;
|
|
129
|
+
sessionCount: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface DiskUsageTopSession {
|
|
133
|
+
projectId: string;
|
|
134
|
+
sessionId: string;
|
|
135
|
+
title: string;
|
|
136
|
+
customTitle: string | null;
|
|
137
|
+
totalBytes: number;
|
|
138
|
+
lastAt: string | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface DiskUsage {
|
|
142
|
+
byProject: DiskUsageProjectRow[];
|
|
143
|
+
byMonth: DiskUsageMonthRow[];
|
|
144
|
+
topSessions: DiskUsageTopSession[];
|
|
145
|
+
totalBytes: number;
|
|
146
|
+
totalSessions: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── 清理建议:把"可行动"的 cleanup target 显式列出来 ──────────────────────────
|
|
150
|
+
//
|
|
151
|
+
// largeSessions:top 10 最大的会话(按 jsonl + subdir + file-history + session-env 合计)
|
|
152
|
+
// orphanFileHistory / orphanSessionEnv:file-history/<sid>/ 或 session-env/<sid>/ 存在,
|
|
153
|
+
// 但对应 sid 在 projects/*/<sid>.jsonl 全集中找不到——典型情况是会话主体已被手动删
|
|
154
|
+
// 掉但侧 store 没清,纯属浪费磁盘。
|
|
155
|
+
|
|
156
|
+
export interface DiskCleanupLargeSession {
|
|
157
|
+
sessionId: string;
|
|
158
|
+
projectId: string;
|
|
159
|
+
projectPath: string;
|
|
160
|
+
title: string;
|
|
161
|
+
customTitle: string | null;
|
|
162
|
+
sizeBytes: number;
|
|
163
|
+
lastActivity: string | null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export type DiskOrphanKind = 'file-history' | 'session-env';
|
|
167
|
+
|
|
168
|
+
export interface DiskCleanupOrphan {
|
|
169
|
+
sessionId: string;
|
|
170
|
+
sizeBytes: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface DiskCleanupSuggestions {
|
|
174
|
+
largeSessions: DiskCleanupLargeSession[];
|
|
175
|
+
orphanFileHistory: DiskCleanupOrphan[];
|
|
176
|
+
orphanSessionEnv: DiskCleanupOrphan[];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface DiskOrphanDeleteResult {
|
|
180
|
+
sessionId: string;
|
|
181
|
+
kind: DiskOrphanKind;
|
|
182
|
+
freedBytes: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export type MemoryType = 'user' | 'feedback' | 'project' | 'reference';
|
|
186
|
+
|
|
187
|
+
export interface MemoryEntry {
|
|
188
|
+
filename: string;
|
|
189
|
+
name: string | null;
|
|
190
|
+
description: string | null;
|
|
191
|
+
type: MemoryType | null;
|
|
192
|
+
body: string;
|
|
193
|
+
bytes: number;
|
|
194
|
+
mtime: string | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface MemoryResponse {
|
|
198
|
+
index: string | null;
|
|
199
|
+
entries: MemoryEntry[];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface HealthResponse {
|
|
203
|
+
ok: boolean;
|
|
204
|
+
claudeRoot: string;
|
|
205
|
+
claudeRootExists: boolean;
|
|
206
|
+
platform: string;
|
|
207
|
+
node: string;
|
|
208
|
+
pid: number;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ── Version check & self-update ─────────────────────────────────────────────
|
|
212
|
+
//
|
|
213
|
+
// current = package.json version (same source as `ccsm --version`).
|
|
214
|
+
// latest/releaseNotes/releaseUrl come from the GitHub "latest release" API; when
|
|
215
|
+
// that check fails (offline, rate-limited) `checkError` carries the reason and the
|
|
216
|
+
// UI silently degrades to showing just the current version.
|
|
217
|
+
|
|
218
|
+
export interface VersionInfo {
|
|
219
|
+
current: string;
|
|
220
|
+
/** Latest release tag with leading `v` stripped; null if the check failed. */
|
|
221
|
+
latest: string | null;
|
|
222
|
+
hasUpdate: boolean;
|
|
223
|
+
/** Release title (GitHub `name`), falling back to the tag. */
|
|
224
|
+
releaseName: string | null;
|
|
225
|
+
/** Markdown release notes (GitHub `body`). */
|
|
226
|
+
releaseNotes: string | null;
|
|
227
|
+
/** Link to the GitHub release page. */
|
|
228
|
+
releaseUrl: string | null;
|
|
229
|
+
publishedAt: string | null;
|
|
230
|
+
repositoryUrl: string;
|
|
231
|
+
/** Non-null when the latest-release lookup failed. */
|
|
232
|
+
checkError: string | null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface VersionUpdateResult {
|
|
236
|
+
/** True when `npm install -g …` exited 0. */
|
|
237
|
+
ok: boolean;
|
|
238
|
+
fromVersion: string;
|
|
239
|
+
/** Target version on success; null on failure. */
|
|
240
|
+
toVersion: string | null;
|
|
241
|
+
/** Tail of the package-manager stdout/stderr. */
|
|
242
|
+
output: string;
|
|
243
|
+
/** True after a successful update — the running process still serves the old code. */
|
|
244
|
+
restartRequired: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Cross-device share: export/import bundles ───────────────────────────────
|
|
248
|
+
//
|
|
249
|
+
// A bundle is a path-INDEPENDENT folder a user copies / commits-to-git / cloud-
|
|
250
|
+
// syncs between devices. The structural absolute path is replaced with a sentinel
|
|
251
|
+
// (`placeholder`) on export and substituted with the local path on import:
|
|
252
|
+
// - session `.jsonl` lines carry the project root in their `cwd` field
|
|
253
|
+
// - `history.jsonl` lines carry it in their `project` field (different name!)
|
|
254
|
+
|
|
255
|
+
export const BUNDLE_KIND = 'claude-session-bundle' as const;
|
|
256
|
+
export const BUNDLE_SCHEMA_VERSION = 1 as const;
|
|
257
|
+
|
|
258
|
+
export interface BundleFileMeta {
|
|
259
|
+
/** sha256 of the bytes as written into the bundle (sentinel form). */
|
|
260
|
+
sha256: string;
|
|
261
|
+
/** Line count for .jsonl / .ndjson members. */
|
|
262
|
+
lines: number;
|
|
263
|
+
bytes: number;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface BundleMemoryFileMeta {
|
|
267
|
+
filename: string;
|
|
268
|
+
/** True only for the MEMORY.md index. */
|
|
269
|
+
isIndex: boolean;
|
|
270
|
+
sha256: string;
|
|
271
|
+
bytes: number;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface BundleMemoryInventory {
|
|
275
|
+
hasIndex: boolean;
|
|
276
|
+
entries: BundleMemoryFileMeta[];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface BundleSessionMeta {
|
|
280
|
+
sessionId: string;
|
|
281
|
+
title: string;
|
|
282
|
+
customTitle: string | null;
|
|
283
|
+
firstAt: string | null;
|
|
284
|
+
lastAt: string | null;
|
|
285
|
+
messageCount: number;
|
|
286
|
+
/** True if at least one conversation line had its `cwd` replaced by the sentinel. */
|
|
287
|
+
cwdRewritten: boolean;
|
|
288
|
+
conversation: BundleFileMeta;
|
|
289
|
+
/** Null when no history.jsonl lines matched this session. */
|
|
290
|
+
history: BundleFileMeta | null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface BundleSource {
|
|
294
|
+
platform: string;
|
|
295
|
+
pathSep: string;
|
|
296
|
+
projectId: string;
|
|
297
|
+
cwd: string;
|
|
298
|
+
cwdResolvedAtExport: boolean;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface BundleManifest {
|
|
302
|
+
schemaVersion: number;
|
|
303
|
+
kind: typeof BUNDLE_KIND;
|
|
304
|
+
exportedAt: string;
|
|
305
|
+
/** The literal sentinel string standing in for the project root. */
|
|
306
|
+
placeholder: string;
|
|
307
|
+
source: BundleSource;
|
|
308
|
+
memory: BundleMemoryInventory;
|
|
309
|
+
sessions: BundleSessionMeta[];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface ExportRequest {
|
|
313
|
+
projectId: string;
|
|
314
|
+
/** Session ids to include; null/'all' exports every session in the project. */
|
|
315
|
+
sessionIds: string[] | 'all';
|
|
316
|
+
destDir: string;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface ExportResult {
|
|
320
|
+
destDir: string;
|
|
321
|
+
sessionsExported: number;
|
|
322
|
+
memoryFilesExported: number;
|
|
323
|
+
historyLinesExported: number;
|
|
324
|
+
totalBytes: number;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export type ImportCollisionPolicy = 'skip' | 'overwrite-if-newer' | 'keep-both';
|
|
328
|
+
|
|
329
|
+
export interface ImportTargetSuggestion {
|
|
330
|
+
cwd: string;
|
|
331
|
+
projectId: string;
|
|
332
|
+
reason: 'existing-project' | 'original-path' | 'same-basename';
|
|
333
|
+
/** True if the directory currently resolves on disk. */
|
|
334
|
+
resolved: boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export interface ImportRemapPlan {
|
|
338
|
+
sourceCwd: string;
|
|
339
|
+
targetCwd: string;
|
|
340
|
+
targetProjectId: string;
|
|
341
|
+
/** True if a project dir already exists locally for the target id. */
|
|
342
|
+
targetProjectExists: boolean;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export type ImportSessionAction = 'create' | 'skip' | 'overwrite' | 'keep-both';
|
|
346
|
+
|
|
347
|
+
export interface ImportSessionPlan {
|
|
348
|
+
sessionId: string;
|
|
349
|
+
title: string;
|
|
350
|
+
action: ImportSessionAction;
|
|
351
|
+
/** Present for skip; explains why. */
|
|
352
|
+
reason?: string;
|
|
353
|
+
/** For keep-both: the freshly minted session id the copy lands under. */
|
|
354
|
+
newSessionId?: string;
|
|
355
|
+
isLivePid: boolean;
|
|
356
|
+
isRecentlyActive: boolean;
|
|
357
|
+
/** Local lastAt when a same-id session already exists (drives overwrite-if-newer). */
|
|
358
|
+
localLastAt: string | null;
|
|
359
|
+
bundleLastAt: string | null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export type ImportMemoryAction = 'create' | 'skip' | 'conflict';
|
|
363
|
+
|
|
364
|
+
export interface ImportMemoryPlan {
|
|
365
|
+
filename: string;
|
|
366
|
+
isIndex: boolean;
|
|
367
|
+
action: ImportMemoryAction;
|
|
368
|
+
/** For conflict: the alternate filename the incoming copy will be written as. */
|
|
369
|
+
writtenAs?: string;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export interface ImportPreviewRequest {
|
|
373
|
+
bundleDir: string;
|
|
374
|
+
/** Omit to let the server pick the best-suggested target. */
|
|
375
|
+
targetCwd?: string;
|
|
376
|
+
collisionPolicy: ImportCollisionPolicy;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export interface ImportPreviewResult {
|
|
380
|
+
source: BundleSource;
|
|
381
|
+
remap: ImportRemapPlan;
|
|
382
|
+
suggestions: ImportTargetSuggestion[];
|
|
383
|
+
sessions: ImportSessionPlan[];
|
|
384
|
+
memory: ImportMemoryPlan[];
|
|
385
|
+
historyLinesToAdd: number;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface ImportCommitRequest {
|
|
389
|
+
bundleDir: string;
|
|
390
|
+
targetCwd: string;
|
|
391
|
+
collisionPolicy: ImportCollisionPolicy;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export interface ImportedSession {
|
|
395
|
+
sessionId: string;
|
|
396
|
+
action: ImportSessionAction;
|
|
397
|
+
newSessionId?: string;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export interface ImportResult {
|
|
401
|
+
targetProjectId: string;
|
|
402
|
+
targetCwd: string;
|
|
403
|
+
imported: ImportedSession[];
|
|
404
|
+
skipped: SkippedItem[];
|
|
405
|
+
historyLinesAdded: number;
|
|
406
|
+
memoryWritten: string[];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ── Session: modified files ─────────────────────────────────────────────────
|
|
410
|
+
//
|
|
411
|
+
// 一个会话里"被修改过的文件" = 该会话 .jsonl 中 tool_use(Edit/Write/MultiEdit/
|
|
412
|
+
// NotebookEdit) 的 input.file_path / notebook_path 聚合。文件快照本身在
|
|
413
|
+
// ~/.claude/file-history/<sid>/<hash>@v<n>,文件名是 hash 反查不出路径,所以
|
|
414
|
+
// 我们以 jsonl 里的 tool_use 记录为单一事实源。
|
|
415
|
+
//
|
|
416
|
+
// errored = 该 tool_use 对应的 tool_result 标了 is_error。
|
|
417
|
+
// pending = 没找到对应 tool_result(截断或仍在进行中)。
|
|
418
|
+
// totalCount/errorCount 把这两种都算在内,UI 区分展示。
|
|
419
|
+
|
|
420
|
+
export type ModifiedFileToolName = 'Edit' | 'Write' | 'MultiEdit' | 'NotebookEdit';
|
|
421
|
+
|
|
422
|
+
/** One hunk of a structured patch, copied verbatim from Claude Code's
|
|
423
|
+
* `toolUseResult.structuredPatch`. Carries the *real* file line numbers so the
|
|
424
|
+
* UI can render a GitHub-style unified diff with accurate gutters + omitted gaps. */
|
|
425
|
+
export interface DiffHunk {
|
|
426
|
+
oldStart: number;
|
|
427
|
+
oldLines: number;
|
|
428
|
+
newStart: number;
|
|
429
|
+
newLines: number;
|
|
430
|
+
/** Each entry prefixed with ' ' (context), '-' (removed), or '+' (added). */
|
|
431
|
+
lines: string[];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface ModifiedFileOperation {
|
|
435
|
+
toolUseId: string;
|
|
436
|
+
toolName: ModifiedFileToolName;
|
|
437
|
+
ts: string | null;
|
|
438
|
+
/** uuid of the assistant message that issued this tool_use; lets the UI focus it. */
|
|
439
|
+
messageUuid: string | null;
|
|
440
|
+
errored: boolean;
|
|
441
|
+
pending: boolean;
|
|
442
|
+
/** Accurate diff from the tool_result, with real file line numbers. Empty array
|
|
443
|
+
* for a brand-new file (Write/NotebookEdit create — render input content as all-added);
|
|
444
|
+
* null when still pending or the session was truncated before the result. */
|
|
445
|
+
structuredPatch: DiffHunk[] | null;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export interface ModifiedFileSummary {
|
|
449
|
+
/** Absolute path as recorded in the tool_use input. */
|
|
450
|
+
filePath: string;
|
|
451
|
+
/** Path relativized against session.cwd when filePath sits under it; else null. */
|
|
452
|
+
relativePath: string | null;
|
|
453
|
+
editCount: number;
|
|
454
|
+
writeCount: number;
|
|
455
|
+
multiEditCount: number;
|
|
456
|
+
notebookEditCount: number;
|
|
457
|
+
totalCount: number;
|
|
458
|
+
errorCount: number;
|
|
459
|
+
firstAt: string | null;
|
|
460
|
+
lastAt: string | null;
|
|
461
|
+
/** Operations sorted by ts asc (null ts last). */
|
|
462
|
+
operations: ModifiedFileOperation[];
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export interface ModifiedFilesResponse {
|
|
466
|
+
sessionId: string;
|
|
467
|
+
projectId: string;
|
|
468
|
+
/** Session-recorded cwd; used for relative paths and display. */
|
|
469
|
+
cwd: string | null;
|
|
470
|
+
/** Files sorted by lastAt desc (no-ts entries last). */
|
|
471
|
+
files: ModifiedFileSummary[];
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** Response of opening a session-modified file in the OS default app. */
|
|
475
|
+
export interface OpenFileResult {
|
|
476
|
+
ok: true;
|
|
477
|
+
path: string;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export type SearchBlockKind = 'text' | 'tool_use' | 'tool_result' | 'thinking';
|
|
481
|
+
|
|
482
|
+
export interface SearchSnippet {
|
|
483
|
+
uuid: string;
|
|
484
|
+
ts: string | null;
|
|
485
|
+
role: 'user' | 'assistant';
|
|
486
|
+
blockKind: SearchBlockKind;
|
|
487
|
+
before: string;
|
|
488
|
+
match: string;
|
|
489
|
+
after: string;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export interface SearchSessionHit {
|
|
493
|
+
type: 'session';
|
|
494
|
+
projectId: string;
|
|
495
|
+
sessionId: string;
|
|
496
|
+
projectDecodedCwd: string;
|
|
497
|
+
title: string;
|
|
498
|
+
customTitle: string | null;
|
|
499
|
+
lastAt: string | null;
|
|
500
|
+
/** True if the per-session snippet cap was hit; UI shows "+more". */
|
|
501
|
+
hasMore: boolean;
|
|
502
|
+
snippets: SearchSnippet[];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export interface SearchDone {
|
|
506
|
+
type: 'done';
|
|
507
|
+
scanned: number;
|
|
508
|
+
matched: number;
|
|
509
|
+
durationMs: number;
|
|
510
|
+
truncated: boolean;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export type SearchEvent = SearchSessionHit | SearchDone;
|