| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- import { Command } from "commander";
- import { StateManager, formatLengthCount, readGenreProfile, resolveLengthCountingMode } from "@actalk/inkos-core";
- import { findProjectRoot, getLegacyMigrationHint, log, logError } from "../utils.js";
- export const statusCommand = new Command("status")
- .description("Show project status")
- .argument("[book-id]", "Book ID (optional, shows all if omitted)")
- .option("--chapters", "Show per-chapter status and issues")
- .option("--json", "Output JSON")
- .action(async (bookIdArg: string | undefined, opts) => {
- try {
- const root = findProjectRoot();
- const state = new StateManager(root);
- const allBookIds = await state.listBooks();
- const bookIds = bookIdArg ? [bookIdArg] : allBookIds;
- if (bookIdArg && !allBookIds.includes(bookIdArg)) {
- throw new Error(
- `Book "${bookIdArg}" not found. Available: ${allBookIds.join(", ") || "(none)"}`,
- );
- }
- const booksData = [];
- if (!opts.json) {
- log(`InkOS Project: ${root}`);
- log(`Books: ${allBookIds.length}`);
- log("");
- }
- for (const id of bookIds) {
- const book = await state.loadBookConfig(id);
- const index = await state.loadChapterIndex(id);
- const migrationHint = await getLegacyMigrationHint(root, id);
- const persistedChapterCount = await state.getPersistedChapterCount(id);
- const { profile: genreProfile } = await readGenreProfile(root, book.genre);
- const countingMode = resolveLengthCountingMode(book.language ?? genreProfile.language);
- const approved = index.filter((ch) => ch.status === "approved").length;
- const pending = index.filter(
- (ch) => ch.status === "ready-for-review",
- ).length;
- const failed = index.filter(
- (ch) => ch.status === "audit-failed",
- ).length;
- const degraded = index.filter(
- (ch) => ch.status === "state-degraded",
- ).length;
- const totalWords = index.reduce((sum, ch) => sum + ch.wordCount, 0);
- const avgWords = index.length > 0 ? Math.round(totalWords / index.length) : 0;
- booksData.push({
- id,
- title: book.title,
- status: book.status,
- genre: book.genre,
- platform: book.platform,
- chapters: persistedChapterCount,
- targetChapters: book.targetChapters,
- totalWords,
- avgWordsPerChapter: avgWords,
- approved,
- pending,
- failed,
- degraded,
- ...(migrationHint ? { migrationHint } : {}),
- ...(opts.chapters ? {
- chapterList: index.map((ch) => ({
- number: ch.number,
- title: ch.title,
- status: ch.status,
- wordCount: ch.wordCount,
- ...(ch.status === "audit-failed" || ch.status === "state-degraded"
- ? { issues: ch.auditIssues }
- : {}),
- })),
- } : {}),
- });
- if (!opts.json) {
- log(` ${book.title} (${id})`);
- log(` Status: ${book.status}`);
- log(` Platform: ${book.platform} | Genre: ${book.genre}`);
- log(` Chapters: ${persistedChapterCount} / ${book.targetChapters}`);
- log(` Words: ${totalWords.toLocaleString()} (avg ${avgWords}/ch)`);
- log(` Approved: ${approved} | Pending: ${pending} | Failed: ${failed} | Degraded: ${degraded}`);
- if (migrationHint) {
- log(` Migration: ${migrationHint}`);
- }
- if (opts.chapters && index.length > 0) {
- log("");
- for (const ch of index) {
- const icon = ch.status === "approved"
- ? "+"
- : ch.status === "audit-failed"
- ? "!"
- : ch.status === "state-degraded"
- ? "x"
- : "~";
- log(` [${icon}] Ch.${ch.number} "${ch.title}" | ${formatLengthCount(ch.wordCount, countingMode)} | ${ch.status}`);
- if ((ch.status === "audit-failed" || ch.status === "state-degraded") && ch.auditIssues.length > 0) {
- const criticals = ch.auditIssues.filter((i: string) => i.startsWith("[critical]"));
- const warnings = ch.auditIssues.filter((i: string) => i.startsWith("[warning]"));
- if (criticals.length > 0) {
- for (const issue of criticals) {
- log(` ${issue}`);
- }
- }
- if (warnings.length > 0) {
- if (ch.status === "state-degraded") {
- for (const issue of warnings) {
- log(` ${issue}`);
- }
- } else {
- log(` + ${warnings.length} warning(s)`);
- }
- }
- }
- }
- }
- log("");
- }
- }
- if (opts.json) {
- log(JSON.stringify({ project: root, books: booksData }, null, 2));
- }
- } catch (e) {
- if (opts.json) {
- log(JSON.stringify({ error: String(e) }));
- } else {
- logError(`Failed to get status: ${e}`);
- }
- process.exit(1);
- }
- });
|