@zzusp/ccsm 1.0.1 → 1.0.3
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 -236
- package/bin/cli.mjs +52 -52
- package/dist/assets/{DiskUsage-CKhggLs5.js → DiskUsage-BY6XwffG.js} +2 -2
- package/dist/assets/DiskUsage-BY6XwffG.js.map +1 -0
- package/dist/assets/{ImportPage-wge4VhZ-.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-Q4XX40j_.js → ProjectMemory-CcE3KbUK.js} +2 -2
- package/dist/assets/ProjectMemory-CcE3KbUK.js.map +1 -0
- 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/index.html +26 -26
- package/package.json +81 -83
- package/server/index.ts +130 -130
- package/server/lib/active-sessions.test.ts +119 -119
- package/server/lib/active-sessions.ts +95 -95
- package/server/lib/bundle.test.ts +182 -182
- package/server/lib/bundle.ts +86 -86
- package/server/lib/claude-paths.test.ts +126 -126
- package/server/lib/claude-paths.ts +43 -43
- package/server/lib/cleanup-suggestions.ts +131 -131
- package/server/lib/constants.ts +8 -8
- package/server/lib/delete-project.ts +100 -100
- package/server/lib/delete.test.ts +244 -244
- package/server/lib/delete.ts +192 -192
- package/server/lib/disk-usage.ts +81 -81
- 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 -337
- 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 -280
- package/server/lib/modified-files.ts +228 -228
- package/server/lib/open-folder.ts +47 -47
- package/server/lib/parse-jsonl.ts +160 -139
- package/server/lib/port.ts +23 -23
- package/server/lib/safe-id.test.ts +41 -41
- package/server/lib/safe-id.ts +6 -6
- package/server/lib/safe-remove.test.ts +73 -73
- package/server/lib/safe-remove.ts +25 -25
- package/server/lib/scan.ts +289 -286
- 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 -67
- package/server/lib/version.test.ts +39 -39
- package/server/lib/version.ts +117 -117
- package/server/routes/disk-cleanup.ts +54 -54
- 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 -130
- package/server/routes/version.ts +34 -34
- package/server/types.ts +1 -1
- package/shared/constants.ts +7 -7
- package/shared/types.ts +513 -511
- package/dist/assets/DiskUsage-CKhggLs5.js.map +0 -1
- package/dist/assets/ImportPage-wge4VhZ-.js.map +0 -1
- package/dist/assets/ProjectMemory-Q4XX40j_.js.map +0 -1
- package/dist/assets/index-7aMrnHJG.js +0 -7
- package/dist/assets/index-7aMrnHJG.js.map +0 -1
- package/dist/assets/index-BOeI_J4B.css +0 -1
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { buildActiveSessionMap } from './active-sessions.ts';
|
|
4
|
-
import { isUnderClaudeRoot, PATHS } from './claude-paths.ts';
|
|
5
|
-
import { RECENT_ACTIVITY_WINDOW_MS } from './constants.ts';
|
|
6
|
-
import { deleteSessions } from './delete.ts';
|
|
7
|
-
import { isSafeId } from './safe-id.ts';
|
|
8
|
-
import type { DeleteProjectResult, SkippedItem } from '../types.ts';
|
|
9
|
-
|
|
10
|
-
const JSONL_EXT = '.jsonl';
|
|
11
|
-
|
|
12
|
-
export async function deleteProject(projectId: string): Promise<DeleteProjectResult> {
|
|
13
|
-
if (!isSafeId(projectId)) {
|
|
14
|
-
return {
|
|
15
|
-
deleted: [],
|
|
16
|
-
skipped: [{ projectId, sessionId: '', reason: 'invalid project id' }],
|
|
17
|
-
historyLinesRemoved: 0,
|
|
18
|
-
projectDirRemoved: false,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const projectDir = path.join(PATHS.projects, projectId);
|
|
23
|
-
if (!isUnderClaudeRoot(projectDir)) {
|
|
24
|
-
return {
|
|
25
|
-
deleted: [],
|
|
26
|
-
skipped: [{ projectId, sessionId: '', reason: 'path escapes ~/.claude' }],
|
|
27
|
-
historyLinesRemoved: 0,
|
|
28
|
-
projectDirRemoved: false,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
if (!fs.existsSync(projectDir)) {
|
|
32
|
-
return {
|
|
33
|
-
deleted: [],
|
|
34
|
-
skipped: [{ projectId, sessionId: '', reason: 'project directory does not exist' }],
|
|
35
|
-
historyLinesRemoved: 0,
|
|
36
|
-
projectDirRemoved: false,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const sessionIds: string[] = [];
|
|
41
|
-
for (const ent of fs.readdirSync(projectDir, { withFileTypes: true })) {
|
|
42
|
-
if (ent.isFile() && ent.name.endsWith(JSONL_EXT)) {
|
|
43
|
-
sessionIds.push(ent.name.slice(0, -JSONL_EXT.length));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// All-or-nothing precheck: refuse to touch any session if even one is live or
|
|
48
|
-
// recently active. Confirmed by the user — partial deletes leave the project
|
|
49
|
-
// half-cleared, which is more confusing than a clean "try again later".
|
|
50
|
-
const liveMap = buildActiveSessionMap();
|
|
51
|
-
const blockers: SkippedItem[] = [];
|
|
52
|
-
for (const sid of sessionIds) {
|
|
53
|
-
if (liveMap.has(sid)) {
|
|
54
|
-
blockers.push({
|
|
55
|
-
projectId,
|
|
56
|
-
sessionId: sid,
|
|
57
|
-
reason: `live PID ${liveMap.get(sid)} owns this session`,
|
|
58
|
-
});
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const jsonlPath = path.join(projectDir, `${sid}${JSONL_EXT}`);
|
|
62
|
-
try {
|
|
63
|
-
if (Date.now() - fs.statSync(jsonlPath).mtimeMs < RECENT_ACTIVITY_WINDOW_MS) {
|
|
64
|
-
blockers.push({
|
|
65
|
-
projectId,
|
|
66
|
-
sessionId: sid,
|
|
67
|
-
reason: 'jsonl modified within the last 5 minutes — could still be in use',
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
/* missing file is fine — deleteSessions will skip it */
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (blockers.length > 0) {
|
|
76
|
-
return {
|
|
77
|
-
deleted: [],
|
|
78
|
-
skipped: blockers,
|
|
79
|
-
historyLinesRemoved: 0,
|
|
80
|
-
projectDirRemoved: false,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const result = await deleteSessions(
|
|
85
|
-
sessionIds.map((sessionId) => ({ projectId, sessionId })),
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
let projectDirRemoved = false;
|
|
89
|
-
if (result.skipped.length === 0) {
|
|
90
|
-
try {
|
|
91
|
-
// Recursive remove also catches any orphan subdirs whose .jsonl was missing.
|
|
92
|
-
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
93
|
-
projectDirRemoved = true;
|
|
94
|
-
} catch {
|
|
95
|
-
/* leave dir for manual cleanup */
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { ...result, projectDirRemoved };
|
|
100
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { buildActiveSessionMap } from './active-sessions.ts';
|
|
4
|
+
import { isUnderClaudeRoot, PATHS } from './claude-paths.ts';
|
|
5
|
+
import { RECENT_ACTIVITY_WINDOW_MS } from './constants.ts';
|
|
6
|
+
import { deleteSessions } from './delete.ts';
|
|
7
|
+
import { isSafeId } from './safe-id.ts';
|
|
8
|
+
import type { DeleteProjectResult, SkippedItem } from '../types.ts';
|
|
9
|
+
|
|
10
|
+
const JSONL_EXT = '.jsonl';
|
|
11
|
+
|
|
12
|
+
export async function deleteProject(projectId: string): Promise<DeleteProjectResult> {
|
|
13
|
+
if (!isSafeId(projectId)) {
|
|
14
|
+
return {
|
|
15
|
+
deleted: [],
|
|
16
|
+
skipped: [{ projectId, sessionId: '', reason: 'invalid project id' }],
|
|
17
|
+
historyLinesRemoved: 0,
|
|
18
|
+
projectDirRemoved: false,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const projectDir = path.join(PATHS.projects, projectId);
|
|
23
|
+
if (!isUnderClaudeRoot(projectDir)) {
|
|
24
|
+
return {
|
|
25
|
+
deleted: [],
|
|
26
|
+
skipped: [{ projectId, sessionId: '', reason: 'path escapes ~/.claude' }],
|
|
27
|
+
historyLinesRemoved: 0,
|
|
28
|
+
projectDirRemoved: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (!fs.existsSync(projectDir)) {
|
|
32
|
+
return {
|
|
33
|
+
deleted: [],
|
|
34
|
+
skipped: [{ projectId, sessionId: '', reason: 'project directory does not exist' }],
|
|
35
|
+
historyLinesRemoved: 0,
|
|
36
|
+
projectDirRemoved: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const sessionIds: string[] = [];
|
|
41
|
+
for (const ent of fs.readdirSync(projectDir, { withFileTypes: true })) {
|
|
42
|
+
if (ent.isFile() && ent.name.endsWith(JSONL_EXT)) {
|
|
43
|
+
sessionIds.push(ent.name.slice(0, -JSONL_EXT.length));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// All-or-nothing precheck: refuse to touch any session if even one is live or
|
|
48
|
+
// recently active. Confirmed by the user — partial deletes leave the project
|
|
49
|
+
// half-cleared, which is more confusing than a clean "try again later".
|
|
50
|
+
const liveMap = buildActiveSessionMap();
|
|
51
|
+
const blockers: SkippedItem[] = [];
|
|
52
|
+
for (const sid of sessionIds) {
|
|
53
|
+
if (liveMap.has(sid)) {
|
|
54
|
+
blockers.push({
|
|
55
|
+
projectId,
|
|
56
|
+
sessionId: sid,
|
|
57
|
+
reason: `live PID ${liveMap.get(sid)} owns this session`,
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const jsonlPath = path.join(projectDir, `${sid}${JSONL_EXT}`);
|
|
62
|
+
try {
|
|
63
|
+
if (Date.now() - fs.statSync(jsonlPath).mtimeMs < RECENT_ACTIVITY_WINDOW_MS) {
|
|
64
|
+
blockers.push({
|
|
65
|
+
projectId,
|
|
66
|
+
sessionId: sid,
|
|
67
|
+
reason: 'jsonl modified within the last 5 minutes — could still be in use',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
/* missing file is fine — deleteSessions will skip it */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (blockers.length > 0) {
|
|
76
|
+
return {
|
|
77
|
+
deleted: [],
|
|
78
|
+
skipped: blockers,
|
|
79
|
+
historyLinesRemoved: 0,
|
|
80
|
+
projectDirRemoved: false,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result = await deleteSessions(
|
|
85
|
+
sessionIds.map((sessionId) => ({ projectId, sessionId })),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
let projectDirRemoved = false;
|
|
89
|
+
if (result.skipped.length === 0) {
|
|
90
|
+
try {
|
|
91
|
+
// Recursive remove also catches any orphan subdirs whose .jsonl was missing.
|
|
92
|
+
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
93
|
+
projectDirRemoved = true;
|
|
94
|
+
} catch {
|
|
95
|
+
/* leave dir for manual cleanup */
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { ...result, projectDirRemoved };
|
|
100
|
+
}
|