plan.ts 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import { Command } from "commander";
  2. import { PipelineRunner } from "@actalk/inkos-core";
  3. import { buildPipelineConfig, findProjectRoot, loadConfig, log, logError, resolveBookId, resolveContext } from "../utils.js";
  4. export const planCommand = new Command("plan")
  5. .description("Plan chapter input artifacts");
  6. planCommand
  7. .command("chapter")
  8. .description("Generate chapter intent for the next chapter")
  9. .argument("[book-id]", "Book ID (auto-detected if only one book)")
  10. .option("--context <text>", "Chapter steering guidance")
  11. .option("--context-file <path>", "Read guidance from file")
  12. .option("--json", "Output JSON")
  13. .option("-q, --quiet", "Suppress console output")
  14. .action(async (bookIdArg: string | undefined, opts) => {
  15. try {
  16. const config = await loadConfig({ requireApiKey: false });
  17. const root = findProjectRoot();
  18. const bookId = await resolveBookId(bookIdArg, root);
  19. const context = await resolveContext(opts);
  20. const pipeline = new PipelineRunner(
  21. buildPipelineConfig(config, root, {
  22. externalContext: context,
  23. inputGovernanceMode: "v2",
  24. quiet: opts.quiet,
  25. }),
  26. );
  27. const result = await pipeline.planChapter(bookId, context);
  28. if (opts.json) {
  29. log(JSON.stringify(result, null, 2));
  30. } else {
  31. log(`Planned chapter ${result.chapterNumber} for "${bookId}"`);
  32. log(` Goal: ${result.goal}`);
  33. log(` Intent: ${result.intentPath}`);
  34. if (result.conflicts.length > 0) {
  35. log(" Conflicts:");
  36. for (const conflict of result.conflicts) {
  37. log(` - ${conflict}`);
  38. }
  39. }
  40. }
  41. } catch (e) {
  42. if (opts.json) {
  43. log(JSON.stringify({ error: String(e) }));
  44. } else {
  45. logError(`Failed to plan chapter: ${e}`);
  46. }
  47. process.exit(1);
  48. }
  49. });