@promptbook/cli 0.114.0-0 → 0.114.0-2

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 (113) hide show
  1. package/README.md +16 -15
  2. package/apps/agents-server/package.json +6 -4
  3. package/apps/agents-server/playwright.config.ts +28 -38
  4. package/apps/agents-server/scripts/build-agents-server.js +62 -0
  5. package/apps/agents-server/scripts/build-e2e.js +38 -0
  6. package/apps/agents-server/scripts/ensure-root-dependencies.js +86 -0
  7. package/apps/agents-server/scripts/prerender-homepage.js +1 -1
  8. package/apps/agents-server/scripts/run-e2e-tests.js +34 -0
  9. package/apps/agents-server/scripts/run-npm.js +42 -0
  10. package/apps/agents-server/src/app/admin/email-server/page.tsx +14 -6
  11. package/apps/agents-server/src/app/api/elevenlabs/tts/route.ts +3 -3
  12. package/apps/agents-server/src/app/api/onboarding/book/route.ts +7 -6
  13. package/apps/agents-server/src/app/superadmin/servers/ServersRegistryTable.tsx +105 -102
  14. package/apps/agents-server/src/app/superadmin/servers/useServersRegistryState.ts +15 -7
  15. package/apps/agents-server/src/components/AgentProjectDnsInstructions/AgentProjectDnsInstructions.tsx +11 -11
  16. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ManGoBookEditor.tsx +82 -0
  17. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/WizardShell.tsx +2 -2
  18. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/BookStep.tsx +13 -16
  19. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/ZadaniStep.tsx +4 -4
  20. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/bookSections.ts +23 -16
  21. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/bookSource.ts +54 -0
  22. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/bookService.ts +15 -8
  23. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoAgentSource.ts +3 -37
  24. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoOpenEditorRequest.ts +1 -1
  25. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/state/OnboardingProvider.tsx +2 -2
  26. package/apps/agents-server/src/components/UsersList/CreateUserDialog.tsx +13 -22
  27. package/apps/agents-server/src/components/UsersList/PasswordGeneratorDialog.tsx +216 -0
  28. package/apps/agents-server/src/components/UsersList/UsersList.tsx +16 -11
  29. package/apps/agents-server/src/components/UsersList/generateSecurePassword.ts +112 -24
  30. package/apps/agents-server/src/components/UsersList/userDialogClassNames.ts +11 -0
  31. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +11 -0
  32. package/apps/agents-server/src/languages/translations/czech.yaml +11 -0
  33. package/apps/agents-server/src/languages/translations/english.yaml +11 -0
  34. package/apps/agents-server/src/utils/agentProjects/agentProjectHrefs.ts +11 -0
  35. package/apps/agents-server/src/utils/agentProjects/createAgentProjectDnsRecords.ts +53 -7
  36. package/apps/agents-server/src/utils/agentProjects/createAgentProjectMarkdownReferences.ts +37 -27
  37. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentBooks.ts +2 -13
  38. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentRuntime.ts +21 -73
  39. package/apps/agents-server/src/utils/stalwart/createEmailDnsInstructions.ts +9 -5
  40. package/apps/agents-server/src/utils/stalwart/readStalwartEmailSnapshot.ts +23 -5
  41. package/apps/agents-server/src/utils/stalwart/stalwartBootstrap.ts +2 -1
  42. package/apps/agents-server/src/utils/stalwart/stalwartMailBridge.ts +7 -0
  43. package/apps/agents-server/tests/e2e/e2eEnvironment.cjs +49 -0
  44. package/esm/index.es.js +523 -169
  45. package/esm/index.es.js.map +1 -1
  46. package/esm/scripts/run-agent-chat/runCoderPing.d.ts +48 -0
  47. package/esm/scripts/run-codex-prompts/prompts/findNextTodoPrompt.d.ts +2 -1
  48. package/esm/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +27 -0
  49. package/esm/scripts/run-codex-prompts/prompts/listRunnablePrompts.d.ts +2 -1
  50. package/esm/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +2 -1
  51. package/esm/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +7 -0
  52. package/esm/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +16 -0
  53. package/esm/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +9 -0
  54. package/esm/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +4 -0
  55. package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -2
  56. package/esm/scripts/run-codex-prompts/testing/runTestBefore.d.ts +24 -0
  57. package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +1 -0
  58. package/esm/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.d.ts +1 -15
  59. package/esm/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.d.ts +1 -1
  60. package/esm/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.d.ts +0 -4
  61. package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +7 -0
  62. package/esm/src/cli/cli-commands/coder/agentCodingFile.d.ts +2 -3
  63. package/esm/src/cli/cli-commands/coder/ping.d.ts +13 -0
  64. package/esm/src/cli/cli-commands/coder/ping.test.d.ts +1 -0
  65. package/esm/src/cli/cli-commands/coder.d.ts +1 -0
  66. package/package.json +1 -1
  67. package/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.ts +1 -15
  68. package/src/book-2.0/book-language-documentation/bookLanguageDocumentationExamples.ts +1 -2
  69. package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +92 -32
  70. package/src/book-2.0/book-language-documentation/czechBookLanguageManualDictionary.ts +16 -39
  71. package/src/book-2.0/book-language-documentation/englishBookLanguageManualDictionary.ts +16 -36
  72. package/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.ts +9 -1
  73. package/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.ts +4 -51
  74. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +291 -4
  75. package/src/book-components/Chat/utils/renderMarkdown.ts +133 -9
  76. package/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.ts +1 -0
  77. package/src/cli/cli-commands/coder/agentCodingFile.ts +6 -129
  78. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  79. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  80. package/src/cli/cli-commands/coder/ping.ts +94 -0
  81. package/src/cli/cli-commands/coder/run.ts +25 -2
  82. package/src/cli/cli-commands/coder.ts +4 -0
  83. package/src/other/templates/getTemplatesPipelineCollection.ts +805 -842
  84. package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +101 -0
  85. package/src/version.ts +1 -1
  86. package/src/versions.txt +1 -1
  87. package/umd/index.umd.js +525 -171
  88. package/umd/index.umd.js.map +1 -1
  89. package/umd/scripts/run-agent-chat/runCoderPing.d.ts +48 -0
  90. package/umd/scripts/run-codex-prompts/prompts/findNextTodoPrompt.d.ts +2 -1
  91. package/umd/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +27 -0
  92. package/umd/scripts/run-codex-prompts/prompts/listRunnablePrompts.d.ts +2 -1
  93. package/umd/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +2 -1
  94. package/umd/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +7 -0
  95. package/umd/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +16 -0
  96. package/umd/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +9 -0
  97. package/umd/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +4 -0
  98. package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -2
  99. package/umd/scripts/run-codex-prompts/testing/runTestBefore.d.ts +24 -0
  100. package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +1 -0
  101. package/umd/src/book-2.0/book-language-documentation/BookLanguageManualDictionary.d.ts +1 -15
  102. package/umd/src/book-2.0/book-language-documentation/getBookLanguageManualCommitmentGroups.d.ts +1 -1
  103. package/umd/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.d.ts +0 -4
  104. package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +7 -0
  105. package/umd/src/cli/cli-commands/coder/agentCodingFile.d.ts +2 -3
  106. package/umd/src/cli/cli-commands/coder/ping.d.ts +13 -0
  107. package/umd/src/cli/cli-commands/coder/ping.test.d.ts +1 -0
  108. package/umd/src/cli/cli-commands/coder.d.ts +1 -0
  109. package/apps/agents-server/src/app/api/onboarding/draft/route.ts +0 -23
  110. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/BookLanguagePanel.tsx +0 -125
  111. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownBookEditor.tsx +0 -154
  112. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownPreview.tsx +0 -188
  113. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/draftService.ts +0 -32
package/umd/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('crypto'), require('child_process'), require('moment'), require('fs'), require('dotenv'), require('prompts'), require('readline'), require('waitasecond'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('socket.io-client'), require('jszip'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('rxjs'), require('@openai/agents'), require('openai'), require('typescript'), require('ignore'), require('events'), require('os'), require('mime-types'), require('papaparse'), require('pg'), require('@supabase/supabase-js'), require('url')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'crypto', 'child_process', 'moment', 'fs', 'dotenv', 'prompts', 'readline', 'waitasecond', 'crypto-js/enc-hex', 'crypto-js/sha256', 'socket.io-client', 'jszip', '@mozilla/readability', 'jsdom', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'rxjs', '@openai/agents', 'openai', 'typescript', 'ignore', 'events', 'os', 'mime-types', 'papaparse', 'pg', '@supabase/supabase-js', 'url'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.crypto, global.child_process, global.moment, global.fs, global.dotenv, global.prompts, global.readline, global.waitasecond, global.hexEncoder, global.sha256, global.socket_ioClient, global.JSZip, global.readability, global.jsdom, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.rxjs, global.agents, global.OpenAI, global.ts, global.ignore, global.events, global.os, global.mimeTypes, global.papaparse, global.pg, null, global.url));
5
- })(this, (function (exports, colors, commander, _spaceTrim, promises, path, crypto, child_process, moment, fs, dotenv, prompts, readline, waitasecond, hexEncoder, sha256, socket_ioClient, JSZip, readability, jsdom, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Anthropic, Bottleneck, openai, rxjs, agents, OpenAI, ts, ignore, events, os, mimeTypes, papaparse, pg, supabaseJs, url) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('crypto'), require('child_process'), require('moment'), require('fs'), require('dotenv'), require('readline'), require('waitasecond'), require('prompts'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('socket.io-client'), require('jszip'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('rxjs'), require('@openai/agents'), require('openai'), require('typescript'), require('ignore'), require('os'), require('events'), require('mime-types'), require('papaparse'), require('pg'), require('@supabase/supabase-js'), require('url')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'crypto', 'child_process', 'moment', 'fs', 'dotenv', 'readline', 'waitasecond', 'prompts', 'crypto-js/enc-hex', 'crypto-js/sha256', 'socket.io-client', 'jszip', '@mozilla/readability', 'jsdom', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'rxjs', '@openai/agents', 'openai', 'typescript', 'ignore', 'os', 'events', 'mime-types', 'papaparse', 'pg', '@supabase/supabase-js', 'url'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.crypto, global.child_process, global.moment, global.fs, global.dotenv, global.readline, global.waitasecond, global.prompts, global.hexEncoder, global.sha256, global.socket_ioClient, global.JSZip, global.readability, global.jsdom, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.rxjs, global.agents, global.OpenAI, global.ts, global.ignore, global.os, global.events, global.mimeTypes, global.papaparse, global.pg, null, global.url));
5
+ })(this, (function (exports, colors, commander, _spaceTrim, promises, path, crypto, child_process, moment, fs, dotenv, readline, waitasecond, prompts, hexEncoder, sha256, socket_ioClient, JSZip, readability, jsdom, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Anthropic, Bottleneck, openai, rxjs, agents, OpenAI, ts, ignore, os, events, mimeTypes, papaparse, pg, supabaseJs, url) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -30,8 +30,8 @@
30
30
  var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
31
31
  var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
32
32
  var dotenv__namespace = /*#__PURE__*/_interopNamespace(dotenv);
33
- var prompts__default = /*#__PURE__*/_interopDefaultLegacy(prompts);
34
33
  var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
34
+ var prompts__default = /*#__PURE__*/_interopDefaultLegacy(prompts);
35
35
  var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
36
36
  var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
37
37
  var JSZip__default = /*#__PURE__*/_interopDefaultLegacy(JSZip);
@@ -63,7 +63,7 @@
63
63
  * @generated
64
64
  * @see https://github.com/webgptorg/promptbook
65
65
  */
66
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-0';
66
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-2';
67
67
  /**
68
68
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
69
69
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -26970,12 +26970,12 @@
26970
26970
  * Builds the coder-run control pills shown in the footer box.
26971
26971
  */
26972
26972
  function buildCoderRunControlPills(options) {
26973
- const { pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26973
+ const { phase, pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26974
26974
  return buildControlPills({
26975
26975
  pauseControl,
26976
26976
  pendingEnterLabel,
26977
26977
  additionalControls: [
26978
- buildSkipCurrentWaitControl(),
26978
+ ...(phase === 'waiting' ? [buildSkipCurrentWaitControl()] : []),
26979
26979
  buildEndAfterCurrentPromptControl(isEndAfterCurrentPromptRequested, sessionTotal),
26980
26980
  ],
26981
26981
  });
@@ -29556,6 +29556,7 @@
29556
29556
  ` -c model_reasoning_effort="${thinkingLevel}" \\`,
29557
29557
  ` --ask-for-approval ${options.askForApproval} \\`,
29558
29558
  ` exec --model ${options.model} \\`,
29559
+ ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
29559
29560
  ' --local-provider none \\',
29560
29561
  ` --sandbox ${options.sandbox} \\`,
29561
29562
  ` -C ${projectPath} \\`,
@@ -29872,6 +29873,10 @@
29872
29873
  * Output line that marks finished Codex usage summary.
29873
29874
  */
29874
29875
  const CODEX_COMPLETION_LINE = /^\s*tokens used\b/i;
29876
+ /**
29877
+ * Output line that marks the completed Codex turn in JSONL output mode.
29878
+ */
29879
+ const CODEX_JSON_COMPLETION_LINE = /^\s*\{"type":"turn\.(?:completed|failed)"/u;
29875
29880
  /**
29876
29881
  * Idle timeout after completion marker to capture trailing output.
29877
29882
  */
@@ -29948,6 +29953,7 @@
29948
29953
  sandbox: this.options.sandbox,
29949
29954
  askForApproval: this.options.askForApproval,
29950
29955
  allowCredits: this.options.allowCredits,
29956
+ isMachineReadableProgressEnabled: this.options.isMachineReadableProgressEnabled,
29951
29957
  codexCommand: this.options.codexCommand,
29952
29958
  });
29953
29959
  for (let retryIndex = 0;; retryIndex++) {
@@ -29962,7 +29968,9 @@
29962
29968
  const output = await $runGoScriptUntilMarkerIdle({
29963
29969
  scriptPath: options.scriptPath,
29964
29970
  scriptContent,
29965
- completionLineMatcher: CODEX_COMPLETION_LINE,
29971
+ completionLineMatcher: this.options.isMachineReadableProgressEnabled
29972
+ ? CODEX_JSON_COMPLETION_LINE
29973
+ : CODEX_COMPLETION_LINE,
29966
29974
  idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
29967
29975
  logPath: options.logPath,
29968
29976
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
@@ -30212,6 +30220,7 @@
30212
30220
  sandbox: 'danger-full-access',
30213
30221
  askForApproval: 'never',
30214
30222
  allowCredits: options.allowCredits,
30223
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
30215
30224
  });
30216
30225
  if (!options.allowCredits && options.isVerbose === true) {
30217
30226
  console.info(colors__default["default"].gray('OpenAI Codex credit spending is disabled. Use `--allow-credits` to explicitly opt in.'));
@@ -31447,6 +31456,7 @@
31447
31456
  : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
31448
31457
  const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
31449
31458
  const controls = buildCoderRunControlPills({
31459
+ phase: options.phase,
31450
31460
  pauseControl: pausePresentation.pauseControl,
31451
31461
  pendingEnterLabel: options.pendingEnterLabel,
31452
31462
  isEndAfterCurrentPromptRequested: options.isEndAfterCurrentPromptRequested,
@@ -32310,6 +32320,7 @@
32310
32320
  autoPull: options.autoPull,
32311
32321
  agentName: options.agentName,
32312
32322
  model: options.model,
32323
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
32313
32324
  priority: 0,
32314
32325
  };
32315
32326
  }
@@ -33299,6 +33310,21 @@
33299
33310
  `);
33300
33311
  }
33301
33312
 
33313
+ /**
33314
+ * Maximum amount of test output embedded into an agent prompt.
33315
+ */
33316
+ const MAX_TEST_OUTPUT_CHARS = 12000;
33317
+ /**
33318
+ * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
33319
+ */
33320
+ function limitTestOutput(testOutput) {
33321
+ const normalizedTestOutput = testOutput.trim();
33322
+ if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
33323
+ return normalizedTestOutput;
33324
+ }
33325
+ return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
33326
+ }
33327
+
33302
33328
  /**
33303
33329
  * Runs the configured verification command inside the project root and returns its output.
33304
33330
  */
@@ -33319,10 +33345,6 @@
33319
33345
  * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33320
33346
  */
33321
33347
  const MAX_PROMPT_TEST_ATTEMPTS = 3;
33322
- /**
33323
- * Maximum amount of verification output sent back to the coding agent as retry feedback.
33324
- */
33325
- const MAX_TEST_FEEDBACK_OUTPUT_CHARS = 12000;
33326
33348
  /**
33327
33349
  * File extension used by generated shell scripts.
33328
33350
  */
@@ -33368,7 +33390,7 @@
33368
33390
  return { ...result, attemptCount, steps };
33369
33391
  }
33370
33392
  const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
33371
- const feedbackVerificationOutput = limitVerificationOutputForFeedback(fullVerificationOutput);
33393
+ const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
33372
33394
  if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
33373
33395
  console.error(colors__default["default"].red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33374
33396
  throw new Error(buildFinalVerificationFailureMessage({
@@ -33498,19 +33520,6 @@
33498
33520
  \`\`\`
33499
33521
  `);
33500
33522
  }
33501
- /**
33502
- * Limits verification output before it is embedded back into the next coding prompt.
33503
- */
33504
- function limitVerificationOutputForFeedback(verificationOutput) {
33505
- const normalizedVerificationOutput = verificationOutput.trim();
33506
- if (normalizedVerificationOutput.length <= MAX_TEST_FEEDBACK_OUTPUT_CHARS) {
33507
- return normalizedVerificationOutput;
33508
- }
33509
- return spaceTrim(`
33510
- [...verification output truncated to the last ${MAX_TEST_FEEDBACK_OUTPUT_CHARS} characters...]
33511
- ${normalizedVerificationOutput.slice(-MAX_TEST_FEEDBACK_OUTPUT_CHARS)}
33512
- `);
33513
- }
33514
33523
  /**
33515
33524
  * Derives a dedicated temp-script path for verification commands.
33516
33525
  */
@@ -41371,6 +41380,7 @@
41371
41380
  autoClone: false,
41372
41381
  maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
41373
41382
  maxParallelMessages: localAgentRunnerLimits.maxParallelMessages,
41383
+ isMachineReadableProgressEnabled: true,
41374
41384
  };
41375
41385
  }
41376
41386
 
@@ -43485,6 +43495,27 @@
43485
43495
  }
43486
43496
  // Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
43487
43497
 
43498
+ /**
43499
+ * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43500
+ *
43501
+ * @private internal utility of `ptbk coder`
43502
+ */
43503
+ const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43504
+ /**
43505
+ * Returns the default coder `AGENT_CODING.md` quick-reference content.
43506
+ *
43507
+ * @private internal utility of `ptbk coder`
43508
+ */
43509
+ function getDefaultCoderAgentCodingFileContent() {
43510
+ return _spaceTrim.spaceTrim(`
43511
+ # ✨ Promptbook Coder agent coding
43512
+
43513
+ This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
43514
+ `);
43515
+ }
43516
+ // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43517
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
43518
+
43488
43519
  /**
43489
43520
  * Relative path to the shared coder context file initialized in project roots.
43490
43521
  *
@@ -43512,106 +43543,6 @@
43512
43543
  // Note: [🟡] Code for coder AGENTS file boilerplate [agentsFile](src/cli/cli-commands/coder/agentsFile.ts) should never be published outside of `@promptbook/cli`
43513
43544
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
43514
43545
 
43515
- /**
43516
- * Normalizes one project-relative path for human-readable CLI output and markdown.
43517
- *
43518
- * @private internal utility of `ptbk coder`
43519
- */
43520
- function formatDisplayPath(relativePath) {
43521
- return relativePath.replace(/\\/gu, '/');
43522
- }
43523
- // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43524
-
43525
- /**
43526
- * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43527
- *
43528
- * @private internal utility of `ptbk coder`
43529
- */
43530
- const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43531
- /**
43532
- * Returns the default coder `AGENT_CODING.md` quick-reference content.
43533
- *
43534
- * @private internal utility of `ptbk coder`
43535
- */
43536
- function getDefaultCoderAgentCodingFileContent({ packageJsonScripts, }) {
43537
- return _spaceTrim.spaceTrim((block) => `
43538
- # Promptbook Coder quick reference
43539
-
43540
- This project is prepared for the \`ptbk coder\` workflow. Promptbook Coder does not create a new model on its own; it orchestrates coding agents such as GitHub Copilot, OpenAI Codex, Claude Code, Opencode, Cline, and Gemini CLI through prompt files in \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\`.
43541
-
43542
- ## Workflow
43543
- 1. Put repository-wide coding rules into \`${AGENTS_FILE_PATH}\`. The default \`npm run coder:run\` script already passes \`--context ${AGENTS_FILE_PATH}\`.
43544
- 2. Create or customize prompt templates in \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/\`. ${buildStarterTemplateSentence()}
43545
- 3. Generate prompt files with \`npm run coder:generate-boilerplates\` or \`ptbk coder generate-boilerplates --template <template> --count <count>\`. To append a single ready-to-run prompt straight from a description, use \`npm run coder:add\` or \`ptbk coder add "some new feature"\`.
43546
- 4. Replace every \`@@@\`, keep drafts as \`[-]\`, and switch prompts to \`[ ]\` when they are ready to run. Completed prompts are marked \`[x]\`.
43547
- 5. Run \`npm run coder:run\` to execute the next ready prompt with the configured coding agent.
43548
- 6. Use \`npm run coder:verify\` to archive finished prompts into \`${formatDisplayPath(PROMPTS_DONE_DIRECTORY_PATH)}/\` and append repair follow-up prompts when more work is needed.
43549
- 7. Use \`ptbk coder find-refactor-candidates\` when you want Promptbook to suggest refactor prompts automatically.
43550
-
43551
- ## Templates
43552
- - Project-owned templates created by \`ptbk coder init\`: ${formatInlineCodeList(getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath)))}
43553
- - Built-in \`--template\` aliases: ${formatInlineCodeList(getDefaultCoderPromptTemplateDefinitions().map(({ id }) => id))}
43554
- - To add a custom template, create a markdown file such as \`${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43555
- - To use a project template, run \`ptbk coder generate-boilerplates --template ${formatDisplayPath(PROMPTS_TEMPLATES_DIRECTORY_PATH)}/backend.md\`.
43556
- - Keep shared repository rules in \`${AGENTS_FILE_PATH}\` and recurring task-family rules in template files so individual prompt files stay focused on the actual task.
43557
-
43558
- ## Created npm scripts
43559
- | Script | Purpose |
43560
- | --- | --- |
43561
- ${block(buildPackageJsonScriptTableLines(packageJsonScripts).join('\n'))}
43562
-
43563
- ## Customizing the workflow
43564
- - Edit \`package.json\` if you want \`npm run coder:run\` to use another coding agent, model, thinking level, context file, or wait mode.
43565
- - Use direct CLI commands when you need one-off flags such as \`--min-priority\`, \`--max-priority\`, \`--ignore-git-changes\`, \`--no-commit\`, \`--dry-run\`, \`--test\`, \`--allow-credits\`, or \`--auto-migrate\`.
43566
- - Use \`ptbk coder --help\` and \`ptbk coder <command> --help\` for the full CLI reference.
43567
- `);
43568
- }
43569
- /**
43570
- * Builds the sentence describing the starter templates created during initialization.
43571
- */
43572
- function buildStarterTemplateSentence() {
43573
- const starterTemplatePaths = getDefaultCoderProjectPromptTemplateDefinitions().map(({ relativeFilePath }) => formatDisplayPath(relativeFilePath));
43574
- if (starterTemplatePaths.length === 1) {
43575
- return `The starter project template created by \`ptbk coder init\` is \`${starterTemplatePaths[0]}\`.`;
43576
- }
43577
- return `The starter project templates created by \`ptbk coder init\` are ${formatInlineCodeList(starterTemplatePaths)}.`;
43578
- }
43579
- /**
43580
- * Builds the markdown table rows describing the initialized npm scripts.
43581
- */
43582
- function buildPackageJsonScriptTableLines(packageJsonScripts) {
43583
- return Object.entries(packageJsonScripts).map(([scriptName, scriptCommand]) => `| \`npm run ${scriptName}\` | ${describeDefaultCoderPackageJsonScript(scriptName, scriptCommand)} |`);
43584
- }
43585
- /**
43586
- * Describes one initialized npm script in human-readable terms.
43587
- */
43588
- function describeDefaultCoderPackageJsonScript(scriptName, scriptCommand) {
43589
- if (scriptName === 'coder:generate-boilerplates') {
43590
- return `Runs \`${scriptCommand}\` to create new prompt files in \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\`.`;
43591
- }
43592
- if (scriptName === 'coder:add') {
43593
- return `Runs \`${scriptCommand}\` to add one ready-to-run prompt file to \`${formatDisplayPath(PROMPTS_DIRECTORY_PATH)}/\` from a plain-language description.`;
43594
- }
43595
- if (scriptName === 'coder:run') {
43596
- return `Runs \`${scriptCommand}\` to execute the next ready prompt with shared repository context from \`${AGENTS_FILE_PATH}\`.`;
43597
- }
43598
- if (scriptName === 'coder:find-refactor-candidates') {
43599
- return `Runs \`${scriptCommand}\` to generate prompt candidates for large or crowded files.`;
43600
- }
43601
- if (scriptName === 'coder:verify') {
43602
- return `Runs \`${scriptCommand}\` to archive verified prompts into \`${formatDisplayPath(PROMPTS_DONE_DIRECTORY_PATH)}/\` and append repair prompts when needed.`;
43603
- }
43604
- return `Runs \`${scriptCommand}\`.`;
43605
- }
43606
- /**
43607
- * Formats one inline code list for human-readable markdown.
43608
- */
43609
- function formatInlineCodeList(values) {
43610
- return values.map((value) => `\`${value}\``).join(', ');
43611
- }
43612
- // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43613
- // Note: [💞] Ignore a discrepancy between file name and exported helper names
43614
-
43615
43546
  /**
43616
43547
  * Relative directory path for agents initialized by `ptbk coder init`.
43617
43548
  *
@@ -43676,6 +43607,16 @@
43676
43607
  }
43677
43608
  // Note: [🟡] Code for coder init developer agent bootstrapping [ensureCoderDeveloperAgentFile](src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts) should never be published outside of `@promptbook/cli`
43678
43609
 
43610
+ /**
43611
+ * Normalizes one project-relative path for human-readable CLI output and markdown.
43612
+ *
43613
+ * @private internal utility of `ptbk coder`
43614
+ */
43615
+ function formatDisplayPath(relativePath) {
43616
+ return relativePath.replace(/\\/gu, '/');
43617
+ }
43618
+ // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43619
+
43679
43620
  /**
43680
43621
  * Fallback `.env` content used when no required variables need to be appended.
43681
43622
  */
@@ -43753,7 +43694,7 @@
43753
43694
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
43754
43695
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
43755
43696
  'coder:add': 'ptbk coder add --template ./prompts/templates/common.md',
43756
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.5 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
43697
+ 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max --agent agents/developer.book --context AGENTS.md --test-before yes-and-fix',
43757
43698
  // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
43758
43699
  'coder:verify': 'ptbk coder verify',
43759
43700
  };
@@ -44002,9 +43943,7 @@
44002
43943
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
44003
43944
  const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
44004
43945
  const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
44005
- const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent({
44006
- packageJsonScripts: getDefaultCoderPackageJsonScripts(),
44007
- }));
43946
+ const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
44008
43947
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44009
43948
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44010
43949
  const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
@@ -44145,6 +44084,66 @@
44145
44084
  // Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
44146
44085
  // Note: [💞] Ignore a discrepancy between file name and entity name
44147
44086
 
44087
+ /**
44088
+ * Initializes `coder ping` command for Promptbook CLI utilities.
44089
+ *
44090
+ * The command makes one small, isolated harness/model call, prints its result
44091
+ * and reports how long the harness/model turn took.
44092
+ *
44093
+ * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI.
44094
+ *
44095
+ * @private internal function of `promptbookCli`
44096
+ */
44097
+ function $initializeCoderPingCommand(program) {
44098
+ const command = program.command('ping');
44099
+ command.description(_spaceTrim.spaceTrim(`
44100
+ Test one harness and model connection with a small disposable task
44101
+
44102
+ ${PROMPT_RUNNER_DESCRIPTION}
44103
+
44104
+ Features:
44105
+ - Runs the dummy task in a temporary directory outside the current project
44106
+ - Prints the result returned by the selected harness and model
44107
+ - Reports the elapsed harness/model response time in milliseconds
44108
+ - Can be used to start consuming an applicable harness/model quota before a longer run
44109
+ `));
44110
+ addPromptRunnerSelectionOptions(command);
44111
+ addPromptRunnerRuntimeOptions(command);
44112
+ command.action(handleActionErrors(async (cliOptions) => {
44113
+ const normalizedOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44114
+ const agentName = normalizedOptions.agentName;
44115
+ if (!agentName) {
44116
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
44117
+ A harness is required for \`ptbk coder ping\`.
44118
+
44119
+ Pass one with \`--harness <harness-name>\`.
44120
+ `));
44121
+ }
44122
+ await $ensureHarnessInstallations([agentName]);
44123
+ // Note: Import the runner-backed implementation dynamically to avoid loading heavy dependencies until needed.
44124
+ const { runCoderPing } = await Promise.resolve().then(function () { return runCoderPing$1; });
44125
+ const pingResult = await runCoderPing({
44126
+ agentName,
44127
+ model: normalizedOptions.model,
44128
+ thinkingLevel: normalizedOptions.thinkingLevel,
44129
+ isUiDisabled: normalizedOptions.noUi,
44130
+ isCreditsAllowed: normalizedOptions.allowCredits,
44131
+ });
44132
+ printCoderPingResult(pingResult);
44133
+ }));
44134
+ }
44135
+ /**
44136
+ * Prints the result and duration of one completed coder ping.
44137
+ */
44138
+ function printCoderPingResult(pingResult) {
44139
+ console.info(_spaceTrim.spaceTrim(`
44140
+ Result: ${pingResult.result}
44141
+ Time: ${pingResult.elapsedTimeMs.toFixed(0)} ms
44142
+ `));
44143
+ }
44144
+ // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44145
+ // Note: [💞] Ignore a discrepancy between file name and entity name
44146
+
44148
44147
  /**
44149
44148
  * Creates a Commander argument parser that accepts only non-negative integers.
44150
44149
  *
@@ -44169,6 +44168,21 @@
44169
44168
  }
44170
44169
  // Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
44171
44170
 
44171
+ /**
44172
+ * Modes supported by `ptbk coder run --test-before`.
44173
+ */
44174
+ const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
44175
+ /**
44176
+ * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
44177
+ */
44178
+ const DEFAULT_CODER_TEST_COMMAND = 'npm test';
44179
+ /**
44180
+ * Checks whether a value is a supported `--test-before` mode.
44181
+ */
44182
+ function isTestBeforeMode(value) {
44183
+ return TEST_BEFORE_MODE_VALUES.includes(value);
44184
+ }
44185
+
44172
44186
  /**
44173
44187
  * Default wait duration applied before retrying a prompt round after an error (10 minutes).
44174
44188
  *
@@ -44213,6 +44227,7 @@
44213
44227
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44214
44228
  - Checks that the selected harness is installed globally and up to date before the first prompt
44215
44229
  - Supports GPG signing of commits
44230
+ - Optional pre-coding test run that can stop or repair pre-existing failures
44216
44231
  - Optional post-prompt verification with test-feedback retries
44217
44232
  - Progress tracking and interactive P/S/X terminal controls
44218
44233
  - Dry-run mode to preview prompts
@@ -44222,6 +44237,9 @@
44222
44237
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
44223
44238
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
44224
44239
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
44240
+ command.addOption(new commander.Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
44241
+ .choices([...TEST_BEFORE_MODE_VALUES])
44242
+ .default('no'));
44225
44243
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
44226
44244
  addPromptRunnerExecutionOptions(command);
44227
44245
  command.option('--isolate', _spaceTrim.spaceTrim(`
@@ -44251,8 +44269,9 @@
44251
44269
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
44252
44270
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
44253
44271
  command.action(handleActionErrors(async (cliOptions) => {
44254
- const { dryRun, agent, context, test, preserveLogs, isolate: isIsolated, priority, minPriority: minimumPriority, maxPriority: maximumPriority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
44255
- const testCommand = normalizeCommandOptionValue$1(test);
44272
+ 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;
44273
+ const configuredTestCommand = normalizeCommandOptionValue$1(test);
44274
+ const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
44256
44275
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
44257
44276
  isAgentRequired: !dryRun,
44258
44277
  });
@@ -44281,6 +44300,7 @@
44281
44300
  agent,
44282
44301
  context,
44283
44302
  testCommand,
44303
+ testBefore,
44284
44304
  preserveLogs,
44285
44305
  isIsolated,
44286
44306
  noUi: runnerOptions.noUi,
@@ -44537,6 +44557,7 @@
44537
44557
  * - add: Add one ready-to-run prompt file to the queue
44538
44558
  * - generate-boilerplates: Generate prompt boilerplate files
44539
44559
  * - find-refactor-candidates: Find files that need refactoring
44560
+ * - ping: Test one harness and model connection with a disposable task
44540
44561
  * - run: Run coding prompts with AI agents
44541
44562
  * - verify: Verify completed prompts
44542
44563
  * - find-fresh-emoji-tags: Find unused emoji tags
@@ -44556,6 +44577,7 @@
44556
44577
  - generate-boilerplates: Generate prompt boilerplate files
44557
44578
  - find-refactor-candidates: Find files that need refactoring
44558
44579
  - find-unwritten: List prompt sections that still need to be authored
44580
+ - ping: Test one harness and model connection with a disposable task
44559
44581
  - run: Run coding prompts with AI agents
44560
44582
  - server: Start a long-running coder server with a kanban web UI
44561
44583
  - verify: Verify completed prompts
@@ -44567,6 +44589,7 @@
44567
44589
  $initializeCoderGenerateBoilerplatesCommand(coderCommand);
44568
44590
  $initializeCoderFindRefactorCandidatesCommand(coderCommand);
44569
44591
  $initializeCoderFindUnwrittenCommand(coderCommand);
44592
+ $initializeCoderPingCommand(coderCommand);
44570
44593
  $initializeCoderRunCommand(coderCommand);
44571
44594
  $initializeCoderServerCommand(coderCommand);
44572
44595
  $initializeCoderVerifyCommand(coderCommand);
@@ -73593,6 +73616,41 @@
73593
73616
  isPromptInPriorityFilter(prompt.section, priorityFilter));
73594
73617
  }
73595
73618
 
73619
+ /**
73620
+ * Checks whether a prompt is unrestricted or matches the selected harness/model.
73621
+ *
73622
+ * A prompt status line can contain one or more backtick-delimited model or harness
73623
+ * names. Matching is intentionally based on normalized substrings so a token such as
73624
+ * `gpt` selects any `gpt-*` model and `opus` selects a `claude-opus-*` model.
73625
+ */
73626
+ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
73627
+ if (promptRunnerIdentity === undefined) {
73628
+ return true;
73629
+ }
73630
+ const statusLine = section.statusLineIndex === undefined ? undefined : file.lines[section.statusLineIndex];
73631
+ const requiredRunnerTokens = statusLine === undefined ? [] : extractPromptRunnerTokens(statusLine);
73632
+ if (requiredRunnerTokens.length === 0) {
73633
+ return true;
73634
+ }
73635
+ const normalizedRunnerNames = [promptRunnerIdentity.harnessName, promptRunnerIdentity.modelName]
73636
+ .filter((name) => name !== undefined && name.trim() !== '')
73637
+ .map((name) => normalizeToKebabCase(name))
73638
+ .filter((name) => name !== '');
73639
+ return requiredRunnerTokens.some((requiredRunnerToken) => {
73640
+ const normalizedRequiredRunnerToken = normalizeToKebabCase(requiredRunnerToken);
73641
+ return (normalizedRequiredRunnerToken !== '' &&
73642
+ normalizedRunnerNames.some((normalizedRunnerName) => normalizedRunnerName.includes(normalizedRequiredRunnerToken)));
73643
+ });
73644
+ }
73645
+ /**
73646
+ * Extracts model and harness tokens from a prompt status line.
73647
+ */
73648
+ function extractPromptRunnerTokens(statusLine) {
73649
+ return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
73650
+ .map((match) => { var _a, _b; return (_b = (_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''; })
73651
+ .filter((token) => token !== '');
73652
+ }
73653
+
73596
73654
  /**
73597
73655
  * Parses a prompt markdown file into sections and metadata.
73598
73656
  */
@@ -73640,11 +73698,11 @@
73640
73698
  };
73641
73699
  }
73642
73700
  /**
73643
- * Parses a status line like "[ ] !!" or "[-]" or "[x] ~$0.65 21 minutes..." into status and priority.
73701
+ * Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
73644
73702
  * For [x] done and [!] failed prompts, allow metadata after the status marker.
73645
73703
  */
73646
73704
  function parseStatusLine(line) {
73647
- var _a, _b, _c, _d, _e;
73705
+ var _a, _b, _c, _d;
73648
73706
  // For done prompts [x], allow any content after (for cost/time metadata)
73649
73707
  const doneMatch = line.match(/^\[(?<status>[xX])\]/);
73650
73708
  if (doneMatch) {
@@ -73655,21 +73713,23 @@
73655
73713
  if (failedMatch) {
73656
73714
  return { status: 'failed', priority: 0 };
73657
73715
  }
73658
- // For todo [ ] and not-ready [-], require clean end with optional priority markers
73659
- const match = line.match(/^\[(?<status>[ -])\]\s*(?<priority>!*)\s*$/);
73660
- if (!match) {
73661
- return undefined;
73716
+ // For not-ready [-], keep the historical clean-line syntax.
73717
+ if (/^\[-\]\s*!*\s*$/u.test(line)) {
73718
+ return { status: 'not-ready', priority: 0 };
73662
73719
  }
73663
- const statusChar = (_b = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.status) === null || _b === void 0 ? void 0 : _b.toLowerCase();
73664
- let status;
73665
- if (statusChar === '-') {
73666
- status = 'not-ready';
73720
+ // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
73721
+ // before or after those tokens. Other trailing text remains an invalid status line.
73722
+ const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
73723
+ if (!todoMatch) {
73724
+ return undefined;
73667
73725
  }
73668
- else {
73669
- status = 'todo';
73726
+ const details = (_b = (_a = todoMatch.groups) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : '';
73727
+ const isPriorityOnly = /^[!\s]*$/u.test(details);
73728
+ const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
73729
+ if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
73730
+ return undefined;
73670
73731
  }
73671
- const priority = status === 'todo' ? (_e = (_d = (_c = match.groups) === null || _c === void 0 ? void 0 : _c.priority) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0 : 0;
73672
- return { status, priority };
73732
+ return { status: 'todo', priority: (_d = (_c = details.match(/!/gu)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0 };
73673
73733
  }
73674
73734
  /**
73675
73735
  * Finds the first non-empty line index between two bounds.
@@ -73805,6 +73865,66 @@
73805
73865
  findUnwrittenPrompts: findUnwrittenPrompts
73806
73866
  });
73807
73867
 
73868
+ /**
73869
+ * Agent source used by `ptbk coder ping` for its disposable connectivity turn.
73870
+ */
73871
+ const PING_AGENT_SOURCE = _spaceTrim.spaceTrim(`
73872
+ Promptbook Coder Ping Agent
73873
+
73874
+ PERSONA You are a connectivity test agent. Perform only the tiny task requested by the user and answer concisely.
73875
+ `);
73876
+ /**
73877
+ * User message used by `ptbk coder ping` to produce a deterministic response.
73878
+ */
73879
+ const PING_MESSAGE = 'Reply with exactly the single word PONG and nothing else.';
73880
+ /**
73881
+ * Runs one small harness/model turn in a disposable temporary project.
73882
+ *
73883
+ * The temporary project is outside the caller's repository, so even a harness
73884
+ * that writes files cannot change the project from which `ptbk coder ping` was
73885
+ * started.
73886
+ */
73887
+ async function runCoderPing(options) {
73888
+ const temporaryProjectPath = await promises.mkdtemp(path.join(os.tmpdir(), 'promptbook-coder-ping-'));
73889
+ const originalWorkingDirectory = process.cwd();
73890
+ try {
73891
+ const agentPath = path.join(temporaryProjectPath, 'ping.book');
73892
+ await promises.writeFile(agentPath, `${PING_AGENT_SOURCE}\n`, 'utf-8');
73893
+ // Note: Some supported CLI wrappers inherit the Node process working directory instead of using projectPath.
73894
+ process.chdir(temporaryProjectPath);
73895
+ const startedAt = performance.now();
73896
+ const result = await executeAgentChatTurn({
73897
+ agentPath,
73898
+ currentWorkingDirectory: temporaryProjectPath,
73899
+ agentName: options.agentName,
73900
+ model: options.model,
73901
+ isVerbose: false,
73902
+ noUi: options.isUiDisabled,
73903
+ thinkingLevel: options.thinkingLevel,
73904
+ allowCredits: options.isCreditsAllowed,
73905
+ messages: [
73906
+ {
73907
+ sender: 'USER',
73908
+ content: PING_MESSAGE,
73909
+ },
73910
+ ],
73911
+ });
73912
+ return {
73913
+ result: result.answer,
73914
+ elapsedTimeMs: performance.now() - startedAt,
73915
+ };
73916
+ }
73917
+ finally {
73918
+ process.chdir(originalWorkingDirectory);
73919
+ await promises.rm(temporaryProjectPath, { recursive: true, force: true });
73920
+ }
73921
+ }
73922
+
73923
+ var runCoderPing$1 = /*#__PURE__*/Object.freeze({
73924
+ __proto__: null,
73925
+ runCoderPing: runCoderPing
73926
+ });
73927
+
73808
73928
  /**
73809
73929
  * Default wait duration applied before retrying a failed prompt round.
73810
73930
  */
@@ -73812,7 +73932,7 @@
73812
73932
  /**
73813
73933
  * CLI usage text for this script.
73814
73934
  */
73815
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
73935
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
73816
73936
  /**
73817
73937
  * Top-level flags supported by this command.
73818
73938
  */
@@ -73822,6 +73942,7 @@
73822
73942
  '--model',
73823
73943
  '--context',
73824
73944
  '--test',
73945
+ '--test-before',
73825
73946
  '--preserve-logs',
73826
73947
  '--isolate',
73827
73948
  '--no-ui',
@@ -73860,6 +73981,8 @@
73860
73981
  const context = readOptionValue(args, '--context');
73861
73982
  const hasTestCommandFlag = args.includes('--test');
73862
73983
  const testCommand = readVariadicOptionValue(args, '--test');
73984
+ const hasTestBeforeFlag = args.includes('--test-before');
73985
+ const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
73863
73986
  const preserveLogs = args.includes('--preserve-logs');
73864
73987
  const isIsolated = args.includes('--isolate');
73865
73988
  const noUi = args.includes('--no-ui');
@@ -73932,6 +74055,7 @@
73932
74055
  model,
73933
74056
  context,
73934
74057
  testCommand,
74058
+ testBefore,
73935
74059
  thinkingLevel,
73936
74060
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
73937
74061
  minimumPriority,
@@ -73940,6 +74064,21 @@
73940
74064
  limit,
73941
74065
  };
73942
74066
  }
74067
+ /**
74068
+ * Parses and validates the optional pre-coding verification mode.
74069
+ */
74070
+ function parseTestBeforeOption(value, hasTestBeforeFlag) {
74071
+ if (value === undefined) {
74072
+ if (hasTestBeforeFlag) {
74073
+ exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74074
+ }
74075
+ return 'no';
74076
+ }
74077
+ if (!isTestBeforeMode(value)) {
74078
+ exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74079
+ }
74080
+ return value;
74081
+ }
73943
74082
  /**
73944
74083
  * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
73945
74084
  */
@@ -76054,6 +76193,16 @@
76054
76193
  return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
76055
76194
  }
76056
76195
 
76196
+ /**
76197
+ * Replaces the complete todo status line while preserving its indentation.
76198
+ *
76199
+ * The complete line is replaced because a todo status can contain a required
76200
+ * model/harness token in addition to priority markers.
76201
+ */
76202
+ function replacePromptTodoStatusLine(line, replacementStatusLine) {
76203
+ return line.replace(/^(?<indentation>\s*)\[\s*\].*$/u, `$<indentation>${replacementStatusLine}`);
76204
+ }
76205
+
76057
76206
  /**
76058
76207
  * Marks a prompt section as done and records the per-step usage pricing and runner details.
76059
76208
  */
@@ -76071,8 +76220,8 @@
76071
76220
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
76072
76221
  const stepsSummary = formatCoderRunSteps(steps);
76073
76222
  const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
76074
- // Replace "[ ]" or "[ ] !!..." with "[x] by runner model thinking level (login method) - Step $price duration; ..."
76075
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76223
+ // Replace the complete todo status, including any required model/harness token.
76224
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76076
76225
  }
76077
76226
 
76078
76227
  /**
@@ -76090,8 +76239,10 @@
76090
76239
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
76091
76240
  const duration = moment__default["default"]().diff(promptExecutionStartedDate);
76092
76241
  const durationString = moment__default["default"].duration(duration).humanize();
76093
- const failureDetails = attemptMetadata === '' ? `failed after ${durationString} by ${runnerSignature}` : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76094
- file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[!] ${failureDetails}`);
76242
+ const failureDetails = attemptMetadata === ''
76243
+ ? `failed after ${durationString} by ${runnerSignature}`
76244
+ : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76245
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[!] ${failureDetails}`);
76095
76246
  }
76096
76247
 
76097
76248
  /**
@@ -77061,17 +77212,18 @@
77061
77212
  /**
77062
77213
  * Lists todo prompts that are ready to run (no authoring placeholders).
77063
77214
  */
77064
- function listRunnablePrompts(files, priorityFilter = {}) {
77215
+ function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
77065
77216
  return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
77066
- isPromptInPriorityFilter(prompt.section, priorityFilter));
77217
+ isPromptInPriorityFilter(prompt.section, priorityFilter) &&
77218
+ isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
77067
77219
  }
77068
77220
 
77069
77221
  /**
77070
77222
  * Selects the next runnable prompt based on priority.
77071
77223
  */
77072
- function findNextTodoPrompt(files, priorityFilter = {}) {
77224
+ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
77073
77225
  let nextPrompt;
77074
- for (const prompt of listRunnablePrompts(files, priorityFilter)) {
77226
+ for (const prompt of listRunnablePrompts(files, priorityFilter, promptRunnerIdentity)) {
77075
77227
  if (!nextPrompt || prompt.section.priority > nextPrompt.section.priority) {
77076
77228
  nextPrompt = prompt;
77077
77229
  }
@@ -77082,8 +77234,8 @@
77082
77234
  /**
77083
77235
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
77084
77236
  */
77085
- function listUpcomingTasks(files, priorityFilter = {}) {
77086
- return listRunnablePrompts(files, priorityFilter).map(({ file, section }) => ({
77237
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
77238
+ return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
77087
77239
  label: buildPromptLabelForDisplay(file, section),
77088
77240
  summary: buildPromptSummary(file, section),
77089
77241
  priority: section.priority,
@@ -77227,6 +77379,71 @@
77227
77379
  }
77228
77380
  }
77229
77381
 
77382
+ /**
77383
+ * Creates the one queue prompt used to repair a pre-existing test failure.
77384
+ */
77385
+ async function createTestBeforeRepairPrompt(options) {
77386
+ const description = spaceTrim((block) => `
77387
+ Fix the existing test failures before implementing any queued coding tasks.
77388
+
77389
+ 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.
77390
+
77391
+ ## Verification output
77392
+
77393
+ \`\`\`
77394
+ ${block(limitTestOutput(options.testOutput))}
77395
+ \`\`\`
77396
+ `);
77397
+ const createdPrompt = await addCoderPrompt({
77398
+ projectPath: options.projectPath,
77399
+ description,
77400
+ priority: 0,
77401
+ });
77402
+ const promptPath = path.join(options.projectPath, createdPrompt.filePath);
77403
+ const promptFile = parsePromptFile(promptPath, await promises.readFile(promptPath, 'utf-8'));
77404
+ const section = promptFile.sections[0];
77405
+ if (!section) {
77406
+ throw new UnexpectedError(spaceTrim(`
77407
+ The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
77408
+ `));
77409
+ }
77410
+ return { file: promptFile, section };
77411
+ }
77412
+
77413
+ /**
77414
+ * Runs the configured verification command before the first coding prompt.
77415
+ */
77416
+ async function runTestBefore(options) {
77417
+ var _a, _b;
77418
+ const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
77419
+ const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
77420
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
77421
+ checkpointLabel: 'running initial tests before the agent coding starts',
77422
+ phase: 'verifying',
77423
+ statusMessage: initialTestStatusMessage,
77424
+ }));
77425
+ console.info(colors__default["default"].gray(initialTestStatusMessage));
77426
+ try {
77427
+ const testOutput = await runPromptTestCommandExecutor({
77428
+ command: options.testCommand,
77429
+ projectPath: options.projectPath,
77430
+ scriptPath: buildTemporaryPromptScriptPath({
77431
+ projectPath: options.projectPath,
77432
+ scriptDirectoryName: 'coder-prompts',
77433
+ sourceFileName: 'test-before',
77434
+ }),
77435
+ });
77436
+ console.info(colors__default["default"].green('Pre-coding tests passed.'));
77437
+ return { isPassed: true, testOutput };
77438
+ }
77439
+ catch (error) {
77440
+ const testOutput = formatUnknownErrorMessage(error);
77441
+ console.error(colors__default["default"].red('Pre-coding tests failed.'));
77442
+ console.error(testOutput);
77443
+ return { isPassed: false, testOutput };
77444
+ }
77445
+ }
77446
+
77230
77447
  /**
77231
77448
  * Constant for prompts dir.
77232
77449
  */
@@ -77254,6 +77471,10 @@
77254
77471
  return;
77255
77472
  }
77256
77473
  const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
77474
+ const promptRunnerIdentity = {
77475
+ harnessName: options.agentName,
77476
+ modelName: actualRunnerModel,
77477
+ };
77257
77478
  console.info(colors__default["default"].green(`Running prompts with ${runner.name}`));
77258
77479
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
77259
77480
  await initializeRunUiAgentVisual(uiHandle, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentSource);
@@ -77268,6 +77489,7 @@
77268
77489
  let previousRoundStartTime;
77269
77490
  let previousRoundEndTime;
77270
77491
  let completedRunCount = 0;
77492
+ let hasRunTestBefore = false;
77271
77493
  while (just(true)) {
77272
77494
  if (options.autoPull && !options.dryRun) {
77273
77495
  await waitForRequestedPause({
@@ -77280,6 +77502,35 @@
77280
77502
  options,
77281
77503
  isRichUiEnabled,
77282
77504
  });
77505
+ if (!hasRunTestBefore && options.testBefore !== 'no') {
77506
+ await waitForRequestedPause({
77507
+ checkpointLabel: 'loading prompts before running initial tests',
77508
+ phase: 'loading',
77509
+ statusMessage: 'Loading prompts before running initial tests...',
77510
+ });
77511
+ await loadPromptQueueSnapshot({
77512
+ options,
77513
+ isRichUiEnabled,
77514
+ progressDisplay,
77515
+ uiHandle,
77516
+ promptRunnerIdentity,
77517
+ });
77518
+ }
77519
+ if (!hasRunTestBefore) {
77520
+ hasWaitedForStart = await runTestBeforeIfNeeded({
77521
+ options,
77522
+ runner,
77523
+ runnerMetadata,
77524
+ resolvedCoderContext,
77525
+ resolvedAgentSystemMessage,
77526
+ isRichUiEnabled,
77527
+ progressDisplay,
77528
+ uiHandle,
77529
+ waitForRequestedPause,
77530
+ hasWaitedForStart,
77531
+ });
77532
+ hasRunTestBefore = true;
77533
+ }
77283
77534
  await waitForRequestedPause({
77284
77535
  checkpointLabel: 'loading prompts',
77285
77536
  phase: 'loading',
@@ -77290,6 +77541,7 @@
77290
77541
  isRichUiEnabled,
77291
77542
  progressDisplay,
77292
77543
  uiHandle,
77544
+ promptRunnerIdentity,
77293
77545
  });
77294
77546
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
77295
77547
  hasShownUpcomingTasks,
@@ -77297,6 +77549,7 @@
77297
77549
  stats: promptQueueSnapshot.stats,
77298
77550
  priorityFilter: options.priorityFilter,
77299
77551
  isRichUiEnabled,
77552
+ promptRunnerIdentity,
77300
77553
  }));
77301
77554
  if (!promptQueueSnapshot.nextPrompt) {
77302
77555
  if (isEndAfterCurrentPromptRequested(completedRunCount)) {
@@ -77399,6 +77652,14 @@
77399
77652
  * Validates cross-flag constraints before the run starts.
77400
77653
  */
77401
77654
  function validateRunCodexPromptOptions(options) {
77655
+ var _a;
77656
+ if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
77657
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
77658
+ Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
77659
+
77660
+ Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
77661
+ `));
77662
+ }
77402
77663
  if (options.allowDestructiveAutoMigrate && !options.autoMigrate) {
77403
77664
  throw new DatabaseError(_spaceTrim.spaceTrim(`
77404
77665
  Flag \`--allow-destructive-auto-migrate\` requires \`--auto-migrate\`.
@@ -77469,20 +77730,103 @@
77469
77730
  * Normalizes legacy and current priority options into one validated run option shape.
77470
77731
  */
77471
77732
  function normalizeRunOptions(options) {
77472
- var _a, _b, _c, _d, _e;
77733
+ var _a, _b, _c, _d, _e, _f, _g;
77734
+ const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
77735
+ const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
77473
77736
  const priorityFilter = normalizePriorityFilter({
77474
77737
  priority: options.priority,
77475
- minimumPriority: (_a = options.minimumPriority) !== null && _a !== void 0 ? _a : (_b = options.priorityFilter) === null || _b === void 0 ? void 0 : _b.minimumPriority,
77476
- maximumPriority: (_c = options.maximumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.maximumPriority,
77738
+ minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
77739
+ maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
77477
77740
  });
77478
77741
  return {
77479
77742
  ...options,
77480
- priority: (_e = priorityFilter.minimumPriority) !== null && _e !== void 0 ? _e : 0,
77743
+ testBefore,
77744
+ testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
77745
+ priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
77481
77746
  minimumPriority: priorityFilter.minimumPriority,
77482
77747
  maximumPriority: priorityFilter.maximumPriority,
77483
77748
  priorityFilter,
77484
77749
  };
77485
77750
  }
77751
+ /**
77752
+ * Runs the optional pre-coding verification and, when requested, its one repair prompt.
77753
+ */
77754
+ async function runTestBeforeIfNeeded(options) {
77755
+ const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, } = options;
77756
+ if (runOptions.testBefore === 'no') {
77757
+ return hasWaitedForStart;
77758
+ }
77759
+ if (!runOptions.testCommand) {
77760
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
77761
+ ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
77762
+
77763
+ Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
77764
+ `));
77765
+ }
77766
+ if (!runOptions.ignoreGitChanges) {
77767
+ await waitForRequestedPause({
77768
+ checkpointLabel: 'checking the git working tree before testing',
77769
+ phase: 'loading',
77770
+ statusMessage: 'Checking the working tree before testing...',
77771
+ });
77772
+ await ensureWorkingTreeClean();
77773
+ }
77774
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
77775
+ const testBeforeResult = await runTestBefore({
77776
+ testCommand: runOptions.testCommand,
77777
+ projectPath: process.cwd(),
77778
+ waitForPauseCheckpoint: waitForRequestedPause,
77779
+ }).finally(() => {
77780
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
77781
+ });
77782
+ if (testBeforeResult.isPassed) {
77783
+ return hasWaitedForStart;
77784
+ }
77785
+ const testOutput = limitTestOutput(testBeforeResult.testOutput);
77786
+ if (runOptions.testBefore === 'yes-and-fail') {
77787
+ throw new NotAllowed(_spaceTrim.spaceTrim((block) => `
77788
+ Pre-coding verification command \`${runOptions.testCommand}\` failed.
77789
+
77790
+ The coding agent was not started because the project was already failing before the first queued prompt.
77791
+
77792
+ ### Test results
77793
+ ${'```'}
77794
+ ${block(testOutput)}
77795
+ ${'```'}
77796
+ `));
77797
+ }
77798
+ const repairPrompt = await createTestBeforeRepairPrompt({
77799
+ projectPath: process.cwd(),
77800
+ testCommand: runOptions.testCommand,
77801
+ testOutput,
77802
+ });
77803
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
77804
+ const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
77805
+ options: runOptions,
77806
+ nextPrompt: repairPrompt,
77807
+ promptLabel: repairPromptLabel,
77808
+ hasWaitedForStart,
77809
+ isRichUiEnabled,
77810
+ progressDisplay,
77811
+ uiHandle,
77812
+ });
77813
+ // The repair prompt is created in the current worktree so it can be recorded and committed with the repair.
77814
+ // It therefore intentionally uses the regular round here even when the queue itself uses --isolate.
77815
+ await runPromptRound({
77816
+ options: runOptions,
77817
+ runner,
77818
+ runnerMetadata,
77819
+ nextPrompt: repairPrompt,
77820
+ promptLabel: repairPromptLabel,
77821
+ resolvedCoderContext,
77822
+ resolvedAgentSystemMessage,
77823
+ isRichUiEnabled,
77824
+ progressDisplay,
77825
+ uiHandle,
77826
+ waitForRequestedPause,
77827
+ });
77828
+ return updatedHasWaitedForStart;
77829
+ }
77486
77830
  /**
77487
77831
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
77488
77832
  */
@@ -77567,7 +77911,7 @@
77567
77911
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
77568
77912
  */
77569
77913
  async function loadPromptQueueSnapshot(options) {
77570
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
77914
+ const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity } = options;
77571
77915
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
77572
77916
  const promptFiles = await loadPromptFiles(PROMPTS_DIR$1);
77573
77917
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
@@ -77579,14 +77923,14 @@
77579
77923
  return {
77580
77924
  promptFiles,
77581
77925
  stats,
77582
- nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter),
77926
+ nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter, promptRunnerIdentity),
77583
77927
  };
77584
77928
  }
77585
77929
  /**
77586
77930
  * Prints upcoming tasks only on the first loop iteration in plain-console mode.
77587
77931
  */
77588
77932
  function showUpcomingTasksOnce(options) {
77589
- const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled } = options;
77933
+ const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled, promptRunnerIdentity } = options;
77590
77934
  if (hasShownUpcomingTasks || isRichUiEnabled) {
77591
77935
  return true;
77592
77936
  }
@@ -77595,7 +77939,7 @@
77595
77939
  printPromptsToBeWritten(promptFiles, priorityFilter);
77596
77940
  console.info('');
77597
77941
  }
77598
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter));
77942
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
77599
77943
  return true;
77600
77944
  }
77601
77945
  /**
@@ -77605,7 +77949,10 @@
77605
77949
  if (promptQueueSnapshot.nextPrompt) {
77606
77950
  return false;
77607
77951
  }
77608
- if (promptQueueSnapshot.stats.toBeWritten > 0) {
77952
+ if (promptQueueSnapshot.stats.forAgent > 0) {
77953
+ announceRunCompletion('No prompts match the selected harness or model.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
77954
+ }
77955
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
77609
77956
  announceRunCompletion('No prompts ready for agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
77610
77957
  }
77611
77958
  else {
@@ -77646,9 +77993,16 @@
77646
77993
  * Updates the UI status message while waiting for new prompts in keepAlive server mode.
77647
77994
  */
77648
77995
  function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
77649
- const message = promptQueueSnapshot.stats.toBeWritten > 0
77650
- ? 'No prompts ready for agent. Watching for changes...'
77651
- : 'All prompts are done. Watching for changes...';
77996
+ let message;
77997
+ if (promptQueueSnapshot.stats.forAgent > 0) {
77998
+ message = 'No prompts match the selected harness or model. Watching for changes...';
77999
+ }
78000
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
78001
+ message = 'No prompts ready for agent. Watching for changes...';
78002
+ }
78003
+ else {
78004
+ message = 'All prompts are done. Watching for changes...';
78005
+ }
77652
78006
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
77653
78007
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
77654
78008
  if (!isRichUiEnabled) {
@@ -77670,7 +78024,7 @@
77670
78024
  * Waits for the optional user confirmation before starting the selected prompt.
77671
78025
  */
77672
78026
  async function waitForPromptConfirmationIfNeeded(options) {
77673
- const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle } = options;
78027
+ const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle, } = options;
77674
78028
  if (!runOptions.waitForUser) {
77675
78029
  return hasWaitedForStart;
77676
78030
  }