diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index 025d9d98..a42ba8d2 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/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"); diff --git a/packages/core/src/__tests__/state-manager.test.ts b/packages/core/src/__tests__/state-manager.test.ts index cc150750..4a50ca87 100644 --- a/packages/core/src/__tests__/state-manager.test.ts +++ b/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)