@zzusp/ccsm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/bin/cli.mjs +52 -0
- package/dist/assets/DiskUsage-Bq4VaoUA.js +2 -0
- package/dist/assets/DiskUsage-Bq4VaoUA.js.map +1 -0
- package/dist/assets/ImportPage-b8NORa8b.js +2 -0
- package/dist/assets/ImportPage-b8NORa8b.js.map +1 -0
- package/dist/assets/ProjectMemory-aSV8UzQ9.js +2 -0
- package/dist/assets/ProjectMemory-aSV8UzQ9.js.map +1 -0
- package/dist/assets/charts-A5eNHLjX.js +56 -0
- package/dist/assets/charts-A5eNHLjX.js.map +1 -0
- package/dist/assets/geist-mono-cyrillic-wght-normal-BZdD_g9V.woff2 +0 -0
- package/dist/assets/geist-mono-latin-ext-wght-normal-b6lpi8_2.woff2 +0 -0
- package/dist/assets/geist-mono-latin-wght-normal-Cjtb1TV-.woff2 +0 -0
- package/dist/assets/index-DLATR3tZ.js +5 -0
- package/dist/assets/index-DLATR3tZ.js.map +1 -0
- package/dist/assets/index-DLDtbkux.css +1 -0
- package/dist/assets/plus-jakarta-sans-latin-ext-wght-italic-DJWiFoht.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-latin-wght-italic-DnD1KgkH.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-vietnamese-wght-italic-CPBsCcxN.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2 +0 -0
- package/dist/assets/query-C1K1uQRu.js +2 -0
- package/dist/assets/query-C1K1uQRu.js.map +1 -0
- package/dist/assets/react-W0jzChlo.js +50 -0
- package/dist/assets/react-W0jzChlo.js.map +1 -0
- package/dist/assets/router-DfbutHY3.js +13 -0
- package/dist/assets/router-DfbutHY3.js.map +1 -0
- package/dist/assets/vendor-CH80ylbS.js +19 -0
- package/dist/assets/vendor-CH80ylbS.js.map +1 -0
- package/dist/favicon.svg +7 -0
- package/dist/index.html +30 -0
- package/package.json +72 -0
- package/server/index.ts +126 -0
- package/server/lib/active-sessions.ts +95 -0
- package/server/lib/bundle.ts +86 -0
- package/server/lib/claude-paths.ts +36 -0
- package/server/lib/constants.ts +7 -0
- package/server/lib/delete-project.ts +100 -0
- package/server/lib/delete.ts +203 -0
- package/server/lib/disk-usage.ts +83 -0
- package/server/lib/encode-cwd.ts +24 -0
- package/server/lib/export-bundle.ts +236 -0
- package/server/lib/fs-size.ts +38 -0
- package/server/lib/import-bundle.ts +488 -0
- package/server/lib/load-memory.ts +120 -0
- package/server/lib/load-session.ts +209 -0
- package/server/lib/open-folder.ts +40 -0
- package/server/lib/parse-jsonl.ts +107 -0
- package/server/lib/port.ts +23 -0
- package/server/lib/rename-session.ts +0 -0
- package/server/lib/safe-id.ts +6 -0
- package/server/lib/scan.ts +183 -0
- package/server/lib/search-all.ts +130 -0
- package/server/lib/search-session.ts +203 -0
- package/server/lib/system-tags.ts +20 -0
- package/server/routes/disk.ts +9 -0
- package/server/routes/import.ts +87 -0
- package/server/routes/projects.ts +104 -0
- package/server/routes/search.ts +79 -0
- package/server/routes/sessions.ts +81 -0
- package/server/types.ts +1 -0
- package/shared/constants.ts +2 -0
- package/shared/types.ts +359 -0
package/shared/types.ts
ADDED
|
@@ -0,0 +1,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
|
+
bytes: number;
|
|
30
|
+
relatedBytes: RelatedBytes;
|
|
31
|
+
isLivePid: boolean;
|
|
32
|
+
isRecentlyActive: boolean;
|
|
33
|
+
livePid: number | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type Block =
|
|
37
|
+
| { type: 'text'; text: string }
|
|
38
|
+
| { type: 'tool_use'; id: string; name: string; input: unknown }
|
|
39
|
+
| { type: 'tool_result'; toolUseId: string; content: string; isError: boolean }
|
|
40
|
+
| { type: 'thinking'; text: string }
|
|
41
|
+
| { type: 'image'; mediaType: string | null }
|
|
42
|
+
| { type: 'unknown'; raw: unknown };
|
|
43
|
+
|
|
44
|
+
export interface Message {
|
|
45
|
+
uuid: string;
|
|
46
|
+
parentUuid: string | null;
|
|
47
|
+
type: 'user' | 'assistant';
|
|
48
|
+
ts: string | null;
|
|
49
|
+
model: string | null;
|
|
50
|
+
blocks: Block[];
|
|
51
|
+
isMeta: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SessionMeta {
|
|
55
|
+
sessionId: string;
|
|
56
|
+
projectId: string;
|
|
57
|
+
cwd: string | null;
|
|
58
|
+
gitBranch: string | null;
|
|
59
|
+
version: string | null;
|
|
60
|
+
firstAt: string | null;
|
|
61
|
+
/** Last activity: max(latest record timestamp, file mtime) — matches `claude code resume`. */
|
|
62
|
+
lastAt: string | null;
|
|
63
|
+
messageCount: number;
|
|
64
|
+
bytes: number;
|
|
65
|
+
/** Auto-derived: latest `ai-title` record, falling back to first user message. */
|
|
66
|
+
title: string;
|
|
67
|
+
/** User-set name; null if never renamed. */
|
|
68
|
+
customTitle: string | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SessionDetail {
|
|
72
|
+
meta: SessionMeta;
|
|
73
|
+
messages: Message[];
|
|
74
|
+
truncated: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DeleteRequestItem {
|
|
78
|
+
projectId: string;
|
|
79
|
+
sessionId: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DeletedItem extends DeleteRequestItem {
|
|
83
|
+
freedBytes: number;
|
|
84
|
+
cleaned: string[];
|
|
85
|
+
relatedBytes: RelatedBytes;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SkippedItem extends DeleteRequestItem {
|
|
89
|
+
reason: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface DeleteResult {
|
|
93
|
+
deleted: DeletedItem[];
|
|
94
|
+
skipped: SkippedItem[];
|
|
95
|
+
historyLinesRemoved: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface DeleteProjectResult extends DeleteResult {
|
|
99
|
+
/** True only when the project directory itself was removed (all sessions cleared). */
|
|
100
|
+
projectDirRemoved: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface RevealProjectResult {
|
|
104
|
+
ok: true;
|
|
105
|
+
path: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface DiskUsageProjectRow {
|
|
109
|
+
projectId: string;
|
|
110
|
+
decodedCwd: string;
|
|
111
|
+
totalBytes: number;
|
|
112
|
+
sessionCount: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DiskUsageMonthRow {
|
|
116
|
+
month: string;
|
|
117
|
+
totalBytes: number;
|
|
118
|
+
sessionCount: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface DiskUsageTopSession {
|
|
122
|
+
projectId: string;
|
|
123
|
+
sessionId: string;
|
|
124
|
+
title: string;
|
|
125
|
+
customTitle: string | null;
|
|
126
|
+
totalBytes: number;
|
|
127
|
+
lastAt: string | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface DiskUsage {
|
|
131
|
+
byProject: DiskUsageProjectRow[];
|
|
132
|
+
byMonth: DiskUsageMonthRow[];
|
|
133
|
+
topSessions: DiskUsageTopSession[];
|
|
134
|
+
totalBytes: number;
|
|
135
|
+
totalSessions: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type MemoryType = 'user' | 'feedback' | 'project' | 'reference';
|
|
139
|
+
|
|
140
|
+
export interface MemoryEntry {
|
|
141
|
+
filename: string;
|
|
142
|
+
name: string | null;
|
|
143
|
+
description: string | null;
|
|
144
|
+
type: MemoryType | null;
|
|
145
|
+
body: string;
|
|
146
|
+
bytes: number;
|
|
147
|
+
mtime: string | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface MemoryResponse {
|
|
151
|
+
index: string | null;
|
|
152
|
+
entries: MemoryEntry[];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface HealthResponse {
|
|
156
|
+
ok: boolean;
|
|
157
|
+
claudeRoot: string;
|
|
158
|
+
claudeRootExists: boolean;
|
|
159
|
+
platform: string;
|
|
160
|
+
node: string;
|
|
161
|
+
pid: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── Cross-device share: export/import bundles ───────────────────────────────
|
|
165
|
+
//
|
|
166
|
+
// A bundle is a path-INDEPENDENT folder a user copies / commits-to-git / cloud-
|
|
167
|
+
// syncs between devices. The structural absolute path is replaced with a sentinel
|
|
168
|
+
// (`placeholder`) on export and substituted with the local path on import:
|
|
169
|
+
// - session `.jsonl` lines carry the project root in their `cwd` field
|
|
170
|
+
// - `history.jsonl` lines carry it in their `project` field (different name!)
|
|
171
|
+
|
|
172
|
+
export const BUNDLE_KIND = 'claude-session-bundle' as const;
|
|
173
|
+
export const BUNDLE_SCHEMA_VERSION = 1 as const;
|
|
174
|
+
|
|
175
|
+
export interface BundleFileMeta {
|
|
176
|
+
/** sha256 of the bytes as written into the bundle (sentinel form). */
|
|
177
|
+
sha256: string;
|
|
178
|
+
/** Line count for .jsonl / .ndjson members. */
|
|
179
|
+
lines: number;
|
|
180
|
+
bytes: number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface BundleMemoryFileMeta {
|
|
184
|
+
filename: string;
|
|
185
|
+
/** True only for the MEMORY.md index. */
|
|
186
|
+
isIndex: boolean;
|
|
187
|
+
sha256: string;
|
|
188
|
+
bytes: number;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface BundleMemoryInventory {
|
|
192
|
+
hasIndex: boolean;
|
|
193
|
+
entries: BundleMemoryFileMeta[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface BundleSessionMeta {
|
|
197
|
+
sessionId: string;
|
|
198
|
+
title: string;
|
|
199
|
+
customTitle: string | null;
|
|
200
|
+
firstAt: string | null;
|
|
201
|
+
lastAt: string | null;
|
|
202
|
+
messageCount: number;
|
|
203
|
+
/** True if at least one conversation line had its `cwd` replaced by the sentinel. */
|
|
204
|
+
cwdRewritten: boolean;
|
|
205
|
+
conversation: BundleFileMeta;
|
|
206
|
+
/** Null when no history.jsonl lines matched this session. */
|
|
207
|
+
history: BundleFileMeta | null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface BundleSource {
|
|
211
|
+
platform: string;
|
|
212
|
+
pathSep: string;
|
|
213
|
+
projectId: string;
|
|
214
|
+
cwd: string;
|
|
215
|
+
cwdResolvedAtExport: boolean;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface BundleManifest {
|
|
219
|
+
schemaVersion: number;
|
|
220
|
+
kind: typeof BUNDLE_KIND;
|
|
221
|
+
exportedAt: string;
|
|
222
|
+
/** The literal sentinel string standing in for the project root. */
|
|
223
|
+
placeholder: string;
|
|
224
|
+
source: BundleSource;
|
|
225
|
+
memory: BundleMemoryInventory;
|
|
226
|
+
sessions: BundleSessionMeta[];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface ExportRequest {
|
|
230
|
+
projectId: string;
|
|
231
|
+
/** Session ids to include; null/'all' exports every session in the project. */
|
|
232
|
+
sessionIds: string[] | 'all';
|
|
233
|
+
destDir: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface ExportResult {
|
|
237
|
+
destDir: string;
|
|
238
|
+
sessionsExported: number;
|
|
239
|
+
memoryFilesExported: number;
|
|
240
|
+
historyLinesExported: number;
|
|
241
|
+
totalBytes: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export type ImportCollisionPolicy = 'skip' | 'overwrite-if-newer' | 'keep-both';
|
|
245
|
+
|
|
246
|
+
export interface ImportTargetSuggestion {
|
|
247
|
+
cwd: string;
|
|
248
|
+
projectId: string;
|
|
249
|
+
reason: 'existing-project' | 'original-path' | 'same-basename';
|
|
250
|
+
/** True if the directory currently resolves on disk. */
|
|
251
|
+
resolved: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface ImportRemapPlan {
|
|
255
|
+
sourceCwd: string;
|
|
256
|
+
targetCwd: string;
|
|
257
|
+
targetProjectId: string;
|
|
258
|
+
/** True if a project dir already exists locally for the target id. */
|
|
259
|
+
targetProjectExists: boolean;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export type ImportSessionAction = 'create' | 'skip' | 'overwrite' | 'keep-both';
|
|
263
|
+
|
|
264
|
+
export interface ImportSessionPlan {
|
|
265
|
+
sessionId: string;
|
|
266
|
+
title: string;
|
|
267
|
+
action: ImportSessionAction;
|
|
268
|
+
/** Present for skip; explains why. */
|
|
269
|
+
reason?: string;
|
|
270
|
+
/** For keep-both: the freshly minted session id the copy lands under. */
|
|
271
|
+
newSessionId?: string;
|
|
272
|
+
isLivePid: boolean;
|
|
273
|
+
isRecentlyActive: boolean;
|
|
274
|
+
/** Local lastAt when a same-id session already exists (drives overwrite-if-newer). */
|
|
275
|
+
localLastAt: string | null;
|
|
276
|
+
bundleLastAt: string | null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export type ImportMemoryAction = 'create' | 'skip' | 'conflict';
|
|
280
|
+
|
|
281
|
+
export interface ImportMemoryPlan {
|
|
282
|
+
filename: string;
|
|
283
|
+
isIndex: boolean;
|
|
284
|
+
action: ImportMemoryAction;
|
|
285
|
+
/** For conflict: the alternate filename the incoming copy will be written as. */
|
|
286
|
+
writtenAs?: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface ImportPreviewRequest {
|
|
290
|
+
bundleDir: string;
|
|
291
|
+
/** Omit to let the server pick the best-suggested target. */
|
|
292
|
+
targetCwd?: string;
|
|
293
|
+
collisionPolicy: ImportCollisionPolicy;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface ImportPreviewResult {
|
|
297
|
+
source: BundleSource;
|
|
298
|
+
remap: ImportRemapPlan;
|
|
299
|
+
suggestions: ImportTargetSuggestion[];
|
|
300
|
+
sessions: ImportSessionPlan[];
|
|
301
|
+
memory: ImportMemoryPlan[];
|
|
302
|
+
historyLinesToAdd: number;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export interface ImportCommitRequest {
|
|
306
|
+
bundleDir: string;
|
|
307
|
+
targetCwd: string;
|
|
308
|
+
collisionPolicy: ImportCollisionPolicy;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface ImportedSession {
|
|
312
|
+
sessionId: string;
|
|
313
|
+
action: ImportSessionAction;
|
|
314
|
+
newSessionId?: string;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export interface ImportResult {
|
|
318
|
+
targetProjectId: string;
|
|
319
|
+
targetCwd: string;
|
|
320
|
+
imported: ImportedSession[];
|
|
321
|
+
skipped: SkippedItem[];
|
|
322
|
+
historyLinesAdded: number;
|
|
323
|
+
memoryWritten: string[];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export type SearchBlockKind = 'text' | 'tool_use' | 'tool_result' | 'thinking';
|
|
327
|
+
|
|
328
|
+
export interface SearchSnippet {
|
|
329
|
+
uuid: string;
|
|
330
|
+
ts: string | null;
|
|
331
|
+
role: 'user' | 'assistant';
|
|
332
|
+
blockKind: SearchBlockKind;
|
|
333
|
+
before: string;
|
|
334
|
+
match: string;
|
|
335
|
+
after: string;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface SearchSessionHit {
|
|
339
|
+
type: 'session';
|
|
340
|
+
projectId: string;
|
|
341
|
+
sessionId: string;
|
|
342
|
+
projectDecodedCwd: string;
|
|
343
|
+
title: string;
|
|
344
|
+
customTitle: string | null;
|
|
345
|
+
lastAt: string | null;
|
|
346
|
+
/** True if the per-session snippet cap was hit; UI shows "+more". */
|
|
347
|
+
hasMore: boolean;
|
|
348
|
+
snippets: SearchSnippet[];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface SearchDone {
|
|
352
|
+
type: 'done';
|
|
353
|
+
scanned: number;
|
|
354
|
+
matched: number;
|
|
355
|
+
durationMs: number;
|
|
356
|
+
truncated: boolean;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export type SearchEvent = SearchSessionHit | SearchDone;
|