Browse Source

feat(cli): add book delete command (#56)

Ma 7 months ago
parent
commit
e5f75284c8
1 changed files with 54 additions and 1 deletions
  1. 54 1
      packages/cli/src/commands/book.ts

+ 54 - 1
packages/cli/src/commands/book.ts

@@ -1,5 +1,6 @@
 import { Command } from "commander";
-import { access, readFile } from "node:fs/promises";
+import { access, readFile, rm } from "node:fs/promises";
+import { createInterface } from "node:readline";
 import { join, resolve } from "node:path";
 import { PipelineRunner, StateManager, type BookConfig } from "@actalk/inkos-core";
 import { loadConfig, buildPipelineConfig, findProjectRoot, resolveBookId, log, logError } from "../utils.js";
@@ -204,3 +205,55 @@ bookCommand
       process.exit(1);
     }
   });
+
+bookCommand
+  .command("delete")
+  .description("Delete a book and all its chapters, truth files, and snapshots")
+  .argument("<book-id>", "Book ID to delete")
+  .option("--force", "Skip confirmation prompt")
+  .option("--json", "Output JSON")
+  .action(async (bookId: string, opts) => {
+    try {
+      const root = findProjectRoot();
+      const state = new StateManager(root);
+
+      const allBooks = await state.listBooks();
+      if (!allBooks.includes(bookId)) {
+        throw new Error(`Book "${bookId}" not found. Available: ${allBooks.join(", ") || "(none)"}`);
+      }
+
+      const book = await state.loadBookConfig(bookId);
+      const index = await state.loadChapterIndex(bookId);
+
+      if (!opts.force) {
+        const rl = createInterface({ input: process.stdin, output: process.stdout });
+        const answer = await new Promise<string>((resolve) => {
+          rl.question(
+            `Delete "${book.title}" (${bookId})? This will remove ${index.length} chapter(s) and all data. (y/N) `,
+            resolve,
+          );
+        });
+        rl.close();
+        if (answer.toLowerCase() !== "y") {
+          log("Cancelled.");
+          return;
+        }
+      }
+
+      const bookDir = join(root, "books", bookId);
+      await rm(bookDir, { recursive: true, force: true });
+
+      if (opts.json) {
+        log(JSON.stringify({ deleted: bookId, chapters: index.length }));
+      } else {
+        log(`Deleted "${book.title}" (${bookId}): ${index.length} chapter(s) removed.`);
+      }
+    } catch (e) {
+      if (opts.json) {
+        log(JSON.stringify({ error: String(e) }));
+      } else {
+        logError(`Failed to delete book: ${e}`);
+      }
+      process.exit(1);
+    }
+  });