* Shared execution tail for any already-resolved choice (persisted via * `quickadd:run` or built ad-hoc via `quickadd:run-template`): variable wiring, * the non-interactive missing-input guard, execution, and the JSON envelope.
( plugin: QuickAdd, params: CliData, command: string, choice: IChoice, reservedParams: Set<string>, /** * Report the real execution outcome via executeWithOutcome (Template/Capture * only) instead of trusting that the void execute() resolving means success. * The Template/Capture engines swallow runtime failures (missing/empty file * name, failing inline script, write error) — without this the CLI would * report ok:true with no file created. quickadd:run keeps the legacy path. */ useOutcome = false, )
| 342 | * the non-interactive missing-input guard, execution, and the JSON envelope. |
| 343 | */ |
| 344 | async function runResolvedChoice( |
| 345 | plugin: QuickAdd, |
| 346 | params: CliData, |
| 347 | command: string, |
| 348 | choice: IChoice, |
| 349 | reservedParams: Set<string>, |
| 350 | /** |
| 351 | * Report the real execution outcome via executeWithOutcome (Template/Capture |
| 352 | * only) instead of trusting that the void execute() resolving means success. |
| 353 | * The Template/Capture engines swallow runtime failures (missing/empty file |
| 354 | * name, failing inline script, write error) — without this the CLI would |
| 355 | * report ok:true with no file created. quickadd:run keeps the legacy path. |
| 356 | */ |
| 357 | useOutcome = false, |
| 358 | ): Promise<string> { |
| 359 | const startedAt = Date.now(); |
| 360 | |
| 361 | try { |
| 362 | if (choice.type === "Multi") { |
| 363 | return serialize({ |
| 364 | ok: false, |
| 365 | command, |
| 366 | error: "Multi choices are interactive and cannot be run via CLI.", |
| 367 | choice: describeChoice(choice), |
| 368 | }); |
| 369 | } |
| 370 | |
| 371 | const variables = extractVariables(params, reservedParams); |
| 372 | const choiceExecutor = new ChoiceExecutor( |
| 373 | plugin.app, |
| 374 | plugin, |
| 375 | ) as IChoiceExecutor; |
| 376 | setExecutorVariables(choiceExecutor, variables); |
| 377 | |
| 378 | const interactiveMode = isTruthy(params.ui); |
| 379 | // Without `ui`, engine prompts the requirement collector can't pre-satisfy |
| 380 | // (the "file already exists" prompt, folder chooser, heading picker) would |
| 381 | // hang forever on an unanswerable modal. Mark the run non-interactive so the |
| 382 | // engines abort those with a clear error instead. `ui` keeps full prompting. |
| 383 | choiceExecutor.interactive = interactiveMode; |
| 384 | if (!interactiveMode) { |
| 385 | const requirements = await collectChoiceRequirements( |
| 386 | plugin.app, |
| 387 | plugin, |
| 388 | choiceExecutor, |
| 389 | choice, |
| 390 | // Loading a user script to read quickadd.inputs executes its module |
| 391 | // body; cache it on the executor so the run below consumes this |
| 392 | // load instead of executing every script's top level twice. |
| 393 | { preloadedUserScripts: choiceExecutor.preloadedUserScripts }, |
| 394 | ); |
| 395 | const unresolved = getUnresolvedRequirements( |
| 396 | requirements, |
| 397 | choiceExecutor.variables, |
| 398 | ); |
| 399 | if (unresolved.length > 0) { |
| 400 | return serialize({ |
| 401 | ok: false, |
no test coverage detected