Просмотр исходного кода

feat: add version migration check to doctor command

Detects books using legacy pre-v0.6 format (no story/state/ directory)
and advises running write next to auto-migrate.

Addresses #95
Ma 7 месяцев назад
Родитель
Сommit
4c0ffed7ff

+ 30 - 0
packages/cli/src/commands/doctor.ts

@@ -117,6 +117,36 @@ export const doctorCommand = new Command("doctor")
       checks.push({ name: "Books", ok: true, detail: "0 books" });
     }
 
+    // 5b. Check version migration status
+    {
+      const { existsSync } = await import("node:fs");
+      const hasStructuredState = existsSync(join(root, "books"));
+      if (hasStructuredState) {
+        const { StateManager } = await import("@actalk/inkos-core");
+        const sm = new StateManager(root);
+        const bookIds = await sm.listBooks();
+        let legacyCount = 0;
+        for (const bid of bookIds) {
+          const stateDir = join(sm.bookDir(bid), "story", "state");
+          const hasNewState = existsSync(stateDir);
+          if (!hasNewState) legacyCount++;
+        }
+        if (legacyCount > 0) {
+          checks.push({
+            name: "Version Migration",
+            ok: false,
+            detail: `${legacyCount} book(s) using legacy format (pre-v0.6). Run 'inkos write next' on each to auto-migrate, or re-init with 'inkos init'.`,
+          });
+        } else if (bookIds.length > 0) {
+          checks.push({
+            name: "Version Migration",
+            ok: true,
+            detail: "All books use current format",
+          });
+        }
+      }
+    }
+
     // 6. API connectivity test
     try {
       const { createLLMClient, chatCompletion, LLMConfigSchema, isApiKeyOptionalForEndpoint } = await import("@actalk/inkos-core");

+ 5 - 5
packages/core/src/__tests__/state-manager.test.ts

@@ -430,11 +430,11 @@ describe("StateManager", () => {
       await writeFile(join(chapDir, "0001_ch1.md"), "# Chapter 1\nContent 1", "utf-8");
       await writeFile(join(chapDir, "0002_ch2.md"), "# Chapter 2\nContent 2", "utf-8");
       await writeFile(join(chapDir, "0003_ch3.md"), "# Chapter 3\nContent 3", "utf-8");
-      const fullIndex = [
-        { number: 1, title: "Ch1", status: "approved", wordCount: 100, createdAt: "", updatedAt: "", auditIssues: [] },
-        { number: 2, title: "Ch2", status: "approved", wordCount: 100, createdAt: "", updatedAt: "", auditIssues: [] },
-        { number: 3, title: "Ch3", status: "approved", wordCount: 100, createdAt: "", updatedAt: "", auditIssues: [] },
-      ];
+      const mkEntry = (n: number) => ({
+        number: n, title: `Ch${n}`, status: "approved" as const, wordCount: 100,
+        createdAt: "", updatedAt: "", auditIssues: [] as string[], lengthWarnings: [] as string[],
+      });
+      const fullIndex = [mkEntry(1), mkEntry(2), mkEntry(3)];
       await manager.saveChapterIndex(rwBookId, fullIndex);
 
       // Snapshot state at chapter 1 (before chapter 2)