@kienmatu/inkos 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/LICENSE +661 -0
  2. package/dist/book-backup.d.ts +33 -0
  3. package/dist/book-backup.d.ts.map +1 -0
  4. package/dist/book-backup.js +74 -0
  5. package/dist/book-backup.js.map +1 -0
  6. package/dist/commands/agent.d.ts +3 -0
  7. package/dist/commands/agent.d.ts.map +1 -0
  8. package/dist/commands/agent.js +71 -0
  9. package/dist/commands/agent.js.map +1 -0
  10. package/dist/commands/analytics.d.ts +3 -0
  11. package/dist/commands/analytics.d.ts.map +1 -0
  12. package/dist/commands/analytics.js +74 -0
  13. package/dist/commands/analytics.js.map +1 -0
  14. package/dist/commands/audit.d.ts +3 -0
  15. package/dist/commands/audit.d.ts.map +1 -0
  16. package/dist/commands/audit.js +81 -0
  17. package/dist/commands/audit.js.map +1 -0
  18. package/dist/commands/auto.d.ts +3 -0
  19. package/dist/commands/auto.d.ts.map +1 -0
  20. package/dist/commands/auto.js +136 -0
  21. package/dist/commands/auto.js.map +1 -0
  22. package/dist/commands/book.d.ts +3 -0
  23. package/dist/commands/book.d.ts.map +1 -0
  24. package/dist/commands/book.js +324 -0
  25. package/dist/commands/book.js.map +1 -0
  26. package/dist/commands/chapter.d.ts +3 -0
  27. package/dist/commands/chapter.d.ts.map +1 -0
  28. package/dist/commands/chapter.js +103 -0
  29. package/dist/commands/chapter.js.map +1 -0
  30. package/dist/commands/compose.d.ts +3 -0
  31. package/dist/commands/compose.d.ts.map +1 -0
  32. package/dist/commands/compose.js +46 -0
  33. package/dist/commands/compose.js.map +1 -0
  34. package/dist/commands/config.d.ts +3 -0
  35. package/dist/commands/config.d.ts.map +1 -0
  36. package/dist/commands/config.js +321 -0
  37. package/dist/commands/config.js.map +1 -0
  38. package/dist/commands/consolidate.d.ts +3 -0
  39. package/dist/commands/consolidate.d.ts.map +1 -0
  40. package/dist/commands/consolidate.js +50 -0
  41. package/dist/commands/consolidate.js.map +1 -0
  42. package/dist/commands/daemon.d.ts +4 -0
  43. package/dist/commands/daemon.d.ts.map +1 -0
  44. package/dist/commands/daemon.js +118 -0
  45. package/dist/commands/daemon.js.map +1 -0
  46. package/dist/commands/detect.d.ts +3 -0
  47. package/dist/commands/detect.d.ts.map +1 -0
  48. package/dist/commands/detect.js +104 -0
  49. package/dist/commands/detect.js.map +1 -0
  50. package/dist/commands/doctor.d.ts +8 -0
  51. package/dist/commands/doctor.d.ts.map +1 -0
  52. package/dist/commands/doctor.js +356 -0
  53. package/dist/commands/doctor.js.map +1 -0
  54. package/dist/commands/draft.d.ts +3 -0
  55. package/dist/commands/draft.d.ts.map +1 -0
  56. package/dist/commands/draft.js +42 -0
  57. package/dist/commands/draft.js.map +1 -0
  58. package/dist/commands/eval.d.ts +3 -0
  59. package/dist/commands/eval.d.ts.map +1 -0
  60. package/dist/commands/eval.js +57 -0
  61. package/dist/commands/eval.js.map +1 -0
  62. package/dist/commands/export.d.ts +3 -0
  63. package/dist/commands/export.d.ts.map +1 -0
  64. package/dist/commands/export.js +46 -0
  65. package/dist/commands/export.js.map +1 -0
  66. package/dist/commands/fanfic.d.ts +3 -0
  67. package/dist/commands/fanfic.d.ts.map +1 -0
  68. package/dist/commands/fanfic.js +177 -0
  69. package/dist/commands/fanfic.js.map +1 -0
  70. package/dist/commands/forecast.d.ts +12 -0
  71. package/dist/commands/forecast.d.ts.map +1 -0
  72. package/dist/commands/forecast.js +156 -0
  73. package/dist/commands/forecast.js.map +1 -0
  74. package/dist/commands/genre.d.ts +11 -0
  75. package/dist/commands/genre.d.ts.map +1 -0
  76. package/dist/commands/genre.js +186 -0
  77. package/dist/commands/genre.js.map +1 -0
  78. package/dist/commands/import.d.ts +3 -0
  79. package/dist/commands/import.d.ts.map +1 -0
  80. package/dist/commands/import.js +110 -0
  81. package/dist/commands/import.js.map +1 -0
  82. package/dist/commands/init.d.ts +3 -0
  83. package/dist/commands/init.d.ts.map +1 -0
  84. package/dist/commands/init.js +63 -0
  85. package/dist/commands/init.js.map +1 -0
  86. package/dist/commands/interact.d.ts +6 -0
  87. package/dist/commands/interact.d.ts.map +1 -0
  88. package/dist/commands/interact.js +95 -0
  89. package/dist/commands/interact.js.map +1 -0
  90. package/dist/commands/plan.d.ts +3 -0
  91. package/dist/commands/plan.d.ts.map +1 -0
  92. package/dist/commands/plan.js +50 -0
  93. package/dist/commands/plan.js.map +1 -0
  94. package/dist/commands/radar.d.ts +3 -0
  95. package/dist/commands/radar.d.ts.map +1 -0
  96. package/dist/commands/radar.js +52 -0
  97. package/dist/commands/radar.js.map +1 -0
  98. package/dist/commands/review.d.ts +3 -0
  99. package/dist/commands/review.d.ts.map +1 -0
  100. package/dist/commands/review.js +229 -0
  101. package/dist/commands/review.js.map +1 -0
  102. package/dist/commands/revise.d.ts +3 -0
  103. package/dist/commands/revise.d.ts.map +1 -0
  104. package/dist/commands/revise.js +91 -0
  105. package/dist/commands/revise.js.map +1 -0
  106. package/dist/commands/short-fiction.d.ts +4 -0
  107. package/dist/commands/short-fiction.d.ts.map +1 -0
  108. package/dist/commands/short-fiction.js +220 -0
  109. package/dist/commands/short-fiction.js.map +1 -0
  110. package/dist/commands/status.d.ts +3 -0
  111. package/dist/commands/status.d.ts.map +1 -0
  112. package/dist/commands/status.js +123 -0
  113. package/dist/commands/status.js.map +1 -0
  114. package/dist/commands/studio.d.ts +21 -0
  115. package/dist/commands/studio.d.ts.map +1 -0
  116. package/dist/commands/studio.js +151 -0
  117. package/dist/commands/studio.js.map +1 -0
  118. package/dist/commands/style.d.ts +3 -0
  119. package/dist/commands/style.d.ts.map +1 -0
  120. package/dist/commands/style.js +95 -0
  121. package/dist/commands/style.js.map +1 -0
  122. package/dist/commands/translate.d.ts +3 -0
  123. package/dist/commands/translate.d.ts.map +1 -0
  124. package/dist/commands/translate.js +111 -0
  125. package/dist/commands/translate.js.map +1 -0
  126. package/dist/commands/tui.d.ts +6 -0
  127. package/dist/commands/tui.d.ts.map +1 -0
  128. package/dist/commands/tui.js +14 -0
  129. package/dist/commands/tui.js.map +1 -0
  130. package/dist/commands/update.d.ts +3 -0
  131. package/dist/commands/update.d.ts.map +1 -0
  132. package/dist/commands/update.js +40 -0
  133. package/dist/commands/update.js.map +1 -0
  134. package/dist/commands/write.d.ts +3 -0
  135. package/dist/commands/write.d.ts.map +1 -0
  136. package/dist/commands/write.js +371 -0
  137. package/dist/commands/write.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +4 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/localization.d.ts +103 -0
  143. package/dist/localization.d.ts.map +1 -0
  144. package/dist/localization.js +377 -0
  145. package/dist/localization.js.map +1 -0
  146. package/dist/notify-helper.d.ts +17 -0
  147. package/dist/notify-helper.d.ts.map +1 -0
  148. package/dist/notify-helper.js +27 -0
  149. package/dist/notify-helper.js.map +1 -0
  150. package/dist/program.d.ts +10 -0
  151. package/dist/program.d.ts.map +1 -0
  152. package/dist/program.js +97 -0
  153. package/dist/program.js.map +1 -0
  154. package/dist/progress-text.d.ts +26 -0
  155. package/dist/progress-text.d.ts.map +1 -0
  156. package/dist/progress-text.js +35 -0
  157. package/dist/progress-text.js.map +1 -0
  158. package/dist/project-bootstrap.d.ts +8 -0
  159. package/dist/project-bootstrap.d.ts.map +1 -0
  160. package/dist/project-bootstrap.js +139 -0
  161. package/dist/project-bootstrap.js.map +1 -0
  162. package/dist/runtime-requirements.d.ts +24 -0
  163. package/dist/runtime-requirements.d.ts.map +1 -0
  164. package/dist/runtime-requirements.js +89 -0
  165. package/dist/runtime-requirements.js.map +1 -0
  166. package/dist/tui/__tests__/markdown.test.d.ts +2 -0
  167. package/dist/tui/__tests__/markdown.test.d.ts.map +1 -0
  168. package/dist/tui/__tests__/markdown.test.js +58 -0
  169. package/dist/tui/__tests__/markdown.test.js.map +1 -0
  170. package/dist/tui/activity-state.d.ts +9 -0
  171. package/dist/tui/activity-state.d.ts.map +1 -0
  172. package/dist/tui/activity-state.js +6 -0
  173. package/dist/tui/activity-state.js.map +1 -0
  174. package/dist/tui/agent-input.d.ts +118 -0
  175. package/dist/tui/agent-input.d.ts.map +1 -0
  176. package/dist/tui/agent-input.js +255 -0
  177. package/dist/tui/agent-input.js.map +1 -0
  178. package/dist/tui/ansi.d.ts +40 -0
  179. package/dist/tui/ansi.d.ts.map +1 -0
  180. package/dist/tui/ansi.js +62 -0
  181. package/dist/tui/ansi.js.map +1 -0
  182. package/dist/tui/app.d.ts +13 -0
  183. package/dist/tui/app.d.ts.map +1 -0
  184. package/dist/tui/app.js +109 -0
  185. package/dist/tui/app.js.map +1 -0
  186. package/dist/tui/chat-depth.d.ts +9 -0
  187. package/dist/tui/chat-depth.d.ts.map +1 -0
  188. package/dist/tui/chat-depth.js +14 -0
  189. package/dist/tui/chat-depth.js.map +1 -0
  190. package/dist/tui/chat-draft.d.ts +4 -0
  191. package/dist/tui/chat-draft.d.ts.map +1 -0
  192. package/dist/tui/chat-draft.js +28 -0
  193. package/dist/tui/chat-draft.js.map +1 -0
  194. package/dist/tui/composer-caret.d.ts +10 -0
  195. package/dist/tui/composer-caret.d.ts.map +1 -0
  196. package/dist/tui/composer-caret.js +13 -0
  197. package/dist/tui/composer-caret.js.map +1 -0
  198. package/dist/tui/composer-display.d.ts +7 -0
  199. package/dist/tui/composer-display.d.ts.map +1 -0
  200. package/dist/tui/composer-display.js +17 -0
  201. package/dist/tui/composer-display.js.map +1 -0
  202. package/dist/tui/dashboard-model.d.ts +46 -0
  203. package/dist/tui/dashboard-model.d.ts.map +1 -0
  204. package/dist/tui/dashboard-model.js +105 -0
  205. package/dist/tui/dashboard-model.js.map +1 -0
  206. package/dist/tui/dashboard.d.ts +38 -0
  207. package/dist/tui/dashboard.d.ts.map +1 -0
  208. package/dist/tui/dashboard.js +357 -0
  209. package/dist/tui/dashboard.js.map +1 -0
  210. package/dist/tui/effects.d.ts +61 -0
  211. package/dist/tui/effects.d.ts.map +1 -0
  212. package/dist/tui/effects.js +451 -0
  213. package/dist/tui/effects.js.map +1 -0
  214. package/dist/tui/i18n.d.ts +63 -0
  215. package/dist/tui/i18n.d.ts.map +1 -0
  216. package/dist/tui/i18n.js +205 -0
  217. package/dist/tui/i18n.js.map +1 -0
  218. package/dist/tui/input-history.d.ts +12 -0
  219. package/dist/tui/input-history.d.ts.map +1 -0
  220. package/dist/tui/input-history.js +47 -0
  221. package/dist/tui/input-history.js.map +1 -0
  222. package/dist/tui/local-commands.d.ts +12 -0
  223. package/dist/tui/local-commands.d.ts.map +1 -0
  224. package/dist/tui/local-commands.js +50 -0
  225. package/dist/tui/local-commands.js.map +1 -0
  226. package/dist/tui/markdown.d.ts +2 -0
  227. package/dist/tui/markdown.d.ts.map +1 -0
  228. package/dist/tui/markdown.js +60 -0
  229. package/dist/tui/markdown.js.map +1 -0
  230. package/dist/tui/session-store.d.ts +2 -0
  231. package/dist/tui/session-store.d.ts.map +1 -0
  232. package/dist/tui/session-store.js +2 -0
  233. package/dist/tui/session-store.js.map +1 -0
  234. package/dist/tui/setup.d.ts +53 -0
  235. package/dist/tui/setup.d.ts.map +1 -0
  236. package/dist/tui/setup.js +313 -0
  237. package/dist/tui/setup.js.map +1 -0
  238. package/dist/tui/slash-autocomplete.d.ts +8 -0
  239. package/dist/tui/slash-autocomplete.d.ts.map +1 -0
  240. package/dist/tui/slash-autocomplete.js +48 -0
  241. package/dist/tui/slash-autocomplete.js.map +1 -0
  242. package/dist/tui/theme.d.ts +28 -0
  243. package/dist/tui/theme.d.ts.map +1 -0
  244. package/dist/tui/theme.js +53 -0
  245. package/dist/tui/theme.js.map +1 -0
  246. package/dist/utils.d.ts +32 -0
  247. package/dist/utils.d.ts.map +1 -0
  248. package/dist/utils.js +165 -0
  249. package/dist/utils.js.map +1 -0
  250. package/package.json +72 -0
@@ -0,0 +1,46 @@
1
+ import { Command } from "commander";
2
+ import { StateManager, writeExportArtifact } from "@kienmatu/inkos-core";
3
+ import { join } from "node:path";
4
+ import { findProjectRoot, resolveBookId, log, logError } from "../utils.js";
5
+ export const exportCommand = new Command("export")
6
+ .description("Export book chapters to a single file")
7
+ .argument("[book-id]", "Book ID (auto-detected if only one book)")
8
+ .option("--format <format>", "Output format (txt, md, epub)", "txt")
9
+ .option("--output <path>", "Output file path")
10
+ .option("--approved-only", "Only export approved chapters")
11
+ .option("--json", "Output JSON metadata")
12
+ .action(async (bookIdArg, opts) => {
13
+ try {
14
+ const root = findProjectRoot();
15
+ const bookId = await resolveBookId(bookIdArg, root);
16
+ const state = new StateManager(root);
17
+ const result = await writeExportArtifact(state, bookId, {
18
+ format: opts.format,
19
+ approvedOnly: Boolean(opts.approvedOnly),
20
+ outputPath: opts.output ?? join(root, `${bookId}_export.${opts.format}`),
21
+ });
22
+ if (opts.json) {
23
+ log(JSON.stringify({
24
+ bookId,
25
+ chaptersExported: result.chaptersExported,
26
+ totalWords: result.totalWords,
27
+ format: result.format,
28
+ outputPath: result.outputPath,
29
+ }, null, 2));
30
+ }
31
+ else {
32
+ log(`Exported ${result.chaptersExported} chapters (${result.totalWords} words)`);
33
+ log(`Output: ${result.outputPath}`);
34
+ }
35
+ }
36
+ catch (e) {
37
+ if (opts.json) {
38
+ log(JSON.stringify({ error: String(e) }));
39
+ }
40
+ else {
41
+ logError(`Failed to export: ${e}`);
42
+ }
43
+ process.exit(1);
44
+ }
45
+ });
46
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5E,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,uCAAuC,CAAC;KACpD,QAAQ,CAAC,WAAW,EAAE,0CAA0C,CAAC;KACjE,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,KAAK,CAAC;KACnE,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;KAC7C,MAAM,CAAC,iBAAiB,EAAE,+BAA+B,CAAC;KAC1D,MAAM,CAAC,QAAQ,EAAE,sBAAsB,CAAC;KACxC,MAAM,CAAC,KAAK,EAAE,SAA6B,EAAE,IAAI,EAAE,EAAE;IACpD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QAErC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE;YACtD,MAAM,EAAE,IAAI,CAAC,MAA+B;YAC5C,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YACxC,UAAU,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC;SACzE,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACjB,MAAM;gBACN,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;gBACzC,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACf,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,YAAY,MAAM,CAAC,gBAAgB,cAAc,MAAM,CAAC,UAAU,SAAS,CAAC,CAAC;YACjF,GAAG,CAAC,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const fanficCommand: Command;
3
+ //# sourceMappingURL=fanfic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fanfic.d.ts","sourceRoot":"","sources":["../../src/commands/fanfic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,eAAO,MAAM,aAAa,SACiB,CAAC"}
@@ -0,0 +1,177 @@
1
+ import { Command } from "commander";
2
+ import { readFile, readdir, stat } from "node:fs/promises";
3
+ import { join, resolve, basename } from "node:path";
4
+ import { deriveBookIdFromTitle, normalizePlatformOrOther, PipelineRunner, toWritingLanguage } from "@kienmatu/inkos-core";
5
+ import { loadConfig, buildPipelineConfig, findProjectRoot, resolveBookId, log, logError } from "../utils.js";
6
+ import { formatFanficCanonMissingError, formatFanficInvalidModeError, formatFanficSourceDirEmptyError, formatFanficSourceTooShortError, } from "../localization.js";
7
+ export const fanficCommand = new Command("fanfic")
8
+ .description("Fan fiction writing tools");
9
+ fanficCommand
10
+ .command("init")
11
+ .description("Create a fanfic book from external source material")
12
+ .requiredOption("--title <title>", "Book title")
13
+ .requiredOption("--from <path>", "Source file or directory (novel text, wiki, character docs)")
14
+ .option("--mode <mode>", "Fanfic mode: canon|au|ooc|cp", "canon")
15
+ .option("--genre <genre>", "Genre", "other")
16
+ .option("--platform <platform>", "Target platform", "other")
17
+ .option("--target-chapters <n>", "Target chapter count", "100")
18
+ .option("--chapter-words <n>", "Words per chapter", "3000")
19
+ .option("--lang <language>", "Writing language: zh or en. Defaults from genre.")
20
+ .option("--json", "Output JSON")
21
+ .action(async (opts) => {
22
+ try {
23
+ const config = await loadConfig();
24
+ const root = findProjectRoot();
25
+ const mode = opts.mode;
26
+ if (!["canon", "au", "ooc", "cp"].includes(mode)) {
27
+ throw new Error(formatFanficInvalidModeError(mode));
28
+ }
29
+ // Read source material
30
+ const sourcePath = resolve(opts.from);
31
+ const sourceText = await readSourceMaterial(sourcePath);
32
+ const sourceName = basename(sourcePath);
33
+ if (!sourceText || sourceText.length < 100) {
34
+ throw new Error(formatFanficSourceTooShortError(sourceText.length));
35
+ }
36
+ const bookId = deriveBookIdFromTitle(opts.title) || `book-${Date.now().toString(36)}`;
37
+ const now = new Date().toISOString();
38
+ const book = {
39
+ id: bookId,
40
+ title: opts.title,
41
+ platform: normalizePlatformOrOther(opts.platform),
42
+ genre: opts.genre,
43
+ status: "outlining",
44
+ targetChapters: parseInt(opts.targetChapters, 10),
45
+ chapterWordCount: parseInt(opts.chapterWords, 10),
46
+ language: toWritingLanguage(opts.lang ?? config.language),
47
+ createdAt: now,
48
+ updatedAt: now,
49
+ fanficMode: mode,
50
+ };
51
+ if (!opts.json)
52
+ log(`Creating fanfic "${book.title}" (${mode} mode, ${book.genre})...`);
53
+ if (!opts.json)
54
+ log(` Source: ${sourceName} (${sourceText.length} chars)`);
55
+ const pipeline = new PipelineRunner(buildPipelineConfig(config, root));
56
+ await pipeline.initFanficBook(book, sourceText, sourceName, mode);
57
+ if (opts.json) {
58
+ log(JSON.stringify({
59
+ bookId,
60
+ title: book.title,
61
+ genre: book.genre,
62
+ fanficMode: mode,
63
+ source: sourceName,
64
+ location: `books/${bookId}/`,
65
+ nextStep: `inkos write next ${bookId}`,
66
+ }, null, 2));
67
+ }
68
+ else {
69
+ log(`Fanfic created: ${bookId}`);
70
+ log(` Mode: ${mode}`);
71
+ log(` Location: books/${bookId}/`);
72
+ log(` fanfic_canon.md + foundation generated.`);
73
+ log("");
74
+ log(`Next: inkos write next ${bookId}`);
75
+ }
76
+ }
77
+ catch (e) {
78
+ if (opts.json) {
79
+ log(JSON.stringify({ error: String(e) }));
80
+ }
81
+ else {
82
+ logError(`Failed to create fanfic: ${e}`);
83
+ }
84
+ process.exit(1);
85
+ }
86
+ });
87
+ fanficCommand
88
+ .command("show")
89
+ .description("Display parsed fanfic canon")
90
+ .argument("[book-id]", "Book ID (auto-detected if only one book)")
91
+ .option("--json", "Output JSON")
92
+ .action(async (bookIdArg, opts) => {
93
+ try {
94
+ await loadConfig();
95
+ const root = findProjectRoot();
96
+ const bookId = await resolveBookId(bookIdArg, root);
97
+ const { StateManager } = await import("@kienmatu/inkos-core");
98
+ const state = new StateManager(root);
99
+ const bookDir = state.bookDir(bookId);
100
+ let canon;
101
+ try {
102
+ canon = await readFile(join(bookDir, "story/fanfic_canon.md"), "utf-8");
103
+ }
104
+ catch {
105
+ throw new Error(formatFanficCanonMissingError());
106
+ }
107
+ if (opts.json) {
108
+ log(JSON.stringify({ bookId, fanficCanon: canon }, null, 2));
109
+ }
110
+ else {
111
+ log(`Fanfic Canon for "${bookId}":\n`);
112
+ log(canon);
113
+ }
114
+ }
115
+ catch (e) {
116
+ if (opts.json) {
117
+ log(JSON.stringify({ error: String(e) }));
118
+ }
119
+ else {
120
+ logError(String(e));
121
+ }
122
+ process.exit(1);
123
+ }
124
+ });
125
+ fanficCommand
126
+ .command("refresh")
127
+ .description("Re-import source material and regenerate fanfic canon")
128
+ .argument("[book-id]", "Book ID (auto-detected if only one book)")
129
+ .requiredOption("--from <path>", "Source file or directory")
130
+ .option("--json", "Output JSON")
131
+ .action(async (bookIdArg, opts) => {
132
+ try {
133
+ const config = await loadConfig();
134
+ const root = findProjectRoot();
135
+ const bookId = await resolveBookId(bookIdArg, root);
136
+ const { StateManager } = await import("@kienmatu/inkos-core");
137
+ const state = new StateManager(root);
138
+ const book = await state.loadBookConfig(bookId);
139
+ const mode = (book.fanficMode ?? "canon");
140
+ const sourcePath = resolve(opts.from);
141
+ const sourceText = await readSourceMaterial(sourcePath);
142
+ const sourceName = basename(sourcePath);
143
+ if (!opts.json)
144
+ log(`Refreshing fanfic canon for "${bookId}" from ${sourceName}...`);
145
+ const pipeline = new PipelineRunner(buildPipelineConfig(config, root));
146
+ await pipeline.importFanficCanon(bookId, sourceText, sourceName, mode);
147
+ if (opts.json) {
148
+ log(JSON.stringify({ bookId, source: sourceName, refreshedAt: new Date().toISOString() }));
149
+ }
150
+ else {
151
+ log(`Canon refreshed from "${sourceName}".`);
152
+ }
153
+ }
154
+ catch (e) {
155
+ if (opts.json) {
156
+ log(JSON.stringify({ error: String(e) }));
157
+ }
158
+ else {
159
+ logError(`Failed to refresh canon: ${e}`);
160
+ }
161
+ process.exit(1);
162
+ }
163
+ });
164
+ async function readSourceMaterial(sourcePath) {
165
+ const s = await stat(sourcePath);
166
+ if (s.isDirectory()) {
167
+ const files = await readdir(sourcePath);
168
+ const textFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".md"));
169
+ if (textFiles.length === 0) {
170
+ throw new Error(formatFanficSourceDirEmptyError(sourcePath));
171
+ }
172
+ const contents = await Promise.all(textFiles.sort().map((f) => readFile(join(sourcePath, f), "utf-8")));
173
+ return contents.join("\n\n---\n\n");
174
+ }
175
+ return readFile(sourcePath, "utf-8");
176
+ }
177
+ //# sourceMappingURL=fanfic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fanfic.js","sourceRoot":"","sources":["../../src/commands/fanfic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Command } from "commander";
2
+ export declare const forecastCommand: Command;
3
+ export declare function parseForecastShowArgs(args: ReadonlyArray<string>): {
4
+ readonly bookIdArg?: string;
5
+ readonly forecastId: string;
6
+ };
7
+ export declare function parseForecastSelectArgs(args: ReadonlyArray<string>): {
8
+ readonly bookIdArg?: string;
9
+ readonly forecastId: string;
10
+ readonly branchId: string;
11
+ };
12
+ //# sourceMappingURL=forecast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../src/commands/forecast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,eAAO,MAAM,eAAe,SAC8D,CAAC;AAqI3F,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAIA;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAIA"}
@@ -0,0 +1,156 @@
1
+ import { Command } from "commander";
2
+ import { FORECAST_DEFAULT_BRANCHES, FORECAST_DEFAULT_HORIZON, FORECAST_MAX_BRANCHES, FORECAST_MAX_HORIZON, FORECAST_MIN_BRANCHES, FORECAST_MIN_HORIZON, PipelineRunner, createNarrativeForecast, getNarrativeForecast, selectNarrativeBranch, } from "@kienmatu/inkos-core";
3
+ import { buildPipelineConfig, findProjectRoot, loadConfig, log, logError, resolveBookId } from "../utils.js";
4
+ // CLI surface for RFC #342: create / show / select. All three operate only on
5
+ // story/runtime/narrative-forecasts/ artifacts; canonical files stay untouched.
6
+ export const forecastCommand = new Command("forecast")
7
+ .description("Narrative forecast: compare non-canonical future branches before writing");
8
+ forecastCommand
9
+ .command("create")
10
+ .description("Create a narrative forecast (2-5 isolated candidate branches) from the current canon")
11
+ .argument("[book-id]", "Book ID (auto-detected if only one book)")
12
+ .requiredOption("--divergence <text>", "Divergence point to branch on, e.g. \"主角是否接受对手的合作提议\"")
13
+ .option("--branches <n>", `Number of isolated candidate branches (${FORECAST_MIN_BRANCHES}-${FORECAST_MAX_BRANCHES})`, String(FORECAST_DEFAULT_BRANCHES))
14
+ .option("--horizon <n>", `Future chapters each branch covers (${FORECAST_MIN_HORIZON}-${FORECAST_MAX_HORIZON})`, String(FORECAST_DEFAULT_HORIZON))
15
+ .option("--model <model>", "Override the forecast model")
16
+ .option("--llm-base-url <url>", "Override LLM base URL")
17
+ .option("--json", "Output JSON")
18
+ .action(async (bookIdArg, opts) => {
19
+ try {
20
+ const root = findProjectRoot();
21
+ const bookId = await resolveBookId(bookIdArg, root);
22
+ const branchCount = parseBoundedInteger(opts.branches, FORECAST_DEFAULT_BRANCHES, "branches", FORECAST_MIN_BRANCHES, FORECAST_MAX_BRANCHES);
23
+ const horizon = parseBoundedInteger(opts.horizon, FORECAST_DEFAULT_HORIZON, "horizon", FORECAST_MIN_HORIZON, FORECAST_MAX_HORIZON);
24
+ const config = await loadConfig({ projectRoot: root });
25
+ if (opts.llmBaseUrl)
26
+ config.llm.baseUrl = opts.llmBaseUrl;
27
+ if (opts.model)
28
+ config.llm.model = opts.model;
29
+ const pipeline = new PipelineRunner(buildPipelineConfig(config, root, { quiet: Boolean(opts.json) }));
30
+ const result = await createNarrativeForecast({
31
+ projectRoot: root,
32
+ bookId,
33
+ divergence: opts.divergence,
34
+ branchCount,
35
+ horizon,
36
+ runtime: pipeline.createAgentContext("forecast", bookId),
37
+ onProgress: opts.json ? undefined : (message) => log(message),
38
+ });
39
+ if (opts.json) {
40
+ log(JSON.stringify({
41
+ forecast: result.forecast,
42
+ forecastJsonPath: result.forecastJsonPath,
43
+ comparisonPath: result.comparisonPath,
44
+ }, null, 2));
45
+ return;
46
+ }
47
+ log(`Forecast created: ${result.forecast.forecastId} (base chapter ${result.forecast.baseChapter})`);
48
+ for (const line of formatBranchLines(result.forecast))
49
+ log(line);
50
+ log(`Comparison: ${result.comparisonPath}`);
51
+ log(`Select a branch with: inkos forecast select ${bookId} ${result.forecast.forecastId} <branch-id>`);
52
+ }
53
+ catch (e) {
54
+ failForecastCommand("Forecast create failed", e, opts.json);
55
+ }
56
+ });
57
+ forecastCommand
58
+ .command("show")
59
+ .description("Show a narrative forecast and re-check it against the current canon (marks it stale when canon changed)")
60
+ .argument("<args...>", "[book-id] <forecast-id>")
61
+ .option("--json", "Output JSON")
62
+ .action(async (args, opts) => {
63
+ try {
64
+ const { bookIdArg, forecastId } = parseForecastShowArgs(args);
65
+ const root = findProjectRoot();
66
+ const bookId = await resolveBookId(bookIdArg, root);
67
+ const result = await getNarrativeForecast({ projectRoot: root, bookId, forecastId });
68
+ if (opts.json) {
69
+ log(JSON.stringify({
70
+ forecast: result.forecast,
71
+ stale: result.stale,
72
+ forecastJsonPath: result.forecastJsonPath,
73
+ comparisonPath: result.comparisonPath,
74
+ }, null, 2));
75
+ return;
76
+ }
77
+ log(`Forecast ${result.forecast.forecastId} — status: ${result.stale ? "stale" : result.forecast.status}`);
78
+ log(`Divergence: ${result.forecast.divergence}`);
79
+ log(`Base chapter: ${result.forecast.baseChapter}, horizon: ~${result.forecast.horizon} chapters`);
80
+ if (result.stale) {
81
+ log("WARNING: canon changed after this forecast was generated; regenerate before relying on it.");
82
+ }
83
+ for (const line of formatBranchLines(result.forecast))
84
+ log(line);
85
+ log(`Comparison: ${result.comparisonPath}`);
86
+ }
87
+ catch (e) {
88
+ failForecastCommand("Forecast show failed", e, opts.json);
89
+ }
90
+ });
91
+ forecastCommand
92
+ .command("select")
93
+ .description("Select a branch: writes only selected-branch-plan.md; canonical files are never modified")
94
+ .argument("<args...>", "[book-id] <forecast-id> <branch-id>")
95
+ .option("--json", "Output JSON")
96
+ .action(async (args, opts) => {
97
+ try {
98
+ const { bookIdArg, forecastId, branchId } = parseForecastSelectArgs(args);
99
+ const root = findProjectRoot();
100
+ const bookId = await resolveBookId(bookIdArg, root);
101
+ const result = await selectNarrativeBranch({ projectRoot: root, bookId, forecastId, branchId });
102
+ if (opts.json) {
103
+ log(JSON.stringify({
104
+ forecastId: result.forecast.forecastId,
105
+ branchId: result.branch.branchId,
106
+ branchTitle: result.branch.title,
107
+ stale: result.stale,
108
+ planPath: result.planPath,
109
+ }, null, 2));
110
+ return;
111
+ }
112
+ log(`Selected ${result.branch.branchId} "${result.branch.title}" from ${result.forecast.forecastId}.`);
113
+ if (result.stale) {
114
+ log("WARNING: this forecast is stale; the plan includes a stale warning.");
115
+ }
116
+ log(`Plan: ${result.planPath}`);
117
+ log("Canonical files were not modified. Applying the plan to the outline is a separate, explicit step.");
118
+ }
119
+ catch (e) {
120
+ failForecastCommand("Forecast select failed", e, opts.json);
121
+ }
122
+ });
123
+ export function parseForecastShowArgs(args) {
124
+ if (args.length === 1)
125
+ return { forecastId: args[0] };
126
+ if (args.length === 2)
127
+ return { bookIdArg: args[0], forecastId: args[1] };
128
+ throw new Error("Usage: inkos forecast show [book-id] <forecast-id>");
129
+ }
130
+ export function parseForecastSelectArgs(args) {
131
+ if (args.length === 2)
132
+ return { forecastId: args[0], branchId: args[1] };
133
+ if (args.length === 3)
134
+ return { bookIdArg: args[0], forecastId: args[1], branchId: args[2] };
135
+ throw new Error("Usage: inkos forecast select [book-id] <forecast-id> <branch-id>");
136
+ }
137
+ function formatBranchLines(forecast) {
138
+ return forecast.branches.map((branch) => `- ${branch.branchId} "${branch.title}" — intent fit ${branch.intentAlignment.score}/100, ${branch.risks.length} risk(s)`);
139
+ }
140
+ function parseBoundedInteger(value, fallback, name, min, max) {
141
+ const parsed = value ? Number.parseInt(value, 10) : fallback;
142
+ if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
143
+ throw new Error(`${name} must be an integer between ${min} and ${max}.`);
144
+ }
145
+ return parsed;
146
+ }
147
+ function failForecastCommand(prefix, error, json) {
148
+ if (json) {
149
+ log(JSON.stringify({ error: `${prefix}: ${String(error)}` }, null, 2));
150
+ }
151
+ else {
152
+ logError(`${prefix}: ${String(error)}`);
153
+ }
154
+ process.exitCode = 1;
155
+ }
156
+ //# sourceMappingURL=forecast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forecast.js","sourceRoot":"","sources":["../../src/commands/forecast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { Command } from "commander";
2
+ import { type CliLanguage } from "../localization.js";
3
+ export declare function buildGenreTemplate(params: {
4
+ readonly id: string;
5
+ readonly name: string;
6
+ readonly numerical: boolean;
7
+ readonly power: boolean;
8
+ readonly era: boolean;
9
+ }, language?: CliLanguage): string;
10
+ export declare const genreCommand: Command;
11
+ //# sourceMappingURL=genre.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"genre.d.ts","sourceRoot":"","sources":["../../src/commands/genre.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE1E,wBAAgB,kBAAkB,CAChC,MAAM,EAAE;IACN,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB,EACD,QAAQ,GAAE,WAAkB,GAC3B,MAAM,CA8CR;AAED,eAAO,MAAM,YAAY,SACc,CAAC"}
@@ -0,0 +1,186 @@
1
+ import { Command } from "commander";
2
+ import { writeFile, mkdir, readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { listAvailableGenres, readGenreProfile, getBuiltinGenresDir } from "@kienmatu/inkos-core";
5
+ import { findProjectRoot, log, logError } from "../utils.js";
6
+ import { resolveCliLanguage } from "../localization.js";
7
+ export function buildGenreTemplate(params, language = "zh") {
8
+ if (language === "en") {
9
+ return `---
10
+ name: ${params.name}
11
+ id: ${params.id}
12
+ chapterTypes: ["progression", "setup", "transition", "payoff"]
13
+ fatigueWords: ["shocked", "unbelievable", "incredible"]
14
+ numericalSystem: ${params.numerical}
15
+ powerScaling: ${params.power}
16
+ eraResearch: ${params.era}
17
+ pacingRule: "A clear advance or payoff every 2-3 chapters"
18
+ satisfactionTypes: ["goal achieved", "obstacle overcome", "truth revealed"]
19
+ auditDimensions: [1,2,3,6,7,8,9,10,13,14,15,16,17,18,19]
20
+ ---
21
+
22
+ ## Genre Taboos
23
+
24
+ - (add taboos for this genre)
25
+
26
+ ## Narrative Guidance
27
+
28
+ (describe the narrative focus and style requirements for this genre)
29
+ `;
30
+ }
31
+ return `---
32
+ name: ${params.name}
33
+ id: ${params.id}
34
+ chapterTypes: ["推进章", "布局章", "过渡章", "回收章"]
35
+ fatigueWords: ["震惊", "不可思议", "难以置信"]
36
+ numericalSystem: ${params.numerical}
37
+ powerScaling: ${params.power}
38
+ eraResearch: ${params.era}
39
+ pacingRule: "每2-3章有一个明确的进展或反馈"
40
+ satisfactionTypes: ["目标达成", "困难克服", "真相揭示"]
41
+ auditDimensions: [1,2,3,6,7,8,9,10,13,14,15,16,17,18,19]
42
+ ---
43
+
44
+ ## 题材禁忌
45
+
46
+ - (根据题材添加禁忌)
47
+
48
+ ## 叙事指导
49
+
50
+ (根据题材描述叙事重心和风格要求)
51
+ `;
52
+ }
53
+ export const genreCommand = new Command("genre")
54
+ .description("Manage genre profiles");
55
+ genreCommand
56
+ .command("list")
57
+ .description("List all available genre profiles (built-in + project)")
58
+ .action(async () => {
59
+ try {
60
+ const root = findProjectRoot();
61
+ const genres = await listAvailableGenres(root);
62
+ if (genres.length === 0) {
63
+ log("No genre profiles found.");
64
+ return;
65
+ }
66
+ log("Available genres:\n");
67
+ for (const g of genres) {
68
+ const tag = g.source === "project" ? "[project]" : "[builtin]";
69
+ log(` ${g.id.padEnd(12)} ${g.name.padEnd(8)} ${tag}`);
70
+ }
71
+ log(`\nTotal: ${genres.length} genre(s)`);
72
+ }
73
+ catch (e) {
74
+ logError(`Failed to list genres: ${e}`);
75
+ process.exit(1);
76
+ }
77
+ });
78
+ genreCommand
79
+ .command("show")
80
+ .description("Display a genre profile")
81
+ .argument("<id>", "Genre ID (e.g. xuanhuan, urban, horror)")
82
+ .action(async (id) => {
83
+ try {
84
+ const root = findProjectRoot();
85
+ const genres = await listAvailableGenres(root);
86
+ const exactMatch = genres.some(g => g.id === id);
87
+ if (!exactMatch) {
88
+ logError(`Genre "${id}" not found. Available: ${genres.map(g => g.id).join(", ")}`);
89
+ process.exit(1);
90
+ }
91
+ const { profile, body } = await readGenreProfile(root, id);
92
+ log(`Genre: ${profile.name} (${profile.id})\n`);
93
+ log(` Chapter types: ${profile.chapterTypes.join(", ")}`);
94
+ log(` Fatigue words: ${profile.fatigueWords.join(", ")}`);
95
+ log(` Numerical system: ${profile.numericalSystem}`);
96
+ log(` Power scaling: ${profile.powerScaling}`);
97
+ log(` Era research: ${profile.eraResearch}`);
98
+ log(` Pacing rule: ${profile.pacingRule}`);
99
+ log(` Satisfaction types: ${profile.satisfactionTypes.join(", ")}`);
100
+ log(` Audit dimensions: ${profile.auditDimensions.join(", ")}`);
101
+ if (body) {
102
+ log(`\n--- Body ---\n${body}`);
103
+ }
104
+ }
105
+ catch (e) {
106
+ logError(`Failed to show genre: ${e}`);
107
+ process.exit(1);
108
+ }
109
+ });
110
+ genreCommand
111
+ .command("create")
112
+ .description("Scaffold a new genre profile in the project genres/ directory")
113
+ .argument("<id>", "Genre ID (e.g. scifi, wuxia, romance)")
114
+ .option("--name <name>", "Genre display name", "")
115
+ .option("--numerical", "Enable numerical system", false)
116
+ .option("--power", "Enable power scaling", false)
117
+ .option("--era", "Enable era research", false)
118
+ .option("--lang <language>", "Template language: zh or en (defaults to INKOS_LOCALE/LANG, then zh)")
119
+ .action(async (id, opts) => {
120
+ try {
121
+ const root = findProjectRoot();
122
+ const genresDir = join(root, "genres");
123
+ const filePath = join(genresDir, `${id}.md`);
124
+ // Check if already exists
125
+ try {
126
+ await readFile(filePath, "utf-8");
127
+ logError(`Genre profile already exists: ${filePath}`);
128
+ process.exit(1);
129
+ }
130
+ catch { /* file doesn't exist, good */ }
131
+ await mkdir(genresDir, { recursive: true });
132
+ const name = opts.name || id;
133
+ const template = buildGenreTemplate({
134
+ id,
135
+ name,
136
+ numerical: opts.numerical,
137
+ power: opts.power,
138
+ era: opts.era,
139
+ }, resolveCliLanguage(opts.lang));
140
+ await writeFile(filePath, template, "utf-8");
141
+ log(`Created genre profile: ${filePath}`);
142
+ log(`Edit the file to customize chapter types, fatigue words, rules, etc.`);
143
+ }
144
+ catch (e) {
145
+ logError(`Failed to create genre: ${e}`);
146
+ process.exit(1);
147
+ }
148
+ });
149
+ genreCommand
150
+ .command("copy")
151
+ .description("Copy a built-in genre profile to project for customization")
152
+ .argument("<id>", "Genre ID to copy (e.g. xuanhuan)")
153
+ .action(async (id) => {
154
+ try {
155
+ const root = findProjectRoot();
156
+ const builtinDir = getBuiltinGenresDir();
157
+ const srcPath = join(builtinDir, `${id}.md`);
158
+ const genresDir = join(root, "genres");
159
+ const destPath = join(genresDir, `${id}.md`);
160
+ // Check if project override already exists
161
+ try {
162
+ await readFile(destPath, "utf-8");
163
+ logError(`Project genre profile already exists: ${destPath}`);
164
+ process.exit(1);
165
+ }
166
+ catch { /* doesn't exist, good */ }
167
+ let content;
168
+ try {
169
+ content = await readFile(srcPath, "utf-8");
170
+ }
171
+ catch {
172
+ logError(`Built-in genre "${id}" not found. Use 'inkos genre list' to see available genres.`);
173
+ process.exit(1);
174
+ return;
175
+ }
176
+ await mkdir(genresDir, { recursive: true });
177
+ await writeFile(destPath, content, "utf-8");
178
+ log(`Copied to: ${destPath}`);
179
+ log(`This project-level copy will override the built-in profile.`);
180
+ }
181
+ catch (e) {
182
+ logError(`Failed to copy genre: ${e}`);
183
+ process.exit(1);
184
+ }
185
+ });
186
+ //# sourceMappingURL=genre.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"genre.js","sourceRoot":"","sources":["../../src/commands/genre.ts"],"names":[],"mappings":"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"}