@lingjingai/scriptctl 0.50.0 → 0.51.1

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 (287) hide show
  1. package/changes/0.51.0.md +24 -0
  2. package/changes/0.51.1.md +15 -0
  3. package/changes/unreleased.md +1 -2
  4. package/dist/cli/register-batch.d.ts +3 -0
  5. package/dist/cli/register-batch.js +28 -0
  6. package/dist/cli/register-batch.js.map +1 -0
  7. package/dist/cli/register-edits.d.ts +4 -0
  8. package/dist/cli/register-edits.js +226 -0
  9. package/dist/cli/register-edits.js.map +1 -0
  10. package/dist/cli/register-query.d.ts +4 -0
  11. package/dist/cli/register-query.js +93 -0
  12. package/dist/cli/register-query.js.map +1 -0
  13. package/dist/cli/register-tools.d.ts +3 -0
  14. package/dist/cli/register-tools.js +23 -0
  15. package/dist/cli/register-tools.js.map +1 -0
  16. package/dist/cli/register-workflows.d.ts +4 -0
  17. package/dist/cli/register-workflows.js +71 -0
  18. package/dist/cli/register-workflows.js.map +1 -0
  19. package/dist/cli/registry.d.ts +14 -0
  20. package/dist/cli/registry.js +65 -0
  21. package/dist/cli/registry.js.map +1 -0
  22. package/dist/cli.d.ts +3 -0
  23. package/dist/cli.js +146 -596
  24. package/dist/cli.js.map +1 -1
  25. package/dist/common.d.ts +12 -38
  26. package/dist/common.js +5 -11
  27. package/dist/common.js.map +1 -1
  28. package/dist/domain/authoring/shared/bracket.js +3 -3
  29. package/dist/domain/authoring/shared/bracket.js.map +1 -1
  30. package/dist/domain/authoring/v3/apply.js +3 -3
  31. package/dist/domain/authoring/v3/apply.js.map +1 -1
  32. package/dist/domain/authoring/v4/apply.js +6 -6
  33. package/dist/domain/authoring/v4/apply.js.map +1 -1
  34. package/dist/domain/authoring/v4/parser.js +2 -2
  35. package/dist/domain/authoring/v4/parser.js.map +1 -1
  36. package/dist/domain/authoring/v4/serializer.js +2 -2
  37. package/dist/domain/authoring/v4/serializer.js.map +1 -1
  38. package/dist/domain/ingest/assembler.d.ts +1 -0
  39. package/dist/domain/ingest/assembler.js +8 -2
  40. package/dist/domain/ingest/assembler.js.map +1 -1
  41. package/dist/domain/ingest/video-apply.js +6 -3
  42. package/dist/domain/ingest/video-apply.js.map +1 -1
  43. package/dist/domain/ingest/video-context.js +2 -2
  44. package/dist/domain/ingest/video-context.js.map +1 -1
  45. package/dist/domain/ingest/video-records.d.ts +6 -5
  46. package/dist/domain/ingest/video-serial-md.d.ts +6 -5
  47. package/dist/domain/ingest/video-serial-md.js +11 -4
  48. package/dist/domain/ingest/video-serial-md.js.map +1 -1
  49. package/dist/domain/script/dispatch.js +5 -7
  50. package/dist/domain/script/dispatch.js.map +1 -1
  51. package/dist/domain/script/shared.js +4 -6
  52. package/dist/domain/script/shared.js.map +1 -1
  53. package/dist/domain/script/v3/address.js +29 -38
  54. package/dist/domain/script/v3/address.js.map +1 -1
  55. package/dist/domain/script/v3/lookups.js +31 -32
  56. package/dist/domain/script/v3/lookups.js.map +1 -1
  57. package/dist/domain/script/v3/ordinal.js +4 -5
  58. package/dist/domain/script/v3/ordinal.js.map +1 -1
  59. package/dist/domain/script/v3/parse.js +3 -4
  60. package/dist/domain/script/v3/parse.js.map +1 -1
  61. package/dist/domain/script/v3/patch/helpers.js +6 -8
  62. package/dist/domain/script/v3/patch/helpers.js.map +1 -1
  63. package/dist/domain/script/v3/selector.js +6 -8
  64. package/dist/domain/script/v3/selector.js.map +1 -1
  65. package/dist/domain/script/v4/address.js +2 -3
  66. package/dist/domain/script/v4/address.js.map +1 -1
  67. package/dist/domain/script/v4/ids.js +6 -7
  68. package/dist/domain/script/v4/ids.js.map +1 -1
  69. package/dist/domain/script/v4/ordinal.js +4 -5
  70. package/dist/domain/script/v4/ordinal.js.map +1 -1
  71. package/dist/domain/script/v4/parse.js +3 -4
  72. package/dist/domain/script/v4/parse.js.map +1 -1
  73. package/dist/domain/script/v4/patch/apply.js +3 -3
  74. package/dist/domain/script/v4/patch/apply.js.map +1 -1
  75. package/dist/domain/script/v4/patch/helpers.js +3 -4
  76. package/dist/domain/script/v4/patch/helpers.js.map +1 -1
  77. package/dist/domain/script/v4/patch/ops-action.js +46 -49
  78. package/dist/domain/script/v4/patch/ops-action.js.map +1 -1
  79. package/dist/domain/script/v4/selector.js +2 -3
  80. package/dist/domain/script/v4/selector.js.map +1 -1
  81. package/dist/infra/ingest/video-discovery.js +7 -4
  82. package/dist/infra/ingest/video-discovery.js.map +1 -1
  83. package/dist/infra/script-output-store.js +6 -11
  84. package/dist/infra/script-output-store.js.map +1 -1
  85. package/dist/infra/script-working-copy.js +15 -16
  86. package/dist/infra/script-working-copy.js.map +1 -1
  87. package/dist/llm/config.js +3 -4
  88. package/dist/llm/config.js.map +1 -1
  89. package/dist/llm/router.js +6 -8
  90. package/dist/llm/router.js.map +1 -1
  91. package/dist/llm/tasks/ingest/text-normalize.js +3 -3
  92. package/dist/llm/tasks/ingest/text-normalize.js.map +1 -1
  93. package/dist/llm/tasks/ingest/video-serial.js +29 -22
  94. package/dist/llm/tasks/ingest/video-serial.js.map +1 -1
  95. package/dist/llm/tasks/schemas.js +5 -5
  96. package/dist/llm/tasks/schemas.js.map +1 -1
  97. package/dist/output.d.ts +2 -3
  98. package/dist/output.js +72 -109
  99. package/dist/output.js.map +1 -1
  100. package/dist/usecases/changelog.d.ts +2 -2
  101. package/dist/usecases/changelog.js +28 -36
  102. package/dist/usecases/changelog.js.map +1 -1
  103. package/dist/usecases/doctor.d.ts +2 -2
  104. package/dist/usecases/doctor.js +3 -5
  105. package/dist/usecases/doctor.js.map +1 -1
  106. package/dist/usecases/ingest/command.d.ts +2 -2
  107. package/dist/usecases/ingest/command.js +22 -5
  108. package/dist/usecases/ingest/command.js.map +1 -1
  109. package/dist/usecases/ingest/pass5-extract.js +5 -4
  110. package/dist/usecases/ingest/pass5-extract.js.map +1 -1
  111. package/dist/usecases/ingest/pass5-govern.js +13 -11
  112. package/dist/usecases/ingest/pass5-govern.js.map +1 -1
  113. package/dist/usecases/ingest/pipeline.js +19 -16
  114. package/dist/usecases/ingest/pipeline.js.map +1 -1
  115. package/dist/usecases/ingest/serial-govern.js +5 -4
  116. package/dist/usecases/ingest/serial-govern.js.map +1 -1
  117. package/dist/usecases/ingest/status.d.ts +2 -2
  118. package/dist/usecases/ingest/status.js +1 -1
  119. package/dist/usecases/ingest/status.js.map +1 -1
  120. package/dist/usecases/ingest/video-pipeline.js +23 -17
  121. package/dist/usecases/ingest/video-pipeline.js.map +1 -1
  122. package/dist/usecases/ingest/view-command.d.ts +2 -2
  123. package/dist/usecases/ingest/view-command.js +7 -6
  124. package/dist/usecases/ingest/view-command.js.map +1 -1
  125. package/dist/usecases/ingest/view-server.d.ts +2 -2
  126. package/dist/usecases/ingest/view-server.js +7 -5
  127. package/dist/usecases/ingest/view-server.js.map +1 -1
  128. package/dist/usecases/script/actions.d.ts +2 -2
  129. package/dist/usecases/script/actions.js +12 -13
  130. package/dist/usecases/script/actions.js.map +1 -1
  131. package/dist/usecases/script/actor.d.ts +2 -2
  132. package/dist/usecases/script/actor.js +3 -4
  133. package/dist/usecases/script/actor.js.map +1 -1
  134. package/dist/usecases/script/actors.d.ts +2 -2
  135. package/dist/usecases/script/actors.js +5 -5
  136. package/dist/usecases/script/actors.js.map +1 -1
  137. package/dist/usecases/script/add-actor.d.ts +2 -2
  138. package/dist/usecases/script/add-episode.d.ts +2 -2
  139. package/dist/usecases/script/add-location.d.ts +2 -2
  140. package/dist/usecases/script/add-prop.d.ts +2 -2
  141. package/dist/usecases/script/alias.d.ts +2 -2
  142. package/dist/usecases/script/alias.js +6 -8
  143. package/dist/usecases/script/alias.js.map +1 -1
  144. package/dist/usecases/script/assets.d.ts +2 -2
  145. package/dist/usecases/script/assets.js +15 -7
  146. package/dist/usecases/script/assets.js.map +1 -1
  147. package/dist/usecases/script/audit-assets.d.ts +2 -2
  148. package/dist/usecases/script/audit-assets.js +12 -15
  149. package/dist/usecases/script/audit-assets.js.map +1 -1
  150. package/dist/usecases/script/command-constraints.js +17 -18
  151. package/dist/usecases/script/command-constraints.js.map +1 -1
  152. package/dist/usecases/script/context.d.ts +2 -2
  153. package/dist/usecases/script/context.js +3 -4
  154. package/dist/usecases/script/context.js.map +1 -1
  155. package/dist/usecases/script/create.d.ts +2 -2
  156. package/dist/usecases/script/create.js +3 -4
  157. package/dist/usecases/script/create.js.map +1 -1
  158. package/dist/usecases/script/delete.d.ts +2 -2
  159. package/dist/usecases/script/delete.js +9 -12
  160. package/dist/usecases/script/delete.js.map +1 -1
  161. package/dist/usecases/script/describe.d.ts +2 -2
  162. package/dist/usecases/script/describe.js +6 -8
  163. package/dist/usecases/script/describe.js.map +1 -1
  164. package/dist/usecases/script/dialogue.d.ts +2 -2
  165. package/dist/usecases/script/dialogue.js +5 -6
  166. package/dist/usecases/script/dialogue.js.map +1 -1
  167. package/dist/usecases/script/do.d.ts +2 -2
  168. package/dist/usecases/script/do.js +63 -66
  169. package/dist/usecases/script/do.js.map +1 -1
  170. package/dist/usecases/script/emotion.d.ts +2 -2
  171. package/dist/usecases/script/emotion.js +3 -3
  172. package/dist/usecases/script/emotion.js.map +1 -1
  173. package/dist/usecases/script/episodes.d.ts +2 -2
  174. package/dist/usecases/script/episodes.js +5 -5
  175. package/dist/usecases/script/episodes.js.map +1 -1
  176. package/dist/usecases/script/export-authoring.d.ts +2 -2
  177. package/dist/usecases/script/export-authoring.js +26 -29
  178. package/dist/usecases/script/export-authoring.js.map +1 -1
  179. package/dist/usecases/script/extend.d.ts +2 -2
  180. package/dist/usecases/script/extend.js +22 -18
  181. package/dist/usecases/script/extend.js.map +1 -1
  182. package/dist/usecases/script/ids.d.ts +2 -2
  183. package/dist/usecases/script/ids.js +21 -17
  184. package/dist/usecases/script/ids.js.map +1 -1
  185. package/dist/usecases/script/importance.d.ts +2 -2
  186. package/dist/usecases/script/importance.js +3 -4
  187. package/dist/usecases/script/importance.js.map +1 -1
  188. package/dist/usecases/script/insert.d.ts +2 -2
  189. package/dist/usecases/script/insert.js +8 -10
  190. package/dist/usecases/script/insert.js.map +1 -1
  191. package/dist/usecases/script/issues.d.ts +2 -2
  192. package/dist/usecases/script/issues.js +10 -2
  193. package/dist/usecases/script/issues.js.map +1 -1
  194. package/dist/usecases/script/lib.d.ts +15 -9
  195. package/dist/usecases/script/lib.js +226 -166
  196. package/dist/usecases/script/lib.js.map +1 -1
  197. package/dist/usecases/script/locations.d.ts +2 -2
  198. package/dist/usecases/script/locations.js +5 -5
  199. package/dist/usecases/script/locations.js.map +1 -1
  200. package/dist/usecases/script/merge.d.ts +2 -2
  201. package/dist/usecases/script/merge.js +9 -12
  202. package/dist/usecases/script/merge.js.map +1 -1
  203. package/dist/usecases/script/move.d.ts +2 -2
  204. package/dist/usecases/script/move.js +6 -8
  205. package/dist/usecases/script/move.js.map +1 -1
  206. package/dist/usecases/script/parenthetical.d.ts +2 -2
  207. package/dist/usecases/script/parenthetical.js +9 -12
  208. package/dist/usecases/script/parenthetical.js.map +1 -1
  209. package/dist/usecases/script/props.d.ts +2 -2
  210. package/dist/usecases/script/props.js +5 -5
  211. package/dist/usecases/script/props.js.map +1 -1
  212. package/dist/usecases/script/refs.d.ts +2 -2
  213. package/dist/usecases/script/refs.js +23 -17
  214. package/dist/usecases/script/refs.js.map +1 -1
  215. package/dist/usecases/script/rename.d.ts +2 -2
  216. package/dist/usecases/script/rename.js +6 -8
  217. package/dist/usecases/script/rename.js.map +1 -1
  218. package/dist/usecases/script/replace.d.ts +2 -2
  219. package/dist/usecases/script/replace.js +6 -8
  220. package/dist/usecases/script/replace.js.map +1 -1
  221. package/dist/usecases/script/role.d.ts +2 -2
  222. package/dist/usecases/script/role.js +3 -4
  223. package/dist/usecases/script/role.js.map +1 -1
  224. package/dist/usecases/script/scenes.d.ts +2 -2
  225. package/dist/usecases/script/scenes.js +7 -5
  226. package/dist/usecases/script/scenes.js.map +1 -1
  227. package/dist/usecases/script/script-patch.d.ts +2 -2
  228. package/dist/usecases/script/script-patch.js +95 -112
  229. package/dist/usecases/script/script-patch.js.map +1 -1
  230. package/dist/usecases/script/script-validate.d.ts +2 -2
  231. package/dist/usecases/script/script-validate.js +11 -7
  232. package/dist/usecases/script/script-validate.js.map +1 -1
  233. package/dist/usecases/script/session.js +52 -63
  234. package/dist/usecases/script/session.js.map +1 -1
  235. package/dist/usecases/script/split.d.ts +2 -2
  236. package/dist/usecases/script/split.js +6 -8
  237. package/dist/usecases/script/split.js.map +1 -1
  238. package/dist/usecases/script/state-add.d.ts +2 -2
  239. package/dist/usecases/script/state-add.js +3 -4
  240. package/dist/usecases/script/state-add.js.map +1 -1
  241. package/dist/usecases/script/state-delete.d.ts +2 -2
  242. package/dist/usecases/script/state-delete.js +3 -4
  243. package/dist/usecases/script/state-delete.js.map +1 -1
  244. package/dist/usecases/script/state-rename.d.ts +2 -2
  245. package/dist/usecases/script/state-rename.js +3 -4
  246. package/dist/usecases/script/state-rename.js.map +1 -1
  247. package/dist/usecases/script/states.d.ts +2 -2
  248. package/dist/usecases/script/states.js +54 -27
  249. package/dist/usecases/script/states.js.map +1 -1
  250. package/dist/usecases/script/summary.d.ts +2 -2
  251. package/dist/usecases/script/summary.js +21 -2
  252. package/dist/usecases/script/summary.js.map +1 -1
  253. package/dist/usecases/script/synopsis-generate.d.ts +2 -2
  254. package/dist/usecases/script/synopsis-generate.js +38 -39
  255. package/dist/usecases/script/synopsis-generate.js.map +1 -1
  256. package/dist/usecases/script/synopsis.d.ts +2 -2
  257. package/dist/usecases/script/synopsis.js +9 -12
  258. package/dist/usecases/script/synopsis.js.map +1 -1
  259. package/dist/usecases/script/transition.d.ts +2 -2
  260. package/dist/usecases/script/transition.js +3 -4
  261. package/dist/usecases/script/transition.js.map +1 -1
  262. package/dist/usecases/script/type.d.ts +2 -2
  263. package/dist/usecases/script/type.js +3 -4
  264. package/dist/usecases/script/type.js.map +1 -1
  265. package/dist/usecases/script/v4/queries.d.ts +3 -2
  266. package/dist/usecases/script/v4/queries.js +43 -6
  267. package/dist/usecases/script/v4/queries.js.map +1 -1
  268. package/dist/usecases/script/working-copy.d.ts +4 -4
  269. package/dist/usecases/script/working-copy.js +96 -92
  270. package/dist/usecases/script/working-copy.js.map +1 -1
  271. package/dist/usecases/script/worldview.d.ts +2 -2
  272. package/dist/usecases/script/worldview.js +3 -4
  273. package/dist/usecases/script/worldview.js.map +1 -1
  274. package/dist/usecases/script/write-authoring.d.ts +2 -2
  275. package/dist/usecases/script/write-authoring.js +62 -81
  276. package/dist/usecases/script/write-authoring.js.map +1 -1
  277. package/dist/usecases/update.d.ts +2 -2
  278. package/dist/usecases/update.js +32 -47
  279. package/dist/usecases/update.js.map +1 -1
  280. package/package.json +1 -1
  281. package/skills/scriptctl/SKILL.md +46 -37
  282. package/skills/scriptctl/references/atomic-edit-workflow.md +6 -6
  283. package/skills/scriptctl/references/ingest-workflow.md +20 -20
  284. package/skills/scriptctl/references/state-reference-repair.md +6 -6
  285. package/dist/help-text.d.ts +0 -2
  286. package/dist/help-text.js +0 -1144
  287. package/dist/help-text.js.map +0 -1
package/dist/cli.js CHANGED
@@ -2,635 +2,185 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { Command, CommanderError } from "commander";
5
- import { CliError, EXIT_OK, EXIT_USAGE, scriptJsonPath } from "./common.js";
6
- import { getHelp } from "./help-text.js";
5
+ import { CliError, EXIT_OK, EXIT_USAGE } from "./common.js";
6
+ import { registerBatchCommands } from "./cli/register-batch.js";
7
+ import { registerEditCommands } from "./cli/register-edits.js";
8
+ import { registerQueryCommands } from "./cli/register-query.js";
9
+ import { registerToolCommands } from "./cli/register-tools.js";
10
+ import { registerWorkflowCommands } from "./cli/register-workflows.js";
7
11
  import { loadLocalEnv } from "./infra/env.js";
8
- import { emit, emitError } from "./output.js";
9
12
  import { PRODUCTION_LLM_COMPOSITION } from "./llm/composition.js";
10
- import { commandIngest } from "./usecases/ingest/command.js";
11
- import { commandIngestStatus } from "./usecases/ingest/status.js";
12
- import { commandView } from "./usecases/ingest/view-command.js";
13
- import { withScriptFileLock } from "./infra/script-working-copy.js";
14
- import { commandDoctor } from "./usecases/doctor.js";
15
- import { commandUpdate } from "./usecases/update.js";
16
- import { commandChangelog } from "./usecases/changelog.js";
17
- import { commandActions } from "./usecases/script/actions.js";
18
- import { commandActor } from "./usecases/script/actor.js";
19
- import { commandActors } from "./usecases/script/actors.js";
20
- import { commandAddEpisode } from "./usecases/script/add-episode.js";
21
- import { commandAddLocation } from "./usecases/script/add-location.js";
22
- import { commandAddProp } from "./usecases/script/add-prop.js";
23
- import { commandAddActor } from "./usecases/script/add-actor.js";
24
- import { commandAlias } from "./usecases/script/alias.js";
25
- import { commandAssets } from "./usecases/script/assets.js";
26
- import { commandAuditAssets } from "./usecases/script/audit-assets.js";
27
- import { commandContext } from "./usecases/script/context.js";
28
- import { commandCreate } from "./usecases/script/create.js";
29
- import { commandDelete } from "./usecases/script/delete.js";
30
- import { commandDescribe } from "./usecases/script/describe.js";
31
- import { commandExtend } from "./usecases/script/extend.js";
32
- import { commandDialogue } from "./usecases/script/dialogue.js";
33
- import { commandImportance } from "./usecases/script/importance.js";
34
- import { commandDo } from "./usecases/script/do.js";
35
- import { commandEmotion } from "./usecases/script/emotion.js";
36
- import { commandParenthetical } from "./usecases/script/parenthetical.js";
37
- import { commandEpisodes } from "./usecases/script/episodes.js";
38
- import { commandInsert } from "./usecases/script/insert.js";
39
- import { commandIssues } from "./usecases/script/issues.js";
40
- import { commandLocations } from "./usecases/script/locations.js";
41
- import { commandMerge } from "./usecases/script/merge.js";
42
- import { commandMove } from "./usecases/script/move.js";
43
- import { commandProps } from "./usecases/script/props.js";
44
- import { commandRefs } from "./usecases/script/refs.js";
45
- import { commandRename } from "./usecases/script/rename.js";
46
- import { commandReplace } from "./usecases/script/replace.js";
47
- import { commandRole } from "./usecases/script/role.js";
48
- import { commandScenes } from "./usecases/script/scenes.js";
49
- import { commandScriptPatch } from "./usecases/script/script-patch.js";
50
- import { commandScriptValidate } from "./usecases/script/script-validate.js";
51
- import { commandSplit } from "./usecases/script/split.js";
52
- import { commandStates } from "./usecases/script/states.js";
53
- import { commandStateAdd } from "./usecases/script/state-add.js";
54
- import { commandStateDelete } from "./usecases/script/state-delete.js";
55
- import { commandStateRename } from "./usecases/script/state-rename.js";
56
- import { commandSummary } from "./usecases/script/summary.js";
57
- import { commandSynopsis } from "./usecases/script/synopsis.js";
58
- import { commandSynopsisGenerate } from "./usecases/script/synopsis-generate.js";
59
- import { commandTransition } from "./usecases/script/transition.js";
60
- import { commandType } from "./usecases/script/type.js";
61
- import { commandWorldview } from "./usecases/script/worldview.js";
62
- import { commandWriteAuthoring } from "./usecases/script/write-authoring.js";
63
- import { commandExportAuthoring } from "./usecases/script/export-authoring.js";
64
- import { commandIds } from "./usecases/script/ids.js";
65
- import { commandCheckout, commandWorkingCopyPublish, commandWorkingCopyStatus, } from "./usecases/script/working-copy.js";
66
- function packageRoot() {
67
- const here = fileURLToPath(import.meta.url);
68
- return path.resolve(path.dirname(here), "..");
69
- }
13
+ import { emitError } from "./output.js";
70
14
  function readPackageVersion() {
71
- try {
72
- const pkgPath = path.join(packageRoot(), "package.json");
73
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
74
- return pkg.version ?? "0.0.0";
75
- }
76
- catch {
77
- return "0.0.0";
78
- }
79
- }
80
- const KNOWN_COMMANDS = new Set([
81
- "doctor",
82
- "update",
83
- "changelog",
84
- // Workflow
85
- "ingest",
86
- "ingest.status",
87
- "view",
88
- "checkout",
89
- "status",
90
- "publish",
91
- "write",
92
- "ids",
93
- "export",
94
- "export.script",
95
- "export.episodes",
96
- "export.episode-spans",
97
- "export.scenes",
98
- "export.scene-spans",
99
- "export.action-spans",
100
- // Query (plural-noun)
101
- "summary", "episodes", "scenes", "actions", "actors", "locations", "props",
102
- "assets", "states", "issues", "refs", "validate", "audit-assets",
103
- // Create + add entities
104
- "create", "add-actor", "add-location", "add-prop", "add-episode",
105
- // Edit content
106
- "replace", "type", "actor", "emotion", "parenthetical",
107
- // Structure (multipolar)
108
- "insert", "delete", "move", "split", "merge",
109
- // Scene refs & transitions on actions
110
- "transition", "scene-ref",
111
- // Asset metadata
112
- "rename", "describe", "alias", "role", "importance", "worldview", "synopsis", "synopsis.generate",
113
- // Free-form namespace (any dimension)
114
- "extend",
115
- // State management
116
- "state-add", "state-rename", "state-delete",
117
- // Dialogue speaker / patch
118
- "dialogue",
119
- "patch", "do",
120
- ]);
121
- // Groups are command nodes that exist only to host subcommands. Extra non-flag
122
- // tokens after a group path indicate an unknown subcommand (e.g. `direct nope` → error).
123
- // Leaves are real commands that can accept positional arguments.
124
- const GROUP_COMMANDS = new Set(["export"]);
125
- function extractLeadingPath(args) {
126
- // Collect leading non-flag tokens, then take only as many as form the longest known
127
- // command prefix. Trailing tokens are treated as either:
128
- // - positional arguments (when the matched node is a leaf, e.g. `episode draft 1`)
129
- // - unknown subcommands (when the matched node is a group, e.g. `direct nope`)
130
- const allNonFlag = [];
131
- for (const tok of args) {
132
- if (tok.startsWith("-"))
133
- break;
134
- allNonFlag.push(tok);
135
- if (allNonFlag.length >= 5)
136
- break;
137
- }
138
- const matchLen = longestKnownPrefix(allNonFlag);
139
- if (matchLen === 0) {
140
- // No known command at all — return up to 3 tokens for a readable error.
141
- return allNonFlag.slice(0, 3);
142
- }
143
- const matchedKey = allNonFlag.slice(0, matchLen).join(".");
144
- const isGroup = GROUP_COMMANDS.has(matchedKey);
145
- if (isGroup && allNonFlag.length > matchLen) {
146
- // Group + extra tokens = unknown subcommand. Return the truncated path so the
147
- // validator (knownLen < cmdPath.length) reports an error.
148
- return allNonFlag.slice(0, Math.min(allNonFlag.length, 3));
149
- }
150
- return allNonFlag.slice(0, matchLen);
151
- }
152
- function longestKnownPrefix(path) {
153
- for (let len = path.length; len > 0; len--) {
154
- if (KNOWN_COMMANDS.has(path.slice(0, len).join(".")))
155
- return len;
156
- }
157
- return 0;
158
- }
159
- function commandPath(cmd) {
160
- const parts = [];
161
- let c = cmd;
162
- while (c) {
163
- const name = c.name();
164
- if (!name || name === "scriptctl")
165
- break;
166
- parts.unshift(name);
167
- c = c.parent;
168
- }
169
- return parts;
170
- }
171
- function camelToSnake(s) {
172
- return s.replace(/([A-Z])/g, "_$1").toLowerCase();
173
- }
174
- function normalizeOpts(opts) {
175
- const out = {};
176
- for (const [k, v] of Object.entries(opts))
177
- out[camelToSnake(k)] = v;
178
- return out;
179
- }
180
- function mergedCommandOpts(cmd) {
181
- const chain = [];
182
- let current = cmd;
183
- while (current) {
184
- chain.unshift(current);
185
- current = current.parent;
186
- }
187
- const merged = {};
188
- for (const item of chain) {
189
- Object.assign(merged, item.opts());
190
- }
191
- return merged;
192
- }
193
- const SCRIPT_ACCESS = new WeakMap();
194
- function dispatch(handler, state) {
195
- return async (...args) => {
196
- const cmd = args[args.length - 1];
197
- const positionals = args.slice(0, -2).filter((p) => p !== undefined);
198
- const normalized = normalizeOpts(mergedCommandOpts(cmd));
199
- if (positionals.length > 0)
200
- normalized._args = positionals;
201
- const invoke = async () => handler(normalized);
202
- const result = SCRIPT_ACCESS.get(cmd) === "write"
203
- ? await withScriptFileLock(scriptJsonPath(normalized), invoke)
204
- : await invoke();
205
- const [report, exitCode] = result;
206
- emit(report, exitCode, state.jsonMode, state.format);
207
- state.exitCode = exitCode;
208
- };
209
- }
210
- function showGroupHelp(commandPathParts, state) {
211
- return () => {
212
- process.stdout.write(getHelp(commandPathParts) ?? getHelp([]) ?? "");
213
- state.exitCode = EXIT_OK;
214
- };
215
- }
216
- const appendStr = (value, prev) => [...prev, value];
217
- function withScriptOpts(cmd, access = "write") {
218
- SCRIPT_ACCESS.set(cmd, access);
219
- return cmd.option("--script-path <path>");
220
- }
221
- function withWorkingCopyOpts(cmd) {
222
- SCRIPT_ACCESS.set(cmd, "write");
223
- return cmd
224
- .option("--script-path <path>")
225
- .option("--project-group-no <no>");
226
- }
227
- // Selection flags shared by query commands and selector-driven write verbs:
228
- // the SAME vocabulary reads and writes. `--in` scopes by address (range /
229
- // region / '*' = whole script); the predicates AND-refine within scope.
230
- // On write verbs, `--apply` commits (default is a dry-run that reports the
231
- // blast radius and writes nothing); `--dry-run` is accepted as a no-op (it is
232
- // already the default) for symmetry.
233
- function withSelection(cmd) {
234
- return cmd
235
- .option("--in <selector>")
236
- .option("--type <type>")
237
- .option("--actor <id>")
238
- .option("--grep <pattern>")
239
- .option("--has <facet>")
240
- .option("--apply")
241
- .option("--plan <digest>")
242
- .option("--dry-run");
15
+ const here = fileURLToPath(import.meta.url);
16
+ const packagePath = path.resolve(path.dirname(here), "..", "package.json");
17
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
18
+ return packageJson.version;
243
19
  }
244
- function buildProgram(state, llm) {
20
+ export function buildProgram(state, llm = PRODUCTION_LLM_COMPOSITION) {
245
21
  const program = new Command("scriptctl")
22
+ .description("Read, edit, ingest, and publish structured scripts")
23
+ .usage("<command> [options]")
246
24
  .exitOverride()
247
25
  .allowExcessArguments(false)
248
26
  .allowUnknownOption(false)
27
+ .showHelpAfterError(false)
249
28
  .configureOutput({
250
- writeOut: (str) => process.stdout.write(str),
29
+ writeOut: (text) => process.stdout.write(text),
251
30
  writeErr: () => {
252
- /* swallowed; we emit our own envelope */
31
+ // Commander errors are emitted once through the scriptctl error model.
253
32
  },
254
33
  })
255
- .configureHelp({
256
- formatHelp: (cmd) => getHelp(commandPath(cmd)) ?? getHelp([]) ?? "",
257
- });
258
- program.command("doctor")
259
- .option("--llm-config <path>")
260
- .action(dispatch((o) => commandDoctor(o), state));
261
- program.command("update")
262
- .option("--dry-run")
263
- .option("--patch")
264
- .action(dispatch((o) => commandUpdate(o), state));
265
- program.command("changelog [version]")
266
- .option("--from <version>")
267
- .option("--to <version>")
268
- .option("--unreleased")
269
- .action(dispatch((o) => commandChangelog(o), state));
270
- const ingest = program.command("ingest")
271
- .option("--source-path <path>")
272
- .option("--workspace-path <path>", "", "workspace")
273
- .option("--llm-config <path>")
274
- .option("--concurrency <n>")
275
- .option("--fps <n>")
276
- .option("--force")
277
- .action(dispatch((options) => commandIngest(options, llm), state));
278
- // `ingest status` — derive progress from the workspace's real file state (no
279
- // model, no store). The runtime watcher / frontend consume `--json`.
280
- // NB: --workspace-path is inherited from the parent `ingest` command — if the
281
- // subcommand redeclared it, commander would attribute the user's value to the
282
- // parent and clobber it with the subcommand's default in mergedCommandOpts.
283
- ingest.command("status")
284
- .action(dispatch((options) => commandIngestStatus(options), state));
285
- program.command("view")
286
- .option("--workspace-path <path>", "", "workspace")
287
- .option("--watch")
288
- .option("--port <n>")
289
- .action(dispatch((options) => commandView(options), state));
290
- withWorkingCopyOpts(program.command("checkout"))
291
- .option("--discard-local")
292
- .action(dispatch(commandCheckout, state));
293
- withScriptOpts(program.command("status"))
294
- .action(dispatch(commandWorkingCopyStatus, state));
295
- withWorkingCopyOpts(program.command("publish"))
296
- .action(dispatch(commandWorkingCopyPublish, state));
297
- withScriptOpts(program.command("write <file>"))
298
- .option("--create")
299
- .option("--replace")
300
- .option("--apply")
301
- .option("--plan <digest>")
302
- .action(dispatch(commandWriteAuthoring, state));
303
- withScriptOpts(program.command("ids"), "read")
304
- .requiredOption("--kind <kind>")
305
- .requiredOption("--count <n>")
306
- .action(dispatch(commandIds, state));
307
- const exportCommand = program.command("export")
308
- .action(showGroupHelp(["export"], state));
309
- withScriptOpts(exportCommand.command("script"), "read")
310
- .requiredOption("--output <file>")
311
- .action(dispatch((opts) => commandExportAuthoring("script", opts), state));
312
- for (const kind of ["episodes", "episode-spans", "scenes", "scene-spans", "action-spans"]) {
313
- withScriptOpts(exportCommand.command(kind), "read")
314
- .requiredOption("--in <selector>")
315
- .requiredOption("--output <file>")
316
- .action(dispatch((opts) => commandExportAuthoring(kind, opts), state));
317
- }
318
- // ============================== query (plural-noun) ==============================
319
- withScriptOpts(program.command("summary"), "read")
320
- .action(dispatch(commandSummary, state));
321
- withScriptOpts(program.command("episodes"), "read")
322
- .option("--id <id>")
323
- .option("--in <addr>")
324
- .option("--min-chars <n>")
325
- .option("--max-chars <n>")
326
- .action(dispatch(commandEpisodes, state));
327
- withScriptOpts(program.command("scenes"), "read")
328
- .option("--in <addr>")
329
- .option("--has-actor <id>")
330
- .option("--has-location <id>")
331
- .option("--has-prop <id>")
332
- .action(dispatch(commandScenes, state));
333
- withScriptOpts(program.command("actions"), "read")
334
- .option("--in <addr>")
335
- .option("--grep <text>")
336
- .option("--type <type>")
337
- .option("--actor <id>")
338
- .option("--has <kind>")
339
- .option("--context <n>")
340
- .action(dispatch(commandActions, state));
341
- withScriptOpts(program.command("actors"), "read")
342
- .option("--id <id>")
343
- .option("--name <text>")
344
- .option("--in <addr>")
345
- .option("--counts")
346
- .action(dispatch(commandActors, state));
347
- withScriptOpts(program.command("locations"), "read")
348
- .option("--id <id>")
349
- .option("--name <text>")
350
- .option("--in <addr>")
351
- .option("--counts")
352
- .action(dispatch(commandLocations, state));
353
- withScriptOpts(program.command("props"), "read")
354
- .option("--id <id>")
355
- .option("--name <text>")
356
- .option("--in <addr>")
357
- .option("--counts")
358
- .action(dispatch(commandProps, state));
359
- withScriptOpts(program.command("assets"), "read")
360
- .option("--kind <kind>")
361
- .option("--id <id>")
362
- .option("--name <text>")
363
- .option("--in <addr>")
364
- .option("--counts")
365
- .action(dispatch(commandAssets, state));
366
- withScriptOpts(program.command("states <asset-addr>"), "read")
367
- .action(dispatch(commandStates, state));
368
- withScriptOpts(program.command("issues"), "read")
369
- .option("--severity <severity>")
370
- .option("--code <code>")
371
- .action(dispatch(commandIssues, state));
372
- withScriptOpts(program.command("refs <addr>"), "read")
373
- .option("--level <level>")
374
- .action(dispatch(commandRefs, state));
375
- withScriptOpts(program.command("validate"), "read")
376
- .action(dispatch(commandScriptValidate, state));
377
- withScriptOpts(program.command("audit-assets"), "read")
378
- .option("--llm-config <path>")
379
- .option("--output <path>")
380
- .action(dispatch((options) => commandAuditAssets(options, llm), state));
381
- // ============================== create + add entities ==============================
382
- withScriptOpts(program.command("create"))
383
- .option("--title <title>")
384
- .option("--force")
385
- .action(dispatch(commandCreate, state));
386
- withScriptOpts(program.command("add-actor <name>"))
387
- .option("--id <id>")
388
- .option("--role <role>")
389
- .option("--description <text>")
390
- .option("--alias <alias>", "", appendStr, [])
391
- .action(dispatch(commandAddActor, state));
392
- withScriptOpts(program.command("add-location <name>"))
393
- .option("--id <id>")
394
- .option("--description <text>")
395
- .action(dispatch(commandAddLocation, state));
396
- withScriptOpts(program.command("add-prop <name>"))
397
- .option("--id <id>")
398
- .option("--description <text>")
399
- .action(dispatch(commandAddProp, state));
400
- withScriptOpts(program.command("add-episode"))
401
- .option("--id <id>")
402
- .option("--title <title>")
403
- .action(dispatch(commandAddEpisode, state));
404
- // ============================== edit verbs ==============================
405
- // ----- content (single-point address OR a selection for a sweep) -----
406
- withSelection(withScriptOpts(program.command("replace [at]")))
407
- .requiredOption("--from <text>")
408
- .option("--to <text>", "", "")
409
- .option("--all")
410
- .option("--regex")
411
- .action(dispatch(commandReplace, state));
412
- withSelection(withScriptOpts(program.command("type [at] [type]")))
413
- .action(dispatch(commandType, state));
414
- withSelection(withScriptOpts(program.command("actor [at] [actor-id]")))
415
- .action(dispatch(commandActor, state));
416
- withSelection(withScriptOpts(program.command("emotion [at] [emotion]")))
417
- .option("--clear")
418
- .action(dispatch(commandEmotion, state));
419
- withSelection(withScriptOpts(program.command("parenthetical [at]")))
420
- .option("--set <text>")
421
- .option("--remove")
422
- .action(dispatch(commandParenthetical, state));
423
- // ----- structure (multipolar by first-arg address format) -----
424
- withScriptOpts(program.command("insert <addr>"))
425
- .option("--type <type>")
426
- .option("--content <text>")
427
- .option("--emotion <emotion>")
428
- .option("--parenthetical <text>")
429
- .option("--action-id <id>")
430
- .option("--at <idx>")
431
- .option("--before <addr>")
432
- .option("--after <addr>")
433
- .option("--actor <id>")
434
- .option("--speaker-state <id>")
435
- .option("--kind <kind>", "non-actor speaker: system|broadcast|offscreen|group")
436
- .option("--label <text>", "optional label for a non-actor speaker")
437
- .option("--location <id>")
438
- .option("--time <time>")
439
- .option("--space <space>")
440
- .option("--scene-id <id>")
441
- .option("--state <id>", "", appendStr, [])
442
- .action(dispatch(commandInsert, state));
443
- withScriptOpts(program.command("delete <addr>"))
444
- .option("--strategy <strategy>")
445
- .option("--replacement <addr>")
446
- .option("--force")
447
- .action(dispatch(commandDelete, state));
448
- withScriptOpts(program.command("move <addr> <to>"))
449
- .option("--at <idx>")
450
- .option("--before <id>")
451
- .option("--after <id>")
452
- .action(dispatch(commandMove, state));
453
- withScriptOpts(program.command("split <ep/scn>"))
454
- .requiredOption("--at <index-or-action-id>")
455
- .option("--new-scene-id <id>")
456
- .action(dispatch(commandSplit, state));
457
- withScriptOpts(program.command("merge <src>"))
458
- .requiredOption("--into <dst>")
459
- .action(dispatch(commandMerge, state));
460
- // ----- transition / scene refs -----
461
- withSelection(withScriptOpts(program.command("transition [at] [target]")))
462
- .option("--process <text>")
463
- .option("--contrast <text>")
464
- .option("--clear")
465
- .action(dispatch(commandTransition, state));
466
- withScriptOpts(program.command("scene-ref <at> <target>"))
467
- .option("--state <id>", "", appendStr, [])
468
- .option("--clear")
469
- .option("--remove")
470
- .option("--append")
471
- .option("--occurrence <n>")
472
- .action(dispatch(commandContext, state));
473
- // ----- asset metadata -----
474
- withScriptOpts(program.command("rename <target> <name>"))
475
- .action(dispatch(commandRename, state));
476
- withScriptOpts(program.command("describe <target> <text>"))
477
- .action(dispatch(commandDescribe, state));
478
- withScriptOpts(program.command("alias <target>"))
479
- .option("--add <alias>", "", appendStr, [])
480
- .option("--remove <alias>", "", appendStr, [])
481
- .action(dispatch(commandAlias, state));
482
- withScriptOpts(program.command("role <target> <role>"))
483
- .action(dispatch(commandRole, state));
484
- withScriptOpts(program.command("importance <target> <importance>"))
485
- .action(dispatch(commandImportance, state));
486
- withScriptOpts(program.command("worldview <value>"))
487
- .action(dispatch(commandWorldview, state));
488
- const synopsis = withScriptOpts(program.command("synopsis [target-or-text] [text]"))
489
- .action(dispatch(commandSynopsis, state));
490
- withScriptOpts(synopsis.command("generate"))
491
- .option("--llm-config <path>")
492
- .option("--concurrency <n>", "", "30")
493
- .option("--checkpoint <dir>")
494
- .option("--overwrite")
495
- .option("--apply")
496
- .option("--dry-run")
497
- .action(dispatch((options) => commandSynopsisGenerate(options, llm), state));
498
- // NB: the value-parsing flag is `--json-value`, NOT `--json` — the global
499
- // `--json` output-mode flag is stripped from argv before commander runs (see
500
- // main()), so it can never reach this subcommand.
501
- withScriptOpts(program.command("extend <addr> [key] [value]"))
502
- .option("--json-value", "parse value as JSON (number/bool/object/array)")
503
- .option("--clear", "delete the key (or the whole container if no key)")
504
- .action(dispatch(commandExtend, state));
505
- // ----- state management -----
506
- withScriptOpts(program.command("state-add <target> <name>"))
507
- .option("--description <text>", "", "")
508
- .option("--state-id <id>")
509
- .action(dispatch(commandStateAdd, state));
510
- withScriptOpts(program.command("state-rename <target> <name>"))
511
- .action(dispatch(commandStateRename, state));
512
- withScriptOpts(program.command("state-delete <target>"))
513
- .requiredOption("--strategy <strategy>")
514
- .option("--replacement <id>")
515
- .action(dispatch(commandStateDelete, state));
516
- // ----- dialogue / inner-thought inline speaker -----
517
- withScriptOpts(program.command("dialogue <at>"))
518
- .option("--actor <id>", "set the line's speaker to an actor")
519
- .option("--state <id>", "v4 optional actor state from this scene's state set")
520
- .option("--kind <kind>", "non-actor speaker: system|broadcast|offscreen|group")
521
- .option("--label <text>", "optional label for a non-actor speaker")
522
- .action(dispatch(commandDialogue, state));
523
- // ----- do (verb-script transaction) -----
524
- withScriptOpts(program.command("do [file]"))
525
- .option("--apply")
526
- .option("--plan <digest>")
527
- .option("--dry-run")
528
- .action(dispatch(commandDo, state));
529
- // ----- patch (machine / jq op interchange) -----
530
- // <file> is OPTIONAL because --schema dumps op schemas without a file.
531
- // When --schema is absent the patch / dry-run paths require <file>; the
532
- // handler enforces that.
533
- withScriptOpts(program.command("patch [file]"))
534
- .option("--schema [op]")
535
- .option("--dry-run")
536
- .option("--apply")
537
- .option("--plan <digest>")
538
- .action(dispatch((o) => {
539
- const args = o["_args"] || [];
540
- if (args[0])
541
- o["patch"] = args[0];
542
- return commandScriptPatch(o);
543
- }, state));
34
+ .option("-j, --json", "Output structured JSON")
35
+ .option("--format <format>", "Output format (addr)")
36
+ .version(readPackageVersion(), "-v, --version", "Show the scriptctl version");
37
+ registerWorkflowCommands(program, state, llm);
38
+ registerQueryCommands(program, state, llm);
39
+ registerEditCommands(program, state, llm);
40
+ registerBatchCommands(program, state);
41
+ registerToolCommands(program, state);
544
42
  return program;
545
43
  }
546
- export async function main(argv = null, llm = PRODUCTION_LLM_COMPOSITION) {
547
- loadLocalEnv();
548
- let args = argv ?? process.argv.slice(2);
549
- // `--format <fmt>` is a global output toggle (like --json). `addr` emits a bare
550
- // newline-delimited address stream from a query's rows[] clean xargs input.
551
- let format;
552
- for (let i = 0; i < args.length; i++) {
553
- const a = args[i];
554
- if (a.startsWith("--format=")) {
555
- format = a.slice("--format=".length);
556
- args.splice(i, 1);
557
- i--;
44
+ function optionFor(command, token) {
45
+ const flag = token.split("=", 1)[0];
46
+ let current = command;
47
+ while (current) {
48
+ const option = current.options.find((candidate) => candidate.short === flag || candidate.long === flag);
49
+ if (option)
50
+ return option;
51
+ current = current.parent;
52
+ }
53
+ return undefined;
54
+ }
55
+ function scanShortOptions(command, token) {
56
+ if (!token.startsWith("-") || token.startsWith("--") || token.length < 2) {
57
+ return { recognized: false, json: false, hasAttachedValue: false };
58
+ }
59
+ let json = false;
60
+ for (let index = 1; index < token.length; index += 1) {
61
+ const option = optionFor(command, `-${token[index]}`);
62
+ if (!option?.short) {
63
+ return { recognized: false, json: false, hasAttachedValue: false };
558
64
  }
559
- else if (a === "--format") {
560
- const v = args[i + 1];
561
- // Only consume the next token as the value when it isn't itself a flag —
562
- // otherwise `--format --json` would silently eat --json.
563
- if (v !== undefined && !v.startsWith("-")) {
564
- format = v;
565
- args.splice(i, 2);
566
- }
567
- else
568
- args.splice(i, 1);
569
- i--;
65
+ if (option.short === "-j")
66
+ json = true;
67
+ if (option.required || option.optional) {
68
+ return {
69
+ recognized: true,
70
+ json,
71
+ valueOption: option,
72
+ hasAttachedValue: index < token.length - 1,
73
+ };
570
74
  }
571
75
  }
572
- const state = { jsonMode: args.includes("--json"), exitCode: EXIT_OK, format };
573
- args = args.filter((a) => a !== "--json");
574
- if (args.includes("--version")) {
575
- process.stdout.write(`${readPackageVersion()}\n`);
576
- return EXIT_OK;
76
+ return { recognized: true, json, hasAttachedValue: false };
77
+ }
78
+ function isRecognizedOptionToken(command, token) {
79
+ if (token === "--")
80
+ return true;
81
+ if (token.startsWith("--"))
82
+ return optionFor(command, token) !== undefined;
83
+ return scanShortOptions(command, token).recognized;
84
+ }
85
+ function consumesNextValue(command, option, next) {
86
+ if (next === undefined || next === "--")
87
+ return false;
88
+ if (option.required)
89
+ return !isRecognizedOptionToken(command, next);
90
+ return option.optional && !next.startsWith("-");
91
+ }
92
+ function initialJsonMode(program, args) {
93
+ let command = program;
94
+ for (let index = 0; index < args.length; index += 1) {
95
+ const token = args[index];
96
+ if (token === "--")
97
+ break;
98
+ if (token.startsWith("--")) {
99
+ if (token === "--json")
100
+ return true;
101
+ const option = optionFor(command, token);
102
+ if (option
103
+ && !token.includes("=")
104
+ && consumesNextValue(command, option, args[index + 1]))
105
+ index += 1;
106
+ continue;
107
+ }
108
+ if (token.startsWith("-")) {
109
+ const scanned = scanShortOptions(command, token);
110
+ if (scanned.recognized && scanned.json)
111
+ return true;
112
+ if (scanned.recognized
113
+ && scanned.valueOption
114
+ && !scanned.hasAttachedValue
115
+ && consumesNextValue(command, scanned.valueOption, args[index + 1]))
116
+ index += 1;
117
+ continue;
118
+ }
119
+ const child = command.commands.find((candidate) => candidate.name() === token);
120
+ if (child)
121
+ command = child;
577
122
  }
578
- const wantsHelp = args.includes("--help") || args.includes("-h");
579
- const cmdPath = extractLeadingPath(args);
580
- if (cmdPath.length === 0) {
581
- if (args.length === 0 || wantsHelp) {
582
- process.stdout.write(getHelp([]) ?? "");
583
- return EXIT_OK;
123
+ return false;
124
+ }
125
+ function helpTarget(program, args) {
126
+ let command = program;
127
+ for (let index = 0; index < args.length; index += 1) {
128
+ const token = args[index];
129
+ if (token.startsWith("-")) {
130
+ const option = optionFor(command, token);
131
+ if (option && !token.includes("=") && (option.required || option.optional))
132
+ index += 1;
133
+ continue;
584
134
  }
585
- return emitError(new CliError("USAGE ERROR: Unknown command", "Unknown command.", {
586
- exitCode: EXIT_USAGE,
587
- required: ["valid scriptctl command"],
588
- received: [args.join(" ") || "<empty>"],
589
- nextSteps: ["Run: scriptctl --help"],
590
- }), state.jsonMode);
135
+ const child = command.commands.find((candidate) => candidate.name() === token);
136
+ if (!child)
137
+ break;
138
+ command = child;
591
139
  }
592
- const knownLen = longestKnownPrefix(cmdPath);
593
- if (knownLen < cmdPath.length) {
594
- return emitError(new CliError("USAGE ERROR: Unknown command", "Unknown command.", {
595
- exitCode: EXIT_USAGE,
596
- required: ["valid scriptctl command"],
597
- received: [cmdPath.join(" ")],
598
- nextSteps: ["Run: scriptctl --help"],
599
- }), state.jsonMode);
140
+ const names = [];
141
+ let current = command;
142
+ while (current && current.parent) {
143
+ names.unshift(current.name());
144
+ current = current.parent;
600
145
  }
601
- if (wantsHelp) {
602
- process.stdout.write(getHelp(cmdPath) ?? getHelp([]) ?? "");
146
+ return ["scriptctl", ...names, "--help"].join(" ");
147
+ }
148
+ function commanderMessage(error) {
149
+ return error.message.replace(/^error:\s*/i, "").replace(/\.$/, "");
150
+ }
151
+ export async function main(argv = null, llm = PRODUCTION_LLM_COMPOSITION) {
152
+ loadLocalEnv();
153
+ const args = argv ?? process.argv.slice(2);
154
+ const state = { jsonMode: false, exitCode: EXIT_OK };
155
+ const program = buildProgram(state, llm);
156
+ state.jsonMode = initialJsonMode(program, args);
157
+ if (args.length === 0) {
158
+ program.outputHelp();
603
159
  return EXIT_OK;
604
160
  }
605
- const program = buildProgram(state, llm);
606
161
  try {
607
162
  await program.parseAsync(args, { from: "user" });
608
163
  return state.exitCode;
609
164
  }
610
- catch (err) {
611
- if (err instanceof CommanderError) {
612
- const code = err.code;
613
- if (code === "commander.help" ||
614
- code === "commander.helpDisplayed" ||
615
- code === "commander.version") {
165
+ catch (error) {
166
+ if (error instanceof CommanderError) {
167
+ if (error.code === "commander.help"
168
+ || error.code === "commander.helpDisplayed"
169
+ || error.code === "commander.version") {
616
170
  return EXIT_OK;
617
171
  }
618
- const message = err.message || "Invalid arguments.";
619
- return emitError(new CliError("USAGE ERROR: Invalid arguments", "Invalid arguments.", {
172
+ return emitError(new CliError(commanderMessage(error), {
620
173
  exitCode: EXIT_USAGE,
621
- required: ["Run: scriptctl --help"],
622
- received: [message],
623
- nextSteps: ["Run: scriptctl --help"],
174
+ errorCode: "USAGE_ERROR",
175
+ hint: `Run '${helpTarget(program, args)}'.`,
624
176
  }), state.jsonMode);
625
177
  }
626
- if (err instanceof CliError)
627
- return emitError(err, state.jsonMode);
628
- const e = err;
629
- return emitError(new CliError("RUNTIME FAILED: Unexpected error", "Unexpected error.", {
178
+ if (error instanceof CliError)
179
+ return emitError(error, state.jsonMode);
180
+ const message = error instanceof Error ? error.message : String(error);
181
+ return emitError(new CliError(message, {
630
182
  exitCode: 70,
631
- required: ["successful command execution"],
632
- received: [e?.name ?? "Error"],
633
- nextSteps: ["Rerun with valid inputs or inspect workspace artifacts."],
183
+ errorCode: "RUNTIME_ERROR",
634
184
  }), state.jsonMode);
635
185
  }
636
186
  }