Procházet zdrojové kódy

fix: init/genre/doctor UX improvements

init: error if inkos.json already exists (no silent overwrite).
genre show: error if genre not found instead of silent fallback.
doctor: fallback to global .env for API test when outside a project.
Ma před 7 měsíci
rodič
revize
9e734bea5d

+ 38 - 12
packages/cli/src/commands/doctor.ts

@@ -78,21 +78,47 @@ export const doctorCommand = new Command("doctor")
 
     // 6. API connectivity test
     try {
-      const { createLLMClient, chatCompletion } = await import("@actalk/inkos-core");
+      const { createLLMClient, chatCompletion, LLMConfigSchema } = await import("@actalk/inkos-core");
       const { loadConfig } = await import("../utils.js");
-      const config = await loadConfig();
-      const client = createLLMClient(config.llm);
 
-      log("\n  [..] Testing API connectivity...");
-      const response = await chatCompletion(client, config.llm.model, [
-        { role: "user", content: "Say OK" },
-      ], { maxTokens: 16 });
+      let llmConfig;
+      try {
+        const config = await loadConfig();
+        llmConfig = config.llm;
+      } catch {
+        // No project config — try building from global env
+        const { config: loadDotenv } = await import("dotenv");
+        loadDotenv({ path: GLOBAL_ENV_PATH });
+        const env = process.env;
+        if (env.INKOS_LLM_API_KEY && env.INKOS_LLM_BASE_URL && env.INKOS_LLM_MODEL) {
+          llmConfig = LLMConfigSchema.parse({
+            provider: env.INKOS_LLM_PROVIDER ?? "custom",
+            baseUrl: env.INKOS_LLM_BASE_URL,
+            apiKey: env.INKOS_LLM_API_KEY,
+            model: env.INKOS_LLM_MODEL,
+          });
+        }
+      }
 
-      checks.push({
-        name: "API Connectivity",
-        ok: true,
-        detail: `OK (model: ${config.llm.model}, tokens: ${response.usage.totalTokens})`,
-      });
+      if (!llmConfig) {
+        checks.push({
+          name: "API Connectivity",
+          ok: false,
+          detail: "No LLM config available (no project config or global .env)",
+        });
+      } else {
+        const client = createLLMClient(llmConfig);
+        log("\n  [..] Testing API connectivity...");
+        const response = await chatCompletion(client, llmConfig.model, [
+          { role: "user", content: "Say OK" },
+        ], { maxTokens: 16 });
+
+        checks.push({
+          name: "API Connectivity",
+          ok: true,
+          detail: `OK (model: ${llmConfig.model}, tokens: ${response.usage.totalTokens})`,
+        });
+      }
     } catch (e) {
       checks.push({
         name: "API Connectivity",

+ 6 - 0
packages/cli/src/commands/genre.ts

@@ -39,6 +39,12 @@ genreCommand
   .action(async (id: string) => {
     try {
       const root = findProjectRoot();
+      const genres = await listAvailableGenres(root);
+      const exactMatch = genres.some(g => g.id === id);
+      if (!exactMatch) {
+        logError(`Genre "${id}" not found. Available: ${genres.map(g => g.id).join(", ")}`);
+        process.exit(1);
+      }
       const { profile, body } = await readGenreProfile(root, id);
 
       log(`Genre: ${profile.name} (${profile.id})\n`);

+ 12 - 1
packages/cli/src/commands/init.ts

@@ -1,5 +1,5 @@
 import { Command } from "commander";
-import { readFile, writeFile, mkdir } from "node:fs/promises";
+import { access, readFile, writeFile, mkdir } from "node:fs/promises";
 import { join, basename } from "node:path";
 import { log, logError, GLOBAL_ENV_PATH } from "../utils.js";
 
@@ -21,6 +21,17 @@ export const initCommand = new Command("init")
 
     try {
       await mkdir(projectDir, { recursive: true });
+
+      // Check if inkos.json already exists
+      const configPath = join(projectDir, "inkos.json");
+      try {
+        await access(configPath);
+        throw new Error(`inkos.json already exists in ${projectDir}. Use a different directory or delete the existing project.`);
+      } catch (e) {
+        if (e instanceof Error && e.message.includes("already exists")) throw e;
+        // File doesn't exist, good
+      }
+
       await mkdir(join(projectDir, "books"), { recursive: true });
       await mkdir(join(projectDir, "radar"), { recursive: true });