@promptbook/cli 0.114.0-1 → 0.114.0-3

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 (199) hide show
  1. package/README.md +16 -15
  2. package/apps/agents-server/next.config.ts +11 -0
  3. package/apps/agents-server/package.json +6 -4
  4. package/apps/agents-server/playwright.config.ts +28 -38
  5. package/apps/agents-server/scripts/build-agents-server.js +62 -0
  6. package/apps/agents-server/scripts/build-e2e.js +38 -0
  7. package/apps/agents-server/scripts/ensure-root-dependencies.js +86 -0
  8. package/apps/agents-server/scripts/prerender-homepage.js +1 -1
  9. package/apps/agents-server/scripts/run-e2e-tests.js +34 -0
  10. package/apps/agents-server/scripts/run-npm.js +42 -0
  11. package/apps/agents-server/src/app/admin/email-server/page.tsx +14 -6
  12. package/apps/agents-server/src/app/api/elevenlabs/tts/route.ts +3 -3
  13. package/apps/agents-server/src/app/api/onboarding/book/route.ts +7 -6
  14. package/apps/agents-server/src/app/layout.tsx +6 -0
  15. package/apps/agents-server/src/app/superadmin/servers/ServersRegistryTable.tsx +105 -102
  16. package/apps/agents-server/src/app/superadmin/servers/useServersRegistryState.ts +15 -7
  17. package/apps/agents-server/src/components/AgentProjectDnsInstructions/AgentProjectDnsInstructions.tsx +11 -11
  18. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ManGoBookEditor.tsx +82 -0
  19. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/WizardShell.tsx +2 -2
  20. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/BookStep.tsx +13 -16
  21. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/ZadaniStep.tsx +4 -4
  22. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/bookSections.ts +23 -16
  23. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/bookSource.ts +54 -0
  24. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/bookService.ts +15 -8
  25. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoAgentSource.ts +3 -37
  26. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoOpenEditorRequest.ts +1 -1
  27. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/state/OnboardingProvider.tsx +2 -2
  28. package/apps/agents-server/src/components/UsersList/CreateUserDialog.tsx +13 -22
  29. package/apps/agents-server/src/components/UsersList/PasswordGeneratorDialog.tsx +216 -0
  30. package/apps/agents-server/src/components/UsersList/UsersList.tsx +16 -11
  31. package/apps/agents-server/src/components/UsersList/generateSecurePassword.ts +112 -24
  32. package/apps/agents-server/src/components/UsersList/userDialogClassNames.ts +11 -0
  33. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +11 -0
  34. package/apps/agents-server/src/languages/translations/czech.yaml +11 -0
  35. package/apps/agents-server/src/languages/translations/english.yaml +11 -0
  36. package/apps/agents-server/src/utils/agentProjects/agentProjectHrefs.ts +11 -0
  37. package/apps/agents-server/src/utils/agentProjects/createAgentProjectDnsRecords.ts +53 -7
  38. package/apps/agents-server/src/utils/agentProjects/createAgentProjectMarkdownReferences.ts +37 -27
  39. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentBooks.ts +2 -13
  40. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentRuntime.ts +21 -73
  41. package/apps/agents-server/src/utils/stalwart/createEmailDnsInstructions.ts +9 -5
  42. package/apps/agents-server/src/utils/stalwart/readStalwartEmailSnapshot.ts +23 -5
  43. package/apps/agents-server/src/utils/stalwart/stalwartBootstrap.ts +2 -1
  44. package/apps/agents-server/src/utils/stalwart/stalwartMailBridge.ts +7 -0
  45. package/apps/agents-server/tests/e2e/e2eEnvironment.cjs +49 -0
  46. package/esm/index.es.js +1105 -273
  47. package/esm/index.es.js.map +1 -1
  48. package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
  49. package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
  50. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
  51. package/esm/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
  52. package/esm/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
  53. package/esm/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
  54. package/esm/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
  55. package/esm/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
  56. package/esm/scripts/run-codex-prompts/prompts/findNextTodoPrompt.d.ts +2 -1
  57. package/esm/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +27 -0
  58. package/esm/scripts/run-codex-prompts/prompts/listRunnablePrompts.d.ts +2 -1
  59. package/esm/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +2 -1
  60. package/esm/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +7 -0
  61. package/esm/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +16 -0
  62. package/esm/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +9 -0
  63. package/esm/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +4 -0
  64. package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -2
  65. package/esm/scripts/run-codex-prompts/testing/runTestBefore.d.ts +24 -0
  66. package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +1 -0
  67. package/esm/scripts/verify-prompts/verify-prompts.d.ts +5 -0
  68. package/esm/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.d.ts +1 -15
  69. package/esm/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.d.ts +1 -1
  70. package/esm/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.d.ts +0 -4
  71. package/esm/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
  72. package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +7 -0
  73. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
  74. package/esm/src/cli/cli-commands/coder/agentCodingFile.d.ts +2 -3
  75. package/esm/src/cli/cli-commands/coder/ping.d.ts +10 -0
  76. package/esm/src/cli/cli-commands/coder/ping.test.d.ts +1 -0
  77. package/esm/src/cli/cli-commands/coder.d.ts +1 -0
  78. package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
  79. package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
  80. package/esm/src/cli/common/loadPromptsModule.d.ts +16 -0
  81. package/esm/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
  82. package/esm/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
  83. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
  84. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
  85. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
  86. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
  87. package/esm/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
  88. package/esm/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
  89. package/esm/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
  90. package/esm/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
  91. package/esm/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
  92. package/esm/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
  93. package/esm/src/version.d.ts +1 -1
  94. package/package.json +1 -1
  95. package/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.ts +1 -15
  96. package/src/book-2.0/book-language-documentation/bookLanguageDocumentationExamples.ts +1 -2
  97. package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +92 -32
  98. package/src/book-2.0/book-language-documentation/czechBookLanguageManualDictionary.ts +16 -39
  99. package/src/book-2.0/book-language-documentation/englishBookLanguageManualDictionary.ts +16 -36
  100. package/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.ts +9 -1
  101. package/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.ts +4 -51
  102. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +291 -4
  103. package/src/book-3.0/LiteAgent.ts +11 -9
  104. package/src/book-components/Chat/utils/$provideServerDomWindow.ts +77 -0
  105. package/src/book-components/Chat/utils/renderMarkdown.ts +135 -32
  106. package/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.ts +14 -0
  107. package/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.ts +1 -0
  108. package/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.ts +1 -0
  109. package/src/cli/cli-commands/coder/add.ts +40 -12
  110. package/src/cli/cli-commands/coder/agentCodingFile.ts +6 -129
  111. package/src/cli/cli-commands/coder/generate-boilerplates.ts +31 -5
  112. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  113. package/src/cli/cli-commands/coder/init.ts +27 -1
  114. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  115. package/src/cli/cli-commands/coder/ping.ts +72 -0
  116. package/src/cli/cli-commands/coder/run.ts +25 -2
  117. package/src/cli/cli-commands/coder/verify.ts +27 -12
  118. package/src/cli/cli-commands/coder.ts +4 -0
  119. package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +78 -0
  120. package/src/cli/cli-commands/run/prepareRunCommandResources.ts +2 -1
  121. package/src/cli/cli-commands/run/resolveRunInputParameters.ts +2 -1
  122. package/src/cli/cli-commands/runInteractiveChatbot.ts +2 -1
  123. package/src/cli/common/$provideLlmToolsForCli.ts +2 -1
  124. package/src/cli/common/loadPromptsModule.ts +13 -0
  125. package/src/conversion/archive/loadArchive.ts +2 -1
  126. package/src/conversion/archive/loadJsZipModule.ts +10 -0
  127. package/src/conversion/archive/saveArchive.ts +2 -1
  128. package/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.ts +13 -1
  129. package/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.ts +13 -1
  130. package/src/llm-providers/openai/OpenAiAgentKitExecutionTools.ts +6 -2
  131. package/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.ts +7 -5
  132. package/src/llm-providers/openai/OpenAiAssistantExecutionTools.ts +1 -1
  133. package/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.ts +1 -1
  134. package/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.ts +1 -1
  135. package/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.ts +1 -1
  136. package/src/llm-providers/openai/OpenAiCompatibleRequestManager.ts +12 -1
  137. package/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.ts +1 -1
  138. package/src/llm-providers/openai/utils/loadOpenAiAgentsModule.ts +11 -0
  139. package/src/llm-providers/openai/utils/uploadFilesToOpenAi.ts +1 -1
  140. package/src/other/templates/getTemplatesPipelineCollection.ts +731 -923
  141. package/src/remote-server/createRemoteClient.ts +12 -1
  142. package/src/scrapers/website/WebsiteScraper.ts +22 -2
  143. package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +101 -0
  144. package/src/utils/misc/createLazyModuleLoader.ts +27 -0
  145. package/src/version.ts +2 -2
  146. package/src/versions.txt +2 -1
  147. package/umd/index.umd.js +1180 -342
  148. package/umd/index.umd.js.map +1 -1
  149. package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
  150. package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
  151. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
  152. package/umd/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
  153. package/umd/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
  154. package/umd/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
  155. package/umd/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
  156. package/umd/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
  157. package/umd/scripts/run-codex-prompts/prompts/findNextTodoPrompt.d.ts +2 -1
  158. package/umd/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +27 -0
  159. package/umd/scripts/run-codex-prompts/prompts/listRunnablePrompts.d.ts +2 -1
  160. package/umd/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +2 -1
  161. package/umd/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +7 -0
  162. package/umd/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +16 -0
  163. package/umd/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +9 -0
  164. package/umd/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +4 -0
  165. package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -2
  166. package/umd/scripts/run-codex-prompts/testing/runTestBefore.d.ts +24 -0
  167. package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +1 -0
  168. package/umd/scripts/verify-prompts/verify-prompts.d.ts +5 -0
  169. package/umd/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.d.ts +1 -15
  170. package/umd/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.d.ts +1 -1
  171. package/umd/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.d.ts +0 -4
  172. package/umd/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
  173. package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +7 -0
  174. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
  175. package/umd/src/cli/cli-commands/coder/agentCodingFile.d.ts +2 -3
  176. package/umd/src/cli/cli-commands/coder/ping.d.ts +10 -0
  177. package/umd/src/cli/cli-commands/coder/ping.test.d.ts +1 -0
  178. package/umd/src/cli/cli-commands/coder.d.ts +1 -0
  179. package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
  180. package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
  181. package/umd/src/cli/common/loadPromptsModule.d.ts +16 -0
  182. package/umd/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
  183. package/umd/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
  184. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
  185. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
  186. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
  187. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
  188. package/umd/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
  189. package/umd/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
  190. package/umd/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
  191. package/umd/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
  192. package/umd/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
  193. package/umd/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
  194. package/umd/src/version.d.ts +1 -1
  195. package/apps/agents-server/src/app/api/onboarding/draft/route.ts +0 -23
  196. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/BookLanguagePanel.tsx +0 -125
  197. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownBookEditor.tsx +0 -154
  198. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownPreview.tsx +0 -188
  199. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/draftService.ts +0 -32
package/umd/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('crypto'), require('child_process'), require('moment'), require('fs'), require('dotenv'), require('prompts'), require('readline'), require('waitasecond'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('socket.io-client'), require('jszip'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('rxjs'), require('@openai/agents'), require('openai'), require('typescript'), require('ignore'), require('events'), require('os'), require('mime-types'), require('papaparse'), require('pg'), require('@supabase/supabase-js'), require('url')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'crypto', 'child_process', 'moment', 'fs', 'dotenv', 'prompts', 'readline', 'waitasecond', 'crypto-js/enc-hex', 'crypto-js/sha256', 'socket.io-client', 'jszip', '@mozilla/readability', 'jsdom', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'rxjs', '@openai/agents', 'openai', 'typescript', 'ignore', 'events', 'os', 'mime-types', 'papaparse', 'pg', '@supabase/supabase-js', 'url'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.crypto, global.child_process, global.moment, global.fs, global.dotenv, global.prompts, global.readline, global.waitasecond, global.hexEncoder, global.sha256, global.socket_ioClient, global.JSZip, global.readability, global.jsdom, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.rxjs, global.agents, global.OpenAI, global.ts, global.ignore, global.events, global.os, global.mimeTypes, global.papaparse, global.pg, null, global.url));
5
- })(this, (function (exports, colors, commander, _spaceTrim, promises, path, crypto, child_process, moment, fs, dotenv, prompts, readline, waitasecond, hexEncoder, sha256, socket_ioClient, JSZip, readability, jsdom, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Anthropic, Bottleneck, openai, rxjs, agents, OpenAI, ts, ignore, events, os, mimeTypes, papaparse, pg, supabaseJs, url) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('crypto'), require('child_process'), require('moment'), require('fs'), require('dotenv'), require('readline'), require('waitasecond'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('bottleneck'), require('rxjs'), require('ignore'), require('events'), require('os'), require('mime-types'), require('papaparse'), require('@supabase/supabase-js'), require('url')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'crypto', 'child_process', 'moment', 'fs', 'dotenv', 'readline', 'waitasecond', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', 'bottleneck', 'rxjs', 'ignore', 'events', 'os', 'mime-types', 'papaparse', '@supabase/supabase-js', 'url'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.crypto, global.child_process, global.moment, global.fs, global.dotenv, global.readline, global.waitasecond, global.hexEncoder, global.sha256, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Bottleneck, global.rxjs, global.ignore, global.events, global.os, global.mimeTypes, global.papaparse, null, global.url));
5
+ })(this, (function (exports, colors, commander, _spaceTrim, promises, path, crypto, child_process, moment, fs, dotenv, readline, waitasecond, hexEncoder, sha256, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Bottleneck, rxjs, ignore, events, os, mimeTypes, papaparse, supabaseJs, url) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -30,11 +30,9 @@
30
30
  var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
31
31
  var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
32
32
  var dotenv__namespace = /*#__PURE__*/_interopNamespace(dotenv);
33
- var prompts__default = /*#__PURE__*/_interopDefaultLegacy(prompts);
34
33
  var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
35
34
  var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
36
35
  var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
37
- var JSZip__default = /*#__PURE__*/_interopDefaultLegacy(JSZip);
38
36
  var CryptoJS__default = /*#__PURE__*/_interopDefaultLegacy(CryptoJS);
39
37
  var showdown__default = /*#__PURE__*/_interopDefaultLegacy(showdown);
40
38
  var glob__default = /*#__PURE__*/_interopDefaultLegacy(glob);
@@ -42,10 +40,7 @@
42
40
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
43
41
  var OpenApiValidator__namespace = /*#__PURE__*/_interopNamespace(OpenApiValidator);
44
42
  var swaggerUi__default = /*#__PURE__*/_interopDefaultLegacy(swaggerUi);
45
- var Anthropic__default = /*#__PURE__*/_interopDefaultLegacy(Anthropic);
46
43
  var Bottleneck__default = /*#__PURE__*/_interopDefaultLegacy(Bottleneck);
47
- var OpenAI__default = /*#__PURE__*/_interopDefaultLegacy(OpenAI);
48
- var ts__namespace = /*#__PURE__*/_interopNamespace(ts);
49
44
  var ignore__default = /*#__PURE__*/_interopDefaultLegacy(ignore);
50
45
  var papaparse__default = /*#__PURE__*/_interopDefaultLegacy(papaparse);
51
46
 
@@ -63,7 +58,7 @@
63
58
  * @generated
64
59
  * @see https://github.com/webgptorg/promptbook
65
60
  */
66
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-1';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-3';
67
62
  /**
68
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
69
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3297,6 +3292,12 @@
3297
3292
  * @private internal constant of `buildAgentsServer`
3298
3293
  */
3299
3294
  const PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV = 'PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION';
3295
+ /**
3296
+ * Environment variable that disables throwaway webpack filesystem caches for CLI-owned production builds.
3297
+ *
3298
+ * @private internal constant of `buildAgentsServer`
3299
+ */
3300
+ const PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV = 'PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE';
3300
3301
  /**
3301
3302
  * Conservative Next.js build worker count used by CLI-owned Agents Server production builds.
3302
3303
  *
@@ -3321,6 +3322,11 @@
3321
3322
  [PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
3322
3323
  }
3323
3324
  : {}),
3325
+ ...(options.isWebpackFilesystemCacheDisabled
3326
+ ? {
3327
+ [PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV]: 'true',
3328
+ }
3329
+ : {}),
3324
3330
  };
3325
3331
  }
3326
3332
  /**
@@ -4054,6 +4060,7 @@
4054
4060
  });
4055
4061
  const buildEnvironment = createAgentsServerRuntimeEnvironment(environment, preparedRuntime.nodeModulesPath, {
4056
4062
  isNextValidationIgnored: preparedRuntime.isAppPathMaterialized,
4063
+ isWebpackFilesystemCacheDisabled: true,
4057
4064
  });
4058
4065
  if (!options.isBuildForced &&
4059
4066
  (await isAgentsServerBuildCacheCurrent({
@@ -26970,12 +26977,12 @@
26970
26977
  * Builds the coder-run control pills shown in the footer box.
26971
26978
  */
26972
26979
  function buildCoderRunControlPills(options) {
26973
- const { pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26980
+ const { phase, pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26974
26981
  return buildControlPills({
26975
26982
  pauseControl,
26976
26983
  pendingEnterLabel,
26977
26984
  additionalControls: [
26978
- buildSkipCurrentWaitControl(),
26985
+ ...(phase === 'waiting' ? [buildSkipCurrentWaitControl()] : []),
26979
26986
  buildEndAfterCurrentPromptControl(isEndAfterCurrentPromptRequested, sessionTotal),
26980
26987
  ],
26981
26988
  });
@@ -28230,6 +28237,13 @@
28230
28237
  * Environment variable read by the shell wrapper to tee live output into the temporary runtime log file.
28231
28238
  */
28232
28239
  const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
28240
+ /**
28241
+ * Log line which separates the raw script input from the raw script output of one execution section.
28242
+ *
28243
+ * Readers of a runtime log split on this marker to look only at what the harness really produced,
28244
+ * without the generated script and the prompt it embeds.
28245
+ */
28246
+ const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
28233
28247
  /**
28234
28248
  * Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
28235
28249
  */
@@ -28265,7 +28279,7 @@
28265
28279
  --- raw input ---
28266
28280
  ${block(normalizedInput)}
28267
28281
 
28268
- --- raw output ---
28282
+ ${SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER}
28269
28283
  `);
28270
28284
  await promises.appendFile(logPath, `${logSection}\n`, 'utf-8');
28271
28285
  }
@@ -29556,6 +29570,7 @@
29556
29570
  ` -c model_reasoning_effort="${thinkingLevel}" \\`,
29557
29571
  ` --ask-for-approval ${options.askForApproval} \\`,
29558
29572
  ` exec --model ${options.model} \\`,
29573
+ ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
29559
29574
  ' --local-provider none \\',
29560
29575
  ` --sandbox ${options.sandbox} \\`,
29561
29576
  ` -C ${projectPath} \\`,
@@ -29872,6 +29887,10 @@
29872
29887
  * Output line that marks finished Codex usage summary.
29873
29888
  */
29874
29889
  const CODEX_COMPLETION_LINE = /^\s*tokens used\b/i;
29890
+ /**
29891
+ * Output line that marks the completed Codex turn in JSONL output mode.
29892
+ */
29893
+ const CODEX_JSON_COMPLETION_LINE = /^\s*\{"type":"turn\.(?:completed|failed)"/u;
29875
29894
  /**
29876
29895
  * Idle timeout after completion marker to capture trailing output.
29877
29896
  */
@@ -29948,6 +29967,7 @@
29948
29967
  sandbox: this.options.sandbox,
29949
29968
  askForApproval: this.options.askForApproval,
29950
29969
  allowCredits: this.options.allowCredits,
29970
+ isMachineReadableProgressEnabled: this.options.isMachineReadableProgressEnabled,
29951
29971
  codexCommand: this.options.codexCommand,
29952
29972
  });
29953
29973
  for (let retryIndex = 0;; retryIndex++) {
@@ -29962,7 +29982,9 @@
29962
29982
  const output = await $runGoScriptUntilMarkerIdle({
29963
29983
  scriptPath: options.scriptPath,
29964
29984
  scriptContent,
29965
- completionLineMatcher: CODEX_COMPLETION_LINE,
29985
+ completionLineMatcher: this.options.isMachineReadableProgressEnabled
29986
+ ? CODEX_JSON_COMPLETION_LINE
29987
+ : CODEX_COMPLETION_LINE,
29966
29988
  idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
29967
29989
  logPath: options.logPath,
29968
29990
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
@@ -30212,6 +30234,7 @@
30212
30234
  sandbox: 'danger-full-access',
30213
30235
  askForApproval: 'never',
30214
30236
  allowCredits: options.allowCredits,
30237
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
30215
30238
  });
30216
30239
  if (!options.allowCredits && options.isVerbose === true) {
30217
30240
  console.info(colors__default["default"].gray('OpenAI Codex credit spending is disabled. Use `--allow-credits` to explicitly opt in.'));
@@ -31447,6 +31470,7 @@
31447
31470
  : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
31448
31471
  const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
31449
31472
  const controls = buildCoderRunControlPills({
31473
+ phase: options.phase,
31450
31474
  pauseControl: pausePresentation.pauseControl,
31451
31475
  pendingEnterLabel: options.pendingEnterLabel,
31452
31476
  isEndAfterCurrentPromptRequested: options.isEndAfterCurrentPromptRequested,
@@ -32310,6 +32334,7 @@
32310
32334
  autoPull: options.autoPull,
32311
32335
  agentName: options.agentName,
32312
32336
  model: options.model,
32337
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
32313
32338
  priority: 0,
32314
32339
  };
32315
32340
  }
@@ -32931,8 +32956,12 @@
32931
32956
  * `options.includePaths` can restrict staging, `options.onlyPaths` can restrict the commit pathspec,
32932
32957
  * `options.excludePaths` can keep temporary artifacts out of the created commit and
32933
32958
  * `options.isEmptyCommitAllowed` keeps a round without any file change from failing.
32959
+ *
32960
+ * Note: The temporary commit message file is written inside the project, so it is always excluded from the commit
32961
+ * itself for projects which do not keep the Promptbook temporary directory out of version control.
32934
32962
  */
32935
32963
  async function commitChanges(message, options) {
32964
+ var _a;
32936
32965
  const projectPath = (options === null || options === void 0 ? void 0 : options.projectPath) || process.cwd();
32937
32966
  const commitMessagePath = resolvePromptbookTemporaryPath(projectPath, 'ptbk-coder', 'commit-messages', `COMMIT_MESSAGE_${Date.now()}.txt`);
32938
32967
  await promises.mkdir(path.dirname(commitMessagePath), { recursive: true });
@@ -32940,7 +32969,10 @@
32940
32969
  try {
32941
32970
  const agentEnv = buildAgentGitEnv();
32942
32971
  const signingFlag = buildAgentGitSigningFlag();
32943
- await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, options === null || options === void 0 ? void 0 : options.excludePaths);
32972
+ await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, [
32973
+ commitMessagePath,
32974
+ ...((_a = options === null || options === void 0 ? void 0 : options.excludePaths) !== null && _a !== void 0 ? _a : []),
32975
+ ]);
32944
32976
  await runGitCommand({
32945
32977
  command: buildGitCommitCommand({
32946
32978
  commitMessagePath,
@@ -33299,6 +33331,21 @@
33299
33331
  `);
33300
33332
  }
33301
33333
 
33334
+ /**
33335
+ * Maximum amount of test output embedded into an agent prompt.
33336
+ */
33337
+ const MAX_TEST_OUTPUT_CHARS = 12000;
33338
+ /**
33339
+ * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
33340
+ */
33341
+ function limitTestOutput(testOutput) {
33342
+ const normalizedTestOutput = testOutput.trim();
33343
+ if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
33344
+ return normalizedTestOutput;
33345
+ }
33346
+ return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
33347
+ }
33348
+
33302
33349
  /**
33303
33350
  * Runs the configured verification command inside the project root and returns its output.
33304
33351
  */
@@ -33319,10 +33366,6 @@
33319
33366
  * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33320
33367
  */
33321
33368
  const MAX_PROMPT_TEST_ATTEMPTS = 3;
33322
- /**
33323
- * Maximum amount of verification output sent back to the coding agent as retry feedback.
33324
- */
33325
- const MAX_TEST_FEEDBACK_OUTPUT_CHARS = 12000;
33326
33369
  /**
33327
33370
  * File extension used by generated shell scripts.
33328
33371
  */
@@ -33368,7 +33411,7 @@
33368
33411
  return { ...result, attemptCount, steps };
33369
33412
  }
33370
33413
  const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
33371
- const feedbackVerificationOutput = limitVerificationOutputForFeedback(fullVerificationOutput);
33414
+ const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
33372
33415
  if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
33373
33416
  console.error(colors__default["default"].red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33374
33417
  throw new Error(buildFinalVerificationFailureMessage({
@@ -33498,19 +33541,6 @@
33498
33541
  \`\`\`
33499
33542
  `);
33500
33543
  }
33501
- /**
33502
- * Limits verification output before it is embedded back into the next coding prompt.
33503
- */
33504
- function limitVerificationOutputForFeedback(verificationOutput) {
33505
- const normalizedVerificationOutput = verificationOutput.trim();
33506
- if (normalizedVerificationOutput.length <= MAX_TEST_FEEDBACK_OUTPUT_CHARS) {
33507
- return normalizedVerificationOutput;
33508
- }
33509
- return spaceTrim(`
33510
- [...verification output truncated to the last ${MAX_TEST_FEEDBACK_OUTPUT_CHARS} characters...]
33511
- ${normalizedVerificationOutput.slice(-MAX_TEST_FEEDBACK_OUTPUT_CHARS)}
33512
- `);
33513
- }
33514
33544
  /**
33515
33545
  * Derives a dedicated temp-script path for verification commands.
33516
33546
  */
@@ -41371,6 +41401,7 @@
41371
41401
  autoClone: false,
41372
41402
  maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
41373
41403
  maxParallelMessages: localAgentRunnerLimits.maxParallelMessages,
41404
+ isMachineReadableProgressEnabled: true,
41374
41405
  };
41375
41406
  }
41376
41407
 
@@ -42281,6 +42312,91 @@
42281
42312
  // Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
42282
42313
  // Note: [💞] Ignore a discrepancy between file name and entity name
42283
42314
 
42315
+ /**
42316
+ * Creates a loader which imports one module on the first call and reuses the very same module afterwards
42317
+ *
42318
+ * Note: [🐌] Heavy third-party dependencies are imported lazily to keep the startup of the Promptbook CLI fast.
42319
+ * A statically imported dependency is loaded every single time the bundle is loaded, even when the running
42320
+ * command never touches it. A lazily imported dependency is loaded only when the feature is really used.
42321
+ *
42322
+ * @example
42323
+ * const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
42324
+ * const { JSDOM } = await loadJsdomModule();
42325
+ *
42326
+ * @private internal utility of Promptbook
42327
+ */
42328
+ function createLazyModuleLoader(importModule) {
42329
+ let importedModulePromise = null;
42330
+ return function loadModule() {
42331
+ if (importedModulePromise === null) {
42332
+ importedModulePromise = importModule();
42333
+ }
42334
+ return importedModulePromise;
42335
+ };
42336
+ }
42337
+ // Note: [🐌] Do not convert the lazy `import(...)` calls back to static `import` statements, it would bring back the
42338
+ // slow startup of the `ptbk` CLI utility
42339
+
42340
+ /**
42341
+ * Loads the interactive terminal prompt library (`prompts`) on demand
42342
+ *
42343
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast - most commands never ask the user
42344
+ * anything interactively
42345
+ *
42346
+ * @private internal utility of Promptbook CLI
42347
+ */
42348
+ const loadPromptsModule = createLazyModuleLoader(() => import('prompts'));
42349
+ // Note: [🟡] Code for CLI prompt loading [loadPromptsModule](src/cli/common/loadPromptsModule.ts) should never be published outside of `@promptbook/cli`
42350
+
42351
+ /**
42352
+ * Description block shared by the `ptbk coder` commands which can synchronize their changes with git.
42353
+ *
42354
+ * @private internal utility of `promptbookCli`
42355
+ */
42356
+ const CODER_GIT_SYNC_DESCRIPTION = _spaceTrim.spaceTrim(`
42357
+ Git synchronization:
42358
+ - --auto-pull pulls the latest changes before this command changes anything
42359
+ - --commit commits the changes made by this command
42360
+ - --auto-push pushes the created commit to the remote repository
42361
+ `);
42362
+ /**
42363
+ * Registers the shared `--commit`, `--auto-push` and `--auto-pull` flags on a `ptbk coder` command.
42364
+ *
42365
+ * Note: Unlike `ptbk coder run`, which commits by default and opts out through `--no-commit`,
42366
+ * these commands never touch git unless the flags are used explicitly.
42367
+ *
42368
+ * @private internal utility of `promptbookCli`
42369
+ */
42370
+ function addCoderGitSyncOptions(command) {
42371
+ command.option('--commit', 'Commit the changes made by this command with the coding-agent git identity', false);
42372
+ command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
42373
+ command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
42374
+ }
42375
+ /**
42376
+ * Converts the Commander git synchronization flags into normalized git synchronization options.
42377
+ *
42378
+ * @private internal utility of `promptbookCli`
42379
+ */
42380
+ function normalizeCoderGitSyncCliOptions(cliOptions) {
42381
+ if (cliOptions.autoPush && !cliOptions.commit) {
42382
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
42383
+ Flag \`--auto-push\` can be used only together with \`--commit\`.
42384
+
42385
+ **There is nothing to push when the changes are not committed.**
42386
+
42387
+ Actionable hint:
42388
+ - Add \`--commit\`, for example \`ptbk coder init --commit --auto-push\`.
42389
+ `));
42390
+ }
42391
+ return {
42392
+ isCommitEnabled: cliOptions.commit,
42393
+ isAutoPushEnabled: cliOptions.autoPush,
42394
+ isAutoPullEnabled: cliOptions.autoPull,
42395
+ };
42396
+ }
42397
+ // Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
42398
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
42399
+
42284
42400
  /**
42285
42401
  * Relative path to the root prompts directory used by Promptbook coder utilities.
42286
42402
  *
@@ -42534,15 +42650,17 @@
42534
42650
  */
42535
42651
  function $initializeCoderAddCommand(program) {
42536
42652
  const command = program.command('add');
42537
- command.description(_spaceTrim.spaceTrim(`
42538
- Add one ready-to-run prompt file to the queue
42653
+ command.description(_spaceTrim.spaceTrim((block) => `
42654
+ Add one ready-to-run prompt file to the queue
42539
42655
 
42540
- Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42541
- - \`ptbk coder add "some new feature"\`
42542
- - \`ptbk coder add --priority 1 "some new feature"\`
42543
- - \`ptbk coder add <<EOF ... EOF\`
42544
- - \`ptbk coder add\`
42545
- `));
42656
+ Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42657
+ - \`ptbk coder add "some new feature"\`
42658
+ - \`ptbk coder add --priority 1 "some new feature"\`
42659
+ - \`ptbk coder add <<EOF ... EOF\`
42660
+ - \`ptbk coder add\`
42661
+
42662
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
42663
+ `));
42546
42664
  command.argument('[description]', 'Plain-language description of the feature or task to implement');
42547
42665
  command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
42548
42666
  command.option('--template <template>', _spaceTrim.spaceTrim(`
@@ -42552,15 +42670,26 @@
42552
42670
  .map(({ id }) => id)
42553
42671
  .join(', ')}) or a markdown file path relative to the current project root.
42554
42672
  `));
42673
+ addCoderGitSyncOptions(command);
42555
42674
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
42556
42675
  const { priority, template: templateOption } = cliOptions;
42676
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
42677
+ const projectPath = process.cwd();
42557
42678
  const description = await resolveCoderPromptDescription(descriptionArgument);
42558
- await addCoderPrompt({
42559
- projectPath: process.cwd(),
42679
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
42680
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
42681
+ await $pullCoderChanges({ gitSync, projectPath });
42682
+ const { /* filePath,*/ emojiTag } = await addCoderPrompt({
42683
+ projectPath,
42560
42684
  description,
42561
42685
  priority,
42562
42686
  templateOption,
42563
42687
  });
42688
+ await $commitCoderChanges({
42689
+ gitSync,
42690
+ projectPath,
42691
+ commitMessage: `${emojiTag} Add prompt`,
42692
+ });
42564
42693
  }));
42565
42694
  }
42566
42695
  /**
@@ -42634,7 +42763,8 @@
42634
42763
  }
42635
42764
  return standardInputDescription;
42636
42765
  }
42637
- const response = await prompts__default["default"]({
42766
+ const { default: prompts } = await loadPromptsModule();
42767
+ const response = await prompts({
42638
42768
  type: 'text',
42639
42769
  name: 'description',
42640
42770
  message: 'Describe the feature or task to add',
@@ -43054,9 +43184,11 @@
43054
43184
  */
43055
43185
  function $initializeCoderGenerateBoilerplatesCommand(program) {
43056
43186
  const command = program.command('generate-boilerplates');
43057
- command.description(_spaceTrim.spaceTrim(`
43058
- Generate prompt boilerplate files with unique emoji tags
43059
- `));
43187
+ command.description(_spaceTrim.spaceTrim((block) => `
43188
+ Generate prompt boilerplate files with unique emoji tags
43189
+
43190
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
43191
+ `));
43060
43192
  command.option('--count <count>', `Number of prompt boilerplate files to generate`, '5');
43061
43193
  command.option('--template <template>', _spaceTrim.spaceTrim(`
43062
43194
  Prompt template to use.
@@ -43065,14 +43197,25 @@
43065
43197
  .map(({ id }) => id)
43066
43198
  .join(', ')}) or a markdown file path relative to the current project root.
43067
43199
  `));
43200
+ addCoderGitSyncOptions(command);
43068
43201
  command.action(handleActionErrors(async (cliOptions) => {
43069
43202
  const { count: countOption, template: templateOption } = cliOptions;
43070
43203
  const filesCount = parseFilesCount(countOption);
43204
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
43205
+ const projectPath = process.cwd();
43206
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
43207
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
43208
+ await $pullCoderChanges({ gitSync, projectPath });
43071
43209
  await generatePromptBoilerplate({
43072
- projectPath: process.cwd(),
43210
+ projectPath,
43073
43211
  filesCount,
43074
43212
  templateOption,
43075
43213
  });
43214
+ await $commitCoderChanges({
43215
+ gitSync,
43216
+ projectPath,
43217
+ commitMessage: `Prompts ${filesCount}x`,
43218
+ });
43076
43219
  return process.exit(0);
43077
43220
  }));
43078
43221
  }
@@ -43485,6 +43628,27 @@
43485
43628
  }
43486
43629
  // Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
43487
43630
 
43631
+ /**
43632
+ * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43633
+ *
43634
+ * @private internal utility of `ptbk coder`
43635
+ */
43636
+ const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43637
+ /**
43638
+ * Returns the default coder `AGENT_CODING.md` quick-reference content.
43639
+ *
43640
+ * @private internal utility of `ptbk coder`
43641
+ */
43642
+ function getDefaultCoderAgentCodingFileContent() {
43643
+ return _spaceTrim.spaceTrim(`
43644
+ # ✨ Promptbook Coder agent coding
43645
+
43646
+ This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
43647
+ `);
43648
+ }
43649
+ // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43650
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
43651
+
43488
43652
  /**
43489
43653
  * Relative path to the shared coder context file initialized in project roots.
43490
43654
  *
@@ -43512,106 +43676,6 @@
43512
43676
  // Note: [🟡] Code for coder AGENTS file boilerplate [agentsFile](src/cli/cli-commands/coder/agentsFile.ts) should never be published outside of `@promptbook/cli`
43513
43677
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
43514
43678
 
43515
- /**
43516
- * Normalizes one project-relative path for human-readable CLI output and markdown.
43517
- *
43518
- * @private internal utility of `ptbk coder`
43519
- */
43520
- function formatDisplayPath(relativePath) {
43521
- return relativePath.replace(/\\/gu, '/');
43522
- }
43523
- // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43524
-
43525
- /**
43526
- * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43527
- *
43528
- * @private internal utility of `ptbk coder`
43529
- */
43530
- const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43531
- /**
43532
- * Returns the default coder `AGENT_CODING.md` quick-reference content.
43533
- *
43534
- * @private internal utility of `ptbk coder`
43535
- */
43536
- function getDefaultCoderAgentCodingFileContent({ packageJsonScripts, }) {
43537
- return _spaceTrim.spaceTrim((block) => `
43538
- # Promptbook Coder quick reference
43539
-
43540
- This project is prepared for the \`ptbk coder\` workflow. Promptbook Coder does not create a new model on its own; it orchestrates coding agents such as GitHub Copilot, OpenAI Codex, Claude Code, Opencode, Cline, and Gemini CLI through prompt files in \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\`.
43541
-
43542
- ## Workflow
43543
- 1. Put repository-wide coding rules into \`${AGENTS_FILE_PATH}\`. The default \`npm run coder:run\` script already passes \`--context ${AGENTS_FILE_PATH}\`.
43544
- 2. Create or customize prompt templates in \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/\`. ${buildStarterTemplateSentence()}
43545
- 3. Generate prompt files with \`npm run coder:generate-boilerplates\` or \`ptbk coder generate-boilerplates --template <template> --count <count>\`. To append a single ready-to-run prompt straight from a description, use \`npm run coder:add\` or \`ptbk coder add "some new feature"\`.
43546
- 4. Replace every \`@@@\`, keep drafts as \`[-]\`, and switch prompts to \`[ ]\` when they are ready to run. Completed prompts are marked \`[x]\`.
43547
- 5. Run \`npm run coder:run\` to execute the next ready prompt with the configured coding agent.
43548
- 6. Use \`npm run coder:verify\` to archive finished prompts into \`${formatDisplayPath(PROMPTS_DONE_DIRECTORY_PATH)}/\` and append repair follow-up prompts when more work is needed.
43549
- 7. Use \`ptbk coder find-refactor-candidates\` when you want Promptbook to suggest refactor prompts automatically.
43550
-
43551
- ## Templates
43552
- - Project-owned templates created by \`ptbk coder init\`: ${formatInlineCodeList(getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath)))}
43553
- - Built-in \`--template\` aliases: ${formatInlineCodeList(getDefaultCoderPromptTemplateDefinitions().map(({ id }) => id))}
43554
- - To add a custom template, create a markdown file such as \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43555
- - To use a project template, run \`ptbk coder generate-boilerplates --template ${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43556
- - Keep shared repository rules in \`${AGENTS_FILE_PATH}\` and recurring task-family rules in template files so individual prompt files stay focused on the actual task.
43557
-
43558
- ## Created npm scripts
43559
- | Script | Purpose |
43560
- | --- | --- |
43561
- ${block(buildPackageJsonScriptTableLines(packageJsonScripts).join('\n'))}
43562
-
43563
- ## Customizing the workflow
43564
- - Edit \`package.json\` if you want \`npm run coder:run\` to use another coding agent, model, thinking level, context file, or wait mode.
43565
- - Use direct CLI commands when you need one-off flags such as \`--min-priority\`, \`--max-priority\`, \`--ignore-git-changes\`, \`--no-commit\`, \`--dry-run\`, \`--test\`, \`--allow-credits\`, or \`--auto-migrate\`.
43566
- - Use \`ptbk coder --help\` and \`ptbk coder <command> --help\` for the full CLI reference.
43567
- `);
43568
- }
43569
- /**
43570
- * Builds the sentence describing the starter templates created during initialization.
43571
- */
43572
- function buildStarterTemplateSentence() {
43573
- const starterTemplatePaths = getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath));
43574
- if (starterTemplatePaths.length === 1) {
43575
- return `The starter project template created by \`ptbk coder init\` is \`${starterTemplatePaths[0]}\`.`;
43576
- }
43577
- return `The starter project templates created by \`ptbk coder init\` are ${formatInlineCodeList(starterTemplatePaths)}.`;
43578
- }
43579
- /**
43580
- * Builds the markdown table rows describing the initialized npm scripts.
43581
- */
43582
- function buildPackageJsonScriptTableLines(packageJsonScripts) {
43583
- return Object.entries(packageJsonScripts).map(([scriptName, scriptCommand]) => `| \`npm run ${scriptName}\` | ${describeDefaultCoderPackageJsonScript(scriptName, scriptCommand)} |`);
43584
- }
43585
- /**
43586
- * Describes one initialized npm script in human-readable terms.
43587
- */
43588
- function describeDefaultCoderPackageJsonScript(scriptName, scriptCommand) {
43589
- if (scriptName === 'coder:generate-boilerplates') {
43590
- return `Runs \`${scriptCommand}\` to create new prompt files in \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\`.`;
43591
- }
43592
- if (scriptName === 'coder:add') {
43593
- return `Runs \`${scriptCommand}\` to add one ready-to-run prompt file to \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\` from a plain-language description.`;
43594
- }
43595
- if (scriptName === 'coder:run') {
43596
- return `Runs \`${scriptCommand}\` to execute the next ready prompt with shared repository context from \`${AGENTS_FILE_PATH}\`.`;
43597
- }
43598
- if (scriptName === 'coder:find-refactor-candidates') {
43599
- return `Runs \`${scriptCommand}\` to generate prompt candidates for large or crowded files.`;
43600
- }
43601
- if (scriptName === 'coder:verify') {
43602
- return `Runs \`${scriptCommand}\` to archive verified prompts into \`${formatDisplayPath(PROMPTS_DONE_DIRECTORY_PATH)}/\` and append repair prompts when needed.`;
43603
- }
43604
- return `Runs \`${scriptCommand}\`.`;
43605
- }
43606
- /**
43607
- * Formats one inline code list for human-readable markdown.
43608
- */
43609
- function formatInlineCodeList(values) {
43610
- return values.map((value) => `\`${value}\``).join(', ');
43611
- }
43612
- // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43613
- // Note: [💞] Ignore a discrepancy between file name and exported helper names
43614
-
43615
43679
  /**
43616
43680
  * Relative directory path for agents initialized by `ptbk coder init`.
43617
43681
  *
@@ -43676,6 +43740,16 @@
43676
43740
  }
43677
43741
  // Note: [🟡] Code for coder init developer agent bootstrapping [ensureCoderDeveloperAgentFile](src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts) should never be published outside of `@promptbook/cli`
43678
43742
 
43743
+ /**
43744
+ * Normalizes one project-relative path for human-readable CLI output and markdown.
43745
+ *
43746
+ * @private internal utility of `ptbk coder`
43747
+ */
43748
+ function formatDisplayPath(relativePath) {
43749
+ return relativePath.replace(/\\/gu, '/');
43750
+ }
43751
+ // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43752
+
43679
43753
  /**
43680
43754
  * Fallback `.env` content used when no required variables need to be appended.
43681
43755
  */
@@ -43753,7 +43827,7 @@
43753
43827
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
43754
43828
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
43755
43829
  'coder:add': 'ptbk coder add --template ./prompts/templates/common.md',
43756
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.5 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
43830
+ 'coder:run': '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',
43757
43831
  // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
43758
43832
  'coder:verify': 'ptbk coder verify',
43759
43833
  };
@@ -44002,9 +44076,7 @@
44002
44076
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
44003
44077
  const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
44004
44078
  const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
44005
- const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent({
44006
- packageJsonScripts: getDefaultCoderPackageJsonScripts(),
44007
- }));
44079
+ const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
44008
44080
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44009
44081
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44010
44082
  const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
@@ -44119,12 +44191,24 @@
44119
44191
 
44120
44192
  Checks that the coding harnesses are installed globally and up to date:
44121
44193
  ${block(listCheckedHarnessLabels())}
44194
+
44195
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44122
44196
  `));
44123
- command.action(handleActionErrors(async () => {
44197
+ addCoderGitSyncOptions(command);
44198
+ command.action(handleActionErrors(async (cliOptions) => {
44199
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44124
44200
  const projectPath = process.cwd();
44201
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
44202
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
44203
+ await $pullCoderChanges({ gitSync, projectPath });
44125
44204
  const summary = await initializeCoderProjectConfiguration(projectPath);
44126
44205
  printInitializationSummary(summary);
44127
44206
  await generatePromptBoilerplate({ projectPath, filesCount: 5 });
44207
+ await $commitCoderChanges({
44208
+ gitSync,
44209
+ projectPath,
44210
+ commitMessage: 'Initialize Promptbook Coder',
44211
+ });
44128
44212
  await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
44129
44213
  }));
44130
44214
  }
@@ -44145,6 +44229,48 @@
44145
44229
  // Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
44146
44230
  // Note: [💞] Ignore a discrepancy between file name and entity name
44147
44231
 
44232
+ /**
44233
+ * Initializes `coder ping` command for Promptbook CLI utilities
44234
+ *
44235
+ * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
44236
+ *
44237
+ * @private internal function of `promptbookCli`
44238
+ */
44239
+ function $initializeCoderPingCommand(program) {
44240
+ const command = program.command('ping');
44241
+ command.description(_spaceTrim.spaceTrim(`
44242
+ Send one tiny dummy prompt to a harness and model to measure and warm them up
44243
+
44244
+ ${PROMPT_RUNNER_DESCRIPTION}
44245
+
44246
+ Features:
44247
+ - Verifies that the selected harness, model, thinking level and authentication really work
44248
+ - Reports the answer of the harness, the response time and the reported usage
44249
+ - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44250
+ - Leaves the project exactly as it was — nothing is read, written, changed or committed
44251
+ - Use --no-ui to stream the raw harness output instead of only the compact result
44252
+ `));
44253
+ addPromptRunnerSelectionOptions(command);
44254
+ addPromptRunnerRuntimeOptions(command);
44255
+ command.action(handleActionErrors(async (cliOptions) => {
44256
+ const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44257
+ await $ensureHarnessInstallations([runnerOptions.agentName]);
44258
+ // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
44259
+ const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
44260
+ const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
44261
+ const result = await pingCoderHarness({
44262
+ agentName: runnerOptions.agentName,
44263
+ model: runnerOptions.model,
44264
+ thinkingLevel: runnerOptions.thinkingLevel,
44265
+ allowCredits: runnerOptions.allowCredits,
44266
+ shouldPrintLiveOutput: runnerOptions.noUi,
44267
+ });
44268
+ printCoderPingResult(result);
44269
+ }));
44270
+ }
44271
+ // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44272
+ // Note: [💞] Ignore a discrepancy between file name and entity name
44273
+
44148
44274
  /**
44149
44275
  * Creates a Commander argument parser that accepts only non-negative integers.
44150
44276
  *
@@ -44169,6 +44295,21 @@
44169
44295
  }
44170
44296
  // Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
44171
44297
 
44298
+ /**
44299
+ * Modes supported by `ptbk coder run --test-before`.
44300
+ */
44301
+ const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
44302
+ /**
44303
+ * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
44304
+ */
44305
+ const DEFAULT_CODER_TEST_COMMAND = 'npm test';
44306
+ /**
44307
+ * Checks whether a value is a supported `--test-before` mode.
44308
+ */
44309
+ function isTestBeforeMode(value) {
44310
+ return TEST_BEFORE_MODE_VALUES.includes(value);
44311
+ }
44312
+
44172
44313
  /**
44173
44314
  * Default wait duration applied before retrying a prompt round after an error (10 minutes).
44174
44315
  *
@@ -44213,6 +44354,7 @@
44213
44354
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44214
44355
  - Checks that the selected harness is installed globally and up to date before the first prompt
44215
44356
  - Supports GPG signing of commits
44357
+ - Optional pre-coding test run that can stop or repair pre-existing failures
44216
44358
  - Optional post-prompt verification with test-feedback retries
44217
44359
  - Progress tracking and interactive P/S/X terminal controls
44218
44360
  - Dry-run mode to preview prompts
@@ -44222,6 +44364,9 @@
44222
44364
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
44223
44365
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
44224
44366
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
44367
+ 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)`)
44368
+ .choices([...TEST_BEFORE_MODE_VALUES])
44369
+ .default('no'));
44225
44370
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
44226
44371
  addPromptRunnerExecutionOptions(command);
44227
44372
  command.option('--isolate', _spaceTrim.spaceTrim(`
@@ -44251,8 +44396,9 @@
44251
44396
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
44252
44397
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
44253
44398
  command.action(handleActionErrors(async (cliOptions) => {
44254
- const { dryRun, agent, context, test, preserveLogs, isolate: isIsolated, priority, minPriority: minimumPriority, maxPriority: maximumPriority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
44255
- const testCommand = normalizeCommandOptionValue$1(test);
44399
+ const { dryRun, agent, context, test, testBefore, preserveLogs, isolate: isIsolated, priority, minPriority: minimumPriority, maxPriority: maximumPriority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
44400
+ const configuredTestCommand = normalizeCommandOptionValue$1(test);
44401
+ const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
44256
44402
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
44257
44403
  isAgentRequired: !dryRun,
44258
44404
  });
@@ -44281,6 +44427,7 @@
44281
44427
  agent,
44282
44428
  context,
44283
44429
  testCommand,
44430
+ testBefore,
44284
44431
  preserveLogs,
44285
44432
  isIsolated,
44286
44433
  noUi: runnerOptions.noUi,
@@ -44491,25 +44638,31 @@
44491
44638
  */
44492
44639
  function $initializeCoderVerifyCommand(program) {
44493
44640
  const command = program.command('verify');
44494
- command.description(_spaceTrim.spaceTrim(`
44495
- Interactive verification helper for completed prompts
44641
+ command.description(_spaceTrim.spaceTrim((block) => `
44642
+ Interactive verification helper for completed prompts
44496
44643
 
44497
- Features:
44498
- - Displays list of prompt files with status counts
44499
- - Guides through verification of completed prompts marked [x]
44500
- - Archives verified prompt files to prompts/done/ directory
44501
- - Auto-appends repair prompts for incomplete work
44502
- - Processes files with all-done prompts first
44503
- - Supports ignoring matching prompt candidates for one verification run
44504
- `));
44644
+ Features:
44645
+ - Displays list of prompt files with status counts
44646
+ - Guides through verification of completed prompts marked [x]
44647
+ - Archives verified prompt files to prompts/done/ directory
44648
+ - Auto-appends repair prompts for incomplete work
44649
+ - Processes files with all-done prompts first
44650
+ - Supports ignoring matching prompt candidates for one verification run
44651
+
44652
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44653
+
44654
+ Note: The git synchronization is applied around each single verification, not once per run.
44655
+ `));
44505
44656
  command.option('--reverse', 'Process prompt files in reverse order', false);
44506
44657
  command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
44658
+ addCoderGitSyncOptions(command);
44507
44659
  command.action(handleActionErrors(async (cliOptions) => {
44508
44660
  const { reverse, ignore } = cliOptions;
44661
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44509
44662
  // Note: Import the main function dynamically to avoid loading heavy dependencies until needed
44510
44663
  const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
44511
44664
  try {
44512
- await verifyPrompts({ reverse, ignore });
44665
+ await verifyPrompts({ reverse, ignore, gitSync });
44513
44666
  }
44514
44667
  catch (error) {
44515
44668
  console.error(colors__default["default"].bgRed('Prompt verification failed:'), error);
@@ -44538,6 +44691,7 @@
44538
44691
  * - generate-boilerplates: Generate prompt boilerplate files
44539
44692
  * - find-refactor-candidates: Find files that need refactoring
44540
44693
  * - run: Run coding prompts with AI agents
44694
+ * - ping: Test one harness and model with a tiny dummy prompt
44541
44695
  * - verify: Verify completed prompts
44542
44696
  * - find-fresh-emoji-tags: Find unused emoji tags
44543
44697
  *
@@ -44557,6 +44711,7 @@
44557
44711
  - find-refactor-candidates: Find files that need refactoring
44558
44712
  - find-unwritten: List prompt sections that still need to be authored
44559
44713
  - run: Run coding prompts with AI agents
44714
+ - ping: Test the connection, response time and quota of one harness and model
44560
44715
  - server: Start a long-running coder server with a kanban web UI
44561
44716
  - verify: Verify completed prompts
44562
44717
  - find-fresh-emoji-tags: Find unused emoji tags
@@ -44568,6 +44723,7 @@
44568
44723
  $initializeCoderFindRefactorCandidatesCommand(coderCommand);
44569
44724
  $initializeCoderFindUnwrittenCommand(coderCommand);
44570
44725
  $initializeCoderRunCommand(coderCommand);
44726
+ $initializeCoderPingCommand(coderCommand);
44571
44727
  $initializeCoderServerCommand(coderCommand);
44572
44728
  $initializeCoderVerifyCommand(coderCommand);
44573
44729
  $initializeCoderFindFreshEmojiTagCommand(coderCommand);
@@ -45401,6 +45557,14 @@
45401
45557
  // Note: [🟢] Code for Node file-cache storage [FileCacheStorage](src/storage/file-cache-storage/FileCacheStorage.ts) should never be published into packages that could be imported into browser environment
45402
45558
  // TODO: [🌗] Maybe some checkers, not all valid JSONs are desired and valid values
45403
45559
 
45560
+ /**
45561
+ * Loads the Socket.io client (`socket.io-client`) on demand
45562
+ *
45563
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
45564
+ *
45565
+ * @private internal utility of `createRemoteClient`
45566
+ */
45567
+ const loadSocketIoClientModule = createLazyModuleLoader(() => import('socket.io-client'));
45404
45568
  /**
45405
45569
  * Creates a connection to the remote proxy server.
45406
45570
  *
@@ -45429,8 +45593,9 @@
45429
45593
 
45430
45594
  `));
45431
45595
  }
45596
+ const { io } = await loadSocketIoClientModule();
45432
45597
  return new Promise((resolve, reject) => {
45433
- const socket = socket_ioClient.io(remoteServerUrl, {
45598
+ const socket = io(remoteServerUrl, {
45434
45599
  retries: CONNECTION_RETRIES_LIMIT,
45435
45600
  timeout: CONNECTION_TIMEOUT_MS,
45436
45601
  path: '/socket.io',
@@ -46096,7 +46261,8 @@
46096
46261
  You will be logged in to ${remoteServerUrl}
46097
46262
  If you don't have an account, it will be created automatically.
46098
46263
  `)));
46099
- const { username, password } = await prompts__default["default"]([
46264
+ const { default: prompts } = await loadPromptsModule();
46265
+ const { username, password } = await prompts([
46100
46266
  {
46101
46267
  type: 'text',
46102
46268
  name: 'username',
@@ -46471,6 +46637,15 @@
46471
46637
  // TODO: Implement non-interactive login
46472
46638
  // Note: [💞] Ignore a discrepancy between file name and entity name
46473
46639
 
46640
+ /**
46641
+ * Loads the ZIP archive library (`jszip`) on demand
46642
+ *
46643
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
46644
+ *
46645
+ * @private internal utility of `loadArchive` and `saveArchive`
46646
+ */
46647
+ const loadJsZipModule = createLazyModuleLoader(() => import('jszip'));
46648
+
46474
46649
  /**
46475
46650
  * Loads the books from the archive file with `.bookc` extension
46476
46651
  *
@@ -46485,7 +46660,8 @@
46485
46660
  throw new UnexpectedError(`Archive file must have '.bookc' extension`);
46486
46661
  }
46487
46662
  const data = await fs.readFile(filePath);
46488
- const archive = await JSZip__default["default"].loadAsync(data);
46663
+ const { default: JSZip } = await loadJsZipModule();
46664
+ const archive = await JSZip.loadAsync(data);
46489
46665
  const indexFile = archive.file('index.book.json');
46490
46666
  if (!indexFile) {
46491
46667
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
@@ -50548,7 +50724,23 @@
50548
50724
  });
50549
50725
  }
50550
50726
 
50551
- // TODO: [🏳‍🌈] Finally take pick of .json vs .ts
50727
+ /**
50728
+ * Loads `jsdom` on demand
50729
+ *
50730
+ * Note: [🐌] `jsdom` is by far the heaviest dependency of Promptbook, loading it eagerly would slow down every single
50731
+ * run of the `ptbk` CLI utility even when no website is scraped
50732
+ *
50733
+ * @private internal utility of `WebsiteScraper`
50734
+ */
50735
+ const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
50736
+ /**
50737
+ * Loads `@mozilla/readability` on demand
50738
+ *
50739
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
50740
+ *
50741
+ * @private internal utility of `WebsiteScraper`
50742
+ */
50743
+ const loadReadabilityModule = createLazyModuleLoader(() => import('@mozilla/readability'));
50552
50744
  /**
50553
50745
  * Scraper for websites
50554
50746
  *
@@ -50584,14 +50776,15 @@
50584
50776
  if (this.tools.fs === undefined) {
50585
50777
  throw new EnvironmentMismatchError('Can not scrape websites without filesystem tools');
50586
50778
  }
50587
- const jsdom$1 = new jsdom.JSDOM(await source.asText(), {
50779
+ const [{ JSDOM }, { Readability }] = await Promise.all([loadJsdomModule(), loadReadabilityModule()]);
50780
+ const jsdom = new JSDOM(await source.asText(), {
50588
50781
  url: source.url,
50589
50782
  });
50590
- const reader = new readability.Readability(jsdom$1.window.document);
50783
+ const reader = new Readability(jsdom.window.document);
50591
50784
  const article = reader.parse();
50592
50785
  // console.log(article);
50593
50786
  // await forTime(10000);
50594
- let html = (article === null || article === void 0 ? void 0 : article.content) || (article === null || article === void 0 ? void 0 : article.textContent) || jsdom$1.window.document.body.innerHTML;
50787
+ let html = (article === null || article === void 0 ? void 0 : article.content) || (article === null || article === void 0 ? void 0 : article.textContent) || jsdom.window.document.body.innerHTML;
50595
50788
  // Note: Unwrap html such as it is convertable by `markdownConverter`
50596
50789
  for (let i = 0; i < 2; i++) {
50597
50790
  html = html.replace(/<div\s*(?:id="readability-page-\d+"\s+class="page")?>(.*)<\/div>/is, '$1');
@@ -50624,7 +50817,7 @@
50624
50817
  throw error;
50625
50818
  }
50626
50819
  }
50627
- const markdown = this.showdownConverter.makeMarkdown(html, jsdom$1.window.document);
50820
+ const markdown = this.showdownConverter.makeMarkdown(html, jsdom.window.document);
50628
50821
  return { ...cacheFilehandler, markdown };
50629
50822
  }
50630
50823
  /**
@@ -54187,7 +54380,8 @@
54187
54380
  for (const pipelineJson of collectionJson) {
54188
54381
  validatePipeline(pipelineJson);
54189
54382
  }
54190
- const archive = new JSZip__default["default"]();
54383
+ const { default: JSZip } = await loadJsZipModule();
54384
+ const archive = new JSZip();
54191
54385
  const collectionJsonString = stringifyPipelineJson(collectionJson);
54192
54386
  archive.file('index.book.json', collectionJsonString);
54193
54387
  const data = await archive.generateAsync({ type: 'nodebuffer', streamFiles: true });
@@ -54815,7 +55009,8 @@
54815
55009
  else {
54816
55010
  console.info(colors__default["default"].gray(`---`));
54817
55011
  }
54818
- const response = await prompts__default["default"]({
55012
+ const { default: prompts } = await loadPromptsModule();
55013
+ const response = await prompts({
54819
55014
  type: 'text',
54820
55015
  name: 'userMessage',
54821
55016
  message: 'User message',
@@ -55122,7 +55317,8 @@
55122
55317
  if (pipelineSource) {
55123
55318
  return pipelineSource;
55124
55319
  }
55125
- const response = await prompts__default["default"]({
55320
+ const { default: prompts } = await loadPromptsModule();
55321
+ const response = await prompts({
55126
55322
  type: 'text',
55127
55323
  name: 'pipelineSource',
55128
55324
  message: '',
@@ -55209,7 +55405,8 @@
55209
55405
  console.error(colors__default["default"].red(createRunMissingInputParametersMessage(pipeline, inputParameters, questions)));
55210
55406
  return process.exit(1);
55211
55407
  }
55212
- const response = await prompts__default["default"](questions);
55408
+ const { default: prompts } = await loadPromptsModule();
55409
+ const response = await prompts(questions);
55213
55410
  // <- TODO: [🧠][🍼] Change behavior according to the formfactor
55214
55411
  return { ...inputParameters, ...response };
55215
55412
  // <- TODO: Maybe do some validation of the response (and --json argument which is passed)
@@ -58114,6 +58311,14 @@
58114
58311
  fullname: 'Anthropic Claude',
58115
58312
  color: '#d97706',
58116
58313
  };
58314
+ /**
58315
+ * Loads the Anthropic Claude SDK (`@anthropic-ai/sdk`) on demand
58316
+ *
58317
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58318
+ *
58319
+ * @private internal utility of `AnthropicClaudeExecutionTools`
58320
+ */
58321
+ const loadAnthropicClaudeModule = createLazyModuleLoader(() => import('@anthropic-ai/sdk'));
58117
58322
  /**
58118
58323
  * Execution Tools for calling Anthropic Claude API.
58119
58324
  *
@@ -58151,7 +58356,8 @@
58151
58356
  const anthropicOptions = { ...this.options };
58152
58357
  delete anthropicOptions.isVerbose;
58153
58358
  delete anthropicOptions.isProxied;
58154
- this.client = new Anthropic__default["default"](anthropicOptions);
58359
+ const { Anthropic } = await loadAnthropicClaudeModule();
58360
+ this.client = new Anthropic(anthropicOptions);
58155
58361
  }
58156
58362
  return this.client;
58157
58363
  }
@@ -58414,6 +58620,14 @@
58414
58620
  fullname: 'Azure OpenAI',
58415
58621
  color: '#0078d4',
58416
58622
  };
58623
+ /**
58624
+ * Loads the Azure OpenAI SDK (`@azure/openai`) on demand
58625
+ *
58626
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58627
+ *
58628
+ * @private internal utility of `AzureOpenAiExecutionTools`
58629
+ */
58630
+ const loadAzureOpenAiModule = createLazyModuleLoader(() => import('@azure/openai'));
58417
58631
  /**
58418
58632
  * Execution Tools for calling Azure OpenAI API.
58419
58633
  *
@@ -58447,7 +58661,8 @@
58447
58661
  }
58448
58662
  async getClient() {
58449
58663
  if (this.client === null) {
58450
- this.client = new openai.OpenAIClient(`https://${this.options.resourceName}.openai.azure.com/`, new openai.AzureKeyCredential(this.options.apiKey));
58664
+ const { AzureKeyCredential, OpenAIClient } = await loadAzureOpenAiModule();
58665
+ this.client = new OpenAIClient(`https://${this.options.resourceName}.openai.azure.com/`, new AzureKeyCredential(this.options.apiKey));
58451
58666
  }
58452
58667
  return this.client;
58453
58668
  }
@@ -60256,6 +60471,14 @@
60256
60471
  }
60257
60472
  }
60258
60473
 
60474
+ /**
60475
+ * Loads the OpenAI SDK (`openai`) on demand
60476
+ *
60477
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
60478
+ *
60479
+ * @private internal utility of `OpenAiCompatibleRequestManager`
60480
+ */
60481
+ const loadOpenAiModule = createLazyModuleLoader(() => import('openai'));
60259
60482
  /**
60260
60483
  * Manages OpenAI-compatible client creation plus shared retry and rate-limit behavior.
60261
60484
  *
@@ -60282,7 +60505,8 @@
60282
60505
  timeout: API_REQUEST_TIMEOUT,
60283
60506
  maxRetries: CONNECTION_RETRIES_LIMIT,
60284
60507
  };
60285
- this.client = new OpenAI__default["default"](enhancedOptions);
60508
+ const { default: OpenAI } = await loadOpenAiModule();
60509
+ this.client = new OpenAI(enhancedOptions);
60286
60510
  }
60287
60511
  return this.client;
60288
60512
  }
@@ -67995,8 +68219,9 @@
67995
68219
  if (options.isVerbose) {
67996
68220
  console.info(colors__default["default"].gray('Type "exit" or "quit" to end the chat.'));
67997
68221
  }
68222
+ const { default: prompts } = await loadPromptsModule();
67998
68223
  while (true) {
67999
- const response = await prompts__default["default"]({
68224
+ const response = await prompts({
68000
68225
  type: 'text',
68001
68226
  name: 'userMessage',
68002
68227
  message: 'User message',
@@ -69208,6 +69433,16 @@
69208
69433
  }
69209
69434
  }
69210
69435
 
69436
+ /**
69437
+ * Loads the OpenAI AgentKit SDK (`@openai/agents`) on demand
69438
+ *
69439
+ * Note: [🐌] The AgentKit SDK is one of the heaviest dependencies of Promptbook, loading it eagerly would slow down
69440
+ * every single run of the `ptbk` CLI utility even when no AgentKit agent is used
69441
+ *
69442
+ * @private internal utility of `@promptbook/openai`
69443
+ */
69444
+ const loadOpenAiAgentsModule = createLazyModuleLoader(() => import('@openai/agents'));
69445
+
69211
69446
  /**
69212
69447
  * Constant for default model used for nested DeepSearch tool invocations.
69213
69448
  */
@@ -69261,11 +69496,12 @@
69261
69496
  /**
69262
69497
  * Builds the tool list for AgentKit, including hosted file search when applicable.
69263
69498
  */
69264
- buildAgentKitTools(options) {
69499
+ async buildAgentKitTools(options) {
69265
69500
  const { tools, vectorStoreId } = options;
69501
+ const { fileSearchTool, tool: agentKitTool } = await loadOpenAiAgentsModule();
69266
69502
  const agentKitTools = [];
69267
69503
  if (vectorStoreId) {
69268
- agentKitTools.push(agents.fileSearchTool(vectorStoreId));
69504
+ agentKitTools.push(fileSearchTool(vectorStoreId));
69269
69505
  }
69270
69506
  if (!tools || tools.length === 0) {
69271
69507
  return agentKitTools;
@@ -69273,11 +69509,11 @@
69273
69509
  let scriptTools = null;
69274
69510
  for (const toolDefinition of tools) {
69275
69511
  if (this.isDeepSearchToolDefinition(toolDefinition)) {
69276
- agentKitTools.push(this.createDeepSearchAgentKitTool(toolDefinition));
69512
+ agentKitTools.push(await this.createDeepSearchAgentKitTool(toolDefinition));
69277
69513
  continue;
69278
69514
  }
69279
69515
  scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
69280
- agentKitTools.push(agents.tool({
69516
+ agentKitTools.push(agentKitTool({
69281
69517
  name: toolDefinition.name,
69282
69518
  description: toolDefinition.description,
69283
69519
  parameters: this.normalizeAgentKitToolParameters(toolDefinition.parameters),
@@ -69484,12 +69720,13 @@
69484
69720
  /**
69485
69721
  * Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
69486
69722
  */
69487
- createDeepSearchAgentKitTool(toolDefinition) {
69488
- const deepSearchAgent = new agents.Agent({
69723
+ async createDeepSearchAgentKitTool(toolDefinition) {
69724
+ const { Agent: AgentFromKit, webSearchTool } = await loadOpenAiAgentsModule();
69725
+ const deepSearchAgent = new AgentFromKit({
69489
69726
  name: 'DeepSearch',
69490
69727
  model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
69491
69728
  instructions: this.createDeepSearchAgentInstructions(toolDefinition.description),
69492
- tools: [agents.webSearchTool({ searchContextSize: 'high' })],
69729
+ tools: [webSearchTool({ searchContextSize: 'high' })],
69493
69730
  });
69494
69731
  return deepSearchAgent.asTool({
69495
69732
  toolName: toolDefinition.name,
@@ -69698,8 +69935,9 @@
69698
69935
  vectorStoreId,
69699
69936
  });
69700
69937
  }
69701
- const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
69702
- const openAiAgentKitAgent = new agents.Agent({
69938
+ const { Agent: AgentFromKit } = await loadOpenAiAgentsModule();
69939
+ const agentKitTools = await this.buildAgentKitTools({ tools, vectorStoreId });
69940
+ const openAiAgentKitAgent = new AgentFromKit({
69703
69941
  name,
69704
69942
  model: this.agentKitModelName,
69705
69943
  instructions: instructions || 'You are a helpful assistant.',
@@ -69744,7 +69982,8 @@
69744
69982
  agentName: agentForRun.name,
69745
69983
  input: inputItems,
69746
69984
  };
69747
- const streamResult = await agents.run(agentForRun, inputItems, {
69985
+ const { run } = await loadOpenAiAgentsModule();
69986
+ const streamResult = await run(agentForRun, inputItems, {
69748
69987
  stream: true,
69749
69988
  maxTurns: 200,
69750
69989
  context: {
@@ -69886,11 +70125,12 @@
69886
70125
  * Ensures the AgentKit SDK is wired to the OpenAI client and API key.
69887
70126
  */
69888
70127
  async ensureAgentKitDefaults() {
70128
+ const { setDefaultOpenAIClient, setDefaultOpenAIKey } = await loadOpenAiAgentsModule();
69889
70129
  const client = await this.getClient();
69890
- agents.setDefaultOpenAIClient(client);
70130
+ setDefaultOpenAIClient(client);
69891
70131
  const apiKey = this.agentKitOptions.apiKey;
69892
70132
  if (apiKey && typeof apiKey === 'string') {
69893
- agents.setDefaultOpenAIKey(apiKey);
70133
+ setDefaultOpenAIKey(apiKey);
69894
70134
  }
69895
70135
  }
69896
70136
  /**
@@ -72218,6 +72458,66 @@
72218
72458
  RemoteAgent: RemoteAgent
72219
72459
  });
72220
72460
 
72461
+ /**
72462
+ * Git synchronization which leaves the repository completely untouched.
72463
+ *
72464
+ * Note: This is the default for every command and helper which supports the git synchronization.
72465
+ */
72466
+ const DISABLED_CODER_GIT_SYNC_OPTIONS = Object.freeze({
72467
+ isCommitEnabled: false,
72468
+ isAutoPushEnabled: false,
72469
+ isAutoPullEnabled: false,
72470
+ });
72471
+ /**
72472
+ * Pulls the latest repository changes before a `ptbk coder` command changes the project.
72473
+ */
72474
+ async function $pullCoderChanges(options) {
72475
+ const { gitSync, projectPath = process.cwd() } = options;
72476
+ if (!gitSync.isAutoPullEnabled) {
72477
+ return;
72478
+ }
72479
+ console.info(colors__default["default"].gray('Pulling the latest changes from the remote repository...'));
72480
+ await pullLatestChanges(projectPath);
72481
+ }
72482
+ /**
72483
+ * Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
72484
+ *
72485
+ * Note: A repository without any change is left alone instead of creating an empty commit.
72486
+ */
72487
+ async function $commitCoderChanges(options) {
72488
+ const { gitSync, commitMessage, projectPath = process.cwd() } = options;
72489
+ if (!gitSync.isCommitEnabled) {
72490
+ return;
72491
+ }
72492
+ if (!(await hasChangesToCommit(projectPath))) {
72493
+ console.info(colors__default["default"].gray('Nothing to commit, the working tree is clean'));
72494
+ return;
72495
+ }
72496
+ await commitChanges(commitMessage, {
72497
+ projectPath,
72498
+ autoPush: gitSync.isAutoPushEnabled,
72499
+ });
72500
+ console.info(colors__default["default"].green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
72501
+ }
72502
+ /**
72503
+ * Checks whether the repository holds any change which can be committed.
72504
+ */
72505
+ async function hasChangesToCommit(projectPath) {
72506
+ const gitStatus = await runGitCommand({
72507
+ command: 'git status --porcelain',
72508
+ cwd: projectPath,
72509
+ isVerbose: false,
72510
+ });
72511
+ return gitStatus.trim() !== '';
72512
+ }
72513
+
72514
+ var coderGitSync = /*#__PURE__*/Object.freeze({
72515
+ __proto__: null,
72516
+ DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
72517
+ $pullCoderChanges: $pullCoderChanges,
72518
+ $commitCoderChanges: $commitCoderChanges
72519
+ });
72520
+
72221
72521
  /**
72222
72522
  * Calculates the next available prompt numbering sequence for a month.
72223
72523
  */
@@ -72692,6 +72992,31 @@
72692
72992
  }
72693
72993
  // Note: [🟡] Code for repository script [normalizeRefactorCandidatePath](scripts/find-refactor-candidates/normalizeRefactorCandidatePath.ts) should never be published outside of `@promptbook/cli`
72694
72994
 
72995
+ /**
72996
+ * The TypeScript compiler API once it was loaded by `analyzeSourceFileForRefactorCandidate`
72997
+ *
72998
+ * Note: [🐌] `typescript` is a heavy package, it is loaded on demand so that it does not slow down every single run
72999
+ * of the `ptbk` CLI utility
73000
+ *
73001
+ * @private variable of analyzeSourceFileForRefactorCandidate
73002
+ */
73003
+ let loadedTypescriptModule = null;
73004
+ /**
73005
+ * Returns the TypeScript compiler API which was already loaded for the structural analysis.
73006
+ *
73007
+ * @private function of analyzeSourceFileForRefactorCandidate
73008
+ */
73009
+ function getLoadedTypescriptModule() {
73010
+ if (loadedTypescriptModule === null) {
73011
+ throw new UnexpectedError(spaceTrim(`
73012
+ The \`typescript\` module was not loaded yet.
73013
+
73014
+ Structural analysis helpers must be called only from \`analyzeSourceFileForRefactorCandidate\` which
73015
+ loads \`typescript\` lazily.
73016
+ `));
73017
+ }
73018
+ return loadedTypescriptModule;
73019
+ }
72695
73020
  /**
72696
73021
  * Resolves whether a source file should produce a refactor candidate entry.
72697
73022
  *
@@ -72717,6 +73042,7 @@
72717
73042
  }
72718
73043
  }
72719
73044
  if (STRUCTURAL_ANALYSIS_EXTENSIONS.includes(extension)) {
73045
+ loadedTypescriptModule !== null && loadedTypescriptModule !== void 0 ? loadedTypescriptModule : (loadedTypescriptModule = await getTypescriptModule());
72720
73046
  const structureSummary = summarizeSourceFileStructure(content, extension, filePath);
72721
73047
  if (structureSummary.entityCount > heuristics.maxEntityCountPerFile) {
72722
73048
  reasons.push(`entities ${structureSummary.entityCount}/${heuristics.maxEntityCountPerFile}`);
@@ -72776,8 +73102,9 @@
72776
73102
  * @private function of analyzeSourceFileForRefactorCandidate
72777
73103
  */
72778
73104
  function summarizeSourceFileStructure(content, extension, filePath) {
73105
+ const ts = getLoadedTypescriptModule();
72779
73106
  const scriptKind = getScriptKindForExtension(extension);
72780
- const sourceFile = ts__namespace.createSourceFile(filePath, content, ts__namespace.ScriptTarget.Latest, true, scriptKind);
73107
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKind);
72781
73108
  return {
72782
73109
  entityCount: countEntitiesInSourceFile(sourceFile),
72783
73110
  ...summarizeFunctionsInSourceFile(sourceFile),
@@ -72789,25 +73116,26 @@
72789
73116
  * @private function of analyzeSourceFileForRefactorCandidate
72790
73117
  */
72791
73118
  function countEntitiesInSourceFile(sourceFile) {
73119
+ const ts = getLoadedTypescriptModule();
72792
73120
  let count = 0;
72793
73121
  // Only count top-level declarations to avoid inflating with members or nested scopes.
72794
73122
  for (const statement of sourceFile.statements) {
72795
- if (ts__namespace.isFunctionDeclaration(statement) ||
72796
- ts__namespace.isClassDeclaration(statement) ||
72797
- ts__namespace.isInterfaceDeclaration(statement) ||
72798
- ts__namespace.isTypeAliasDeclaration(statement) ||
72799
- ts__namespace.isEnumDeclaration(statement) ||
72800
- ts__namespace.isModuleDeclaration(statement)) {
73123
+ if (ts.isFunctionDeclaration(statement) ||
73124
+ ts.isClassDeclaration(statement) ||
73125
+ ts.isInterfaceDeclaration(statement) ||
73126
+ ts.isTypeAliasDeclaration(statement) ||
73127
+ ts.isEnumDeclaration(statement) ||
73128
+ ts.isModuleDeclaration(statement)) {
72801
73129
  count += 1;
72802
73130
  continue;
72803
73131
  }
72804
- if (ts__namespace.isVariableStatement(statement)) {
73132
+ if (ts.isVariableStatement(statement)) {
72805
73133
  for (const declaration of statement.declarationList.declarations) {
72806
73134
  const initializer = declaration.initializer;
72807
73135
  if (initializer &&
72808
- (ts__namespace.isArrowFunction(initializer) ||
72809
- ts__namespace.isFunctionExpression(initializer) ||
72810
- ts__namespace.isClassExpression(initializer))) {
73136
+ (ts.isArrowFunction(initializer) ||
73137
+ ts.isFunctionExpression(initializer) ||
73138
+ ts.isClassExpression(initializer))) {
72811
73139
  count += 1;
72812
73140
  }
72813
73141
  }
@@ -72821,6 +73149,7 @@
72821
73149
  * @private function of analyzeSourceFileForRefactorCandidate
72822
73150
  */
72823
73151
  function summarizeFunctionsInSourceFile(sourceFile) {
73152
+ const ts = getLoadedTypescriptModule();
72824
73153
  let functionCount = 0;
72825
73154
  let maxFunctionComplexity = 0;
72826
73155
  let mostComplexFunctionName = null;
@@ -72833,7 +73162,7 @@
72833
73162
  mostComplexFunctionName = getFunctionDisplayName(node);
72834
73163
  }
72835
73164
  }
72836
- ts__namespace.forEachChild(node, visitNode);
73165
+ ts.forEachChild(node, visitNode);
72837
73166
  };
72838
73167
  visitNode(sourceFile);
72839
73168
  return {
@@ -72848,14 +73177,15 @@
72848
73177
  * @private function of analyzeSourceFileForRefactorCandidate
72849
73178
  */
72850
73179
  function isCountedFunctionLikeDeclaration(node) {
72851
- if (ts__namespace.isFunctionDeclaration(node) ||
72852
- ts__namespace.isMethodDeclaration(node) ||
72853
- ts__namespace.isConstructorDeclaration(node) ||
72854
- ts__namespace.isGetAccessorDeclaration(node) ||
72855
- ts__namespace.isSetAccessorDeclaration(node)) {
73180
+ const ts = getLoadedTypescriptModule();
73181
+ if (ts.isFunctionDeclaration(node) ||
73182
+ ts.isMethodDeclaration(node) ||
73183
+ ts.isConstructorDeclaration(node) ||
73184
+ ts.isGetAccessorDeclaration(node) ||
73185
+ ts.isSetAccessorDeclaration(node)) {
72856
73186
  return true;
72857
73187
  }
72858
- if (ts__namespace.isArrowFunction(node) || ts__namespace.isFunctionExpression(node)) {
73188
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
72859
73189
  return isNamedFunctionExpression(node);
72860
73190
  }
72861
73191
  return false;
@@ -72866,8 +73196,9 @@
72866
73196
  * @private function of analyzeSourceFileForRefactorCandidate
72867
73197
  */
72868
73198
  function isNamedFunctionExpression(node) {
73199
+ const ts = getLoadedTypescriptModule();
72869
73200
  const parent = node.parent;
72870
- return (ts__namespace.isVariableDeclaration(parent) || ts__namespace.isPropertyDeclaration(parent) || ts__namespace.isPropertyAssignment(parent));
73201
+ return (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent) || ts.isPropertyAssignment(parent));
72871
73202
  }
72872
73203
  /**
72873
73204
  * Calculates a lightweight cyclomatic-complexity score for one function.
@@ -72878,6 +73209,7 @@
72878
73209
  if (!functionNode.body) {
72879
73210
  return 1;
72880
73211
  }
73212
+ const ts = getLoadedTypescriptModule();
72881
73213
  let complexity = 1;
72882
73214
  const visitNode = (node) => {
72883
73215
  if (node !== functionNode.body && isCountedFunctionLikeDeclaration(node)) {
@@ -72886,7 +73218,7 @@
72886
73218
  if (isComplexityDecisionNode(node)) {
72887
73219
  complexity += 1;
72888
73220
  }
72889
- ts__namespace.forEachChild(node, visitNode);
73221
+ ts.forEachChild(node, visitNode);
72890
73222
  };
72891
73223
  visitNode(functionNode.body);
72892
73224
  return complexity;
@@ -72897,22 +73229,23 @@
72897
73229
  * @private function of analyzeSourceFileForRefactorCandidate
72898
73230
  */
72899
73231
  function isComplexityDecisionNode(node) {
72900
- if (ts__namespace.isIfStatement(node) ||
72901
- ts__namespace.isConditionalExpression(node) ||
72902
- ts__namespace.isCatchClause(node) ||
72903
- ts__namespace.isForStatement(node) ||
72904
- ts__namespace.isForInStatement(node) ||
72905
- ts__namespace.isForOfStatement(node) ||
72906
- ts__namespace.isWhileStatement(node) ||
72907
- ts__namespace.isDoStatement(node) ||
72908
- ts__namespace.isCaseClause(node)) {
73232
+ const ts = getLoadedTypescriptModule();
73233
+ if (ts.isIfStatement(node) ||
73234
+ ts.isConditionalExpression(node) ||
73235
+ ts.isCatchClause(node) ||
73236
+ ts.isForStatement(node) ||
73237
+ ts.isForInStatement(node) ||
73238
+ ts.isForOfStatement(node) ||
73239
+ ts.isWhileStatement(node) ||
73240
+ ts.isDoStatement(node) ||
73241
+ ts.isCaseClause(node)) {
72909
73242
  return true;
72910
73243
  }
72911
- if (ts__namespace.isBinaryExpression(node)) {
73244
+ if (ts.isBinaryExpression(node)) {
72912
73245
  const operatorKind = node.operatorToken.kind;
72913
- return (operatorKind === ts__namespace.SyntaxKind.AmpersandAmpersandToken ||
72914
- operatorKind === ts__namespace.SyntaxKind.BarBarToken ||
72915
- operatorKind === ts__namespace.SyntaxKind.QuestionQuestionToken);
73246
+ return (operatorKind === ts.SyntaxKind.AmpersandAmpersandToken ||
73247
+ operatorKind === ts.SyntaxKind.BarBarToken ||
73248
+ operatorKind === ts.SyntaxKind.QuestionQuestionToken);
72916
73249
  }
72917
73250
  return false;
72918
73251
  }
@@ -72922,27 +73255,28 @@
72922
73255
  * @private function of analyzeSourceFileForRefactorCandidate
72923
73256
  */
72924
73257
  function getFunctionDisplayName(functionNode) {
72925
- if (ts__namespace.isConstructorDeclaration(functionNode)) {
73258
+ const ts = getLoadedTypescriptModule();
73259
+ if (ts.isConstructorDeclaration(functionNode)) {
72926
73260
  return 'constructor';
72927
73261
  }
72928
- if (ts__namespace.isFunctionDeclaration(functionNode) ||
72929
- ts__namespace.isMethodDeclaration(functionNode) ||
72930
- ts__namespace.isGetAccessorDeclaration(functionNode) ||
72931
- ts__namespace.isSetAccessorDeclaration(functionNode)) {
73262
+ if (ts.isFunctionDeclaration(functionNode) ||
73263
+ ts.isMethodDeclaration(functionNode) ||
73264
+ ts.isGetAccessorDeclaration(functionNode) ||
73265
+ ts.isSetAccessorDeclaration(functionNode)) {
72932
73266
  if (!functionNode.name) {
72933
73267
  return null;
72934
73268
  }
72935
73269
  return getPropertyNameText(functionNode.name);
72936
73270
  }
72937
- if (ts__namespace.isArrowFunction(functionNode) || ts__namespace.isFunctionExpression(functionNode)) {
73271
+ if (ts.isArrowFunction(functionNode) || ts.isFunctionExpression(functionNode)) {
72938
73272
  if (functionNode.name) {
72939
73273
  return functionNode.name.text;
72940
73274
  }
72941
73275
  const parent = functionNode.parent;
72942
- if (ts__namespace.isVariableDeclaration(parent)) {
73276
+ if (ts.isVariableDeclaration(parent)) {
72943
73277
  return getBindingNameText(parent.name);
72944
73278
  }
72945
- if (ts__namespace.isPropertyDeclaration(parent) || ts__namespace.isPropertyAssignment(parent)) {
73279
+ if (ts.isPropertyDeclaration(parent) || ts.isPropertyAssignment(parent)) {
72946
73280
  return getPropertyNameText(parent.name);
72947
73281
  }
72948
73282
  }
@@ -72954,7 +73288,8 @@
72954
73288
  * @private function of analyzeSourceFileForRefactorCandidate
72955
73289
  */
72956
73290
  function getBindingNameText(name) {
72957
- return ts__namespace.isIdentifier(name) ? name.text : null;
73291
+ const ts = getLoadedTypescriptModule();
73292
+ return ts.isIdentifier(name) ? name.text : null;
72958
73293
  }
72959
73294
  /**
72960
73295
  * Resolves text for a property name while preserving computed names when necessary.
@@ -72962,7 +73297,8 @@
72962
73297
  * @private function of analyzeSourceFileForRefactorCandidate
72963
73298
  */
72964
73299
  function getPropertyNameText(name) {
72965
- if (ts__namespace.isIdentifier(name) || ts__namespace.isPrivateIdentifier(name) || ts__namespace.isStringLiteral(name) || ts__namespace.isNumericLiteral(name)) {
73300
+ const ts = getLoadedTypescriptModule();
73301
+ if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
72966
73302
  return name.text;
72967
73303
  }
72968
73304
  return name.getText();
@@ -72984,16 +73320,17 @@
72984
73320
  * @private function of analyzeSourceFileForRefactorCandidate
72985
73321
  */
72986
73322
  function getScriptKindForExtension(extension) {
73323
+ const ts = getLoadedTypescriptModule();
72987
73324
  if (extension === '.tsx') {
72988
- return ts__namespace.ScriptKind.TSX;
73325
+ return ts.ScriptKind.TSX;
72989
73326
  }
72990
73327
  if (extension === '.jsx') {
72991
- return ts__namespace.ScriptKind.JSX;
73328
+ return ts.ScriptKind.JSX;
72992
73329
  }
72993
73330
  if (extension === '.js') {
72994
- return ts__namespace.ScriptKind.JS;
73331
+ return ts.ScriptKind.JS;
72995
73332
  }
72996
- return ts__namespace.ScriptKind.TS;
73333
+ return ts.ScriptKind.TS;
72997
73334
  }
72998
73335
  /**
72999
73336
  * Normalizes an absolute path for consistent comparisons.
@@ -73593,6 +73930,41 @@
73593
73930
  isPromptInPriorityFilter(prompt.section, priorityFilter));
73594
73931
  }
73595
73932
 
73933
+ /**
73934
+ * Checks whether a prompt is unrestricted or matches the selected harness/model.
73935
+ *
73936
+ * A prompt status line can contain one or more backtick-delimited model or harness
73937
+ * names. Matching is intentionally based on normalized substrings so a token such as
73938
+ * `gpt` selects any `gpt-*` model and `opus` selects a `claude-opus-*` model.
73939
+ */
73940
+ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
73941
+ if (promptRunnerIdentity === undefined) {
73942
+ return true;
73943
+ }
73944
+ const statusLine = section.statusLineIndex === undefined ? undefined : file.lines[section.statusLineIndex];
73945
+ const requiredRunnerTokens = statusLine === undefined ? [] : extractPromptRunnerTokens(statusLine);
73946
+ if (requiredRunnerTokens.length === 0) {
73947
+ return true;
73948
+ }
73949
+ const normalizedRunnerNames = [promptRunnerIdentity.harnessName, promptRunnerIdentity.modelName]
73950
+ .filter((name) => name !== undefined && name.trim() !== '')
73951
+ .map((name) => normalizeToKebabCase(name))
73952
+ .filter((name) => name !== '');
73953
+ return requiredRunnerTokens.some((requiredRunnerToken) => {
73954
+ const normalizedRequiredRunnerToken = normalizeToKebabCase(requiredRunnerToken);
73955
+ return (normalizedRequiredRunnerToken !== '' &&
73956
+ normalizedRunnerNames.some((normalizedRunnerName) => normalizedRunnerName.includes(normalizedRequiredRunnerToken)));
73957
+ });
73958
+ }
73959
+ /**
73960
+ * Extracts model and harness tokens from a prompt status line.
73961
+ */
73962
+ function extractPromptRunnerTokens(statusLine) {
73963
+ return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
73964
+ .map((match) => { var _a, _b; return (_b = (_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''; })
73965
+ .filter((token) => token !== '');
73966
+ }
73967
+
73596
73968
  /**
73597
73969
  * Parses a prompt markdown file into sections and metadata.
73598
73970
  */
@@ -73640,11 +74012,11 @@
73640
74012
  };
73641
74013
  }
73642
74014
  /**
73643
- * Parses a status line like "[ ] !!" or "[-]" or "[x] ~$0.65 21 minutes..." into status and priority.
74015
+ * Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
73644
74016
  * For [x] done and [!] failed prompts, allow metadata after the status marker.
73645
74017
  */
73646
74018
  function parseStatusLine(line) {
73647
- var _a, _b, _c, _d, _e;
74019
+ var _a, _b, _c, _d;
73648
74020
  // For done prompts [x], allow any content after (for cost/time metadata)
73649
74021
  const doneMatch = line.match(/^\[(?<status>[xX])\]/);
73650
74022
  if (doneMatch) {
@@ -73655,21 +74027,23 @@
73655
74027
  if (failedMatch) {
73656
74028
  return { status: 'failed', priority: 0 };
73657
74029
  }
73658
- // For todo [ ] and not-ready [-], require clean end with optional priority markers
73659
- const match = line.match(/^\[(?<status>[ -])\]\s*(?<priority>!*)\s*$/);
73660
- if (!match) {
73661
- return undefined;
74030
+ // For not-ready [-], keep the historical clean-line syntax.
74031
+ if (/^\[-\]\s*!*\s*$/u.test(line)) {
74032
+ return { status: 'not-ready', priority: 0 };
73662
74033
  }
73663
- const statusChar = (_b = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.status) === null || _b === void 0 ? void 0 : _b.toLowerCase();
73664
- let status;
73665
- if (statusChar === '-') {
73666
- status = 'not-ready';
74034
+ // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
74035
+ // before or after those tokens. Other trailing text remains an invalid status line.
74036
+ const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
74037
+ if (!todoMatch) {
74038
+ return undefined;
73667
74039
  }
73668
- else {
73669
- status = 'todo';
74040
+ const details = (_b = (_a = todoMatch.groups) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : '';
74041
+ const isPriorityOnly = /^[!\s]*$/u.test(details);
74042
+ const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
74043
+ if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
74044
+ return undefined;
73670
74045
  }
73671
- const priority = status === 'todo' ? (_e = (_d = (_c = match.groups) === null || _c === void 0 ? void 0 : _c.priority) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0 : 0;
73672
- return { status, priority };
74046
+ return { status: 'todo', priority: (_d = (_c = details.match(/!/gu)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0 };
73673
74047
  }
73674
74048
  /**
73675
74049
  * Finds the first non-empty line index between two bounds.
@@ -73805,6 +74179,243 @@
73805
74179
  findUnwrittenPrompts: findUnwrittenPrompts
73806
74180
  });
73807
74181
 
74182
+ /**
74183
+ * Builds a normalized temporary shell script path for prompt runners.
74184
+ */
74185
+ function buildTemporaryPromptScriptPath(options) {
74186
+ const sourceFileName = path.basename(options.sourceFileName);
74187
+ const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
74188
+ return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
74189
+ }
74190
+
74191
+ /**
74192
+ * Marker the pinged harness is asked to prefix its answer with, so the reply can be recognized
74193
+ * in the raw runner output of every supported harness.
74194
+ *
74195
+ * Note: The marker must stay free of regular-expression metacharacters, because
74196
+ * `extractCoderPingAnswer` builds its pattern from it.
74197
+ */
74198
+ const CODER_PING_ANSWER_MARKER = 'PTBK-CODER-PING-ANSWER';
74199
+ /**
74200
+ * First factor of the dummy multiplication the pinged harness is asked to compute.
74201
+ */
74202
+ const CODER_PING_FIRST_FACTOR = 6;
74203
+ /**
74204
+ * Second factor of the dummy multiplication the pinged harness is asked to compute.
74205
+ */
74206
+ const CODER_PING_SECOND_FACTOR = 7;
74207
+ /**
74208
+ * Answer a working harness and model returns for the dummy work of `ptbk coder ping`.
74209
+ */
74210
+ const CODER_PING_EXPECTED_ANSWER = String(CODER_PING_FIRST_FACTOR * CODER_PING_SECOND_FACTOR);
74211
+ /**
74212
+ * Builds the dummy prompt sent by `ptbk coder ping`.
74213
+ *
74214
+ * The work is intentionally the smallest possible one that still reaches the model: it spends a
74215
+ * negligible amount of the harness quota, it needs no tool and it explicitly forbids touching the
74216
+ * project, so a ping leaves the project exactly as it was.
74217
+ */
74218
+ function buildCoderPingPrompt() {
74219
+ return spaceTrim(`
74220
+ # Promptbook connection check
74221
+
74222
+ This is an automated \`ptbk coder ping\` connection check, not a coding task.
74223
+
74224
+ Do exactly this and nothing else:
74225
+
74226
+ 1. Multiply \`${CODER_PING_FIRST_FACTOR}\` by \`${CODER_PING_SECOND_FACTOR}\`.
74227
+ 2. Answer with one single line \`${CODER_PING_ANSWER_MARKER}: <result>\` where \`<result>\` is the number you computed.
74228
+
74229
+ Rules:
74230
+
74231
+ - Do not read, create, change, move or delete any file.
74232
+ - Do not run any command and do not use any tool.
74233
+ - Do not write anything except the single answer line.
74234
+ `);
74235
+ }
74236
+
74237
+ /**
74238
+ * Pattern matching one answer line produced by the pinged harness.
74239
+ *
74240
+ * The captured answer deliberately stops at a quote, a backslash or a line break, so an answer
74241
+ * embedded in a JSON event stream — as produced by Claude Code, Opencode or Codex `--json` — is
74242
+ * captured without the surrounding JSON.
74243
+ */
74244
+ const CODER_PING_ANSWER_PATTERN = new RegExp(`${CODER_PING_ANSWER_MARKER}\\s*:[ \\t]*([^\\r\\n"\\\\]*)`, 'gu');
74245
+ /**
74246
+ * Extracts the answer of a pinged harness from the runtime log of its runner shell.
74247
+ *
74248
+ * Only the raw output of the last execution is searched, so the answer marker contained in the
74249
+ * prompt of the raw input is never mistaken for the answer of the harness.
74250
+ *
74251
+ * @returns The answer of the harness, or `null` when the harness produced no recognizable answer
74252
+ */
74253
+ function extractCoderPingAnswer(runtimeLog) {
74254
+ var _a;
74255
+ const rawOutput = runtimeLog.split(SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER).pop();
74256
+ if (rawOutput === undefined) {
74257
+ return null;
74258
+ }
74259
+ // Note: The last answer wins because harnesses which stream partial messages repeat the growing answer line
74260
+ const answers = Array.from(rawOutput.matchAll(CODER_PING_ANSWER_PATTERN))
74261
+ .map((match) => (match[1] || '').trim())
74262
+ .filter((answer) => answer !== '');
74263
+ return (_a = answers[answers.length - 1]) !== null && _a !== void 0 ? _a : null;
74264
+ }
74265
+
74266
+ /**
74267
+ * Temporary subdirectory used for the `ptbk coder ping` runner shell script and its runtime log.
74268
+ */
74269
+ const CODER_PING_SCRIPT_DIRECTORY_NAME = 'coder-ping';
74270
+ /**
74271
+ * Base name of the temporary `ptbk coder ping` runner shell script.
74272
+ */
74273
+ const CODER_PING_SCRIPT_SOURCE_NAME = 'ping';
74274
+ /**
74275
+ * Sends one tiny dummy prompt through the selected harness and model and measures the round trip.
74276
+ *
74277
+ * The ping reuses the very same runner the coding queue uses, so it really exercises the configured
74278
+ * harness, model, thinking level and authentication — including the retry behavior on rate limits.
74279
+ * Both temporary artifacts it creates are removed again, so the project is left as it was.
74280
+ */
74281
+ async function pingCoderHarness(options) {
74282
+ const projectPath = options.projectPath || process.cwd();
74283
+ const { runner, runnerMetadata } = resolvePromptRunner(options);
74284
+ const scriptPath = buildTemporaryPromptScriptPath({
74285
+ projectPath,
74286
+ scriptDirectoryName: CODER_PING_SCRIPT_DIRECTORY_NAME,
74287
+ sourceFileName: CODER_PING_SCRIPT_SOURCE_NAME,
74288
+ });
74289
+ const startedTimeMs = Date.now();
74290
+ const { answer, usage, loginMethod } = await withPromptRuntimeLog(scriptPath, async (logPath) => {
74291
+ var _a;
74292
+ const result = await runner.runPrompt({
74293
+ prompt: buildCoderPingPrompt(),
74294
+ scriptPath,
74295
+ projectPath,
74296
+ logPath,
74297
+ shouldPrintLiveOutput: (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : false,
74298
+ preserveArtifactsOnSuccess: false,
74299
+ });
74300
+ return { ...result, answer: extractCoderPingAnswer(await readRuntimeLog(logPath)) };
74301
+ }, { preserveArtifactsOnSuccess: false });
74302
+ return {
74303
+ runnerName: runnerMetadata.runnerName,
74304
+ modelName: runnerMetadata.modelName,
74305
+ thinkingLevel: options.thinkingLevel,
74306
+ answer,
74307
+ isAnswerCorrect: answer === CODER_PING_EXPECTED_ANSWER,
74308
+ durationMs: Date.now() - startedTimeMs,
74309
+ usage,
74310
+ loginMethod,
74311
+ };
74312
+ }
74313
+ /**
74314
+ * Reads the runtime log of the finished ping, treating an unreadable log as no output at all.
74315
+ */
74316
+ async function readRuntimeLog(logPath) {
74317
+ return await promises.readFile(logPath, 'utf-8').catch(() => '');
74318
+ }
74319
+
74320
+ var pingCoderHarness$1 = /*#__PURE__*/Object.freeze({
74321
+ __proto__: null,
74322
+ pingCoderHarness: pingCoderHarness
74323
+ });
74324
+
74325
+ /**
74326
+ * Formats usage price for display in prompt status lines and task details.
74327
+ * Examples:
74328
+ * - "$0.12" (certain)
74329
+ * - "~$3.05" (uncertain)
74330
+ * - "$0.00" (zero cost)
74331
+ * - "<$0.01" (tiny non-zero cost)
74332
+ *
74333
+ * @private internal utility of the prompt runners and the Agents Server task details
74334
+ */
74335
+ function formatUsagePrice(usage) {
74336
+ const price = usage.price.value;
74337
+ const isUncertain = usage.price.isUncertain === true;
74338
+ const prefix = isUncertain ? '~' : '';
74339
+ if (price === 0) {
74340
+ return `${prefix}$0.00`;
74341
+ }
74342
+ if (price < 0.01) {
74343
+ return `${prefix}<$0.01`;
74344
+ }
74345
+ if (price < 1) {
74346
+ return `${prefix}$${price.toFixed(4)}`;
74347
+ }
74348
+ return `${prefix}$${price.toFixed(2)}`;
74349
+ }
74350
+
74351
+ /**
74352
+ * Formats runner details for prompt status lines.
74353
+ */
74354
+ function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
74355
+ const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
74356
+ const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
74357
+ const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
74358
+ if (!normalizedRunner && !normalizedModel) {
74359
+ return 'unknown';
74360
+ }
74361
+ const runnerLabel = normalizedRunner || 'unknown';
74362
+ if (!normalizedModel) {
74363
+ return `${runnerLabel}${thinkingLevelSuffix}`;
74364
+ }
74365
+ return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
74366
+ }
74367
+
74368
+ /**
74369
+ * Prints the compact summary of one finished `ptbk coder ping`.
74370
+ */
74371
+ function printCoderPingResult(result) {
74372
+ const runnerSignature = formatRunnerSignature(result.runnerName, result.modelName, result.thinkingLevel);
74373
+ const loginMethodLabel = formatCodexLoginMethod(result.loginMethod);
74374
+ const loginMethodSuffix = loginMethodLabel === undefined ? '' : ` (${loginMethodLabel})`;
74375
+ console.info(colors__default["default"].green(`🏓 ${runnerSignature}${loginMethodSuffix} answered in ${formatCoderPingResponseTime(result.durationMs)}`));
74376
+ console.info(colors__default["default"].gray(` Answer: ${formatCoderPingAnswer(result)}`));
74377
+ console.info(colors__default["default"].gray(` Usage: ${formatCoderPingUsage(result.usage)}`));
74378
+ }
74379
+ /**
74380
+ * Formats the measured round-trip time, keeping the sub-second precision a response time needs.
74381
+ */
74382
+ function formatCoderPingResponseTime(durationMs) {
74383
+ return `${(durationMs / 1000).toFixed(2)}s`;
74384
+ }
74385
+ /**
74386
+ * Formats the answer of the pinged harness together with what was expected from it.
74387
+ */
74388
+ function formatCoderPingAnswer(result) {
74389
+ if (result.answer === null) {
74390
+ return `Reached, but the answer line was missing from the output (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74391
+ }
74392
+ if (result.isAnswerCorrect) {
74393
+ return result.answer;
74394
+ }
74395
+ return `${result.answer} (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74396
+ }
74397
+ /**
74398
+ * Formats the resources the pinged harness reported for the dummy work.
74399
+ */
74400
+ function formatCoderPingUsage(usage) {
74401
+ return [
74402
+ formatUsagePrice(usage),
74403
+ `${formatUncertainCount(usage.input.tokensCount)} input tokens`,
74404
+ `${formatUncertainCount(usage.output.tokensCount)} output tokens`,
74405
+ ].join(', ');
74406
+ }
74407
+ /**
74408
+ * Formats one counted usage value, marking an estimated count with a leading `~`.
74409
+ */
74410
+ function formatUncertainCount(count) {
74411
+ return `${count.isUncertain === true ? '~' : ''}${Math.round(count.value)}`;
74412
+ }
74413
+
74414
+ var printCoderPingResult$1 = /*#__PURE__*/Object.freeze({
74415
+ __proto__: null,
74416
+ printCoderPingResult: printCoderPingResult
74417
+ });
74418
+
73808
74419
  /**
73809
74420
  * Default wait duration applied before retrying a failed prompt round.
73810
74421
  */
@@ -73812,7 +74423,7 @@
73812
74423
  /**
73813
74424
  * CLI usage text for this script.
73814
74425
  */
73815
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--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] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
74426
+ 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] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
73816
74427
  /**
73817
74428
  * Top-level flags supported by this command.
73818
74429
  */
@@ -73822,6 +74433,7 @@
73822
74433
  '--model',
73823
74434
  '--context',
73824
74435
  '--test',
74436
+ '--test-before',
73825
74437
  '--preserve-logs',
73826
74438
  '--isolate',
73827
74439
  '--no-ui',
@@ -73860,6 +74472,8 @@
73860
74472
  const context = readOptionValue(args, '--context');
73861
74473
  const hasTestCommandFlag = args.includes('--test');
73862
74474
  const testCommand = readVariadicOptionValue(args, '--test');
74475
+ const hasTestBeforeFlag = args.includes('--test-before');
74476
+ const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
73863
74477
  const preserveLogs = args.includes('--preserve-logs');
73864
74478
  const isIsolated = args.includes('--isolate');
73865
74479
  const noUi = args.includes('--no-ui');
@@ -73932,6 +74546,7 @@
73932
74546
  model,
73933
74547
  context,
73934
74548
  testCommand,
74549
+ testBefore,
73935
74550
  thinkingLevel,
73936
74551
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
73937
74552
  minimumPriority,
@@ -73940,6 +74555,21 @@
73940
74555
  limit,
73941
74556
  };
73942
74557
  }
74558
+ /**
74559
+ * Parses and validates the optional pre-coding verification mode.
74560
+ */
74561
+ function parseTestBeforeOption(value, hasTestBeforeFlag) {
74562
+ if (value === undefined) {
74563
+ if (hasTestBeforeFlag) {
74564
+ exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74565
+ }
74566
+ return 'no';
74567
+ }
74568
+ if (!isTestBeforeMode(value)) {
74569
+ exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74570
+ }
74571
+ return value;
74572
+ }
73943
74573
  /**
73944
74574
  * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
73945
74575
  */
@@ -75698,6 +76328,14 @@
75698
76328
  .filter((statement) => statement !== '');
75699
76329
  }
75700
76330
 
76331
+ /**
76332
+ * Loads the PostgreSQL client (`pg`) on demand
76333
+ *
76334
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
76335
+ *
76336
+ * @private function of runAutoMigrateTestingServers
76337
+ */
76338
+ const loadPostgresModule = createLazyModuleLoader(() => import('pg'));
75701
76339
  /**
75702
76340
  * Migration targets for testing servers that should be migrated by coding-script auto-migration.
75703
76341
  */
@@ -75775,7 +76413,8 @@
75775
76413
  * @returns Pending migration files grouped by prefix.
75776
76414
  */
75777
76415
  async function listPendingMigrationsByPrefix(options) {
75778
- const client = new pg.Client({
76416
+ const { Client } = await loadPostgresModule();
76417
+ const client = new Client({
75779
76418
  connectionString: options.connectionString,
75780
76419
  ssl: { rejectUnauthorized: false },
75781
76420
  });
@@ -75932,15 +76571,6 @@
75932
76571
  return lines.join(file.eol);
75933
76572
  }
75934
76573
 
75935
- /**
75936
- * Builds a normalized temporary shell script path for prompt runners.
75937
- */
75938
- function buildTemporaryPromptScriptPath(options) {
75939
- const sourceFileName = path.basename(options.sourceFileName);
75940
- const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
75941
- return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
75942
- }
75943
-
75944
76574
  /**
75945
76575
  * Builds the suffix which disambiguates one prompt section inside its prompt file.
75946
76576
  *
@@ -75967,32 +76597,6 @@
75967
76597
  });
75968
76598
  }
75969
76599
 
75970
- /**
75971
- * Formats usage price for display in prompt status lines and task details.
75972
- * Examples:
75973
- * - "$0.12" (certain)
75974
- * - "~$3.05" (uncertain)
75975
- * - "$0.00" (zero cost)
75976
- * - "<$0.01" (tiny non-zero cost)
75977
- *
75978
- * @private internal utility of the prompt runners and the Agents Server task details
75979
- */
75980
- function formatUsagePrice(usage) {
75981
- const price = usage.price.value;
75982
- const isUncertain = usage.price.isUncertain === true;
75983
- const prefix = isUncertain ? '~' : '';
75984
- if (price === 0) {
75985
- return `${prefix}$0.00`;
75986
- }
75987
- if (price < 0.01) {
75988
- return `${prefix}<$0.01`;
75989
- }
75990
- if (price < 1) {
75991
- return `${prefix}$${price.toFixed(4)}`;
75992
- }
75993
- return `${prefix}$${price.toFixed(2)}`;
75994
- }
75995
-
75996
76600
  /**
75997
76601
  * Human-readable labels for each coder run step kind shown in prompt status lines.
75998
76602
  */
@@ -76038,20 +76642,13 @@
76038
76642
  }
76039
76643
 
76040
76644
  /**
76041
- * Formats runner details for prompt status lines.
76645
+ * Replaces the complete todo status line while preserving its indentation.
76646
+ *
76647
+ * The complete line is replaced because a todo status can contain a required
76648
+ * model/harness token in addition to priority markers.
76042
76649
  */
76043
- function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
76044
- const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
76045
- const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
76046
- const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
76047
- if (!normalizedRunner && !normalizedModel) {
76048
- return 'unknown';
76049
- }
76050
- const runnerLabel = normalizedRunner || 'unknown';
76051
- if (!normalizedModel) {
76052
- return `${runnerLabel}${thinkingLevelSuffix}`;
76053
- }
76054
- return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
76650
+ function replacePromptTodoStatusLine(line, replacementStatusLine) {
76651
+ return line.replace(/^(?<indentation>\s*)\[\s*\].*$/u, `$<indentation>${replacementStatusLine}`);
76055
76652
  }
76056
76653
 
76057
76654
  /**
@@ -76071,8 +76668,8 @@
76071
76668
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
76072
76669
  const stepsSummary = formatCoderRunSteps(steps);
76073
76670
  const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
76074
- // Replace "[ ]" or "[ ] !!..." with "[x] by runner model thinking level (login method) - Step $price duration; ..."
76075
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76671
+ // Replace the complete todo status, including any required model/harness token.
76672
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76076
76673
  }
76077
76674
 
76078
76675
  /**
@@ -76090,8 +76687,10 @@
76090
76687
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
76091
76688
  const duration = moment__default["default"]().diff(promptExecutionStartedDate);
76092
76689
  const durationString = moment__default["default"].duration(duration).humanize();
76093
- const failureDetails = attemptMetadata === '' ? `failed after ${durationString} by ${runnerSignature}` : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76094
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[!] ${failureDetails}`);
76690
+ const failureDetails = attemptMetadata === ''
76691
+ ? `failed after ${durationString} by ${runnerSignature}`
76692
+ : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76693
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[!] ${failureDetails}`);
76095
76694
  }
76096
76695
 
76097
76696
  /**
@@ -77061,17 +77660,18 @@
77061
77660
  /**
77062
77661
  * Lists todo prompts that are ready to run (no authoring placeholders).
77063
77662
  */
77064
- function listRunnablePrompts(files, priorityFilter = {}) {
77663
+ function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
77065
77664
  return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
77066
- isPromptInPriorityFilter(prompt.section, priorityFilter));
77665
+ isPromptInPriorityFilter(prompt.section, priorityFilter) &&
77666
+ isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
77067
77667
  }
77068
77668
 
77069
77669
  /**
77070
77670
  * Selects the next runnable prompt based on priority.
77071
77671
  */
77072
- function findNextTodoPrompt(files, priorityFilter = {}) {
77672
+ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
77073
77673
  let nextPrompt;
77074
- for (const prompt of listRunnablePrompts(files, priorityFilter)) {
77674
+ for (const prompt of listRunnablePrompts(files, priorityFilter, promptRunnerIdentity)) {
77075
77675
  if (!nextPrompt || prompt.section.priority > nextPrompt.section.priority) {
77076
77676
  nextPrompt = prompt;
77077
77677
  }
@@ -77082,8 +77682,8 @@
77082
77682
  /**
77083
77683
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
77084
77684
  */
77085
- function listUpcomingTasks(files, priorityFilter = {}) {
77086
- return listRunnablePrompts(files, priorityFilter).map(({ file, section }) => ({
77685
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
77686
+ return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
77087
77687
  label: buildPromptLabelForDisplay(file, section),
77088
77688
  summary: buildPromptSummary(file, section),
77089
77689
  priority: section.priority,
@@ -77227,6 +77827,71 @@
77227
77827
  }
77228
77828
  }
77229
77829
 
77830
+ /**
77831
+ * Creates the one queue prompt used to repair a pre-existing test failure.
77832
+ */
77833
+ async function createTestBeforeRepairPrompt(options) {
77834
+ const description = spaceTrim((block) => `
77835
+ Fix the existing test failures before implementing any queued coding tasks.
77836
+
77837
+ The verification command \`${options.testCommand}\` failed before coding started. Fix the underlying failure without weakening or removing the tests, and leave the project ready for the remaining coding prompts.
77838
+
77839
+ ## Verification output
77840
+
77841
+ \`\`\`
77842
+ ${block(limitTestOutput(options.testOutput))}
77843
+ \`\`\`
77844
+ `);
77845
+ const createdPrompt = await addCoderPrompt({
77846
+ projectPath: options.projectPath,
77847
+ description,
77848
+ priority: 0,
77849
+ });
77850
+ const promptPath = path.join(options.projectPath, createdPrompt.filePath);
77851
+ const promptFile = parsePromptFile(promptPath, await promises.readFile(promptPath, 'utf-8'));
77852
+ const section = promptFile.sections[0];
77853
+ if (!section) {
77854
+ throw new UnexpectedError(spaceTrim(`
77855
+ The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
77856
+ `));
77857
+ }
77858
+ return { file: promptFile, section };
77859
+ }
77860
+
77861
+ /**
77862
+ * Runs the configured verification command before the first coding prompt.
77863
+ */
77864
+ async function runTestBefore(options) {
77865
+ var _a, _b;
77866
+ const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
77867
+ const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
77868
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
77869
+ checkpointLabel: 'running initial tests before the agent coding starts',
77870
+ phase: 'verifying',
77871
+ statusMessage: initialTestStatusMessage,
77872
+ }));
77873
+ console.info(colors__default["default"].gray(initialTestStatusMessage));
77874
+ try {
77875
+ const testOutput = await runPromptTestCommandExecutor({
77876
+ command: options.testCommand,
77877
+ projectPath: options.projectPath,
77878
+ scriptPath: buildTemporaryPromptScriptPath({
77879
+ projectPath: options.projectPath,
77880
+ scriptDirectoryName: 'coder-prompts',
77881
+ sourceFileName: 'test-before',
77882
+ }),
77883
+ });
77884
+ console.info(colors__default["default"].green('Pre-coding tests passed.'));
77885
+ return { isPassed: true, testOutput };
77886
+ }
77887
+ catch (error) {
77888
+ const testOutput = formatUnknownErrorMessage(error);
77889
+ console.error(colors__default["default"].red('Pre-coding tests failed.'));
77890
+ console.error(testOutput);
77891
+ return { isPassed: false, testOutput };
77892
+ }
77893
+ }
77894
+
77230
77895
  /**
77231
77896
  * Constant for prompts dir.
77232
77897
  */
@@ -77254,6 +77919,10 @@
77254
77919
  return;
77255
77920
  }
77256
77921
  const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
77922
+ const promptRunnerIdentity = {
77923
+ harnessName: options.agentName,
77924
+ modelName: actualRunnerModel,
77925
+ };
77257
77926
  console.info(colors__default["default"].green(`Running prompts with ${runner.name}`));
77258
77927
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
77259
77928
  await initializeRunUiAgentVisual(uiHandle, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentSource);
@@ -77268,6 +77937,7 @@
77268
77937
  let previousRoundStartTime;
77269
77938
  let previousRoundEndTime;
77270
77939
  let completedRunCount = 0;
77940
+ let hasRunTestBefore = false;
77271
77941
  while (just(true)) {
77272
77942
  if (options.autoPull && !options.dryRun) {
77273
77943
  await waitForRequestedPause({
@@ -77280,6 +77950,35 @@
77280
77950
  options,
77281
77951
  isRichUiEnabled,
77282
77952
  });
77953
+ if (!hasRunTestBefore && options.testBefore !== 'no') {
77954
+ await waitForRequestedPause({
77955
+ checkpointLabel: 'loading prompts before running initial tests',
77956
+ phase: 'loading',
77957
+ statusMessage: 'Loading prompts before running initial tests...',
77958
+ });
77959
+ await loadPromptQueueSnapshot({
77960
+ options,
77961
+ isRichUiEnabled,
77962
+ progressDisplay,
77963
+ uiHandle,
77964
+ promptRunnerIdentity,
77965
+ });
77966
+ }
77967
+ if (!hasRunTestBefore) {
77968
+ hasWaitedForStart = await runTestBeforeIfNeeded({
77969
+ options,
77970
+ runner,
77971
+ runnerMetadata,
77972
+ resolvedCoderContext,
77973
+ resolvedAgentSystemMessage,
77974
+ isRichUiEnabled,
77975
+ progressDisplay,
77976
+ uiHandle,
77977
+ waitForRequestedPause,
77978
+ hasWaitedForStart,
77979
+ });
77980
+ hasRunTestBefore = true;
77981
+ }
77283
77982
  await waitForRequestedPause({
77284
77983
  checkpointLabel: 'loading prompts',
77285
77984
  phase: 'loading',
@@ -77290,6 +77989,7 @@
77290
77989
  isRichUiEnabled,
77291
77990
  progressDisplay,
77292
77991
  uiHandle,
77992
+ promptRunnerIdentity,
77293
77993
  });
77294
77994
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
77295
77995
  hasShownUpcomingTasks,
@@ -77297,6 +77997,7 @@
77297
77997
  stats: promptQueueSnapshot.stats,
77298
77998
  priorityFilter: options.priorityFilter,
77299
77999
  isRichUiEnabled,
78000
+ promptRunnerIdentity,
77300
78001
  }));
77301
78002
  if (!promptQueueSnapshot.nextPrompt) {
77302
78003
  if (isEndAfterCurrentPromptRequested(completedRunCount)) {
@@ -77399,6 +78100,14 @@
77399
78100
  * Validates cross-flag constraints before the run starts.
77400
78101
  */
77401
78102
  function validateRunCodexPromptOptions(options) {
78103
+ var _a;
78104
+ if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
78105
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
78106
+ Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
78107
+
78108
+ Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
78109
+ `));
78110
+ }
77402
78111
  if (options.allowDestructiveAutoMigrate && !options.autoMigrate) {
77403
78112
  throw new DatabaseError(_spaceTrim.spaceTrim(`
77404
78113
  Flag \`--allow-destructive-auto-migrate\` requires \`--auto-migrate\`.
@@ -77469,20 +78178,103 @@
77469
78178
  * Normalizes legacy and current priority options into one validated run option shape.
77470
78179
  */
77471
78180
  function normalizeRunOptions(options) {
77472
- var _a, _b, _c, _d, _e;
78181
+ var _a, _b, _c, _d, _e, _f, _g;
78182
+ const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
78183
+ const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
77473
78184
  const priorityFilter = normalizePriorityFilter({
77474
78185
  priority: options.priority,
77475
- minimumPriority: (_a = options.minimumPriority) !== null && _a !== void 0 ? _a : (_b = options.priorityFilter) === null || _b === void 0 ? void 0 : _b.minimumPriority,
77476
- maximumPriority: (_c = options.maximumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.maximumPriority,
78186
+ minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
78187
+ maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
77477
78188
  });
77478
78189
  return {
77479
78190
  ...options,
77480
- priority: (_e = priorityFilter.minimumPriority) !== null && _e !== void 0 ? _e : 0,
78191
+ testBefore,
78192
+ testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
78193
+ priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
77481
78194
  minimumPriority: priorityFilter.minimumPriority,
77482
78195
  maximumPriority: priorityFilter.maximumPriority,
77483
78196
  priorityFilter,
77484
78197
  };
77485
78198
  }
78199
+ /**
78200
+ * Runs the optional pre-coding verification and, when requested, its one repair prompt.
78201
+ */
78202
+ async function runTestBeforeIfNeeded(options) {
78203
+ const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, } = options;
78204
+ if (runOptions.testBefore === 'no') {
78205
+ return hasWaitedForStart;
78206
+ }
78207
+ if (!runOptions.testCommand) {
78208
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
78209
+ ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
78210
+
78211
+ Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
78212
+ `));
78213
+ }
78214
+ if (!runOptions.ignoreGitChanges) {
78215
+ await waitForRequestedPause({
78216
+ checkpointLabel: 'checking the git working tree before testing',
78217
+ phase: 'loading',
78218
+ statusMessage: 'Checking the working tree before testing...',
78219
+ });
78220
+ await ensureWorkingTreeClean();
78221
+ }
78222
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
78223
+ const testBeforeResult = await runTestBefore({
78224
+ testCommand: runOptions.testCommand,
78225
+ projectPath: process.cwd(),
78226
+ waitForPauseCheckpoint: waitForRequestedPause,
78227
+ }).finally(() => {
78228
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
78229
+ });
78230
+ if (testBeforeResult.isPassed) {
78231
+ return hasWaitedForStart;
78232
+ }
78233
+ const testOutput = limitTestOutput(testBeforeResult.testOutput);
78234
+ if (runOptions.testBefore === 'yes-and-fail') {
78235
+ throw new NotAllowed(_spaceTrim.spaceTrim((block) => `
78236
+ Pre-coding verification command \`${runOptions.testCommand}\` failed.
78237
+
78238
+ The coding agent was not started because the project was already failing before the first queued prompt.
78239
+
78240
+ ### Test results
78241
+ ${'```'}
78242
+ ${block(testOutput)}
78243
+ ${'```'}
78244
+ `));
78245
+ }
78246
+ const repairPrompt = await createTestBeforeRepairPrompt({
78247
+ projectPath: process.cwd(),
78248
+ testCommand: runOptions.testCommand,
78249
+ testOutput,
78250
+ });
78251
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
78252
+ const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
78253
+ options: runOptions,
78254
+ nextPrompt: repairPrompt,
78255
+ promptLabel: repairPromptLabel,
78256
+ hasWaitedForStart,
78257
+ isRichUiEnabled,
78258
+ progressDisplay,
78259
+ uiHandle,
78260
+ });
78261
+ // The repair prompt is created in the current worktree so it can be recorded and committed with the repair.
78262
+ // It therefore intentionally uses the regular round here even when the queue itself uses --isolate.
78263
+ await runPromptRound({
78264
+ options: runOptions,
78265
+ runner,
78266
+ runnerMetadata,
78267
+ nextPrompt: repairPrompt,
78268
+ promptLabel: repairPromptLabel,
78269
+ resolvedCoderContext,
78270
+ resolvedAgentSystemMessage,
78271
+ isRichUiEnabled,
78272
+ progressDisplay,
78273
+ uiHandle,
78274
+ waitForRequestedPause,
78275
+ });
78276
+ return updatedHasWaitedForStart;
78277
+ }
77486
78278
  /**
77487
78279
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
77488
78280
  */
@@ -77567,7 +78359,7 @@
77567
78359
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
77568
78360
  */
77569
78361
  async function loadPromptQueueSnapshot(options) {
77570
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
78362
+ const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity } = options;
77571
78363
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
77572
78364
  const promptFiles = await loadPromptFiles(PROMPTS_DIR$1);
77573
78365
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
@@ -77579,14 +78371,14 @@
77579
78371
  return {
77580
78372
  promptFiles,
77581
78373
  stats,
77582
- nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter),
78374
+ nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter, promptRunnerIdentity),
77583
78375
  };
77584
78376
  }
77585
78377
  /**
77586
78378
  * Prints upcoming tasks only on the first loop iteration in plain-console mode.
77587
78379
  */
77588
78380
  function showUpcomingTasksOnce(options) {
77589
- const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled } = options;
78381
+ const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled, promptRunnerIdentity } = options;
77590
78382
  if (hasShownUpcomingTasks || isRichUiEnabled) {
77591
78383
  return true;
77592
78384
  }
@@ -77595,7 +78387,7 @@
77595
78387
  printPromptsToBeWritten(promptFiles, priorityFilter);
77596
78388
  console.info('');
77597
78389
  }
77598
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter));
78390
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
77599
78391
  return true;
77600
78392
  }
77601
78393
  /**
@@ -77605,7 +78397,10 @@
77605
78397
  if (promptQueueSnapshot.nextPrompt) {
77606
78398
  return false;
77607
78399
  }
77608
- if (promptQueueSnapshot.stats.toBeWritten > 0) {
78400
+ if (promptQueueSnapshot.stats.forAgent > 0) {
78401
+ announceRunCompletion('No prompts match the selected harness or model.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
78402
+ }
78403
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
77609
78404
  announceRunCompletion('No prompts ready for agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
77610
78405
  }
77611
78406
  else {
@@ -77646,9 +78441,16 @@
77646
78441
  * Updates the UI status message while waiting for new prompts in keepAlive server mode.
77647
78442
  */
77648
78443
  function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
77649
- const message = promptQueueSnapshot.stats.toBeWritten > 0
77650
- ? 'No prompts ready for agent. Watching for changes...'
77651
- : 'All prompts are done. Watching for changes...';
78444
+ let message;
78445
+ if (promptQueueSnapshot.stats.forAgent > 0) {
78446
+ message = 'No prompts match the selected harness or model. Watching for changes...';
78447
+ }
78448
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
78449
+ message = 'No prompts ready for agent. Watching for changes...';
78450
+ }
78451
+ else {
78452
+ message = 'All prompts are done. Watching for changes...';
78453
+ }
77652
78454
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
77653
78455
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
77654
78456
  if (!isRichUiEnabled) {
@@ -77670,7 +78472,7 @@
77670
78472
  * Waits for the optional user confirmation before starting the selected prompt.
77671
78473
  */
77672
78474
  async function waitForPromptConfirmationIfNeeded(options) {
77673
- const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle } = options;
78475
+ const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle, } = options;
77674
78476
  if (!runOptions.waitForUser) {
77675
78477
  return hasWaitedForStart;
77676
78478
  }
@@ -78992,14 +79794,21 @@
78992
79794
  let promptFiles = initialFiles;
78993
79795
  const skippedFiles = new Set();
78994
79796
  while (true) {
79797
+ // Note: The git synchronization is applied around each single verification, not once per whole run
79798
+ await $pullCoderChanges({ gitSync: normalizedOptions.gitSync });
79799
+ if (normalizedOptions.gitSync.isAutoPullEnabled) {
79800
+ // Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
79801
+ promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79802
+ }
78995
79803
  displayPromptOverview(promptFiles);
78996
79804
  // First priority: verify files where all prompts are marked as done
78997
79805
  const fileWithAllDone = findFileWithAllDonePrompts(promptFiles, skippedFiles);
78998
79806
  if (fileWithAllDone) {
78999
- const wasSkipped = await verifyDonePromptsInFile(fileWithAllDone);
79000
- if (wasSkipped) {
79807
+ const outcome = await verifyDonePromptsInFile(fileWithAllDone);
79808
+ if (outcome.wasSkipped) {
79001
79809
  skippedFiles.add(fileWithAllDone.path);
79002
79810
  }
79811
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
79003
79812
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79004
79813
  continue;
79005
79814
  }
@@ -79009,10 +79818,20 @@
79009
79818
  console.info(colors__default["default"].green('\n✅ All prompts have been verified.'));
79010
79819
  break;
79011
79820
  }
79012
- await resolvePrompt(nextPrompt);
79821
+ const outcome = await resolvePrompt(nextPrompt);
79822
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
79013
79823
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79014
79824
  }
79015
79825
  }
79826
+ /**
79827
+ * Commits and pushes one applied verification when the git synchronization is enabled.
79828
+ */
79829
+ async function $commitVerificationOutcome(gitSync, outcome) {
79830
+ if (outcome.commitMessage === null) {
79831
+ return;
79832
+ }
79833
+ await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
79834
+ }
79016
79835
  /**
79017
79836
  * Parses supported command-line arguments for the standalone verification script.
79018
79837
  */
@@ -79020,6 +79839,11 @@
79020
79839
  return {
79021
79840
  reverse: args.includes('--reverse'),
79022
79841
  ignore: readRepeatableStringOption(args, '--ignore'),
79842
+ gitSync: {
79843
+ isCommitEnabled: args.includes('--commit'),
79844
+ isAutoPushEnabled: args.includes('--auto-push'),
79845
+ isAutoPullEnabled: args.includes('--auto-pull'),
79846
+ },
79023
79847
  };
79024
79848
  }
79025
79849
  /**
@@ -79071,10 +79895,11 @@
79071
79895
  * Normalizes verification options so the rest of the flow can assume stable defaults.
79072
79896
  */
79073
79897
  function normalizeVerifyPromptsOptions(options) {
79074
- var _a, _b;
79898
+ var _a, _b, _c;
79075
79899
  return {
79076
79900
  reverse: (_a = options.reverse) !== null && _a !== void 0 ? _a : false,
79077
79901
  ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
79902
+ gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
79078
79903
  };
79079
79904
  }
79080
79905
  /**
@@ -79205,7 +80030,6 @@
79205
80030
  /**
79206
80031
  * Verifies the last done [x] prompt in a file and decides whether to archive it or add a repair prompt.
79207
80032
  * Ignores not-ready prompts like [-], [.], [?], etc.
79208
- * Returns true if the file was skipped, false otherwise.
79209
80033
  */
79210
80034
  async function verifyDonePromptsInFile(file) {
79211
80035
  const doneCount = file.sections.filter((s) => s.status === 'done').length;
@@ -79225,32 +80049,45 @@
79225
80049
  }
79226
80050
  if (!lastDoneSection) {
79227
80051
  console.info(colors__default["default"].gray('No done [x] prompts found in this file.'));
79228
- return false;
80052
+ return { wasSkipped: false, commitMessage: null };
79229
80053
  }
79230
80054
  console.info(colors__default["default"].gray('Verifying the last [x] prompt in the file...\n'));
79231
80055
  displayPromptSnippet({ file, section: lastDoneSection });
79232
80056
  const decision = await promptForDoneVerification(file, lastDoneSection);
79233
80057
  if (decision === 'done') {
79234
80058
  await archivePromptFile(file);
79235
- return false;
80059
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(file) };
79236
80060
  }
79237
80061
  else if (decision === 'needs-work') {
79238
80062
  console.info(colors__default["default"].yellow('\n⚠️ This prompt needs repair.'));
79239
80063
  await appendRepairPrompt(file, lastDoneSection);
79240
- return false;
80064
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(file) };
79241
80065
  }
79242
80066
  else {
79243
80067
  console.info(colors__default["default"].gray('\n⏩ Skipped, no changes made.'));
79244
- return true;
80068
+ return { wasSkipped: true, commitMessage: null };
79245
80069
  }
79246
80070
  }
80071
+ /**
80072
+ * Builds the commit message describing one archived prompt file.
80073
+ */
80074
+ function buildArchiveCommitMessage(file) {
80075
+ return `✅ Prompt done and archived \`${file.name}\``; // <- $commitCoderChanges({
80076
+ }
80077
+ /**
80078
+ * Builds the commit message describing one appended repair prompt.
80079
+ */
80080
+ function buildRepairCommitMessage(file) {
80081
+ return `❌ Repair prompt added into \`${file.name}\``; // <- $commitCoderChanges({
80082
+ }
79247
80083
  /**
79248
80084
  * Asks the user to verify if a done prompt is actually completed.
79249
80085
  * Returns 'done' if verified, 'needs-work' if not done, or 'skip' to skip this file.
79250
80086
  */
79251
80087
  async function promptForDoneVerification(file, section) {
79252
80088
  const promptLabel = buildPromptLabelForDisplay(file, section);
79253
- const response = await prompts__default["default"]({
80089
+ const { default: prompts } = await loadPromptsModule();
80090
+ const response = await prompts({
79254
80091
  type: 'select',
79255
80092
  name: 'verified',
79256
80093
  message: `Is ${colors__default["default"].bold(promptLabel)} actually done?`,
@@ -79318,17 +80155,18 @@
79318
80155
  const decision = await promptForDecision(selection);
79319
80156
  if (decision === 'done') {
79320
80157
  await archivePromptFile(selection.file);
80158
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(selection.file) };
79321
80159
  }
79322
- else {
79323
- await appendRepairPrompt(selection.file, selection.section);
79324
- }
80160
+ await appendRepairPrompt(selection.file, selection.section);
80161
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(selection.file) };
79325
80162
  }
79326
80163
  /**
79327
80164
  * Presents the interactive decision menu for the current prompt section.
79328
80165
  */
79329
80166
  async function promptForDecision(selection) {
79330
80167
  const promptLabel = buildPromptLabelForDisplay(selection.file, selection.section);
79331
- const response = await prompts__default["default"]({
80168
+ const { default: prompts } = await loadPromptsModule();
80169
+ const response = await prompts({
79332
80170
  type: 'select',
79333
80171
  name: 'decision',
79334
80172
  message: `Is ${colors__default["default"].bold(promptLabel)} resolved?`,