@teddysc/claude-run 0.13.0 → 0.14.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/README.md CHANGED
@@ -36,6 +36,10 @@ See [spec.md](spec.md)
36
36
 
37
37
  ## Changelog
38
38
 
39
+ ### 0.14.0
40
+ - Add session deletion preview endpoint to see what files will be removed
41
+ - Enhance session deletion to remove related files and directories from disk
42
+
39
43
  ### 0.13.0
40
44
  - Display message count, duration, and JSONL size in conversation header
41
45
  - Sidebar shows compact message count, duration, and JSONL size per session
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { streamSSE } from "hono/streaming";
11
11
  import { serve } from "@hono/node-server";
12
12
 
13
13
  // api/storage.ts
14
- import { readdir, readFile, stat, open, unlink, writeFile } from "fs/promises";
14
+ import { readdir, readFile, stat, open, unlink, writeFile, rm } from "fs/promises";
15
15
  import { createReadStream } from "fs";
16
16
  import { join, basename } from "path";
17
17
  import { homedir } from "os";
@@ -480,10 +480,34 @@ async function getSessionsMetadata(ids) {
480
480
  }))
481
481
  );
482
482
  }
483
+ async function getSessionDeletionPaths(sessionId) {
484
+ const relatedPaths = /* @__PURE__ */ new Set();
485
+ const filePath = await findSessionFile(sessionId);
486
+ if (filePath) {
487
+ relatedPaths.add(filePath);
488
+ relatedPaths.add(filePath.replace(/\.jsonl$/, ""));
489
+ }
490
+ const relatedRoots = ["session-env", "todos", "file-history", "debug"];
491
+ for (const root of relatedRoots) {
492
+ const rootPath = join(claudeDir, root);
493
+ try {
494
+ const entries = await readdir(rootPath);
495
+ for (const entry of entries) {
496
+ if (!entry.includes(sessionId)) {
497
+ continue;
498
+ }
499
+ relatedPaths.add(join(rootPath, entry));
500
+ }
501
+ } catch {
502
+ }
503
+ }
504
+ return [...relatedPaths].sort();
505
+ }
483
506
  async function deleteSession(sessionId) {
484
507
  console.log(`[deleteSession] Attempting to delete: ${sessionId}`);
485
508
  const filePath = await findSessionFile(sessionId);
486
509
  let fileDeleted = false;
510
+ let relatedDeleted = false;
487
511
  if (filePath) {
488
512
  console.log(`[deleteSession] Found file: ${filePath}`);
489
513
  try {
@@ -502,6 +526,33 @@ async function deleteSession(sessionId) {
502
526
  } else {
503
527
  console.log(`[deleteSession] No file found for session: ${sessionId}`);
504
528
  }
529
+ const relatedPaths = /* @__PURE__ */ new Set();
530
+ if (filePath) {
531
+ relatedPaths.add(filePath.replace(/\.jsonl$/, ""));
532
+ }
533
+ const relatedRoots = ["session-env", "todos", "file-history", "debug"];
534
+ for (const root of relatedRoots) {
535
+ const rootPath = join(claudeDir, root);
536
+ try {
537
+ const entries = await readdir(rootPath);
538
+ for (const entry of entries) {
539
+ if (!entry.includes(sessionId)) {
540
+ continue;
541
+ }
542
+ relatedPaths.add(join(rootPath, entry));
543
+ }
544
+ } catch {
545
+ }
546
+ }
547
+ for (const relatedPath of relatedPaths) {
548
+ try {
549
+ await rm(relatedPath, { recursive: true, force: true });
550
+ console.log(`[deleteSession] Deleted related: ${relatedPath}`);
551
+ relatedDeleted = true;
552
+ } catch (err) {
553
+ console.error(`[deleteSession] Failed to delete related ${relatedPath}:`, err);
554
+ }
555
+ }
505
556
  fileIndex.delete(sessionId);
506
557
  summaryCache.delete(sessionId);
507
558
  sessionStatsCache.delete(sessionId);
@@ -539,7 +590,7 @@ async function deleteSession(sessionId) {
539
590
  } catch (err) {
540
591
  console.error(`[deleteSession] Failed to update history.jsonl for ${sessionId}:`, err);
541
592
  }
542
- return fileDeleted || historyChanged;
593
+ return fileDeleted || historyChanged || relatedDeleted;
543
594
  }
544
595
  async function getSessionFilePath(sessionId) {
545
596
  return findSessionFile(sessionId);
@@ -988,6 +1039,28 @@ function createServer(options) {
988
1039
  console.log(`[/api/sessions/delete] Failed:`, failed);
989
1040
  return c.json({ deleted, failed });
990
1041
  });
1042
+ app.post("/api/sessions/delete/preview", async (c) => {
1043
+ let body;
1044
+ try {
1045
+ body = await c.req.json();
1046
+ } catch {
1047
+ return c.json({ error: "Invalid JSON body" }, 400);
1048
+ }
1049
+ if (!body || !Array.isArray(body.ids)) {
1050
+ return c.json({ error: "Missing ids" }, 400);
1051
+ }
1052
+ const paths = /* @__PURE__ */ new Set();
1053
+ for (const id of body.ids) {
1054
+ if (typeof id !== "string" || !id) {
1055
+ continue;
1056
+ }
1057
+ const entries = await getSessionDeletionPaths(id);
1058
+ for (const entry of entries) {
1059
+ paths.add(entry);
1060
+ }
1061
+ }
1062
+ return c.json({ paths: [...paths].sort() });
1063
+ });
991
1064
  app.post("/api/sessions/rename", async (c) => {
992
1065
  let body;
993
1066
  try {