@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/esm/index.es.js CHANGED
@@ -9,16 +9,11 @@ import moment from 'moment';
9
9
  import * as fs from 'fs';
10
10
  import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
11
11
  import * as dotenv from 'dotenv';
12
- import prompts from 'prompts';
13
12
  import * as readline from 'readline';
14
13
  import { emitKeypressEvents, clearLine, cursorTo, createInterface } from 'readline';
15
14
  import { forTime, forEver } from 'waitasecond';
16
15
  import hexEncoder from 'crypto-js/enc-hex';
17
16
  import sha256 from 'crypto-js/sha256';
18
- import { io } from 'socket.io-client';
19
- import JSZip from 'jszip';
20
- import { Readability } from '@mozilla/readability';
21
- import { JSDOM } from 'jsdom';
22
17
  import CryptoJS from 'crypto-js';
23
18
  import showdown from 'showdown';
24
19
  import glob from 'glob-promise';
@@ -29,19 +24,13 @@ import * as OpenApiValidator from 'express-openapi-validator';
29
24
  import swaggerUi from 'swagger-ui-express';
30
25
  import { createElement } from 'react';
31
26
  import { renderToStaticMarkup } from 'react-dom/server';
32
- import Anthropic from '@anthropic-ai/sdk';
33
27
  import Bottleneck from 'bottleneck';
34
- import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
35
28
  import { Subject, BehaviorSubject } from 'rxjs';
36
- import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
37
- import OpenAI from 'openai';
38
- import * as ts from 'typescript';
39
29
  import ignore from 'ignore';
40
30
  import { EventEmitter } from 'events';
41
31
  import { tmpdir } from 'os';
42
32
  import { lookup, extension } from 'mime-types';
43
33
  import papaparse from 'papaparse';
44
- import { Client } from 'pg';
45
34
  import '@supabase/supabase-js';
46
35
  import { pathToFileURL } from 'url';
47
36
 
@@ -59,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
59
48
  * @generated
60
49
  * @see https://github.com/webgptorg/promptbook
61
50
  */
62
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-1';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-3';
63
52
  /**
64
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
65
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3293,6 +3282,12 @@ const PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV = 'PTBK_AGENTS_SERVER_BUILD_WORK
3293
3282
  * @private internal constant of `buildAgentsServer`
3294
3283
  */
3295
3284
  const PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV = 'PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION';
3285
+ /**
3286
+ * Environment variable that disables throwaway webpack filesystem caches for CLI-owned production builds.
3287
+ *
3288
+ * @private internal constant of `buildAgentsServer`
3289
+ */
3290
+ const PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV = 'PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE';
3296
3291
  /**
3297
3292
  * Conservative Next.js build worker count used by CLI-owned Agents Server production builds.
3298
3293
  *
@@ -3317,6 +3312,11 @@ function createAgentsServerRuntimeEnvironment(environment, nodeModulesPath, opti
3317
3312
  [PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
3318
3313
  }
3319
3314
  : {}),
3315
+ ...(options.isWebpackFilesystemCacheDisabled
3316
+ ? {
3317
+ [PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV]: 'true',
3318
+ }
3319
+ : {}),
3320
3320
  };
3321
3321
  }
3322
3322
  /**
@@ -4050,6 +4050,7 @@ async function ensureAgentsServerBuild(options = {}) {
4050
4050
  });
4051
4051
  const buildEnvironment = createAgentsServerRuntimeEnvironment(environment, preparedRuntime.nodeModulesPath, {
4052
4052
  isNextValidationIgnored: preparedRuntime.isAppPathMaterialized,
4053
+ isWebpackFilesystemCacheDisabled: true,
4053
4054
  });
4054
4055
  if (!options.isBuildForced &&
4055
4056
  (await isAgentsServerBuildCacheCurrent({
@@ -26966,12 +26967,12 @@ function buildControlPills(options) {
26966
26967
  * Builds the coder-run control pills shown in the footer box.
26967
26968
  */
26968
26969
  function buildCoderRunControlPills(options) {
26969
- const { pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26970
+ const { phase, pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26970
26971
  return buildControlPills({
26971
26972
  pauseControl,
26972
26973
  pendingEnterLabel,
26973
26974
  additionalControls: [
26974
- buildSkipCurrentWaitControl(),
26975
+ ...(phase === 'waiting' ? [buildSkipCurrentWaitControl()] : []),
26975
26976
  buildEndAfterCurrentPromptControl(isEndAfterCurrentPromptRequested, sessionTotal),
26976
26977
  ],
26977
26978
  });
@@ -28226,6 +28227,13 @@ function toPosixPath(filePath) {
28226
28227
  * Environment variable read by the shell wrapper to tee live output into the temporary runtime log file.
28227
28228
  */
28228
28229
  const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
28230
+ /**
28231
+ * Log line which separates the raw script input from the raw script output of one execution section.
28232
+ *
28233
+ * Readers of a runtime log split on this marker to look only at what the harness really produced,
28234
+ * without the generated script and the prompt it embeds.
28235
+ */
28236
+ const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
28229
28237
  /**
28230
28238
  * Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
28231
28239
  */
@@ -28261,7 +28269,7 @@ async function appendScriptExecutionLogStart({ scriptPath, scriptContent, logPat
28261
28269
  --- raw input ---
28262
28270
  ${block(normalizedInput)}
28263
28271
 
28264
- --- raw output ---
28272
+ ${SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER}
28265
28273
  `);
28266
28274
  await appendFile(logPath, `${logSection}\n`, 'utf-8');
28267
28275
  }
@@ -29552,6 +29560,7 @@ function buildCodexScript(options) {
29552
29560
  ` -c model_reasoning_effort="${thinkingLevel}" \\`,
29553
29561
  ` --ask-for-approval ${options.askForApproval} \\`,
29554
29562
  ` exec --model ${options.model} \\`,
29563
+ ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
29555
29564
  ' --local-provider none \\',
29556
29565
  ` --sandbox ${options.sandbox} \\`,
29557
29566
  ` -C ${projectPath} \\`,
@@ -29868,6 +29877,10 @@ function matchesAnyPattern(text, patterns) {
29868
29877
  * Output line that marks finished Codex usage summary.
29869
29878
  */
29870
29879
  const CODEX_COMPLETION_LINE = /^\s*tokens used\b/i;
29880
+ /**
29881
+ * Output line that marks the completed Codex turn in JSONL output mode.
29882
+ */
29883
+ const CODEX_JSON_COMPLETION_LINE = /^\s*\{"type":"turn\.(?:completed|failed)"/u;
29871
29884
  /**
29872
29885
  * Idle timeout after completion marker to capture trailing output.
29873
29886
  */
@@ -29944,6 +29957,7 @@ class OpenAiCodexRunner {
29944
29957
  sandbox: this.options.sandbox,
29945
29958
  askForApproval: this.options.askForApproval,
29946
29959
  allowCredits: this.options.allowCredits,
29960
+ isMachineReadableProgressEnabled: this.options.isMachineReadableProgressEnabled,
29947
29961
  codexCommand: this.options.codexCommand,
29948
29962
  });
29949
29963
  for (let retryIndex = 0;; retryIndex++) {
@@ -29958,7 +29972,9 @@ class OpenAiCodexRunner {
29958
29972
  const output = await $runGoScriptUntilMarkerIdle({
29959
29973
  scriptPath: options.scriptPath,
29960
29974
  scriptContent,
29961
- completionLineMatcher: CODEX_COMPLETION_LINE,
29975
+ completionLineMatcher: this.options.isMachineReadableProgressEnabled
29976
+ ? CODEX_JSON_COMPLETION_LINE
29977
+ : CODEX_COMPLETION_LINE,
29962
29978
  idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
29963
29979
  logPath: options.logPath,
29964
29980
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
@@ -30208,6 +30224,7 @@ function createOpenAiCodexRunnerResolution(options) {
30208
30224
  sandbox: 'danger-full-access',
30209
30225
  askForApproval: 'never',
30210
30226
  allowCredits: options.allowCredits,
30227
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
30211
30228
  });
30212
30229
  if (!options.allowCredits && options.isVerbose === true) {
30213
30230
  console.info(colors.gray('OpenAI Codex credit spending is disabled. Use `--allow-credits` to explicitly opt in.'));
@@ -31443,6 +31460,7 @@ function buildCoderRunUiFrame(options) {
31443
31460
  : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
31444
31461
  const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
31445
31462
  const controls = buildCoderRunControlPills({
31463
+ phase: options.phase,
31446
31464
  pauseControl: pausePresentation.pauseControl,
31447
31465
  pendingEnterLabel: options.pendingEnterLabel,
31448
31466
  isEndAfterCurrentPromptRequested: options.isEndAfterCurrentPromptRequested,
@@ -32306,6 +32324,7 @@ function createCoderRunOptionsForAgent(options) {
32306
32324
  autoPull: options.autoPull,
32307
32325
  agentName: options.agentName,
32308
32326
  model: options.model,
32327
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
32309
32328
  priority: 0,
32310
32329
  };
32311
32330
  }
@@ -32927,8 +32946,12 @@ function readOptionalSigningKeyValue() {
32927
32946
  * `options.includePaths` can restrict staging, `options.onlyPaths` can restrict the commit pathspec,
32928
32947
  * `options.excludePaths` can keep temporary artifacts out of the created commit and
32929
32948
  * `options.isEmptyCommitAllowed` keeps a round without any file change from failing.
32949
+ *
32950
+ * Note: The temporary commit message file is written inside the project, so it is always excluded from the commit
32951
+ * itself for projects which do not keep the Promptbook temporary directory out of version control.
32930
32952
  */
32931
32953
  async function commitChanges(message, options) {
32954
+ var _a;
32932
32955
  const projectPath = (options === null || options === void 0 ? void 0 : options.projectPath) || process.cwd();
32933
32956
  const commitMessagePath = resolvePromptbookTemporaryPath(projectPath, 'ptbk-coder', 'commit-messages', `COMMIT_MESSAGE_${Date.now()}.txt`);
32934
32957
  await mkdir(dirname(commitMessagePath), { recursive: true });
@@ -32936,7 +32959,10 @@ async function commitChanges(message, options) {
32936
32959
  try {
32937
32960
  const agentEnv = buildAgentGitEnv();
32938
32961
  const signingFlag = buildAgentGitSigningFlag();
32939
- await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, options === null || options === void 0 ? void 0 : options.excludePaths);
32962
+ await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, [
32963
+ commitMessagePath,
32964
+ ...((_a = options === null || options === void 0 ? void 0 : options.excludePaths) !== null && _a !== void 0 ? _a : []),
32965
+ ]);
32940
32966
  await runGitCommand({
32941
32967
  command: buildGitCommitCommand({
32942
32968
  commitMessagePath,
@@ -33295,6 +33321,21 @@ function appendCoderContext(prompt, context) {
33295
33321
  `);
33296
33322
  }
33297
33323
 
33324
+ /**
33325
+ * Maximum amount of test output embedded into an agent prompt.
33326
+ */
33327
+ const MAX_TEST_OUTPUT_CHARS = 12000;
33328
+ /**
33329
+ * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
33330
+ */
33331
+ function limitTestOutput(testOutput) {
33332
+ const normalizedTestOutput = testOutput.trim();
33333
+ if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
33334
+ return normalizedTestOutput;
33335
+ }
33336
+ return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
33337
+ }
33338
+
33298
33339
  /**
33299
33340
  * Runs the configured verification command inside the project root and returns its output.
33300
33341
  */
@@ -33315,10 +33356,6 @@ async function runPromptTestCommand(options) {
33315
33356
  * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33316
33357
  */
33317
33358
  const MAX_PROMPT_TEST_ATTEMPTS = 3;
33318
- /**
33319
- * Maximum amount of verification output sent back to the coding agent as retry feedback.
33320
- */
33321
- const MAX_TEST_FEEDBACK_OUTPUT_CHARS = 12000;
33322
33359
  /**
33323
33360
  * File extension used by generated shell scripts.
33324
33361
  */
@@ -33364,7 +33401,7 @@ async function runPromptWithTestFeedback(options) {
33364
33401
  return { ...result, attemptCount, steps };
33365
33402
  }
33366
33403
  const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
33367
- const feedbackVerificationOutput = limitVerificationOutputForFeedback(fullVerificationOutput);
33404
+ const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
33368
33405
  if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
33369
33406
  console.error(colors.red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33370
33407
  throw new Error(buildFinalVerificationFailureMessage({
@@ -33494,19 +33531,6 @@ function buildFinalVerificationFailureMessage({ promptLabel, testCommand, attemp
33494
33531
  \`\`\`
33495
33532
  `);
33496
33533
  }
33497
- /**
33498
- * Limits verification output before it is embedded back into the next coding prompt.
33499
- */
33500
- function limitVerificationOutputForFeedback(verificationOutput) {
33501
- const normalizedVerificationOutput = verificationOutput.trim();
33502
- if (normalizedVerificationOutput.length <= MAX_TEST_FEEDBACK_OUTPUT_CHARS) {
33503
- return normalizedVerificationOutput;
33504
- }
33505
- return spaceTrim(`
33506
- [...verification output truncated to the last ${MAX_TEST_FEEDBACK_OUTPUT_CHARS} characters...]
33507
- ${normalizedVerificationOutput.slice(-MAX_TEST_FEEDBACK_OUTPUT_CHARS)}
33508
- `);
33509
- }
33510
33534
  /**
33511
33535
  * Derives a dedicated temp-script path for verification commands.
33512
33536
  */
@@ -41367,6 +41391,7 @@ function createLocalAgentRunOptions(options, localAgentRunnerLimits) {
41367
41391
  autoClone: false,
41368
41392
  maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
41369
41393
  maxParallelMessages: localAgentRunnerLimits.maxParallelMessages,
41394
+ isMachineReadableProgressEnabled: true,
41370
41395
  };
41371
41396
  }
41372
41397
 
@@ -42277,6 +42302,91 @@ function $initializeAgentsServerCommand(program) {
42277
42302
  // Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
42278
42303
  // Note: [💞] Ignore a discrepancy between file name and entity name
42279
42304
 
42305
+ /**
42306
+ * Creates a loader which imports one module on the first call and reuses the very same module afterwards
42307
+ *
42308
+ * Note: [🐌] Heavy third-party dependencies are imported lazily to keep the startup of the Promptbook CLI fast.
42309
+ * A statically imported dependency is loaded every single time the bundle is loaded, even when the running
42310
+ * command never touches it. A lazily imported dependency is loaded only when the feature is really used.
42311
+ *
42312
+ * @example
42313
+ * const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
42314
+ * const { JSDOM } = await loadJsdomModule();
42315
+ *
42316
+ * @private internal utility of Promptbook
42317
+ */
42318
+ function createLazyModuleLoader(importModule) {
42319
+ let importedModulePromise = null;
42320
+ return function loadModule() {
42321
+ if (importedModulePromise === null) {
42322
+ importedModulePromise = importModule();
42323
+ }
42324
+ return importedModulePromise;
42325
+ };
42326
+ }
42327
+ // Note: [🐌] Do not convert the lazy `import(...)` calls back to static `import` statements, it would bring back the
42328
+ // slow startup of the `ptbk` CLI utility
42329
+
42330
+ /**
42331
+ * Loads the interactive terminal prompt library (`prompts`) on demand
42332
+ *
42333
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast - most commands never ask the user
42334
+ * anything interactively
42335
+ *
42336
+ * @private internal utility of Promptbook CLI
42337
+ */
42338
+ const loadPromptsModule = createLazyModuleLoader(() => import('prompts'));
42339
+ // Note: [🟡] Code for CLI prompt loading [loadPromptsModule](src/cli/common/loadPromptsModule.ts) should never be published outside of `@promptbook/cli`
42340
+
42341
+ /**
42342
+ * Description block shared by the `ptbk coder` commands which can synchronize their changes with git.
42343
+ *
42344
+ * @private internal utility of `promptbookCli`
42345
+ */
42346
+ const CODER_GIT_SYNC_DESCRIPTION = spaceTrim$1(`
42347
+ Git synchronization:
42348
+ - --auto-pull pulls the latest changes before this command changes anything
42349
+ - --commit commits the changes made by this command
42350
+ - --auto-push pushes the created commit to the remote repository
42351
+ `);
42352
+ /**
42353
+ * Registers the shared `--commit`, `--auto-push` and `--auto-pull` flags on a `ptbk coder` command.
42354
+ *
42355
+ * Note: Unlike `ptbk coder run`, which commits by default and opts out through `--no-commit`,
42356
+ * these commands never touch git unless the flags are used explicitly.
42357
+ *
42358
+ * @private internal utility of `promptbookCli`
42359
+ */
42360
+ function addCoderGitSyncOptions(command) {
42361
+ command.option('--commit', 'Commit the changes made by this command with the coding-agent git identity', false);
42362
+ command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
42363
+ command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
42364
+ }
42365
+ /**
42366
+ * Converts the Commander git synchronization flags into normalized git synchronization options.
42367
+ *
42368
+ * @private internal utility of `promptbookCli`
42369
+ */
42370
+ function normalizeCoderGitSyncCliOptions(cliOptions) {
42371
+ if (cliOptions.autoPush && !cliOptions.commit) {
42372
+ throw new NotAllowed(spaceTrim$1(`
42373
+ Flag \`--auto-push\` can be used only together with \`--commit\`.
42374
+
42375
+ **There is nothing to push when the changes are not committed.**
42376
+
42377
+ Actionable hint:
42378
+ - Add \`--commit\`, for example \`ptbk coder init --commit --auto-push\`.
42379
+ `));
42380
+ }
42381
+ return {
42382
+ isCommitEnabled: cliOptions.commit,
42383
+ isAutoPushEnabled: cliOptions.autoPush,
42384
+ isAutoPullEnabled: cliOptions.autoPull,
42385
+ };
42386
+ }
42387
+ // Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
42388
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
42389
+
42280
42390
  /**
42281
42391
  * Relative path to the root prompts directory used by Promptbook coder utilities.
42282
42392
  *
@@ -42530,15 +42640,17 @@ const FALLBACK_PROMPT_SLUG = 'prompt';
42530
42640
  */
42531
42641
  function $initializeCoderAddCommand(program) {
42532
42642
  const command = program.command('add');
42533
- command.description(spaceTrim$1(`
42534
- Add one ready-to-run prompt file to the queue
42643
+ command.description(spaceTrim$1((block) => `
42644
+ Add one ready-to-run prompt file to the queue
42535
42645
 
42536
- Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42537
- - \`ptbk coder add "some new feature"\`
42538
- - \`ptbk coder add --priority 1 "some new feature"\`
42539
- - \`ptbk coder add <<EOF ... EOF\`
42540
- - \`ptbk coder add\`
42541
- `));
42646
+ Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42647
+ - \`ptbk coder add "some new feature"\`
42648
+ - \`ptbk coder add --priority 1 "some new feature"\`
42649
+ - \`ptbk coder add <<EOF ... EOF\`
42650
+ - \`ptbk coder add\`
42651
+
42652
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
42653
+ `));
42542
42654
  command.argument('[description]', 'Plain-language description of the feature or task to implement');
42543
42655
  command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
42544
42656
  command.option('--template <template>', spaceTrim$1(`
@@ -42548,15 +42660,26 @@ function $initializeCoderAddCommand(program) {
42548
42660
  .map(({ id }) => id)
42549
42661
  .join(', ')}) or a markdown file path relative to the current project root.
42550
42662
  `));
42663
+ addCoderGitSyncOptions(command);
42551
42664
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
42552
42665
  const { priority, template: templateOption } = cliOptions;
42666
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
42667
+ const projectPath = process.cwd();
42553
42668
  const description = await resolveCoderPromptDescription(descriptionArgument);
42554
- await addCoderPrompt({
42555
- projectPath: process.cwd(),
42669
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
42670
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
42671
+ await $pullCoderChanges({ gitSync, projectPath });
42672
+ const { /* filePath,*/ emojiTag } = await addCoderPrompt({
42673
+ projectPath,
42556
42674
  description,
42557
42675
  priority,
42558
42676
  templateOption,
42559
42677
  });
42678
+ await $commitCoderChanges({
42679
+ gitSync,
42680
+ projectPath,
42681
+ commitMessage: `${emojiTag} Add prompt`,
42682
+ });
42560
42683
  }));
42561
42684
  }
42562
42685
  /**
@@ -42630,6 +42753,7 @@ async function resolveCoderPromptDescription(descriptionArgument) {
42630
42753
  }
42631
42754
  return standardInputDescription;
42632
42755
  }
42756
+ const { default: prompts } = await loadPromptsModule();
42633
42757
  const response = await prompts({
42634
42758
  type: 'text',
42635
42759
  name: 'description',
@@ -43050,9 +43174,11 @@ function parseIntOption(value) {
43050
43174
  */
43051
43175
  function $initializeCoderGenerateBoilerplatesCommand(program) {
43052
43176
  const command = program.command('generate-boilerplates');
43053
- command.description(spaceTrim$1(`
43054
- Generate prompt boilerplate files with unique emoji tags
43055
- `));
43177
+ command.description(spaceTrim$1((block) => `
43178
+ Generate prompt boilerplate files with unique emoji tags
43179
+
43180
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
43181
+ `));
43056
43182
  command.option('--count <count>', `Number of prompt boilerplate files to generate`, '5');
43057
43183
  command.option('--template <template>', spaceTrim$1(`
43058
43184
  Prompt template to use.
@@ -43061,14 +43187,25 @@ function $initializeCoderGenerateBoilerplatesCommand(program) {
43061
43187
  .map(({ id }) => id)
43062
43188
  .join(', ')}) or a markdown file path relative to the current project root.
43063
43189
  `));
43190
+ addCoderGitSyncOptions(command);
43064
43191
  command.action(handleActionErrors(async (cliOptions) => {
43065
43192
  const { count: countOption, template: templateOption } = cliOptions;
43066
43193
  const filesCount = parseFilesCount(countOption);
43194
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
43195
+ const projectPath = process.cwd();
43196
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
43197
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
43198
+ await $pullCoderChanges({ gitSync, projectPath });
43067
43199
  await generatePromptBoilerplate({
43068
- projectPath: process.cwd(),
43200
+ projectPath,
43069
43201
  filesCount,
43070
43202
  templateOption,
43071
43203
  });
43204
+ await $commitCoderChanges({
43205
+ gitSync,
43206
+ projectPath,
43207
+ commitMessage: `Prompts ${filesCount}x`,
43208
+ });
43072
43209
  return process.exit(0);
43073
43210
  }));
43074
43211
  }
@@ -43481,6 +43618,27 @@ function resolveCheckedHarnessDefinitions(harnessNames) {
43481
43618
  }
43482
43619
  // Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
43483
43620
 
43621
+ /**
43622
+ * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43623
+ *
43624
+ * @private internal utility of `ptbk coder`
43625
+ */
43626
+ const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43627
+ /**
43628
+ * Returns the default coder `AGENT_CODING.md` quick-reference content.
43629
+ *
43630
+ * @private internal utility of `ptbk coder`
43631
+ */
43632
+ function getDefaultCoderAgentCodingFileContent() {
43633
+ return spaceTrim$1(`
43634
+ # ✨ Promptbook Coder agent coding
43635
+
43636
+ This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
43637
+ `);
43638
+ }
43639
+ // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43640
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
43641
+
43484
43642
  /**
43485
43643
  * Relative path to the shared coder context file initialized in project roots.
43486
43644
  *
@@ -43508,106 +43666,6 @@ function getDefaultCoderAgentsFileContent() {
43508
43666
  // Note: [🟡] Code for coder AGENTS file boilerplate [agentsFile](src/cli/cli-commands/coder/agentsFile.ts) should never be published outside of `@promptbook/cli`
43509
43667
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
43510
43668
 
43511
- /**
43512
- * Normalizes one project-relative path for human-readable CLI output and markdown.
43513
- *
43514
- * @private internal utility of `ptbk coder`
43515
- */
43516
- function formatDisplayPath(relativePath) {
43517
- return relativePath.replace(/\\/gu, '/');
43518
- }
43519
- // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43520
-
43521
- /**
43522
- * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43523
- *
43524
- * @private internal utility of `ptbk coder`
43525
- */
43526
- const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43527
- /**
43528
- * Returns the default coder `AGENT_CODING.md` quick-reference content.
43529
- *
43530
- * @private internal utility of `ptbk coder`
43531
- */
43532
- function getDefaultCoderAgentCodingFileContent({ packageJsonScripts, }) {
43533
- return spaceTrim$1((block) => `
43534
- # Promptbook Coder quick reference
43535
-
43536
- 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)}/\`.
43537
-
43538
- ## Workflow
43539
- 1. Put repository-wide coding rules into \`${AGENTS_FILE_PATH}\`. The default \`npm run coder:run\` script already passes \`--context ${AGENTS_FILE_PATH}\`.
43540
- 2. Create or customize prompt templates in \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/\`. ${buildStarterTemplateSentence()}
43541
- 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"\`.
43542
- 4. Replace every \`@@@\`, keep drafts as \`[-]\`, and switch prompts to \`[ ]\` when they are ready to run. Completed prompts are marked \`[x]\`.
43543
- 5. Run \`npm run coder:run\` to execute the next ready prompt with the configured coding agent.
43544
- 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.
43545
- 7. Use \`ptbk coder find-refactor-candidates\` when you want Promptbook to suggest refactor prompts automatically.
43546
-
43547
- ## Templates
43548
- - Project-owned templates created by \`ptbk coder init\`: ${formatInlineCodeList(getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath)))}
43549
- - Built-in \`--template\` aliases: ${formatInlineCodeList(getDefaultCoderPromptTemplateDefinitions().map(({ id }) => id))}
43550
- - To add a custom template, create a markdown file such as \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43551
- - To use a project template, run \`ptbk coder generate-boilerplates --template ${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43552
- - 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.
43553
-
43554
- ## Created npm scripts
43555
- | Script | Purpose |
43556
- | --- | --- |
43557
- ${block(buildPackageJsonScriptTableLines(packageJsonScripts).join('\n'))}
43558
-
43559
- ## Customizing the workflow
43560
- - Edit \`package.json\` if you want \`npm run coder:run\` to use another coding agent, model, thinking level, context file, or wait mode.
43561
- - 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\`.
43562
- - Use \`ptbk coder --help\` and \`ptbk coder <command> --help\` for the full CLI reference.
43563
- `);
43564
- }
43565
- /**
43566
- * Builds the sentence describing the starter templates created during initialization.
43567
- */
43568
- function buildStarterTemplateSentence() {
43569
- const starterTemplatePaths = getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath));
43570
- if (starterTemplatePaths.length === 1) {
43571
- return `The starter project template created by \`ptbk coder init\` is \`${starterTemplatePaths[0]}\`.`;
43572
- }
43573
- return `The starter project templates created by \`ptbk coder init\` are ${formatInlineCodeList(starterTemplatePaths)}.`;
43574
- }
43575
- /**
43576
- * Builds the markdown table rows describing the initialized npm scripts.
43577
- */
43578
- function buildPackageJsonScriptTableLines(packageJsonScripts) {
43579
- return Object.entries(packageJsonScripts).map(([scriptName, scriptCommand]) => `| \`npm run ${scriptName}\` | ${describeDefaultCoderPackageJsonScript(scriptName, scriptCommand)} |`);
43580
- }
43581
- /**
43582
- * Describes one initialized npm script in human-readable terms.
43583
- */
43584
- function describeDefaultCoderPackageJsonScript(scriptName, scriptCommand) {
43585
- if (scriptName === 'coder:generate-boilerplates') {
43586
- return `Runs \`${scriptCommand}\` to create new prompt files in \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\`.`;
43587
- }
43588
- if (scriptName === 'coder:add') {
43589
- return `Runs \`${scriptCommand}\` to add one ready-to-run prompt file to \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\` from a plain-language description.`;
43590
- }
43591
- if (scriptName === 'coder:run') {
43592
- return `Runs \`${scriptCommand}\` to execute the next ready prompt with shared repository context from \`${AGENTS_FILE_PATH}\`.`;
43593
- }
43594
- if (scriptName === 'coder:find-refactor-candidates') {
43595
- return `Runs \`${scriptCommand}\` to generate prompt candidates for large or crowded files.`;
43596
- }
43597
- if (scriptName === 'coder:verify') {
43598
- return `Runs \`${scriptCommand}\` to archive verified prompts into \`${formatDisplayPath(PROMPTS_DONE_DIRECTORY_PATH)}/\` and append repair prompts when needed.`;
43599
- }
43600
- return `Runs \`${scriptCommand}\`.`;
43601
- }
43602
- /**
43603
- * Formats one inline code list for human-readable markdown.
43604
- */
43605
- function formatInlineCodeList(values) {
43606
- return values.map((value) => `\`${value}\``).join(', ');
43607
- }
43608
- // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43609
- // Note: [💞] Ignore a discrepancy between file name and exported helper names
43610
-
43611
43669
  /**
43612
43670
  * Relative directory path for agents initialized by `ptbk coder init`.
43613
43671
  *
@@ -43672,6 +43730,16 @@ async function isExistingFile$2(path) {
43672
43730
  }
43673
43731
  // Note: [🟡] Code for coder init developer agent bootstrapping [ensureCoderDeveloperAgentFile](src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts) should never be published outside of `@promptbook/cli`
43674
43732
 
43733
+ /**
43734
+ * Normalizes one project-relative path for human-readable CLI output and markdown.
43735
+ *
43736
+ * @private internal utility of `ptbk coder`
43737
+ */
43738
+ function formatDisplayPath(relativePath) {
43739
+ return relativePath.replace(/\\/gu, '/');
43740
+ }
43741
+ // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43742
+
43675
43743
  /**
43676
43744
  * Fallback `.env` content used when no required variables need to be appended.
43677
43745
  */
@@ -43749,7 +43817,7 @@ async function ensureCoderGitignoreFile(projectPath) {
43749
43817
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
43750
43818
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
43751
43819
  'coder:add': 'ptbk coder add --template ./prompts/templates/common.md',
43752
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.5 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
43820
+ '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',
43753
43821
  // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
43754
43822
  'coder:verify': 'ptbk coder verify',
43755
43823
  };
@@ -43998,9 +44066,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
43998
44066
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
43999
44067
  const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
44000
44068
  const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
44001
- const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent({
44002
- packageJsonScripts: getDefaultCoderPackageJsonScripts(),
44003
- }));
44069
+ const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
44004
44070
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44005
44071
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44006
44072
  const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
@@ -44115,12 +44181,24 @@ function $initializeCoderInitCommand(program) {
44115
44181
 
44116
44182
  Checks that the coding harnesses are installed globally and up to date:
44117
44183
  ${block(listCheckedHarnessLabels())}
44184
+
44185
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44118
44186
  `));
44119
- command.action(handleActionErrors(async () => {
44187
+ addCoderGitSyncOptions(command);
44188
+ command.action(handleActionErrors(async (cliOptions) => {
44189
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44120
44190
  const projectPath = process.cwd();
44191
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
44192
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
44193
+ await $pullCoderChanges({ gitSync, projectPath });
44121
44194
  const summary = await initializeCoderProjectConfiguration(projectPath);
44122
44195
  printInitializationSummary(summary);
44123
44196
  await generatePromptBoilerplate({ projectPath, filesCount: 5 });
44197
+ await $commitCoderChanges({
44198
+ gitSync,
44199
+ projectPath,
44200
+ commitMessage: 'Initialize Promptbook Coder',
44201
+ });
44124
44202
  await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
44125
44203
  }));
44126
44204
  }
@@ -44141,6 +44219,48 @@ function listDefaultCoderProjectPromptTemplateDisplayPaths() {
44141
44219
  // Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
44142
44220
  // Note: [💞] Ignore a discrepancy between file name and entity name
44143
44221
 
44222
+ /**
44223
+ * Initializes `coder ping` command for Promptbook CLI utilities
44224
+ *
44225
+ * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
44226
+ *
44227
+ * @private internal function of `promptbookCli`
44228
+ */
44229
+ function $initializeCoderPingCommand(program) {
44230
+ const command = program.command('ping');
44231
+ command.description(spaceTrim$1(`
44232
+ Send one tiny dummy prompt to a harness and model to measure and warm them up
44233
+
44234
+ ${PROMPT_RUNNER_DESCRIPTION}
44235
+
44236
+ Features:
44237
+ - Verifies that the selected harness, model, thinking level and authentication really work
44238
+ - Reports the answer of the harness, the response time and the reported usage
44239
+ - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44240
+ - Leaves the project exactly as it was — nothing is read, written, changed or committed
44241
+ - Use --no-ui to stream the raw harness output instead of only the compact result
44242
+ `));
44243
+ addPromptRunnerSelectionOptions(command);
44244
+ addPromptRunnerRuntimeOptions(command);
44245
+ command.action(handleActionErrors(async (cliOptions) => {
44246
+ const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44247
+ await $ensureHarnessInstallations([runnerOptions.agentName]);
44248
+ // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
44249
+ const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
44250
+ const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
44251
+ const result = await pingCoderHarness({
44252
+ agentName: runnerOptions.agentName,
44253
+ model: runnerOptions.model,
44254
+ thinkingLevel: runnerOptions.thinkingLevel,
44255
+ allowCredits: runnerOptions.allowCredits,
44256
+ shouldPrintLiveOutput: runnerOptions.noUi,
44257
+ });
44258
+ printCoderPingResult(result);
44259
+ }));
44260
+ }
44261
+ // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44262
+ // Note: [💞] Ignore a discrepancy between file name and entity name
44263
+
44144
44264
  /**
44145
44265
  * Creates a Commander argument parser that accepts only non-negative integers.
44146
44266
  *
@@ -44165,6 +44285,21 @@ function createNonNegativeIntegerOptionParser(optionName) {
44165
44285
  }
44166
44286
  // Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
44167
44287
 
44288
+ /**
44289
+ * Modes supported by `ptbk coder run --test-before`.
44290
+ */
44291
+ const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
44292
+ /**
44293
+ * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
44294
+ */
44295
+ const DEFAULT_CODER_TEST_COMMAND = 'npm test';
44296
+ /**
44297
+ * Checks whether a value is a supported `--test-before` mode.
44298
+ */
44299
+ function isTestBeforeMode(value) {
44300
+ return TEST_BEFORE_MODE_VALUES.includes(value);
44301
+ }
44302
+
44168
44303
  /**
44169
44304
  * Default wait duration applied before retrying a prompt round after an error (10 minutes).
44170
44305
  *
@@ -44209,6 +44344,7 @@ function $initializeCoderRunCommand(program) {
44209
44344
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44210
44345
  - Checks that the selected harness is installed globally and up to date before the first prompt
44211
44346
  - Supports GPG signing of commits
44347
+ - Optional pre-coding test run that can stop or repair pre-existing failures
44212
44348
  - Optional post-prompt verification with test-feedback retries
44213
44349
  - Progress tracking and interactive P/S/X terminal controls
44214
44350
  - Dry-run mode to preview prompts
@@ -44218,6 +44354,9 @@ function $initializeCoderRunCommand(program) {
44218
44354
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
44219
44355
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
44220
44356
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
44357
+ command.addOption(new Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
44358
+ .choices([...TEST_BEFORE_MODE_VALUES])
44359
+ .default('no'));
44221
44360
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
44222
44361
  addPromptRunnerExecutionOptions(command);
44223
44362
  command.option('--isolate', spaceTrim$1(`
@@ -44247,8 +44386,9 @@ function $initializeCoderRunCommand(program) {
44247
44386
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
44248
44387
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
44249
44388
  command.action(handleActionErrors(async (cliOptions) => {
44250
- const { dryRun, agent, context, test, preserveLogs, isolate: isIsolated, priority, minPriority: minimumPriority, maxPriority: maximumPriority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
44251
- const testCommand = normalizeCommandOptionValue$1(test);
44389
+ 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;
44390
+ const configuredTestCommand = normalizeCommandOptionValue$1(test);
44391
+ const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
44252
44392
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
44253
44393
  isAgentRequired: !dryRun,
44254
44394
  });
@@ -44277,6 +44417,7 @@ function $initializeCoderRunCommand(program) {
44277
44417
  agent,
44278
44418
  context,
44279
44419
  testCommand,
44420
+ testBefore,
44280
44421
  preserveLogs,
44281
44422
  isIsolated,
44282
44423
  noUi: runnerOptions.noUi,
@@ -44487,25 +44628,31 @@ function normalizeCommandOptionValue(value) {
44487
44628
  */
44488
44629
  function $initializeCoderVerifyCommand(program) {
44489
44630
  const command = program.command('verify');
44490
- command.description(spaceTrim$1(`
44491
- Interactive verification helper for completed prompts
44631
+ command.description(spaceTrim$1((block) => `
44632
+ Interactive verification helper for completed prompts
44492
44633
 
44493
- Features:
44494
- - Displays list of prompt files with status counts
44495
- - Guides through verification of completed prompts marked [x]
44496
- - Archives verified prompt files to prompts/done/ directory
44497
- - Auto-appends repair prompts for incomplete work
44498
- - Processes files with all-done prompts first
44499
- - Supports ignoring matching prompt candidates for one verification run
44500
- `));
44634
+ Features:
44635
+ - Displays list of prompt files with status counts
44636
+ - Guides through verification of completed prompts marked [x]
44637
+ - Archives verified prompt files to prompts/done/ directory
44638
+ - Auto-appends repair prompts for incomplete work
44639
+ - Processes files with all-done prompts first
44640
+ - Supports ignoring matching prompt candidates for one verification run
44641
+
44642
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44643
+
44644
+ Note: The git synchronization is applied around each single verification, not once per run.
44645
+ `));
44501
44646
  command.option('--reverse', 'Process prompt files in reverse order', false);
44502
44647
  command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
44648
+ addCoderGitSyncOptions(command);
44503
44649
  command.action(handleActionErrors(async (cliOptions) => {
44504
44650
  const { reverse, ignore } = cliOptions;
44651
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44505
44652
  // Note: Import the main function dynamically to avoid loading heavy dependencies until needed
44506
44653
  const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
44507
44654
  try {
44508
- await verifyPrompts({ reverse, ignore });
44655
+ await verifyPrompts({ reverse, ignore, gitSync });
44509
44656
  }
44510
44657
  catch (error) {
44511
44658
  console.error(colors.bgRed('Prompt verification failed:'), error);
@@ -44534,6 +44681,7 @@ function collectStringOption(value, previousValues) {
44534
44681
  * - generate-boilerplates: Generate prompt boilerplate files
44535
44682
  * - find-refactor-candidates: Find files that need refactoring
44536
44683
  * - run: Run coding prompts with AI agents
44684
+ * - ping: Test one harness and model with a tiny dummy prompt
44537
44685
  * - verify: Verify completed prompts
44538
44686
  * - find-fresh-emoji-tags: Find unused emoji tags
44539
44687
  *
@@ -44553,6 +44701,7 @@ function $initializeCoderCommand(program) {
44553
44701
  - find-refactor-candidates: Find files that need refactoring
44554
44702
  - find-unwritten: List prompt sections that still need to be authored
44555
44703
  - run: Run coding prompts with AI agents
44704
+ - ping: Test the connection, response time and quota of one harness and model
44556
44705
  - server: Start a long-running coder server with a kanban web UI
44557
44706
  - verify: Verify completed prompts
44558
44707
  - find-fresh-emoji-tags: Find unused emoji tags
@@ -44564,6 +44713,7 @@ function $initializeCoderCommand(program) {
44564
44713
  $initializeCoderFindRefactorCandidatesCommand(coderCommand);
44565
44714
  $initializeCoderFindUnwrittenCommand(coderCommand);
44566
44715
  $initializeCoderRunCommand(coderCommand);
44716
+ $initializeCoderPingCommand(coderCommand);
44567
44717
  $initializeCoderServerCommand(coderCommand);
44568
44718
  $initializeCoderVerifyCommand(coderCommand);
44569
44719
  $initializeCoderFindFreshEmojiTagCommand(coderCommand);
@@ -45397,6 +45547,14 @@ class FileCacheStorage {
45397
45547
  // 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
45398
45548
  // TODO: [🌗] Maybe some checkers, not all valid JSONs are desired and valid values
45399
45549
 
45550
+ /**
45551
+ * Loads the Socket.io client (`socket.io-client`) on demand
45552
+ *
45553
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
45554
+ *
45555
+ * @private internal utility of `createRemoteClient`
45556
+ */
45557
+ const loadSocketIoClientModule = createLazyModuleLoader(() => import('socket.io-client'));
45400
45558
  /**
45401
45559
  * Creates a connection to the remote proxy server.
45402
45560
  *
@@ -45425,6 +45583,7 @@ async function createRemoteClient(options) {
45425
45583
 
45426
45584
  `));
45427
45585
  }
45586
+ const { io } = await loadSocketIoClientModule();
45428
45587
  return new Promise((resolve, reject) => {
45429
45588
  const socket = io(remoteServerUrl, {
45430
45589
  retries: CONNECTION_RETRIES_LIMIT,
@@ -46092,6 +46251,7 @@ async function $provideLlmToolsForCli(options) {
46092
46251
  You will be logged in to ${remoteServerUrl}
46093
46252
  If you don't have an account, it will be created automatically.
46094
46253
  `)));
46254
+ const { default: prompts } = await loadPromptsModule();
46095
46255
  const { username, password } = await prompts([
46096
46256
  {
46097
46257
  type: 'text',
@@ -46467,6 +46627,15 @@ function $initializeLoginCommand(program) {
46467
46627
  // TODO: Implement non-interactive login
46468
46628
  // Note: [💞] Ignore a discrepancy between file name and entity name
46469
46629
 
46630
+ /**
46631
+ * Loads the ZIP archive library (`jszip`) on demand
46632
+ *
46633
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
46634
+ *
46635
+ * @private internal utility of `loadArchive` and `saveArchive`
46636
+ */
46637
+ const loadJsZipModule = createLazyModuleLoader(() => import('jszip'));
46638
+
46470
46639
  /**
46471
46640
  * Loads the books from the archive file with `.bookc` extension
46472
46641
  *
@@ -46481,6 +46650,7 @@ async function loadArchive(filePath, fs) {
46481
46650
  throw new UnexpectedError(`Archive file must have '.bookc' extension`);
46482
46651
  }
46483
46652
  const data = await fs.readFile(filePath);
46653
+ const { default: JSZip } = await loadJsZipModule();
46484
46654
  const archive = await JSZip.loadAsync(data);
46485
46655
  const indexFile = archive.file('index.book.json');
46486
46656
  if (!indexFile) {
@@ -50544,7 +50714,23 @@ function createShowdownConverter() {
50544
50714
  });
50545
50715
  }
50546
50716
 
50547
- // TODO: [🏳‍🌈] Finally take pick of .json vs .ts
50717
+ /**
50718
+ * Loads `jsdom` on demand
50719
+ *
50720
+ * Note: [🐌] `jsdom` is by far the heaviest dependency of Promptbook, loading it eagerly would slow down every single
50721
+ * run of the `ptbk` CLI utility even when no website is scraped
50722
+ *
50723
+ * @private internal utility of `WebsiteScraper`
50724
+ */
50725
+ const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
50726
+ /**
50727
+ * Loads `@mozilla/readability` on demand
50728
+ *
50729
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
50730
+ *
50731
+ * @private internal utility of `WebsiteScraper`
50732
+ */
50733
+ const loadReadabilityModule = createLazyModuleLoader(() => import('@mozilla/readability'));
50548
50734
  /**
50549
50735
  * Scraper for websites
50550
50736
  *
@@ -50580,6 +50766,7 @@ class WebsiteScraper {
50580
50766
  if (this.tools.fs === undefined) {
50581
50767
  throw new EnvironmentMismatchError('Can not scrape websites without filesystem tools');
50582
50768
  }
50769
+ const [{ JSDOM }, { Readability }] = await Promise.all([loadJsdomModule(), loadReadabilityModule()]);
50583
50770
  const jsdom = new JSDOM(await source.asText(), {
50584
50771
  url: source.url,
50585
50772
  });
@@ -54183,6 +54370,7 @@ async function saveArchive(filePath, collectionJson, fs) {
54183
54370
  for (const pipelineJson of collectionJson) {
54184
54371
  validatePipeline(pipelineJson);
54185
54372
  }
54373
+ const { default: JSZip } = await loadJsZipModule();
54186
54374
  const archive = new JSZip();
54187
54375
  const collectionJsonString = stringifyPipelineJson(collectionJson);
54188
54376
  archive.file('index.book.json', collectionJsonString);
@@ -54811,6 +54999,7 @@ async function runInteractiveChatbot(options) {
54811
54999
  else {
54812
55000
  console.info(colors.gray(`---`));
54813
55001
  }
55002
+ const { default: prompts } = await loadPromptsModule();
54814
55003
  const response = await prompts({
54815
55004
  type: 'text',
54816
55005
  name: 'userMessage',
@@ -55118,6 +55307,7 @@ async function resolveRunPipelineSource(pipelineSource) {
55118
55307
  if (pipelineSource) {
55119
55308
  return pipelineSource;
55120
55309
  }
55310
+ const { default: prompts } = await loadPromptsModule();
55121
55311
  const response = await prompts({
55122
55312
  type: 'text',
55123
55313
  name: 'pipelineSource',
@@ -55205,6 +55395,7 @@ async function resolveRunInputParameters(options) {
55205
55395
  console.error(colors.red(createRunMissingInputParametersMessage(pipeline, inputParameters, questions)));
55206
55396
  return process.exit(1);
55207
55397
  }
55398
+ const { default: prompts } = await loadPromptsModule();
55208
55399
  const response = await prompts(questions);
55209
55400
  // <- TODO: [🧠][🍼] Change behavior according to the formfactor
55210
55401
  return { ...inputParameters, ...response };
@@ -58110,6 +58301,14 @@ const ANTHROPIC_PROVIDER_PROFILE = {
58110
58301
  fullname: 'Anthropic Claude',
58111
58302
  color: '#d97706',
58112
58303
  };
58304
+ /**
58305
+ * Loads the Anthropic Claude SDK (`@anthropic-ai/sdk`) on demand
58306
+ *
58307
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58308
+ *
58309
+ * @private internal utility of `AnthropicClaudeExecutionTools`
58310
+ */
58311
+ const loadAnthropicClaudeModule = createLazyModuleLoader(() => import('@anthropic-ai/sdk'));
58113
58312
  /**
58114
58313
  * Execution Tools for calling Anthropic Claude API.
58115
58314
  *
@@ -58147,6 +58346,7 @@ class AnthropicClaudeExecutionTools {
58147
58346
  const anthropicOptions = { ...this.options };
58148
58347
  delete anthropicOptions.isVerbose;
58149
58348
  delete anthropicOptions.isProxied;
58349
+ const { Anthropic } = await loadAnthropicClaudeModule();
58150
58350
  this.client = new Anthropic(anthropicOptions);
58151
58351
  }
58152
58352
  return this.client;
@@ -58410,6 +58610,14 @@ const AZURE_OPENAI_PROVIDER_PROFILE = {
58410
58610
  fullname: 'Azure OpenAI',
58411
58611
  color: '#0078d4',
58412
58612
  };
58613
+ /**
58614
+ * Loads the Azure OpenAI SDK (`@azure/openai`) on demand
58615
+ *
58616
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58617
+ *
58618
+ * @private internal utility of `AzureOpenAiExecutionTools`
58619
+ */
58620
+ const loadAzureOpenAiModule = createLazyModuleLoader(() => import('@azure/openai'));
58413
58621
  /**
58414
58622
  * Execution Tools for calling Azure OpenAI API.
58415
58623
  *
@@ -58443,6 +58651,7 @@ class AzureOpenAiExecutionTools {
58443
58651
  }
58444
58652
  async getClient() {
58445
58653
  if (this.client === null) {
58654
+ const { AzureKeyCredential, OpenAIClient } = await loadAzureOpenAiModule();
58446
58655
  this.client = new OpenAIClient(`https://${this.options.resourceName}.openai.azure.com/`, new AzureKeyCredential(this.options.apiKey));
58447
58656
  }
58448
58657
  return this.client;
@@ -60252,6 +60461,14 @@ class OpenAiCompatibleNonChatPromptCaller {
60252
60461
  }
60253
60462
  }
60254
60463
 
60464
+ /**
60465
+ * Loads the OpenAI SDK (`openai`) on demand
60466
+ *
60467
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
60468
+ *
60469
+ * @private internal utility of `OpenAiCompatibleRequestManager`
60470
+ */
60471
+ const loadOpenAiModule = createLazyModuleLoader(() => import('openai'));
60255
60472
  /**
60256
60473
  * Manages OpenAI-compatible client creation plus shared retry and rate-limit behavior.
60257
60474
  *
@@ -60278,6 +60495,7 @@ class OpenAiCompatibleRequestManager {
60278
60495
  timeout: API_REQUEST_TIMEOUT,
60279
60496
  maxRetries: CONNECTION_RETRIES_LIMIT,
60280
60497
  };
60498
+ const { default: OpenAI } = await loadOpenAiModule();
60281
60499
  this.client = new OpenAI(enhancedOptions);
60282
60500
  }
60283
60501
  return this.client;
@@ -67991,6 +68209,7 @@ async function runAgentChat(options) {
67991
68209
  if (options.isVerbose) {
67992
68210
  console.info(colors.gray('Type "exit" or "quit" to end the chat.'));
67993
68211
  }
68212
+ const { default: prompts } = await loadPromptsModule();
67994
68213
  while (true) {
67995
68214
  const response = await prompts({
67996
68215
  type: 'text',
@@ -69204,6 +69423,16 @@ class OpenAiAgentKitExecutionToolsOutputTypeMapper {
69204
69423
  }
69205
69424
  }
69206
69425
 
69426
+ /**
69427
+ * Loads the OpenAI AgentKit SDK (`@openai/agents`) on demand
69428
+ *
69429
+ * Note: [🐌] The AgentKit SDK is one of the heaviest dependencies of Promptbook, loading it eagerly would slow down
69430
+ * every single run of the `ptbk` CLI utility even when no AgentKit agent is used
69431
+ *
69432
+ * @private internal utility of `@promptbook/openai`
69433
+ */
69434
+ const loadOpenAiAgentsModule = createLazyModuleLoader(() => import('@openai/agents'));
69435
+
69207
69436
  /**
69208
69437
  * Constant for default model used for nested DeepSearch tool invocations.
69209
69438
  */
@@ -69257,8 +69486,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69257
69486
  /**
69258
69487
  * Builds the tool list for AgentKit, including hosted file search when applicable.
69259
69488
  */
69260
- buildAgentKitTools(options) {
69489
+ async buildAgentKitTools(options) {
69261
69490
  const { tools, vectorStoreId } = options;
69491
+ const { fileSearchTool, tool: agentKitTool } = await loadOpenAiAgentsModule();
69262
69492
  const agentKitTools = [];
69263
69493
  if (vectorStoreId) {
69264
69494
  agentKitTools.push(fileSearchTool(vectorStoreId));
@@ -69269,11 +69499,11 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69269
69499
  let scriptTools = null;
69270
69500
  for (const toolDefinition of tools) {
69271
69501
  if (this.isDeepSearchToolDefinition(toolDefinition)) {
69272
- agentKitTools.push(this.createDeepSearchAgentKitTool(toolDefinition));
69502
+ agentKitTools.push(await this.createDeepSearchAgentKitTool(toolDefinition));
69273
69503
  continue;
69274
69504
  }
69275
69505
  scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
69276
- agentKitTools.push(tool({
69506
+ agentKitTools.push(agentKitTool({
69277
69507
  name: toolDefinition.name,
69278
69508
  description: toolDefinition.description,
69279
69509
  parameters: this.normalizeAgentKitToolParameters(toolDefinition.parameters),
@@ -69480,8 +69710,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69480
69710
  /**
69481
69711
  * Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
69482
69712
  */
69483
- createDeepSearchAgentKitTool(toolDefinition) {
69484
- const deepSearchAgent = new Agent$1({
69713
+ async createDeepSearchAgentKitTool(toolDefinition) {
69714
+ const { Agent: AgentFromKit, webSearchTool } = await loadOpenAiAgentsModule();
69715
+ const deepSearchAgent = new AgentFromKit({
69485
69716
  name: 'DeepSearch',
69486
69717
  model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
69487
69718
  instructions: this.createDeepSearchAgentInstructions(toolDefinition.description),
@@ -69694,8 +69925,9 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69694
69925
  vectorStoreId,
69695
69926
  });
69696
69927
  }
69697
- const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
69698
- const openAiAgentKitAgent = new Agent$1({
69928
+ const { Agent: AgentFromKit } = await loadOpenAiAgentsModule();
69929
+ const agentKitTools = await this.buildAgentKitTools({ tools, vectorStoreId });
69930
+ const openAiAgentKitAgent = new AgentFromKit({
69699
69931
  name,
69700
69932
  model: this.agentKitModelName,
69701
69933
  instructions: instructions || 'You are a helpful assistant.',
@@ -69740,6 +69972,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69740
69972
  agentName: agentForRun.name,
69741
69973
  input: inputItems,
69742
69974
  };
69975
+ const { run } = await loadOpenAiAgentsModule();
69743
69976
  const streamResult = await run(agentForRun, inputItems, {
69744
69977
  stream: true,
69745
69978
  maxTurns: 200,
@@ -69882,6 +70115,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69882
70115
  * Ensures the AgentKit SDK is wired to the OpenAI client and API key.
69883
70116
  */
69884
70117
  async ensureAgentKitDefaults() {
70118
+ const { setDefaultOpenAIClient, setDefaultOpenAIKey } = await loadOpenAiAgentsModule();
69885
70119
  const client = await this.getClient();
69886
70120
  setDefaultOpenAIClient(client);
69887
70121
  const apiKey = this.agentKitOptions.apiKey;
@@ -72214,6 +72448,66 @@ var RemoteAgent$1 = /*#__PURE__*/Object.freeze({
72214
72448
  RemoteAgent: RemoteAgent
72215
72449
  });
72216
72450
 
72451
+ /**
72452
+ * Git synchronization which leaves the repository completely untouched.
72453
+ *
72454
+ * Note: This is the default for every command and helper which supports the git synchronization.
72455
+ */
72456
+ const DISABLED_CODER_GIT_SYNC_OPTIONS = Object.freeze({
72457
+ isCommitEnabled: false,
72458
+ isAutoPushEnabled: false,
72459
+ isAutoPullEnabled: false,
72460
+ });
72461
+ /**
72462
+ * Pulls the latest repository changes before a `ptbk coder` command changes the project.
72463
+ */
72464
+ async function $pullCoderChanges(options) {
72465
+ const { gitSync, projectPath = process.cwd() } = options;
72466
+ if (!gitSync.isAutoPullEnabled) {
72467
+ return;
72468
+ }
72469
+ console.info(colors.gray('Pulling the latest changes from the remote repository...'));
72470
+ await pullLatestChanges(projectPath);
72471
+ }
72472
+ /**
72473
+ * Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
72474
+ *
72475
+ * Note: A repository without any change is left alone instead of creating an empty commit.
72476
+ */
72477
+ async function $commitCoderChanges(options) {
72478
+ const { gitSync, commitMessage, projectPath = process.cwd() } = options;
72479
+ if (!gitSync.isCommitEnabled) {
72480
+ return;
72481
+ }
72482
+ if (!(await hasChangesToCommit(projectPath))) {
72483
+ console.info(colors.gray('Nothing to commit, the working tree is clean'));
72484
+ return;
72485
+ }
72486
+ await commitChanges(commitMessage, {
72487
+ projectPath,
72488
+ autoPush: gitSync.isAutoPushEnabled,
72489
+ });
72490
+ console.info(colors.green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
72491
+ }
72492
+ /**
72493
+ * Checks whether the repository holds any change which can be committed.
72494
+ */
72495
+ async function hasChangesToCommit(projectPath) {
72496
+ const gitStatus = await runGitCommand({
72497
+ command: 'git status --porcelain',
72498
+ cwd: projectPath,
72499
+ isVerbose: false,
72500
+ });
72501
+ return gitStatus.trim() !== '';
72502
+ }
72503
+
72504
+ var coderGitSync = /*#__PURE__*/Object.freeze({
72505
+ __proto__: null,
72506
+ DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
72507
+ $pullCoderChanges: $pullCoderChanges,
72508
+ $commitCoderChanges: $commitCoderChanges
72509
+ });
72510
+
72217
72511
  /**
72218
72512
  * Calculates the next available prompt numbering sequence for a month.
72219
72513
  */
@@ -72688,6 +72982,31 @@ function normalizeRefactorCandidatePath(pathValue) {
72688
72982
  }
72689
72983
  // Note: [🟡] Code for repository script [normalizeRefactorCandidatePath](scripts/find-refactor-candidates/normalizeRefactorCandidatePath.ts) should never be published outside of `@promptbook/cli`
72690
72984
 
72985
+ /**
72986
+ * The TypeScript compiler API once it was loaded by `analyzeSourceFileForRefactorCandidate`
72987
+ *
72988
+ * Note: [🐌] `typescript` is a heavy package, it is loaded on demand so that it does not slow down every single run
72989
+ * of the `ptbk` CLI utility
72990
+ *
72991
+ * @private variable of analyzeSourceFileForRefactorCandidate
72992
+ */
72993
+ let loadedTypescriptModule = null;
72994
+ /**
72995
+ * Returns the TypeScript compiler API which was already loaded for the structural analysis.
72996
+ *
72997
+ * @private function of analyzeSourceFileForRefactorCandidate
72998
+ */
72999
+ function getLoadedTypescriptModule() {
73000
+ if (loadedTypescriptModule === null) {
73001
+ throw new UnexpectedError(spaceTrim(`
73002
+ The \`typescript\` module was not loaded yet.
73003
+
73004
+ Structural analysis helpers must be called only from \`analyzeSourceFileForRefactorCandidate\` which
73005
+ loads \`typescript\` lazily.
73006
+ `));
73007
+ }
73008
+ return loadedTypescriptModule;
73009
+ }
72691
73010
  /**
72692
73011
  * Resolves whether a source file should produce a refactor candidate entry.
72693
73012
  *
@@ -72713,6 +73032,7 @@ async function analyzeSourceFileForRefactorCandidate(options) {
72713
73032
  }
72714
73033
  }
72715
73034
  if (STRUCTURAL_ANALYSIS_EXTENSIONS.includes(extension)) {
73035
+ loadedTypescriptModule !== null && loadedTypescriptModule !== void 0 ? loadedTypescriptModule : (loadedTypescriptModule = await getTypescriptModule());
72716
73036
  const structureSummary = summarizeSourceFileStructure(content, extension, filePath);
72717
73037
  if (structureSummary.entityCount > heuristics.maxEntityCountPerFile) {
72718
73038
  reasons.push(`entities ${structureSummary.entityCount}/${heuristics.maxEntityCountPerFile}`);
@@ -72772,6 +73092,7 @@ function countLines(content) {
72772
73092
  * @private function of analyzeSourceFileForRefactorCandidate
72773
73093
  */
72774
73094
  function summarizeSourceFileStructure(content, extension, filePath) {
73095
+ const ts = getLoadedTypescriptModule();
72775
73096
  const scriptKind = getScriptKindForExtension(extension);
72776
73097
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKind);
72777
73098
  return {
@@ -72785,6 +73106,7 @@ function summarizeSourceFileStructure(content, extension, filePath) {
72785
73106
  * @private function of analyzeSourceFileForRefactorCandidate
72786
73107
  */
72787
73108
  function countEntitiesInSourceFile(sourceFile) {
73109
+ const ts = getLoadedTypescriptModule();
72788
73110
  let count = 0;
72789
73111
  // Only count top-level declarations to avoid inflating with members or nested scopes.
72790
73112
  for (const statement of sourceFile.statements) {
@@ -72817,6 +73139,7 @@ function countEntitiesInSourceFile(sourceFile) {
72817
73139
  * @private function of analyzeSourceFileForRefactorCandidate
72818
73140
  */
72819
73141
  function summarizeFunctionsInSourceFile(sourceFile) {
73142
+ const ts = getLoadedTypescriptModule();
72820
73143
  let functionCount = 0;
72821
73144
  let maxFunctionComplexity = 0;
72822
73145
  let mostComplexFunctionName = null;
@@ -72844,6 +73167,7 @@ function summarizeFunctionsInSourceFile(sourceFile) {
72844
73167
  * @private function of analyzeSourceFileForRefactorCandidate
72845
73168
  */
72846
73169
  function isCountedFunctionLikeDeclaration(node) {
73170
+ const ts = getLoadedTypescriptModule();
72847
73171
  if (ts.isFunctionDeclaration(node) ||
72848
73172
  ts.isMethodDeclaration(node) ||
72849
73173
  ts.isConstructorDeclaration(node) ||
@@ -72862,6 +73186,7 @@ function isCountedFunctionLikeDeclaration(node) {
72862
73186
  * @private function of analyzeSourceFileForRefactorCandidate
72863
73187
  */
72864
73188
  function isNamedFunctionExpression(node) {
73189
+ const ts = getLoadedTypescriptModule();
72865
73190
  const parent = node.parent;
72866
73191
  return (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent) || ts.isPropertyAssignment(parent));
72867
73192
  }
@@ -72874,6 +73199,7 @@ function calculateFunctionComplexity(functionNode) {
72874
73199
  if (!functionNode.body) {
72875
73200
  return 1;
72876
73201
  }
73202
+ const ts = getLoadedTypescriptModule();
72877
73203
  let complexity = 1;
72878
73204
  const visitNode = (node) => {
72879
73205
  if (node !== functionNode.body && isCountedFunctionLikeDeclaration(node)) {
@@ -72893,6 +73219,7 @@ function calculateFunctionComplexity(functionNode) {
72893
73219
  * @private function of analyzeSourceFileForRefactorCandidate
72894
73220
  */
72895
73221
  function isComplexityDecisionNode(node) {
73222
+ const ts = getLoadedTypescriptModule();
72896
73223
  if (ts.isIfStatement(node) ||
72897
73224
  ts.isConditionalExpression(node) ||
72898
73225
  ts.isCatchClause(node) ||
@@ -72918,6 +73245,7 @@ function isComplexityDecisionNode(node) {
72918
73245
  * @private function of analyzeSourceFileForRefactorCandidate
72919
73246
  */
72920
73247
  function getFunctionDisplayName(functionNode) {
73248
+ const ts = getLoadedTypescriptModule();
72921
73249
  if (ts.isConstructorDeclaration(functionNode)) {
72922
73250
  return 'constructor';
72923
73251
  }
@@ -72950,6 +73278,7 @@ function getFunctionDisplayName(functionNode) {
72950
73278
  * @private function of analyzeSourceFileForRefactorCandidate
72951
73279
  */
72952
73280
  function getBindingNameText(name) {
73281
+ const ts = getLoadedTypescriptModule();
72953
73282
  return ts.isIdentifier(name) ? name.text : null;
72954
73283
  }
72955
73284
  /**
@@ -72958,6 +73287,7 @@ function getBindingNameText(name) {
72958
73287
  * @private function of analyzeSourceFileForRefactorCandidate
72959
73288
  */
72960
73289
  function getPropertyNameText(name) {
73290
+ const ts = getLoadedTypescriptModule();
72961
73291
  if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
72962
73292
  return name.text;
72963
73293
  }
@@ -72980,6 +73310,7 @@ function buildComplexityReason(structureSummary, maxAllowedFunctionComplexity) {
72980
73310
  * @private function of analyzeSourceFileForRefactorCandidate
72981
73311
  */
72982
73312
  function getScriptKindForExtension(extension) {
73313
+ const ts = getLoadedTypescriptModule();
72983
73314
  if (extension === '.tsx') {
72984
73315
  return ts.ScriptKind.TSX;
72985
73316
  }
@@ -73589,6 +73920,41 @@ function listPromptsToBeWritten(files, priorityFilter = {}) {
73589
73920
  isPromptInPriorityFilter(prompt.section, priorityFilter));
73590
73921
  }
73591
73922
 
73923
+ /**
73924
+ * Checks whether a prompt is unrestricted or matches the selected harness/model.
73925
+ *
73926
+ * A prompt status line can contain one or more backtick-delimited model or harness
73927
+ * names. Matching is intentionally based on normalized substrings so a token such as
73928
+ * `gpt` selects any `gpt-*` model and `opus` selects a `claude-opus-*` model.
73929
+ */
73930
+ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
73931
+ if (promptRunnerIdentity === undefined) {
73932
+ return true;
73933
+ }
73934
+ const statusLine = section.statusLineIndex === undefined ? undefined : file.lines[section.statusLineIndex];
73935
+ const requiredRunnerTokens = statusLine === undefined ? [] : extractPromptRunnerTokens(statusLine);
73936
+ if (requiredRunnerTokens.length === 0) {
73937
+ return true;
73938
+ }
73939
+ const normalizedRunnerNames = [promptRunnerIdentity.harnessName, promptRunnerIdentity.modelName]
73940
+ .filter((name) => name !== undefined && name.trim() !== '')
73941
+ .map((name) => normalizeToKebabCase(name))
73942
+ .filter((name) => name !== '');
73943
+ return requiredRunnerTokens.some((requiredRunnerToken) => {
73944
+ const normalizedRequiredRunnerToken = normalizeToKebabCase(requiredRunnerToken);
73945
+ return (normalizedRequiredRunnerToken !== '' &&
73946
+ normalizedRunnerNames.some((normalizedRunnerName) => normalizedRunnerName.includes(normalizedRequiredRunnerToken)));
73947
+ });
73948
+ }
73949
+ /**
73950
+ * Extracts model and harness tokens from a prompt status line.
73951
+ */
73952
+ function extractPromptRunnerTokens(statusLine) {
73953
+ return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
73954
+ .map((match) => { var _a, _b; return (_b = (_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''; })
73955
+ .filter((token) => token !== '');
73956
+ }
73957
+
73592
73958
  /**
73593
73959
  * Parses a prompt markdown file into sections and metadata.
73594
73960
  */
@@ -73636,11 +74002,11 @@ function parsePromptFile(filePath, content) {
73636
74002
  };
73637
74003
  }
73638
74004
  /**
73639
- * Parses a status line like "[ ] !!" or "[-]" or "[x] ~$0.65 21 minutes..." into status and priority.
74005
+ * Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
73640
74006
  * For [x] done and [!] failed prompts, allow metadata after the status marker.
73641
74007
  */
73642
74008
  function parseStatusLine(line) {
73643
- var _a, _b, _c, _d, _e;
74009
+ var _a, _b, _c, _d;
73644
74010
  // For done prompts [x], allow any content after (for cost/time metadata)
73645
74011
  const doneMatch = line.match(/^\[(?<status>[xX])\]/);
73646
74012
  if (doneMatch) {
@@ -73651,21 +74017,23 @@ function parseStatusLine(line) {
73651
74017
  if (failedMatch) {
73652
74018
  return { status: 'failed', priority: 0 };
73653
74019
  }
73654
- // For todo [ ] and not-ready [-], require clean end with optional priority markers
73655
- const match = line.match(/^\[(?<status>[ -])\]\s*(?<priority>!*)\s*$/);
73656
- if (!match) {
73657
- return undefined;
74020
+ // For not-ready [-], keep the historical clean-line syntax.
74021
+ if (/^\[-\]\s*!*\s*$/u.test(line)) {
74022
+ return { status: 'not-ready', priority: 0 };
73658
74023
  }
73659
- const statusChar = (_b = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.status) === null || _b === void 0 ? void 0 : _b.toLowerCase();
73660
- let status;
73661
- if (statusChar === '-') {
73662
- status = 'not-ready';
74024
+ // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
74025
+ // before or after those tokens. Other trailing text remains an invalid status line.
74026
+ const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
74027
+ if (!todoMatch) {
74028
+ return undefined;
73663
74029
  }
73664
- else {
73665
- status = 'todo';
74030
+ const details = (_b = (_a = todoMatch.groups) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : '';
74031
+ const isPriorityOnly = /^[!\s]*$/u.test(details);
74032
+ const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
74033
+ if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
74034
+ return undefined;
73666
74035
  }
73667
- 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;
73668
- return { status, priority };
74036
+ return { status: 'todo', priority: (_d = (_c = details.match(/!/gu)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0 };
73669
74037
  }
73670
74038
  /**
73671
74039
  * Finds the first non-empty line index between two bounds.
@@ -73801,6 +74169,243 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
73801
74169
  findUnwrittenPrompts: findUnwrittenPrompts
73802
74170
  });
73803
74171
 
74172
+ /**
74173
+ * Builds a normalized temporary shell script path for prompt runners.
74174
+ */
74175
+ function buildTemporaryPromptScriptPath(options) {
74176
+ const sourceFileName = basename(options.sourceFileName);
74177
+ const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
74178
+ return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
74179
+ }
74180
+
74181
+ /**
74182
+ * Marker the pinged harness is asked to prefix its answer with, so the reply can be recognized
74183
+ * in the raw runner output of every supported harness.
74184
+ *
74185
+ * Note: The marker must stay free of regular-expression metacharacters, because
74186
+ * `extractCoderPingAnswer` builds its pattern from it.
74187
+ */
74188
+ const CODER_PING_ANSWER_MARKER = 'PTBK-CODER-PING-ANSWER';
74189
+ /**
74190
+ * First factor of the dummy multiplication the pinged harness is asked to compute.
74191
+ */
74192
+ const CODER_PING_FIRST_FACTOR = 6;
74193
+ /**
74194
+ * Second factor of the dummy multiplication the pinged harness is asked to compute.
74195
+ */
74196
+ const CODER_PING_SECOND_FACTOR = 7;
74197
+ /**
74198
+ * Answer a working harness and model returns for the dummy work of `ptbk coder ping`.
74199
+ */
74200
+ const CODER_PING_EXPECTED_ANSWER = String(CODER_PING_FIRST_FACTOR * CODER_PING_SECOND_FACTOR);
74201
+ /**
74202
+ * Builds the dummy prompt sent by `ptbk coder ping`.
74203
+ *
74204
+ * The work is intentionally the smallest possible one that still reaches the model: it spends a
74205
+ * negligible amount of the harness quota, it needs no tool and it explicitly forbids touching the
74206
+ * project, so a ping leaves the project exactly as it was.
74207
+ */
74208
+ function buildCoderPingPrompt() {
74209
+ return spaceTrim(`
74210
+ # Promptbook connection check
74211
+
74212
+ This is an automated \`ptbk coder ping\` connection check, not a coding task.
74213
+
74214
+ Do exactly this and nothing else:
74215
+
74216
+ 1. Multiply \`${CODER_PING_FIRST_FACTOR}\` by \`${CODER_PING_SECOND_FACTOR}\`.
74217
+ 2. Answer with one single line \`${CODER_PING_ANSWER_MARKER}: <result>\` where \`<result>\` is the number you computed.
74218
+
74219
+ Rules:
74220
+
74221
+ - Do not read, create, change, move or delete any file.
74222
+ - Do not run any command and do not use any tool.
74223
+ - Do not write anything except the single answer line.
74224
+ `);
74225
+ }
74226
+
74227
+ /**
74228
+ * Pattern matching one answer line produced by the pinged harness.
74229
+ *
74230
+ * The captured answer deliberately stops at a quote, a backslash or a line break, so an answer
74231
+ * embedded in a JSON event stream — as produced by Claude Code, Opencode or Codex `--json` — is
74232
+ * captured without the surrounding JSON.
74233
+ */
74234
+ const CODER_PING_ANSWER_PATTERN = new RegExp(`${CODER_PING_ANSWER_MARKER}\\s*:[ \\t]*([^\\r\\n"\\\\]*)`, 'gu');
74235
+ /**
74236
+ * Extracts the answer of a pinged harness from the runtime log of its runner shell.
74237
+ *
74238
+ * Only the raw output of the last execution is searched, so the answer marker contained in the
74239
+ * prompt of the raw input is never mistaken for the answer of the harness.
74240
+ *
74241
+ * @returns The answer of the harness, or `null` when the harness produced no recognizable answer
74242
+ */
74243
+ function extractCoderPingAnswer(runtimeLog) {
74244
+ var _a;
74245
+ const rawOutput = runtimeLog.split(SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER).pop();
74246
+ if (rawOutput === undefined) {
74247
+ return null;
74248
+ }
74249
+ // Note: The last answer wins because harnesses which stream partial messages repeat the growing answer line
74250
+ const answers = Array.from(rawOutput.matchAll(CODER_PING_ANSWER_PATTERN))
74251
+ .map((match) => (match[1] || '').trim())
74252
+ .filter((answer) => answer !== '');
74253
+ return (_a = answers[answers.length - 1]) !== null && _a !== void 0 ? _a : null;
74254
+ }
74255
+
74256
+ /**
74257
+ * Temporary subdirectory used for the `ptbk coder ping` runner shell script and its runtime log.
74258
+ */
74259
+ const CODER_PING_SCRIPT_DIRECTORY_NAME = 'coder-ping';
74260
+ /**
74261
+ * Base name of the temporary `ptbk coder ping` runner shell script.
74262
+ */
74263
+ const CODER_PING_SCRIPT_SOURCE_NAME = 'ping';
74264
+ /**
74265
+ * Sends one tiny dummy prompt through the selected harness and model and measures the round trip.
74266
+ *
74267
+ * The ping reuses the very same runner the coding queue uses, so it really exercises the configured
74268
+ * harness, model, thinking level and authentication — including the retry behavior on rate limits.
74269
+ * Both temporary artifacts it creates are removed again, so the project is left as it was.
74270
+ */
74271
+ async function pingCoderHarness(options) {
74272
+ const projectPath = options.projectPath || process.cwd();
74273
+ const { runner, runnerMetadata } = resolvePromptRunner(options);
74274
+ const scriptPath = buildTemporaryPromptScriptPath({
74275
+ projectPath,
74276
+ scriptDirectoryName: CODER_PING_SCRIPT_DIRECTORY_NAME,
74277
+ sourceFileName: CODER_PING_SCRIPT_SOURCE_NAME,
74278
+ });
74279
+ const startedTimeMs = Date.now();
74280
+ const { answer, usage, loginMethod } = await withPromptRuntimeLog(scriptPath, async (logPath) => {
74281
+ var _a;
74282
+ const result = await runner.runPrompt({
74283
+ prompt: buildCoderPingPrompt(),
74284
+ scriptPath,
74285
+ projectPath,
74286
+ logPath,
74287
+ shouldPrintLiveOutput: (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : false,
74288
+ preserveArtifactsOnSuccess: false,
74289
+ });
74290
+ return { ...result, answer: extractCoderPingAnswer(await readRuntimeLog(logPath)) };
74291
+ }, { preserveArtifactsOnSuccess: false });
74292
+ return {
74293
+ runnerName: runnerMetadata.runnerName,
74294
+ modelName: runnerMetadata.modelName,
74295
+ thinkingLevel: options.thinkingLevel,
74296
+ answer,
74297
+ isAnswerCorrect: answer === CODER_PING_EXPECTED_ANSWER,
74298
+ durationMs: Date.now() - startedTimeMs,
74299
+ usage,
74300
+ loginMethod,
74301
+ };
74302
+ }
74303
+ /**
74304
+ * Reads the runtime log of the finished ping, treating an unreadable log as no output at all.
74305
+ */
74306
+ async function readRuntimeLog(logPath) {
74307
+ return await readFile(logPath, 'utf-8').catch(() => '');
74308
+ }
74309
+
74310
+ var pingCoderHarness$1 = /*#__PURE__*/Object.freeze({
74311
+ __proto__: null,
74312
+ pingCoderHarness: pingCoderHarness
74313
+ });
74314
+
74315
+ /**
74316
+ * Formats usage price for display in prompt status lines and task details.
74317
+ * Examples:
74318
+ * - "$0.12" (certain)
74319
+ * - "~$3.05" (uncertain)
74320
+ * - "$0.00" (zero cost)
74321
+ * - "<$0.01" (tiny non-zero cost)
74322
+ *
74323
+ * @private internal utility of the prompt runners and the Agents Server task details
74324
+ */
74325
+ function formatUsagePrice(usage) {
74326
+ const price = usage.price.value;
74327
+ const isUncertain = usage.price.isUncertain === true;
74328
+ const prefix = isUncertain ? '~' : '';
74329
+ if (price === 0) {
74330
+ return `${prefix}$0.00`;
74331
+ }
74332
+ if (price < 0.01) {
74333
+ return `${prefix}<$0.01`;
74334
+ }
74335
+ if (price < 1) {
74336
+ return `${prefix}$${price.toFixed(4)}`;
74337
+ }
74338
+ return `${prefix}$${price.toFixed(2)}`;
74339
+ }
74340
+
74341
+ /**
74342
+ * Formats runner details for prompt status lines.
74343
+ */
74344
+ function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
74345
+ const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
74346
+ const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
74347
+ const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
74348
+ if (!normalizedRunner && !normalizedModel) {
74349
+ return 'unknown';
74350
+ }
74351
+ const runnerLabel = normalizedRunner || 'unknown';
74352
+ if (!normalizedModel) {
74353
+ return `${runnerLabel}${thinkingLevelSuffix}`;
74354
+ }
74355
+ return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
74356
+ }
74357
+
74358
+ /**
74359
+ * Prints the compact summary of one finished `ptbk coder ping`.
74360
+ */
74361
+ function printCoderPingResult(result) {
74362
+ const runnerSignature = formatRunnerSignature(result.runnerName, result.modelName, result.thinkingLevel);
74363
+ const loginMethodLabel = formatCodexLoginMethod(result.loginMethod);
74364
+ const loginMethodSuffix = loginMethodLabel === undefined ? '' : ` (${loginMethodLabel})`;
74365
+ console.info(colors.green(`🏓 ${runnerSignature}${loginMethodSuffix} answered in ${formatCoderPingResponseTime(result.durationMs)}`));
74366
+ console.info(colors.gray(` Answer: ${formatCoderPingAnswer(result)}`));
74367
+ console.info(colors.gray(` Usage: ${formatCoderPingUsage(result.usage)}`));
74368
+ }
74369
+ /**
74370
+ * Formats the measured round-trip time, keeping the sub-second precision a response time needs.
74371
+ */
74372
+ function formatCoderPingResponseTime(durationMs) {
74373
+ return `${(durationMs / 1000).toFixed(2)}s`;
74374
+ }
74375
+ /**
74376
+ * Formats the answer of the pinged harness together with what was expected from it.
74377
+ */
74378
+ function formatCoderPingAnswer(result) {
74379
+ if (result.answer === null) {
74380
+ return `Reached, but the answer line was missing from the output (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74381
+ }
74382
+ if (result.isAnswerCorrect) {
74383
+ return result.answer;
74384
+ }
74385
+ return `${result.answer} (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74386
+ }
74387
+ /**
74388
+ * Formats the resources the pinged harness reported for the dummy work.
74389
+ */
74390
+ function formatCoderPingUsage(usage) {
74391
+ return [
74392
+ formatUsagePrice(usage),
74393
+ `${formatUncertainCount(usage.input.tokensCount)} input tokens`,
74394
+ `${formatUncertainCount(usage.output.tokensCount)} output tokens`,
74395
+ ].join(', ');
74396
+ }
74397
+ /**
74398
+ * Formats one counted usage value, marking an estimated count with a leading `~`.
74399
+ */
74400
+ function formatUncertainCount(count) {
74401
+ return `${count.isUncertain === true ? '~' : ''}${Math.round(count.value)}`;
74402
+ }
74403
+
74404
+ var printCoderPingResult$1 = /*#__PURE__*/Object.freeze({
74405
+ __proto__: null,
74406
+ printCoderPingResult: printCoderPingResult
74407
+ });
74408
+
73804
74409
  /**
73805
74410
  * Default wait duration applied before retrying a failed prompt round.
73806
74411
  */
@@ -73808,7 +74413,7 @@ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
73808
74413
  /**
73809
74414
  * CLI usage text for this script.
73810
74415
  */
73811
- 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]';
74416
+ 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]';
73812
74417
  /**
73813
74418
  * Top-level flags supported by this command.
73814
74419
  */
@@ -73818,6 +74423,7 @@ const KNOWN_OPTION_FLAGS = new Set([
73818
74423
  '--model',
73819
74424
  '--context',
73820
74425
  '--test',
74426
+ '--test-before',
73821
74427
  '--preserve-logs',
73822
74428
  '--isolate',
73823
74429
  '--no-ui',
@@ -73856,6 +74462,8 @@ function parseRunOptions(args) {
73856
74462
  const context = readOptionValue(args, '--context');
73857
74463
  const hasTestCommandFlag = args.includes('--test');
73858
74464
  const testCommand = readVariadicOptionValue(args, '--test');
74465
+ const hasTestBeforeFlag = args.includes('--test-before');
74466
+ const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
73859
74467
  const preserveLogs = args.includes('--preserve-logs');
73860
74468
  const isIsolated = args.includes('--isolate');
73861
74469
  const noUi = args.includes('--no-ui');
@@ -73928,6 +74536,7 @@ function parseRunOptions(args) {
73928
74536
  model,
73929
74537
  context,
73930
74538
  testCommand,
74539
+ testBefore,
73931
74540
  thinkingLevel,
73932
74541
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
73933
74542
  minimumPriority,
@@ -73936,6 +74545,21 @@ function parseRunOptions(args) {
73936
74545
  limit,
73937
74546
  };
73938
74547
  }
74548
+ /**
74549
+ * Parses and validates the optional pre-coding verification mode.
74550
+ */
74551
+ function parseTestBeforeOption(value, hasTestBeforeFlag) {
74552
+ if (value === undefined) {
74553
+ if (hasTestBeforeFlag) {
74554
+ exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74555
+ }
74556
+ return 'no';
74557
+ }
74558
+ if (!isTestBeforeMode(value)) {
74559
+ exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74560
+ }
74561
+ return value;
74562
+ }
73939
74563
  /**
73940
74564
  * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
73941
74565
  */
@@ -75694,6 +76318,14 @@ function splitSqlStatements(sql) {
75694
76318
  .filter((statement) => statement !== '');
75695
76319
  }
75696
76320
 
76321
+ /**
76322
+ * Loads the PostgreSQL client (`pg`) on demand
76323
+ *
76324
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
76325
+ *
76326
+ * @private function of runAutoMigrateTestingServers
76327
+ */
76328
+ const loadPostgresModule = createLazyModuleLoader(() => import('pg'));
75697
76329
  /**
75698
76330
  * Migration targets for testing servers that should be migrated by coding-script auto-migration.
75699
76331
  */
@@ -75771,6 +76403,7 @@ async function runAutoMigrateTestingServersImmediately(options) {
75771
76403
  * @returns Pending migration files grouped by prefix.
75772
76404
  */
75773
76405
  async function listPendingMigrationsByPrefix(options) {
76406
+ const { Client } = await loadPostgresModule();
75774
76407
  const client = new Client({
75775
76408
  connectionString: options.connectionString,
75776
76409
  ssl: { rejectUnauthorized: false },
@@ -75928,15 +76561,6 @@ function buildCommitMessage(file, section) {
75928
76561
  return lines.join(file.eol);
75929
76562
  }
75930
76563
 
75931
- /**
75932
- * Builds a normalized temporary shell script path for prompt runners.
75933
- */
75934
- function buildTemporaryPromptScriptPath(options) {
75935
- const sourceFileName = basename(options.sourceFileName);
75936
- const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
75937
- return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
75938
- }
75939
-
75940
76564
  /**
75941
76565
  * Builds the suffix which disambiguates one prompt section inside its prompt file.
75942
76566
  *
@@ -75963,32 +76587,6 @@ function buildScriptPath(file, section, projectPath = process.cwd()) {
75963
76587
  });
75964
76588
  }
75965
76589
 
75966
- /**
75967
- * Formats usage price for display in prompt status lines and task details.
75968
- * Examples:
75969
- * - "$0.12" (certain)
75970
- * - "~$3.05" (uncertain)
75971
- * - "$0.00" (zero cost)
75972
- * - "<$0.01" (tiny non-zero cost)
75973
- *
75974
- * @private internal utility of the prompt runners and the Agents Server task details
75975
- */
75976
- function formatUsagePrice(usage) {
75977
- const price = usage.price.value;
75978
- const isUncertain = usage.price.isUncertain === true;
75979
- const prefix = isUncertain ? '~' : '';
75980
- if (price === 0) {
75981
- return `${prefix}$0.00`;
75982
- }
75983
- if (price < 0.01) {
75984
- return `${prefix}<$0.01`;
75985
- }
75986
- if (price < 1) {
75987
- return `${prefix}$${price.toFixed(4)}`;
75988
- }
75989
- return `${prefix}$${price.toFixed(2)}`;
75990
- }
75991
-
75992
76590
  /**
75993
76591
  * Human-readable labels for each coder run step kind shown in prompt status lines.
75994
76592
  */
@@ -76034,20 +76632,13 @@ function formatPromptAttemptMetadata(status, attemptCount) {
76034
76632
  }
76035
76633
 
76036
76634
  /**
76037
- * Formats runner details for prompt status lines.
76635
+ * Replaces the complete todo status line while preserving its indentation.
76636
+ *
76637
+ * The complete line is replaced because a todo status can contain a required
76638
+ * model/harness token in addition to priority markers.
76038
76639
  */
76039
- function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
76040
- const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
76041
- const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
76042
- const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
76043
- if (!normalizedRunner && !normalizedModel) {
76044
- return 'unknown';
76045
- }
76046
- const runnerLabel = normalizedRunner || 'unknown';
76047
- if (!normalizedModel) {
76048
- return `${runnerLabel}${thinkingLevelSuffix}`;
76049
- }
76050
- return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
76640
+ function replacePromptTodoStatusLine(line, replacementStatusLine) {
76641
+ return line.replace(/^(?<indentation>\s*)\[\s*\].*$/u, `$<indentation>${replacementStatusLine}`);
76051
76642
  }
76052
76643
 
76053
76644
  /**
@@ -76067,8 +76658,8 @@ function markPromptDone(file, section, steps, runnerName, modelName, attemptCoun
76067
76658
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
76068
76659
  const stepsSummary = formatCoderRunSteps(steps);
76069
76660
  const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
76070
- // Replace "[ ]" or "[ ] !!..." with "[x] by runner model thinking level (login method) - Step $price duration; ..."
76071
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76661
+ // Replace the complete todo status, including any required model/harness token.
76662
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76072
76663
  }
76073
76664
 
76074
76665
  /**
@@ -76086,8 +76677,10 @@ function markPromptFailed(file, section, runnerName, modelName, promptExecutionS
76086
76677
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
76087
76678
  const duration = moment().diff(promptExecutionStartedDate);
76088
76679
  const durationString = moment.duration(duration).humanize();
76089
- const failureDetails = attemptMetadata === '' ? `failed after ${durationString} by ${runnerSignature}` : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76090
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[!] ${failureDetails}`);
76680
+ const failureDetails = attemptMetadata === ''
76681
+ ? `failed after ${durationString} by ${runnerSignature}`
76682
+ : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76683
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[!] ${failureDetails}`);
76091
76684
  }
76092
76685
 
76093
76686
  /**
@@ -77057,17 +77650,18 @@ function toProjectRelativeGitPath(projectPath, path) {
77057
77650
  /**
77058
77651
  * Lists todo prompts that are ready to run (no authoring placeholders).
77059
77652
  */
77060
- function listRunnablePrompts(files, priorityFilter = {}) {
77653
+ function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
77061
77654
  return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
77062
- isPromptInPriorityFilter(prompt.section, priorityFilter));
77655
+ isPromptInPriorityFilter(prompt.section, priorityFilter) &&
77656
+ isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
77063
77657
  }
77064
77658
 
77065
77659
  /**
77066
77660
  * Selects the next runnable prompt based on priority.
77067
77661
  */
77068
- function findNextTodoPrompt(files, priorityFilter = {}) {
77662
+ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
77069
77663
  let nextPrompt;
77070
- for (const prompt of listRunnablePrompts(files, priorityFilter)) {
77664
+ for (const prompt of listRunnablePrompts(files, priorityFilter, promptRunnerIdentity)) {
77071
77665
  if (!nextPrompt || prompt.section.priority > nextPrompt.section.priority) {
77072
77666
  nextPrompt = prompt;
77073
77667
  }
@@ -77078,8 +77672,8 @@ function findNextTodoPrompt(files, priorityFilter = {}) {
77078
77672
  /**
77079
77673
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
77080
77674
  */
77081
- function listUpcomingTasks(files, priorityFilter = {}) {
77082
- return listRunnablePrompts(files, priorityFilter).map(({ file, section }) => ({
77675
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
77676
+ return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
77083
77677
  label: buildPromptLabelForDisplay(file, section),
77084
77678
  summary: buildPromptSummary(file, section),
77085
77679
  priority: section.priority,
@@ -77223,6 +77817,71 @@ async function buildCoderRunAgentVisual(agentSource) {
77223
77817
  }
77224
77818
  }
77225
77819
 
77820
+ /**
77821
+ * Creates the one queue prompt used to repair a pre-existing test failure.
77822
+ */
77823
+ async function createTestBeforeRepairPrompt(options) {
77824
+ const description = spaceTrim((block) => `
77825
+ Fix the existing test failures before implementing any queued coding tasks.
77826
+
77827
+ 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.
77828
+
77829
+ ## Verification output
77830
+
77831
+ \`\`\`
77832
+ ${block(limitTestOutput(options.testOutput))}
77833
+ \`\`\`
77834
+ `);
77835
+ const createdPrompt = await addCoderPrompt({
77836
+ projectPath: options.projectPath,
77837
+ description,
77838
+ priority: 0,
77839
+ });
77840
+ const promptPath = join(options.projectPath, createdPrompt.filePath);
77841
+ const promptFile = parsePromptFile(promptPath, await readFile(promptPath, 'utf-8'));
77842
+ const section = promptFile.sections[0];
77843
+ if (!section) {
77844
+ throw new UnexpectedError(spaceTrim(`
77845
+ The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
77846
+ `));
77847
+ }
77848
+ return { file: promptFile, section };
77849
+ }
77850
+
77851
+ /**
77852
+ * Runs the configured verification command before the first coding prompt.
77853
+ */
77854
+ async function runTestBefore(options) {
77855
+ var _a, _b;
77856
+ const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
77857
+ const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
77858
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
77859
+ checkpointLabel: 'running initial tests before the agent coding starts',
77860
+ phase: 'verifying',
77861
+ statusMessage: initialTestStatusMessage,
77862
+ }));
77863
+ console.info(colors.gray(initialTestStatusMessage));
77864
+ try {
77865
+ const testOutput = await runPromptTestCommandExecutor({
77866
+ command: options.testCommand,
77867
+ projectPath: options.projectPath,
77868
+ scriptPath: buildTemporaryPromptScriptPath({
77869
+ projectPath: options.projectPath,
77870
+ scriptDirectoryName: 'coder-prompts',
77871
+ sourceFileName: 'test-before',
77872
+ }),
77873
+ });
77874
+ console.info(colors.green('Pre-coding tests passed.'));
77875
+ return { isPassed: true, testOutput };
77876
+ }
77877
+ catch (error) {
77878
+ const testOutput = formatUnknownErrorMessage(error);
77879
+ console.error(colors.red('Pre-coding tests failed.'));
77880
+ console.error(testOutput);
77881
+ return { isPassed: false, testOutput };
77882
+ }
77883
+ }
77884
+
77226
77885
  /**
77227
77886
  * Constant for prompts dir.
77228
77887
  */
@@ -77250,6 +77909,10 @@ async function runCodexPrompts(providedOptions) {
77250
77909
  return;
77251
77910
  }
77252
77911
  const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
77912
+ const promptRunnerIdentity = {
77913
+ harnessName: options.agentName,
77914
+ modelName: actualRunnerModel,
77915
+ };
77253
77916
  console.info(colors.green(`Running prompts with ${runner.name}`));
77254
77917
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
77255
77918
  await initializeRunUiAgentVisual(uiHandle, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentSource);
@@ -77264,6 +77927,7 @@ async function runCodexPrompts(providedOptions) {
77264
77927
  let previousRoundStartTime;
77265
77928
  let previousRoundEndTime;
77266
77929
  let completedRunCount = 0;
77930
+ let hasRunTestBefore = false;
77267
77931
  while (just(true)) {
77268
77932
  if (options.autoPull && !options.dryRun) {
77269
77933
  await waitForRequestedPause({
@@ -77276,6 +77940,35 @@ async function runCodexPrompts(providedOptions) {
77276
77940
  options,
77277
77941
  isRichUiEnabled,
77278
77942
  });
77943
+ if (!hasRunTestBefore && options.testBefore !== 'no') {
77944
+ await waitForRequestedPause({
77945
+ checkpointLabel: 'loading prompts before running initial tests',
77946
+ phase: 'loading',
77947
+ statusMessage: 'Loading prompts before running initial tests...',
77948
+ });
77949
+ await loadPromptQueueSnapshot({
77950
+ options,
77951
+ isRichUiEnabled,
77952
+ progressDisplay,
77953
+ uiHandle,
77954
+ promptRunnerIdentity,
77955
+ });
77956
+ }
77957
+ if (!hasRunTestBefore) {
77958
+ hasWaitedForStart = await runTestBeforeIfNeeded({
77959
+ options,
77960
+ runner,
77961
+ runnerMetadata,
77962
+ resolvedCoderContext,
77963
+ resolvedAgentSystemMessage,
77964
+ isRichUiEnabled,
77965
+ progressDisplay,
77966
+ uiHandle,
77967
+ waitForRequestedPause,
77968
+ hasWaitedForStart,
77969
+ });
77970
+ hasRunTestBefore = true;
77971
+ }
77279
77972
  await waitForRequestedPause({
77280
77973
  checkpointLabel: 'loading prompts',
77281
77974
  phase: 'loading',
@@ -77286,6 +77979,7 @@ async function runCodexPrompts(providedOptions) {
77286
77979
  isRichUiEnabled,
77287
77980
  progressDisplay,
77288
77981
  uiHandle,
77982
+ promptRunnerIdentity,
77289
77983
  });
77290
77984
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
77291
77985
  hasShownUpcomingTasks,
@@ -77293,6 +77987,7 @@ async function runCodexPrompts(providedOptions) {
77293
77987
  stats: promptQueueSnapshot.stats,
77294
77988
  priorityFilter: options.priorityFilter,
77295
77989
  isRichUiEnabled,
77990
+ promptRunnerIdentity,
77296
77991
  }));
77297
77992
  if (!promptQueueSnapshot.nextPrompt) {
77298
77993
  if (isEndAfterCurrentPromptRequested(completedRunCount)) {
@@ -77395,6 +78090,14 @@ async function runCodexPrompts(providedOptions) {
77395
78090
  * Validates cross-flag constraints before the run starts.
77396
78091
  */
77397
78092
  function validateRunCodexPromptOptions(options) {
78093
+ var _a;
78094
+ if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
78095
+ throw new NotAllowed(spaceTrim$1(`
78096
+ Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
78097
+
78098
+ Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
78099
+ `));
78100
+ }
77398
78101
  if (options.allowDestructiveAutoMigrate && !options.autoMigrate) {
77399
78102
  throw new DatabaseError(spaceTrim$1(`
77400
78103
  Flag \`--allow-destructive-auto-migrate\` requires \`--auto-migrate\`.
@@ -77465,20 +78168,103 @@ function createRunDisplays(options, runStartDate) {
77465
78168
  * Normalizes legacy and current priority options into one validated run option shape.
77466
78169
  */
77467
78170
  function normalizeRunOptions(options) {
77468
- var _a, _b, _c, _d, _e;
78171
+ var _a, _b, _c, _d, _e, _f, _g;
78172
+ const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
78173
+ const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
77469
78174
  const priorityFilter = normalizePriorityFilter({
77470
78175
  priority: options.priority,
77471
- minimumPriority: (_a = options.minimumPriority) !== null && _a !== void 0 ? _a : (_b = options.priorityFilter) === null || _b === void 0 ? void 0 : _b.minimumPriority,
77472
- maximumPriority: (_c = options.maximumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.maximumPriority,
78176
+ minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
78177
+ maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
77473
78178
  });
77474
78179
  return {
77475
78180
  ...options,
77476
- priority: (_e = priorityFilter.minimumPriority) !== null && _e !== void 0 ? _e : 0,
78181
+ testBefore,
78182
+ testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
78183
+ priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
77477
78184
  minimumPriority: priorityFilter.minimumPriority,
77478
78185
  maximumPriority: priorityFilter.maximumPriority,
77479
78186
  priorityFilter,
77480
78187
  };
77481
78188
  }
78189
+ /**
78190
+ * Runs the optional pre-coding verification and, when requested, its one repair prompt.
78191
+ */
78192
+ async function runTestBeforeIfNeeded(options) {
78193
+ const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, } = options;
78194
+ if (runOptions.testBefore === 'no') {
78195
+ return hasWaitedForStart;
78196
+ }
78197
+ if (!runOptions.testCommand) {
78198
+ throw new NotAllowed(spaceTrim$1(`
78199
+ ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
78200
+
78201
+ Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
78202
+ `));
78203
+ }
78204
+ if (!runOptions.ignoreGitChanges) {
78205
+ await waitForRequestedPause({
78206
+ checkpointLabel: 'checking the git working tree before testing',
78207
+ phase: 'loading',
78208
+ statusMessage: 'Checking the working tree before testing...',
78209
+ });
78210
+ await ensureWorkingTreeClean();
78211
+ }
78212
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
78213
+ const testBeforeResult = await runTestBefore({
78214
+ testCommand: runOptions.testCommand,
78215
+ projectPath: process.cwd(),
78216
+ waitForPauseCheckpoint: waitForRequestedPause,
78217
+ }).finally(() => {
78218
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
78219
+ });
78220
+ if (testBeforeResult.isPassed) {
78221
+ return hasWaitedForStart;
78222
+ }
78223
+ const testOutput = limitTestOutput(testBeforeResult.testOutput);
78224
+ if (runOptions.testBefore === 'yes-and-fail') {
78225
+ throw new NotAllowed(spaceTrim$1((block) => `
78226
+ Pre-coding verification command \`${runOptions.testCommand}\` failed.
78227
+
78228
+ The coding agent was not started because the project was already failing before the first queued prompt.
78229
+
78230
+ ### Test results
78231
+ ${'```'}
78232
+ ${block(testOutput)}
78233
+ ${'```'}
78234
+ `));
78235
+ }
78236
+ const repairPrompt = await createTestBeforeRepairPrompt({
78237
+ projectPath: process.cwd(),
78238
+ testCommand: runOptions.testCommand,
78239
+ testOutput,
78240
+ });
78241
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
78242
+ const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
78243
+ options: runOptions,
78244
+ nextPrompt: repairPrompt,
78245
+ promptLabel: repairPromptLabel,
78246
+ hasWaitedForStart,
78247
+ isRichUiEnabled,
78248
+ progressDisplay,
78249
+ uiHandle,
78250
+ });
78251
+ // The repair prompt is created in the current worktree so it can be recorded and committed with the repair.
78252
+ // It therefore intentionally uses the regular round here even when the queue itself uses --isolate.
78253
+ await runPromptRound({
78254
+ options: runOptions,
78255
+ runner,
78256
+ runnerMetadata,
78257
+ nextPrompt: repairPrompt,
78258
+ promptLabel: repairPromptLabel,
78259
+ resolvedCoderContext,
78260
+ resolvedAgentSystemMessage,
78261
+ isRichUiEnabled,
78262
+ progressDisplay,
78263
+ uiHandle,
78264
+ waitForRequestedPause,
78265
+ });
78266
+ return updatedHasWaitedForStart;
78267
+ }
77482
78268
  /**
77483
78269
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
77484
78270
  */
@@ -77563,7 +78349,7 @@ async function initializeRunUiAgentVisual(uiHandle, agentSource) {
77563
78349
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
77564
78350
  */
77565
78351
  async function loadPromptQueueSnapshot(options) {
77566
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
78352
+ const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity } = options;
77567
78353
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
77568
78354
  const promptFiles = await loadPromptFiles(PROMPTS_DIR$1);
77569
78355
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
@@ -77575,14 +78361,14 @@ async function loadPromptQueueSnapshot(options) {
77575
78361
  return {
77576
78362
  promptFiles,
77577
78363
  stats,
77578
- nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter),
78364
+ nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter, promptRunnerIdentity),
77579
78365
  };
77580
78366
  }
77581
78367
  /**
77582
78368
  * Prints upcoming tasks only on the first loop iteration in plain-console mode.
77583
78369
  */
77584
78370
  function showUpcomingTasksOnce(options) {
77585
- const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled } = options;
78371
+ const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled, promptRunnerIdentity } = options;
77586
78372
  if (hasShownUpcomingTasks || isRichUiEnabled) {
77587
78373
  return true;
77588
78374
  }
@@ -77591,7 +78377,7 @@ function showUpcomingTasksOnce(options) {
77591
78377
  printPromptsToBeWritten(promptFiles, priorityFilter);
77592
78378
  console.info('');
77593
78379
  }
77594
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter));
78380
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
77595
78381
  return true;
77596
78382
  }
77597
78383
  /**
@@ -77601,7 +78387,10 @@ function finishWhenNoPromptIsAvailable(promptQueueSnapshot, isRichUiEnabled, uiH
77601
78387
  if (promptQueueSnapshot.nextPrompt) {
77602
78388
  return false;
77603
78389
  }
77604
- if (promptQueueSnapshot.stats.toBeWritten > 0) {
78390
+ if (promptQueueSnapshot.stats.forAgent > 0) {
78391
+ announceRunCompletion('No prompts match the selected harness or model.', colors.yellow, isRichUiEnabled, uiHandle);
78392
+ }
78393
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
77605
78394
  announceRunCompletion('No prompts ready for agent.', colors.yellow, isRichUiEnabled, uiHandle);
77606
78395
  }
77607
78396
  else {
@@ -77642,9 +78431,16 @@ function finishWhenEndAfterCurrentPromptIsRequested(options) {
77642
78431
  * Updates the UI status message while waiting for new prompts in keepAlive server mode.
77643
78432
  */
77644
78433
  function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
77645
- const message = promptQueueSnapshot.stats.toBeWritten > 0
77646
- ? 'No prompts ready for agent. Watching for changes...'
77647
- : 'All prompts are done. Watching for changes...';
78434
+ let message;
78435
+ if (promptQueueSnapshot.stats.forAgent > 0) {
78436
+ message = 'No prompts match the selected harness or model. Watching for changes...';
78437
+ }
78438
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
78439
+ message = 'No prompts ready for agent. Watching for changes...';
78440
+ }
78441
+ else {
78442
+ message = 'All prompts are done. Watching for changes...';
78443
+ }
77648
78444
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
77649
78445
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
77650
78446
  if (!isRichUiEnabled) {
@@ -77666,7 +78462,7 @@ function announceRunCompletion(message, colorize, isRichUiEnabled, uiHandle) {
77666
78462
  * Waits for the optional user confirmation before starting the selected prompt.
77667
78463
  */
77668
78464
  async function waitForPromptConfirmationIfNeeded(options) {
77669
- const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle } = options;
78465
+ const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle, } = options;
77670
78466
  if (!runOptions.waitForUser) {
77671
78467
  return hasWaitedForStart;
77672
78468
  }
@@ -78988,14 +79784,21 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
78988
79784
  let promptFiles = initialFiles;
78989
79785
  const skippedFiles = new Set();
78990
79786
  while (true) {
79787
+ // Note: The git synchronization is applied around each single verification, not once per whole run
79788
+ await $pullCoderChanges({ gitSync: normalizedOptions.gitSync });
79789
+ if (normalizedOptions.gitSync.isAutoPullEnabled) {
79790
+ // Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
79791
+ promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79792
+ }
78991
79793
  displayPromptOverview(promptFiles);
78992
79794
  // First priority: verify files where all prompts are marked as done
78993
79795
  const fileWithAllDone = findFileWithAllDonePrompts(promptFiles, skippedFiles);
78994
79796
  if (fileWithAllDone) {
78995
- const wasSkipped = await verifyDonePromptsInFile(fileWithAllDone);
78996
- if (wasSkipped) {
79797
+ const outcome = await verifyDonePromptsInFile(fileWithAllDone);
79798
+ if (outcome.wasSkipped) {
78997
79799
  skippedFiles.add(fileWithAllDone.path);
78998
79800
  }
79801
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
78999
79802
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79000
79803
  continue;
79001
79804
  }
@@ -79005,10 +79808,20 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
79005
79808
  console.info(colors.green('\n✅ All prompts have been verified.'));
79006
79809
  break;
79007
79810
  }
79008
- await resolvePrompt(nextPrompt);
79811
+ const outcome = await resolvePrompt(nextPrompt);
79812
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
79009
79813
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79010
79814
  }
79011
79815
  }
79816
+ /**
79817
+ * Commits and pushes one applied verification when the git synchronization is enabled.
79818
+ */
79819
+ async function $commitVerificationOutcome(gitSync, outcome) {
79820
+ if (outcome.commitMessage === null) {
79821
+ return;
79822
+ }
79823
+ await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
79824
+ }
79012
79825
  /**
79013
79826
  * Parses supported command-line arguments for the standalone verification script.
79014
79827
  */
@@ -79016,6 +79829,11 @@ function parseVerifyPromptsCliOptions(args) {
79016
79829
  return {
79017
79830
  reverse: args.includes('--reverse'),
79018
79831
  ignore: readRepeatableStringOption(args, '--ignore'),
79832
+ gitSync: {
79833
+ isCommitEnabled: args.includes('--commit'),
79834
+ isAutoPushEnabled: args.includes('--auto-push'),
79835
+ isAutoPullEnabled: args.includes('--auto-pull'),
79836
+ },
79019
79837
  };
79020
79838
  }
79021
79839
  /**
@@ -79067,10 +79885,11 @@ async function prepareArchiveDirectory() {
79067
79885
  * Normalizes verification options so the rest of the flow can assume stable defaults.
79068
79886
  */
79069
79887
  function normalizeVerifyPromptsOptions(options) {
79070
- var _a, _b;
79888
+ var _a, _b, _c;
79071
79889
  return {
79072
79890
  reverse: (_a = options.reverse) !== null && _a !== void 0 ? _a : false,
79073
79891
  ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
79892
+ gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
79074
79893
  };
79075
79894
  }
79076
79895
  /**
@@ -79201,7 +80020,6 @@ function findFileWithAllDonePrompts(promptFiles, skippedFiles) {
79201
80020
  /**
79202
80021
  * Verifies the last done [x] prompt in a file and decides whether to archive it or add a repair prompt.
79203
80022
  * Ignores not-ready prompts like [-], [.], [?], etc.
79204
- * Returns true if the file was skipped, false otherwise.
79205
80023
  */
79206
80024
  async function verifyDonePromptsInFile(file) {
79207
80025
  const doneCount = file.sections.filter((s) => s.status === 'done').length;
@@ -79221,31 +80039,44 @@ async function verifyDonePromptsInFile(file) {
79221
80039
  }
79222
80040
  if (!lastDoneSection) {
79223
80041
  console.info(colors.gray('No done [x] prompts found in this file.'));
79224
- return false;
80042
+ return { wasSkipped: false, commitMessage: null };
79225
80043
  }
79226
80044
  console.info(colors.gray('Verifying the last [x] prompt in the file...\n'));
79227
80045
  displayPromptSnippet({ file, section: lastDoneSection });
79228
80046
  const decision = await promptForDoneVerification(file, lastDoneSection);
79229
80047
  if (decision === 'done') {
79230
80048
  await archivePromptFile(file);
79231
- return false;
80049
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(file) };
79232
80050
  }
79233
80051
  else if (decision === 'needs-work') {
79234
80052
  console.info(colors.yellow('\n⚠️ This prompt needs repair.'));
79235
80053
  await appendRepairPrompt(file, lastDoneSection);
79236
- return false;
80054
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(file) };
79237
80055
  }
79238
80056
  else {
79239
80057
  console.info(colors.gray('\n⏩ Skipped, no changes made.'));
79240
- return true;
80058
+ return { wasSkipped: true, commitMessage: null };
79241
80059
  }
79242
80060
  }
80061
+ /**
80062
+ * Builds the commit message describing one archived prompt file.
80063
+ */
80064
+ function buildArchiveCommitMessage(file) {
80065
+ return `✅ Prompt done and archived \`${file.name}\``; // <- $commitCoderChanges({
80066
+ }
80067
+ /**
80068
+ * Builds the commit message describing one appended repair prompt.
80069
+ */
80070
+ function buildRepairCommitMessage(file) {
80071
+ return `❌ Repair prompt added into \`${file.name}\``; // <- $commitCoderChanges({
80072
+ }
79243
80073
  /**
79244
80074
  * Asks the user to verify if a done prompt is actually completed.
79245
80075
  * Returns 'done' if verified, 'needs-work' if not done, or 'skip' to skip this file.
79246
80076
  */
79247
80077
  async function promptForDoneVerification(file, section) {
79248
80078
  const promptLabel = buildPromptLabelForDisplay(file, section);
80079
+ const { default: prompts } = await loadPromptsModule();
79249
80080
  const response = await prompts({
79250
80081
  type: 'select',
79251
80082
  name: 'verified',
@@ -79314,16 +80145,17 @@ async function resolvePrompt(selection) {
79314
80145
  const decision = await promptForDecision(selection);
79315
80146
  if (decision === 'done') {
79316
80147
  await archivePromptFile(selection.file);
80148
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(selection.file) };
79317
80149
  }
79318
- else {
79319
- await appendRepairPrompt(selection.file, selection.section);
79320
- }
80150
+ await appendRepairPrompt(selection.file, selection.section);
80151
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(selection.file) };
79321
80152
  }
79322
80153
  /**
79323
80154
  * Presents the interactive decision menu for the current prompt section.
79324
80155
  */
79325
80156
  async function promptForDecision(selection) {
79326
80157
  const promptLabel = buildPromptLabelForDisplay(selection.file, selection.section);
80158
+ const { default: prompts } = await loadPromptsModule();
79327
80159
  const response = await prompts({
79328
80160
  type: 'select',
79329
80161
  name: 'decision',