@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/esm/index.es.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import colors from 'colors';
2
2
  import commander, { Option } from 'commander';
3
3
  import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { writeFile, stat, mkdir, readFile, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, access, constants, watch, rmdir } from 'fs/promises';
4
+ import { writeFile, stat, mkdir, readFile, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, access, constants, watch, rmdir, mkdtemp } from 'fs/promises';
5
5
  import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
6
6
  import { createHash, randomBytes } from 'crypto';
7
7
  import { spawn } from 'child_process';
@@ -9,10 +9,10 @@ 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';
15
+ import prompts from 'prompts';
16
16
  import hexEncoder from 'crypto-js/enc-hex';
17
17
  import sha256 from 'crypto-js/sha256';
18
18
  import { io } from 'socket.io-client';
@@ -37,8 +37,8 @@ import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultO
37
37
  import OpenAI from 'openai';
38
38
  import * as ts from 'typescript';
39
39
  import ignore from 'ignore';
40
- import { EventEmitter } from 'events';
41
40
  import { tmpdir } from 'os';
41
+ import { EventEmitter } from 'events';
42
42
  import { lookup, extension } from 'mime-types';
43
43
  import papaparse from 'papaparse';
44
44
  import { Client } from 'pg';
@@ -59,7 +59,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
59
59
  * @generated
60
60
  * @see https://github.com/webgptorg/promptbook
61
61
  */
62
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-0';
62
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-2';
63
63
  /**
64
64
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
65
65
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -26966,12 +26966,12 @@ function buildControlPills(options) {
26966
26966
  * Builds the coder-run control pills shown in the footer box.
26967
26967
  */
26968
26968
  function buildCoderRunControlPills(options) {
26969
- const { pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26969
+ const { phase, pauseControl, pendingEnterLabel, isEndAfterCurrentPromptRequested, sessionTotal } = options;
26970
26970
  return buildControlPills({
26971
26971
  pauseControl,
26972
26972
  pendingEnterLabel,
26973
26973
  additionalControls: [
26974
- buildSkipCurrentWaitControl(),
26974
+ ...(phase === 'waiting' ? [buildSkipCurrentWaitControl()] : []),
26975
26975
  buildEndAfterCurrentPromptControl(isEndAfterCurrentPromptRequested, sessionTotal),
26976
26976
  ],
26977
26977
  });
@@ -29552,6 +29552,7 @@ function buildCodexScript(options) {
29552
29552
  ` -c model_reasoning_effort="${thinkingLevel}" \\`,
29553
29553
  ` --ask-for-approval ${options.askForApproval} \\`,
29554
29554
  ` exec --model ${options.model} \\`,
29555
+ ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
29555
29556
  ' --local-provider none \\',
29556
29557
  ` --sandbox ${options.sandbox} \\`,
29557
29558
  ` -C ${projectPath} \\`,
@@ -29868,6 +29869,10 @@ function matchesAnyPattern(text, patterns) {
29868
29869
  * Output line that marks finished Codex usage summary.
29869
29870
  */
29870
29871
  const CODEX_COMPLETION_LINE = /^\s*tokens used\b/i;
29872
+ /**
29873
+ * Output line that marks the completed Codex turn in JSONL output mode.
29874
+ */
29875
+ const CODEX_JSON_COMPLETION_LINE = /^\s*\{"type":"turn\.(?:completed|failed)"/u;
29871
29876
  /**
29872
29877
  * Idle timeout after completion marker to capture trailing output.
29873
29878
  */
@@ -29944,6 +29949,7 @@ class OpenAiCodexRunner {
29944
29949
  sandbox: this.options.sandbox,
29945
29950
  askForApproval: this.options.askForApproval,
29946
29951
  allowCredits: this.options.allowCredits,
29952
+ isMachineReadableProgressEnabled: this.options.isMachineReadableProgressEnabled,
29947
29953
  codexCommand: this.options.codexCommand,
29948
29954
  });
29949
29955
  for (let retryIndex = 0;; retryIndex++) {
@@ -29958,7 +29964,9 @@ class OpenAiCodexRunner {
29958
29964
  const output = await $runGoScriptUntilMarkerIdle({
29959
29965
  scriptPath: options.scriptPath,
29960
29966
  scriptContent,
29961
- completionLineMatcher: CODEX_COMPLETION_LINE,
29967
+ completionLineMatcher: this.options.isMachineReadableProgressEnabled
29968
+ ? CODEX_JSON_COMPLETION_LINE
29969
+ : CODEX_COMPLETION_LINE,
29962
29970
  idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
29963
29971
  logPath: options.logPath,
29964
29972
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
@@ -30208,6 +30216,7 @@ function createOpenAiCodexRunnerResolution(options) {
30208
30216
  sandbox: 'danger-full-access',
30209
30217
  askForApproval: 'never',
30210
30218
  allowCredits: options.allowCredits,
30219
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
30211
30220
  });
30212
30221
  if (!options.allowCredits && options.isVerbose === true) {
30213
30222
  console.info(colors.gray('OpenAI Codex credit spending is disabled. Use `--allow-credits` to explicitly opt in.'));
@@ -31443,6 +31452,7 @@ function buildCoderRunUiFrame(options) {
31443
31452
  : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
31444
31453
  const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
31445
31454
  const controls = buildCoderRunControlPills({
31455
+ phase: options.phase,
31446
31456
  pauseControl: pausePresentation.pauseControl,
31447
31457
  pendingEnterLabel: options.pendingEnterLabel,
31448
31458
  isEndAfterCurrentPromptRequested: options.isEndAfterCurrentPromptRequested,
@@ -32306,6 +32316,7 @@ function createCoderRunOptionsForAgent(options) {
32306
32316
  autoPull: options.autoPull,
32307
32317
  agentName: options.agentName,
32308
32318
  model: options.model,
32319
+ isMachineReadableProgressEnabled: options.isMachineReadableProgressEnabled,
32309
32320
  priority: 0,
32310
32321
  };
32311
32322
  }
@@ -33295,6 +33306,21 @@ function appendCoderContext(prompt, context) {
33295
33306
  `);
33296
33307
  }
33297
33308
 
33309
+ /**
33310
+ * Maximum amount of test output embedded into an agent prompt.
33311
+ */
33312
+ const MAX_TEST_OUTPUT_CHARS = 12000;
33313
+ /**
33314
+ * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
33315
+ */
33316
+ function limitTestOutput(testOutput) {
33317
+ const normalizedTestOutput = testOutput.trim();
33318
+ if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
33319
+ return normalizedTestOutput;
33320
+ }
33321
+ return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
33322
+ }
33323
+
33298
33324
  /**
33299
33325
  * Runs the configured verification command inside the project root and returns its output.
33300
33326
  */
@@ -33315,10 +33341,6 @@ async function runPromptTestCommand(options) {
33315
33341
  * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33316
33342
  */
33317
33343
  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
33344
  /**
33323
33345
  * File extension used by generated shell scripts.
33324
33346
  */
@@ -33364,7 +33386,7 @@ async function runPromptWithTestFeedback(options) {
33364
33386
  return { ...result, attemptCount, steps };
33365
33387
  }
33366
33388
  const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
33367
- const feedbackVerificationOutput = limitVerificationOutputForFeedback(fullVerificationOutput);
33389
+ const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
33368
33390
  if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
33369
33391
  console.error(colors.red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33370
33392
  throw new Error(buildFinalVerificationFailureMessage({
@@ -33494,19 +33516,6 @@ function buildFinalVerificationFailureMessage({ promptLabel, testCommand, attemp
33494
33516
  \`\`\`
33495
33517
  `);
33496
33518
  }
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
33519
  /**
33511
33520
  * Derives a dedicated temp-script path for verification commands.
33512
33521
  */
@@ -41367,6 +41376,7 @@ function createLocalAgentRunOptions(options, localAgentRunnerLimits) {
41367
41376
  autoClone: false,
41368
41377
  maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
41369
41378
  maxParallelMessages: localAgentRunnerLimits.maxParallelMessages,
41379
+ isMachineReadableProgressEnabled: true,
41370
41380
  };
41371
41381
  }
41372
41382
 
@@ -43481,6 +43491,27 @@ function resolveCheckedHarnessDefinitions(harnessNames) {
43481
43491
  }
43482
43492
  // 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
43493
 
43494
+ /**
43495
+ * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
43496
+ *
43497
+ * @private internal utility of `ptbk coder`
43498
+ */
43499
+ const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
43500
+ /**
43501
+ * Returns the default coder `AGENT_CODING.md` quick-reference content.
43502
+ *
43503
+ * @private internal utility of `ptbk coder`
43504
+ */
43505
+ function getDefaultCoderAgentCodingFileContent() {
43506
+ return spaceTrim$1(`
43507
+ # ✨ Promptbook Coder agent coding
43508
+
43509
+ This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
43510
+ `);
43511
+ }
43512
+ // Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
43513
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
43514
+
43484
43515
  /**
43485
43516
  * Relative path to the shared coder context file initialized in project roots.
43486
43517
  *
@@ -43508,106 +43539,6 @@ function getDefaultCoderAgentsFileContent() {
43508
43539
  // Note: [🟡] Code for coder AGENTS file boilerplate [agentsFile](src/cli/cli-commands/coder/agentsFile.ts) should never be published outside of `@promptbook/cli`
43509
43540
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
43510
43541
 
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
43542
  /**
43612
43543
  * Relative directory path for agents initialized by `ptbk coder init`.
43613
43544
  *
@@ -43672,6 +43603,16 @@ async function isExistingFile$2(path) {
43672
43603
  }
43673
43604
  // 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
43605
 
43606
+ /**
43607
+ * Normalizes one project-relative path for human-readable CLI output and markdown.
43608
+ *
43609
+ * @private internal utility of `ptbk coder`
43610
+ */
43611
+ function formatDisplayPath(relativePath) {
43612
+ return relativePath.replace(/\\/gu, '/');
43613
+ }
43614
+ // Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
43615
+
43675
43616
  /**
43676
43617
  * Fallback `.env` content used when no required variables need to be appended.
43677
43618
  */
@@ -43749,7 +43690,7 @@ async function ensureCoderGitignoreFile(projectPath) {
43749
43690
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
43750
43691
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
43751
43692
  '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',
43693
+ '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
43694
  // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
43754
43695
  'coder:verify': 'ptbk coder verify',
43755
43696
  };
@@ -43998,9 +43939,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
43998
43939
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
43999
43940
  const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
44000
43941
  const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
44001
- const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent({
44002
- packageJsonScripts: getDefaultCoderPackageJsonScripts(),
44003
- }));
43942
+ const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
44004
43943
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44005
43944
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44006
43945
  const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
@@ -44141,6 +44080,66 @@ function listDefaultCoderProjectPromptTemplateDisplayPaths() {
44141
44080
  // Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
44142
44081
  // Note: [💞] Ignore a discrepancy between file name and entity name
44143
44082
 
44083
+ /**
44084
+ * Initializes `coder ping` command for Promptbook CLI utilities.
44085
+ *
44086
+ * The command makes one small, isolated harness/model call, prints its result
44087
+ * and reports how long the harness/model turn took.
44088
+ *
44089
+ * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI.
44090
+ *
44091
+ * @private internal function of `promptbookCli`
44092
+ */
44093
+ function $initializeCoderPingCommand(program) {
44094
+ const command = program.command('ping');
44095
+ command.description(spaceTrim$1(`
44096
+ Test one harness and model connection with a small disposable task
44097
+
44098
+ ${PROMPT_RUNNER_DESCRIPTION}
44099
+
44100
+ Features:
44101
+ - Runs the dummy task in a temporary directory outside the current project
44102
+ - Prints the result returned by the selected harness and model
44103
+ - Reports the elapsed harness/model response time in milliseconds
44104
+ - Can be used to start consuming an applicable harness/model quota before a longer run
44105
+ `));
44106
+ addPromptRunnerSelectionOptions(command);
44107
+ addPromptRunnerRuntimeOptions(command);
44108
+ command.action(handleActionErrors(async (cliOptions) => {
44109
+ const normalizedOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44110
+ const agentName = normalizedOptions.agentName;
44111
+ if (!agentName) {
44112
+ throw new NotAllowed(spaceTrim$1(`
44113
+ A harness is required for \`ptbk coder ping\`.
44114
+
44115
+ Pass one with \`--harness <harness-name>\`.
44116
+ `));
44117
+ }
44118
+ await $ensureHarnessInstallations([agentName]);
44119
+ // Note: Import the runner-backed implementation dynamically to avoid loading heavy dependencies until needed.
44120
+ const { runCoderPing } = await Promise.resolve().then(function () { return runCoderPing$1; });
44121
+ const pingResult = await runCoderPing({
44122
+ agentName,
44123
+ model: normalizedOptions.model,
44124
+ thinkingLevel: normalizedOptions.thinkingLevel,
44125
+ isUiDisabled: normalizedOptions.noUi,
44126
+ isCreditsAllowed: normalizedOptions.allowCredits,
44127
+ });
44128
+ printCoderPingResult(pingResult);
44129
+ }));
44130
+ }
44131
+ /**
44132
+ * Prints the result and duration of one completed coder ping.
44133
+ */
44134
+ function printCoderPingResult(pingResult) {
44135
+ console.info(spaceTrim$1(`
44136
+ Result: ${pingResult.result}
44137
+ Time: ${pingResult.elapsedTimeMs.toFixed(0)} ms
44138
+ `));
44139
+ }
44140
+ // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44141
+ // Note: [💞] Ignore a discrepancy between file name and entity name
44142
+
44144
44143
  /**
44145
44144
  * Creates a Commander argument parser that accepts only non-negative integers.
44146
44145
  *
@@ -44165,6 +44164,21 @@ function createNonNegativeIntegerOptionParser(optionName) {
44165
44164
  }
44166
44165
  // Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
44167
44166
 
44167
+ /**
44168
+ * Modes supported by `ptbk coder run --test-before`.
44169
+ */
44170
+ const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
44171
+ /**
44172
+ * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
44173
+ */
44174
+ const DEFAULT_CODER_TEST_COMMAND = 'npm test';
44175
+ /**
44176
+ * Checks whether a value is a supported `--test-before` mode.
44177
+ */
44178
+ function isTestBeforeMode(value) {
44179
+ return TEST_BEFORE_MODE_VALUES.includes(value);
44180
+ }
44181
+
44168
44182
  /**
44169
44183
  * Default wait duration applied before retrying a prompt round after an error (10 minutes).
44170
44184
  *
@@ -44209,6 +44223,7 @@ function $initializeCoderRunCommand(program) {
44209
44223
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44210
44224
  - Checks that the selected harness is installed globally and up to date before the first prompt
44211
44225
  - Supports GPG signing of commits
44226
+ - Optional pre-coding test run that can stop or repair pre-existing failures
44212
44227
  - Optional post-prompt verification with test-feedback retries
44213
44228
  - Progress tracking and interactive P/S/X terminal controls
44214
44229
  - Dry-run mode to preview prompts
@@ -44218,6 +44233,9 @@ function $initializeCoderRunCommand(program) {
44218
44233
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
44219
44234
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
44220
44235
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
44236
+ 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)`)
44237
+ .choices([...TEST_BEFORE_MODE_VALUES])
44238
+ .default('no'));
44221
44239
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
44222
44240
  addPromptRunnerExecutionOptions(command);
44223
44241
  command.option('--isolate', spaceTrim$1(`
@@ -44247,8 +44265,9 @@ function $initializeCoderRunCommand(program) {
44247
44265
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
44248
44266
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
44249
44267
  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);
44268
+ 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;
44269
+ const configuredTestCommand = normalizeCommandOptionValue$1(test);
44270
+ const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
44252
44271
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
44253
44272
  isAgentRequired: !dryRun,
44254
44273
  });
@@ -44277,6 +44296,7 @@ function $initializeCoderRunCommand(program) {
44277
44296
  agent,
44278
44297
  context,
44279
44298
  testCommand,
44299
+ testBefore,
44280
44300
  preserveLogs,
44281
44301
  isIsolated,
44282
44302
  noUi: runnerOptions.noUi,
@@ -44533,6 +44553,7 @@ function collectStringOption(value, previousValues) {
44533
44553
  * - add: Add one ready-to-run prompt file to the queue
44534
44554
  * - generate-boilerplates: Generate prompt boilerplate files
44535
44555
  * - find-refactor-candidates: Find files that need refactoring
44556
+ * - ping: Test one harness and model connection with a disposable task
44536
44557
  * - run: Run coding prompts with AI agents
44537
44558
  * - verify: Verify completed prompts
44538
44559
  * - find-fresh-emoji-tags: Find unused emoji tags
@@ -44552,6 +44573,7 @@ function $initializeCoderCommand(program) {
44552
44573
  - generate-boilerplates: Generate prompt boilerplate files
44553
44574
  - find-refactor-candidates: Find files that need refactoring
44554
44575
  - find-unwritten: List prompt sections that still need to be authored
44576
+ - ping: Test one harness and model connection with a disposable task
44555
44577
  - run: Run coding prompts with AI agents
44556
44578
  - server: Start a long-running coder server with a kanban web UI
44557
44579
  - verify: Verify completed prompts
@@ -44563,6 +44585,7 @@ function $initializeCoderCommand(program) {
44563
44585
  $initializeCoderGenerateBoilerplatesCommand(coderCommand);
44564
44586
  $initializeCoderFindRefactorCandidatesCommand(coderCommand);
44565
44587
  $initializeCoderFindUnwrittenCommand(coderCommand);
44588
+ $initializeCoderPingCommand(coderCommand);
44566
44589
  $initializeCoderRunCommand(coderCommand);
44567
44590
  $initializeCoderServerCommand(coderCommand);
44568
44591
  $initializeCoderVerifyCommand(coderCommand);
@@ -73589,6 +73612,41 @@ function listPromptsToBeWritten(files, priorityFilter = {}) {
73589
73612
  isPromptInPriorityFilter(prompt.section, priorityFilter));
73590
73613
  }
73591
73614
 
73615
+ /**
73616
+ * Checks whether a prompt is unrestricted or matches the selected harness/model.
73617
+ *
73618
+ * A prompt status line can contain one or more backtick-delimited model or harness
73619
+ * names. Matching is intentionally based on normalized substrings so a token such as
73620
+ * `gpt` selects any `gpt-*` model and `opus` selects a `claude-opus-*` model.
73621
+ */
73622
+ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
73623
+ if (promptRunnerIdentity === undefined) {
73624
+ return true;
73625
+ }
73626
+ const statusLine = section.statusLineIndex === undefined ? undefined : file.lines[section.statusLineIndex];
73627
+ const requiredRunnerTokens = statusLine === undefined ? [] : extractPromptRunnerTokens(statusLine);
73628
+ if (requiredRunnerTokens.length === 0) {
73629
+ return true;
73630
+ }
73631
+ const normalizedRunnerNames = [promptRunnerIdentity.harnessName, promptRunnerIdentity.modelName]
73632
+ .filter((name) => name !== undefined && name.trim() !== '')
73633
+ .map((name) => normalizeToKebabCase(name))
73634
+ .filter((name) => name !== '');
73635
+ return requiredRunnerTokens.some((requiredRunnerToken) => {
73636
+ const normalizedRequiredRunnerToken = normalizeToKebabCase(requiredRunnerToken);
73637
+ return (normalizedRequiredRunnerToken !== '' &&
73638
+ normalizedRunnerNames.some((normalizedRunnerName) => normalizedRunnerName.includes(normalizedRequiredRunnerToken)));
73639
+ });
73640
+ }
73641
+ /**
73642
+ * Extracts model and harness tokens from a prompt status line.
73643
+ */
73644
+ function extractPromptRunnerTokens(statusLine) {
73645
+ return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
73646
+ .map((match) => { var _a, _b; return (_b = (_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : ''; })
73647
+ .filter((token) => token !== '');
73648
+ }
73649
+
73592
73650
  /**
73593
73651
  * Parses a prompt markdown file into sections and metadata.
73594
73652
  */
@@ -73636,11 +73694,11 @@ function parsePromptFile(filePath, content) {
73636
73694
  };
73637
73695
  }
73638
73696
  /**
73639
- * Parses a status line like "[ ] !!" or "[-]" or "[x] ~$0.65 21 minutes..." into status and priority.
73697
+ * Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
73640
73698
  * For [x] done and [!] failed prompts, allow metadata after the status marker.
73641
73699
  */
73642
73700
  function parseStatusLine(line) {
73643
- var _a, _b, _c, _d, _e;
73701
+ var _a, _b, _c, _d;
73644
73702
  // For done prompts [x], allow any content after (for cost/time metadata)
73645
73703
  const doneMatch = line.match(/^\[(?<status>[xX])\]/);
73646
73704
  if (doneMatch) {
@@ -73651,21 +73709,23 @@ function parseStatusLine(line) {
73651
73709
  if (failedMatch) {
73652
73710
  return { status: 'failed', priority: 0 };
73653
73711
  }
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;
73712
+ // For not-ready [-], keep the historical clean-line syntax.
73713
+ if (/^\[-\]\s*!*\s*$/u.test(line)) {
73714
+ return { status: 'not-ready', priority: 0 };
73658
73715
  }
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';
73716
+ // Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
73717
+ // before or after those tokens. Other trailing text remains an invalid status line.
73718
+ const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
73719
+ if (!todoMatch) {
73720
+ return undefined;
73663
73721
  }
73664
- else {
73665
- status = 'todo';
73722
+ const details = (_b = (_a = todoMatch.groups) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : '';
73723
+ const isPriorityOnly = /^[!\s]*$/u.test(details);
73724
+ const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
73725
+ if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
73726
+ return undefined;
73666
73727
  }
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 };
73728
+ return { status: 'todo', priority: (_d = (_c = details.match(/!/gu)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0 };
73669
73729
  }
73670
73730
  /**
73671
73731
  * Finds the first non-empty line index between two bounds.
@@ -73801,6 +73861,66 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
73801
73861
  findUnwrittenPrompts: findUnwrittenPrompts
73802
73862
  });
73803
73863
 
73864
+ /**
73865
+ * Agent source used by `ptbk coder ping` for its disposable connectivity turn.
73866
+ */
73867
+ const PING_AGENT_SOURCE = spaceTrim$1(`
73868
+ Promptbook Coder Ping Agent
73869
+
73870
+ PERSONA You are a connectivity test agent. Perform only the tiny task requested by the user and answer concisely.
73871
+ `);
73872
+ /**
73873
+ * User message used by `ptbk coder ping` to produce a deterministic response.
73874
+ */
73875
+ const PING_MESSAGE = 'Reply with exactly the single word PONG and nothing else.';
73876
+ /**
73877
+ * Runs one small harness/model turn in a disposable temporary project.
73878
+ *
73879
+ * The temporary project is outside the caller's repository, so even a harness
73880
+ * that writes files cannot change the project from which `ptbk coder ping` was
73881
+ * started.
73882
+ */
73883
+ async function runCoderPing(options) {
73884
+ const temporaryProjectPath = await mkdtemp(join(tmpdir(), 'promptbook-coder-ping-'));
73885
+ const originalWorkingDirectory = process.cwd();
73886
+ try {
73887
+ const agentPath = join(temporaryProjectPath, 'ping.book');
73888
+ await writeFile(agentPath, `${PING_AGENT_SOURCE}\n`, 'utf-8');
73889
+ // Note: Some supported CLI wrappers inherit the Node process working directory instead of using projectPath.
73890
+ process.chdir(temporaryProjectPath);
73891
+ const startedAt = performance.now();
73892
+ const result = await executeAgentChatTurn({
73893
+ agentPath,
73894
+ currentWorkingDirectory: temporaryProjectPath,
73895
+ agentName: options.agentName,
73896
+ model: options.model,
73897
+ isVerbose: false,
73898
+ noUi: options.isUiDisabled,
73899
+ thinkingLevel: options.thinkingLevel,
73900
+ allowCredits: options.isCreditsAllowed,
73901
+ messages: [
73902
+ {
73903
+ sender: 'USER',
73904
+ content: PING_MESSAGE,
73905
+ },
73906
+ ],
73907
+ });
73908
+ return {
73909
+ result: result.answer,
73910
+ elapsedTimeMs: performance.now() - startedAt,
73911
+ };
73912
+ }
73913
+ finally {
73914
+ process.chdir(originalWorkingDirectory);
73915
+ await rm(temporaryProjectPath, { recursive: true, force: true });
73916
+ }
73917
+ }
73918
+
73919
+ var runCoderPing$1 = /*#__PURE__*/Object.freeze({
73920
+ __proto__: null,
73921
+ runCoderPing: runCoderPing
73922
+ });
73923
+
73804
73924
  /**
73805
73925
  * Default wait duration applied before retrying a failed prompt round.
73806
73926
  */
@@ -73808,7 +73928,7 @@ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
73808
73928
  /**
73809
73929
  * CLI usage text for this script.
73810
73930
  */
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]';
73931
+ 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
73932
  /**
73813
73933
  * Top-level flags supported by this command.
73814
73934
  */
@@ -73818,6 +73938,7 @@ const KNOWN_OPTION_FLAGS = new Set([
73818
73938
  '--model',
73819
73939
  '--context',
73820
73940
  '--test',
73941
+ '--test-before',
73821
73942
  '--preserve-logs',
73822
73943
  '--isolate',
73823
73944
  '--no-ui',
@@ -73856,6 +73977,8 @@ function parseRunOptions(args) {
73856
73977
  const context = readOptionValue(args, '--context');
73857
73978
  const hasTestCommandFlag = args.includes('--test');
73858
73979
  const testCommand = readVariadicOptionValue(args, '--test');
73980
+ const hasTestBeforeFlag = args.includes('--test-before');
73981
+ const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
73859
73982
  const preserveLogs = args.includes('--preserve-logs');
73860
73983
  const isIsolated = args.includes('--isolate');
73861
73984
  const noUi = args.includes('--no-ui');
@@ -73928,6 +74051,7 @@ function parseRunOptions(args) {
73928
74051
  model,
73929
74052
  context,
73930
74053
  testCommand,
74054
+ testBefore,
73931
74055
  thinkingLevel,
73932
74056
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
73933
74057
  minimumPriority,
@@ -73936,6 +74060,21 @@ function parseRunOptions(args) {
73936
74060
  limit,
73937
74061
  };
73938
74062
  }
74063
+ /**
74064
+ * Parses and validates the optional pre-coding verification mode.
74065
+ */
74066
+ function parseTestBeforeOption(value, hasTestBeforeFlag) {
74067
+ if (value === undefined) {
74068
+ if (hasTestBeforeFlag) {
74069
+ exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74070
+ }
74071
+ return 'no';
74072
+ }
74073
+ if (!isTestBeforeMode(value)) {
74074
+ exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
74075
+ }
74076
+ return value;
74077
+ }
73939
74078
  /**
73940
74079
  * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
73941
74080
  */
@@ -76050,6 +76189,16 @@ function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
76050
76189
  return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
76051
76190
  }
76052
76191
 
76192
+ /**
76193
+ * Replaces the complete todo status line while preserving its indentation.
76194
+ *
76195
+ * The complete line is replaced because a todo status can contain a required
76196
+ * model/harness token in addition to priority markers.
76197
+ */
76198
+ function replacePromptTodoStatusLine(line, replacementStatusLine) {
76199
+ return line.replace(/^(?<indentation>\s*)\[\s*\].*$/u, `$<indentation>${replacementStatusLine}`);
76200
+ }
76201
+
76053
76202
  /**
76054
76203
  * Marks a prompt section as done and records the per-step usage pricing and runner details.
76055
76204
  */
@@ -76067,8 +76216,8 @@ function markPromptDone(file, section, steps, runnerName, modelName, attemptCoun
76067
76216
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
76068
76217
  const stepsSummary = formatCoderRunSteps(steps);
76069
76218
  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}`);
76219
+ // Replace the complete todo status, including any required model/harness token.
76220
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
76072
76221
  }
76073
76222
 
76074
76223
  /**
@@ -76086,8 +76235,10 @@ function markPromptFailed(file, section, runnerName, modelName, promptExecutionS
76086
76235
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
76087
76236
  const duration = moment().diff(promptExecutionStartedDate);
76088
76237
  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}`);
76238
+ const failureDetails = attemptMetadata === ''
76239
+ ? `failed after ${durationString} by ${runnerSignature}`
76240
+ : `${attemptMetadata}${durationString} by ${runnerSignature}`;
76241
+ file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[!] ${failureDetails}`);
76091
76242
  }
76092
76243
 
76093
76244
  /**
@@ -77057,17 +77208,18 @@ function toProjectRelativeGitPath(projectPath, path) {
77057
77208
  /**
77058
77209
  * Lists todo prompts that are ready to run (no authoring placeholders).
77059
77210
  */
77060
- function listRunnablePrompts(files, priorityFilter = {}) {
77211
+ function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
77061
77212
  return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
77062
- isPromptInPriorityFilter(prompt.section, priorityFilter));
77213
+ isPromptInPriorityFilter(prompt.section, priorityFilter) &&
77214
+ isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
77063
77215
  }
77064
77216
 
77065
77217
  /**
77066
77218
  * Selects the next runnable prompt based on priority.
77067
77219
  */
77068
- function findNextTodoPrompt(files, priorityFilter = {}) {
77220
+ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
77069
77221
  let nextPrompt;
77070
- for (const prompt of listRunnablePrompts(files, priorityFilter)) {
77222
+ for (const prompt of listRunnablePrompts(files, priorityFilter, promptRunnerIdentity)) {
77071
77223
  if (!nextPrompt || prompt.section.priority > nextPrompt.section.priority) {
77072
77224
  nextPrompt = prompt;
77073
77225
  }
@@ -77078,8 +77230,8 @@ function findNextTodoPrompt(files, priorityFilter = {}) {
77078
77230
  /**
77079
77231
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
77080
77232
  */
77081
- function listUpcomingTasks(files, priorityFilter = {}) {
77082
- return listRunnablePrompts(files, priorityFilter).map(({ file, section }) => ({
77233
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
77234
+ return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
77083
77235
  label: buildPromptLabelForDisplay(file, section),
77084
77236
  summary: buildPromptSummary(file, section),
77085
77237
  priority: section.priority,
@@ -77223,6 +77375,71 @@ async function buildCoderRunAgentVisual(agentSource) {
77223
77375
  }
77224
77376
  }
77225
77377
 
77378
+ /**
77379
+ * Creates the one queue prompt used to repair a pre-existing test failure.
77380
+ */
77381
+ async function createTestBeforeRepairPrompt(options) {
77382
+ const description = spaceTrim((block) => `
77383
+ Fix the existing test failures before implementing any queued coding tasks.
77384
+
77385
+ 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.
77386
+
77387
+ ## Verification output
77388
+
77389
+ \`\`\`
77390
+ ${block(limitTestOutput(options.testOutput))}
77391
+ \`\`\`
77392
+ `);
77393
+ const createdPrompt = await addCoderPrompt({
77394
+ projectPath: options.projectPath,
77395
+ description,
77396
+ priority: 0,
77397
+ });
77398
+ const promptPath = join(options.projectPath, createdPrompt.filePath);
77399
+ const promptFile = parsePromptFile(promptPath, await readFile(promptPath, 'utf-8'));
77400
+ const section = promptFile.sections[0];
77401
+ if (!section) {
77402
+ throw new UnexpectedError(spaceTrim(`
77403
+ The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
77404
+ `));
77405
+ }
77406
+ return { file: promptFile, section };
77407
+ }
77408
+
77409
+ /**
77410
+ * Runs the configured verification command before the first coding prompt.
77411
+ */
77412
+ async function runTestBefore(options) {
77413
+ var _a, _b;
77414
+ const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
77415
+ const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
77416
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
77417
+ checkpointLabel: 'running initial tests before the agent coding starts',
77418
+ phase: 'verifying',
77419
+ statusMessage: initialTestStatusMessage,
77420
+ }));
77421
+ console.info(colors.gray(initialTestStatusMessage));
77422
+ try {
77423
+ const testOutput = await runPromptTestCommandExecutor({
77424
+ command: options.testCommand,
77425
+ projectPath: options.projectPath,
77426
+ scriptPath: buildTemporaryPromptScriptPath({
77427
+ projectPath: options.projectPath,
77428
+ scriptDirectoryName: 'coder-prompts',
77429
+ sourceFileName: 'test-before',
77430
+ }),
77431
+ });
77432
+ console.info(colors.green('Pre-coding tests passed.'));
77433
+ return { isPassed: true, testOutput };
77434
+ }
77435
+ catch (error) {
77436
+ const testOutput = formatUnknownErrorMessage(error);
77437
+ console.error(colors.red('Pre-coding tests failed.'));
77438
+ console.error(testOutput);
77439
+ return { isPassed: false, testOutput };
77440
+ }
77441
+ }
77442
+
77226
77443
  /**
77227
77444
  * Constant for prompts dir.
77228
77445
  */
@@ -77250,6 +77467,10 @@ async function runCodexPrompts(providedOptions) {
77250
77467
  return;
77251
77468
  }
77252
77469
  const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
77470
+ const promptRunnerIdentity = {
77471
+ harnessName: options.agentName,
77472
+ modelName: actualRunnerModel,
77473
+ };
77253
77474
  console.info(colors.green(`Running prompts with ${runner.name}`));
77254
77475
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
77255
77476
  await initializeRunUiAgentVisual(uiHandle, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentSource);
@@ -77264,6 +77485,7 @@ async function runCodexPrompts(providedOptions) {
77264
77485
  let previousRoundStartTime;
77265
77486
  let previousRoundEndTime;
77266
77487
  let completedRunCount = 0;
77488
+ let hasRunTestBefore = false;
77267
77489
  while (just(true)) {
77268
77490
  if (options.autoPull && !options.dryRun) {
77269
77491
  await waitForRequestedPause({
@@ -77276,6 +77498,35 @@ async function runCodexPrompts(providedOptions) {
77276
77498
  options,
77277
77499
  isRichUiEnabled,
77278
77500
  });
77501
+ if (!hasRunTestBefore && options.testBefore !== 'no') {
77502
+ await waitForRequestedPause({
77503
+ checkpointLabel: 'loading prompts before running initial tests',
77504
+ phase: 'loading',
77505
+ statusMessage: 'Loading prompts before running initial tests...',
77506
+ });
77507
+ await loadPromptQueueSnapshot({
77508
+ options,
77509
+ isRichUiEnabled,
77510
+ progressDisplay,
77511
+ uiHandle,
77512
+ promptRunnerIdentity,
77513
+ });
77514
+ }
77515
+ if (!hasRunTestBefore) {
77516
+ hasWaitedForStart = await runTestBeforeIfNeeded({
77517
+ options,
77518
+ runner,
77519
+ runnerMetadata,
77520
+ resolvedCoderContext,
77521
+ resolvedAgentSystemMessage,
77522
+ isRichUiEnabled,
77523
+ progressDisplay,
77524
+ uiHandle,
77525
+ waitForRequestedPause,
77526
+ hasWaitedForStart,
77527
+ });
77528
+ hasRunTestBefore = true;
77529
+ }
77279
77530
  await waitForRequestedPause({
77280
77531
  checkpointLabel: 'loading prompts',
77281
77532
  phase: 'loading',
@@ -77286,6 +77537,7 @@ async function runCodexPrompts(providedOptions) {
77286
77537
  isRichUiEnabled,
77287
77538
  progressDisplay,
77288
77539
  uiHandle,
77540
+ promptRunnerIdentity,
77289
77541
  });
77290
77542
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
77291
77543
  hasShownUpcomingTasks,
@@ -77293,6 +77545,7 @@ async function runCodexPrompts(providedOptions) {
77293
77545
  stats: promptQueueSnapshot.stats,
77294
77546
  priorityFilter: options.priorityFilter,
77295
77547
  isRichUiEnabled,
77548
+ promptRunnerIdentity,
77296
77549
  }));
77297
77550
  if (!promptQueueSnapshot.nextPrompt) {
77298
77551
  if (isEndAfterCurrentPromptRequested(completedRunCount)) {
@@ -77395,6 +77648,14 @@ async function runCodexPrompts(providedOptions) {
77395
77648
  * Validates cross-flag constraints before the run starts.
77396
77649
  */
77397
77650
  function validateRunCodexPromptOptions(options) {
77651
+ var _a;
77652
+ if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
77653
+ throw new NotAllowed(spaceTrim$1(`
77654
+ Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
77655
+
77656
+ Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
77657
+ `));
77658
+ }
77398
77659
  if (options.allowDestructiveAutoMigrate && !options.autoMigrate) {
77399
77660
  throw new DatabaseError(spaceTrim$1(`
77400
77661
  Flag \`--allow-destructive-auto-migrate\` requires \`--auto-migrate\`.
@@ -77465,20 +77726,103 @@ function createRunDisplays(options, runStartDate) {
77465
77726
  * Normalizes legacy and current priority options into one validated run option shape.
77466
77727
  */
77467
77728
  function normalizeRunOptions(options) {
77468
- var _a, _b, _c, _d, _e;
77729
+ var _a, _b, _c, _d, _e, _f, _g;
77730
+ const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
77731
+ const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
77469
77732
  const priorityFilter = normalizePriorityFilter({
77470
77733
  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,
77734
+ minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
77735
+ maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
77473
77736
  });
77474
77737
  return {
77475
77738
  ...options,
77476
- priority: (_e = priorityFilter.minimumPriority) !== null && _e !== void 0 ? _e : 0,
77739
+ testBefore,
77740
+ testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
77741
+ priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
77477
77742
  minimumPriority: priorityFilter.minimumPriority,
77478
77743
  maximumPriority: priorityFilter.maximumPriority,
77479
77744
  priorityFilter,
77480
77745
  };
77481
77746
  }
77747
+ /**
77748
+ * Runs the optional pre-coding verification and, when requested, its one repair prompt.
77749
+ */
77750
+ async function runTestBeforeIfNeeded(options) {
77751
+ const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, } = options;
77752
+ if (runOptions.testBefore === 'no') {
77753
+ return hasWaitedForStart;
77754
+ }
77755
+ if (!runOptions.testCommand) {
77756
+ throw new NotAllowed(spaceTrim$1(`
77757
+ ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
77758
+
77759
+ Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
77760
+ `));
77761
+ }
77762
+ if (!runOptions.ignoreGitChanges) {
77763
+ await waitForRequestedPause({
77764
+ checkpointLabel: 'checking the git working tree before testing',
77765
+ phase: 'loading',
77766
+ statusMessage: 'Checking the working tree before testing...',
77767
+ });
77768
+ await ensureWorkingTreeClean();
77769
+ }
77770
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
77771
+ const testBeforeResult = await runTestBefore({
77772
+ testCommand: runOptions.testCommand,
77773
+ projectPath: process.cwd(),
77774
+ waitForPauseCheckpoint: waitForRequestedPause,
77775
+ }).finally(() => {
77776
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
77777
+ });
77778
+ if (testBeforeResult.isPassed) {
77779
+ return hasWaitedForStart;
77780
+ }
77781
+ const testOutput = limitTestOutput(testBeforeResult.testOutput);
77782
+ if (runOptions.testBefore === 'yes-and-fail') {
77783
+ throw new NotAllowed(spaceTrim$1((block) => `
77784
+ Pre-coding verification command \`${runOptions.testCommand}\` failed.
77785
+
77786
+ The coding agent was not started because the project was already failing before the first queued prompt.
77787
+
77788
+ ### Test results
77789
+ ${'```'}
77790
+ ${block(testOutput)}
77791
+ ${'```'}
77792
+ `));
77793
+ }
77794
+ const repairPrompt = await createTestBeforeRepairPrompt({
77795
+ projectPath: process.cwd(),
77796
+ testCommand: runOptions.testCommand,
77797
+ testOutput,
77798
+ });
77799
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
77800
+ const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
77801
+ options: runOptions,
77802
+ nextPrompt: repairPrompt,
77803
+ promptLabel: repairPromptLabel,
77804
+ hasWaitedForStart,
77805
+ isRichUiEnabled,
77806
+ progressDisplay,
77807
+ uiHandle,
77808
+ });
77809
+ // The repair prompt is created in the current worktree so it can be recorded and committed with the repair.
77810
+ // It therefore intentionally uses the regular round here even when the queue itself uses --isolate.
77811
+ await runPromptRound({
77812
+ options: runOptions,
77813
+ runner,
77814
+ runnerMetadata,
77815
+ nextPrompt: repairPrompt,
77816
+ promptLabel: repairPromptLabel,
77817
+ resolvedCoderContext,
77818
+ resolvedAgentSystemMessage,
77819
+ isRichUiEnabled,
77820
+ progressDisplay,
77821
+ uiHandle,
77822
+ waitForRequestedPause,
77823
+ });
77824
+ return updatedHasWaitedForStart;
77825
+ }
77482
77826
  /**
77483
77827
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
77484
77828
  */
@@ -77563,7 +77907,7 @@ async function initializeRunUiAgentVisual(uiHandle, agentSource) {
77563
77907
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
77564
77908
  */
77565
77909
  async function loadPromptQueueSnapshot(options) {
77566
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
77910
+ const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity } = options;
77567
77911
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
77568
77912
  const promptFiles = await loadPromptFiles(PROMPTS_DIR$1);
77569
77913
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
@@ -77575,14 +77919,14 @@ async function loadPromptQueueSnapshot(options) {
77575
77919
  return {
77576
77920
  promptFiles,
77577
77921
  stats,
77578
- nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter),
77922
+ nextPrompt: findNextTodoPrompt(promptFiles, runOptions.priorityFilter, promptRunnerIdentity),
77579
77923
  };
77580
77924
  }
77581
77925
  /**
77582
77926
  * Prints upcoming tasks only on the first loop iteration in plain-console mode.
77583
77927
  */
77584
77928
  function showUpcomingTasksOnce(options) {
77585
- const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled } = options;
77929
+ const { hasShownUpcomingTasks, promptFiles, stats, priorityFilter, isRichUiEnabled, promptRunnerIdentity } = options;
77586
77930
  if (hasShownUpcomingTasks || isRichUiEnabled) {
77587
77931
  return true;
77588
77932
  }
@@ -77591,7 +77935,7 @@ function showUpcomingTasksOnce(options) {
77591
77935
  printPromptsToBeWritten(promptFiles, priorityFilter);
77592
77936
  console.info('');
77593
77937
  }
77594
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter));
77938
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
77595
77939
  return true;
77596
77940
  }
77597
77941
  /**
@@ -77601,7 +77945,10 @@ function finishWhenNoPromptIsAvailable(promptQueueSnapshot, isRichUiEnabled, uiH
77601
77945
  if (promptQueueSnapshot.nextPrompt) {
77602
77946
  return false;
77603
77947
  }
77604
- if (promptQueueSnapshot.stats.toBeWritten > 0) {
77948
+ if (promptQueueSnapshot.stats.forAgent > 0) {
77949
+ announceRunCompletion('No prompts match the selected harness or model.', colors.yellow, isRichUiEnabled, uiHandle);
77950
+ }
77951
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
77605
77952
  announceRunCompletion('No prompts ready for agent.', colors.yellow, isRichUiEnabled, uiHandle);
77606
77953
  }
77607
77954
  else {
@@ -77642,9 +77989,16 @@ function finishWhenEndAfterCurrentPromptIsRequested(options) {
77642
77989
  * Updates the UI status message while waiting for new prompts in keepAlive server mode.
77643
77990
  */
77644
77991
  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...';
77992
+ let message;
77993
+ if (promptQueueSnapshot.stats.forAgent > 0) {
77994
+ message = 'No prompts match the selected harness or model. Watching for changes...';
77995
+ }
77996
+ else if (promptQueueSnapshot.stats.toBeWritten > 0) {
77997
+ message = 'No prompts ready for agent. Watching for changes...';
77998
+ }
77999
+ else {
78000
+ message = 'All prompts are done. Watching for changes...';
78001
+ }
77648
78002
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
77649
78003
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
77650
78004
  if (!isRichUiEnabled) {
@@ -77666,7 +78020,7 @@ function announceRunCompletion(message, colorize, isRichUiEnabled, uiHandle) {
77666
78020
  * Waits for the optional user confirmation before starting the selected prompt.
77667
78021
  */
77668
78022
  async function waitForPromptConfirmationIfNeeded(options) {
77669
- const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle } = options;
78023
+ const { options: runOptions, nextPrompt, promptLabel, hasWaitedForStart, isRichUiEnabled, progressDisplay, uiHandle, } = options;
77670
78024
  if (!runOptions.waitForUser) {
77671
78025
  return hasWaitedForStart;
77672
78026
  }