@promptbook/cli 0.114.0-33 → 0.114.0-35

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 (167) hide show
  1. package/esm/index.es.js +1254 -318
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.d.ts +25 -0
  4. package/esm/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
  5. package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +21 -3
  6. package/esm/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +1 -1
  7. package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +77 -0
  8. package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTracePath.d.ts +10 -0
  9. package/esm/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +11 -6
  10. package/esm/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +1 -1
  11. package/esm/scripts/run-codex-prompts/prompts/writePromptRunTrace.d.ts +19 -0
  12. package/esm/scripts/run-codex-prompts/runners/common/buildHarnessAuthenticationError.d.ts +20 -0
  13. package/esm/scripts/run-codex-prompts/runners/common/createAuthenticationAwarePromptRunner.d.ts +11 -0
  14. package/esm/scripts/run-codex-prompts/runners/common/extractHarnessAuthenticationFailureReason.d.ts +9 -0
  15. package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -0
  16. package/esm/src/book-2.0/book-language-documentation/getSafeCodeBlock.d.ts +1 -1
  17. package/esm/src/book-components/Chat/MockedChat/MockedChat.test.d.ts +2 -0
  18. package/esm/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.d.ts +2 -1
  19. package/esm/src/cli/cli-commands/coder/agentCliOptions.d.ts +21 -0
  20. package/esm/src/cli/cli-commands/coder/boilerplateTemplates.d.ts +8 -21
  21. package/esm/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +33 -0
  22. package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +4 -2
  23. package/esm/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.d.ts +2 -0
  24. package/esm/src/cli/cli-commands/coder/ensureDirectory.d.ts +6 -0
  25. package/esm/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.d.ts +6 -0
  26. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
  27. package/esm/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +19 -1
  28. package/esm/src/cli/cli-commands/coder/printCoderRunFailure.d.ts +6 -0
  29. package/esm/src/cli/cli-commands/common/$askForConfirmation.d.ts +4 -2
  30. package/esm/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.d.ts +12 -0
  31. package/esm/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.test.d.ts +1 -0
  32. package/esm/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.d.ts +11 -0
  33. package/esm/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.test.d.ts +1 -0
  34. package/esm/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.d.ts +33 -0
  35. package/esm/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.d.ts +6 -0
  36. package/esm/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.test.d.ts +1 -0
  37. package/esm/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.d.ts +10 -0
  38. package/esm/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.d.ts +28 -0
  39. package/esm/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.d.ts +7 -0
  40. package/esm/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.test.d.ts +1 -0
  41. package/esm/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +2 -1
  42. package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -2
  43. package/esm/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
  44. package/esm/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts +4 -2
  45. package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +2 -1
  46. package/esm/src/cli/cli-commands/common/questionsCliOptions.d.ts +45 -0
  47. package/esm/src/cli/cli-commands/common/questionsCliOptions.test.d.ts +1 -0
  48. package/esm/src/cli/common/$deprecateCliCommand.d.ts +3 -0
  49. package/esm/src/cli/common/$hideCliCommandFromHelp.d.ts +11 -0
  50. package/esm/src/cli/common/$hideCliCommandFromHelp.test.d.ts +1 -0
  51. package/esm/src/cli/common/$requireCliSubcommand.d.ts +18 -0
  52. package/esm/src/cli/common/$requireCliSubcommand.test.d.ts +1 -0
  53. package/esm/src/llm-providers/vercel/VercelExecutionToolsOptions.d.ts +5 -2
  54. package/esm/src/version.d.ts +1 -1
  55. package/package.json +7 -10
  56. package/src/book-2.0/book-language-documentation/getSafeCodeBlock.ts +1 -1
  57. package/src/book-components/Chat/MockedChat/MockedChat.tsx +25 -1
  58. package/src/cli/cli-commands/agent-folder.ts +2 -6
  59. package/src/cli/cli-commands/agent.ts +2 -6
  60. package/src/cli/cli-commands/agents-server.ts +2 -6
  61. package/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.ts +3 -0
  62. package/src/cli/cli-commands/coder/add.ts +27 -2
  63. package/src/cli/cli-commands/coder/agentCliOptions.ts +32 -0
  64. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +17 -44
  65. package/src/cli/cli-commands/coder/coderReferencedArtifacts.ts +106 -0
  66. package/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts +4 -2
  67. package/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.ts +5 -1
  68. package/src/cli/cli-commands/coder/ensureDirectory.ts +27 -1
  69. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +99 -10
  70. package/src/cli/cli-commands/coder/init.ts +23 -14
  71. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +18 -27
  72. package/src/cli/cli-commands/coder/list.ts +15 -3
  73. package/src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts +31 -7
  74. package/src/cli/cli-commands/coder/ping.ts +25 -22
  75. package/src/cli/cli-commands/coder/printCoderRunFailure.ts +14 -0
  76. package/src/cli/cli-commands/coder/printInitializationSummary.ts +23 -16
  77. package/src/cli/cli-commands/coder/run.ts +34 -23
  78. package/src/cli/cli-commands/coder/server.ts +43 -32
  79. package/src/cli/cli-commands/coder.ts +2 -6
  80. package/src/cli/cli-commands/common/$askForConfirmation.ts +12 -2
  81. package/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.ts +35 -0
  82. package/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.ts +37 -0
  83. package/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.ts +39 -0
  84. package/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.ts +26 -0
  85. package/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.ts +33 -0
  86. package/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.ts +33 -0
  87. package/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.ts +21 -0
  88. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +17 -5
  89. package/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts +8 -3
  90. package/src/cli/cli-commands/common/harness/HarnessDefinition.ts +36 -0
  91. package/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.ts +8 -3
  92. package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +13 -2
  93. package/src/cli/cli-commands/common/questionsCliOptions.ts +89 -0
  94. package/src/cli/cli-commands/run.ts +3 -1
  95. package/src/cli/common/$deprecateCliCommand.ts +5 -0
  96. package/src/cli/common/$hideCliCommandFromHelp.ts +27 -0
  97. package/src/cli/common/$requireCliSubcommand.ts +35 -0
  98. package/src/cli/promptbookCli.ts +13 -9
  99. package/src/llm-providers/vercel/VercelExecutionToolsOptions.ts +7 -2
  100. package/src/llm-providers/vercel/createExecutionToolsFromVercelProvider.ts +18 -17
  101. package/src/other/templates/getTemplatesPipelineCollection.ts +920 -830
  102. package/src/version.ts +2 -2
  103. package/src/versions.txt +2 -0
  104. package/umd/index.umd.js +1253 -317
  105. package/umd/index.umd.js.map +1 -1
  106. package/umd/scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.d.ts +25 -0
  107. package/umd/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
  108. package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +21 -3
  109. package/umd/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +1 -1
  110. package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +77 -0
  111. package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTracePath.d.ts +10 -0
  112. package/umd/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +11 -6
  113. package/umd/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +1 -1
  114. package/umd/scripts/run-codex-prompts/prompts/writePromptRunTrace.d.ts +19 -0
  115. package/umd/scripts/run-codex-prompts/runners/common/buildHarnessAuthenticationError.d.ts +20 -0
  116. package/umd/scripts/run-codex-prompts/runners/common/createAuthenticationAwarePromptRunner.d.ts +11 -0
  117. package/umd/scripts/run-codex-prompts/runners/common/extractHarnessAuthenticationFailureReason.d.ts +9 -0
  118. package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -0
  119. package/umd/src/book-2.0/book-language-documentation/getSafeCodeBlock.d.ts +1 -1
  120. package/umd/src/book-components/Chat/MockedChat/MockedChat.test.d.ts +2 -0
  121. package/umd/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.d.ts +2 -1
  122. package/umd/src/cli/cli-commands/coder/agentCliOptions.d.ts +21 -0
  123. package/umd/src/cli/cli-commands/coder/boilerplateTemplates.d.ts +8 -21
  124. package/umd/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +33 -0
  125. package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +4 -2
  126. package/umd/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.d.ts +2 -0
  127. package/umd/src/cli/cli-commands/coder/ensureDirectory.d.ts +6 -0
  128. package/umd/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.d.ts +6 -0
  129. package/umd/src/cli/cli-commands/coder/init.test.d.ts +1 -0
  130. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
  131. package/umd/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +19 -1
  132. package/umd/src/cli/cli-commands/coder/printCoderRunFailure.d.ts +6 -0
  133. package/umd/src/cli/cli-commands/common/$askForConfirmation.d.ts +4 -2
  134. package/umd/src/cli/cli-commands/common/$askForConfirmation.test.d.ts +1 -0
  135. package/umd/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.d.ts +12 -0
  136. package/umd/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.test.d.ts +1 -0
  137. package/umd/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.d.ts +11 -0
  138. package/umd/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.test.d.ts +1 -0
  139. package/umd/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.d.ts +33 -0
  140. package/umd/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.d.ts +6 -0
  141. package/umd/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.test.d.ts +1 -0
  142. package/umd/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.d.ts +10 -0
  143. package/umd/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.d.ts +28 -0
  144. package/umd/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.d.ts +7 -0
  145. package/umd/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.test.d.ts +1 -0
  146. package/umd/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +2 -1
  147. package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -2
  148. package/umd/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
  149. package/umd/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts +4 -2
  150. package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +2 -1
  151. package/umd/src/cli/cli-commands/common/questionsCliOptions.d.ts +45 -0
  152. package/umd/src/cli/cli-commands/common/questionsCliOptions.test.d.ts +1 -0
  153. package/umd/src/cli/common/$deprecateCliCommand.d.ts +3 -0
  154. package/umd/src/cli/common/$hideCliCommandFromHelp.d.ts +11 -0
  155. package/umd/src/cli/common/$hideCliCommandFromHelp.test.d.ts +1 -0
  156. package/umd/src/cli/common/$requireCliSubcommand.d.ts +18 -0
  157. package/umd/src/cli/common/$requireCliSubcommand.test.d.ts +1 -0
  158. package/umd/src/llm-providers/vercel/VercelExecutionToolsOptions.d.ts +5 -2
  159. package/umd/src/version.d.ts +1 -1
  160. package/esm/src/cli/cli-commands/coder/agentCodingFile.d.ts +0 -13
  161. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +0 -29
  162. package/src/cli/cli-commands/coder/agentCodingFile.ts +0 -25
  163. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +0 -46
  164. package/umd/src/cli/cli-commands/coder/agentCodingFile.d.ts +0 -13
  165. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +0 -29
  166. /package/esm/src/cli/cli-commands/{common/harnessUpdateCliOptions.test.d.ts → coder/init.test.d.ts} +0 -0
  167. /package/{umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts → esm/src/cli/cli-commands/common/$askForConfirmation.test.d.ts} +0 -0
package/umd/index.umd.js CHANGED
@@ -58,7 +58,7 @@
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-33';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-35';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1718,6 +1718,35 @@
1718
1718
  // TODO: [🗽] Unite branding and make single place for it
1719
1719
  // Note: [💞] Ignore a discrepancy between file name and entity name
1720
1720
 
1721
+ /**
1722
+ * Message printed when one CLI command which only groups subcommands is run without picking any of them.
1723
+ *
1724
+ * @private internal constant of `$requireCliSubcommand`
1725
+ */
1726
+ const MISSING_CLI_SUBCOMMAND_MESSAGE = 'Please specify a subcommand.';
1727
+ /**
1728
+ * Makes one CLI command which only groups subcommands ask for a subcommand instead of doing anything on its own.
1729
+ *
1730
+ * Note: `$` is used to indicate that this function is not a pure function - it registers an action in the CLI
1731
+ *
1732
+ * @private utility of CLI
1733
+ */
1734
+ function $requireCliSubcommand(command) {
1735
+ command.action(() => $reportMissingCliSubcommand(command));
1736
+ }
1737
+ /**
1738
+ * Asks for a subcommand of one CLI command, prints its help and exits the process.
1739
+ *
1740
+ * Note: `$` is used to indicate that this function is not a pure function - it writes to the console and exits the process
1741
+ *
1742
+ * @private utility of CLI
1743
+ */
1744
+ function $reportMissingCliSubcommand(command) {
1745
+ console.info(colors__default["default"].yellow(MISSING_CLI_SUBCOMMAND_MESSAGE));
1746
+ console.info('');
1747
+ return command.help();
1748
+ }
1749
+
1721
1750
  // Note: [💞] Ignore a discrepancy between file name and entity name
1722
1751
  /**
1723
1752
  * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
@@ -2113,11 +2142,7 @@
2113
2142
  `));
2114
2143
  $initializeAgentChatCommand(agentCommand);
2115
2144
  $initializeAgentExecCommand(agentCommand);
2116
- agentCommand.action(() => {
2117
- console.info(colors__default["default"].yellow('Please specify a subcommand.'));
2118
- console.info('');
2119
- agentCommand.help();
2120
- });
2145
+ $requireCliSubcommand(agentCommand);
2121
2146
  }
2122
2147
  // Note: [🟡] Code for CLI command [agent](src/cli/cli-commands/agent.ts) should never be published outside of `@promptbook/cli`
2123
2148
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2275,6 +2300,23 @@
2275
2300
  }
2276
2301
  return 'unchanged';
2277
2302
  }
2303
+ /**
2304
+ * Checks whether a project-relative directory is missing or contains no entries.
2305
+ *
2306
+ * @private internal utility of `coder init` command
2307
+ */
2308
+ async function isDirectoryEmpty(projectPath, relativeDirectoryPath) {
2309
+ const directoryPath = path.join(projectPath, relativeDirectoryPath);
2310
+ try {
2311
+ return (await promises.readdir(directoryPath)).length === 0;
2312
+ }
2313
+ catch (error) {
2314
+ if (isNodeJsErrorWithCode$1(error, 'ENOENT')) {
2315
+ return true;
2316
+ }
2317
+ throw error;
2318
+ }
2319
+ }
2278
2320
  /**
2279
2321
  * Checks whether a path exists and is a directory.
2280
2322
  */
@@ -2286,6 +2328,12 @@
2286
2328
  return false;
2287
2329
  }
2288
2330
  }
2331
+ /**
2332
+ * Checks whether a caught value is a Node.js error with the given error code.
2333
+ */
2334
+ function isNodeJsErrorWithCode$1(error, code) {
2335
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
2336
+ }
2289
2337
  // Note: [🟡] Code for coder init directory creation [ensureDirectory](src/cli/cli-commands/coder/ensureDirectory.ts) should never be published outside of `@promptbook/cli`
2290
2338
 
2291
2339
  /**
@@ -2573,11 +2621,7 @@
2573
2621
  $initializeAgentTickCommand(agentFolderCommand);
2574
2622
  $initializeAgentRunCommand(agentFolderCommand);
2575
2623
  $initializeAgentRunMultipleCommand(agentFolderCommand);
2576
- agentFolderCommand.action(() => {
2577
- console.info(colors__default["default"].yellow('Please specify a subcommand.'));
2578
- console.info('');
2579
- agentFolderCommand.help();
2580
- });
2624
+ $requireCliSubcommand(agentFolderCommand);
2581
2625
  }
2582
2626
  // Note: [🟡] Code for CLI command [agent-folder](src/cli/cli-commands/agent-folder.ts) should never be published outside of `@promptbook/cli`
2583
2627
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -21428,10 +21472,30 @@
21428
21472
  return relativePath.replace(/\\/gu, '/');
21429
21473
  }
21430
21474
 
21475
+ /**
21476
+ * AuthenticationError is thrown from login function which is dependency of remote server
21477
+ *
21478
+ * @public exported from `@promptbook/core`
21479
+ */
21480
+ class AuthenticationError extends Error {
21481
+ constructor(message) {
21482
+ super(message);
21483
+ this.name = 'AuthenticationError';
21484
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
21485
+ }
21486
+ }
21487
+
21431
21488
  /**
21432
21489
  * Formats one unknown error-like value into readable text for logs and feedback.
21490
+ *
21491
+ * An `AuthenticationError` is reported without its stack, because its message is written for the user - it
21492
+ * says which harness has to be signed in again and how - and the frames of a failure nobody can debug would
21493
+ * only push those instructions out of sight.
21433
21494
  */
21434
21495
  function formatUnknownErrorDetails(error) {
21496
+ if (error instanceof AuthenticationError) {
21497
+ return error.message;
21498
+ }
21435
21499
  if (error instanceof Error) {
21436
21500
  return error.stack || error.message;
21437
21501
  }
@@ -24148,6 +24212,7 @@
24148
24212
  commandName: 'codex',
24149
24213
  npmPackageName: '@openai/codex',
24150
24214
  projectGitignoreRules: ['.codex'],
24215
+ authenticationMethod: { command: 'codex login' },
24151
24216
  // Note: The official standalone installer keeps its package in `~/.codex` and links it onto the `PATH`
24152
24217
  standaloneInstallation: {
24153
24218
  directoryNames: ['.codex'],
@@ -24160,6 +24225,7 @@
24160
24225
  commandName: 'copilot',
24161
24226
  npmPackageName: '@github/copilot',
24162
24227
  projectGitignoreRules: ['.github/copilot/settings.local.json'],
24228
+ authenticationMethod: { command: 'copilot', interactiveStep: 'running the `/login` command inside it' },
24163
24229
  // Note: The GitHub Copilot CLI downloads its native binary in a postinstall script
24164
24230
  npmInstallEnvironment: { npm_config_ignore_scripts: 'false' },
24165
24231
  },
@@ -24169,6 +24235,7 @@
24169
24235
  commandName: 'cline',
24170
24236
  npmPackageName: 'cline',
24171
24237
  projectGitignoreRules: ['.cline'],
24238
+ authenticationMethod: { command: 'cline', interactiveStep: 'signing in to the provider inside it' },
24172
24239
  },
24173
24240
  'claude-code': {
24174
24241
  harnessName: 'claude-code',
@@ -24176,6 +24243,7 @@
24176
24243
  commandName: 'claude',
24177
24244
  npmPackageName: '@anthropic-ai/claude-code',
24178
24245
  projectGitignoreRules: ['.claude'],
24246
+ authenticationMethod: { command: 'claude', interactiveStep: 'running the `/login` command inside it' },
24179
24247
  },
24180
24248
  opencode: {
24181
24249
  harnessName: 'opencode',
@@ -24183,6 +24251,7 @@
24183
24251
  commandName: 'opencode',
24184
24252
  npmPackageName: 'opencode-ai',
24185
24253
  projectGitignoreRules: ['.opencode'],
24254
+ authenticationMethod: { command: 'opencode auth login' },
24186
24255
  },
24187
24256
  gemini: {
24188
24257
  harnessName: 'gemini',
@@ -24190,6 +24259,7 @@
24190
24259
  commandName: 'gemini',
24191
24260
  npmPackageName: '@google/gemini-cli',
24192
24261
  projectGitignoreRules: ['.gemini'],
24262
+ authenticationMethod: { command: 'gemini', interactiveStep: 'running the `/auth` command inside it' },
24193
24263
  },
24194
24264
  'qwen-code': {
24195
24265
  harnessName: 'qwen-code',
@@ -24197,6 +24267,7 @@
24197
24267
  commandName: 'qwen',
24198
24268
  npmPackageName: '@qwen-code/qwen-code',
24199
24269
  projectGitignoreRules: ['.qwen'],
24270
+ authenticationMethod: { command: 'qwen', interactiveStep: 'running the `/auth` command inside it' },
24200
24271
  },
24201
24272
  };
24202
24273
  /**
@@ -26230,6 +26301,183 @@
26230
26301
  return `${sessionId.slice(0, 8)}...`;
26231
26302
  }
26232
26303
 
26304
+ /**
26305
+ * Formats one unknown error-like value into its readable message without the stack trace.
26306
+ *
26307
+ * Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
26308
+ * inside a branded error, where the stack of the wrapped error would only bury the actual cause.
26309
+ */
26310
+ function formatUnknownErrorMessage(error) {
26311
+ if (error instanceof Error) {
26312
+ return error.message;
26313
+ }
26314
+ if (typeof error === 'string') {
26315
+ return error;
26316
+ }
26317
+ const serializedError = JSON.stringify(error, null, 2);
26318
+ return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
26319
+ }
26320
+
26321
+ /**
26322
+ * Builds the branded error which tells the user that the selected harness has to be signed in again.
26323
+ */
26324
+ function buildHarnessAuthenticationError(options) {
26325
+ const { harnessName, reason } = options;
26326
+ const { label, authenticationMethod } = getHarnessDefinition(harnessName);
26327
+ return new AuthenticationError(spaceTrim((block) => `
26328
+ The **${label}** harness is not authenticated, so the prompt was not run.
26329
+
26330
+ ${label} reported:
26331
+ > ${reason}
26332
+
26333
+ ${block(buildSignInInstruction(authenticationMethod))}
26334
+
26335
+ Once you are signed in, start the very same \`ptbk coder\` command again.
26336
+ `));
26337
+ }
26338
+ /**
26339
+ * Builds the sentence which tells the user how to sign in to one harness again.
26340
+ */
26341
+ function buildSignInInstruction(authenticationMethod) {
26342
+ const { command, interactiveStep } = authenticationMethod;
26343
+ if (interactiveStep === undefined) {
26344
+ return `Sign in again by running \`${command}\`.`;
26345
+ }
26346
+ return `Sign in again by running \`${command}\` and then ${interactiveStep}.`;
26347
+ }
26348
+
26349
+ /**
26350
+ * Maximum length of the harness reason quoted back to the user.
26351
+ */
26352
+ const MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH = 300;
26353
+ /**
26354
+ * Keys of machine-readable harness output whose string value carries the human-readable failure reason.
26355
+ *
26356
+ * Harnesses which stream JSON events - like Claude Code with its `stream-json` output - report the reason only
26357
+ * inside such a value, surrounded by hundreds of characters of session metadata which must never be shown to
26358
+ * the user. The keys are ordered from the most to the least conclusive one.
26359
+ */
26360
+ const HARNESS_FAILURE_MESSAGE_KEYS = ['result', 'error_message', 'message', 'text'];
26361
+ /**
26362
+ * Patterns which extract one JSON string value reported under a harness failure message key.
26363
+ */
26364
+ const HARNESS_FAILURE_MESSAGE_PATTERNS = HARNESS_FAILURE_MESSAGE_KEYS.map((messageKey) => new RegExp(`"${messageKey}"\\s*:\\s*("(?:[^"\\\\]|\\\\.)*")`, 'g'));
26365
+ /**
26366
+ * Signatures which mean that the harness refused to work because it is not logged in.
26367
+ *
26368
+ * They are deliberately narrow phrases instead of single words like `login`, because the output of a harness
26369
+ * routinely mentions its own `/login` command among the commands it offers, and such a mention must never be
26370
+ * read as a failed login.
26371
+ */
26372
+ const AUTHENTICATION_FAILURE_PATTERNS = [
26373
+ /\bauthentication[ _-]?failed\b/i,
26374
+ /\bfailed to authenticate\b/i,
26375
+ /\bnot authenticated\b/i,
26376
+ /\bnot logged[ -]?in\b/i,
26377
+ /\bnot signed[ -]?in\b/i,
26378
+ /\bno authentication information found\b/i,
26379
+ /\bauthentication (?:is )?required\b/i,
26380
+ /\blogin (?:is )?required\b/i,
26381
+ /\bplease (?:log|sign) in\b/i,
26382
+ /\bunauthorized\b/i,
26383
+ /\b(?:oauth|session|token|credentials?|login)\b[^.]{0,60}?\bexpired\b/i,
26384
+ /\bexpired\b[^.]{0,60}?\b(?:oauth|session|token|credentials?|login)\b/i,
26385
+ /\binvalid api[ _-]?key\b/i,
26386
+ /\bmissing api[ _-]?key\b/i,
26387
+ /\binvalid credentials?\b/i,
26388
+ ];
26389
+ /**
26390
+ * Reads the reason why a harness refused to work because it is not logged in, from its raw CLI output.
26391
+ *
26392
+ * Messages the harness reports in machine-readable output are preferred over its raw output lines, so the
26393
+ * reason is the sentence the harness wrote for a human and not the JSON event which transported it.
26394
+ *
26395
+ * @returns One short single-line reason, or `undefined` when the failure was not caused by authentication.
26396
+ */
26397
+ function extractHarnessAuthenticationFailureReason(harnessOutput) {
26398
+ const reportedMessages = [...extractHarnessReportedMessages(harnessOutput), ...harnessOutput.split(/\r?\n/)];
26399
+ for (const reportedMessage of reportedMessages) {
26400
+ const reason = reportedMessage.replace(/\s+/g, ' ').trim();
26401
+ if (isAuthenticationFailureReason(reason)) {
26402
+ return limitAuthenticationFailureReason(reason);
26403
+ }
26404
+ }
26405
+ return undefined;
26406
+ }
26407
+ /**
26408
+ * Lists the human-readable messages a harness reported in its machine-readable output.
26409
+ */
26410
+ function extractHarnessReportedMessages(harnessOutput) {
26411
+ const reportedMessages = [];
26412
+ for (const messagePattern of HARNESS_FAILURE_MESSAGE_PATTERNS) {
26413
+ for (const match of harnessOutput.matchAll(messagePattern)) {
26414
+ const reportedMessage = parseSerializedJsonString(match[1]);
26415
+ if (reportedMessage !== undefined) {
26416
+ reportedMessages.push(reportedMessage);
26417
+ }
26418
+ }
26419
+ }
26420
+ return reportedMessages;
26421
+ }
26422
+ /**
26423
+ * Reads one JSON string literal back into plain text, tolerating output which only looks like JSON.
26424
+ */
26425
+ function parseSerializedJsonString(serializedString) {
26426
+ if (serializedString === undefined) {
26427
+ return undefined;
26428
+ }
26429
+ try {
26430
+ const parsedString = JSON.parse(serializedString);
26431
+ return typeof parsedString === 'string' ? parsedString : undefined;
26432
+ }
26433
+ catch (_a) {
26434
+ return undefined;
26435
+ }
26436
+ }
26437
+ /**
26438
+ * Returns true when one message of the harness says that it is not logged in.
26439
+ */
26440
+ function isAuthenticationFailureReason(reportedMessage) {
26441
+ return AUTHENTICATION_FAILURE_PATTERNS.some((pattern) => pattern.test(reportedMessage));
26442
+ }
26443
+ /**
26444
+ * Shortens a very long reason, so the guidance which follows it stays visible in the terminal.
26445
+ */
26446
+ function limitAuthenticationFailureReason(reason) {
26447
+ if (reason.length <= MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH) {
26448
+ return reason;
26449
+ }
26450
+ return `${reason.slice(0, MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH)}...`;
26451
+ }
26452
+
26453
+ /**
26454
+ * Wraps one prompt runner so a failure caused by a missing or expired harness login is reported as a branded
26455
+ * `AuthenticationError` which tells the user how to sign in again.
26456
+ *
26457
+ * Each harness reports its own failures differently, but all of them report them by failing `runPrompt` with
26458
+ * the raw CLI output as the message. Translating the failure here therefore keeps exactly one place which has
26459
+ * to recognize an unauthenticated harness, for every harness and for every command which runs prompts.
26460
+ */
26461
+ function createAuthenticationAwarePromptRunner(runner, harnessName) {
26462
+ var _a;
26463
+ return {
26464
+ name: runner.name,
26465
+ runPrompt: async (options) => {
26466
+ try {
26467
+ return await runner.runPrompt(options);
26468
+ }
26469
+ catch (error) {
26470
+ const reason = extractHarnessAuthenticationFailureReason(formatUnknownErrorMessage(error));
26471
+ if (reason === undefined) {
26472
+ throw error;
26473
+ }
26474
+ throw buildHarnessAuthenticationError({ harnessName, reason });
26475
+ }
26476
+ },
26477
+ getSubscriptionUsage: (_a = runner.getSubscriptionUsage) === null || _a === void 0 ? void 0 : _a.bind(runner),
26478
+ };
26479
+ }
26480
+
26233
26481
  /**
26234
26482
  * Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
26235
26483
  */
@@ -27969,6 +28217,18 @@
27969
28217
  if (!agentName) {
27970
28218
  throw new Error('Missing --harness in non-dry run mode');
27971
28219
  }
28220
+ const resolution = resolveHarnessPromptRunner(agentName, options);
28221
+ return {
28222
+ ...resolution,
28223
+ // Note: Every harness is wrapped here, so a harness which is not logged in is reported the same clear
28224
+ // way no matter which harness it is and which command has resolved the runner
28225
+ runner: createAuthenticationAwarePromptRunner(resolution.runner, agentName),
28226
+ };
28227
+ }
28228
+ /**
28229
+ * Creates the runner of one selected harness together with its status-line metadata.
28230
+ */
28231
+ function resolveHarnessPromptRunner(agentName, options) {
27972
28232
  if (agentName === 'openai-codex') {
27973
28233
  return createOpenAiCodexRunnerResolution(options);
27974
28234
  }
@@ -28436,11 +28696,19 @@
28436
28696
  * Maximum width used for the rich coder-run frame.
28437
28697
  */
28438
28698
  const MAX_FRAME_WIDTH = 96;
28699
+ /**
28700
+ * Number of terminal columns deliberately left unused by the rich coder-run frame.
28701
+ *
28702
+ * A row which fills the final physical terminal column can auto-wrap. The incremental terminal
28703
+ * updater moves by logical rows, so an auto-wrapped row would put its cursor out of sync with the
28704
+ * frame and let a later animated avatar redraw overwrite dashboard content.
28705
+ */
28706
+ const TERMINAL_AUTO_WRAP_RESERVED_COLUMNS = 1;
28439
28707
  /**
28440
28708
  * Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
28441
28709
  */
28442
28710
  function buildCoderRunUiFrame(options) {
28443
- const totalWidth = Math.max(MIN_FRAME_WIDTH, Math.min(options.terminalWidth, MAX_FRAME_WIDTH));
28711
+ const totalWidth = resolveCoderRunUiFrameWidth(options.terminalWidth);
28444
28712
  const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
28445
28713
  const promptStatusPrefix = isPromptActive ? `${colors__default["default"].yellow(`${options.spinner} `)}` : '';
28446
28714
  const pausePresentation = buildPausePresentation(options.phase, options.pauseState, options.pauseTargetLabel, options.statusMessage);
@@ -28477,6 +28745,16 @@
28477
28745
  frame.push(...renderBox('Controls', controlsBoxLines, totalWidth, colors__default["default"].white.bold));
28478
28746
  return frame;
28479
28747
  }
28748
+ /**
28749
+ * Resolves the safe width of the rich terminal frame from the available physical terminal columns.
28750
+ *
28751
+ * The reserved trailing column keeps every boxed row from auto-wrapping, which makes the frame's
28752
+ * logical row count match its physical terminal row count during animated incremental redraws.
28753
+ */
28754
+ function resolveCoderRunUiFrameWidth(terminalWidth) {
28755
+ const availableFrameWidth = terminalWidth - TERMINAL_AUTO_WRAP_RESERVED_COLUMNS;
28756
+ return Math.max(MIN_FRAME_WIDTH, Math.min(availableFrameWidth, MAX_FRAME_WIDTH));
28757
+ }
28480
28758
  /**
28481
28759
  * Builds the header visual above the dashboard boxes.
28482
28760
  *
@@ -29953,16 +30231,14 @@
29953
30231
  */
29954
30232
  const DEFAULT_TERMINAL_WIDTH = 80;
29955
30233
  /**
29956
- * Maximum terminal width supported by the boxed runner UI.
29957
- */
29958
- const MAX_TERMINAL_WIDTH = 96;
29959
- /**
29960
- * Returns the usable terminal width, capped at 96.
30234
+ * Returns the number of columns reported by the current terminal.
30235
+ *
30236
+ * The frame builder owns clamping and reserves its trailing auto-wrap safety column.
29961
30237
  *
29962
30238
  * @private internal utility of coder run UI
29963
30239
  */
29964
- function getTerminalWidth() {
29965
- return Math.min(process.stdout.columns || DEFAULT_TERMINAL_WIDTH, MAX_TERMINAL_WIDTH);
30240
+ function getTerminalColumnCount() {
30241
+ return process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
29966
30242
  }
29967
30243
  /**
29968
30244
  * Boots the ANSI terminal UI for `ptbk coder run`.
@@ -30069,7 +30345,7 @@
30069
30345
  */
30070
30346
  function buildFrameLines() {
30071
30347
  return buildFrameLinesFromState({
30072
- terminalWidth: getTerminalWidth(),
30348
+ terminalWidth: getTerminalColumnCount(),
30073
30349
  animationFrame: spinnerFrame,
30074
30350
  animationTimeMs: Date.now() - animationStartTimeMs,
30075
30351
  spinner: SPINNER_FRAMES[spinnerFrame],
@@ -33800,19 +34076,6 @@
33800
34076
  }
33801
34077
  }
33802
34078
 
33803
- /**
33804
- * AuthenticationError is thrown from login function which is dependency of remote server
33805
- *
33806
- * @public exported from `@promptbook/core`
33807
- */
33808
- class AuthenticationError extends Error {
33809
- constructor(message) {
33810
- super(message);
33811
- this.name = 'AuthenticationError';
33812
- Object.setPrototypeOf(this, AuthenticationError.prototype);
33813
- }
33814
- }
33815
-
33816
34079
  /**
33817
34080
  * This error indicates that the pipeline collection cannot be properly loaded
33818
34081
  *
@@ -42041,11 +42304,7 @@
42041
42304
  $initializeAgentsServerDevCommand(agentsServerCommand);
42042
42305
  $initializeAgentsServerInitCommand(agentsServerCommand);
42043
42306
  $initializeAgentsServerStartCommand(agentsServerCommand);
42044
- agentsServerCommand.action(() => {
42045
- console.info(colors__default["default"].yellow('Please specify a subcommand.'));
42046
- console.info('');
42047
- agentsServerCommand.help();
42048
- });
42307
+ $requireCliSubcommand(agentsServerCommand);
42049
42308
  }
42050
42309
  // Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
42051
42310
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -42135,6 +42394,58 @@
42135
42394
  // Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
42136
42395
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
42137
42396
 
42397
+ /**
42398
+ * Description block shared by the `ptbk coder` commands which can ask interactive questions.
42399
+ *
42400
+ * @private internal utility of `promptbookCli`
42401
+ */
42402
+ const QUESTIONS_DESCRIPTION = _spaceTrim.spaceTrim(`
42403
+ Interactive questions:
42404
+ - Steps which need an answer to continue are asked in the terminal
42405
+ - --no-questions never asks: an optional step is skipped with a note how to do it manually, a required one fails right away
42406
+ `);
42407
+ /**
42408
+ * Registers the shared `--no-questions` option on a `ptbk coder` command which can ask interactive questions.
42409
+ *
42410
+ * @private internal utility of `promptbookCli`
42411
+ */
42412
+ function addQuestionsOption(command) {
42413
+ command.option('--no-questions', 'Never ask an interactive question and skip every optional step which needs an answer to continue');
42414
+ }
42415
+ /**
42416
+ * Converts the Commander questions flag into the normalized interactive-questions option.
42417
+ *
42418
+ * @private internal utility of `promptbookCli`
42419
+ */
42420
+ function normalizeQuestionsCliOptions(cliOptions) {
42421
+ return {
42422
+ isAskingQuestionsEnabled: cliOptions.questions,
42423
+ };
42424
+ }
42425
+ /**
42426
+ * Asserts that a command which waits for a user confirmation before each prompt is allowed to ask for it.
42427
+ *
42428
+ * @throws {NotAllowed} when `--no-auto` is combined with `--no-questions`
42429
+ * @private internal utility of `promptbookCli`
42430
+ */
42431
+ function assertUserConfirmationIsAllowed(options) {
42432
+ const { isAskingQuestionsEnabled, isWaitingForUser } = options;
42433
+ if (!isWaitingForUser || isAskingQuestionsEnabled) {
42434
+ return;
42435
+ }
42436
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
42437
+ Flag \`--no-auto\` can not be used together with \`--no-questions\`.
42438
+
42439
+ **There is nobody who could confirm each prompt when no question may be asked.**
42440
+
42441
+ Actionable hints:
42442
+ - Keep \`--no-auto\` for a supervised run, for example \`ptbk coder run --harness claude-code --no-auto\`.
42443
+ - Keep \`--no-questions\` for an unattended run, for example \`ptbk coder run --harness claude-code --no-questions\`.
42444
+ `));
42445
+ }
42446
+ // Note: [🟡] Code for CLI interactive questions options [questionsCliOptions](src/cli/cli-commands/common/questionsCliOptions.ts) should never be published outside of `@promptbook/cli`
42447
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
42448
+
42138
42449
  /**
42139
42450
  * Relative path to the root prompts directory used by Promptbook coder utilities.
42140
42451
  *
@@ -42153,6 +42464,12 @@
42153
42464
  * @private internal utility of `ptbk coder`
42154
42465
  */
42155
42466
  const PROMPTS_TEMPLATES_DIRECTORY_PATH = path.join(PROMPTS_DIRECTORY_PATH$1, 'templates');
42467
+ /**
42468
+ * Relative path to the project-agnostic `common` boilerplate template referenced by the default coder scripts.
42469
+ *
42470
+ * @private internal utility of `ptbk coder`
42471
+ */
42472
+ const COMMON_PROMPT_TEMPLATE_FILE_PATH = path.join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'common.md');
42156
42473
  /**
42157
42474
  * Built-in boilerplate templates available to `coder generate-boilerplates`.
42158
42475
  *
@@ -42161,7 +42478,7 @@
42161
42478
  const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
42162
42479
  {
42163
42480
  id: 'common',
42164
- relativeFilePath: path.join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'common.md'),
42481
+ relativeFilePath: COMMON_PROMPT_TEMPLATE_FILE_PATH,
42165
42482
  slugPrefix: null,
42166
42483
  content: _spaceTrim.spaceTrim(`
42167
42484
  - @@@
@@ -42207,30 +42524,17 @@
42207
42524
  return definition;
42208
42525
  }
42209
42526
  /**
42210
- * Ensures the default project-owned coder template files exist without overwriting user customizations.
42527
+ * Ensures one project-owned coder template file exists without overwriting user customizations.
42211
42528
  *
42212
42529
  * @private internal utility of `ptbk coder`
42213
42530
  */
42214
- async function ensureDefaultCoderPromptTemplateFiles(projectPath) {
42215
- const ensuredTemplateFiles = [];
42216
- for (const definition of DEFAULT_CODER_PROJECT_PROMPT_TEMPLATE_DEFINITIONS) {
42217
- const absoluteTemplatePath = path.join(projectPath, definition.relativeFilePath);
42218
- if (await isExistingFile$3(absoluteTemplatePath)) {
42219
- ensuredTemplateFiles.push({
42220
- id: definition.id,
42221
- relativeFilePath: definition.relativeFilePath,
42222
- status: 'unchanged',
42223
- });
42224
- continue;
42225
- }
42226
- await promises.writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
42227
- ensuredTemplateFiles.push({
42228
- id: definition.id,
42229
- relativeFilePath: definition.relativeFilePath,
42230
- status: 'created',
42231
- });
42531
+ async function ensureDefaultCoderPromptTemplateFile(projectPath, definition) {
42532
+ const absoluteTemplatePath = path.join(projectPath, definition.relativeFilePath);
42533
+ if (await isExistingFile$3(absoluteTemplatePath)) {
42534
+ return 'unchanged';
42232
42535
  }
42233
- return ensuredTemplateFiles;
42536
+ await promises.writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
42537
+ return 'created';
42234
42538
  }
42235
42539
  /**
42236
42540
  * Resolves the template requested by `coder generate-boilerplates`.
@@ -42386,6 +42690,8 @@
42386
42690
  - \`ptbk coder add\`
42387
42691
 
42388
42692
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
42693
+
42694
+ ${block(QUESTIONS_DESCRIPTION)}
42389
42695
  `));
42390
42696
  command.argument('[description]', 'Plain-language description of the feature or task to implement');
42391
42697
  command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
@@ -42397,11 +42703,13 @@
42397
42703
  .join(', ')}) or a markdown file path relative to the current project root.
42398
42704
  `));
42399
42705
  addCoderGitSyncOptions(command);
42706
+ addQuestionsOption(command);
42400
42707
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
42401
42708
  const { priority, template: templateOption } = cliOptions;
42402
42709
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
42710
+ const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
42403
42711
  const projectPath = process.cwd();
42404
- const description = await resolveCoderPromptDescription(descriptionArgument);
42712
+ const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
42405
42713
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
42406
42714
  const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
42407
42715
  const commitScope = await $startCoderGitSync({ gitSync, projectPath });
@@ -42471,7 +42779,7 @@
42471
42779
  *
42472
42780
  * @private internal utility of `coder add` command
42473
42781
  */
42474
- async function resolveCoderPromptDescription(descriptionArgument) {
42782
+ async function resolveCoderPromptDescription(descriptionArgument, { isAskingQuestionsEnabled }) {
42475
42783
  if (descriptionArgument !== undefined && descriptionArgument.trim() !== '') {
42476
42784
  return descriptionArgument.trim();
42477
42785
  }
@@ -42489,6 +42797,14 @@
42489
42797
  }
42490
42798
  return standardInputDescription;
42491
42799
  }
42800
+ if (!isAskingQuestionsEnabled) {
42801
+ throw new ParseError(_spaceTrim.spaceTrim(`
42802
+ Cannot add a prompt without a description.
42803
+
42804
+ The description can not be typed interactively because \`--no-questions\` is used — provide it as an argument or pipe it through stdin instead:
42805
+ - \`ptbk coder add --no-questions "some new feature"\`
42806
+ `));
42807
+ }
42492
42808
  const { default: prompts } = await loadPromptsModule();
42493
42809
  const response = await prompts({
42494
42810
  type: 'text',
@@ -43139,10 +43455,15 @@
43139
43455
  *
43140
43456
  * Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin.
43141
43457
  *
43142
- * @returns `true` when the user confirms, `false` when the user declines or the terminal is not interactive.
43458
+ * @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
43459
+ * or the questions are disabled by `--no-questions`.
43143
43460
  * @private internal utility of `promptbookCli`
43144
43461
  */
43145
- async function $askForConfirmation(question) {
43462
+ async function $askForConfirmation(question, { isAskingQuestionsEnabled }) {
43463
+ if (!isAskingQuestionsEnabled) {
43464
+ // Note: `--no-questions` forbids asking, so the optional change is declined without blocking the run.
43465
+ return false;
43466
+ }
43146
43467
  if (!process.stdin.isTTY) {
43147
43468
  // Note: In non-interactive environments like CI there is nobody who could confirm the change.
43148
43469
  return false;
@@ -43165,11 +43486,12 @@
43165
43486
  *
43166
43487
  * Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
43167
43488
  *
43168
- * @returns `true` when the user confirms, `false` when the user declines or the terminal is not interactive
43489
+ * @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
43490
+ * or the questions are disabled by `--no-questions`
43169
43491
  * @private internal utility of `promptbookCli`
43170
43492
  */
43171
- async function $askForNpmPackageInstallationApproval(question) {
43172
- return $askForConfirmation(question);
43493
+ async function $askForNpmPackageInstallationApproval(question, questionsOptions) {
43494
+ return $askForConfirmation(question, questionsOptions);
43173
43495
  }
43174
43496
  // Note: [🟡] Code for CLI npm package installation approval [$askForNpmPackageInstallationApproval](src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.ts) should never be published outside of `@promptbook/cli`
43175
43497
 
@@ -43359,7 +43681,7 @@
43359
43681
  *
43360
43682
  * @private internal utility of `promptbookCli`
43361
43683
  */
43362
- async function $applyHarnessInstallationStatus(status) {
43684
+ async function $applyHarnessInstallationStatus(status, questionsOptions) {
43363
43685
  const { definition, installationState, installedVersion } = status;
43364
43686
  if (installationState === 'up-to-date') {
43365
43687
  console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
@@ -43375,17 +43697,17 @@
43375
43697
  }
43376
43698
  console.warn(colors__default["default"].yellow(formatHarnessInstallationWarning(status)));
43377
43699
  if (installationState === 'not-installed') {
43378
- await $applyMissingHarnessInstallation(definition);
43700
+ await $applyMissingHarnessInstallation(definition, questionsOptions);
43379
43701
  return;
43380
43702
  }
43381
- await $applyOutdatedHarnessInstallation(status);
43703
+ await $applyOutdatedHarnessInstallation(status, questionsOptions);
43382
43704
  }
43383
43705
  /**
43384
43706
  * Offers to install a missing harness globally through npm.
43385
43707
  */
43386
- async function $applyMissingHarnessInstallation(definition) {
43708
+ async function $applyMissingHarnessInstallation(definition, questionsOptions) {
43387
43709
  const installCommand = buildHarnessInstallCommand(definition);
43388
- const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`);
43710
+ const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`, questionsOptions);
43389
43711
  if (!isInstallationApproved) {
43390
43712
  console.info(colors__default["default"].gray(`Skipped, run \`${installCommand}\` to do it manually.`));
43391
43713
  return;
@@ -43400,14 +43722,14 @@
43400
43722
  /**
43401
43723
  * Offers to update an outdated harness exactly where it is installed.
43402
43724
  */
43403
- async function $applyOutdatedHarnessInstallation(status) {
43725
+ async function $applyOutdatedHarnessInstallation(status, questionsOptions) {
43404
43726
  const { definition, latestVersion, installationOrigin } = status;
43405
43727
  const updatePlan = resolveHarnessUpdatePlan(definition, installationOrigin.installationMethod);
43406
43728
  if (updatePlan.command === null || !updatePlan.isRunnableByPromptbook) {
43407
43729
  console.info(colors__default["default"].gray(formatHarnessManualUpdateInstruction(status, updatePlan)));
43408
43730
  return;
43409
43731
  }
43410
- const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`);
43732
+ const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`, questionsOptions);
43411
43733
  if (!isUpdateApproved) {
43412
43734
  console.info(colors__default["default"].gray(`Skipped, run \`${updatePlan.command}\` to do it manually.`));
43413
43735
  return;
@@ -43843,7 +44165,7 @@
43843
44165
  // Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
43844
44166
 
43845
44167
  /**
43846
- * Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
44168
+ * Checks that every given CLI coding harness is installed globally and, when the questions are enabled, up to date.
43847
44169
  * Offers to install missing harnesses and update outdated ones.
43848
44170
  *
43849
44171
  * Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
@@ -43853,15 +44175,18 @@
43853
44175
  *
43854
44176
  * @private internal utility of `promptbookCli`
43855
44177
  */
43856
- async function $ensureHarnessInstallations(harnessNames, isHarnessUpdateCheckEnabled = true) {
44178
+ async function $ensureHarnessInstallations(harnessNames, questionsOptions) {
43857
44179
  const definitions = resolveCheckedHarnessDefinitions(harnessNames);
43858
44180
  if (definitions.length === 0) {
43859
44181
  return;
43860
44182
  }
44183
+ // Note: An outdated harness can be updated only after the user confirms it, so `--no-questions`
44184
+ // skips the slower npm registry lookup entirely and only reports what is installed.
44185
+ const isHarnessUpdateCheckEnabled = questionsOptions.isAskingQuestionsEnabled;
43861
44186
  // Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
43862
44187
  const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)));
43863
44188
  for (const status of statuses) {
43864
- await $applyHarnessInstallationStatus(status);
44189
+ await $applyHarnessInstallationStatus(status, questionsOptions);
43865
44190
  }
43866
44191
  }
43867
44192
  /**
@@ -43873,48 +44198,6 @@
43873
44198
  }
43874
44199
  // Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
43875
44200
 
43876
- /**
43877
- * Registers the shared `--no-harness-update` option on a `ptbk coder` command that uses a coding harness.
43878
- *
43879
- * @private internal utility of `promptbookCli`
43880
- */
43881
- function addHarnessUpdateOption(command) {
43882
- command.option('--no-harness-update', 'Skip checking whether the installed coding harnesses are up to date');
43883
- }
43884
- /**
43885
- * Converts the Commander harness-update flag into the normalized harness-check option.
43886
- *
43887
- * @private internal utility of `promptbookCli`
43888
- */
43889
- function normalizeHarnessUpdateCliOptions(cliOptions) {
43890
- return {
43891
- isHarnessUpdateCheckEnabled: cliOptions.harnessUpdate,
43892
- };
43893
- }
43894
- // Note: [🟡] Code for CLI harness update options [harnessUpdateCliOptions](src/cli/cli-commands/common/harnessUpdateCliOptions.ts) should never be published outside of `@promptbook/cli`
43895
- // Note: [💞] Ignore a discrepancy between file name and exported helper names
43896
-
43897
- /**
43898
- * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43899
- *
43900
- * @private internal utility of `ptbk coder`
43901
- */
43902
- const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43903
- /**
43904
- * Returns the default coder `AGENT_CODING.md` quick-reference content.
43905
- *
43906
- * @private internal utility of `ptbk coder`
43907
- */
43908
- function getDefaultCoderAgentCodingFileContent() {
43909
- return _spaceTrim.spaceTrim(`
43910
- # ✨ Promptbook Coder agent coding
43911
-
43912
- This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
43913
- `);
43914
- }
43915
- // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43916
- // Note: [💞] Ignore a discrepancy between file name and exported helper names
43917
-
43918
44201
  /**
43919
44202
  * Relative path to the shared coder context file initialized in project roots.
43920
44203
  *
@@ -44016,6 +44299,51 @@
44016
44299
  }
44017
44300
  // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
44018
44301
 
44302
+ /**
44303
+ * Creates the artifact definition of one project-owned coder prompt template.
44304
+ */
44305
+ function createCoderPromptTemplateArtifactDefinition(definition) {
44306
+ return {
44307
+ relativeFilePath: definition.relativeFilePath,
44308
+ ensureArtifactFile: (projectPath) => ensureDefaultCoderPromptTemplateFile(projectPath, definition),
44309
+ };
44310
+ }
44311
+ /**
44312
+ * Every artifact which the default coder scripts can reference.
44313
+ */
44314
+ const CODER_REFERENCED_ARTIFACT_DEFINITIONS = [
44315
+ {
44316
+ relativeFilePath: CODER_DEVELOPER_AGENT_FILE_PATH,
44317
+ ensureArtifactFile: ensureCoderDeveloperAgentFile,
44318
+ },
44319
+ {
44320
+ relativeFilePath: AGENTS_FILE_PATH,
44321
+ ensureArtifactFile: (projectPath) => ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent()),
44322
+ },
44323
+ ...getDefaultCoderProjectPromptTemplateDefinitions().map(createCoderPromptTemplateArtifactDefinition),
44324
+ ];
44325
+ /**
44326
+ * Creates only those referenced artifacts which the freshly added coder scripts point at.
44327
+ *
44328
+ * An artifact of a script which the project already defines is intentionally **not** created - the project-owned
44329
+ * script is kept as is, so it does not reference the default artifact in the first place.
44330
+ *
44331
+ * @private function of `initializeCoderProjectConfiguration`
44332
+ */
44333
+ async function ensureCoderReferencedArtifacts(projectPath, referencedArtifactPaths) {
44334
+ const ensuredArtifacts = [];
44335
+ for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
44336
+ if (!referencedArtifactPaths.has(relativeFilePath)) {
44337
+ ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
44338
+ continue;
44339
+ }
44340
+ ensuredArtifacts.push({ relativeFilePath, status: await ensureArtifactFile(projectPath) });
44341
+ }
44342
+ return ensuredArtifacts;
44343
+ }
44344
+ // Note: [🟡] Code for coder init referenced artifacts [coderReferencedArtifacts](src/cli/cli-commands/coder/coderReferencedArtifacts.ts) should never be published outside of `@promptbook/cli`
44345
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
44346
+
44019
44347
  /**
44020
44348
  * Fallback `.env` content used when no required variables need to be appended.
44021
44349
  */
@@ -44110,26 +44438,79 @@
44110
44438
  }
44111
44439
  // Note: [🟡] Code for coder init gitignore bootstrapping [ensureCoderGitignoreFile](src/cli/cli-commands/coder/ensureCoderGitignoreFile.ts) should never be published outside of `@promptbook/cli`
44112
44440
 
44441
+ /**
44442
+ * Name of the npm script which `ptbk coder run` uses to verify the project.
44443
+ */
44444
+ const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
44113
44445
  /**
44114
44446
  * Default npm scripts initialized by `ptbk coder init`.
44447
+ *
44448
+ * Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
44115
44449
  */
44116
- const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
44117
- // Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
44118
- 'coder:generate-boilerplates': `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ./prompts/templates/common.md`,
44119
- 'coder:add': 'npx ptbk coder add --template ./prompts/templates/common.md',
44120
- 'coder:run': 'npx ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max --agent agents/developer.book --context AGENTS.md --test-before yes-and-fix',
44121
- // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
44122
- 'coder:verify': 'npx ptbk coder verify',
44123
- };
44450
+ const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS = [
44451
+ {
44452
+ scriptName: 'coder:generate-boilerplates',
44453
+ scriptCommand: `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
44454
+ referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
44455
+ },
44456
+ {
44457
+ scriptName: 'coder:add',
44458
+ scriptCommand: `npx ptbk coder add --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
44459
+ referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
44460
+ },
44461
+ {
44462
+ scriptName: 'coder:run',
44463
+ scriptCommand: [
44464
+ 'npx ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max',
44465
+ `--agent ${formatDisplayPath(CODER_DEVELOPER_AGENT_FILE_PATH)}`,
44466
+ `--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
44467
+ `--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
44468
+ ].join(' '),
44469
+ referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
44470
+ },
44471
+ // { scriptName: 'coder:find-refactor-candidates', scriptCommand: 'npx ptbk coder find-refactor-candidates', referencedArtifactPaths: [] },
44472
+ {
44473
+ scriptName: 'coder:verify',
44474
+ scriptCommand: 'npx ptbk coder verify',
44475
+ referencedArtifactPaths: [],
44476
+ },
44477
+ {
44478
+ // Note: The verification command of `coder:run` is a project-owned script, so every project can decide
44479
+ // what "verified" means without touching the `coder:run` script itself
44480
+ scriptName: CODER_TEST_SCRIPT_NAME,
44481
+ scriptCommand: 'npm test',
44482
+ referencedArtifactPaths: [],
44483
+ },
44484
+ ];
44124
44485
  /**
44125
44486
  * Lists the default npm scripts initialized by `ptbk coder init`.
44126
44487
  *
44127
44488
  * @private internal utility of `coder init` command
44128
44489
  */
44129
44490
  function getDefaultCoderPackageJsonScripts() {
44130
- return DEFAULT_CODER_PACKAGE_JSON_SCRIPTS;
44491
+ return Object.fromEntries(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.map(({ scriptName, scriptCommand }) => [
44492
+ scriptName,
44493
+ scriptCommand,
44494
+ ]));
44495
+ }
44496
+ /**
44497
+ * Collects the project-relative artifact paths referenced by the given default coder scripts.
44498
+ *
44499
+ * @private internal utility of `coder init` command
44500
+ */
44501
+ function resolveCoderPackageJsonScriptReferencedArtifactPaths(scriptNames) {
44502
+ return new Set(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.filter(({ scriptName }) => scriptNames.includes(scriptName))
44503
+ .map(({ referencedArtifactPaths }) => referencedArtifactPaths)
44504
+ .flat());
44505
+ }
44506
+ /**
44507
+ * Formats one project-relative path as an explicitly project-rooted path usable inside a shell command.
44508
+ */
44509
+ function formatCoderScriptFilePath(relativeFilePath) {
44510
+ return `./${formatDisplayPath(relativeFilePath)}`;
44131
44511
  }
44132
44512
  // Note: [🟡] Code for coder init package scripts [getDefaultCoderPackageJsonScripts](src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts) should never be published outside of `@promptbook/cli`
44513
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
44133
44514
 
44134
44515
  /**
44135
44516
  * Loads the TypeScript runtime used for parsing JSONC-style project files.
@@ -44162,6 +44543,9 @@
44162
44543
  /**
44163
44544
  * Ensures one JSON object field contains the provided string-record entries.
44164
44545
  *
44546
+ * Entries which the project already defines are **never** overridden - only missing keys are added,
44547
+ * so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
44548
+ *
44165
44549
  * @private function of `initializeCoderProjectConfiguration`
44166
44550
  */
44167
44551
  async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, }) {
@@ -44173,21 +44557,24 @@
44173
44557
  const formatting = detectJsonFileFormatting(fileContent);
44174
44558
  const jsonObject = fileContent === undefined ? {} : await parseJsonObjectFile(relativeFilePath, fileContent);
44175
44559
  const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
44176
- let hasChanges = fileContent === undefined;
44560
+ const addedEntryKeys = [];
44177
44561
  const mergedEntries = { ...existingEntries };
44178
44562
  for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
44179
- if (mergedEntries[entryKey] !== entryValue) {
44180
- mergedEntries[entryKey] = entryValue;
44181
- hasChanges = true;
44563
+ if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
44564
+ // Note: The project already defines this entry, keep its own value untouched
44565
+ continue;
44182
44566
  }
44567
+ mergedEntries[entryKey] = entryValue;
44568
+ addedEntryKeys.push(entryKey);
44183
44569
  }
44570
+ const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
44184
44571
  if (!hasChanges) {
44185
- return 'unchanged';
44572
+ return { status: 'unchanged', addedEntryKeys };
44186
44573
  }
44187
44574
  const nextJsonObject = { ...jsonObject };
44188
44575
  nextJsonObject[fieldPath] = mergedEntries;
44189
44576
  await promises.writeFile(absoluteFilePath, serializeJsonObject(nextJsonObject, formatting), 'utf-8');
44190
- return fileContent === undefined ? 'created' : 'updated';
44577
+ return { status: fileContent === undefined ? 'created' : 'updated', addedEntryKeys };
44191
44578
  }
44192
44579
  /**
44193
44580
  * Parses one JSON object file while accepting VS Code style comments and trailing commas.
@@ -44276,6 +44663,8 @@
44276
44663
  /**
44277
44664
  * Ensures `package.json` contains the standalone Promptbook coder helper scripts.
44278
44665
  *
44666
+ * Scripts which the project already defines are kept as they are, only missing ones are added.
44667
+ *
44279
44668
  * @private function of `initializeCoderProjectConfiguration`
44280
44669
  */
44281
44670
  async function ensureCoderPackageJsonFile(projectPath) {
@@ -44329,17 +44718,20 @@
44329
44718
  /**
44330
44719
  * Ensures VS Code routes pasted prompt images into `prompts/screenshots`.
44331
44720
  *
44721
+ * Settings which the project already defines are kept as they are, only missing ones are added.
44722
+ *
44332
44723
  * @private function of `initializeCoderProjectConfiguration`
44333
44724
  */
44334
44725
  async function ensureCoderVscodeSettingsFile(projectPath) {
44335
44726
  const [fieldPath, nextEntries] = resolveDefaultCoderVscodeSettingsEntry();
44336
- return mergeStringRecordJsonFile({
44727
+ const { status } = await mergeStringRecordJsonFile({
44337
44728
  projectPath,
44338
44729
  relativeFilePath: VSCODE_SETTINGS_FILE_PATH,
44339
44730
  fieldPath,
44340
44731
  nextEntries,
44341
44732
  ensureParentDirectoryPath: VSCODE_DIRECTORY_PATH,
44342
44733
  });
44734
+ return status;
44343
44735
  }
44344
44736
  /**
44345
44737
  * Resolves the default string-record entry that `coder init` merges into VS Code settings.
@@ -44356,34 +44748,32 @@
44356
44748
  /**
44357
44749
  * Creates or updates all coder configuration artifacts required in the current project.
44358
44750
  *
44751
+ * Nothing the project already owns is ever overwritten - existing scripts, settings and files are kept as they are,
44752
+ * and the artifacts they would reference are created only together with the scripts which actually reference them.
44753
+ *
44359
44754
  * @private internal utility of `coder init` command
44360
44755
  */
44361
44756
  async function initializeCoderProjectConfiguration(projectPath) {
44362
44757
  const promptsDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DIRECTORY_PATH$1);
44363
44758
  const promptsDoneDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DONE_DIRECTORY_PATH);
44364
44759
  const promptsTemplatesDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_TEMPLATES_DIRECTORY_PATH);
44365
- const promptTemplateFileStatuses = await ensureDefaultCoderPromptTemplateFiles(projectPath);
44366
44760
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
44367
- const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
44368
- const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
44369
- const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
44370
44761
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44371
44762
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44372
- const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
44763
+ const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await ensureCoderPackageJsonFile(projectPath);
44373
44764
  const vscodeSettingsFileStatus = await ensureCoderVscodeSettingsFile(projectPath);
44765
+ const referencedArtifactStatuses = await ensureCoderReferencedArtifacts(projectPath, resolveCoderPackageJsonScriptReferencedArtifactPaths(addedPackageJsonScriptNames));
44374
44766
  return {
44375
44767
  promptsDirectoryStatus,
44376
44768
  promptsDoneDirectoryStatus,
44377
44769
  promptsTemplatesDirectoryStatus,
44378
- promptTemplateFileStatuses,
44379
44770
  agentsDirectoryStatus,
44380
- developerAgentFileStatus,
44381
- agentsFileStatus,
44382
- agentCodingFileStatus,
44383
44771
  envFileStatus,
44384
44772
  gitignoreFileStatus,
44385
44773
  packageJsonFileStatus,
44386
44774
  vscodeSettingsFileStatus,
44775
+ addedPackageJsonScriptNames,
44776
+ referencedArtifactStatuses,
44387
44777
  initializedEnvVariableNames,
44388
44778
  };
44389
44779
  }
@@ -44399,17 +44789,20 @@
44399
44789
  printInitializationStatusLine('prompts/', summary.promptsDirectoryStatus);
44400
44790
  printInitializationStatusLine('prompts/done/', summary.promptsDoneDirectoryStatus);
44401
44791
  printInitializationStatusLine('prompts/templates/', summary.promptsTemplatesDirectoryStatus);
44402
- for (const templateFileStatus of summary.promptTemplateFileStatuses) {
44403
- printInitializationStatusLine(formatDisplayPath(templateFileStatus.relativeFilePath), templateFileStatus.status);
44404
- }
44405
44792
  printInitializationStatusLine('agents/', summary.agentsDirectoryStatus);
44406
- printInitializationStatusLine(CODER_DEVELOPER_AGENT_FILE_PATH, summary.developerAgentFileStatus);
44407
- printInitializationStatusLine(AGENTS_FILE_PATH, summary.agentsFileStatus);
44408
- printInitializationStatusLine(AGENT_CODING_FILE_PATH, summary.agentCodingFileStatus);
44409
44793
  printInitializationStatusLine('.env', summary.envFileStatus);
44410
44794
  printInitializationStatusLine('.gitignore', summary.gitignoreFileStatus);
44411
44795
  printInitializationStatusLine('package.json', summary.packageJsonFileStatus);
44412
44796
  printInitializationStatusLine('.vscode/settings.json', summary.vscodeSettingsFileStatus);
44797
+ for (const referencedArtifactStatus of summary.referencedArtifactStatuses) {
44798
+ printInitializationStatusLine(formatDisplayPath(referencedArtifactStatus.relativeFilePath), referencedArtifactStatus.status);
44799
+ }
44800
+ if (summary.addedPackageJsonScriptNames.length > 0) {
44801
+ printInitializationNote(`Added npm scripts: ${summary.addedPackageJsonScriptNames.join(', ')}`, colors__default["default"].cyan);
44802
+ }
44803
+ else {
44804
+ printInitializationNote('All Promptbook coder npm scripts are already present.', colors__default["default"].gray);
44805
+ }
44413
44806
  if (summary.initializedEnvVariableNames.length > 0) {
44414
44807
  printInitializationNote(`Added env variables: ${summary.initializedEnvVariableNames.join(', ')}`, colors__default["default"].cyan);
44415
44808
  }
@@ -44427,6 +44820,9 @@
44427
44820
  if (status === 'updated') {
44428
44821
  return 'updated';
44429
44822
  }
44823
+ if (status === 'not-referenced') {
44824
+ return 'not referenced by the added scripts, kept as is';
44825
+ }
44430
44826
  return 'unchanged';
44431
44827
  }
44432
44828
  /**
@@ -44469,39 +44865,48 @@
44469
44865
  ${block(listDefaultCoderProjectPromptTemplateDisplayPaths())}
44470
44866
  - ${CODER_DEVELOPER_AGENT_FILE_PATH}
44471
44867
  - ${AGENTS_FILE_PATH}
44472
- - ${AGENT_CODING_FILE_PATH}
44473
44868
  - .gitignore with local artifacts from every supported harness
44474
44869
  - package.json
44475
44870
  - .vscode/settings.json
44476
44871
 
44872
+ Never overwrites what the project already owns:
44873
+ - Existing package.json scripts and .vscode/settings.json settings are kept, only missing ones are added
44874
+ - Files referenced by a script, like the agent and context of coder:run, are created only together
44875
+ with the script which references them
44876
+
44477
44877
  Ensures required coding-agent environment variables in .env:
44478
44878
  - CODING_AGENT_GIT_NAME
44479
44879
  - CODING_AGENT_GIT_EMAIL
44480
44880
  - CODING_AGENT_GIT_SIGNING_KEY
44481
44881
 
44482
- Checks that the coding harnesses are installed and up to date unless \`--no-harness-update\` is used:
44882
+ Checks that the coding harnesses are installed and up to date unless \`--no-questions\` is used:
44483
44883
  ${block(listCheckedHarnessLabels())}
44484
44884
 
44485
44885
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
44886
+
44887
+ ${block(QUESTIONS_DESCRIPTION)}
44486
44888
  `));
44487
44889
  addCoderGitSyncOptions(command);
44488
- addHarnessUpdateOption(command);
44890
+ addQuestionsOption(command);
44489
44891
  command.action(handleActionErrors(async (cliOptions) => {
44490
44892
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44491
- const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
44893
+ const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
44492
44894
  const projectPath = process.cwd();
44493
44895
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
44494
44896
  const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
44495
44897
  const commitScope = await $startCoderGitSync({ gitSync, projectPath });
44898
+ const isPromptsDirectoryEmpty = await isDirectoryEmpty(projectPath, PROMPTS_DIRECTORY_PATH$1);
44496
44899
  const summary = await initializeCoderProjectConfiguration(projectPath);
44497
44900
  printInitializationSummary(summary);
44498
- await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
44901
+ if (isPromptsDirectoryEmpty) {
44902
+ await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
44903
+ }
44499
44904
  await $commitCoderChanges({
44500
44905
  gitSync,
44501
44906
  commitScope,
44502
44907
  commitMessage: 'Initialize Promptbook Coder',
44503
44908
  });
44504
- await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, isHarnessUpdateCheckEnabled);
44909
+ await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, questionsOptions);
44505
44910
  }));
44506
44911
  }
44507
44912
  /**
@@ -44558,6 +44963,23 @@
44558
44963
  // Note: [🟡] Code for CLI prompt priority options [promptPriorityCliOptions](src/cli/cli-commands/common/promptPriorityCliOptions.ts) should never be published outside of `@promptbook/cli`
44559
44964
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
44560
44965
 
44966
+ /**
44967
+ * Description shared by coder commands which accept an agent Book.
44968
+ *
44969
+ * @private internal utility of `ptbk coder`
44970
+ */
44971
+ const CODER_AGENT_OPTION_DESCRIPTION = 'Path to a .book file used to personalize coding prompts and select agent-specific tasks';
44972
+ /**
44973
+ * Registers the shared `--agent` option for coder commands.
44974
+ *
44975
+ * @private internal utility of `ptbk coder`
44976
+ */
44977
+ function addCoderAgentOption(command) {
44978
+ command.option('--agent <agent-book-path>', CODER_AGENT_OPTION_DESCRIPTION);
44979
+ }
44980
+ // Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
44981
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
44982
+
44561
44983
  /**
44562
44984
  * Initializes `coder list` command for Promptbook CLI utilities.
44563
44985
  *
@@ -44575,19 +44997,27 @@
44575
44997
  Features:
44576
44998
  - Lists only ready, fully authored prompts
44577
44999
  - Groups prompts from highest to lowest priority
44578
- - Optional --harness and --model filters show only prompts compatible with that runner
45000
+ - Optional --harness, --model and --agent filters show only prompts compatible with that selection
44579
45001
  - Does not start a coding harness or modify prompt files
44580
45002
  `));
44581
45003
  addPromptRunnerSelectionOptions(command);
45004
+ addCoderAgentOption(command);
44582
45005
  addPromptPriorityOptions(command);
44583
45006
  command.action(handleActionErrors(async (cliOptions) => {
44584
- const { priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
45007
+ const { agent, priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
44585
45008
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
44586
- const promptRunnerIdentity = runnerOptions.agentName === undefined && runnerOptions.model === undefined
45009
+ const resolvedCoderAgentBook = agent === undefined
45010
+ ? undefined
45011
+ : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; }))
45012
+ .resolveCoderAgentBook(agent, process.cwd());
45013
+ const promptRunnerIdentity = runnerOptions.agentName === undefined &&
45014
+ runnerOptions.model === undefined &&
45015
+ resolvedCoderAgentBook === undefined
44587
45016
  ? undefined
44588
45017
  : {
44589
45018
  harnessName: runnerOptions.agentName,
44590
45019
  modelName: runnerOptions.model,
45020
+ agentReferences: resolvedCoderAgentBook === null || resolvedCoderAgentBook === void 0 ? void 0 : resolvedCoderAgentBook.agentReferences,
44591
45021
  };
44592
45022
  // Note: Import dynamically to avoid loading prompt parsing dependencies until this command is used.
44593
45023
  const { listCoderPrompts } = await Promise.resolve().then(function () { return listCoderPrompts$1; });
@@ -44655,7 +45085,7 @@
44655
45085
  *
44656
45086
  * @private internal utility of `ptbk coder`
44657
45087
  */
44658
- async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName) {
45088
+ async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName, questionsOptions) {
44659
45089
  if (harnessName === undefined) {
44660
45090
  return;
44661
45091
  }
@@ -44666,7 +45096,7 @@
44666
45096
  const { label } = getHarnessDefinition(harnessName);
44667
45097
  const formattedRules = formatGitignoreRules(missingRules);
44668
45098
  const entryLabel = missingRules.length === 1 ? 'entry' : 'entries';
44669
- const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now?`);
45099
+ const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now?`, questionsOptions);
44670
45100
  if (!isAdditionApproved) {
44671
45101
  console.info(colors__default["default"].gray(`Skipped, add ${formattedRules} to \`.gitignore\` manually.`));
44672
45102
  return;
@@ -44691,22 +45121,24 @@
44691
45121
  */
44692
45122
  function $initializeCoderPingCommand(program) {
44693
45123
  const command = program.command('ping');
44694
- command.description(_spaceTrim.spaceTrim(`
44695
- Send one tiny dummy prompt to a harness and model to measure and warm them up
45124
+ command.description(_spaceTrim.spaceTrim((block) => `
45125
+ Send one tiny dummy prompt to a harness and model to measure and warm them up
44696
45126
 
44697
- ${PROMPT_RUNNER_DESCRIPTION}
45127
+ ${block(PROMPT_RUNNER_DESCRIPTION)}
44698
45128
 
44699
- Features:
44700
- - Verifies that the selected harness, model, thinking level and authentication really work
44701
- - Reports the answer of the harness, the response time and the reported usage
44702
- - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44703
- - Optional --period keeps the quota window refreshing by pinging once per period until stopped
44704
- - Makes no coding changes or commits; if the selected harness has missing local ignore rules, offers to add them to .gitignore
44705
- - Checks that the selected harness is installed and up to date unless --no-harness-update is used
44706
- - Use --no-ui to stream the raw harness output instead of only the compact result
44707
- `));
45129
+ ${block(QUESTIONS_DESCRIPTION)}
45130
+
45131
+ Features:
45132
+ - Verifies that the selected harness, model, thinking level and authentication really work
45133
+ - Reports the answer of the harness, the response time and the reported usage
45134
+ - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
45135
+ - Optional --period keeps the quota window refreshing by pinging once per period until stopped
45136
+ - Makes no coding changes or commits; if the selected harness has missing local ignore rules, offers to add them to .gitignore
45137
+ - Checks that the selected harness is installed and up to date unless --no-questions is used
45138
+ - Use --no-ui to stream the raw harness output instead of only the compact result
45139
+ `));
44708
45140
  addPromptRunnerSelectionOptions(command);
44709
- addHarnessUpdateOption(command);
45141
+ addQuestionsOption(command);
44710
45142
  addPromptRunnerRuntimeOptions(command);
44711
45143
  command.option('--period <duration>', _spaceTrim.spaceTrim(`
44712
45144
  Keep pinging once per period instead of pinging only once.
@@ -44715,11 +45147,11 @@
44715
45147
  command.action(handleActionErrors(async (cliOptions) => {
44716
45148
  const { period: periodValue } = cliOptions;
44717
45149
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44718
- const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
45150
+ const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
44719
45151
  // Note: The period is validated before the harness installation check, so a mistyped duration fails fast
44720
45152
  const periodMs = parseOptionalPeriodDuration('--period', periodValue);
44721
- await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
44722
- await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
45153
+ await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
45154
+ await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
44723
45155
  const pingOptions = {
44724
45156
  agentName: runnerOptions.agentName,
44725
45157
  model: runnerOptions.model,
@@ -44742,6 +45174,159 @@
44742
45174
  // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44743
45175
  // Note: [💞] Ignore a discrepancy between file name and entity name
44744
45176
 
45177
+ /**
45178
+ * Free disk space below which `ptbk coder` refuses to start a new run.
45179
+ *
45180
+ * A coding round writes prompt and log artifacts, lets the harness rewrite the project, may install npm packages,
45181
+ * may create a git worktree for `--isolate` and usually runs a build, so a run started with less than this
45182
+ * would very likely die somewhere in the middle with a filesystem error.
45183
+ *
45184
+ * @private internal utility of `promptbookCli`
45185
+ */
45186
+ const LOW_FREE_DISK_SPACE_BYTES = 2 * 1024 * 1024 * 1024;
45187
+ /**
45188
+ * Free disk space below which a started `ptbk coder` run stops at its next checkpoint and waits until space is freed.
45189
+ *
45190
+ * This limit is lower than `LOW_FREE_DISK_SPACE_BYTES`, because interrupting a run which is already in progress
45191
+ * is more expensive than not starting one at all.
45192
+ *
45193
+ * @private internal utility of `promptbookCli`
45194
+ */
45195
+ const CRITICALLY_LOW_FREE_DISK_SPACE_BYTES = 1 * 1024 * 1024 * 1024;
45196
+ /**
45197
+ * How long one free disk space measurement is reused before the filesystem is measured again during a run.
45198
+ *
45199
+ * Pause checkpoints are passed very often - for example on every tick of a timed wait - so without this
45200
+ * interval the filesystem would be measured many times per second.
45201
+ *
45202
+ * @private internal utility of `promptbookCli`
45203
+ */
45204
+ const FREE_DISK_SPACE_RECHECK_INTERVAL_MS = 60 * 1000;
45205
+ // Note: [🟡] Code for CLI free disk space handling [freeDiskSpaceConstants](src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.ts) should never be published outside of `@promptbook/cli`
45206
+ // Note: [💞] Ignore a discrepancy between file name and entity name
45207
+
45208
+ /**
45209
+ * Classifies one measured amount of free disk space against the `ptbk coder` free disk space limits.
45210
+ *
45211
+ * @private internal utility of `promptbookCli`
45212
+ */
45213
+ function resolveFreeDiskSpaceLevel(availableBytes) {
45214
+ if (availableBytes < CRITICALLY_LOW_FREE_DISK_SPACE_BYTES) {
45215
+ return 'critical';
45216
+ }
45217
+ if (availableBytes < LOW_FREE_DISK_SPACE_BYTES) {
45218
+ return 'low';
45219
+ }
45220
+ return 'sufficient';
45221
+ }
45222
+ // Note: [🟡] Code for CLI free disk space handling [resolveFreeDiskSpaceLevel](src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.ts) should never be published outside of `@promptbook/cli`
45223
+
45224
+ /**
45225
+ * Measures the free disk space on the filesystem which hosts the given path.
45226
+ *
45227
+ * Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
45228
+ *
45229
+ * @returns the measurement, or `null` when the filesystem can not be measured, for example on a platform which
45230
+ * does not report filesystem statistics - a run is never stopped by a measurement which could not be taken
45231
+ * @private internal utility of `promptbookCli`
45232
+ */
45233
+ async function $readFreeDiskSpaceStatus(inspectedPath) {
45234
+ try {
45235
+ const fileSystemStats = await promises.statfs(inspectedPath);
45236
+ const blockSizeBytes = fileSystemStats.bsize;
45237
+ const availableBytes = fileSystemStats.bavail * blockSizeBytes;
45238
+ const totalBytes = fileSystemStats.blocks * blockSizeBytes;
45239
+ if (!Number.isFinite(availableBytes) || availableBytes < 0) {
45240
+ return null;
45241
+ }
45242
+ return {
45243
+ inspectedPath,
45244
+ availableBytes,
45245
+ totalBytes,
45246
+ level: resolveFreeDiskSpaceLevel(availableBytes),
45247
+ };
45248
+ }
45249
+ catch (_a) {
45250
+ // Note: A path or a platform which can not report its free disk space must not stop `ptbk coder` from running
45251
+ return null;
45252
+ }
45253
+ }
45254
+ // Note: [🟡] Code for CLI free disk space handling [$readFreeDiskSpaceStatus](src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.ts) should never be published outside of `@promptbook/cli`
45255
+
45256
+ /**
45257
+ * Byte units used when one amount of disk space is written for a human.
45258
+ */
45259
+ const FREE_DISK_SPACE_BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
45260
+ /**
45261
+ * Writes one byte count as a compact human-readable amount of disk space, for example `812 MB` or `1.5 GB`.
45262
+ *
45263
+ * @private internal utility of `promptbookCli`
45264
+ */
45265
+ function formatFreeDiskSpaceBytes(bytes) {
45266
+ if (!Number.isFinite(bytes) || bytes <= 0) {
45267
+ return '0 B';
45268
+ }
45269
+ const unitIndex = Math.min(FREE_DISK_SPACE_BYTE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
45270
+ const value = bytes / Math.pow(1024, unitIndex);
45271
+ // Note: One decimal place only matters for small values, `476 GB` is more readable than `476.3 GB`
45272
+ const decimalPlaceCount = value >= 10 || unitIndex === 0 ? 0 : 1;
45273
+ // Note: An empty decimal place only adds noise, `2 GB` is more readable than `2.0 GB`
45274
+ const displayedValue = value.toFixed(decimalPlaceCount).replace(/\.0$/, '');
45275
+ return `${displayedValue} ${FREE_DISK_SPACE_BYTE_UNITS[unitIndex]}`;
45276
+ }
45277
+ // Note: [🟡] Code for CLI free disk space handling [formatFreeDiskSpaceBytes](src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.ts) should never be published outside of `@promptbook/cli`
45278
+
45279
+ /**
45280
+ * Writes the shared warning which explains one low free disk space measurement and how to get out of it.
45281
+ *
45282
+ * The very same wording is used by the check which refuses to start a run and by the check which pauses
45283
+ * a run that ran out of disk space while it was working.
45284
+ *
45285
+ * @private internal utility of `promptbookCli`
45286
+ */
45287
+ function formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus) {
45288
+ const { inspectedPath } = freeDiskSpaceStatus;
45289
+ const availableSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes);
45290
+ const totalSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.totalBytes);
45291
+ const requiredSpaceToStart = formatFreeDiskSpaceBytes(LOW_FREE_DISK_SPACE_BYTES);
45292
+ const requiredSpaceToContinue = formatFreeDiskSpaceBytes(CRITICALLY_LOW_FREE_DISK_SPACE_BYTES);
45293
+ return _spaceTrim.spaceTrim(`
45294
+ Only **${availableSpace}** of ${totalSpace} is left on the disk which hosts \`${inspectedPath}\`.
45295
+
45296
+ **\`ptbk coder\` needs at least ${requiredSpaceToStart} of free disk space to start a run and at least ${requiredSpaceToContinue} to keep one running.**
45297
+
45298
+ Actionable hints:
45299
+ - Free up disk space on the drive which hosts \`${inspectedPath}\`.
45300
+ - Remove build artifacts, package caches and \`node_modules\` folders of projects you are not working on.
45301
+ - Remove the temporary worktrees which earlier \`--isolate\` runs left behind.
45302
+ `);
45303
+ }
45304
+ // Note: [🟡] Code for CLI free disk space handling [formatLowFreeDiskSpaceWarning](src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.ts) should never be published outside of `@promptbook/cli`
45305
+
45306
+ /**
45307
+ * Refuses to start one `ptbk coder` command when the disk which hosts the project is nearly full.
45308
+ *
45309
+ * A run which starts without enough free disk space fails somewhere in the middle, after the coding agent has
45310
+ * already rewritten a part of the project, so it is stopped before it writes anything at all.
45311
+ *
45312
+ * Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
45313
+ *
45314
+ * @throws {LimitReachedError} when there is not enough free disk space left to start
45315
+ * @private internal utility of `promptbookCli`
45316
+ */
45317
+ async function $assertSufficientFreeDiskSpace(inspectedPath) {
45318
+ const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
45319
+ if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level === 'sufficient') {
45320
+ return;
45321
+ }
45322
+ throw new LimitReachedError(_spaceTrim.spaceTrim((block) => `
45323
+ There is not enough free disk space to run \`ptbk coder\`.
45324
+
45325
+ ${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
45326
+ `));
45327
+ }
45328
+ // Note: [🟡] Code for CLI free disk space handling [$assertSufficientFreeDiskSpace](src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.ts) should never be published outside of `@promptbook/cli`
45329
+
44745
45330
  /**
44746
45331
  * Npm packages which directly provide the `ptbk` executable.
44747
45332
  *
@@ -45070,7 +45655,11 @@
45070
45655
  * @returns `true` when at least one installation was updated and the current coder run should stop
45071
45656
  * @private internal utility of `promptbookCli`
45072
45657
  */
45073
- async function $ensurePromptbookCliInstallations() {
45658
+ async function $ensurePromptbookCliInstallations(questionsOptions) {
45659
+ if (!questionsOptions.isAskingQuestionsEnabled) {
45660
+ // Note: `--no-questions` forbids asking for the approval, so the registry checks would be useless
45661
+ return false;
45662
+ }
45074
45663
  if (!process.stdin.isTTY) {
45075
45664
  // Note: Non-interactive runs cannot approve an update and should not wait for registry checks
45076
45665
  return false;
@@ -45082,7 +45671,7 @@
45082
45671
  return false;
45083
45672
  }
45084
45673
  console.warn(colors__default["default"].yellow(formatPromptbookCliInstallationWarning(outdatedStatuses)));
45085
- const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?');
45674
+ const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?', questionsOptions);
45086
45675
  if (!isUpdateApproved) {
45087
45676
  reportManualPromptbookCliUpdateCommands(outdatedStatuses);
45088
45677
  return false;
@@ -45149,6 +45738,17 @@
45149
45738
  return TEST_BEFORE_MODE_VALUES.includes(value);
45150
45739
  }
45151
45740
 
45741
+ /**
45742
+ * Prints why a coder run has ended to the terminal.
45743
+ *
45744
+ * @private internal utility of `coder run` and `coder server` commands
45745
+ */
45746
+ function printCoderRunFailure(error) {
45747
+ console.error(colors__default["default"].bgRed(`${error.name}`));
45748
+ console.error(colors__default["default"].red(formatUnknownErrorDetails(error)));
45749
+ }
45750
+ // Note: [🟡] Code for coder failure reporting [printCoderRunFailure](src/cli/cli-commands/coder/printCoderRunFailure.ts) should never be published outside of `@promptbook/cli`
45751
+
45152
45752
  /**
45153
45753
  * Initializes `coder run` command for Promptbook CLI utilities
45154
45754
  *
@@ -45158,10 +45758,12 @@
45158
45758
  */
45159
45759
  function $initializeCoderRunCommand(program) {
45160
45760
  const command = program.command('run');
45161
- command.description(_spaceTrim.spaceTrim(`
45761
+ command.description(_spaceTrim.spaceTrim((block) => `
45162
45762
  Execute coding prompts through selected AI agent
45163
45763
 
45164
- ${PROMPT_RUNNER_DESCRIPTION}
45764
+ ${block(PROMPT_RUNNER_DESCRIPTION)}
45765
+
45766
+ ${block(QUESTIONS_DESCRIPTION)}
45165
45767
 
45166
45768
  Features:
45167
45769
  - Automatically stages and commits changes with agent identity unless --no-commit is used
@@ -45171,9 +45773,11 @@
45171
45773
  - Optional post-commit git push with explicit --auto-push opt-in
45172
45774
  - Optional pre-prompt git pull with explicit --auto-pull opt-in
45173
45775
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
45776
+ - Saves the run trace of every round into prompts/traces/, named after its prompt file
45174
45777
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
45175
45778
  - Optional --no-ui keeps plain streaming console output for logging and debugging
45176
- - Checks that the selected harness is installed and up to date before the first prompt unless --no-harness-update is used
45779
+ - Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
45780
+ - Checks that the selected harness is installed and up to date before the first prompt unless --no-questions is used
45177
45781
  - Offers to add missing project-local ignore rules for the selected harness
45178
45782
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
45179
45783
  - Supports GPG signing of commits
@@ -45184,8 +45788,8 @@
45184
45788
  `));
45185
45789
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
45186
45790
  addPromptRunnerSelectionOptions(command);
45187
- addHarnessUpdateOption(command);
45188
- command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
45791
+ addQuestionsOption(command);
45792
+ addCoderAgentOption(command);
45189
45793
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
45190
45794
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
45191
45795
  command.addOption(new commander.Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
@@ -45224,7 +45828,7 @@
45224
45828
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
45225
45829
  isAgentRequired: !dryRun,
45226
45830
  });
45227
- const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
45831
+ const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
45228
45832
  // [1] Parse the wait options and --no-auto:
45229
45833
  // default: run automatically through the queue (no waiting between prompts)
45230
45834
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
@@ -45232,11 +45836,15 @@
45232
45836
  // --wait-between-prompts: pace from start of one prompt to start of next
45233
45837
  // --wait-after-error: wait before retrying after an error (default 10m)
45234
45838
  const waitForUser = !auto;
45235
- if (await $ensurePromptbookCliInstallations()) {
45839
+ assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
45840
+ // Note: Checked before anything is installed or written, because installing a harness, updating the
45841
+ // Promptbook CLI and coding itself all need the disk space which is missing
45842
+ await $assertSufficientFreeDiskSpace(process.cwd());
45843
+ if (await $ensurePromptbookCliInstallations(questionsOptions)) {
45236
45844
  return process.exit(0);
45237
45845
  }
45238
- await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
45239
- await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
45846
+ await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
45847
+ await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
45240
45848
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
45241
45849
  const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
45242
45850
  const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
@@ -45269,6 +45877,7 @@
45269
45877
  allowDestructiveAutoMigrate,
45270
45878
  autoPush: runnerOptions.autoPush,
45271
45879
  autoPull: runnerOptions.autoPull,
45880
+ isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
45272
45881
  };
45273
45882
  // Note: Import the function dynamically to avoid loading heavy dependencies until needed
45274
45883
  const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
@@ -45278,8 +45887,7 @@
45278
45887
  }
45279
45888
  catch (error) {
45280
45889
  assertsError(error);
45281
- console.error(colors__default["default"].bgRed(`${error.name}`));
45282
- console.error(colors__default["default"].red(error.stack || error.message));
45890
+ printCoderRunFailure(error);
45283
45891
  return process.exit(1);
45284
45892
  }
45285
45893
  return process.exit(0);
@@ -45323,28 +45931,31 @@
45323
45931
  */
45324
45932
  function $initializeCoderServerCommand(program) {
45325
45933
  const command = program.command('server');
45326
- command.description(_spaceTrim.spaceTrim(`
45327
- Start a coder server that watches for prompts and serves a kanban web UI
45934
+ command.description(_spaceTrim.spaceTrim((block) => `
45935
+ Start a coder server that watches for prompts and serves a kanban web UI
45328
45936
 
45329
- ${PROMPT_RUNNER_DESCRIPTION}
45937
+ ${block(PROMPT_RUNNER_DESCRIPTION)}
45330
45938
 
45331
- Features:
45332
- - Runs the same prompt processing as \`ptbk coder run\`
45333
- - Checks that the selected harness is installed and up to date on startup unless --no-harness-update is used
45334
- - Offers to add missing project-local ignore rules for the selected harness
45335
- - Does not exit when all prompts are done; polls for new prompt files instead
45336
- - Serves a kanban board at http://localhost:<port> for visual progress tracking
45337
- - Allows editing prompt files directly from the browser (Trello-style)
45338
- - Play / pause button in the browser stays in sync with the CLI pause state
45339
- - Terminal controls match \`ptbk coder run\`: P pause/resume, S skip waiting, X end after current prompt
45340
- `));
45939
+ ${block(QUESTIONS_DESCRIPTION)}
45940
+
45941
+ Features:
45942
+ - Runs the same prompt processing as \`ptbk coder run\`
45943
+ - Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
45944
+ - Checks that the selected harness is installed and up to date on startup unless --no-questions is used
45945
+ - Offers to add missing project-local ignore rules for the selected harness
45946
+ - Does not exit when all prompts are done; polls for new prompt files instead
45947
+ - Serves a kanban board at http://localhost:<port> for visual progress tracking
45948
+ - Allows editing prompt files directly from the browser (Trello-style)
45949
+ - Play / pause button in the browser stays in sync with the CLI pause state
45950
+ - Terminal controls match \`ptbk coder run\`: P pause/resume, S skip waiting, X end after current prompt
45951
+ `));
45341
45952
  command.addOption(new commander.Option('--port <port>', 'Port to start the coder server on')
45342
45953
  .env('PTBK_CODER_SERVER_PORT')
45343
45954
  .default(DEFAULT_CODER_SERVER_PORT));
45344
45955
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
45345
45956
  addPromptRunnerSelectionOptions(command);
45346
- addHarnessUpdateOption(command);
45347
- command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
45957
+ addQuestionsOption(command);
45958
+ addCoderAgentOption(command);
45348
45959
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
45349
45960
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
45350
45961
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
@@ -45373,11 +45984,15 @@
45373
45984
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
45374
45985
  isAgentRequired: !dryRun,
45375
45986
  });
45376
- const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
45377
- await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
45378
- await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
45987
+ const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
45379
45988
  // [1] Parse the wait options and --no-auto (same logic as `coder run`)
45380
45989
  const waitForUser = !auto;
45990
+ assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
45991
+ // Note: Checked before anything is installed or written, because installing a harness and coding
45992
+ // itself both need the disk space which is missing
45993
+ await $assertSufficientFreeDiskSpace(process.cwd());
45994
+ await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
45995
+ await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
45381
45996
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
45382
45997
  const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
45383
45998
  const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
@@ -45407,6 +46022,7 @@
45407
46022
  allowDestructiveAutoMigrate,
45408
46023
  autoPush: runnerOptions.autoPush,
45409
46024
  autoPull: runnerOptions.autoPull,
46025
+ isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
45410
46026
  };
45411
46027
  // Note: Import dynamically to avoid loading heavy dependencies until needed
45412
46028
  const { runCodexPromptsServer } = await Promise.resolve().then(function () { return runCodexPromptsServer$1; });
@@ -45415,8 +46031,7 @@
45415
46031
  }
45416
46032
  catch (error) {
45417
46033
  assertsError(error);
45418
- console.error(colors__default["default"].bgRed(`${error.name}`));
45419
- console.error(colors__default["default"].red(error.stack || error.message));
46034
+ printCoderRunFailure(error);
45420
46035
  return process.exit(1);
45421
46036
  }
45422
46037
  return process.exit(0);
@@ -45605,11 +46220,7 @@
45605
46220
  $initializeCoderVerifyCommand(coderCommand);
45606
46221
  $initializeCoderFindFreshEmojiTagCommand(coderCommand);
45607
46222
  // If no subcommand is provided, show help
45608
- coderCommand.action(() => {
45609
- console.info(colors__default["default"].yellow('Please specify a subcommand.'));
45610
- console.info('');
45611
- coderCommand.help();
45612
- });
46223
+ $requireCliSubcommand(coderCommand);
45613
46224
  }
45614
46225
  // Note: [🟡] Code for CLI command [coder](src/cli/cli-commands/coder.ts) should never be published outside of `@promptbook/cli`
45615
46226
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -56199,7 +56810,9 @@
56199
56810
  * @private internal function of `promptbookCli`
56200
56811
  */
56201
56812
  function $initializeRunCommand(program) {
56202
- const runCommand = program.command('run', { isDefault: true });
56813
+ // Note: `run` is deprecated, so it is intentionally NOT the default command of `ptbk`
56814
+ // and it must be asked for explicitly
56815
+ const runCommand = program.command('run');
56203
56816
  configureRunCommand(runCommand);
56204
56817
  runCommand.action(handleActionErrors((pipelineSource, cliOptions) => runCommandAction(pipelineSource, cliOptions)));
56205
56818
  }
@@ -58038,9 +58651,24 @@
58038
58651
  command.option('--remote-server-url <url>', `URL of remote server to use when `, DEFAULT_REMOTE_SERVER_URL);
58039
58652
  }
58040
58653
 
58654
+ /**
58655
+ * Keeps one already registered CLI command out of the help of its parent command while the command itself stays
58656
+ * fully usable - it can still be run, it still has its own help and it is still reachable through `ptbk help <command>`.
58657
+ *
58658
+ * Note: `$` is used to indicate that this function is not a pure function - it changes how the CLI renders its help
58659
+ *
58660
+ * @private utility of CLI
58661
+ */
58662
+ function $hideCliCommandFromHelp(command) {
58663
+ command._hidden = true;
58664
+ }
58665
+
58041
58666
  /**
58042
58667
  * Marks one CLI command as deprecated while keeping it available for existing callers.
58043
58668
  *
58669
+ * Deprecated commands are not advertised anymore - they are hidden from the list of commands in the help of their
58670
+ * parent command, they warn when they are used and they explain the deprecation in their own help.
58671
+ *
58044
58672
  * @private utility of CLI
58045
58673
  */
58046
58674
  function $deprecateCliCommand(command, deprecationMessage) {
@@ -58052,6 +58680,7 @@
58052
58680
  command.hook('preAction', () => {
58053
58681
  console.warn(colors__default["default"].yellow(createDeprecatedCliCommandWarning(command, deprecationMessage)));
58054
58682
  });
58683
+ $hideCliCommandFromHelp(command);
58055
58684
  }
58056
58685
  /**
58057
58686
  * Creates the runtime warning printed before a deprecated CLI command action starts.
@@ -58119,10 +58748,17 @@
58119
58748
  program.version(PROMPTBOOK_ENGINE_VERSION);
58120
58749
  program.description(CLAIM);
58121
58750
  // Note: These options are valid for all commands
58751
+ // Note: Commands are listed in the help in the order they are registered,
58752
+ // `coder` is the most used one so it goes first and the deprecated ones,
58753
+ // which `$deprecateTopLevelCommands` hides from the help entirely, go last
58754
+ $initializeCoderCommand(program);
58755
+ $initializeAgentCommand(program);
58756
+ $initializeAgentFolderCommand(program);
58757
+ $initializeAgentsServerCommand(program);
58122
58758
  $initializeAboutCommand(program);
58759
+ $initializeHelloCommand(program);
58123
58760
  $initializeRunCommand(program);
58124
58761
  $initializeLoginCommand(program);
58125
- $initializeHelloCommand(program);
58126
58762
  $initializeMakeCommand(program);
58127
58763
  $initializePrettifyCommand(program);
58128
58764
  $initializeTestCommand(program);
@@ -58130,16 +58766,13 @@
58130
58766
  $initializeListScrapersCommand(program);
58131
58767
  $initializeStartAgentsServerCommand(program);
58132
58768
  $initializeStartPipelinesServerCommand(program);
58133
- $initializeAgentCommand(program);
58134
- $initializeAgentFolderCommand(program);
58135
- $initializeAgentsServerCommand(program);
58136
- $initializeCoderCommand(program);
58137
58769
  $deprecateTopLevelCommands(program);
58138
58770
  // TODO: [🧠] Should it be here or not> $addGlobalOptionsToCommand(program);
58139
58771
  program.commands.forEach($addGlobalOptionsToCommand);
58772
+ // Note: There is no default command, so `ptbk` without a subcommand asks for one
58773
+ // instead of running the deprecated `ptbk run`
58140
58774
  if (commandLineArguments.length === 0) {
58141
- program.outputHelp();
58142
- return process.exit(0);
58775
+ return $reportMissingCliSubcommand(program);
58143
58776
  }
58144
58777
  program.parse(process.argv);
58145
58778
  }
@@ -58153,7 +58786,7 @@
58153
58786
  return firstCommandLineArgument !== undefined && VERSION_OPTION_ARGUMENTS.has(firstCommandLineArgument);
58154
58787
  }
58155
58788
  /**
58156
- * Adds one deprecation notice to each configured top-level legacy command.
58789
+ * Marks each configured top-level legacy command as deprecated, which keeps it usable but takes it out of the help.
58157
58790
  */
58158
58791
  function $deprecateTopLevelCommands(program) {
58159
58792
  for (const command of program.commands) {
@@ -58164,7 +58797,6 @@
58164
58797
  }
58165
58798
  }
58166
58799
  // Note: [🟡] Code for CLI program [promptbookCli](src/cli/promptbookCli.ts) should never be published outside of `@promptbook/cli`
58167
- // TODO: [🧠] Maybe `run` command the default, instead of `ptbk run ./foo.book` -> `ptbk ./foo.book`
58168
58800
  // TODO: [🥠] Do not export, its just for CLI script
58169
58801
  // TODO: [🕌] When more functionalities, rename
58170
58802
  // Note: 11:11
@@ -59276,7 +59908,9 @@
59276
59908
  */
59277
59909
  function createExecutionToolsFromVercelProvider(options) {
59278
59910
  let { title, description } = options;
59279
- const { vercelProvider, availableModels, userId, additionalChatSettings = {} } = options;
59911
+ const { vercelProvider, availableModels, additionalChatSettings = {} } = options;
59912
+ // <- Note: `userId` is not forwarded, because Vercel AI SDK moved provider-specific settings like `user` from
59913
+ // `vercelProvider.chat(modelName, settings)` into `providerOptions` which are keyed by the provider
59280
59914
  if (!/Vercel/i.test(title)) {
59281
59915
  title = `${title} (through Vercel)`;
59282
59916
  // <- TODO: [🧈] Maybe standartize the suffix
@@ -59317,10 +59951,7 @@
59317
59951
 
59318
59952
  `));
59319
59953
  }
59320
- const model = await vercelProvider.chat(modelName, {
59321
- user: (userId === null || userId === void 0 ? void 0 : userId.toString()) || undefined,
59322
- ...additionalChatSettings,
59323
- });
59954
+ const model = await vercelProvider.chat(modelName);
59324
59955
  const rawPromptContent = templateParameters(content, { ...parameters, modelName });
59325
59956
  // Support for passing a chat thread (multi-message conversation)
59326
59957
  let promptMessages;
@@ -59358,14 +59989,9 @@
59358
59989
  }
59359
59990
  const rawRequest = {
59360
59991
  // <- TODO: [☂]
59361
- inputFormat: 'messages',
59362
- mode: {
59363
- type: 'regular',
59364
- tools: [
59365
- /* <- TODO: Pass the tools */
59366
- ],
59367
- },
59368
59992
  prompt: promptMessages,
59993
+ // <- TODO: Pass the tools via `tools`
59994
+ ...additionalChatSettings,
59369
59995
  };
59370
59996
  const start = $getCurrentDate();
59371
59997
  if (options.isVerbose) {
@@ -59382,28 +60008,31 @@
59382
60008
  if (options.isVerbose) {
59383
60009
  console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
59384
60010
  }
59385
- if (rawResponse.text === undefined) {
60011
+ // Note: Vercel AI SDK returns ordered content parts, the chat message is made only of the text parts
60012
+ const responseTextParts = rawResponse.content.flatMap((contentPart) => contentPart.type === 'text' ? [contentPart.text] : []);
60013
+ if (responseTextParts.length === 0) {
59386
60014
  throw new PipelineExecutionError('No response message');
59387
60015
  }
60016
+ const responseText = responseTextParts.join('');
59388
60017
  const complete = $getCurrentDate();
59389
60018
  const duration = uncertainNumber((new Date(complete).getTime() - new Date(start).getTime()) / 1000);
59390
60019
  const usage = {
59391
60020
  price: UNCERTAIN_ZERO_VALUE,
59392
60021
  duration,
59393
60022
  input: {
59394
- tokensCount: uncertainNumber(rawResponse.usage.promptTokens),
60023
+ tokensCount: uncertainNumber(rawResponse.usage.inputTokens),
59395
60024
  ...computeUsageCounts(rawPromptContent),
59396
60025
  },
59397
60026
  output: {
59398
- tokensCount: uncertainNumber(rawResponse.usage.completionTokens),
59399
- ...computeUsageCounts(rawResponse.text),
60027
+ tokensCount: uncertainNumber(rawResponse.usage.outputTokens),
60028
+ ...computeUsageCounts(responseText),
59400
60029
  },
59401
60030
  };
59402
60031
  return exportJson({
59403
60032
  name: 'promptResult',
59404
60033
  message: `Result of \`createExecutionToolsFromVercelProvider.callChatModel\``,
59405
60034
  value: {
59406
- content: rawResponse.text,
60035
+ content: responseText,
59407
60036
  modelName,
59408
60037
  timing: {
59409
60038
  start,
@@ -74708,13 +75337,15 @@
74708
75337
  }
74709
75338
 
74710
75339
  /**
74711
- * Checks whether a prompt is unrestricted or matches the selected harness/model.
75340
+ * Checks whether a prompt is unrestricted or matches the selected harness, model or Book agent.
74712
75341
  *
74713
- * A prompt status line can contain one or more backtick-delimited model or harness
74714
- * names. Matching is intentionally based on normalized substrings so a token such as
74715
- * `gpt` selects any `gpt-*` model and `opus` selects a `claude-opus-*` model.
75342
+ * A prompt status line can contain one or more backtick-delimited model, harness or agent
75343
+ * references. Matching is intentionally based on normalized substrings so a token such as
75344
+ * `gpt` selects any `gpt-*` model, `developer` selects an agent Book named `developer.book`,
75345
+ * and `opus` selects a `claude-opus-*` model.
74716
75346
  */
74717
75347
  function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
75348
+ var _a;
74718
75349
  if (promptRunnerIdentity === undefined) {
74719
75350
  return true;
74720
75351
  }
@@ -74723,7 +75354,11 @@
74723
75354
  if (requiredRunnerTokens.length === 0) {
74724
75355
  return true;
74725
75356
  }
74726
- const normalizedRunnerNames = [promptRunnerIdentity.harnessName, promptRunnerIdentity.modelName]
75357
+ const normalizedRunnerNames = [
75358
+ promptRunnerIdentity.harnessName,
75359
+ promptRunnerIdentity.modelName,
75360
+ ...((_a = promptRunnerIdentity.agentReferences) !== null && _a !== void 0 ? _a : []),
75361
+ ]
74727
75362
  .filter((name) => name !== undefined && name.trim() !== '')
74728
75363
  .map((name) => normalizeToKebabCase(name))
74729
75364
  .filter((name) => name !== '');
@@ -74734,7 +75369,7 @@
74734
75369
  });
74735
75370
  }
74736
75371
  /**
74737
- * Extracts model and harness tokens from a prompt status line.
75372
+ * Extracts model, harness and Book-agent tokens from a prompt status line.
74738
75373
  */
74739
75374
  function extractPromptRunnerTokens(statusLine) {
74740
75375
  return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
@@ -74749,7 +75384,7 @@
74749
75384
  *
74750
75385
  * @private internal constant of `parsePromptFile`
74751
75386
  */
74752
- const PROMPT_STATUS_MARKER_PATTERN = /^\[(?: |-|[xX]|\^|!)\]/u;
75387
+ const PROMPT_STATUS_LINE_PATTERN = /^\[(?<marker> |-|\.|[xX]|\^|!)\](?<details>.*)$/u;
74753
75388
  /**
74754
75389
  * Parses a prompt markdown file into sections and metadata.
74755
75390
  */
@@ -74773,7 +75408,7 @@
74773
75408
  if (firstNonEmptyLine !== undefined) {
74774
75409
  const statusLine = (lines[firstNonEmptyLine] || '').trim();
74775
75410
  const parsedStatus = parseStatusLine(statusLine);
74776
- const status = (_a = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.status) !== null && _a !== void 0 ? _a : (hasPromptStatusMarker(statusLine) ? 'not-ready' : 'todo');
75411
+ const status = (_a = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.status) !== null && _a !== void 0 ? _a : (PROMPT_STATUS_LINE_PATTERN.test(statusLine) ? 'not-ready' : 'todo');
74777
75412
  const priority = (_b = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.priority) !== null && _b !== void 0 ? _b : 0;
74778
75413
  sections.push({
74779
75414
  index,
@@ -74801,51 +75436,47 @@
74801
75436
  * For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
74802
75437
  */
74803
75438
  function parseStatusLine(line) {
74804
- var _a, _b, _c, _d;
74805
- // For done prompts [x], allow any content after (for cost/time metadata)
74806
- const doneMatch = line.match(/^\[(?<status>[xX])\]/);
74807
- if (doneMatch) {
75439
+ var _a, _b, _c, _d, _e;
75440
+ const statusLineMatch = line.match(PROMPT_STATUS_LINE_PATTERN);
75441
+ const marker = (_a = statusLineMatch === null || statusLineMatch === void 0 ? void 0 : statusLineMatch.groups) === null || _a === void 0 ? void 0 : _a.marker;
75442
+ const details = (_c = (_b = statusLineMatch === null || statusLineMatch === void 0 ? void 0 : statusLineMatch.groups) === null || _b === void 0 ? void 0 : _b.details) !== null && _c !== void 0 ? _c : '';
75443
+ if (marker === 'x' || marker === 'X') {
75444
+ // For done prompts [x], allow any content after (for cost/time metadata).
74808
75445
  return { status: 'done', priority: 0 };
74809
75446
  }
74810
- // For failed prompts [!], allow any content after (for failure metadata)
74811
- const failedMatch = line.match(/^\[(?<status>!)\]/);
74812
- if (failedMatch) {
75447
+ if (marker === '!') {
75448
+ // For failed prompts [!], allow any content after (for failure metadata).
74813
75449
  return { status: 'failed', priority: 0 };
74814
75450
  }
74815
- // For in-progress prompts [^], allow any content after (for the steps recorded so far)
74816
- const inProgressMatch = line.match(/^\[(?<status>\^)\]/);
74817
- if (inProgressMatch) {
75451
+ if (marker === '^') {
75452
+ // For in-progress prompts [^], allow any content after (for the steps recorded so far).
74818
75453
  return { status: 'in-progress', priority: 0 };
74819
75454
  }
74820
- // For not-ready [-], keep the historical clean-line syntax.
74821
- if (/^\[-\]\s*!*\s*$/u.test(line)) {
75455
+ // Treat [.] as the alternative spelling of not-ready [-], preserving the historical clean-line syntax.
75456
+ if ((marker === '-' || marker === '.') && /^[!\s]*$/u.test(details)) {
74822
75457
  return { status: 'not-ready', priority: 0 };
74823
75458
  }
74824
- // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
74825
- // before or after those tokens. Other trailing text remains an invalid status line.
74826
- const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
74827
- if (!todoMatch) {
75459
+ if (marker !== ' ') {
74828
75460
  return undefined;
74829
75461
  }
74830
- const details = (_b = (_a = todoMatch.groups) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : '';
75462
+ // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
75463
+ // before or after those tokens. Other trailing text remains an invalid status line.
74831
75464
  const isPriorityOnly = /^[!\s]*$/u.test(details);
74832
75465
  const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
74833
75466
  if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
74834
75467
  return undefined;
74835
75468
  }
74836
- return { status: 'todo', priority: (_d = (_c = details.match(/!/gu)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0 };
75469
+ return { status: 'todo', priority: (_e = (_d = details.match(/!/gu)) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0 };
74837
75470
  }
75471
+
74838
75472
  /**
74839
- * Checks whether a line starts with one of the supported prompt status markers.
75473
+ * HTML comment which excludes a Markdown file from all `ptbk coder` queues.
74840
75474
  *
74841
- * @private internal utility of `parsePromptFile`
75475
+ * @private internal constant of `loadPromptFiles`
74842
75476
  */
74843
- function hasPromptStatusMarker(line) {
74844
- return PROMPT_STATUS_MARKER_PATTERN.test(line);
74845
- }
74846
-
75477
+ const PTBK_CODER_IGNORE_MARKER = '<!--ptbk-coder-ignore-->';
74847
75478
  /**
74848
- * Loads and parses prompt files from the prompts directory.
75479
+ * Loads and parses prompt files from the prompts directory, excluding files marked for `ptbk coder` to ignore.
74849
75480
  */
74850
75481
  async function loadPromptFiles(promptsDir) {
74851
75482
  const entries = await promises.readdir(promptsDir, { withFileTypes: true });
@@ -74856,6 +75487,9 @@
74856
75487
  const promptFiles = [];
74857
75488
  for (const filePath of files) {
74858
75489
  const content = await promises.readFile(filePath, 'utf-8');
75490
+ if (content.includes(PTBK_CODER_IGNORE_MARKER)) {
75491
+ continue;
75492
+ }
74859
75493
  promptFiles.push(parsePromptFile(filePath, content));
74860
75494
  }
74861
75495
  return promptFiles;
@@ -74965,6 +75599,77 @@
74965
75599
  findUnwrittenPrompts: findUnwrittenPrompts
74966
75600
  });
74967
75601
 
75602
+ /**
75603
+ * Reads an optional agent `.book` file and prepares the references that route prompts to it.
75604
+ *
75605
+ * Returns `undefined` when no agent path is provided.
75606
+ */
75607
+ async function resolveCoderAgentBook(agentBookReference, currentWorkingDirectory) {
75608
+ const normalizedAgentBookReference = agentBookReference === null || agentBookReference === void 0 ? void 0 : agentBookReference.trim();
75609
+ if (!normalizedAgentBookReference) {
75610
+ return undefined;
75611
+ }
75612
+ const resolvedAgentBookPath = path.resolve(currentWorkingDirectory, normalizedAgentBookReference);
75613
+ const agentSource = (await promises.readFile(resolvedAgentBookPath, 'utf-8').catch((error) => {
75614
+ if (error.code === 'ENOENT' || error.code === 'EISDIR') {
75615
+ throw new NotFoundError(spaceTrim(`
75616
+ Agent book \`${normalizedAgentBookReference}\` was not found or is not a file.
75617
+
75618
+ Pass a path to a \`.book\` file in \`--agent\`.
75619
+ `));
75620
+ }
75621
+ throw error;
75622
+ }));
75623
+ return {
75624
+ agentSource,
75625
+ agentReferences: createCoderAgentReferences({
75626
+ agentBookReference: normalizedAgentBookReference,
75627
+ resolvedAgentBookPath,
75628
+ currentWorkingDirectory,
75629
+ agentSource,
75630
+ }),
75631
+ };
75632
+ }
75633
+ /**
75634
+ * Reads an optional agent `.book` file and compiles its system message for injection into coder prompts.
75635
+ *
75636
+ * Returns `undefined` when no agent path is provided.
75637
+ */
75638
+ async function resolveCoderAgent(agentBookReference, currentWorkingDirectory) {
75639
+ const resolvedAgentBook = await resolveCoderAgentBook(agentBookReference, currentWorkingDirectory);
75640
+ if (resolvedAgentBook === undefined) {
75641
+ return undefined;
75642
+ }
75643
+ return {
75644
+ ...resolvedAgentBook,
75645
+ systemMessage: await createAgentRunnerSystemMessage(resolvedAgentBook.agentSource),
75646
+ };
75647
+ }
75648
+ /**
75649
+ * Creates the path and Book-title aliases which a prompt can use to target one selected agent.
75650
+ *
75651
+ * @private internal utility of `resolveCoderAgentBook`
75652
+ */
75653
+ function createCoderAgentReferences(options) {
75654
+ const relativeAgentBookPath = path.relative(options.currentWorkingDirectory, options.resolvedAgentBookPath).replaceAll('\\', '/');
75655
+ const agentBookFileName = path.basename(options.resolvedAgentBookPath);
75656
+ const agentBookName = path.basename(options.resolvedAgentBookPath, path.extname(options.resolvedAgentBookPath));
75657
+ const agentNameFromBook = parseAgentSource(options.agentSource).agentName;
75658
+ return Array.from(new Set([
75659
+ options.agentBookReference,
75660
+ relativeAgentBookPath,
75661
+ agentBookFileName,
75662
+ agentBookName,
75663
+ agentNameFromBook,
75664
+ ].filter((agentReference) => agentReference.trim() !== '')));
75665
+ }
75666
+
75667
+ var resolveCoderAgent$1 = /*#__PURE__*/Object.freeze({
75668
+ __proto__: null,
75669
+ resolveCoderAgentBook: resolveCoderAgentBook,
75670
+ resolveCoderAgent: resolveCoderAgent
75671
+ });
75672
+
74968
75673
  /**
74969
75674
  * Lists todo prompts that are ready to run (no authoring placeholders).
74970
75675
  */
@@ -75047,23 +75752,6 @@
75047
75752
  listCoderPrompts: listCoderPrompts
75048
75753
  });
75049
75754
 
75050
- /**
75051
- * Formats one unknown error-like value into its readable message without the stack trace.
75052
- *
75053
- * Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
75054
- * inside a branded error, where the stack of the wrapped error would only bury the actual cause.
75055
- */
75056
- function formatUnknownErrorMessage(error) {
75057
- if (error instanceof Error) {
75058
- return error.message;
75059
- }
75060
- if (typeof error === 'string') {
75061
- return error;
75062
- }
75063
- const serializedError = JSON.stringify(error, null, 2);
75064
- return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
75065
- }
75066
-
75067
75755
  /**
75068
75756
  * Builds a normalized temporary shell script path for prompt runners.
75069
75757
  */
@@ -75362,7 +76050,7 @@
75362
76050
  /**
75363
76051
  * CLI usage text for this script.
75364
76052
  */
75365
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
76053
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--agent <agent-book-path>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
75366
76054
  /**
75367
76055
  * Top-level flags supported by this command.
75368
76056
  */
@@ -75370,6 +76058,7 @@
75370
76058
  '--dry-run',
75371
76059
  '--harness',
75372
76060
  '--model',
76061
+ '--agent',
75373
76062
  '--context',
75374
76063
  '--test',
75375
76064
  '--test-before',
@@ -75408,6 +76097,7 @@
75408
76097
  }
75409
76098
  }
75410
76099
  const model = readOptionValue(args, '--model');
76100
+ const agent = readOptionValue(args, '--agent');
75411
76101
  const context = readOptionValue(args, '--context');
75412
76102
  const hasTestCommandFlag = args.includes('--test');
75413
76103
  const testCommand = readVariadicOptionValue(args, '--test');
@@ -75484,6 +76174,7 @@
75484
76174
  noUi,
75485
76175
  agentName,
75486
76176
  model,
76177
+ agent,
75487
76178
  context,
75488
76179
  testCommand,
75489
76180
  testBefore,
@@ -75918,30 +76609,43 @@
75918
76609
  // Note: [🟡] Code in this file is only used from `@promptbook/cli` orchestration scripts and is not shipped to other consumers.
75919
76610
 
75920
76611
  /**
75921
- * Reads an optional agent `.book` file and compiles its system message for injection into coder prompts.
76612
+ * Creates the guard which watches the free disk space while one `ptbk coder` run is in progress.
75922
76613
  *
75923
- * Returns `undefined` when no agent path is provided.
76614
+ * The guard runs at every pause checkpoint of the run. When the disk became critically full it reports the
76615
+ * warning and requests a pause, so the run stops at that checkpoint instead of failing in the middle of a
76616
+ * coding round, and continues once the user has freed some space and pressed `P`.
76617
+ *
76618
+ * @private internal utility of `ptbk coder` free disk space handling
75924
76619
  */
75925
- async function resolveCoderAgent(agentPath, currentWorkingDirectory) {
75926
- if (!agentPath) {
75927
- return undefined;
75928
- }
75929
- const resolvedAgentPath = path.resolve(currentWorkingDirectory, agentPath);
75930
- const agentSource = await promises.readFile(resolvedAgentPath, 'utf-8').catch((error) => {
75931
- if (error.code === 'ENOENT' || error.code === 'EISDIR') {
75932
- throw new NotFoundError(spaceTrim(`
75933
- Agent book \`${agentPath}\` was not found or is not a file.
76620
+ function createFreeDiskSpaceGuard(options) {
76621
+ const { inspectedPath, isAskingQuestionsEnabled } = options;
76622
+ let lastMeasurementTimeMs;
76623
+ return async () => {
76624
+ if (lastMeasurementTimeMs !== undefined &&
76625
+ Date.now() - lastMeasurementTimeMs < FREE_DISK_SPACE_RECHECK_INTERVAL_MS) {
76626
+ return;
76627
+ }
76628
+ lastMeasurementTimeMs = Date.now();
76629
+ const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
76630
+ if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level !== 'critical') {
76631
+ return;
76632
+ }
76633
+ console.warn(colors__default["default"].yellow(_spaceTrim.spaceTrim((block) => `
76634
+ The disk is running out of space while \`ptbk coder\` is working.
75934
76635
 
75935
- Pass a path to a \`.book\` file in \`--agent\`.
75936
- `));
76636
+ ${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
76637
+ - Press \`P\` to continue once the disk space is freed, or start the run with \`--no-questions\` to never pause on a full disk.
76638
+ `)));
76639
+ if (!isAskingQuestionsEnabled) {
76640
+ // Note: `--no-questions` forbids stopping and waiting for somebody who would free the disk space,
76641
+ // so the run keeps going and the warning above is the only thing which is reported
76642
+ return;
75937
76643
  }
75938
- throw error;
75939
- });
75940
- return {
75941
- agentSource: agentSource,
75942
- systemMessage: await createAgentRunnerSystemMessage(agentSource),
76644
+ announcePauseTargetLabel(`continuing with only ${formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes)} of free disk space left`);
76645
+ requestPause();
75943
76646
  };
75944
76647
  }
76648
+ // Note: [🟡] Code for `ptbk coder` free disk space handling [createFreeDiskSpaceGuard](scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.ts) should never be published outside of `@promptbook/cli`
75945
76649
 
75946
76650
  /**
75947
76651
  * Refresh interval used by the countdown display while waiting.
@@ -77912,6 +78616,169 @@
77912
78616
  await promises.writeFile(file.path, content, 'utf-8');
77913
78617
  }
77914
78618
 
78619
+ /**
78620
+ * Creates a safe markdown fenced code block even when content contains backticks.
78621
+ *
78622
+ * @param content - Raw code content.
78623
+ * @param language - Optional info-string language label.
78624
+ * @returns Fenced code block.
78625
+ *
78626
+ * @private internal utility of `createStandaloneBookLanguageMarkdown` and of the `ptbk coder` run traces
78627
+ */
78628
+ function getSafeCodeBlock(content, language = 'markdown') {
78629
+ const maxBacktickCount = Math.max(0, ...(content.match(/`+/g) || []).map((match) => match.length));
78630
+ const fence = '`'.repeat(Math.max(3, maxBacktickCount + 1));
78631
+ return `${fence}${language}\n${content}\n${fence}`;
78632
+ }
78633
+
78634
+ /**
78635
+ * Renders the markdown run trace of one finished or failed prompt round.
78636
+ *
78637
+ * The trace pairs the metadata of the round - which harness, model and thinking level ran it, how long each of
78638
+ * its steps took and what it cost - with the untouched runtime log, so a finished round can still be analyzed
78639
+ * after its temporary artifacts are cleaned up.
78640
+ */
78641
+ function buildPromptRunTraceContent(options) {
78642
+ const sections = [
78643
+ buildPromptRunTraceSummarySection(options),
78644
+ buildPromptRunTraceFailureSection(options.outcome),
78645
+ buildPromptRunTraceRuntimeLogSection(options.runtimeLog),
78646
+ ].filter((section) => section !== undefined);
78647
+ return `${sections.join('\n\n')}\n`;
78648
+ }
78649
+ /**
78650
+ * Renders the heading and the metadata bullet list of one run trace.
78651
+ */
78652
+ function buildPromptRunTraceSummarySection(options) {
78653
+ const { file, section, outcome } = options;
78654
+ const runnerSignature = formatRunnerSignature(options.runnerName, options.modelName, options.thinkingLevel);
78655
+ const loginMethodLabel = outcome.kind === 'succeeded' ? formatCodexLoginMethod(outcome.loginMethod) : undefined;
78656
+ const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
78657
+ const detailLines = [
78658
+ `- **Prompt:** ${buildPromptSummary(file, section)}`,
78659
+ ...buildPromptRunTraceSectionLines(file, section),
78660
+ `- **Outcome:** ${formatPromptRunTraceOutcomeLabel(outcome)}`,
78661
+ `- **Runner:** ${runnerSignature}${loginMethodSuffix}`,
78662
+ `- **Attempts:** ${options.attemptCount}`,
78663
+ ...buildPromptRunTraceStepsLines(outcome),
78664
+ ...buildPromptRunTraceTestCommandLines(options.testCommand),
78665
+ `- **Started:** ${options.startedDate.toISOString()}`,
78666
+ `- **Finished:** ${options.finishedDate.toISOString()}`,
78667
+ `- **Duration:** ${moment__default["default"].duration(options.finishedDate.diff(options.startedDate)).humanize()}`,
78668
+ ];
78669
+ return _spaceTrim.spaceTrim((block) => `
78670
+ # Run trace of \`${buildPromptLabelForDisplay(file, section)}\`
78671
+
78672
+ ${block(detailLines.join('\n'))}
78673
+ `);
78674
+ }
78675
+ /**
78676
+ * Renders which section of its prompt file the round implemented.
78677
+ *
78678
+ * A prompt file holding exactly one section says nothing by naming it, exactly like the section suffix of its
78679
+ * generated artifact names, so only a file with more than one section reports the section it traced.
78680
+ */
78681
+ function buildPromptRunTraceSectionLines(file, section) {
78682
+ if (file.sections.length <= 1) {
78683
+ return [];
78684
+ }
78685
+ return [`- **Prompt section:** ${section.index + 1} of ${file.sections.length}`];
78686
+ }
78687
+ /**
78688
+ * Renders the per-step usage breakdown of a successful round, omitted for a round which never finished one.
78689
+ */
78690
+ function buildPromptRunTraceStepsLines(outcome) {
78691
+ if (outcome.kind !== 'succeeded') {
78692
+ return [];
78693
+ }
78694
+ const stepsSummary = formatCoderRunSteps(outcome.steps);
78695
+ if (stepsSummary === '') {
78696
+ return [];
78697
+ }
78698
+ return [`- **Steps:** ${stepsSummary}`];
78699
+ }
78700
+ /**
78701
+ * Renders the verification command of the round, omitted when the round ran without one.
78702
+ */
78703
+ function buildPromptRunTraceTestCommandLines(testCommand) {
78704
+ if (!testCommand) {
78705
+ return [];
78706
+ }
78707
+ return [`- **Verification command:** \`${testCommand}\``];
78708
+ }
78709
+ /**
78710
+ * Renders the failure details of a failed round, omitted for a successful one.
78711
+ */
78712
+ function buildPromptRunTraceFailureSection(outcome) {
78713
+ if (outcome.kind !== 'failed') {
78714
+ return undefined;
78715
+ }
78716
+ return `## Failure\n\n${getSafeCodeBlock(formatUnknownErrorDetails(outcome.error), 'text')}`;
78717
+ }
78718
+ /**
78719
+ * Renders the untouched runtime log of the round, or says that the round produced none.
78720
+ */
78721
+ function buildPromptRunTraceRuntimeLogSection(runtimeLog) {
78722
+ if (runtimeLog.trim() === '') {
78723
+ return '## Runtime log\n\n_This round has produced no readable runtime log._';
78724
+ }
78725
+ return `## Runtime log\n\n${getSafeCodeBlock(runtimeLog.replace(/\r\n/gu, '\n').trimEnd(), 'text')}`;
78726
+ }
78727
+ /**
78728
+ * Formats the outcome of the round as the human-readable label of its trace.
78729
+ */
78730
+ function formatPromptRunTraceOutcomeLabel(outcome) {
78731
+ return outcome.kind === 'succeeded' ? 'Succeeded' : 'Failed';
78732
+ }
78733
+
78734
+ /**
78735
+ * Name of the directory which keeps the run traces inside the prompts directory.
78736
+ */
78737
+ const PROMPT_RUN_TRACES_DIRECTORY_NAME = 'traces';
78738
+ /**
78739
+ * Builds the path of the run trace which belongs to one prompt section.
78740
+ *
78741
+ * The trace is stored in the `traces` directory of the very same prompts directory the prompt itself lives in,
78742
+ * and it carries the name of its prompt file. A prompt file holding more than one section appends the section
78743
+ * suffix, exactly like the temporary scripts and the isolation worktrees of the same section do.
78744
+ */
78745
+ function buildPromptRunTracePath(file, section) {
78746
+ const promptFileBaseName = file.name.replace(/\.[^.]+$/u, '');
78747
+ const traceFileName = `${promptFileBaseName}${buildPromptSectionSuffix(file, section)}.md`;
78748
+ return path.join(path.dirname(file.path), PROMPT_RUN_TRACES_DIRECTORY_NAME, traceFileName);
78749
+ }
78750
+
78751
+ /**
78752
+ * Writes the run trace of one prompt round into the `traces` directory of the prompts directory.
78753
+ *
78754
+ * Returns the path of the written trace so callers can include it in a commit.
78755
+ */
78756
+ async function writePromptRunTrace(options) {
78757
+ const { logPath, ...contentOptions } = options;
78758
+ const tracePath = buildPromptRunTracePath(options.file, options.section);
78759
+ const runtimeLog = await readPromptRuntimeLog(logPath);
78760
+ await promises.mkdir(path.dirname(tracePath), { recursive: true });
78761
+ await promises.writeFile(tracePath, buildPromptRunTraceContent({ ...contentOptions, runtimeLog }), 'utf-8');
78762
+ return tracePath;
78763
+ }
78764
+ /**
78765
+ * Reads the live runtime log of one round.
78766
+ *
78767
+ * A round which never produced a readable log still gets its trace, because the metadata of the round is worth
78768
+ * keeping on its own - so a missing log is reported as empty instead of failing the round which has just passed.
78769
+ */
78770
+ async function readPromptRuntimeLog(logPath) {
78771
+ if (!logPath) {
78772
+ return '';
78773
+ }
78774
+ try {
78775
+ return await promises.readFile(logPath, 'utf-8');
78776
+ }
78777
+ catch (_a) {
78778
+ return '';
78779
+ }
78780
+ }
78781
+
77915
78782
  /**
77916
78783
  * Refreshes optional harness subscription usage in the shared coder-run UI state.
77917
78784
  *
@@ -78034,7 +78901,9 @@
78034
78901
  catch (error) {
78035
78902
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
78036
78903
  lastError = error;
78037
- if (errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
78904
+ // Note: A harness which is not logged in answers every retry the same way, so the user gets
78905
+ // the sign-in instructions right away instead of after every retry has waited its delay
78906
+ if (error instanceof AuthenticationError || errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
78038
78907
  break;
78039
78908
  }
78040
78909
  await waitAfterErrorBeforeRetry({
@@ -78056,6 +78925,7 @@
78056
78925
  attemptCount,
78057
78926
  error: lastError,
78058
78927
  options,
78928
+ logPath,
78059
78929
  roundCommitScope,
78060
78930
  uiHandle,
78061
78931
  waitForRequestedPause,
@@ -78172,6 +79042,17 @@
78172
79042
  // Note: The prompt status is always written into the original project, an isolated round transports
78173
79043
  // its own changes back through the merge instead
78174
79044
  await writePromptFile(nextPrompt.file);
79045
+ // Note: Written before the round is committed, so the trace of the round lands in the very same commit
79046
+ // as the prompt it describes, and before the live runtime log it is built from is deleted
79047
+ await recordPromptRoundTrace({
79048
+ options: runOptions,
79049
+ nextPrompt,
79050
+ runnerMetadata,
79051
+ promptExecutionStartedDate,
79052
+ attemptCount: result.attemptCount,
79053
+ logPath,
79054
+ outcome: { kind: 'succeeded', steps: result.steps, loginMethod: result.loginMethod },
79055
+ });
78175
79056
  await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
78176
79057
  await recordPromptDurationInEstimateCache({
78177
79058
  options: runOptions,
@@ -78219,7 +79100,7 @@
78219
79100
  * Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
78220
79101
  */
78221
79102
  async function finalizeFailedPromptRound(options) {
78222
- const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
79103
+ const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, logPath, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
78223
79104
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
78224
79105
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
78225
79106
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
@@ -78245,8 +79126,42 @@
78245
79126
  modelName: runnerMetadata.modelName,
78246
79127
  error,
78247
79128
  });
79129
+ // Note: A failed round is exactly the round whose trace is worth reading, so it is written before the live
79130
+ // runtime log it is built from is deleted
79131
+ await recordPromptRoundTrace({
79132
+ options: runOptions,
79133
+ nextPrompt,
79134
+ runnerMetadata,
79135
+ promptExecutionStartedDate,
79136
+ attemptCount,
79137
+ logPath,
79138
+ outcome: { kind: 'failed', error },
79139
+ });
78248
79140
  await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
78249
79141
  }
79142
+ /**
79143
+ * Persists the run trace of one prompt round, so the round can still be analyzed after its temporary
79144
+ * artifacts are gone.
79145
+ *
79146
+ * This is the single place where both the successful and the failed round record what they did, which harness,
79147
+ * model and thinking level did it and everything that harness has written while doing it.
79148
+ */
79149
+ async function recordPromptRoundTrace(options) {
79150
+ const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
79151
+ await writePromptRunTrace({
79152
+ file: nextPrompt.file,
79153
+ section: nextPrompt.section,
79154
+ runnerName: runnerMetadata.runnerName,
79155
+ modelName: runnerMetadata.modelName,
79156
+ thinkingLevel: runOptions.thinkingLevel,
79157
+ testCommand: runOptions.testCommand,
79158
+ attemptCount,
79159
+ startedDate: promptExecutionStartedDate,
79160
+ finishedDate: moment__default["default"](),
79161
+ outcome,
79162
+ logPath,
79163
+ });
79164
+ }
78250
79165
  /**
78251
79166
  * Waits for the optional user confirmation immediately before creating the commit.
78252
79167
  */
@@ -78895,7 +79810,13 @@
78895
79810
  await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
78896
79811
  autoPush: options.options.autoPush,
78897
79812
  projectPath: worktree.projectPath,
78898
- relevantPaths: [nextPrompt.file.path, errorLogPath].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
79813
+ // Note: The round itself has already succeeded, so it has left its run trace in the original project.
79814
+ // It belongs to this commit, which is the only one this task still gets.
79815
+ relevantPaths: [
79816
+ nextPrompt.file.path,
79817
+ errorLogPath,
79818
+ buildPromptRunTracePath(nextPrompt.file, nextPrompt.section),
79819
+ ].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
78899
79820
  });
78900
79821
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
78901
79822
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
@@ -79170,12 +80091,23 @@
79170
80091
  * @public exported from `@promptbook/cli`
79171
80092
  */
79172
80093
  async function runCodexPrompts(providedOptions) {
80094
+ var _a;
79173
80095
  const options = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
79174
80096
  validateRunCodexPromptOptions(options);
79175
80097
  resetCoderRunControls();
79176
80098
  const runStartDate = moment__default["default"]();
79177
80099
  const { isRichUiEnabled, progressDisplay, uiHandle } = createRunDisplays(options, runStartDate);
79178
- const waitForRequestedPause = createPauseWaiter({ isRichUiEnabled, progressDisplay, uiHandle });
80100
+ const waitForRequestedPause = createPauseWaiter({
80101
+ isRichUiEnabled,
80102
+ progressDisplay,
80103
+ uiHandle,
80104
+ // Note: Every pause checkpoint of the whole run goes through this one waiter, so watching the free disk
80105
+ // space here covers each round, each verification and each runner without repeating the check
80106
+ guardFreeDiskSpace: createFreeDiskSpaceGuard({
80107
+ inspectedPath: process.cwd(),
80108
+ isAskingQuestionsEnabled: (_a = options.isAskingQuestionsEnabled) !== null && _a !== void 0 ? _a : true,
80109
+ }),
80110
+ });
79179
80111
  startPauseListenerIfNeeded(isRichUiEnabled);
79180
80112
  try {
79181
80113
  const resolvedCoderContext = await resolveCoderContext(options.context, process.cwd());
@@ -79188,6 +80120,7 @@
79188
80120
  const promptRunnerIdentity = {
79189
80121
  harnessName: options.agentName,
79190
80122
  modelName: actualRunnerModel,
80123
+ agentReferences: resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentReferences,
79191
80124
  };
79192
80125
  console.info(colors__default["default"].green(`Running prompts with ${runner.name}`));
79193
80126
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
@@ -79624,11 +80557,14 @@
79624
80557
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
79625
80558
  */
79626
80559
  function createPauseWaiter(options) {
79627
- const { isRichUiEnabled, progressDisplay, uiHandle } = options;
80560
+ const { isRichUiEnabled, progressDisplay, uiHandle, guardFreeDiskSpace } = options;
79628
80561
  return async (checkpoint) => {
79629
80562
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase(checkpoint.phase);
79630
80563
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(checkpoint.statusMessage);
79631
80564
  announcePauseTargetLabel(checkpoint.checkpointLabel);
80565
+ // Note: Runs after the checkpoint label is announced, so a pause requested because of a full disk
80566
+ // replaces that label and says why the run is standing still
80567
+ await guardFreeDiskSpace();
79632
80568
  await checkPause({
79633
80569
  silent: isRichUiEnabled,
79634
80570
  onPaused: () => {
@@ -79747,7 +80683,7 @@
79747
80683
  return false;
79748
80684
  }
79749
80685
  if (promptQueueSnapshot.stats.forAgent > 0) {
79750
- announceRunCompletion('No prompts match the selected harness or model.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
80686
+ announceRunCompletion('No prompts match the selected harness, model or agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
79751
80687
  }
79752
80688
  else if (promptQueueSnapshot.stats.toBeWritten > 0) {
79753
80689
  announceRunCompletion('No prompts ready for agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
@@ -79792,7 +80728,7 @@
79792
80728
  function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
79793
80729
  let message;
79794
80730
  if (promptQueueSnapshot.stats.forAgent > 0) {
79795
- message = 'No prompts match the selected harness or model. Watching for changes...';
80731
+ message = 'No prompts match the selected harness, model or agent. Watching for changes...';
79796
80732
  }
79797
80733
  else if (promptQueueSnapshot.stats.toBeWritten > 0) {
79798
80734
  message = 'No prompts ready for agent. Watching for changes...';