Przeglądaj źródła

fix: variadic args for review approve/reject, fix double error output

- review approve/reject use variadic args to support both
  "approve 3" (auto-detect book) and "approve my-book 3"
- write rewrite uses same pattern for "rewrite 3" / "rewrite my-book 3"
- Replace process.exit(1) inside try blocks with throw to prevent
  double error output (caught by both inline handler and catch block)
majx_mac 8 miesięcy temu
rodzic
commit
6e999d5846

+ 1 - 7
packages/cli/src/commands/export.ts

@@ -27,13 +27,7 @@ export const exportCommand = new Command("export")
         : index;
         : index;
 
 
       if (chapters.length === 0) {
       if (chapters.length === 0) {
-        const msg = "No chapters to export.";
-        if (opts.json) {
-          log(JSON.stringify({ error: msg }));
-        } else {
-          logError(msg);
-        }
-        process.exit(1);
+        throw new Error("No chapters to export.");
       }
       }
 
 
       const parts: string[] = [];
       const parts: string[] = [];

+ 36 - 42
packages/cli/src/commands/review.ts

@@ -78,37 +78,46 @@ reviewCommand
     }
     }
   });
   });
 
 
+/**
+ * Parse "[book-id] <chapter>" style arguments from variadic args.
+ * Supports: "3" (auto-detect book) or "my-book 3"
+ */
+function parseBookAndChapter(
+  args: ReadonlyArray<string>,
+): { readonly bookIdArg: string | undefined; readonly chapterNum: number } {
+  if (args.length === 1) {
+    const num = parseInt(args[0]!, 10);
+    if (isNaN(num)) {
+      throw new Error(`Expected chapter number, got "${args[0]}"`);
+    }
+    return { bookIdArg: undefined, chapterNum: num };
+  }
+  if (args.length === 2) {
+    const num = parseInt(args[1]!, 10);
+    if (isNaN(num)) {
+      throw new Error(`Expected chapter number as second argument, got "${args[1]}"`);
+    }
+    return { bookIdArg: args[0], chapterNum: num };
+  }
+  throw new Error("Usage: inkos review approve [book-id] <chapter>");
+}
+
 reviewCommand
 reviewCommand
   .command("approve")
   .command("approve")
-  .description("Approve a chapter")
-  .argument("[book-id]", "Book ID (auto-detected if only one book)")
-  .argument("<chapter>", "Chapter number")
+  .description("Approve a chapter: approve [book-id] <chapter>")
+  .argument("<args...>", "Book ID (optional) and chapter number")
   .option("--json", "Output JSON")
   .option("--json", "Output JSON")
-  .action(async (bookIdArg: string, chapterStr: string, opts) => {
+  .action(async (args: ReadonlyArray<string>, opts) => {
     try {
     try {
       const root = findProjectRoot();
       const root = findProjectRoot();
-
-      let bookId: string;
-      let chapterNum: number;
-      if (!chapterStr || isNaN(parseInt(chapterStr, 10))) {
-        bookId = await resolveBookId(undefined, root);
-        chapterNum = parseInt(bookIdArg, 10);
-      } else {
-        bookId = await resolveBookId(bookIdArg, root);
-        chapterNum = parseInt(chapterStr, 10);
-      }
+      const { bookIdArg, chapterNum } = parseBookAndChapter(args);
+      const bookId = await resolveBookId(bookIdArg, root);
 
 
       const state = new StateManager(root);
       const state = new StateManager(root);
       const index = [...(await state.loadChapterIndex(bookId))];
       const index = [...(await state.loadChapterIndex(bookId))];
       const idx = index.findIndex((ch) => ch.number === chapterNum);
       const idx = index.findIndex((ch) => ch.number === chapterNum);
       if (idx === -1) {
       if (idx === -1) {
-        const msg = `Chapter ${chapterNum} not found in "${bookId}"`;
-        if (opts.json) {
-          log(JSON.stringify({ error: msg }));
-        } else {
-          logError(msg);
-        }
-        process.exit(1);
+        throw new Error(`Chapter ${chapterNum} not found in "${bookId}"`);
       }
       }
 
 
       index[idx] = {
       index[idx] = {
@@ -175,36 +184,21 @@ reviewCommand
 
 
 reviewCommand
 reviewCommand
   .command("reject")
   .command("reject")
-  .description("Reject a chapter")
-  .argument("[book-id]", "Book ID (auto-detected if only one book)")
-  .argument("<chapter>", "Chapter number")
+  .description("Reject a chapter: reject [book-id] <chapter>")
+  .argument("<args...>", "Book ID (optional) and chapter number")
   .option("--reason <reason>", "Rejection reason")
   .option("--reason <reason>", "Rejection reason")
   .option("--json", "Output JSON")
   .option("--json", "Output JSON")
-  .action(async (bookIdArg: string, chapterStr: string, opts) => {
+  .action(async (args: ReadonlyArray<string>, opts) => {
     try {
     try {
       const root = findProjectRoot();
       const root = findProjectRoot();
-
-      let bookId: string;
-      let chapterNum: number;
-      if (!chapterStr || isNaN(parseInt(chapterStr, 10))) {
-        bookId = await resolveBookId(undefined, root);
-        chapterNum = parseInt(bookIdArg, 10);
-      } else {
-        bookId = await resolveBookId(bookIdArg, root);
-        chapterNum = parseInt(chapterStr, 10);
-      }
+      const { bookIdArg, chapterNum } = parseBookAndChapter(args);
+      const bookId = await resolveBookId(bookIdArg, root);
 
 
       const state = new StateManager(root);
       const state = new StateManager(root);
       const index = [...(await state.loadChapterIndex(bookId))];
       const index = [...(await state.loadChapterIndex(bookId))];
       const idx = index.findIndex((ch) => ch.number === chapterNum);
       const idx = index.findIndex((ch) => ch.number === chapterNum);
       if (idx === -1) {
       if (idx === -1) {
-        const msg = `Chapter ${chapterNum} not found in "${bookId}"`;
-        if (opts.json) {
-          log(JSON.stringify({ error: msg }));
-        } else {
-          logError(msg);
-        }
-        process.exit(1);
+        throw new Error(`Chapter ${chapterNum} not found in "${bookId}"`);
       }
       }
 
 
       index[idx] = {
       index[idx] = {

+ 3 - 7
packages/cli/src/commands/status.ts

@@ -15,13 +15,9 @@ export const statusCommand = new Command("status")
       const bookIds = bookIdArg ? [bookIdArg] : allBookIds;
       const bookIds = bookIdArg ? [bookIdArg] : allBookIds;
 
 
       if (bookIdArg && !allBookIds.includes(bookIdArg)) {
       if (bookIdArg && !allBookIds.includes(bookIdArg)) {
-        const msg = `Book "${bookIdArg}" not found. Available: ${allBookIds.join(", ") || "(none)"}`;
-        if (opts.json) {
-          log(JSON.stringify({ error: msg }));
-        } else {
-          logError(msg);
-        }
-        process.exit(1);
+        throw new Error(
+          `Book "${bookIdArg}" not found. Available: ${allBookIds.join(", ") || "(none)"}`,
+        );
       }
       }
 
 
       const booksData = [];
       const booksData = [];

+ 12 - 11
packages/cli/src/commands/write.ts

@@ -78,28 +78,29 @@ writeCommand
 
 
 writeCommand
 writeCommand
   .command("rewrite")
   .command("rewrite")
-  .description("Re-generate a specific chapter (removes it and writes fresh)")
-  .argument("[book-id]", "Book ID (auto-detected if only one book)")
-  .argument("<chapter>", "Chapter number to rewrite")
+  .description("Re-generate a specific chapter: rewrite [book-id] <chapter>")
+  .argument("<args...>", "Book ID (optional) and chapter number")
   .option("--force", "Skip confirmation prompt")
   .option("--force", "Skip confirmation prompt")
   .option("--json", "Output JSON")
   .option("--json", "Output JSON")
-  .action(async (bookIdArg: string, chapterStr: string, opts) => {
+  .action(async (args: ReadonlyArray<string>, opts) => {
     try {
     try {
       const config = await loadConfig();
       const config = await loadConfig();
       const client = createClient(config);
       const client = createClient(config);
       const root = findProjectRoot();
       const root = findProjectRoot();
-      const chapterNum = parseInt(chapterStr, 10);
 
 
-      // If only one argument, it's the chapter number (book-id auto-detected)
       let bookId: string;
       let bookId: string;
-      if (!chapterStr || isNaN(chapterNum)) {
-        // bookIdArg is actually the chapter number
+      let chapter: number;
+      if (args.length === 1) {
+        chapter = parseInt(args[0]!, 10);
+        if (isNaN(chapter)) throw new Error(`Expected chapter number, got "${args[0]}"`);
         bookId = await resolveBookId(undefined, root);
         bookId = await resolveBookId(undefined, root);
-        chapterStr = bookIdArg;
+      } else if (args.length === 2) {
+        chapter = parseInt(args[1]!, 10);
+        if (isNaN(chapter)) throw new Error(`Expected chapter number, got "${args[1]}"`);
+        bookId = await resolveBookId(args[0], root);
       } else {
       } else {
-        bookId = await resolveBookId(bookIdArg, root);
+        throw new Error("Usage: inkos write rewrite [book-id] <chapter>");
       }
       }
-      const chapter = parseInt(chapterStr, 10);
 
 
       if (!opts.force) {
       if (!opts.force) {
         const rl = createInterface({ input: process.stdin, output: process.stdout });
         const rl = createInterface({ input: process.stdin, output: process.stdout });