@promptbook/cli 0.114.0-48 → 0.114.0-49

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 (153) hide show
  1. package/README.md +59 -25
  2. package/apps/agents-server/tests/e2e/mockSupabaseServer.cjs +79 -2
  3. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +12 -2
  4. package/apps/agents-server/tests/e2e/support/auth.ts +6 -0
  5. package/esm/index.es.js +1327 -478
  6. package/esm/index.es.js.map +1 -1
  7. package/esm/scripts/run-codex-prompts/checks/CheckBeforeMode.d.ts +20 -0
  8. package/esm/scripts/run-codex-prompts/checks/checkRepairInstructions.d.ts +4 -0
  9. package/esm/scripts/run-codex-prompts/checks/createCheckBeforeRepairPrompt.d.ts +9 -0
  10. package/esm/scripts/run-codex-prompts/checks/limitCheckOutput.d.ts +4 -0
  11. package/esm/scripts/run-codex-prompts/checks/projectCheck.d.ts +26 -0
  12. package/esm/scripts/run-codex-prompts/checks/projectScriptReferences.d.ts +7 -0
  13. package/esm/scripts/run-codex-prompts/checks/runCheckBefore.d.ts +26 -0
  14. package/esm/scripts/run-codex-prompts/checks/runPromptCheckCommand.d.ts +16 -0
  15. package/esm/scripts/run-codex-prompts/{testing/runPromptWithTestFeedback.d.ts → checks/runPromptWithCheckFeedback.d.ts} +9 -9
  16. package/esm/scripts/run-codex-prompts/common/resolveCoderContext.d.ts +1 -1
  17. package/esm/scripts/run-codex-prompts/common/resolveCoderProjectContext.d.ts +17 -0
  18. package/esm/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +2 -0
  19. package/esm/scripts/run-codex-prompts/common/runGoScript/quoteBashArgument.d.ts +2 -0
  20. package/esm/scripts/run-codex-prompts/isolation/mapProjectReferenceToWorktree.d.ts +5 -0
  21. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +3 -1
  22. package/esm/scripts/run-codex-prompts/prompts/buildPromptLabel.d.ts +1 -1
  23. package/esm/scripts/run-codex-prompts/prompts/buildPromptLabelForDisplay.d.ts +1 -1
  24. package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +3 -1
  25. package/esm/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +2 -2
  26. package/esm/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +1 -1
  27. package/esm/scripts/run-codex-prompts/prompts/printPromptsToBeWritten.d.ts +1 -1
  28. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +2 -0
  29. package/esm/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +1 -1
  30. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -1
  31. package/esm/src/cli/cli-commands/coder/agentCliOptions.d.ts +5 -0
  32. package/esm/src/cli/cli-commands/coder/coderAgentRole.d.ts +5 -0
  33. package/esm/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +2 -2
  34. package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +3 -2
  35. package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.test.d.ts +1 -0
  36. package/esm/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.d.ts +5 -0
  37. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +6 -0
  38. package/esm/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +5 -3
  39. package/esm/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +3 -0
  40. package/esm/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.d.ts +12 -0
  41. package/esm/src/cli/cli-commands/coder.d.ts +1 -1
  42. package/esm/src/cli/cli-commands/common/coderCheckCliOptions.d.ts +21 -0
  43. package/esm/src/cli/cli-commands/common/coderCheckCliOptions.test.d.ts +1 -0
  44. package/esm/src/cli/cli-commands/common/projectCliOptions.d.ts +30 -0
  45. package/esm/src/cli/cli-commands/common/projectCliOptions.test.d.ts +1 -0
  46. package/esm/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +1 -1
  47. package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +1 -1
  48. package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +1 -1
  49. package/esm/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +1 -1
  50. package/esm/src/version.d.ts +1 -1
  51. package/package.json +1 -1
  52. package/src/_packages/cli.readme.md +15 -0
  53. package/src/cli/cli-commands/agent/chat.ts +11 -2
  54. package/src/cli/cli-commands/agent/exec.ts +11 -2
  55. package/src/cli/cli-commands/agent-folder/init.ts +6 -2
  56. package/src/cli/cli-commands/agent-folder/initializeAgentRunnerCommand.ts +9 -2
  57. package/src/cli/cli-commands/coder/add.ts +7 -1
  58. package/src/cli/cli-commands/coder/agentCliOptions.ts +11 -1
  59. package/src/cli/cli-commands/coder/coderAgentRole.ts +6 -0
  60. package/src/cli/cli-commands/coder/coderReferencedArtifacts.ts +3 -3
  61. package/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts +22 -3
  62. package/src/cli/cli-commands/coder/find-fresh-emoji-tags.ts +3 -0
  63. package/src/cli/cli-commands/coder/find-refactor-candidates.ts +3 -0
  64. package/src/cli/cli-commands/coder/find-unwritten.ts +3 -0
  65. package/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.ts +87 -0
  66. package/src/cli/cli-commands/coder/generate-boilerplates.ts +3 -0
  67. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +6 -7
  68. package/src/cli/cli-commands/coder/init.ts +8 -2
  69. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +16 -4
  70. package/src/cli/cli-commands/coder/list.ts +6 -1
  71. package/src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts +14 -4
  72. package/src/cli/cli-commands/coder/ping.ts +7 -1
  73. package/src/cli/cli-commands/coder/plan.ts +17 -5
  74. package/src/cli/cli-commands/coder/planning/README.md +5 -3
  75. package/src/cli/cli-commands/coder/planning/fixtures/coding.cjs +22 -0
  76. package/src/cli/cli-commands/coder/planning/runPlanningSession.ts +14 -3
  77. package/src/cli/cli-commands/coder/planning/savePlanningChanges.ts +1 -3
  78. package/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.ts +304 -0
  79. package/src/cli/cli-commands/coder/printInitializationSummary.ts +5 -1
  80. package/src/cli/cli-commands/coder/promptsReadmeTemplate.ts +31 -13
  81. package/src/cli/cli-commands/coder/run.ts +31 -65
  82. package/src/cli/cli-commands/coder/server.ts +21 -39
  83. package/src/cli/cli-commands/coder/verify.ts +3 -0
  84. package/src/cli/cli-commands/coder.ts +2 -2
  85. package/src/cli/cli-commands/common/coderCheckCliOptions.ts +107 -0
  86. package/src/cli/cli-commands/common/projectCliOptions.ts +78 -0
  87. package/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.ts +4 -2
  88. package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +3 -2
  89. package/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.ts +8 -4
  90. package/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.ts +5 -2
  91. package/src/cli/cli-commands/common/validateCoderRunOptions.ts +12 -5
  92. package/src/cli/cli-commands/common/workspaceRepository.ts +7 -19
  93. package/src/cli/cli-commands/common/workspaceRepositoryCliOptions.ts +2 -0
  94. package/src/execution/translation/automatic-translate/translateMessages.ts +1 -3
  95. package/src/other/templates/getTemplatesPipelineCollection.ts +1074 -1210
  96. package/src/version.ts +2 -2
  97. package/src/versions.txt +1 -0
  98. package/umd/index.umd.js +1324 -475
  99. package/umd/index.umd.js.map +1 -1
  100. package/umd/scripts/run-codex-prompts/checks/CheckBeforeMode.d.ts +20 -0
  101. package/umd/scripts/run-codex-prompts/checks/checkRepairInstructions.d.ts +4 -0
  102. package/umd/scripts/run-codex-prompts/checks/createCheckBeforeRepairPrompt.d.ts +9 -0
  103. package/umd/scripts/run-codex-prompts/checks/limitCheckOutput.d.ts +4 -0
  104. package/umd/scripts/run-codex-prompts/checks/projectCheck.d.ts +26 -0
  105. package/umd/scripts/run-codex-prompts/checks/projectScriptReferences.d.ts +7 -0
  106. package/umd/scripts/run-codex-prompts/checks/runCheckBefore.d.ts +26 -0
  107. package/umd/scripts/run-codex-prompts/checks/runPromptCheckCommand.d.ts +16 -0
  108. package/umd/scripts/run-codex-prompts/{testing/runPromptWithTestFeedback.d.ts → checks/runPromptWithCheckFeedback.d.ts} +9 -9
  109. package/umd/scripts/run-codex-prompts/common/resolveCoderContext.d.ts +1 -1
  110. package/umd/scripts/run-codex-prompts/common/resolveCoderProjectContext.d.ts +17 -0
  111. package/umd/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +2 -0
  112. package/umd/scripts/run-codex-prompts/common/runGoScript/quoteBashArgument.d.ts +2 -0
  113. package/umd/scripts/run-codex-prompts/isolation/mapProjectReferenceToWorktree.d.ts +5 -0
  114. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +3 -1
  115. package/umd/scripts/run-codex-prompts/prompts/buildPromptLabel.d.ts +1 -1
  116. package/umd/scripts/run-codex-prompts/prompts/buildPromptLabelForDisplay.d.ts +1 -1
  117. package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +3 -1
  118. package/umd/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +2 -2
  119. package/umd/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +1 -1
  120. package/umd/scripts/run-codex-prompts/prompts/printPromptsToBeWritten.d.ts +1 -1
  121. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +2 -0
  122. package/umd/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +1 -1
  123. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -1
  124. package/umd/src/cli/cli-commands/coder/agentCliOptions.d.ts +5 -0
  125. package/umd/src/cli/cli-commands/coder/coderAgentRole.d.ts +5 -0
  126. package/umd/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +2 -2
  127. package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +3 -2
  128. package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.test.d.ts +1 -0
  129. package/umd/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.d.ts +5 -0
  130. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +6 -0
  131. package/umd/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +5 -3
  132. package/umd/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +3 -0
  133. package/umd/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.d.ts +12 -0
  134. package/umd/src/cli/cli-commands/coder.d.ts +1 -1
  135. package/umd/src/cli/cli-commands/common/coderCheckCliOptions.d.ts +21 -0
  136. package/umd/src/cli/cli-commands/common/coderCheckCliOptions.test.d.ts +1 -0
  137. package/umd/src/cli/cli-commands/common/projectCliOptions.d.ts +30 -0
  138. package/umd/src/cli/cli-commands/common/projectCliOptions.test.d.ts +1 -0
  139. package/umd/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +1 -1
  140. package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +1 -1
  141. package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +1 -1
  142. package/umd/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +1 -1
  143. package/umd/src/version.d.ts +1 -1
  144. package/esm/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +0 -16
  145. package/esm/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +0 -9
  146. package/esm/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +0 -4
  147. package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +0 -14
  148. package/esm/scripts/run-codex-prompts/testing/runTestBefore.d.ts +0 -24
  149. package/umd/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +0 -16
  150. package/umd/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +0 -9
  151. package/umd/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +0 -4
  152. package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +0 -14
  153. package/umd/scripts/run-codex-prompts/testing/runTestBefore.d.ts +0 -24
package/umd/index.umd.js CHANGED
@@ -58,7 +58,7 @@
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-48';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-49';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1753,6 +1753,56 @@
1753
1753
  return command.help();
1754
1754
  }
1755
1755
 
1756
+ /**
1757
+ * Registers project selection without capturing a directory during command registration.
1758
+ * @private internal CLI registration helper
1759
+ */
1760
+ function addProjectPathOption(command) {
1761
+ command.option('--path <directory>', 'Project directory (default: invocation current directory); relative paths start there');
1762
+ }
1763
+ /**
1764
+ * Registers additional context independently of primary-agent selection.
1765
+ * @private internal CLI registration helper
1766
+ */
1767
+ function addProjectContextOption(command) {
1768
+ command.option('--context <context-or-file>', 'Additional instructions (default: project AGENTS.md); inline text or a project-relative file replaces the default; an empty value disables it');
1769
+ }
1770
+ /**
1771
+ * Captures an invocation's directory at action time. No process-global state is changed.
1772
+ * @private internal CLI normalization helper
1773
+ */
1774
+ function normalizeProjectCliOptions(options, invocationDirectory = process.cwd()) {
1775
+ const selectedPath = options.path;
1776
+ if (selectedPath !== undefined && !selectedPath.trim()) {
1777
+ throw new EnvironmentMismatchError(spaceTrim('Pass a non-empty project directory in `--path`, or omit it to use the current directory.'));
1778
+ }
1779
+ return { projectDirectory: path.resolve(invocationDirectory, selectedPath !== null && selectedPath !== void 0 ? selectedPath : '.') };
1780
+ }
1781
+ /**
1782
+ * Validates the selected directory before any setup writes, without selecting an enclosing Git root.
1783
+ * @private shared project resolution for CLI services
1784
+ */
1785
+ async function resolveProjectDirectory(projectDirectory) {
1786
+ const requestedPath = path.resolve(projectDirectory);
1787
+ try {
1788
+ const projectPath = await promises.realpath(requestedPath);
1789
+ if (!(await promises.stat(projectPath)).isDirectory()) {
1790
+ throw new EnvironmentMismatchError(spaceTrim(`Project path \`${requestedPath}\` is not a directory.`));
1791
+ }
1792
+ await promises.access(projectPath, fs.constants.R_OK | fs.constants.X_OK);
1793
+ return projectPath;
1794
+ }
1795
+ catch (error) {
1796
+ throw new EnvironmentMismatchError(spaceTrim(`
1797
+ Cannot access project directory \`${requestedPath}\`.
1798
+ Check that it exists, is a directory, and is readable and searchable; correct \`--path\` before retrying.
1799
+
1800
+ ${error instanceof Error ? error.message : String(error)}
1801
+ `));
1802
+ }
1803
+ }
1804
+ // Note: [💞] Shared project registration and resolution helpers.
1805
+
1756
1806
  // Note: [💞] Ignore a discrepancy between file name and entity name
1757
1807
  /**
1758
1808
  * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
@@ -2112,15 +2162,18 @@
2112
2162
  command.requiredOption('--agent <agent-book-path>', 'Path to the agent .book file');
2113
2163
  addPromptRunnerSelectionOptions(command);
2114
2164
  addPromptRunnerRuntimeOptions(command);
2115
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
2165
+ addProjectPathOption(command);
2166
+ addProjectContextOption(command);
2116
2167
  command.action(handleActionErrors(async (cliOptions, commandProgram) => {
2168
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2169
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2117
2170
  const options = cliOptions;
2118
2171
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram);
2119
2172
  const { runAgentChat } = await Promise.resolve().then(function () { return runAgentChat$1; });
2120
2173
  await runAgentChat({
2121
2174
  agentPath: resolveRequiredAgentPath(options),
2122
2175
  context: options.context,
2123
- currentWorkingDirectory: process.cwd(),
2176
+ currentWorkingDirectory: projectPath,
2124
2177
  ...runnerOptions,
2125
2178
  });
2126
2179
  }));
@@ -2146,8 +2199,11 @@
2146
2199
  command.requiredOption('--message <message>', 'User message to send to the agent');
2147
2200
  addPromptRunnerSelectionOptions(command);
2148
2201
  addPromptRunnerRuntimeOptions(command);
2149
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
2202
+ addProjectPathOption(command);
2203
+ addProjectContextOption(command);
2150
2204
  command.action(handleActionErrors(async (cliOptions, commandProgram) => {
2205
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2206
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2151
2207
  const options = cliOptions;
2152
2208
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram);
2153
2209
  const { runAgentExec } = await Promise.resolve().then(function () { return runAgentExec$1; });
@@ -2155,7 +2211,7 @@
2155
2211
  agentPath: resolveRequiredAgentPath(options),
2156
2212
  context: options.context,
2157
2213
  message: resolveRequiredAgentMessage(options),
2158
- currentWorkingDirectory: process.cwd(),
2214
+ currentWorkingDirectory: projectPath,
2159
2215
  ...runnerOptions,
2160
2216
  });
2161
2217
  }));
@@ -2459,8 +2515,11 @@
2459
2515
  - ${AGENT_BOOK_FILE_PATH}
2460
2516
  - ${AGENT_BOOK_LANGUAGE_MANUAL_FILE_PATH.replace(/\\/gu, '/')}
2461
2517
  `));
2462
- command.action(handleActionErrors(async () => {
2463
- const summary = await initializeAgentProjectConfiguration(process.cwd());
2518
+ addProjectPathOption(command);
2519
+ command.action(handleActionErrors(async (cliOptions) => {
2520
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2521
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2522
+ const summary = await initializeAgentProjectConfiguration(projectPath);
2464
2523
  printAgentInitializationSummary(summary);
2465
2524
  }));
2466
2525
  }
@@ -2515,15 +2574,21 @@
2515
2574
  `));
2516
2575
  addPromptRunnerSelectionOptions(command);
2517
2576
  addPromptRunnerExecutionOptions(command);
2577
+ addProjectPathOption(command);
2518
2578
  (_a = options.configureCommand) === null || _a === void 0 ? void 0 : _a.call(options, command);
2519
2579
  command.action(handleActionErrors(async (cliOptions) => {
2520
- const runOptions = createAgentRunOptionsFromCliOptions(cliOptions);
2580
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2581
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2582
+ const runOptions = {
2583
+ ...createAgentRunOptionsFromCliOptions(cliOptions),
2584
+ projectPath,
2585
+ };
2521
2586
  const execute = await options.loadExecutor();
2522
2587
  if (options.executionMode === 'watch') {
2523
2588
  const { runPersistentAgentWatch } = await Promise.resolve().then(function () { return runPersistentAgentWatch$1; });
2524
2589
  await runPersistentAgentWatch({
2525
2590
  commandDisplayName: `ptbk agent-folder ${options.commandName}`,
2526
- logDirectoryPath: process.cwd(),
2591
+ logDirectoryPath: projectPath,
2527
2592
  runWatch: async () => {
2528
2593
  await execute(runOptions);
2529
2594
  },
@@ -24757,7 +24822,7 @@
24757
24822
  * Distinguishes prompt-runner and verification temp shells in the shared runtime log.
24758
24823
  */
24759
24824
  function describeTempScriptKind(scriptPath) {
24760
- return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
24825
+ return scriptPath.toLowerCase().endsWith('.check.sh') ? 'check shell' : 'runner shell';
24761
24826
  }
24762
24827
 
24763
24828
  /**
@@ -24960,28 +25025,44 @@
24960
25025
  /**
24961
25026
  * Handles process exit and resolves or rejects accordingly.
24962
25027
  */
24963
- const handleExit = (code) => {
25028
+ const handleExit = (code, signal) => {
24964
25029
  var _a;
24965
25030
  if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
24966
25031
  settleWithLog('cancelled', () => reject(options.signal.reason), options.signal.reason);
24967
25032
  return;
24968
25033
  }
24969
- if (code === 0) {
24970
- settleWithLog('succeeded', () => resolve(_spaceTrim.spaceTrim(output)));
25034
+ if (code === 0 && !signal) {
25035
+ settleWithLog('succeeded', () => {
25036
+ var _a;
25037
+ // Cancellation can arrive while the runtime log footer is being flushed.
25038
+ if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
25039
+ reject(options.signal.reason);
25040
+ else
25041
+ resolve(_spaceTrim.spaceTrim(output));
25042
+ });
24971
25043
  return;
24972
25044
  }
24973
- const failure = new Error(_spaceTrim.spaceTrim(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
24974
- settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
25045
+ const failure = new NotAllowed(_spaceTrim.spaceTrim((block) => `
25046
+ Command "bash ${scriptPathPosix}" exited with code ${code !== null && code !== void 0 ? code : 'unknown'}${signal ? ` and signal ${signal}` : ''}.
25047
+ ${block(output)}
25048
+ `));
25049
+ settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}${signal ? ` and signal ${signal}` : ''}`, () => reject(failure), failure);
24975
25050
  };
24976
25051
  // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
24977
25052
  // substitutions after the direct shell exits.
24978
25053
  commandProcess.on('close', handleExit);
24979
25054
  commandProcess.on('disconnect', () => {
24980
- const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
25055
+ const failure = new NotAllowed(_spaceTrim.spaceTrim((block) => `
25056
+ Command "bash ${scriptPathPosix}" disconnected.
25057
+ ${block(output)}
25058
+ `));
24981
25059
  settleWithLog('failed after disconnect', () => reject(failure), failure);
24982
25060
  });
24983
25061
  commandProcess.on('error', (error) => {
24984
- const failure = new Error(`Command "bash ${scriptPathPosix}" failed: ${error.message}`);
25062
+ const failure = new NotAllowed(_spaceTrim.spaceTrim((block) => `
25063
+ Command "bash ${scriptPathPosix}" failed: ${error.message}
25064
+ ${block(output)}
25065
+ `));
24985
25066
  settleWithLog('failed before completion', () => reject(failure), failure);
24986
25067
  });
24987
25068
  /** Reuses the process-tree terminator; settlement waits for stream closure and the trace footer. */
@@ -26437,6 +26518,11 @@
26437
26518
  }
26438
26519
  }
26439
26520
 
26521
+ /** Quotes one literal argument, including spaces and shell metacharacters, for a generated Bash script. */
26522
+ function quoteBashArgument(value) {
26523
+ return `'${value.replace(/'/gu, "'\\''")}'`;
26524
+ }
26525
+
26440
26526
  /**
26441
26527
  * Builds the shell script that runs GitHub Copilot CLI with the prompt and coding context.
26442
26528
  */
@@ -26446,7 +26532,7 @@
26446
26532
  const modelArgument = options.model ? ` --model ${options.model}` : '';
26447
26533
  const thinkingLevelArgument = options.thinkingLevel ? ` --reasoning-effort ${options.thinkingLevel}` : '';
26448
26534
  return spaceTrim((block) => `
26449
- cd "${projectPath}"
26535
+ cd ${quoteBashArgument(projectPath)}
26450
26536
 
26451
26537
  if [ -f .env ]; then
26452
26538
  set -a
@@ -26887,7 +26973,7 @@
26887
26973
  ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
26888
26974
  ' --local-provider none \\',
26889
26975
  ` --sandbox ${options.sandbox} \\`,
26890
- ` -C ${projectPath} \\`,
26976
+ ` -C ${quoteBashArgument(projectPath)} \\`,
26891
26977
  ' --skip-git-repo-check \\',
26892
26978
  ` <<'${delimiter}'`,
26893
26979
  '',
@@ -29223,7 +29309,7 @@
29223
29309
  ...buildOptionalSessionRow('Server', options.config.serverUrl
29224
29310
  ? buildTerminalUrlLink(options.config.serverUrl, bodyWidth - SESSION_LABEL_WIDTH - 1)
29225
29311
  : undefined),
29226
- ...buildOptionalSessionRow('Test', options.config.testCommand),
29312
+ ...buildOptionalSessionRow('Check', options.config.checkCommand),
29227
29313
  ];
29228
29314
  return [
29229
29315
  {
@@ -31717,7 +31803,7 @@
31717
31803
  return {
31718
31804
  dryRun: false,
31719
31805
  context: undefined,
31720
- testCommand: undefined,
31806
+ checkCommand: undefined,
31721
31807
  preserveLogs: false,
31722
31808
  noUi: options.noUi,
31723
31809
  thinkingLevel: options.thinkingLevel,
@@ -32819,40 +32905,207 @@
32819
32905
  }
32820
32906
 
32821
32907
  /**
32822
- * Maximum amount of test output embedded into an agent prompt.
32908
+ * Maximum amount of check output embedded into an agent prompt.
32909
+ */
32910
+ const MAX_CHECK_OUTPUT_CHARS = 12000;
32911
+ /**
32912
+ * Limits check output while keeping the end of the output, where check tools usually print the failure summary.
32913
+ */
32914
+ function limitCheckOutput(checkOutput) {
32915
+ const normalizedCheckOutput = checkOutput.trim();
32916
+ if (normalizedCheckOutput.length <= MAX_CHECK_OUTPUT_CHARS) {
32917
+ return normalizedCheckOutput;
32918
+ }
32919
+ return `[..., check output truncated to the last ${MAX_CHECK_OUTPUT_CHARS} characters...]\n${normalizedCheckOutput.slice(-MAX_CHECK_OUTPUT_CHARS)}`;
32920
+ }
32921
+
32922
+ /**
32923
+ * Modes supported by `ptbk coder run --check-before`.
32924
+ */
32925
+ const CHECK_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
32926
+ /**
32927
+ * Default check command used when a pre-coding mode is enabled without an explicit `--check` command.
32928
+ */
32929
+ const DEFAULT_CODER_CHECK_COMMAND = 'npm run check';
32930
+ /**
32931
+ * Resolves the shared command without enabling checks when both command and preflight mode are absent.
32932
+ */
32933
+ function resolveCoderCheckCommand(checkCommand, checkBefore = 'no') {
32934
+ return checkCommand !== null && checkCommand !== void 0 ? checkCommand : (checkBefore === 'no' ? undefined : DEFAULT_CODER_CHECK_COMMAND);
32935
+ }
32936
+ /**
32937
+ * Checks whether a value is a supported `--check-before` mode.
32938
+ */
32939
+ function isCheckBeforeMode(value) {
32940
+ return CHECK_BEFORE_MODE_VALUES.includes(value);
32941
+ }
32942
+
32943
+ /** Conventional root command boundaries, including subshells and environment assignments. */
32944
+ const SCRIPT_COMMAND_PREFIX = String.raw `(?:^|&&|\|\||;|\n|[()])\s*(?:(?:env\s+)?[A-Za-z_]\w*=(?:[^\s'";&|()]+|"[^"]*"|'[^']*')\s+)*`;
32945
+ /** Package-manager options which do not select a different directory or workspace. */
32946
+ const QUIET_OPTIONS = String.raw `(?:(?:--silent|--quiet|-s|-q)\s+)*`;
32947
+ /** Quoted script names preserve spaces, while unquoted names use conventional npm identifier characters. */
32948
+ const SCRIPT_NAME = String.raw `(?:(["'])([^"'\r\n]+)\1|([\w:.-]+))(?=\s|$|[;&|()])`;
32949
+ /** Quoted payloads and escaped characters are opaque to this configuration inspection. */
32950
+ const OPAQUE_ARGUMENT = String.raw `"(?:\\.|[^"\\])*"|'[^']*'|\\[\s\S]`;
32951
+ /** Only references before a conventional directory change can confidently belong to the root package. */
32952
+ const DIRECTORY_CHANGE_COMMAND = new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:cd|pushd|popd)(?=\\s|$)|(?<argument>${OPAQUE_ARGUMENT})`, 'gu');
32953
+ /** Arguments up to the next conventional command boundary, preserving quoted payloads as opaque text. */
32954
+ const SCRIPT_ARGUMENTS = new RegExp(String.raw `^(?:${OPAQUE_ARGUMENT}|[^;&|\n()'"\\])*`, 'u');
32955
+ /** Package-manager options that select validation from a different directory or workspace. */
32956
+ const PROJECT_OVERRIDE_OPTION = /\s(?:--prefix|--dir|--cwd|-C|--workspace|--workspaces|-w|--filter)(?:\s|=|$)/u;
32957
+ /**
32958
+ * Reads conventional npm/pnpm/yarn script references for configuration inspection.
32959
+ * Commands are still executed verbatim by the existing shell runner; this helper neither parses nor rewrites shells.
32960
+ * Options selecting other workspaces remain opaque so their scripts are not mistaken for root validation.
32961
+ * Inspection stops at the first directory change; quoted examples do not change the inspected workspace.
32823
32962
  */
32824
- const MAX_TEST_OUTPUT_CHARS = 12000;
32963
+ function projectScriptReferences(command) {
32964
+ var _a;
32965
+ const directoryChange = [...command.matchAll(DIRECTORY_CHANGE_COMMAND)].find((match) => { var _a; return ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.argument) === undefined; });
32966
+ if (directoryChange)
32967
+ command = command.slice(0, directoryChange.index);
32968
+ const references = [
32969
+ ...command.matchAll(new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:npm|pnpm|yarn)\\s+${QUIET_OPTIONS}(?:run(?:-script)?\\s+${QUIET_OPTIONS}${SCRIPT_NAME}|(?:test|t)(?=\\s|$|[;&|()]))` +
32970
+ `|(?<argument>${OPAQUE_ARGUMENT})`, 'gu')),
32971
+ ]
32972
+ .filter((match) => isRootProjectScriptReference(command, match))
32973
+ .map((match) => { var _a, _b; return (_b = (_a = match[2]) !== null && _a !== void 0 ? _a : match[3]) !== null && _b !== void 0 ? _b : 'test'; });
32974
+ for (const match of command.matchAll(new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:pnpm|yarn)\\s+${QUIET_OPTIONS}(?!run\\b|-)${SCRIPT_NAME}` +
32975
+ `|(?<argument>${OPAQUE_ARGUMENT})`, 'gu')))
32976
+ if (isRootProjectScriptReference(command, match))
32977
+ references.push(((_a = match[2]) !== null && _a !== void 0 ? _a : match[3]));
32978
+ return [...new Set(references)];
32979
+ }
32980
+ /** Excludes quoted examples and package-manager directory overrides without altering the executed command. */
32981
+ function isRootProjectScriptReference(command, match) {
32982
+ var _a, _b, _c, _d;
32983
+ if (((_a = match.groups) === null || _a === void 0 ? void 0 : _a.argument) !== undefined)
32984
+ return false;
32985
+ const argumentsText = (_c = (_b = command.slice(match.index + match[0].length).match(SCRIPT_ARGUMENTS)) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : '';
32986
+ // Flags after npm's `--` separator belong to the leaf tool, rather than the package manager.
32987
+ const packageManagerArguments = (_d = argumentsText.split(/\s--(?:\s|$)/u)[0]) !== null && _d !== void 0 ? _d : '';
32988
+ return !PROJECT_OVERRIDE_OPTION.test(packageManagerArguments);
32989
+ }
32990
+
32825
32991
  /**
32826
- * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
32992
+ * Failing setup placeholder used only when initialization finds no usable project validation scripts.
32827
32993
  */
32828
- function limitTestOutput(testOutput) {
32829
- const normalizedTestOutput = testOutput.trim();
32830
- if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
32831
- return normalizedTestOutput;
32994
+ const CHECK_SETUP_PLACEHOLDER_COMMAND = `node -e "console.error('Configure package.json scripts.check with project validation commands before running Coder.'); process.exit(1)"`;
32995
+ /** Retired generated script names inspected only to report stale caller configuration. */
32996
+ const LEGACY_CHECK_SCRIPT_NAMES$1 = new Set(['test-for-ptbk-coder', 'check-for-ptbk-coder']);
32997
+ /**
32998
+ * A missing or unconfigured project check needs project-owner setup, rather than model repair retries.
32999
+ */
33000
+ class CoderCheckSetupError extends NotAllowed {
33001
+ /** Creates an actionable project check setup error. */
33002
+ constructor(details) {
33003
+ super(_spaceTrim.spaceTrim(`
33004
+ Project check is not configured: ${details}
33005
+
33006
+ Configure the selected validation scripts in \`package.json\`. The canonical aggregate is \`scripts.check\`, invoked as \`${DEFAULT_CODER_CHECK_COMMAND}\`.
33007
+ Coder will not replace missing validation with a successful placeholder or tests alone.
33008
+ `));
33009
+ Object.setPrototypeOf(this, CoderCheckSetupError.prototype);
32832
33010
  }
32833
- return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
32834
33011
  }
33012
+ /**
33013
+ * Detects the generated setup placeholder and the conventional npm unconfigured-test placeholder.
33014
+ */
33015
+ function isCheckSetupRequired(command) {
33016
+ return (!command.trim() ||
33017
+ command.includes('Configure package.json scripts.check') ||
33018
+ /no test specified/iu.test(command));
33019
+ }
33020
+ /**
33021
+ * Inspects root project check references before an initial check or repair loop can start.
33022
+ * Flags and shell composition do not bypass setup validation; execution still uses the existing shell runner.
33023
+ */
33024
+ async function assertProjectCheckIsConfigured(command, projectPath) {
33025
+ var _a, _b;
33026
+ if (!command)
33027
+ return;
33028
+ const references = projectScriptReferences(command);
33029
+ if (references.length === 0)
33030
+ return;
33031
+ let packageJson;
33032
+ try {
33033
+ packageJson = JSON.parse(await promises.readFile(path.join(projectPath, 'package.json'), 'utf-8'));
33034
+ }
33035
+ catch (error) {
33036
+ throw new CoderCheckSetupError(`cannot read package.json (${error instanceof Error ? error.message : String(error)}).`);
33037
+ }
33038
+ for (const reference of references) {
33039
+ if (LEGACY_CHECK_SCRIPT_NAMES$1.has(reference) && ((_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) === null || _a === void 0 ? void 0 : _a[reference]) === undefined) {
33040
+ throw new CoderCheckSetupError(`scripts.${reference} is missing and is no longer initialized by Coder. Update this caller to \`--check "${DEFAULT_CODER_CHECK_COMMAND}"\`; no compatibility alias is generated.`);
33041
+ }
33042
+ assertProjectCheckScriptsAreConfigured((_b = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) !== null && _b !== void 0 ? _b : {}, reference);
33043
+ }
33044
+ }
33045
+ /**
33046
+ * Validates the project-owned check definition without running commands or changing its scope.
33047
+ * Initialization and execution use this same setup diagnostic for missing leaves and recursive callers.
33048
+ */
33049
+ function assertProjectCheckScriptsAreConfigured(scripts, name = 'check') {
33050
+ assertReferencedScriptsAreConfigured(name, scripts, new Set());
33051
+ }
33052
+ /** Detects missing/placeholder leaf validation and recursive npm callers before a model repair loop can start. */
33053
+ function assertReferencedScriptsAreConfigured(name, scripts, ancestors) {
33054
+ if (ancestors.has(name))
33055
+ throw new CoderCheckSetupError(`scripts.${name} recursively calls the check pipeline.`);
33056
+ const command = scripts[name];
33057
+ if (typeof command !== 'string' || isCheckSetupRequired(command)) {
33058
+ throw new CoderCheckSetupError(`scripts.${name} is missing, empty, or contains a setup placeholder.`);
33059
+ }
33060
+ if (/^coder:(?:run|fix)$/u.test(name) || /\b(?:ptbk|promptbook)\s+coder\s+(?:run|fix)\b/u.test(command)) {
33061
+ throw new CoderCheckSetupError(`scripts.${name} calls Coder execution instead of project validation.`);
33062
+ }
33063
+ const nextAncestors = new Set([...ancestors, name]);
33064
+ // The shared inspector keeps root references before a directory change and leaves child workspaces opaque.
33065
+ for (const reference of projectScriptReferences(command)) {
33066
+ assertReferencedScriptsAreConfigured(reference, scripts, nextAncestors);
33067
+ }
33068
+ for (const prefix of ['pre', 'post']) {
33069
+ const lifecycleName = `${prefix}${name}`;
33070
+ if (scripts[lifecycleName] !== undefined)
33071
+ assertReferencedScriptsAreConfigured(lifecycleName, scripts, nextAncestors);
33072
+ }
33073
+ }
33074
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
32835
33075
 
32836
33076
  /**
32837
- * Runs the configured verification command inside the project root and returns its output.
33077
+ * Runs the configured check command inside the project root and returns its output.
32838
33078
  */
32839
- async function runPromptTestCommand(options) {
33079
+ async function runPromptCheckCommand(options) {
33080
+ await assertProjectCheckIsConfigured(options.command, options.projectPath);
32840
33081
  const projectPath = toPosixPath(options.projectPath);
32841
33082
  return await $runGoScriptWithOutput({
32842
33083
  scriptPath: options.scriptPath,
33084
+ projectPath: options.projectPath,
32843
33085
  scriptContent: spaceTrim(`
32844
- cd "${projectPath}"
33086
+ cd ${quoteBashArgument(projectPath)} || exit 1
32845
33087
  ${options.command}
32846
33088
  `),
32847
33089
  logPath: options.logPath,
32848
33090
  preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
33091
+ signal: options.signal,
32849
33092
  });
32850
33093
  }
32851
33094
 
32852
33095
  /**
32853
- * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33096
+ * Shared constraints for initial repair PRDs and post-prompt check feedback.
33097
+ */
33098
+ const CHECK_REPAIR_INSTRUCTIONS = _spaceTrim.spaceTrim(`
33099
+ Fix the underlying lint, typechecking, build, generated-code consistency, or test failure without weakening validation.
33100
+ Do not delete assertions, disable lint rules, remove failing checks from the aggregate, lower quality thresholds,
33101
+ skip a build, or force exit code zero merely to obtain a pass. Keep the project's chosen check scope intact.
33102
+ Missing or unconfigured validation requires project-owner setup; never replace it with a meaningless green result.
33103
+ `);
33104
+
33105
+ /**
33106
+ * Maximum number of coding attempts allowed for the same prompt when check keeps failing.
32854
33107
  */
32855
- const MAX_PROMPT_TEST_ATTEMPTS = 3;
33108
+ const MAX_PROMPT_CHECK_ATTEMPTS = 3;
32856
33109
  /**
32857
33110
  * File extension used by generated shell scripts.
32858
33111
  */
@@ -32860,12 +33113,18 @@
32860
33113
  /**
32861
33114
  * Runs one coding prompt and, when configured, verifies it with a shell command that can feed failures back.
32862
33115
  */
32863
- async function runPromptWithTestFeedback(options) {
32864
- var _a, _b, _c, _d;
32865
- const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
33116
+ async function runPromptWithCheckFeedback(options) {
33117
+ var _a, _b, _c, _d, _e;
33118
+ (_a = options.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
33119
+ const normalizedCheckCommand = (_b = options.checkCommand) === null || _b === void 0 ? void 0 : _b.trim();
33120
+ // Injected executors own their setup validation, just as they own command execution. The shared real executor
33121
+ // validates again after each agent attempt in case it changed the project's validation configuration.
33122
+ if (!options.runPromptCheckCommandExecutor) {
33123
+ await assertProjectCheckIsConfigured(normalizedCheckCommand, options.projectPath);
33124
+ }
32866
33125
  const stepTracker = createCoderRunStepTracker(options.onStepStarted);
32867
- if (!normalizedTestCommand) {
32868
- (_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
33126
+ if (!normalizedCheckCommand) {
33127
+ (_c = options.onAttemptStarted) === null || _c === void 0 ? void 0 : _c.call(options, 1);
32869
33128
  await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, 1);
32870
33129
  const result = await runRunnerPromptStep({
32871
33130
  runOptions: options,
@@ -32875,10 +33134,10 @@
32875
33134
  });
32876
33135
  return { ...result, attemptCount: 1, steps: stepTracker.steps };
32877
33136
  }
32878
- const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
33137
+ const runPromptCheckCommandExecutor = (_d = options.runPromptCheckCommandExecutor) !== null && _d !== void 0 ? _d : runPromptCheckCommand;
32879
33138
  let promptForCurrentAttempt = options.prompt;
32880
- for (let attemptCount = 1; attemptCount <= MAX_PROMPT_TEST_ATTEMPTS; attemptCount++) {
32881
- (_d = options.onAttemptStarted) === null || _d === void 0 ? void 0 : _d.call(options, attemptCount);
33139
+ for (let attemptCount = 1; attemptCount <= MAX_PROMPT_CHECK_ATTEMPTS; attemptCount++) {
33140
+ (_e = options.onAttemptStarted) === null || _e === void 0 ? void 0 : _e.call(options, attemptCount);
32882
33141
  await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, attemptCount);
32883
33142
  const result = await runRunnerPromptStep({
32884
33143
  runOptions: options,
@@ -32886,42 +33145,44 @@
32886
33145
  kind: attemptCount === 1 ? 'implementation' : 'fixing',
32887
33146
  stepTracker,
32888
33147
  });
32889
- await waitForVerificationPauseCheckpoint(options.waitForPauseCheckpoint, normalizedTestCommand, attemptCount);
32890
- console.info(colors__default["default"].gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
32891
- const failedVerification = await runVerificationStep({
32892
- runPromptTestCommandExecutor,
32893
- testCommand: normalizedTestCommand,
33148
+ await waitForCheckPauseCheckpoint(options.waitForPauseCheckpoint, normalizedCheckCommand, attemptCount);
33149
+ console.info(colors__default["default"].gray(`Running check command after attempt #${attemptCount}: ${normalizedCheckCommand}`));
33150
+ const failedCheck = await runCheckStep({
33151
+ runPromptCheckCommandExecutor,
33152
+ checkCommand: normalizedCheckCommand,
32894
33153
  runOptions: options,
32895
33154
  stepTracker,
32896
33155
  });
32897
- if (failedVerification === undefined) {
33156
+ if (failedCheck === undefined) {
32898
33157
  return { ...result, attemptCount, steps: stepTracker.steps };
32899
33158
  }
32900
- const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
32901
- const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
32902
- if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
32903
- console.error(colors__default["default"].red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
32904
- throw new Error(buildFinalVerificationFailureMessage({
33159
+ const fullCheckOutput = formatUnknownErrorDetails(failedCheck.error);
33160
+ const feedbackCheckOutput = limitCheckOutput(fullCheckOutput);
33161
+ if (attemptCount >= MAX_PROMPT_CHECK_ATTEMPTS) {
33162
+ console.error(colors__default["default"].red(`Check failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33163
+ throw new NotAllowed(buildFinalCheckFailureMessage({
32905
33164
  promptLabel: options.promptLabel,
32906
- testCommand: normalizedTestCommand,
33165
+ checkCommand: normalizedCheckCommand,
32907
33166
  attemptCount,
32908
- verificationOutput: fullVerificationOutput,
33167
+ checkOutput: fullCheckOutput,
32909
33168
  }));
32910
33169
  }
32911
- console.warn(colors__default["default"].yellow(`Verification failed for ${options.promptLabel} on attempt #${attemptCount}. Sending feedback to ${options.runner.name} and retrying...`));
32912
- promptForCurrentAttempt = appendCoderContext(options.prompt, buildVerificationFeedback({
32913
- testCommand: normalizedTestCommand,
33170
+ console.warn(colors__default["default"].yellow(`Check failed for ${options.promptLabel} on attempt #${attemptCount}. Sending feedback to ${options.runner.name} and retrying...`));
33171
+ promptForCurrentAttempt = appendCoderContext(options.prompt, buildCheckFeedback({
33172
+ checkCommand: normalizedCheckCommand,
32914
33173
  failedAttemptCount: attemptCount,
32915
- verificationOutput: feedbackVerificationOutput,
33174
+ checkOutput: feedbackCheckOutput,
32916
33175
  }));
32917
33176
  }
32918
- throw new Error('Unexpected prompt verification state.');
33177
+ throw new UnexpectedError('Unexpected prompt check state.');
32919
33178
  }
32920
33179
  /**
32921
33180
  * Runs one coding attempt through the runner, timing it and recording it as an implementation or fixing step.
32922
33181
  */
32923
33182
  async function runRunnerPromptStep(options) {
33183
+ var _a, _b;
32924
33184
  const { runOptions, prompt, kind, stepTracker } = options;
33185
+ (_a = runOptions.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
32925
33186
  await stepTracker.startStep(kind);
32926
33187
  const stepStartedTimeMs = Date.now();
32927
33188
  const result = await runOptions.runner.runPrompt({
@@ -32931,33 +33192,41 @@
32931
33192
  logPath: runOptions.logPath,
32932
33193
  preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
32933
33194
  waitForPauseCheckpoint: runOptions.waitForPauseCheckpoint,
33195
+ ...(runOptions.signal ? { signal: runOptions.signal } : {}),
32934
33196
  });
33197
+ (_b = runOptions.signal) === null || _b === void 0 ? void 0 : _b.throwIfAborted();
32935
33198
  stepTracker.finishStep({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs }, result.loginMethod);
32936
33199
  return result;
32937
33200
  }
32938
33201
  /**
32939
- * Runs the verification command, timing it and recording it as a testing step regardless of the outcome, and
32940
- * returns the failure (or `undefined` when the verification passed).
33202
+ * Runs the check command, timing it and recording it as a checking step regardless of the outcome, and
33203
+ * returns the failure (or `undefined` when the check passed).
32941
33204
  */
32942
- async function runVerificationStep(options) {
32943
- const { runPromptTestCommandExecutor, testCommand, runOptions, stepTracker } = options;
32944
- await stepTracker.startStep('testing');
33205
+ async function runCheckStep(options) {
33206
+ var _a, _b, _c;
33207
+ const { runPromptCheckCommandExecutor, checkCommand, runOptions, stepTracker } = options;
33208
+ (_a = runOptions.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
33209
+ await stepTracker.startStep('checking');
32945
33210
  const stepStartedTimeMs = Date.now();
32946
33211
  try {
32947
- await runPromptTestCommandExecutor({
32948
- command: testCommand,
33212
+ await runPromptCheckCommandExecutor({
33213
+ command: checkCommand,
32949
33214
  projectPath: runOptions.projectPath,
32950
- scriptPath: buildPromptTestScriptPath(runOptions.scriptPath),
33215
+ scriptPath: buildPromptCheckScriptPath(runOptions.scriptPath),
32951
33216
  logPath: runOptions.logPath,
32952
33217
  preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
33218
+ ...(runOptions.signal ? { signal: runOptions.signal } : {}),
32953
33219
  });
33220
+ (_b = runOptions.signal) === null || _b === void 0 ? void 0 : _b.throwIfAborted();
32954
33221
  return undefined;
32955
33222
  }
32956
33223
  catch (error) {
33224
+ if (error instanceof CoderCheckSetupError || ((_c = runOptions.signal) === null || _c === void 0 ? void 0 : _c.aborted))
33225
+ throw error;
32957
33226
  return { error };
32958
33227
  }
32959
33228
  finally {
32960
- stepTracker.finishStep({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
33229
+ stepTracker.finishStep({ kind: 'checking', usage: null, durationMs: Date.now() - stepStartedTimeMs });
32961
33230
  }
32962
33231
  }
32963
33232
  /**
@@ -32971,13 +33240,13 @@
32971
33240
  }));
32972
33241
  }
32973
33242
  /**
32974
- * Waits for a pause checkpoint immediately before one verification command begins.
33243
+ * Waits for a pause checkpoint immediately before one check command begins.
32975
33244
  */
32976
- async function waitForVerificationPauseCheckpoint(waitForPauseCheckpoint, testCommand, attemptCount) {
33245
+ async function waitForCheckPauseCheckpoint(waitForPauseCheckpoint, checkCommand, attemptCount) {
32977
33246
  await (waitForPauseCheckpoint === null || waitForPauseCheckpoint === void 0 ? void 0 : waitForPauseCheckpoint({
32978
- checkpointLabel: buildVerificationPauseLabel(attemptCount),
33247
+ checkpointLabel: buildCheckPauseLabel(attemptCount),
32979
33248
  phase: 'verifying',
32980
- statusMessage: `Running verification after attempt #${attemptCount}: ${testCommand}`,
33249
+ statusMessage: `Running check after attempt #${attemptCount}: ${checkCommand}`,
32981
33250
  }));
32982
33251
  }
32983
33252
  /**
@@ -32993,51 +33262,53 @@
32993
33262
  return `Calling ${runnerName} (attempt ${attemptCount})`;
32994
33263
  }
32995
33264
  /**
32996
- * Builds the human-readable pause label used before one verification command begins.
33265
+ * Builds the human-readable pause label used before one check command begins.
32997
33266
  */
32998
- function buildVerificationPauseLabel(attemptCount) {
32999
- return `running verification after attempt #${attemptCount}`;
33267
+ function buildCheckPauseLabel(attemptCount) {
33268
+ return `running check after attempt #${attemptCount}`;
33000
33269
  }
33001
33270
  /**
33002
- * Builds one feedback block appended to the next coding attempt after tests fail.
33271
+ * Builds one feedback block appended to the next coding attempt after checks fail.
33003
33272
  */
33004
- function buildVerificationFeedback({ testCommand, failedAttemptCount, verificationOutput, }) {
33273
+ function buildCheckFeedback({ checkCommand, failedAttemptCount, checkOutput, }) {
33005
33274
  const nextAttemptCount = failedAttemptCount + 1;
33006
33275
  return spaceTrim((block) => `
33007
- The previous implementation did not pass the required verification command.
33276
+ The previous implementation did not pass the required check command.
33008
33277
 
33009
- ## Automated verification feedback
33010
- - Retry attempt: ${nextAttemptCount} of ${MAX_PROMPT_TEST_ATTEMPTS}
33011
- - Verification command: \`${testCommand}\`
33012
- - Update the current implementation so the verification command passes without breaking the original task requirements.
33278
+ ## Automated check feedback
33279
+ - Retry attempt: ${nextAttemptCount} of ${MAX_PROMPT_CHECK_ATTEMPTS}
33280
+ - Check command: \`${checkCommand}\`
33281
+ - Update the current implementation so the check command passes without breaking the original task requirements.
33013
33282
 
33014
- ### Verification output
33283
+ ${block(CHECK_REPAIR_INSTRUCTIONS)}
33284
+
33285
+ ### Check output
33015
33286
  \`\`\`
33016
- ${block(verificationOutput)}
33287
+ ${block(checkOutput)}
33017
33288
  \`\`\`
33018
33289
  `);
33019
33290
  }
33020
33291
  /**
33021
- * Builds the final error message written when verification still fails after all retries.
33292
+ * Builds the final error message written when check still fails after all retries.
33022
33293
  */
33023
- function buildFinalVerificationFailureMessage({ promptLabel, testCommand, attemptCount, verificationOutput, }) {
33294
+ function buildFinalCheckFailureMessage({ promptLabel, checkCommand, attemptCount, checkOutput, }) {
33024
33295
  return spaceTrim((block) => `
33025
- Verification command \`${testCommand}\` failed for \`${promptLabel}\` after ${attemptCount} attempts.
33296
+ Check command \`${checkCommand}\` failed for \`${promptLabel}\` after ${attemptCount} attempts.
33026
33297
 
33027
- ### Verification output
33298
+ ### Check output
33028
33299
  \`\`\`
33029
- ${block(verificationOutput)}
33300
+ ${block(checkOutput)}
33030
33301
  \`\`\`
33031
33302
  `);
33032
33303
  }
33033
33304
  /**
33034
- * Derives a dedicated temp-script path for verification commands.
33305
+ * Derives a dedicated temp-script path for check commands.
33035
33306
  */
33036
- function buildPromptTestScriptPath(scriptPath) {
33307
+ function buildPromptCheckScriptPath(scriptPath) {
33037
33308
  if (scriptPath.toLowerCase().endsWith(SHELL_SCRIPT_EXTENSION)) {
33038
- return `${scriptPath.slice(0, -SHELL_SCRIPT_EXTENSION.length)}.test.sh`;
33309
+ return `${scriptPath.slice(0, -SHELL_SCRIPT_EXTENSION.length)}.check.sh`;
33039
33310
  }
33040
- return `${scriptPath}.test.sh`;
33311
+ return `${scriptPath}.check.sh`;
33041
33312
  }
33042
33313
 
33043
33314
  /**
@@ -41937,16 +42208,16 @@
41937
42208
  * Answers one queued `.book` message thread and moves it to `messages/finished`.
41938
42209
  */
41939
42210
  async function tickAgentMessages(options, tickOptions = {}) {
41940
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
42211
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
41941
42212
  validateAgentRunOptions(options);
41942
- const projectPath = tickOptions.projectPath || process.cwd();
42213
+ const projectPath = (_b = (_a = tickOptions.projectPath) !== null && _a !== void 0 ? _a : options.projectPath) !== null && _b !== void 0 ? _b : process.cwd();
41943
42214
  let queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
41944
42215
  let queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
41945
42216
  if (!queuedMessage) {
41946
42217
  announceNoQueuedMessages(tickOptions);
41947
42218
  return { isMessageProcessed: false };
41948
42219
  }
41949
- if (tickOptions.uiHandle && !((_a = tickOptions.uiPresentation) === null || _a === void 0 ? void 0 : _a.isSharedDashboard)) {
42220
+ if (tickOptions.uiHandle && !((_c = tickOptions.uiPresentation) === null || _c === void 0 ? void 0 : _c.isSharedDashboard)) {
41950
42221
  updateAgentRunUiForPulling(tickOptions.uiHandle, queueSnapshot, 'Pulling latest changes before answering the next message');
41951
42222
  }
41952
42223
  const autoPullTimestamp = await pullLatestChangesForAgentQueueIfEnabled({
@@ -41958,7 +42229,7 @@
41958
42229
  queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
41959
42230
  queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
41960
42231
  if (!queuedMessage) {
41961
- if (tickOptions.uiHandle && !((_b = tickOptions.uiPresentation) === null || _b === void 0 ? void 0 : _b.isSharedDashboard)) {
42232
+ if (tickOptions.uiHandle && !((_d = tickOptions.uiPresentation) === null || _d === void 0 ? void 0 : _d.isSharedDashboard)) {
41962
42233
  updateAgentRunUiForWatching(tickOptions.uiHandle, queueSnapshot);
41963
42234
  }
41964
42235
  announceNoQueuedMessages(tickOptions);
@@ -41977,24 +42248,24 @@
41977
42248
  actualRunnerModel,
41978
42249
  queuedMessage,
41979
42250
  uiHandle,
41980
- isSharedDashboard: (_c = tickOptions.uiPresentation) === null || _c === void 0 ? void 0 : _c.isSharedDashboard,
42251
+ isSharedDashboard: (_e = tickOptions.uiPresentation) === null || _e === void 0 ? void 0 : _e.isSharedDashboard,
41981
42252
  });
41982
- if (!((_d = tickOptions.uiPresentation) === null || _d === void 0 ? void 0 : _d.isSharedDashboard)) {
41983
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(((_e = tickOptions.uiPresentation) === null || _e === void 0 ? void 0 : _e.completedProgressStats) ||
42253
+ if (!((_f = tickOptions.uiPresentation) === null || _f === void 0 ? void 0 : _f.isSharedDashboard)) {
42254
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(((_g = tickOptions.uiPresentation) === null || _g === void 0 ? void 0 : _g.completedProgressStats) ||
41984
42255
  createAgentQueueProgressSnapshot({
41985
42256
  finishedMessageCount: queueSnapshot.finishedMessageCount + 1,
41986
42257
  queuedMessages: removeQueuedMessageFromSnapshot(queueSnapshot, queuedMessage),
41987
42258
  }));
41988
- if ((_f = tickOptions.uiPresentation) === null || _f === void 0 ? void 0 : _f.completedAgentStatusLines) {
42259
+ if ((_h = tickOptions.uiPresentation) === null || _h === void 0 ? void 0 : _h.completedAgentStatusLines) {
41989
42260
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAgentStatusLines([...tickOptions.uiPresentation.completedAgentStatusLines]);
41990
42261
  }
41991
- if ((_g = tickOptions.uiPresentation) === null || _g === void 0 ? void 0 : _g.completedAgentStatusTableRows) {
42262
+ if ((_j = tickOptions.uiPresentation) === null || _j === void 0 ? void 0 : _j.completedAgentStatusTableRows) {
41992
42263
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAgentStatusTableRows([...tickOptions.uiPresentation.completedAgentStatusTableRows]);
41993
42264
  }
41994
- if ((_h = tickOptions.uiPresentation) === null || _h === void 0 ? void 0 : _h.completedMessagePreviewLines) {
42265
+ if ((_k = tickOptions.uiPresentation) === null || _k === void 0 ? void 0 : _k.completedMessagePreviewLines) {
41995
42266
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewLines([...tickOptions.uiPresentation.completedMessagePreviewLines]);
41996
42267
  }
41997
- if ((_j = tickOptions.uiPresentation) === null || _j === void 0 ? void 0 : _j.completedMessagePreviewSections) {
42268
+ if ((_l = tickOptions.uiPresentation) === null || _l === void 0 ? void 0 : _l.completedMessagePreviewSections) {
41998
42269
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewSections([
41999
42270
  ...tickOptions.uiPresentation.completedMessagePreviewSections,
42000
42271
  ]);
@@ -42078,7 +42349,7 @@
42078
42349
  try {
42079
42350
  try {
42080
42351
  promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) => {
42081
- const runResult = await runPromptWithTestFeedback({
42352
+ const runResult = await runPromptWithCheckFeedback({
42082
42353
  runner,
42083
42354
  prompt,
42084
42355
  scriptPath,
@@ -42505,12 +42776,12 @@
42505
42776
  * Watches all direct child agent repositories from the current directory in one shared session.
42506
42777
  */
42507
42778
  async function runMultipleAgentMessages(options, controls = {}) {
42508
- var _a, _b;
42779
+ var _a, _b, _c;
42509
42780
  validateAgentRunOptions(options);
42510
42781
  validateAgentWatchOptions('ptbk agent-folder run-multiple', options);
42511
- const rootPath = process.cwd();
42782
+ const rootPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
42512
42783
  const shouldContinue = controls.shouldContinue || (() => just(true));
42513
- const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : MULTI_AGENT_QUEUE_POLL_INTERVAL_MS;
42784
+ const queuePollIntervalMs = (_b = controls.queuePollIntervalMs) !== null && _b !== void 0 ? _b : MULTI_AGENT_QUEUE_POLL_INTERVAL_MS;
42514
42785
  const watchErrorLogDirectoryPath = controls.watchErrorLogDirectoryPath || rootPath;
42515
42786
  const ignoreMatcher = createAgentIgnoreMatcher(options.ignorePatterns);
42516
42787
  const githubSynchronizer = new MultipleAgentGithubSynchronizer();
@@ -42534,7 +42805,7 @@
42534
42805
  watchErrorLogDirectoryPath,
42535
42806
  });
42536
42807
  isWatchSessionInitialized = true;
42537
- (_b = controls.onUiInitialized) === null || _b === void 0 ? void 0 : _b.call(controls, uiPresenter.uiHandle);
42808
+ (_c = controls.onUiInitialized) === null || _c === void 0 ? void 0 : _c.call(controls, uiPresenter.uiHandle);
42538
42809
  if (!uiPresenter.uiHandle) {
42539
42810
  console.info(colors__default["default"].green('Watching direct child agent repositories for queued messages.'));
42540
42811
  }
@@ -43793,6 +44064,7 @@
43793
44064
  * @private internal registration helper for workspace CLI commands
43794
44065
  */
43795
44066
  function addWorkspaceRepositoryOptions(command) {
44067
+ addProjectPathOption(command);
43796
44068
  if (!command.options.some((option) => option.long === '--no-questions'))
43797
44069
  addQuestionsOption(command);
43798
44070
  command.addHelpText('after', `\n${WORKSPACE_REPOSITORY_DESCRIPTION}\n`);
@@ -43894,17 +44166,7 @@
43894
44166
  * @private shared workspace discovery for CLI commands
43895
44167
  */
43896
44168
  async function $resolveWorkspaceRepository(projectDirectory = process.cwd()) {
43897
- const requestedPath = path.resolve(projectDirectory);
43898
- let projectPath;
43899
- try {
43900
- projectPath = await promises.realpath(requestedPath);
43901
- if (!(await promises.stat(projectPath)).isDirectory()) {
43902
- throw new EnvironmentMismatchError(`The project path \`${requestedPath}\` is not a directory.`);
43903
- }
43904
- }
43905
- catch (error) {
43906
- throw new EnvironmentMismatchError(_spaceTrim.spaceTrim(`Cannot access project directory \`${requestedPath}\`. Check that it exists and is accessible.\n\n${describeWorkspaceError(error)}`));
43907
- }
44169
+ const projectPath = await resolveProjectDirectory(projectDirectory);
43908
44170
  return $inspectWorkspaceRepository(projectPath);
43909
44171
  }
43910
44172
  /**
@@ -44016,7 +44278,7 @@
44016
44278
  // Git may skip damaged inner metadata and find a valid parent. Do not silently use that parent
44017
44279
  // for a project whose own checkout needs repair.
44018
44280
  await $assertNoBrokenRepositoryMetadata(projectPath, repositoryRoot);
44019
- const gitDirectory = (await $runWorkspaceGit(projectPath, ['rev-parse', '--absolute-git-dir'])).trim();
44281
+ const gitDirectory = await promises.realpath((await $runWorkspaceGit(projectPath, ['rev-parse', '--absolute-git-dir'])).trim());
44020
44282
  // Also validate the index and metadata, without refreshing the index or depending on an existing commit.
44021
44283
  await $runWorkspaceGit(projectPath, ['status', '--porcelain', '--untracked-files=no']);
44022
44284
  return { projectPath, repositoryRoot, gitDirectory, repositoryStatus: 'reused' };
@@ -44344,10 +44606,15 @@
44344
44606
  addQuestionsOption(command);
44345
44607
  addWorkspaceRepositoryOptions(command);
44346
44608
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
44609
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44347
44610
  const { priority, template: templateOption } = cliOptions;
44348
44611
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44349
44612
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
44350
- const workspace = await $preflightWorkspaceRepository({ policy: 'mutate', ...questionsOptions });
44613
+ const workspace = await $preflightWorkspaceRepository({
44614
+ ...projectOptions,
44615
+ policy: 'mutate',
44616
+ ...questionsOptions,
44617
+ });
44351
44618
  const { projectPath } = workspace;
44352
44619
  const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
44353
44620
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
@@ -44570,7 +44837,9 @@
44570
44837
  `));
44571
44838
  addWorkspaceRepositoryOptions(command);
44572
44839
  command.action(handleActionErrors(async (cliOptions) => {
44840
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44573
44841
  const workspace = await $preflightWorkspaceRepository({
44842
+ ...projectOptions,
44574
44843
  policy: 'read-only',
44575
44844
  isAskingQuestionsEnabled: cliOptions.questions,
44576
44845
  });
@@ -44811,8 +45080,10 @@
44811
45080
  command.option('--limit <candidate-count>', 'Create at most this many refactor prompts, keeping the most important candidates', createPositiveIntegerOptionParser('--limit'));
44812
45081
  addWorkspaceRepositoryOptions(command);
44813
45082
  command.action(handleActionErrors(async (cliOptions) => {
45083
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44814
45084
  const { level = DEFAULT_REFACTOR_CANDIDATE_LEVEL, limit } = cliOptions;
44815
45085
  const workspace = await $preflightWorkspaceRepository({
45086
+ ...projectOptions,
44816
45087
  policy: 'mutate',
44817
45088
  isAskingQuestionsEnabled: cliOptions.questions,
44818
45089
  });
@@ -44846,8 +45117,10 @@
44846
45117
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
44847
45118
  addWorkspaceRepositoryOptions(command);
44848
45119
  command.action(handleActionErrors(async (cliOptions) => {
45120
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44849
45121
  const { priority = 0 } = cliOptions;
44850
45122
  const workspace = await $preflightWorkspaceRepository({
45123
+ ...projectOptions,
44851
45124
  policy: 'read-only',
44852
45125
  isAskingQuestionsEnabled: cliOptions.questions,
44853
45126
  });
@@ -45001,10 +45274,12 @@
45001
45274
  addCoderGitSyncOptions(command);
45002
45275
  addWorkspaceRepositoryOptions(command);
45003
45276
  command.action(handleActionErrors(async (cliOptions) => {
45277
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
45004
45278
  const { count: countOption, template: templateOption } = cliOptions;
45005
45279
  const boilerplateCount = parseBoilerplateCount(countOption);
45006
45280
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
45007
45281
  const workspace = await $preflightWorkspaceRepository({
45282
+ ...projectOptions,
45008
45283
  policy: 'mutate',
45009
45284
  isAskingQuestionsEnabled: cliOptions.questions,
45010
45285
  });
@@ -46043,16 +46318,16 @@
46043
46318
  ...getDefaultCoderProjectPromptTemplateDefinitions().map(createCoderPromptTemplateArtifactDefinition),
46044
46319
  ];
46045
46320
  /**
46046
- * Initializes artifacts referenced by freshly added scripts. Default Books are ensured separately.
46321
+ * Initializes essential context and artifacts referenced by freshly added scripts. Books are ensured separately.
46047
46322
  *
46048
- * For artifacts other than role Books, an existing project script does not cause default files to be created.
46323
+ * AGENTS.md is essential even when every script already exists or uses implicit defaults.
46049
46324
  *
46050
46325
  * @private function of `initializeCoderProjectConfiguration`
46051
46326
  */
46052
46327
  async function ensureCoderReferencedArtifacts(projectPath, referencedArtifactPaths) {
46053
46328
  const ensuredArtifacts = [];
46054
46329
  for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
46055
- if (!referencedArtifactPaths.has(relativeFilePath)) {
46330
+ if (relativeFilePath !== AGENTS_FILE_PATH && !referencedArtifactPaths.has(relativeFilePath)) {
46056
46331
  ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
46057
46332
  continue;
46058
46333
  }
@@ -46671,7 +46946,7 @@
46671
46946
  /**
46672
46947
  * Name of the npm script which `ptbk coder run` uses to verify the project.
46673
46948
  */
46674
- const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
46949
+ const CHECK_SCRIPT_NAME = 'check';
46675
46950
  /**
46676
46951
  * Default npm scripts initialized by `ptbk coder init`.
46677
46952
  *
@@ -46691,14 +46966,13 @@
46691
46966
  {
46692
46967
  scriptName: 'coder:plan',
46693
46968
  scriptCommand: 'npx ptbk coder plan --harness openai-codex',
46694
- referencedArtifactPaths: [CODER_PLANNER_AGENT_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
46969
+ referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
46695
46970
  },
46696
46971
  {
46697
46972
  scriptName: 'coder:run',
46698
46973
  scriptCommand: [
46699
46974
  'npx ptbk coder run --harness openai-codex --thinking-level max',
46700
- `--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
46701
- `--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
46975
+ `--check "npm run ${CHECK_SCRIPT_NAME}" --check-before yes-and-fix`,
46702
46976
  ].join(' '),
46703
46977
  referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
46704
46978
  },
@@ -46711,8 +46985,8 @@
46711
46985
  {
46712
46986
  // Note: The verification command of `coder:run` is a project-owned script, so every project can decide
46713
46987
  // what "verified" means without touching the `coder:run` script itself
46714
- scriptName: CODER_TEST_SCRIPT_NAME,
46715
- scriptCommand: 'npm test',
46988
+ scriptName: CHECK_SCRIPT_NAME,
46989
+ scriptCommand: CHECK_SETUP_PLACEHOLDER_COMMAND,
46716
46990
  referencedArtifactPaths: [],
46717
46991
  },
46718
46992
  ];
@@ -46777,12 +47051,13 @@
46777
47051
  /**
46778
47052
  * Ensures one JSON object field contains the provided string-record entries.
46779
47053
  *
46780
- * Entries which the project already defines are **never** overridden - only missing keys are added,
46781
- * so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
47054
+ * Entries are additive unless a caller explicitly supplies a scoped migration callback.
47055
+ * Hand-tuned scripts and settings survive repeated `ptbk coder init`; migrations recognize generated entries only.
46782
47056
  *
46783
47057
  * @private function of `initializeCoderProjectConfiguration`
46784
47058
  */
46785
- async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, }) {
47059
+ async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, transformExistingEntries, }) {
47060
+ var _a;
46786
47061
  if (ensureParentDirectoryPath) {
46787
47062
  await promises.mkdir(path.join(projectPath, ensureParentDirectoryPath), { recursive: true });
46788
47063
  }
@@ -46792,7 +47067,7 @@
46792
47067
  const jsonObject = fileContent === undefined ? {} : await parseJsonObjectFile(relativeFilePath, fileContent);
46793
47068
  const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
46794
47069
  const addedEntryKeys = [];
46795
- const mergedEntries = { ...existingEntries };
47070
+ const mergedEntries = { ...((_a = (await (transformExistingEntries === null || transformExistingEntries === void 0 ? void 0 : transformExistingEntries(existingEntries)))) !== null && _a !== void 0 ? _a : existingEntries) };
46796
47071
  for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
46797
47072
  if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
46798
47073
  // Note: The project already defines this entry, keep its own value untouched
@@ -46801,7 +47076,12 @@
46801
47076
  mergedEntries[entryKey] = entryValue;
46802
47077
  addedEntryKeys.push(entryKey);
46803
47078
  }
46804
- const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
47079
+ for (const entryKey of Object.keys(mergedEntries)) {
47080
+ if (!Object.prototype.hasOwnProperty.call(existingEntries, entryKey) && !addedEntryKeys.includes(entryKey)) {
47081
+ addedEntryKeys.push(entryKey);
47082
+ }
47083
+ }
47084
+ const hasChanges = fileContent === undefined || JSON.stringify(mergedEntries) !== JSON.stringify(existingEntries);
46805
47085
  if (!hasChanges) {
46806
47086
  return { status: 'unchanged', addedEntryKeys };
46807
47087
  }
@@ -46890,6 +47170,301 @@
46890
47170
  }
46891
47171
  // Note: [🟡] Code for coder init JSON merging [mergeStringRecordJsonFile](src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts) should never be published outside of `@promptbook/cli`
46892
47172
 
47173
+ /** Old initialized aggregate entries, used only at this migration boundary. */
47174
+ const LEGACY_CHECK_SCRIPT_NAMES = ['test-for-ptbk-coder', 'check-for-ptbk-coder'];
47175
+ /** Conventional project validation scripts, ordered from lightweight checks to tests. */
47176
+ const VALIDATION_SCRIPT_GROUPS = [
47177
+ ['lint', 'test-lint'],
47178
+ ['typecheck', 'type-check', 'type-checking', 'check-types', 'test-types'],
47179
+ ['build', 'test-build'],
47180
+ ];
47181
+ /** Conventional leaf test families used when the project has no usable test aggregate. */
47182
+ const TEST_SCRIPT_GROUPS = [
47183
+ ['test-unit', 'test:unit'],
47184
+ ['test-integration', 'test:integration'],
47185
+ ['test-e2e', 'test:e2e'],
47186
+ ];
47187
+ /** Script names that must never be reached by a generated aggregate. */
47188
+ const NON_VALIDATION_SCRIPT_PATTERN = /^(?:check$|coder:|(?:dev|start|serve|watch|deploy|publish|install|preinstall|postinstall|prepare|reset|clean)(?:$|[:-]))/iu;
47189
+ /** Clearly unsuitable shell bodies are excluded even under a conventional validation name. */
47190
+ const UNSUITABLE_VALIDATION_BODY_PATTERN = /(?:\b(?:ptbk|promptbook)\s+coder\s+(?:run|fix)|\bcoder:(?:run|fix)\b|--watch(?:All)?(?:\b|=)|\b(?:watch|nodemon|concurrently|serve|deploy|publish|install|reset|rm|rmdir|del|remove-item)\b|--if-present|\|\||(?:^|[^&])&(?:$|[^&])|;|\bexit\s+0\b)/iu;
47191
+ /** Npm lifecycle hooks that run implicitly before and after a selected project script. */
47192
+ const SCRIPT_LIFECYCLE_PREFIXES = ['pre', 'post'];
47193
+ /**
47194
+ * Preserves project scripts, creates one canonical check, and migrates only exact generated caller forms.
47195
+ * @private shared package-script preparation for Coder initialization
47196
+ */
47197
+ function prepareCoderPackageJsonScripts(existing, externalCallers = {}) {
47198
+ var _a;
47199
+ const scripts = { ...existing };
47200
+ const migrationInstructions = [];
47201
+ const legacyNames = LEGACY_CHECK_SCRIPT_NAMES.filter((name) => Object.prototype.hasOwnProperty.call(existing, name));
47202
+ let checkScriptSummary;
47203
+ if (Object.prototype.hasOwnProperty.call(existing, 'check')) {
47204
+ checkScriptSummary = 'Preserved project scripts.check exactly; Coder executes npm run check.';
47205
+ }
47206
+ else if (legacyNames.length > 0) {
47207
+ const sourceName = legacyNames[0];
47208
+ const isRecursive = scriptReaches(sourceName, 'check', existing, new Set()) ||
47209
+ scriptReaches(sourceName, sourceName, existing, new Set()) ||
47210
+ scriptReaches(sourceName, 'coder:run', existing, new Set()) ||
47211
+ scriptReaches(sourceName, 'coder:fix', existing, new Set());
47212
+ const conflictingLifecycleNames = SCRIPT_LIFECYCLE_PREFIXES.filter((prefix) => existing[`${prefix}check`] !== undefined &&
47213
+ existing[`${prefix}${sourceName}`] !== existing[`${prefix}check`]).map((prefix) => `${prefix}check`);
47214
+ scripts.check =
47215
+ isRecursive || conflictingLifecycleNames.length > 0
47216
+ ? CHECK_SETUP_PLACEHOLDER_COMMAND
47217
+ : existing[sourceName];
47218
+ if (!isRecursive && conflictingLifecycleNames.length === 0) {
47219
+ // npm runs these hooks implicitly. Preserve their bodies as well as the main script's scope.
47220
+ for (const prefix of SCRIPT_LIFECYCLE_PREFIXES) {
47221
+ const legacyHook = existing[`${prefix}${sourceName}`];
47222
+ if (legacyHook !== undefined)
47223
+ scripts[`${prefix}check`] = legacyHook;
47224
+ }
47225
+ }
47226
+ checkScriptSummary = isRecursive
47227
+ ? `Kept recursive scripts.${sourceName} unchanged; configure scripts.check with validation that does not call check or Coder recursively.`
47228
+ : conflictingLifecycleNames.length > 0
47229
+ ? `Kept scripts.${sourceName} and conflicting ${conflictingLifecycleNames.join(', ')} hooks unchanged; configure scripts.check and its lifecycle hooks to preserve the required validation.`
47230
+ : `Preserved scripts.${sourceName}'s validation command and npm lifecycle hooks in scripts.check: ${scripts.check}`;
47231
+ }
47232
+ else {
47233
+ const selectedScripts = selectValidationScripts(existing);
47234
+ scripts.check =
47235
+ selectedScripts.length > 0
47236
+ ? selectedScripts.map((name) => (name === 'test' ? 'npm test' : `npm run ${name}`)).join(' && ')
47237
+ : CHECK_SETUP_PLACEHOLDER_COMMAND;
47238
+ checkScriptSummary =
47239
+ selectedScripts.length > 0
47240
+ ? `Generated scripts.check from the available validation scripts (${selectedScripts.join(', ')}): ${scripts.check}`
47241
+ : 'No usable validation scripts found. scripts.check contains a failing setup placeholder; configure it before running Coder.';
47242
+ }
47243
+ for (const [name, command] of Object.entries(existing)) {
47244
+ if (['check', ...legacyNames].some((verificationName) => name === verificationName ||
47245
+ SCRIPT_LIFECYCLE_PREFIXES.some((prefix) => name === `${prefix}${verificationName}`)))
47246
+ continue;
47247
+ const migratedCaller = migrateGeneratedCoderRunScript(command);
47248
+ if (migratedCaller !== undefined) {
47249
+ // A different existing check is authoritative, but changing a custom legacy caller's scope is not safe.
47250
+ const referencedLegacyName = legacyNames.find((legacyName) => command.includes(legacyName));
47251
+ const isConflictingCustomCheck = referencedLegacyName !== undefined &&
47252
+ !isLegacyCheckScopePreserved(referencedLegacyName, scripts) &&
47253
+ !isUnchangedLegacyCheckDefault(referencedLegacyName, scripts);
47254
+ if (!isConflictingCustomCheck)
47255
+ scripts[name] = migratedCaller;
47256
+ }
47257
+ }
47258
+ for (const legacyName of legacyNames) {
47259
+ const callers = Object.entries(scripts)
47260
+ .filter(([name, command]) => name !== legacyName && command.includes(legacyName))
47261
+ .map(([name]) => name);
47262
+ const isPreservedScope = isLegacyCheckScopePreserved(legacyName, scripts);
47263
+ const isObsoleteGeneratedDefault = isUnchangedLegacyCheckDefault(legacyName, scripts) && !isCheckSetupRequired(scripts.check);
47264
+ const files = (_a = externalCallers[legacyName]) !== null && _a !== void 0 ? _a : [];
47265
+ if (callers.length === 0 && files.length === 0 && (isPreservedScope || isObsoleteGeneratedDefault)) {
47266
+ delete scripts[legacyName];
47267
+ for (const prefix of SCRIPT_LIFECYCLE_PREFIXES)
47268
+ delete scripts[`${prefix}${legacyName}`];
47269
+ }
47270
+ else {
47271
+ migrationInstructions.push(`Kept scripts.${legacyName}${callers.length ? `; referenced by scripts.${callers.join(', scripts.')}` : ''}${files.length ? `; referenced by ${files.join(', ')}` : ''}${!isPreservedScope && !isObsoleteGeneratedDefault
47272
+ ? '; its custom command or npm lifecycle hooks differ from scripts.check'
47273
+ : ''}. Preserve its required validation and pre/post hooks in scripts.check, update those callers to npm run check, then remove scripts.${legacyName} and its unused lifecycle hooks.`);
47274
+ }
47275
+ }
47276
+ for (const [name, command] of Object.entries(scripts)) {
47277
+ if (/\b(?:ptbk|promptbook)\s+coder\s+(?:run|server)\b|\brun-codex-prompts\b/u.test(command) &&
47278
+ /--test(?:-before)?(?:\s|=|$)/u.test(command)) {
47279
+ migrationInstructions.push(`Update scripts.${name}: replace --test with --check and --test-before with --check-before; review its shell expression manually. Existing command was preserved: ${command}`);
47280
+ }
47281
+ }
47282
+ let isCheckConfigured = true;
47283
+ try {
47284
+ assertProjectCheckScriptsAreConfigured(scripts);
47285
+ }
47286
+ catch (error) {
47287
+ if (!(error instanceof CoderCheckSetupError))
47288
+ throw error;
47289
+ isCheckConfigured = false;
47290
+ checkScriptSummary += ` Additional setup required: ${error.message}`;
47291
+ }
47292
+ return {
47293
+ scripts,
47294
+ checkScriptSummary,
47295
+ migrationInstructions,
47296
+ isCheckConfigured,
47297
+ };
47298
+ }
47299
+ /** Determines whether both the legacy command and its implicit npm validation hooks have been preserved. */
47300
+ function isLegacyCheckScopePreserved(name, scripts) {
47301
+ return (scripts[name] === scripts.check &&
47302
+ SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => scripts[`${prefix}${name}`] === scripts[`${prefix}check`]));
47303
+ }
47304
+ /** Recognizes the generated default only when no custom lifecycle hooks extend its validation scope. */
47305
+ function isUnchangedLegacyCheckDefault(name, scripts) {
47306
+ return (scripts[name] === 'npm test' &&
47307
+ SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => scripts[`${prefix}${name}`] === undefined));
47308
+ }
47309
+ /**
47310
+ * Recognizes unchanged generated run scripts, including the previous explicit Developer/context defaults.
47311
+ * Unknown shell expressions and custom harness configuration are preserved for manual migration.
47312
+ */
47313
+ function migrateGeneratedCoderRunScript(command) {
47314
+ const prefix = 'npx ptbk coder run --harness openai-codex --thinking-level max';
47315
+ const defaultArguments = ['', ' --agent agents/developer.book --context AGENTS.md'];
47316
+ const verificationArguments = [
47317
+ ' --test-before yes-and-fix',
47318
+ ...LEGACY_CHECK_SCRIPT_NAMES.map((name) => ` --test "npm run ${name}" --test-before yes-and-fix`),
47319
+ ...LEGACY_CHECK_SCRIPT_NAMES.map((name) => ` --check "npm run ${name}" --check-before yes-and-fix`),
47320
+ ];
47321
+ const isGenerated = defaultArguments.some((defaults) => verificationArguments.some((verification) => command === prefix + defaults + verification));
47322
+ return isGenerated ? getDefaultCoderPackageJsonScripts()['coder:run'] : undefined;
47323
+ }
47324
+ /** Selects safe conventional scripts, avoiding duplicates already reached by another selected aggregate. */
47325
+ function selectValidationScripts(scripts) {
47326
+ if (SCRIPT_LIFECYCLE_PREFIXES.some((prefix) => scripts[`${prefix}check`] !== undefined &&
47327
+ !isUsableValidationScript(`${prefix}check`, scripts, new Set())))
47328
+ return [];
47329
+ const selected = [];
47330
+ for (const group of VALIDATION_SCRIPT_GROUPS) {
47331
+ const name = group.find((candidate) => isUsableValidationScript(candidate, scripts, new Set()));
47332
+ if (name !== undefined)
47333
+ selected.push(name);
47334
+ }
47335
+ if (isUsableValidationScript('test', scripts, new Set()))
47336
+ selected.push('test');
47337
+ else
47338
+ for (const group of TEST_SCRIPT_GROUPS) {
47339
+ const name = group.find((candidate) => isUsableValidationScript(candidate, scripts, new Set()));
47340
+ if (name !== undefined)
47341
+ selected.push(name);
47342
+ }
47343
+ return selected.filter((name) => !selected.some((other) => other !== name && scriptReaches(other, name, scripts, new Set())));
47344
+ }
47345
+ /** Inspects each body, referenced script, and implicit lifecycle hook before including it in an aggregate. */
47346
+ function isUsableValidationScript(name, scripts, visited) {
47347
+ const command = scripts[name];
47348
+ if (!command ||
47349
+ visited.has(name) ||
47350
+ NON_VALIDATION_SCRIPT_PATTERN.test(name) ||
47351
+ isCheckSetupRequired(command) ||
47352
+ UNSUITABLE_VALIDATION_BODY_PATTERN.test(command))
47353
+ return false;
47354
+ if (command.split('&&').every((part) => /^\s*(?:echo|printf|true|:)(?:\s|$)/iu.test(part)) ||
47355
+ /\bprocess\.exit\(\s*0\s*\)/u.test(command))
47356
+ return false;
47357
+ if (/\b(?:vite|next|astro|webpack-dev-server)\s+(?:dev|start|serve)\b/iu.test(command) ||
47358
+ /(?:^|&&)\s*(?:vite|webpack-dev-server)(?:\s*$|\s+--)/iu.test(command) ||
47359
+ (/\bvitest\b/iu.test(command) && !/\bvitest\s+run\b/iu.test(command)))
47360
+ return false;
47361
+ const nextVisited = new Set([...visited, name]);
47362
+ const references = projectScriptReferences(command);
47363
+ // Ambiguous package-manager flags and cwd switches cannot be composed confidently as root validation.
47364
+ if (/\b(?:npm|pnpm|yarn)\s+(?:--|run(?:-script)?\s+--)/u.test(command) ||
47365
+ (/\b(?:npm|pnpm|yarn)\b/u.test(command) && references.length === 0))
47366
+ return false;
47367
+ if (references.some((reference) => !isUsableValidationScript(reference, scripts, nextVisited)))
47368
+ return false;
47369
+ return SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => {
47370
+ const lifecycleName = `${prefix}${name}`;
47371
+ return scripts[lifecycleName] === undefined || isUsableValidationScript(lifecycleName, scripts, nextVisited);
47372
+ });
47373
+ }
47374
+ /** Checks whether a selected aggregate already reaches another selected script. */
47375
+ function scriptReaches(name, target, scripts, visited) {
47376
+ var _a;
47377
+ if (visited.has(name))
47378
+ return false;
47379
+ const nextVisited = new Set([...visited, name]);
47380
+ const command = (_a = scripts[name]) !== null && _a !== void 0 ? _a : '';
47381
+ if ((target === 'coder:run' && /\b(?:ptbk|promptbook)\s+coder\s+run\b/u.test(command)) ||
47382
+ (target === 'coder:fix' && /\b(?:ptbk|promptbook)\s+coder\s+fix\b/u.test(command)))
47383
+ return true;
47384
+ const references = [
47385
+ ...projectScriptReferences(command),
47386
+ ...SCRIPT_LIFECYCLE_PREFIXES.map((prefix) => `${prefix}${name}`).filter((lifecycleName) => scripts[lifecycleName] !== undefined),
47387
+ ];
47388
+ return references.some((reference) => reference === target || scriptReaches(reference, target, scripts, nextVisited));
47389
+ }
47390
+
47391
+ // cspell:ignore Procfile
47392
+ /** Runtime/dependency/history directories do not contain maintained project callers. */
47393
+ const IGNORED_CALLER_DIRECTORIES = new Set([
47394
+ 'node_modules',
47395
+ '.git',
47396
+ '.promptbook',
47397
+ '.next',
47398
+ '.tmp',
47399
+ 'dist',
47400
+ 'build',
47401
+ 'coverage',
47402
+ ]);
47403
+ /** Only Coder's known task-history paths are excluded; the same names elsewhere can hold active callers. */
47404
+ const IGNORED_CALLER_HISTORY_PATHS = new Set(['prompts/done', 'prompts/traces']);
47405
+ /** Source and workflow files whose references must be preserved during npm script migration. */
47406
+ const CALLER_FILE_PATTERN = /\.(?:[cm]?jsx?|tsx?|json|ya?ml|md|sh|bash|ps1|cmd|bat|py|toml|xml|groovy)$/iu;
47407
+ /** Conventional workflow files which have no filename extension. */
47408
+ const CALLER_FILE_NAMES = new Set(['Makefile', 'Dockerfile', 'Jenkinsfile', 'Procfile']);
47409
+ /**
47410
+ * Finds external project callers conservatively; it never rewrites their shell expressions.
47411
+ * @private migration safety check for the Coder initializer
47412
+ */
47413
+ async function findLegacyCoderCheckCallers(projectPath, legacyNames) {
47414
+ const callers = Object.fromEntries(legacyNames.map((name) => [name, []]));
47415
+ if (legacyNames.length === 0)
47416
+ return callers;
47417
+ /** Scans maintained text files without following symlinks or inspecting runtime state. */
47418
+ async function visit(relativeDirectory) {
47419
+ let entries;
47420
+ try {
47421
+ entries = await promises.readdir(path.join(projectPath, relativeDirectory), { withFileTypes: true });
47422
+ }
47423
+ catch (_a) {
47424
+ for (const legacyName of legacyNames)
47425
+ callers[legacyName].push(`${relativeDirectory} (unreadable directory; review manually)`);
47426
+ return;
47427
+ }
47428
+ for (const entry of entries) {
47429
+ const relativePath = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
47430
+ if (entry.isSymbolicLink()) {
47431
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name)) {
47432
+ for (const legacyName of legacyNames)
47433
+ callers[legacyName].push(`${relativePath} (symbolic link; review manually)`);
47434
+ }
47435
+ continue;
47436
+ }
47437
+ if (entry.isDirectory()) {
47438
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name) && !IGNORED_CALLER_HISTORY_PATHS.has(relativePath))
47439
+ await visit(relativePath);
47440
+ }
47441
+ else if (entry.isFile() && (CALLER_FILE_PATTERN.test(entry.name) || CALLER_FILE_NAMES.has(entry.name))) {
47442
+ try {
47443
+ let content = await promises.readFile(path.join(projectPath, relativePath), 'utf-8');
47444
+ if (relativePath === 'package.json') {
47445
+ // Script callers are already handled by the merge. Other project-owned metadata may also
47446
+ // reference a script, so it must be included when deciding whether removal is safe.
47447
+ const packageMetadata = JSON.parse(content);
47448
+ delete packageMetadata.scripts;
47449
+ content = JSON.stringify(packageMetadata);
47450
+ }
47451
+ for (const legacyName of legacyNames) {
47452
+ if (content.includes(legacyName))
47453
+ callers[legacyName].push(relativePath === 'package.json' ? 'package.json (outside scripts)' : relativePath);
47454
+ }
47455
+ }
47456
+ catch (_b) {
47457
+ // An unreadable file cannot prove that deletion is safe. Keep the entry and explain why.
47458
+ for (const legacyName of legacyNames)
47459
+ callers[legacyName].push(`${relativePath} (unreadable; review manually)`);
47460
+ }
47461
+ }
47462
+ }
47463
+ }
47464
+ await visit('');
47465
+ return callers;
47466
+ }
47467
+
46893
47468
  /**
46894
47469
  * Relative path to `package.json` in the initialized project.
46895
47470
  */
@@ -46897,17 +47472,30 @@
46897
47472
  /**
46898
47473
  * Ensures `package.json` contains the standalone Promptbook coder helper scripts.
46899
47474
  *
46900
- * Scripts which the project already defines are kept as they are, only missing ones are added.
47475
+ * Project definitions are preserved; missing scripts and narrowly recognized generated migrations are applied.
46901
47476
  *
46902
47477
  * @private function of `initializeCoderProjectConfiguration`
46903
47478
  */
46904
47479
  async function ensureCoderPackageJsonFile(projectPath) {
46905
- return mergeStringRecordJsonFile({
47480
+ let preparation;
47481
+ const mergedFile = await mergeStringRecordJsonFile({
46906
47482
  projectPath,
46907
47483
  relativeFilePath: PACKAGE_JSON_FILE_PATH,
46908
47484
  fieldPath: 'scripts',
46909
47485
  nextEntries: getDefaultCoderPackageJsonScripts(),
47486
+ transformExistingEntries: async (entries) => {
47487
+ const legacyNames = Object.keys(entries).filter((name) => name === 'test-for-ptbk-coder' || name === 'check-for-ptbk-coder');
47488
+ const externalCallers = await findLegacyCoderCheckCallers(projectPath, legacyNames);
47489
+ preparation = prepareCoderPackageJsonScripts(entries, externalCallers);
47490
+ return preparation.scripts;
47491
+ },
46910
47492
  });
47493
+ return {
47494
+ ...mergedFile,
47495
+ checkScriptSummary: preparation.checkScriptSummary,
47496
+ migrationInstructions: preparation.migrationInstructions,
47497
+ isCheckConfigured: preparation.isCheckConfigured,
47498
+ };
46911
47499
  }
46912
47500
  // Note: [🟡] Code for coder init package.json bootstrapping [ensureCoderPackageJsonFile](src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts) should never be published outside of `@promptbook/cli`
46913
47501
 
@@ -47201,9 +47789,9 @@
47201
47789
 
47202
47790
  Runner-written completion lines may attribute implementation to a harness and model, include
47203
47791
  reasoning/authentication labels, attempts or interrupted/continued history, and report implementation,
47204
- testing, fixing, or total cost/time information. Those are observed run metadata, not instructions, budgets,
47205
- or a frozen list of supported models. A completed status alone does not prove tests ran: direct runs only
47206
- execute an automated verification command when configured. Preserve actual measurements and record unknown
47792
+ checking, fixing, or total cost/time information. Those are observed run metadata, not instructions, budgets,
47793
+ or a frozen list of supported models. A completed status alone does not prove checks ran: direct runs only
47794
+ execute the project check command when configured. Preserve actual measurements and record unknown
47207
47795
  results or checks that were not run honestly. Manual users must not invent cost/time figures, copy another
47208
47796
  run's attribution, or mark unverified work as verified. The textual metadata rows above demonstrate syntax
47209
47797
  only; use them only when the described events actually happened.
@@ -47254,9 +47842,9 @@
47254
47842
  | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47255
47843
  | \`ptbk init\` / \`ptbk coder init\` | Initializes missing Git, then creates missing configuration, local Books, this guide, and fresh-directory boilerplates; preserves project-owned content. \`--no-questions\` skips installation questions/checks. |
47256
47844
  | \`ptbk coder add "Describe the reviewed change"\` | Creates one numbered, tagged pending PRD. Accepts a description argument, stdin, or interactive input; \`--priority\` and \`--template\` are available. It does not discuss or implement the task. Review the generated requirements before running. |
47257
- | \`ptbk coder plan --harness openai-codex\` | Interactive planning with the local Planner. Review proposals; \`/save\` writes pending PRDs, \`/draft\` writes not-ready drafts, \`/discard\` drops proposals, \`/exit\` ends. It changes PRD Markdown only and never implements or starts the queue. Currently this planning command supports the Codex harness in a compatible interactive terminal. |
47845
+ | \`ptbk coder plan --harness openai-codex\` | Interactive planning with the local Developer. Review proposals; \`/save\` writes pending PRDs, \`/draft\` writes not-ready drafts, \`/discard\` drops proposals, \`/exit\` ends. It changes PRD Markdown only and never implements or starts the queue. Currently this planning command supports the Codex harness in a compatible interactive terminal. |
47258
47846
  | \`ptbk coder list\` | Displays fully authored pending tasks grouped by descending priority without execution or file changes. Optional harness/model/agent and priority filters narrow the list. |
47259
- | \`ptbk coder run --harness openai-codex --limit 1 --no-commit --no-auto --test "npm test"\` | Confirms and implements one selected task, then runs the specified project check. Replace the test command as needed. Without \`--limit\`, execution proceeds through the eligible queue; without \`--no-commit\`, run stages and commits its changes. |
47847
+ | \`ptbk coder run --harness openai-codex --limit 1 --no-commit --no-auto --check "npm run check"\` | Confirms and implements one selected task, then runs the specified project check. Replace the check command as needed. Without \`--limit\`, execution proceeds through the eligible queue; without \`--no-commit\`, run stages and commits its changes. |
47260
47848
  | \`ptbk coder verify\` | Human review helper: shows completion claims, offers archival or a repair section, and can also ask about pending tasks. It does not run acceptance tests itself. Archival moves the whole file to \`done/\`, so inspect every section before agreeing. There is no separate archive command. |
47261
47849
  | \`ptbk coder find-unwritten\` | Lists pending sections still containing authoring placeholders; not-ready drafts are not included. |
47262
47850
  | \`ptbk coder generate-boilerplates\` | Adds not-ready starter sections using reusable templates; author and review them before making them pending. |
@@ -47268,11 +47856,18 @@
47268
47856
  pulling require the corresponding explicit options. Consult \`ptbk coder --help\` and the relevant subcommand's
47269
47857
  \`--help\` for the installed version's full options.
47270
47858
 
47271
- The initialized \`npm run coder:run\` script supplies \`AGENTS.md\` as context, selects the Codex harness, and
47272
- uses \`npm run test-for-ptbk-coder\` for checks (including pre-run repair mode). The new check script delegates
47273
- to \`npm test\`; configure real tests for your project before running it. Existing npm scripts are preserved, so
47274
- read your \`package.json\` to see your actual commands. Direct \`ptbk coder run\` does not inherit npm-script
47275
- flags.
47859
+ The initialized \`npm run coder:run\` script selects the Codex harness and uses
47860
+ \`--check "npm run check" --check-before yes-and-fix\`. Developer, the current project directory, and its
47861
+ \`AGENTS.md\` are shared CLI defaults. A **check** covers the project's chosen tests, linting, typechecking,
47862
+ builds, generated-code consistency, and other validation. Tests remain one kind of check.
47863
+ Init preserves an existing \`scripts.check\` exactly, or composes applicable existing validation scripts and
47864
+ reports the selected scope. If none are usable, it creates a failing setup placeholder: configure real
47865
+ validation before running Coder. Missing checks or setup placeholders stop with setup guidance, without model repairs.
47866
+ Direct \`ptbk coder run\` defaults to \`--check-before no\`; checks after prompts remain opt-in. Enabled
47867
+ preflight without an explicit command uses \`npm run check\`, including during repair verification.
47868
+ Replace retired \`--test\` / \`--test-before\` with \`--check\` / \`--check-before\`. Init migrates recognized
47869
+ generated scripts and preserves custom legacy validation in \`check\`. Conflicting definitions or custom callers
47870
+ are preserved with manual migration instructions; review them before removing a legacy script.
47276
47871
 
47277
47872
  ### Book agent, harness, and model
47278
47873
 
@@ -47284,9 +47879,20 @@
47284
47879
  Init creates local Developer and Planner Books in \`agents/developer.book\` and \`agents/planner.book\`, their
47285
47880
  Lawyer and Copywriter TEAM advisers, and shared \`agents/.core/adam.book\` instructions. Books can
47286
47881
  inherit/import instructions; TEAM entries expose advisers for relevant consultations, not an obligation to
47287
- invoke everyone. \`run\` and \`server\` default to Developer; \`plan\` defaults to Planner.
47288
- \`--agent agents/my-developer.book\` overrides the Book only. \`list\` has no default Book filter. Selecting
47289
- Developer for \`plan\` still permits planning only.
47882
+ invoke everyone. \`run\`, \`server\` and \`plan\` default to Developer.
47883
+ \`--agent ./agents/planner.book\` selects Planner explicitly; another Book path also works. \`list\` has no
47884
+ default Book filter. Every Book used in \`plan\`, including Developer, still permits planning only.
47885
+
47886
+ Project commands default to the current directory captured when invoked. \`--path\` selects another existing
47887
+ directory, relative to that invocation or absolute; an enclosing Git repository never relocates the project's
47888
+ Books, PRDs or context. Additional context defaults to the actual UTF-8 contents of that project's \`AGENTS.md\`.
47889
+ A missing implicit file warns and continues; an unreadable file fails. \`--context\` replaces the default with
47890
+ inline instructions or a file resolved relative to the selected project; \`--context ""\` disables it.
47891
+ Invalid explicit Books and missing/unreadable explicit context files fail without falling back.
47892
+ Thus \`ptbk coder run --harness openai-codex\` and
47893
+ \`ptbk coder run --harness openai-codex --agent ./agents/developer.book --path . --context ./AGENTS.md\` select
47894
+ the same inputs. New npm scripts rely on these defaults; init still creates missing Books and AGENTS.md even
47895
+ when every script already exists, preserving existing custom scripts, settings and context.
47290
47896
 
47291
47897
  Edit local Books to reflect this project's rules; repeat init preserves customizations and adds missing
47292
47898
  defaults/helper references. An invalid explicit Book selection fails rather than silently changing roles.
@@ -47357,7 +47963,7 @@
47357
47963
  const adamArtifact = defaultAgentArtifacts.find(({ relativeFilePath }) => relativeFilePath === adamRelativeFilePath);
47358
47964
  const { envFileStatus, initializedEnvVariableNames } = await completeStep('.env', ensureCoderEnvFile(projectPath));
47359
47965
  const gitignoreFileStatus = await completeStep('.gitignore', ensureCoderGitignoreFile(projectPath));
47360
- const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await completeStep('package.json', ensureCoderPackageJsonFile(projectPath));
47966
+ const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames, checkScriptSummary, migrationInstructions, isCheckConfigured, } = await completeStep('package.json', ensureCoderPackageJsonFile(projectPath));
47361
47967
  const vscodeSettingsFileStatus = await completeStep('.vscode/settings.json', ensureCoderVscodeSettingsFile(projectPath));
47362
47968
  const referencedArtifactStatuses = [
47363
47969
  ...defaultAgentArtifacts.filter(({ relativeFilePath }) => relativeFilePath !== adamRelativeFilePath),
@@ -47378,6 +47984,9 @@
47378
47984
  addedPackageJsonScriptNames,
47379
47985
  referencedArtifactStatuses,
47380
47986
  initializedEnvVariableNames,
47987
+ checkScriptSummary,
47988
+ migrationInstructions,
47989
+ isCheckConfigured,
47381
47990
  };
47382
47991
  }
47383
47992
  // Note: [🟡] Code for coder init project bootstrapping [initializeCoderProjectConfiguration](src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts) should never be published outside of `@promptbook/cli`
@@ -47389,9 +47998,11 @@
47389
47998
  */
47390
47999
  function printInitializationSummary(summary, workspace) {
47391
48000
  const isUnresolved = summary.adamAgentFileStatus === 'unresolved' ||
48001
+ !summary.isCheckConfigured ||
48002
+ summary.migrationInstructions.length > 0 ||
47392
48003
  summary.referencedArtifactStatuses.some(({ status }) => status === 'unresolved');
47393
48004
  console.info(isUnresolved
47394
- ? colors__default["default"].yellow('Promptbook coder initialization has unresolved artifacts.')
48005
+ ? colors__default["default"].yellow('Promptbook coder configuration setup: additional setup is required.')
47395
48006
  : colors__default["default"].green('Promptbook coder configuration setup:'));
47396
48007
  if (workspace)
47397
48008
  console.info(colors__default["default"].gray(`✔ Git repository: ${workspace.repositoryStatus} (${workspace.repositoryRoot})`));
@@ -47420,6 +48031,9 @@
47420
48031
  else {
47421
48032
  printInitializationNote('Required coder env variables are already present.', colors__default["default"].gray);
47422
48033
  }
48034
+ console.info((summary.isCheckConfigured ? colors__default["default"].cyan : colors__default["default"].yellow)(summary.checkScriptSummary));
48035
+ for (const instruction of summary.migrationInstructions)
48036
+ console.info(colors__default["default"].yellow(`! ${instruction}`));
47423
48037
  }
47424
48038
  /**
47425
48039
  * Formats one initialization status into a human-readable label.
@@ -47501,7 +48115,7 @@
47501
48115
  - Adds missing local Lawyer and Copywriter TEAM references to Developer and Planner, preserving existing content
47502
48116
  - Helpers advise on relevant tasks; declaring TEAM does not run them for every task
47503
48117
  - Reports created, augmented, unchanged and unresolved Books; invalid or conflicting files are left untouched
47504
- - Other referenced files, like coder:run context, are created with their newly added scripts
48118
+ - Missing AGENTS.md is initialized independently of scripts; existing content is preserved
47505
48119
 
47506
48120
  Ensures required coding-agent environment variables in .env:
47507
48121
  - CODING_AGENT_GIT_NAME
@@ -47519,9 +48133,14 @@
47519
48133
  addQuestionsOption(command);
47520
48134
  addWorkspaceRepositoryOptions(command);
47521
48135
  command.action(handleActionErrors(async (cliOptions) => {
48136
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47522
48137
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
47523
48138
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
47524
- const workspace = await $preflightWorkspaceRepository({ policy: 'initialize', ...questionsOptions });
48139
+ const workspace = await $preflightWorkspaceRepository({
48140
+ ...projectOptions,
48141
+ policy: 'initialize',
48142
+ ...questionsOptions,
48143
+ });
47525
48144
  const { projectPath } = workspace;
47526
48145
  const completedSteps = [`Git repository ${workspace.repositoryStatus}: ${workspace.repositoryRoot}`];
47527
48146
  try {
@@ -47635,6 +48254,11 @@
47635
48254
  developer: CODER_DEVELOPER_AGENT_FILE_PATH,
47636
48255
  planner: CODER_PLANNER_AGENT_FILE_PATH,
47637
48256
  };
48257
+ /**
48258
+ * Shared persona for optional single-agent execution and authoring; capabilities belong to the command.
48259
+ * @private internal constant of project CLI commands
48260
+ */
48261
+ const DEFAULT_CODER_AGENT_ROLE = 'developer';
47638
48262
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
47639
48263
 
47640
48264
  /**
@@ -47654,6 +48278,14 @@
47654
48278
  : 'No default Book filter; omitting --agent includes tasks for all agents';
47655
48279
  command.option('--agent <agent-book-path>', `${CODER_AGENT_OPTION_DESCRIPTION}. ${defaultDescription}`);
47656
48280
  }
48281
+ /**
48282
+ * Registers the shared optional persona and context for single-agent execution or authoring.
48283
+ * @private internal CLI registration helper
48284
+ */
48285
+ function addCoderExecutionOptions(command) {
48286
+ addCoderAgentOption(command, DEFAULT_CODER_AGENT_ROLE);
48287
+ addProjectContextOption(command);
48288
+ }
47657
48289
  // Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
47658
48290
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
47659
48291
 
@@ -47682,9 +48314,13 @@
47682
48314
  addPromptPriorityOptions(command);
47683
48315
  addWorkspaceRepositoryOptions(command);
47684
48316
  command.action(handleActionErrors(async (cliOptions) => {
48317
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47685
48318
  const { agent, priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
47686
48319
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
47687
- const workspace = await $preflightWorkspaceRepository({ policy: 'read-only' });
48320
+ const workspace = await $preflightWorkspaceRepository({
48321
+ ...projectOptions,
48322
+ policy: 'read-only',
48323
+ });
47688
48324
  const resolvedCoderAgentBook = agent === undefined
47689
48325
  ? undefined
47690
48326
  : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; })).resolveCoderAgentBook(agent, workspace.projectPath);
@@ -47825,12 +48461,17 @@
47825
48461
  `));
47826
48462
  addWorkspaceRepositoryOptions(command);
47827
48463
  command.action(handleActionErrors(async (cliOptions) => {
48464
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47828
48465
  const { period: periodValue } = cliOptions;
47829
48466
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
47830
48467
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
47831
48468
  // Note: The period is validated before the harness installation check, so a mistyped duration fails fast
47832
48469
  const periodMs = parseOptionalPeriodDuration('--period', periodValue);
47833
- const workspace = await $preflightWorkspaceRepository({ policy: 'mutate', ...questionsOptions });
48470
+ const workspace = await $preflightWorkspaceRepository({
48471
+ ...projectOptions,
48472
+ policy: 'mutate',
48473
+ ...questionsOptions,
48474
+ });
47834
48475
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
47835
48476
  await $ensureCoderHarnessGitignoreRules(workspace.projectPath, runnerOptions.agentName, questionsOptions);
47836
48477
  const pingOptions = {
@@ -47862,10 +48503,10 @@
47862
48503
  */
47863
48504
  function $initializeCoderPlanCommand(program) {
47864
48505
  const command = program.command('plan').description(spaceTrim(`
47865
- Discuss features with the project's Planner and review PRDs before saving them.
48506
+ Discuss features with the project's Developer Book and review PRDs before saving them.
47866
48507
 
47867
- Run ptbk coder init first. Default Book: agents/planner.book; --agent overrides the Book,
47868
- independently of --harness and --model. Currently supports openai-codex with restricted tools.
48508
+ Run ptbk coder init first. Default Book: agents/developer.book; --agent overrides the Book,
48509
+ independently of --harness and --model. Use --agent ./agents/planner.book for Planner. Currently supports openai-codex with restricted tools.
47869
48510
  Requires an interactive terminal and a Codex version supporting --ignore-user-config and --ignore-rules.
47870
48511
  Codex configuration, plugins, hooks and native tools are isolated; --model default uses Codex's built-in default.
47871
48512
  /save applies reviewed proposals, /draft saves [-] drafts, /discard drops proposals, /exit ends.
@@ -47874,11 +48515,12 @@
47874
48515
  `));
47875
48516
  addPromptRunnerSelectionOptions(command);
47876
48517
  addPromptRunnerRuntimeOptions(command);
47877
- addCoderAgentOption(command, 'planner');
48518
+ addCoderExecutionOptions(command);
47878
48519
  addCoderGitSyncOptions(command);
47879
48520
  command.option('--template <path>', 'PRD template (defaults to the project-owned prompts/templates/common.md when present)');
47880
48521
  addWorkspaceRepositoryOptions(command);
47881
48522
  command.action(handleActionErrors(async (cliOptions) => {
48523
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47882
48524
  const { createPlanningTerminal } = await Promise.resolve().then(function () { return createPlanningTerminal$1; });
47883
48525
  const { runPlanningSession } = await Promise.resolve().then(function () { return runPlanningSession$1; });
47884
48526
  const { resolvePlanningPath } = await Promise.resolve().then(function () { return resolvePlanningPath$1; });
@@ -47889,12 +48531,18 @@
47889
48531
  assertPlanningHarnessSupported(options.agentName);
47890
48532
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
47891
48533
  const workspace = await $preflightWorkspaceRepository({
48534
+ ...projectOptions,
47892
48535
  policy: 'mutate',
47893
48536
  isAskingQuestionsEnabled: cliOptions.questions,
47894
48537
  });
47895
48538
  if (cliOptions.questions === false) {
47896
48539
  throw new NotAllowed(spaceTrim('`ptbk coder plan` requires an interactive conversation and review. Run it without `--no-questions` in a terminal.'));
47897
48540
  }
48541
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
48542
+ const projectContext = await resolveCoderProjectContext({
48543
+ ...cliOptions,
48544
+ projectPath: workspace.projectPath,
48545
+ });
47898
48546
  const terminal = createPlanningTerminal();
47899
48547
  try {
47900
48548
  const { projectPath } = workspace;
@@ -47907,6 +48555,8 @@
47907
48555
  ...options,
47908
48556
  projectPath,
47909
48557
  agent: cliOptions.agent,
48558
+ context: cliOptions.context,
48559
+ projectContext,
47910
48560
  template: cliOptions.template,
47911
48561
  preexistingChangedPaths: gitSync.isCommitEnabled
47912
48562
  ? new Set(Array.from(commitScope.snapshotBeforeOperation.changedFileHashes.keys(), (path$1) => path.relative(projectPath, path.join(workspace.repositoryRoot, path$1)).replace(/\\/gu, '/')))
@@ -48096,30 +48746,20 @@
48096
48746
  }
48097
48747
  // Note: [🟡] Code for CLI free disk space handling [$assertSufficientFreeDiskSpace](src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.ts) should never be published outside of `@promptbook/cli`
48098
48748
 
48099
- /**
48100
- * Modes supported by `ptbk coder run --test-before`.
48101
- */
48102
- const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
48103
- /**
48104
- * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
48105
- */
48106
- const DEFAULT_CODER_TEST_COMMAND = 'npm test';
48107
- /**
48108
- * Checks whether a value is a supported `--test-before` mode.
48109
- */
48110
- function isTestBeforeMode(value) {
48111
- return TEST_BEFORE_MODE_VALUES.includes(value);
48112
- }
48113
-
48114
48749
  /**
48115
48750
  * Validates cross-flag constraints before workspace setup or the run starts.
48116
48751
  * @private shared validation for Coder execution commands
48117
48752
  */
48118
48753
  function validateCoderRunOptions(options) {
48119
48754
  var _a;
48120
- if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
48755
+ if (options.checkCommand !== undefined && !options.checkCommand.trim()) {
48121
48756
  throw new NotAllowed(_spaceTrim.spaceTrim(`
48122
- Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
48757
+ Option \`--check\` requires a non-empty shell command, for example \`npm run check\`.
48758
+ `));
48759
+ }
48760
+ if (!isCheckBeforeMode((_a = options.checkBefore) !== null && _a !== void 0 ? _a : 'no')) {
48761
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
48762
+ Invalid ${'`--check-before`'} mode: \`${String(options.checkBefore)}\`.
48123
48763
 
48124
48764
  Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
48125
48765
  `));
@@ -48157,9 +48797,9 @@
48157
48797
  An isolated task is implemented in a fresh temporary worktree checked out from the last commit, so the uncommitted changes of the interrupted prompt would be left behind instead of being continued.
48158
48798
  `));
48159
48799
  }
48160
- if (options.gitChanges === 'continue' && options.testBefore === 'yes-and-fix') {
48800
+ if (options.gitChanges === 'continue' && options.checkBefore === 'yes-and-fix') {
48161
48801
  throw new NotAllowed(_spaceTrim.spaceTrim(`
48162
- Flag \`--git-changes continue\` cannot be combined with \`--test-before yes-and-fix\`.
48802
+ Flag \`--git-changes continue\` cannot be combined with \`--check-before yes-and-fix\`.
48163
48803
 
48164
48804
  An interrupted prompt already has changes in progress, so there is no unmodified project state for pre-coding verification to repair.
48165
48805
  `));
@@ -48206,9 +48846,9 @@
48206
48846
  *
48207
48847
  * @private internal utility of `promptbookCli`
48208
48848
  */
48209
- async function $resolvePromptbookCliInstallations() {
48849
+ async function $resolvePromptbookCliInstallations(projectPath = process.cwd()) {
48210
48850
  const [localInstallations, globalInstallations] = await Promise.all([
48211
- $resolveLocalPromptbookCliInstallations(),
48851
+ $resolveLocalPromptbookCliInstallations(projectPath),
48212
48852
  $resolveGlobalPromptbookCliInstallations(),
48213
48853
  ]);
48214
48854
  return [...localInstallations, ...globalInstallations];
@@ -48216,8 +48856,8 @@
48216
48856
  /**
48217
48857
  * Finds the closest ancestor `package.json` which directly declares a Promptbook CLI package.
48218
48858
  */
48219
- async function $resolveLocalPromptbookCliInstallations() {
48220
- let candidateProjectPath = path.resolve(process.cwd());
48859
+ async function $resolveLocalPromptbookCliInstallations(projectPath) {
48860
+ let candidateProjectPath = path.resolve(projectPath);
48221
48861
  let isFileSystemRootReached = false;
48222
48862
  while (!isFileSystemRootReached) {
48223
48863
  const projectPackageJson = await $readJsonObject(path.join(candidateProjectPath, PACKAGE_JSON_FILENAME));
@@ -48373,8 +49013,8 @@
48373
49013
  *
48374
49014
  * @private internal utility of `promptbookCli`
48375
49015
  */
48376
- async function $checkPromptbookCliInstallations() {
48377
- const installations = await $resolvePromptbookCliInstallations();
49016
+ async function $checkPromptbookCliInstallations(projectPath = process.cwd()) {
49017
+ const installations = await $resolvePromptbookCliInstallations(projectPath);
48378
49018
  if (installations.length === 0) {
48379
49019
  return [];
48380
49020
  }
@@ -48429,10 +49069,10 @@
48429
49069
  * @returns `true` when the package update succeeds, `false` when it fails
48430
49070
  * @private internal utility of `promptbookCli`
48431
49071
  */
48432
- async function $updatePromptbookCliInstallation(status) {
49072
+ async function $updatePromptbookCliInstallation(status, projectPath = process.cwd()) {
48433
49073
  const { npmPackageName, installationLocation } = status.installation;
48434
49074
  const installCommand = buildPromptbookCliInstallCommand(status.installation);
48435
- const updateWorkingDirectory = installationLocation === 'global' ? process.cwd() : status.installation.projectPath;
49075
+ const updateWorkingDirectory = installationLocation === 'global' ? projectPath : status.installation.projectPath;
48436
49076
  console.info(colors__default["default"].cyan(`Updating Promptbook CLI package \`${npmPackageName}\` with \`${installCommand}\`...`));
48437
49077
  try {
48438
49078
  await $execCommand({
@@ -48502,7 +49142,7 @@
48502
49142
  * @returns `true` when at least one installation was updated and the current coder run should stop
48503
49143
  * @private internal utility of `promptbookCli`
48504
49144
  */
48505
- async function $ensurePromptbookCliInstallations(questionsOptions) {
49145
+ async function $ensurePromptbookCliInstallations(questionsOptions, projectPath = process.cwd()) {
48506
49146
  if (!questionsOptions.isAskingQuestionsEnabled) {
48507
49147
  // Note: `--no-questions` forbids asking for the approval, so the registry checks would be useless
48508
49148
  return false;
@@ -48511,7 +49151,7 @@
48511
49151
  // Note: Non-interactive runs cannot approve an update and should not wait for registry checks
48512
49152
  return false;
48513
49153
  }
48514
- const statuses = await $checkPromptbookCliInstallations();
49154
+ const statuses = await $checkPromptbookCliInstallations(projectPath);
48515
49155
  const outdatedStatuses = statuses.filter(({ installationState }) => installationState === 'outdated');
48516
49156
  if (outdatedStatuses.length === 0) {
48517
49157
  reportPromptbookCliInstallationStatuses(statuses);
@@ -48525,7 +49165,7 @@
48525
49165
  }
48526
49166
  const updateResults = [];
48527
49167
  for (const status of outdatedStatuses) {
48528
- updateResults.push(await $updatePromptbookCliInstallation(status));
49168
+ updateResults.push(await $updatePromptbookCliInstallation(status, projectPath));
48529
49169
  }
48530
49170
  const isAnyInstallationUpdated = updateResults.some(Boolean);
48531
49171
  const isEveryInstallationUpdated = updateResults.every(Boolean);
@@ -48570,6 +49210,89 @@
48570
49210
  }
48571
49211
  // Note: [🟡] Code for Promptbook CLI installation orchestration [$ensurePromptbookCliInstallations](src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts) should never be published outside of `@promptbook/cli`
48572
49212
 
49213
+ /** Standalone shell composition tokens remain operators when a command is supplied as several arguments. */
49214
+ const SHELL_COMPOSITION_TOKENS = new Set([
49215
+ '&&',
49216
+ '||',
49217
+ '|',
49218
+ '|&',
49219
+ '&',
49220
+ ';',
49221
+ '(',
49222
+ ')',
49223
+ '>',
49224
+ '>>',
49225
+ '<',
49226
+ '2>',
49227
+ '2>>',
49228
+ '2>&1',
49229
+ ]);
49230
+ /**
49231
+ * Retired aggregate option spellings and their canonical replacements, used only for migration diagnostics.
49232
+ * @private shared CLI migration definitions for Coder execution
49233
+ */
49234
+ const LEGACY_CODER_CHECK_OPTIONS = [
49235
+ ['--test', '--check'],
49236
+ ['--test-before', '--check-before'],
49237
+ ];
49238
+ /**
49239
+ * Registers the shared project check command and rejects retired aggregate-test spellings.
49240
+ * @private shared CLI registration for Coder execution
49241
+ */
49242
+ function addCoderCheckOptions(command, isCheckBeforeSupported) {
49243
+ command.option('--check <check-command...>', 'Run project checks after each prompt; quote the full command when it contains flags or shell composition');
49244
+ if (isCheckBeforeSupported) {
49245
+ command.addOption(new commander.Option('--check-before <mode>', `Check before coding: ${CHECK_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm run check when --check is omitted)`)
49246
+ .choices([...CHECK_BEFORE_MODE_VALUES])
49247
+ .default('no'));
49248
+ }
49249
+ // These diagnostic-only options never forward a value or start execution.
49250
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
49251
+ command.addOption(new commander.Option(`${legacyFlag} [value...]`).hideHelp());
49252
+ command.on(`option:${legacyFlag.slice(2)}`, () => {
49253
+ command.error(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
49254
+ });
49255
+ }
49256
+ }
49257
+ /**
49258
+ * Builds the same actionable migration diagnostic for registered and direct-script entrypoints.
49259
+ * @private shared CLI diagnostic for Coder execution
49260
+ */
49261
+ function formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag) {
49262
+ return `Aggregate verification option \`${legacyFlag}\` was renamed to \`${replacementFlag}\`. Update your command and package.json scripts.`;
49263
+ }
49264
+ /**
49265
+ * Normalizes shell command tokens without parsing a quoted command again or losing spaced argument boundaries.
49266
+ * @private shared command normalization for Coder execution
49267
+ */
49268
+ function normalizeCheckCommandOption(value) {
49269
+ var _a, _b;
49270
+ if (value === undefined)
49271
+ return undefined;
49272
+ const parts = typeof value === 'string' ? [value] : value;
49273
+ const firstPart = (_b = (_a = parts[0]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
49274
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
49275
+ if (firstPart === legacyFlag || firstPart.startsWith(`${legacyFlag}=`)) {
49276
+ throw new NotAllowed(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
49277
+ }
49278
+ }
49279
+ const command = parts.length === 1
49280
+ ? parts[0].trim()
49281
+ : parts
49282
+ .map((part) => !SHELL_COMPOSITION_TOKENS.has(part) && (!part || /[\s'"\\$`;&|<>()*?]/u.test(part))
49283
+ ? quoteBashArgument(part)
49284
+ : part)
49285
+ .join(' ')
49286
+ .trim();
49287
+ if (!command || !firstPart || firstPart.startsWith('-')) {
49288
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
49289
+ Option \`--check\` requires a non-empty shell command, for example \`--check "npm run check"\`.
49290
+ `));
49291
+ }
49292
+ return command;
49293
+ }
49294
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
49295
+
48573
49296
  /**
48574
49297
  * Prints why a coder run has ended to the terminal.
48575
49298
  *
@@ -48616,20 +49339,16 @@
48616
49339
  - Offers to add missing project-local ignore rules for the selected harness
48617
49340
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
48618
49341
  - Supports GPG signing of commits
48619
- - Optional pre-coding test run that can stop or repair pre-existing failures
48620
- - Optional post-prompt verification with test-feedback retries
49342
+ - Optional pre-coding check run that can stop or repair pre-existing failures
49343
+ - Optional post-prompt checks with check-feedback retries
48621
49344
  - Progress tracking and interactive P/S/X terminal controls; O changes only the dashboard output view
48622
49345
  - Dry-run mode to preview prompts
48623
49346
  `));
48624
49347
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
48625
49348
  addPromptRunnerSelectionOptions(command);
48626
49349
  addQuestionsOption(command);
48627
- addCoderAgentOption(command, 'developer');
48628
- command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
48629
- command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
48630
- 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)`)
48631
- .choices([...TEST_BEFORE_MODE_VALUES])
48632
- .default('no'));
49350
+ addCoderExecutionOptions(command);
49351
+ addCoderCheckOptions(command, true);
48633
49352
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
48634
49353
  addPromptRunnerExecutionOptions(command);
48635
49354
  command.option('--isolate', _spaceTrim.spaceTrim(`
@@ -48658,9 +49377,9 @@
48658
49377
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
48659
49378
  addWorkspaceRepositoryOptions(command);
48660
49379
  command.action(handleActionErrors(async (cliOptions) => {
48661
- 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;
48662
- const configuredTestCommand = normalizeCommandOptionValue$1(test);
48663
- const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
49380
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49381
+ const { dryRun, agent, context, check, checkBefore, preserveLogs, isolate: isIsolated, priority, minPriority: minimumPriority, maxPriority: maximumPriority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
49382
+ const checkCommand = resolveCoderCheckCommand(normalizeCheckCommandOption(check), checkBefore);
48664
49383
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
48665
49384
  isAgentRequired: !dryRun,
48666
49385
  });
@@ -48689,8 +49408,8 @@
48689
49408
  model: runnerOptions.model,
48690
49409
  agent,
48691
49410
  context,
48692
- testCommand,
48693
- testBefore,
49411
+ checkCommand,
49412
+ checkBefore,
48694
49413
  preserveLogs,
48695
49414
  isIsolated,
48696
49415
  noUi: runnerOptions.noUi,
@@ -48709,16 +49428,20 @@
48709
49428
  };
48710
49429
  validateCoderRunOptions(runOptions);
48711
49430
  const workspace = await $preflightWorkspaceRepository({
49431
+ ...projectOptions,
48712
49432
  policy: dryRun ? 'read-only' : 'mutate',
48713
49433
  ...questionsOptions,
48714
49434
  });
49435
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
49436
+ const projectContext = await resolveCoderProjectContext({
49437
+ projectPath: workspace.projectPath,
49438
+ agent,
49439
+ context,
49440
+ });
48715
49441
  if (!dryRun) {
48716
- // Reject missing or unreadable Books before offering installations or project configuration writes.
48717
- const { resolveCoderAgentBook } = await Promise.resolve().then(function () { return resolveCoderAgent$1; });
48718
- await resolveCoderAgentBook(agent, workspace.projectPath, { defaultRole: 'developer' });
48719
49442
  // Check disk space before installations and repository writes; previews require no setup.
48720
49443
  await $assertSufficientFreeDiskSpace(workspace.projectPath);
48721
- if (await $ensurePromptbookCliInstallations(questionsOptions)) {
49444
+ if (await $ensurePromptbookCliInstallations(questionsOptions, workspace.projectPath)) {
48722
49445
  return process.exit(0);
48723
49446
  }
48724
49447
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
@@ -48727,8 +49450,7 @@
48727
49450
  // Note: Import the function dynamically to avoid loading heavy dependencies until needed
48728
49451
  const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
48729
49452
  try {
48730
- // Override process.argv to pass options to the legacy parseRunOptions if needed
48731
- await runCodexPrompts({ ...runOptions, workspace });
49453
+ await runCodexPrompts({ ...runOptions, workspace, projectContext });
48732
49454
  }
48733
49455
  catch (error) {
48734
49456
  assertsError(error);
@@ -48738,23 +49460,6 @@
48738
49460
  return process.exit(0);
48739
49461
  }));
48740
49462
  }
48741
- /**
48742
- * Joins one Commander option that may be parsed either as a single string or a variadic token array.
48743
- *
48744
- * @private internal utility of `coder run` command
48745
- */
48746
- function normalizeCommandOptionValue$1(value) {
48747
- if (value === undefined) {
48748
- return undefined;
48749
- }
48750
- const parts = Array.isArray(value) ? value : [value];
48751
- const normalizedValue = parts
48752
- .map((part) => part.trim())
48753
- .filter(Boolean)
48754
- .join(' ')
48755
- .trim();
48756
- return normalizedValue === '' ? undefined : normalizedValue;
48757
- }
48758
49463
  // Note: [🟡] Code for CLI command [run](src/cli/cli-commands/coder/run.ts) should never be published outside of `@promptbook/cli`
48759
49464
  // Note: [💞] Ignore a discrepancy between file name and entity name
48760
49465
 
@@ -48800,9 +49505,8 @@
48800
49505
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
48801
49506
  addPromptRunnerSelectionOptions(command);
48802
49507
  addQuestionsOption(command);
48803
- addCoderAgentOption(command, 'developer');
48804
- command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
48805
- command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
49508
+ addCoderExecutionOptions(command);
49509
+ addCoderCheckOptions(command, false);
48806
49510
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
48807
49511
  addPromptRunnerExecutionOptions(command);
48808
49512
  addPromptPriorityOptions(command);
@@ -48824,9 +49528,10 @@
48824
49528
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
48825
49529
  addWorkspaceRepositoryOptions(command);
48826
49530
  command.action(handleActionErrors(async (cliOptions) => {
48827
- const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, minPriority: minimumPriority, maxPriority: maximumPriority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
49531
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49532
+ const { port: rawPort, dryRun, agent, context, check, preserveLogs, priority, minPriority: minimumPriority, maxPriority: maximumPriority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
48828
49533
  const port = parseCoderServerPort(rawPort);
48829
- const testCommand = normalizeCommandOptionValue(test);
49534
+ const checkCommand = normalizeCheckCommandOption(check);
48830
49535
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
48831
49536
  isAgentRequired: !dryRun,
48832
49537
  });
@@ -48850,7 +49555,7 @@
48850
49555
  model: runnerOptions.model,
48851
49556
  agent,
48852
49557
  context,
48853
- testCommand,
49558
+ checkCommand,
48854
49559
  preserveLogs,
48855
49560
  noUi: runnerOptions.noUi,
48856
49561
  thinkingLevel: runnerOptions.thinkingLevel,
@@ -48867,13 +49572,17 @@
48867
49572
  };
48868
49573
  validateCoderRunOptions(runOptions);
48869
49574
  const workspace = await $preflightWorkspaceRepository({
49575
+ ...projectOptions,
48870
49576
  policy: dryRun ? 'read-only' : 'mutate',
48871
49577
  ...questionsOptions,
48872
49578
  });
49579
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
49580
+ const projectContext = await resolveCoderProjectContext({
49581
+ projectPath: workspace.projectPath,
49582
+ agent,
49583
+ context,
49584
+ });
48873
49585
  if (!dryRun) {
48874
- // Reject missing or unreadable Books before offering installations or project configuration writes.
48875
- const { resolveCoderAgentBook } = await Promise.resolve().then(function () { return resolveCoderAgent$1; });
48876
- await resolveCoderAgentBook(agent, workspace.projectPath, { defaultRole: 'developer' });
48877
49586
  // Check disk space before installations and repository writes; previews require no setup.
48878
49587
  await $assertSufficientFreeDiskSpace(workspace.projectPath);
48879
49588
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
@@ -48882,7 +49591,7 @@
48882
49591
  // Note: Import dynamically to avoid loading heavy dependencies until needed
48883
49592
  const { runCodexPromptsServer } = await Promise.resolve().then(function () { return runCodexPromptsServer$1; });
48884
49593
  try {
48885
- await runCodexPromptsServer({ ...runOptions, workspace });
49594
+ await runCodexPromptsServer({ ...runOptions, workspace, projectContext });
48886
49595
  }
48887
49596
  catch (error) {
48888
49597
  assertsError(error);
@@ -48908,23 +49617,6 @@
48908
49617
  }
48909
49618
  return port;
48910
49619
  }
48911
- /**
48912
- * Joins one Commander option that may be parsed either as a single string or a variadic token array.
48913
- *
48914
- * @private internal utility of `coder server` command
48915
- */
48916
- function normalizeCommandOptionValue(value) {
48917
- if (value === undefined) {
48918
- return undefined;
48919
- }
48920
- const parts = Array.isArray(value) ? value : [value];
48921
- const normalizedValue = parts
48922
- .map((part) => part.trim())
48923
- .filter(Boolean)
48924
- .join(' ')
48925
- .trim();
48926
- return normalizedValue === '' ? undefined : normalizedValue;
48927
- }
48928
49620
  // Note: [🟡] Code for CLI command [server](src/cli/cli-commands/coder/server.ts) should never be published outside of `@promptbook/cli`
48929
49621
  // Note: [💞] Ignore a discrepancy between file name and entity name
48930
49622
 
@@ -49002,9 +49694,11 @@
49002
49694
  addCoderGitSyncOptions(command);
49003
49695
  addWorkspaceRepositoryOptions(command);
49004
49696
  command.action(handleActionErrors(async (cliOptions) => {
49697
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49005
49698
  const { order, ignore } = cliOptions;
49006
49699
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
49007
49700
  const workspace = await $preflightWorkspaceRepository({
49701
+ ...projectOptions,
49008
49702
  policy: 'mutate',
49009
49703
  isAskingQuestionsEnabled: cliOptions.questions,
49010
49704
  });
@@ -49040,7 +49734,7 @@
49040
49734
  * The coder command provides utilities for automated coding:
49041
49735
  * - init: Initialize coder configuration in current project
49042
49736
  * - add: Add one ready-to-run prompt file to the queue
49043
- * - plan: Discuss features with Planner and author reviewed PRDs
49737
+ * - plan: Discuss features with Developer and author reviewed PRDs
49044
49738
  * - generate-boilerplates: Generate prompt boilerplate files
49045
49739
  * - find-refactor-candidates: Find files that need refactoring
49046
49740
  * - list: List ready prompts in priority order without running them
@@ -49061,7 +49755,7 @@
49061
49755
  Subcommands:
49062
49756
  - init: Initialize coder configuration in current project
49063
49757
  - add: Add one ready-to-run prompt file to the queue
49064
- - plan: Discuss features with Planner and author reviewed PRDs
49758
+ - plan: Discuss features with Developer and author reviewed PRDs
49065
49759
  - generate-boilerplates: Generate prompt boilerplate files
49066
49760
  - find-refactor-candidates: Find files that need refactoring
49067
49761
  - find-unwritten: List prompt sections that still need to be authored
@@ -72182,12 +72876,21 @@
72182
72876
  }
72183
72877
  const textPath = path.resolve(options.currentWorkingDirectory, normalizedTextReference);
72184
72878
  const textPathStats = await promises.stat(textPath).catch((error) => {
72185
- if (error.code === 'ENOENT') {
72879
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
72186
72880
  return undefined;
72187
72881
  }
72188
- throw error;
72882
+ if (['ENAMETOOLONG', 'EINVAL', 'ERR_INVALID_ARG_VALUE'].includes(error.code || '') && !isContextFileReference(normalizedTextReference)) {
72883
+ return undefined;
72884
+ }
72885
+ throw contextReadError(options, textPath, error);
72189
72886
  });
72190
72887
  if (!textPathStats) {
72888
+ if (options.isMissingFileAnError && isContextFileReference(normalizedTextReference)) {
72889
+ throw new NotFoundError(spaceTrim(`
72890
+ ${options.contextLabel} file \`${textPath}\` does not exist.
72891
+ Correct the file path in \`${options.optionName}\` or pass inline instructions.
72892
+ `));
72893
+ }
72191
72894
  return normalizedTextReference;
72192
72895
  }
72193
72896
  if (!textPathStats.isFile()) {
@@ -72197,7 +72900,55 @@
72197
72900
  Pass a file path or inline text in \`${options.optionName}\`.
72198
72901
  `));
72199
72902
  }
72200
- return promises.readFile(textPath, 'utf-8');
72903
+ return promises.readFile(textPath, 'utf-8').catch((error) => {
72904
+ throw contextReadError(options, textPath, error);
72905
+ });
72906
+ }
72907
+ /** Distinguishes explicit file spellings from ordinary inline instructions, including native Windows paths. */
72908
+ function isContextFileReference(reference) {
72909
+ return path.isAbsolute(reference) || /^(?:\.{1,2}[/\\]|[a-z]:[/\\]|\\\\)/iu.test(reference) ||
72910
+ (!/[\r\n]/u.test(reference) && /\.(?:md|markdown|txt|book|json|ya?ml)$/iu.test(reference)) ||
72911
+ (!/\s/u.test(reference) && /[/\\]/u.test(reference) && !/^[a-z]+:\/\//iu.test(reference));
72912
+ }
72913
+ /** Adds the selected path and recovery guidance to filesystem failures. */
72914
+ function contextReadError(options, path, error) {
72915
+ return new NotAllowed(spaceTrim(`
72916
+ Cannot read ${options.contextLabel.toLowerCase()} file \`${path}\`: ${error.message}
72917
+ Check file permissions and the path in \`${options.optionName}\`.
72918
+ `));
72919
+ }
72920
+
72921
+ /**
72922
+ * Resolves project AGENTS.md on omission; explicit text, files and intentional empty values replace it.
72923
+ */
72924
+ async function resolveCoderContext(contextReference, currentWorkingDirectory) {
72925
+ if (contextReference === undefined) {
72926
+ const contextPath = path.join(currentWorkingDirectory, AGENTS_FILE_PATH);
72927
+ try {
72928
+ return await promises.readFile(contextPath, 'utf-8');
72929
+ }
72930
+ catch (error) {
72931
+ if (error.code === 'ENOENT') {
72932
+ // A broken symlink is an existing, unreadable default, not an absent optional file.
72933
+ const isMissing = await promises.lstat(contextPath).then(() => false, (failure) => failure.code === 'ENOENT');
72934
+ if (isMissing) {
72935
+ console.warn(`No additional context: \`${contextPath}\` is missing.`);
72936
+ return undefined;
72937
+ }
72938
+ }
72939
+ throw new NotAllowed(spaceTrim(`
72940
+ Cannot read default context file \`${contextPath}\`: ${error instanceof Error ? error.message : String(error)}
72941
+ Check its permissions, or replace it with \`--context\` (an empty value disables additional context).
72942
+ `));
72943
+ }
72944
+ }
72945
+ return resolveInlineOrFileText({
72946
+ textReference: contextReference,
72947
+ currentWorkingDirectory,
72948
+ contextLabel: 'Coding context',
72949
+ optionName: '--context',
72950
+ isMissingFileAnError: true,
72951
+ });
72201
72952
  }
72202
72953
 
72203
72954
  /**
@@ -72276,11 +73027,7 @@
72276
73027
  .toLowerCase()
72277
73028
  .replace(/[^a-z0-9._-]+/gu, '-')
72278
73029
  .replace(/^[._-]+|[._-]+$/gu, '');
72279
- const sessionName = [
72280
- safeAgentName || 'agent',
72281
- Date.now().toString(36),
72282
- $randomToken(4),
72283
- ].join('-');
73030
+ const sessionName = [safeAgentName || 'agent', Date.now().toString(36), $randomToken(4)].join('-');
72284
73031
  return resolvePromptbookTemporaryPath(options.currentWorkingDirectory, AGENT_CHAT_TEMPORARY_DIRECTORY_NAME, AGENT_CHAT_SESSIONS_DIRECTORY_NAME, sessionName);
72285
73032
  }
72286
73033
  /**
@@ -72335,7 +73082,7 @@
72335
73082
  return {
72336
73083
  dryRun: false,
72337
73084
  context: undefined,
72338
- testCommand: undefined,
73085
+ checkCommand: undefined,
72339
73086
  preserveLogs: false,
72340
73087
  noUi: options.noUi,
72341
73088
  thinkingLevel: options.thinkingLevel,
@@ -72401,12 +73148,7 @@
72401
73148
  */
72402
73149
  async function runAgentChat(options) {
72403
73150
  const currentWorkingDirectory = options.currentWorkingDirectory || process.cwd();
72404
- const context = await resolveInlineOrFileText({
72405
- textReference: options.context,
72406
- currentWorkingDirectory,
72407
- contextLabel: 'Agent context',
72408
- optionName: '--context',
72409
- });
73151
+ const context = await resolveCoderContext(options.context, currentWorkingDirectory);
72410
73152
  const workspacePath = createAgentChatWorkspacePath({
72411
73153
  currentWorkingDirectory,
72412
73154
  agentPath: options.agentPath,
@@ -72469,12 +73211,7 @@
72469
73211
  throw new NotAllowed('Pass a non-empty user message in `--message`.');
72470
73212
  }
72471
73213
  const currentWorkingDirectory = options.currentWorkingDirectory || process.cwd();
72472
- const context = await resolveInlineOrFileText({
72473
- textReference: options.context,
72474
- currentWorkingDirectory,
72475
- contextLabel: 'Agent context',
72476
- optionName: '--context',
72477
- });
73214
+ const context = await resolveCoderContext(options.context, currentWorkingDirectory);
72478
73215
  const result = await executeAgentChatTurn({
72479
73216
  ...options,
72480
73217
  currentWorkingDirectory,
@@ -72560,13 +73297,13 @@
72560
73297
  * Watches the queued message directory and answers messages one by one.
72561
73298
  */
72562
73299
  async function runAgentMessages(options, controls = {}) {
72563
- var _a, _b;
73300
+ var _a, _b, _c;
72564
73301
  validateAgentRunOptions(options);
72565
73302
  validateAgentWatchOptions('ptbk agent-folder run-agent', options);
72566
- const projectPath = process.cwd();
73303
+ const projectPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
72567
73304
  let autoPullTimestamp = options.autoPull ? Date.now() : undefined;
72568
73305
  const shouldContinue = controls.shouldContinue || (() => just(true));
72569
- const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : AGENT_QUEUE_POLL_INTERVAL_MS;
73306
+ const queuePollIntervalMs = (_b = controls.queuePollIntervalMs) !== null && _b !== void 0 ? _b : AGENT_QUEUE_POLL_INTERVAL_MS;
72570
73307
  let isWatchSessionInitialized = false;
72571
73308
  let uiHandle;
72572
73309
  const messageFailureTracker = new AgentMessageFailureTracker({
@@ -72583,7 +73320,7 @@
72583
73320
  }
72584
73321
  }
72585
73322
  const result = await tickAgentMessages(options, { isQuietWhenIdle: true, uiHandle });
72586
- autoPullTimestamp = (_b = result.autoPullTimestamp) !== null && _b !== void 0 ? _b : autoPullTimestamp;
73323
+ autoPullTimestamp = (_c = result.autoPullTimestamp) !== null && _c !== void 0 ? _c : autoPullTimestamp;
72587
73324
  if (result.isMessageProcessed) {
72588
73325
  messageFailureTracker.clearMessageFailure(projectPath, result.queuedMessage);
72589
73326
  continue;
@@ -78436,8 +79173,8 @@
78436
79173
  /**
78437
79174
  * Builds a display label using the prompt line number for easier navigation.
78438
79175
  */
78439
- function buildPromptLabelForDisplay(file, section) {
78440
- return `${path.relative(process.cwd(), file.path).replace(/\\/g, '/')}#${section.startLine + 1}`;
79176
+ function buildPromptLabelForDisplay(file, section, projectPath = process.cwd()) {
79177
+ return `${path.relative(projectPath, file.path).replace(/\\/g, '/')}#${section.startLine + 1}`;
78441
79178
  }
78442
79179
 
78443
79180
  /**
@@ -78452,11 +79189,11 @@
78452
79189
  /**
78453
79190
  * Prints the list of prompts that still need to be written.
78454
79191
  */
78455
- function printPromptsToBeWritten(files, priorityFilter = {}) {
79192
+ function printPromptsToBeWritten(files, priorityFilter = {}, projectPath) {
78456
79193
  const promptsToWrite = listPromptsToBeWritten(files, priorityFilter);
78457
79194
  let i = 0;
78458
79195
  for (const { file, section } of promptsToWrite) {
78459
- const label = buildPromptLabelForDisplay(file, section);
79196
+ const label = buildPromptLabelForDisplay(file, section, projectPath);
78460
79197
  const summary = buildPromptSummary(file, section);
78461
79198
  console.info(` ${++i}) ${label}: ${summary}`);
78462
79199
  }
@@ -78526,7 +79263,7 @@
78526
79263
  }
78527
79264
  else {
78528
79265
  console.info(colors__default["default"].yellow(`Found ${unwrittenPrompts.length} prompt(s) that need to be written:`));
78529
- printPromptsToBeWritten(promptFiles, priorityFilter);
79266
+ printPromptsToBeWritten(promptFiles, priorityFilter, options.projectPath);
78530
79267
  }
78531
79268
  return unwrittenPrompts.length;
78532
79269
  }
@@ -79370,9 +80107,9 @@
79370
80107
  /**
79371
80108
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
79372
80109
  */
79373
- function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
80110
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity, projectPath) {
79374
80111
  return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
79375
- label: buildPromptLabelForDisplay(file, section),
80112
+ label: buildPromptLabelForDisplay(file, section, projectPath),
79376
80113
  summary: buildPromptSummary(file, section),
79377
80114
  priority: section.priority,
79378
80115
  }));
@@ -79427,7 +80164,7 @@
79427
80164
  const promptFiles = await loadPromptFiles(path.join((_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd(), 'prompts'), {
79428
80165
  isMissingDirectoryAllowed: true,
79429
80166
  });
79430
- const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity);
80167
+ const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity, options.projectPath);
79431
80168
  printUpcomingTasks(upcomingTasks);
79432
80169
  return upcomingTasks.length;
79433
80170
  }
@@ -79807,6 +80544,24 @@
79807
80544
  createPlanningTerminal: createPlanningTerminal
79808
80545
  });
79809
80546
 
80547
+ /**
80548
+ * Resolves single-agent execution/authoring defaults without compiling Books or creating artifacts.
80549
+ * Execution previews use the same identity and routing. Utilities and all-agent discovery resolve
80550
+ * only the project directory and must not call this service to manufacture a primary-agent filter.
80551
+ */
80552
+ async function resolveCoderProjectContext(options) {
80553
+ const agentBook = await resolveCoderAgentBook(options.agent, options.projectPath, {
80554
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
80555
+ });
80556
+ const context = await resolveCoderContext(options.context, options.projectPath);
80557
+ return { projectPath: options.projectPath, agentBook, context };
80558
+ }
80559
+
80560
+ var resolveCoderProjectContext$1 = /*#__PURE__*/Object.freeze({
80561
+ __proto__: null,
80562
+ resolveCoderProjectContext: resolveCoderProjectContext
80563
+ });
80564
+
79810
80565
  /** Maximum question length accepted by both command and planning tools. */
79811
80566
  const MAX_MESSAGE_LENGTH = 32000;
79812
80567
  /** Maximum context length accepted by both command and planning tools. */
@@ -80025,6 +80780,19 @@
80025
80780
  }
80026
80781
  // Note: [💞] Runtime contract types accompany their implementation.
80027
80782
 
80783
+ /**
80784
+ * Rebinds an absolute reference inside a project/repository to its worktree copy. Relative references
80785
+ * already resolve against the execution project; explicit references outside that checkout remain explicit.
80786
+ */
80787
+ function mapProjectReferenceToWorktree(reference, originalProjectPath, worktreeProjectPath) {
80788
+ if (reference === undefined || !path.isAbsolute(reference.trim()))
80789
+ return reference;
80790
+ const relativePath = path.relative(originalProjectPath, reference.trim());
80791
+ if (path.isAbsolute(relativePath) || relativePath === '..' || /^\.\.[/\\]/u.test(relativePath))
80792
+ return reference;
80793
+ return path.join(worktreeProjectPath, relativePath);
80794
+ }
80795
+
80028
80796
  /** Limits apply before JSON parsing, even when a client bypasses the generated command. */
80029
80797
  const MAX_REQUEST_BYTES = 131072;
80030
80798
  /** Each invocation receives an independent 256-bit bearer credential. */
@@ -80211,7 +80979,7 @@
80211
80979
  * Adds a scoped consultation transport around an existing adapter. Both primary and advisers keep exactly
80212
80980
  * that adapter's permissions and authentication. Only the enclosing Coder round owns tests, queues and Git operations.
80213
80981
  */
80214
- function createCoderTeamPromptRunner(runner, agentBookReference) {
80982
+ function createCoderTeamPromptRunner(runner, agentBookReference, originalProjectPath, originalRepositoryRoot = originalProjectPath) {
80215
80983
  var _a;
80216
80984
  return {
80217
80985
  name: runner.name,
@@ -80227,8 +80995,11 @@
80227
80995
  let runtime;
80228
80996
  try {
80229
80997
  // Resolve against this invocation's actual checkout, including an isolated worktree.
80230
- const agent = await resolveCoderAgent(agentBookReference, options.projectPath, {
80231
- defaultRole: 'developer',
80998
+ const reference = originalProjectPath
80999
+ ? mapProjectReferenceToWorktree(agentBookReference, originalRepositoryRoot, path.resolve(options.projectPath, path.relative(originalProjectPath, originalRepositoryRoot)))
81000
+ : agentBookReference;
81001
+ const agent = await resolveCoderAgent(reference, options.projectPath, {
81002
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
80232
81003
  isInitializationAllowed: false,
80233
81004
  signal: controller.signal,
80234
81005
  });
@@ -81109,11 +81880,12 @@
81109
81880
  * @private internal utility of `coder plan`
81110
81881
  */
81111
81882
  async function runPlanningSession(options, io, harness = runPlanningHarness) {
81112
- var _a, _b;
81883
+ var _a, _b, _c;
81113
81884
  const projectPath = await promises.realpath(options.projectPath);
81114
81885
  assertPlanningHarnessSupported(options.agentName);
81886
+ const projectContext = (_a = options.projectContext) !== null && _a !== void 0 ? _a : (await resolveCoderProjectContext({ ...options, projectPath }));
81115
81887
  const agent = await resolveCoderAgent(options.agent, projectPath, {
81116
- defaultRole: 'planner',
81888
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
81117
81889
  isInitializationAllowed: false,
81118
81890
  signal: io.signal,
81119
81891
  });
@@ -81121,9 +81893,9 @@
81121
81893
  throw new NotFoundError(spaceTrim('Planning requires `prompts/`. Run `ptbk coder init` first.'));
81122
81894
  }
81123
81895
  if (!agent)
81124
- throw new NotFoundError(spaceTrim('Planner Book is missing. Run `ptbk coder init`.'));
81896
+ throw new NotFoundError(spaceTrim('Primary Book is missing. Run `ptbk coder init`.'));
81125
81897
  resolvePlanningPath(projectPath, 'prompts');
81126
- const templateOption = (_a = options.template) !== null && _a !== void 0 ? _a : (fs.existsSync(path.join(projectPath, COMMON_PROMPT_TEMPLATE_FILE_PATH))
81898
+ const templateOption = (_b = options.template) !== null && _b !== void 0 ? _b : (fs.existsSync(path.join(projectPath, COMMON_PROMPT_TEMPLATE_FILE_PATH))
81127
81899
  ? COMMON_PROMPT_TEMPLATE_FILE_PATH
81128
81900
  : undefined);
81129
81901
  const template = await resolveCoderPromptTemplate({ projectPath, templateOption });
@@ -81167,7 +81939,10 @@
81167
81939
  },
81168
81940
  { role: 'context', content: await readPlanningContext(projectPath, { kind: 'list', path: '.' }) },
81169
81941
  ];
81170
- for (const path$1 of ['README.md', 'AGENTS.md']) {
81942
+ if (projectContext.context !== undefined) {
81943
+ history.push({ role: 'context', content: projectContext.context });
81944
+ }
81945
+ for (const path$1 of ['README.md']) {
81171
81946
  if (fs.existsSync(path.join(projectPath, path$1))) {
81172
81947
  history.push({
81173
81948
  role: 'context',
@@ -81184,7 +81959,7 @@
81184
81959
  let pending = [];
81185
81960
  io.write(`${agent.agentName}: Discuss features and review PRD proposals. /save applies the preview; /draft saves unresolved drafts; /discard drops it; /exit ends. Ctrl+C cancels.\n`);
81186
81961
  while (!io.signal.aborted) {
81187
- const message = (_b = (await io.readMessage())) === null || _b === void 0 ? void 0 : _b.trim();
81962
+ const message = (_c = (await io.readMessage())) === null || _c === void 0 ? void 0 : _c.trim();
81188
81963
  if (message === undefined || ['/exit', 'exit', 'quit'].includes(message))
81189
81964
  break;
81190
81965
  if (!message)
@@ -81321,7 +82096,7 @@
81321
82096
  /**
81322
82097
  * CLI usage text for this script.
81323
82098
  */
81324
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--agent <agent-book-path>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
82099
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--agent <agent-book-path>] [--context <context-or-file>] [--check <check-command...>] [--check-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
81325
82100
  /**
81326
82101
  * Top-level flags supported by this command.
81327
82102
  */
@@ -81331,8 +82106,8 @@
81331
82106
  '--model',
81332
82107
  '--agent',
81333
82108
  '--context',
81334
- '--test',
81335
- '--test-before',
82109
+ '--check',
82110
+ '--check-before',
81336
82111
  '--preserve-logs',
81337
82112
  '--isolate',
81338
82113
  '--no-ui',
@@ -81358,6 +82133,15 @@
81358
82133
  * Parses CLI arguments into runner options.
81359
82134
  */
81360
82135
  function parseRunOptions(args) {
82136
+ args = args.flatMap((argument) => {
82137
+ const match = argument.match(/^(--check(?:-before)?)=([\s\S]*)$/u);
82138
+ return match ? [match[1], match[2]] : [argument];
82139
+ });
82140
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
82141
+ if (args.some((argument) => argument === legacyFlag || argument.startsWith(`${legacyFlag}=`))) {
82142
+ exitWithUsageError(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
82143
+ }
82144
+ }
81361
82145
  let agentName = undefined;
81362
82146
  const dryRun = args.includes('--dry-run');
81363
82147
  const harnessValue = readOptionValue(args, '--harness');
@@ -81373,10 +82157,10 @@
81373
82157
  exitWithUsageError('Pass a non-empty Book path after `--agent`, or omit it to use `agents/developer.book`.');
81374
82158
  }
81375
82159
  const context = readOptionValue(args, '--context');
81376
- const hasTestCommandFlag = args.includes('--test');
81377
- const testCommand = readVariadicOptionValue(args, '--test');
81378
- const hasTestBeforeFlag = args.includes('--test-before');
81379
- const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
82160
+ const hasCheckCommandFlag = args.includes('--check');
82161
+ const checkCommand = readVariadicOptionValue(args, '--check');
82162
+ const hasCheckBeforeFlag = args.includes('--check-before');
82163
+ const checkBefore = parseCheckBeforeOption(readOptionValue(args, '--check-before'), hasCheckBeforeFlag);
81380
82164
  const preserveLogs = args.includes('--preserve-logs');
81381
82165
  const isIsolated = args.includes('--isolate');
81382
82166
  const noUi = args.includes('--no-ui');
@@ -81414,8 +82198,8 @@
81414
82198
  const waitBetweenPrompts = parseWaitOption(args, '--wait-between-prompts', 0);
81415
82199
  const waitAfterError = parseWaitOption(args, '--wait-after-error', DEFAULT_WAIT_AFTER_ERROR_MS);
81416
82200
  let thinkingLevel;
81417
- if (hasTestCommandFlag && testCommand === undefined) {
81418
- exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
82201
+ if (hasCheckCommandFlag && checkCommand === undefined) {
82202
+ exitWithUsageError('Missing value for --check. Use a shell command such as `npm run check` and quote it when it contains top-level CLI flags.');
81419
82203
  }
81420
82204
  if (hasThinkingLevelFlag && thinkingLevelValue === undefined) {
81421
82205
  exitWithUsageError(`Missing value for --thinking-level. Use one of: ${THINKING_LEVEL_VALUES.join(', ')}.`);
@@ -81450,8 +82234,8 @@
81450
82234
  model,
81451
82235
  agent,
81452
82236
  context,
81453
- testCommand,
81454
- testBefore,
82237
+ checkCommand: resolveCoderCheckCommand(checkCommand, checkBefore),
82238
+ checkBefore,
81455
82239
  thinkingLevel,
81456
82240
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
81457
82241
  minimumPriority,
@@ -81463,15 +82247,15 @@
81463
82247
  /**
81464
82248
  * Parses and validates the optional pre-coding verification mode.
81465
82249
  */
81466
- function parseTestBeforeOption(value, hasTestBeforeFlag) {
82250
+ function parseCheckBeforeOption(value, hasCheckBeforeFlag) {
81467
82251
  if (value === undefined) {
81468
- if (hasTestBeforeFlag) {
81469
- exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
82252
+ if (hasCheckBeforeFlag) {
82253
+ exitWithUsageError(`Missing value for --check-before. Use one of: ${CHECK_BEFORE_MODE_VALUES.join(', ')}.`);
81470
82254
  }
81471
82255
  return 'no';
81472
82256
  }
81473
- if (!isTestBeforeMode(value)) {
81474
- exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
82257
+ if (!isCheckBeforeMode(value)) {
82258
+ exitWithUsageError(`Invalid value for --check-before: "${value}". Use one of: ${CHECK_BEFORE_MODE_VALUES.join(', ')}.`);
81475
82259
  }
81476
82260
  return value;
81477
82261
  }
@@ -81532,8 +82316,14 @@
81532
82316
  }
81533
82317
  valueParts.push(valuePart);
81534
82318
  }
81535
- const normalizedValue = valueParts.join(' ').trim();
81536
- return normalizedValue === '' ? undefined : normalizedValue;
82319
+ if (valueParts.length === 0)
82320
+ return undefined;
82321
+ try {
82322
+ return normalizeCheckCommandOption(valueParts);
82323
+ }
82324
+ catch (error) {
82325
+ exitWithUsageError(error instanceof Error ? error.message : String(error));
82326
+ }
81537
82327
  }
81538
82328
  /**
81539
82329
  * Parses and validates one priority boundary.
@@ -81975,18 +82765,6 @@
81975
82765
  });
81976
82766
  }
81977
82767
 
81978
- /**
81979
- * Resolves optional coding context provided inline or via a file path.
81980
- */
81981
- async function resolveCoderContext(contextReference, currentWorkingDirectory) {
81982
- return resolveInlineOrFileText({
81983
- textReference: contextReference,
81984
- currentWorkingDirectory,
81985
- contextLabel: 'Coding context',
81986
- optionName: '--context',
81987
- });
81988
- }
81989
-
81990
82768
  /**
81991
82769
  * Listens for the terminal control keys while the plain console output is used.
81992
82770
  *
@@ -83581,7 +84359,7 @@
83581
84359
  */
83582
84360
  const CODER_RUN_STEP_LABELS = {
83583
84361
  implementation: 'Implementation',
83584
- testing: 'Testing',
84362
+ checking: 'Checking',
83585
84363
  fixing: 'Fixing',
83586
84364
  };
83587
84365
  /**
@@ -83592,16 +84370,16 @@
83592
84370
  * Formats the per-step usage breakdown recorded for one prompt.
83593
84371
  *
83594
84372
  * Produces a `; `-separated summary such as
83595
- * `Implementation $8.01 6 hours; Testing 1 hour; Fixing $3.14 2 hours` where each coding step carries its
84373
+ * `Implementation $8.01 6 hours; Checking 1 hour; Fixing $3.14 2 hours` where each coding step carries its
83596
84374
  * price and duration and each verification step carries only its duration.
83597
84375
  *
83598
84376
  * @param steps - Steps which have already finished
83599
- * @param inProgressStepKind - Step which has started but has not finished yet, appended as `Testing in progress`
84377
+ * @param inProgressStepKind - Step which has started but has not finished yet, appended as `Checking in progress`
83600
84378
  */
83601
84379
  function formatCoderRunSteps(steps, inProgressStepKind) {
83602
84380
  const formattedSteps = steps.map(formatCoderRunStep);
83603
84381
  if (inProgressStepKind !== undefined) {
83604
- formattedSteps.push(`${CODER_RUN_STEP_LABELS[inProgressStepKind]} ${IN_PROGRESS_STEP_SUFFIX}`);
84382
+ formattedSteps.push(`${CODER_RUN_STEP_LABELS[decodeCoderRunStepKind(inProgressStepKind)]} ${IN_PROGRESS_STEP_SUFFIX}`);
83605
84383
  }
83606
84384
  return formattedSteps.join('; ');
83607
84385
  }
@@ -83609,7 +84387,7 @@
83609
84387
  * Formats one coder run step as `Label $price duration`, omitting the price for steps without model usage.
83610
84388
  */
83611
84389
  function formatCoderRunStep(step) {
83612
- const label = CODER_RUN_STEP_LABELS[step.kind];
84390
+ const label = CODER_RUN_STEP_LABELS[decodeCoderRunStepKind(step.kind)];
83613
84391
  const durationString = moment__default["default"].duration(step.durationMs).humanize();
83614
84392
  const priceString = step.usage === null ? undefined : formatUsagePrice(step.usage);
83615
84393
  if (priceString === undefined) {
@@ -83617,6 +84395,10 @@
83617
84395
  }
83618
84396
  return `${label} ${priceString} ${durationString}`;
83619
84397
  }
84398
+ /** Decodes the old aggregate step name at the display boundary; new execution only records checking. */
84399
+ function decodeCoderRunStepKind(kind) {
84400
+ return kind === 'testing' ? 'checking' : kind;
84401
+ }
83620
84402
 
83621
84403
  /**
83622
84404
  * Formats optional attempt metadata stored in prompt status lines.
@@ -83957,13 +84739,13 @@
83957
84739
  `- **Runner:** ${runnerSignature}${loginMethodSuffix}`,
83958
84740
  `- **Attempts:** ${options.attemptCount}`,
83959
84741
  ...buildPromptRunTraceStepsLines(outcome),
83960
- ...buildPromptRunTraceTestCommandLines(options.testCommand),
84742
+ ...buildPromptRunTraceCheckCommandLines(options.checkCommand),
83961
84743
  `- **Started:** ${options.startedDate.toISOString()}`,
83962
84744
  `- **Finished:** ${options.finishedDate.toISOString()}`,
83963
84745
  `- **Duration:** ${moment__default["default"].duration(options.finishedDate.diff(options.startedDate)).humanize()}`,
83964
84746
  ];
83965
84747
  return _spaceTrim.spaceTrim((block) => `
83966
- # Run trace of \`${buildPromptLabelForDisplay(file, section)}\`
84748
+ # Run trace of \`${buildPromptLabelForDisplay(file, section, options.projectPath)}\`
83967
84749
 
83968
84750
  ${block(detailLines.join('\n'))}
83969
84751
  `);
@@ -83996,11 +84778,11 @@
83996
84778
  /**
83997
84779
  * Renders the verification command of the round, omitted when the round ran without one.
83998
84780
  */
83999
- function buildPromptRunTraceTestCommandLines(testCommand) {
84000
- if (!testCommand) {
84781
+ function buildPromptRunTraceCheckCommandLines(checkCommand) {
84782
+ if (!checkCommand) {
84001
84783
  return [];
84002
84784
  }
84003
- return [`- **Verification command:** \`${testCommand}\``];
84785
+ return [`- **Check command:** \`${checkCommand}\``];
84004
84786
  }
84005
84787
  /**
84006
84788
  * Renders the failure details of a failed round, omitted for a successful one.
@@ -84087,9 +84869,9 @@
84087
84869
  *
84088
84870
  * @private function of runCodexPrompts
84089
84871
  */
84090
- async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, promptLabel, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, projectPath, }) {
84091
- var _a, _b;
84092
- const roundProjectPath = (_b = projectPath !== null && projectPath !== void 0 ? projectPath : (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd();
84872
+ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, promptLabel, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, projectPath, artifactsProjectPath, }) {
84873
+ var _a, _b, _c;
84874
+ const roundProjectPath = (_c = (_b = projectPath !== null && projectPath !== void 0 ? projectPath : (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : options.projectPath) !== null && _c !== void 0 ? _c : process.cwd();
84093
84875
  const commitMessage = buildCommitMessage(nextPrompt.file, nextPrompt.section);
84094
84876
  const taskPrompt = buildCodexPrompt(nextPrompt.file, nextPrompt.section);
84095
84877
  // Prepend agent system message before the task so the harness sees agent instructions first
@@ -84106,8 +84888,7 @@
84106
84888
  `)
84107
84889
  : taskPrompt;
84108
84890
  const codexPrompt = appendCoderContext(promptWithAgent, resolvedCoderContext);
84109
- // Note: Temporary scripts and runtime logs stay in the original project so they outlive an isolated worktree
84110
- const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
84891
+ const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section, artifactsProjectPath !== null && artifactsProjectPath !== void 0 ? artifactsProjectPath : roundProjectPath);
84111
84892
  // Note: Read before the first `[^]` rewrite of this round, which would overwrite the report recorded so far
84112
84893
  const previousRunnerSignatures = resolvePreviousRunnerSignatures(nextPrompt);
84113
84894
  setPromptRoundRunningState({ isRichUiEnabled, promptLabel, scriptPath, uiHandle });
@@ -84126,13 +84907,13 @@
84126
84907
  for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
84127
84908
  try {
84128
84909
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
84129
- const result = await runPromptWithTestFeedback({
84910
+ const result = await runPromptWithCheckFeedback({
84130
84911
  runner,
84131
84912
  prompt: codexPrompt,
84132
84913
  scriptPath,
84133
84914
  projectPath: roundProjectPath,
84134
84915
  promptLabel,
84135
- testCommand: options.testCommand,
84916
+ checkCommand: options.checkCommand,
84136
84917
  preserveArtifactsOnSuccess: options.preserveLogs,
84137
84918
  logPath,
84138
84919
  onAttemptStarted: (nextAttemptCount) => {
@@ -84172,7 +84953,9 @@
84172
84953
  lastError = error;
84173
84954
  // Note: A harness which is not logged in answers every retry the same way, so the user gets
84174
84955
  // the sign-in instructions right away instead of after every retry has waited its delay
84175
- if (error instanceof AuthenticationError || errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
84956
+ if (error instanceof AuthenticationError ||
84957
+ error instanceof CoderCheckSetupError ||
84958
+ errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
84176
84959
  break;
84177
84960
  }
84178
84961
  await waitAfterErrorBeforeRetry({
@@ -84414,13 +85197,15 @@
84414
85197
  * harness, model and thinking level did it and everything that harness has written while doing it.
84415
85198
  */
84416
85199
  async function recordPromptRoundTrace(options) {
85200
+ var _a, _b;
84417
85201
  const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
84418
85202
  await writePromptRunTrace({
85203
+ projectPath: (_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : runOptions.projectPath,
84419
85204
  file: nextPrompt.file,
84420
85205
  section: nextPrompt.section,
84421
85206
  ...runnerMetadata,
84422
85207
  thinkingLevel: runOptions.thinkingLevel,
84423
- testCommand: runOptions.testCommand,
85208
+ checkCommand: runOptions.checkCommand,
84424
85209
  attemptCount,
84425
85210
  startedDate: promptExecutionStartedDate,
84426
85211
  finishedDate: moment__default["default"](),
@@ -85018,12 +85803,12 @@
85018
85803
  * @private function of runCodexPrompts
85019
85804
  */
85020
85805
  async function runIsolatedPromptRound(options) {
85021
- var _a, _b, _c, _d, _e, _f;
85806
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
85022
85807
  const { nextPrompt, promptLabel, isRichUiEnabled, uiHandle, waitForRequestedPause } = options;
85023
- const projectPath = (_c = (_a = options.projectPath) !== null && _a !== void 0 ? _a : (_b = options.options.workspace) === null || _b === void 0 ? void 0 : _b.projectPath) !== null && _c !== void 0 ? _c : process.cwd();
85808
+ const projectPath = (_d = (_c = (_a = options.projectPath) !== null && _a !== void 0 ? _a : (_b = options.options.workspace) === null || _b === void 0 ? void 0 : _b.projectPath) !== null && _c !== void 0 ? _c : options.options.projectPath) !== null && _d !== void 0 ? _d : process.cwd();
85024
85809
  // Note: The original project is left untouched by the isolated round itself, so its scope covers exactly
85025
85810
  // the prompt status update and the changes the merge brings back from the worktree
85026
- const originalProjectCommitScope = await captureCoderCommitScope((_d = options.options.workspace) !== null && _d !== void 0 ? _d : projectPath);
85811
+ const originalProjectCommitScope = await captureCoderCommitScope((_e = options.options.workspace) !== null && _e !== void 0 ? _e : projectPath);
85027
85812
  const worktree = await createCoderIsolationWorktree({
85028
85813
  projectPath,
85029
85814
  repositoryRoot: originalProjectCommitScope.repositoryRoot,
@@ -85031,16 +85816,44 @@
85031
85816
  });
85032
85817
  announceIsolatedRoundStart(worktree, promptLabel, isRichUiEnabled);
85033
85818
  try {
85034
- const isolatedProjectPath = path.join(worktree.worktreePath, path.relative((_e = originalProjectCommitScope.repositoryRoot) !== null && _e !== void 0 ? _e : projectPath, projectPath));
85819
+ const isolatedProjectPath = path.join(worktree.worktreePath, path.relative((_f = originalProjectCommitScope.repositoryRoot) !== null && _f !== void 0 ? _f : projectPath, projectPath));
85035
85820
  // This newly created working tree has its own metadata. Keep the project's repository-relative
85036
85821
  // location so its harness and verification use the same project as the original invocation.
85037
85822
  const isolatedWorkspace = await $resolveWorkspaceRepository(isolatedProjectPath);
85823
+ const isolatedOptions = {
85824
+ ...options.options,
85825
+ workspace: isolatedWorkspace,
85826
+ projectPath: isolatedWorkspace.projectPath,
85827
+ agent: mapProjectReferenceToWorktree(options.options.agent, (_g = originalProjectCommitScope.repositoryRoot) !== null && _g !== void 0 ? _g : projectPath, worktree.worktreePath),
85828
+ context: mapProjectReferenceToWorktree(options.options.context, (_h = originalProjectCommitScope.repositoryRoot) !== null && _h !== void 0 ? _h : projectPath, worktree.worktreePath),
85829
+ autoPush: false,
85830
+ };
85831
+ const projectContext = await resolveCoderProjectContext(isolatedOptions);
85832
+ const agent = await resolveCoderAgent(isolatedOptions.agent, isolatedWorkspace.projectPath, {
85833
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
85834
+ isInitializationAllowed: false,
85835
+ });
85836
+ // Status updates, traces and commits belong to the same checkout as the harness and checks.
85837
+ // Copy the parsed data too, so a failed isolated round does not mutate the original queue in memory.
85838
+ const isolatedFile = {
85839
+ ...nextPrompt.file,
85840
+ path: path.join(isolatedWorkspace.projectPath, path.relative(projectPath, nextPrompt.file.path)),
85841
+ lines: [...nextPrompt.file.lines],
85842
+ sections: nextPrompt.file.sections.map((section) => ({ ...section })),
85843
+ };
85038
85844
  await runPromptRound({
85039
85845
  ...options,
85846
+ nextPrompt: { file: isolatedFile, section: isolatedFile.sections[nextPrompt.section.index] },
85040
85847
  projectPath: isolatedWorkspace.projectPath,
85848
+ // Temporary scripts and raw logs deliberately outlive the worktree; PRDs and traces are merged.
85849
+ artifactsProjectPath: projectPath,
85850
+ resolvedCoderContext: projectContext.context,
85851
+ resolvedAgentSystemMessage: agent === null || agent === void 0 ? void 0 : agent.systemMessage,
85041
85852
  // Note: The isolated commit must never reach the remote, the merged commit on the original branch is pushed instead
85042
- options: { ...options.options, workspace: isolatedWorkspace, autoPush: false },
85853
+ options: { ...isolatedOptions, projectContext },
85043
85854
  });
85855
+ // Keep the display/commit identity in sync without writing to the original checkout before merging.
85856
+ Object.assign(nextPrompt.section, isolatedFile.sections[nextPrompt.section.index]);
85044
85857
  }
85045
85858
  catch (error) {
85046
85859
  // Note: The worktree keeps whatever the failed round produced, which would be lost by deleting it here
@@ -85061,7 +85874,7 @@
85061
85874
  await commitChanges(buildCommitMessage(nextPrompt.file, nextPrompt.section), {
85062
85875
  autoPush: options.options.autoPush,
85063
85876
  relevantPaths: await resolveCoderCommitScopePaths(originalProjectCommitScope),
85064
- projectPath: (_f = originalProjectCommitScope.repositoryRoot) !== null && _f !== void 0 ? _f : projectPath,
85877
+ projectPath: (_j = originalProjectCommitScope.repositoryRoot) !== null && _j !== void 0 ? _j : projectPath,
85065
85878
  });
85066
85879
  await removeCoderIsolationWorktree(worktree);
85067
85880
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merged \`${worktree.taskName}\` into \`${worktree.baseBranchName}\``);
@@ -85070,9 +85883,23 @@
85070
85883
  * Records a failed isolation merge in the original project and keeps the worktree for a manual merge.
85071
85884
  */
85072
85885
  async function recordIsolationMergeFailure(options, worktree, failureDetails) {
85073
- var _a, _b;
85886
+ var _a, _b, _c, _d;
85074
85887
  const { nextPrompt, runnerMetadata, isRichUiEnabled, uiHandle } = options;
85075
85888
  const mergeFailureError = buildCoderIsolationMergeFailureError(worktree, failureDetails);
85889
+ const repositoryRoot = (_b = (_a = options.options.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : worktree.projectPath;
85890
+ const tracePath = buildPromptRunTracePath(nextPrompt.file, nextPrompt.section);
85891
+ const isolatedTracePath = path.join(worktree.worktreePath, path.relative(repositoryRoot, tracePath));
85892
+ // The failed merge did not bring back the worktree's trace. Preserve it beside the failure report.
85893
+ await promises.mkdir(path.dirname(tracePath), { recursive: true });
85894
+ let isTraceCopied = false;
85895
+ try {
85896
+ await promises.copyFile(isolatedTracePath, tracePath);
85897
+ isTraceCopied = true;
85898
+ }
85899
+ catch (error) {
85900
+ if (error.code !== 'ENOENT')
85901
+ throw error;
85902
+ }
85076
85903
  markPromptIsolationMergeFailed(nextPrompt.file, nextPrompt.section, worktree);
85077
85904
  await writePromptFile(nextPrompt.file);
85078
85905
  const errorLogPath = await writePromptErrorLog({
@@ -85084,13 +85911,11 @@
85084
85911
  });
85085
85912
  await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
85086
85913
  autoPush: options.options.autoPush,
85087
- projectPath: (_b = (_a = options.options.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : worktree.projectPath,
85088
- // Note: The round itself has already succeeded, so it has left its run trace in the original project.
85089
- // It belongs to this commit, which is the only one this task still gets.
85914
+ projectPath: (_d = (_c = options.options.workspace) === null || _c === void 0 ? void 0 : _c.repositoryRoot) !== null && _d !== void 0 ? _d : worktree.projectPath,
85090
85915
  relevantPaths: [
85091
85916
  nextPrompt.file.path,
85092
85917
  errorLogPath,
85093
- buildPromptRunTracePath(nextPrompt.file, nextPrompt.section),
85918
+ ...(isTraceCopied ? [tracePath] : []),
85094
85919
  ].map((path) => { var _a, _b; return toProjectRelativeGitPath((_b = (_a = options.options.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : worktree.projectPath, path); }),
85095
85920
  });
85096
85921
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
@@ -85131,8 +85956,8 @@
85131
85956
  /**
85132
85957
  * Builds a label that identifies the prompt section by index.
85133
85958
  */
85134
- function buildPromptLabel(file, section) {
85135
- return `${path.relative(process.cwd(), file.path).replace(/\\/g, '/')}#${section.index + 1}`;
85959
+ function buildPromptLabel(file, section, projectPath = process.cwd()) {
85960
+ return `${path.relative(projectPath, file.path).replace(/\\/g, '/')}#${section.index + 1}`;
85136
85961
  }
85137
85962
 
85138
85963
  /**
@@ -85360,18 +86185,20 @@
85360
86185
  }
85361
86186
 
85362
86187
  /**
85363
- * Creates the one queue prompt used to repair a pre-existing test failure.
86188
+ * Creates the one queue prompt used to repair a pre-existing project check failure.
85364
86189
  */
85365
- async function createTestBeforeRepairPrompt(options) {
86190
+ async function createCheckBeforeRepairPrompt(options) {
85366
86191
  const description = spaceTrim((block) => `
85367
- Fix the existing test failures before implementing any queued coding tasks.
86192
+ Fix the existing check failures before implementing any queued coding tasks.
86193
+
86194
+ The check command \`${options.checkCommand}\` failed before coding started. Leave the project ready for the remaining coding prompts.
85368
86195
 
85369
- 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.
86196
+ ${block(CHECK_REPAIR_INSTRUCTIONS)}
85370
86197
 
85371
- ## Verification output
86198
+ ## Check output
85372
86199
 
85373
86200
  \`\`\`
85374
- ${block(limitTestOutput(options.testOutput))}
86201
+ ${block(limitCheckOutput(options.checkOutput))}
85375
86202
  \`\`\`
85376
86203
  `);
85377
86204
  const createdPrompt = await addCoderPrompt({
@@ -85384,50 +86211,56 @@
85384
86211
  const section = promptFile.sections[0];
85385
86212
  if (!section) {
85386
86213
  throw new UnexpectedError(spaceTrim(`
85387
- The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
86214
+ The pre-coding check repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
85388
86215
  `));
85389
86216
  }
85390
86217
  return { file: promptFile, section };
85391
86218
  }
85392
86219
 
85393
86220
  /**
85394
- * Runs the configured verification command before the first coding prompt.
86221
+ * Runs the configured check command before the first coding prompt.
85395
86222
  */
85396
- async function runTestBefore(options) {
85397
- var _a, _b;
85398
- const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
85399
- const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
85400
- await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
85401
- checkpointLabel: 'running initial tests before the agent coding starts',
86223
+ async function runCheckBefore(options) {
86224
+ var _a, _b, _c, _d, _e, _f;
86225
+ (_a = options.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
86226
+ const runPromptCheckCommandExecutor = (_b = options.runPromptCheckCommandExecutor) !== null && _b !== void 0 ? _b : runPromptCheckCommand;
86227
+ const initialCheckStatusMessage = `Running initial checks before the agent coding starts: ${options.checkCommand}`;
86228
+ await ((_c = options.waitForPauseCheckpoint) === null || _c === void 0 ? void 0 : _c.call(options, {
86229
+ checkpointLabel: 'running initial checks before the agent coding starts',
85402
86230
  phase: 'verifying',
85403
- statusMessage: initialTestStatusMessage,
86231
+ statusMessage: initialCheckStatusMessage,
85404
86232
  }));
85405
- console.info(colors__default["default"].gray(initialTestStatusMessage));
86233
+ (_d = options.signal) === null || _d === void 0 ? void 0 : _d.throwIfAborted();
86234
+ console.info(colors__default["default"].gray(initialCheckStatusMessage));
85406
86235
  try {
85407
- const testOutput = await runPromptTestCommandExecutor({
85408
- command: options.testCommand,
86236
+ const checkOutput = await runPromptCheckCommandExecutor({
86237
+ command: options.checkCommand,
85409
86238
  projectPath: options.projectPath,
85410
86239
  scriptPath: buildTemporaryPromptScriptPath({
85411
86240
  projectPath: options.projectPath,
85412
86241
  scriptDirectoryName: 'coder-prompts',
85413
- sourceFileName: 'test-before',
86242
+ sourceFileName: 'check-before',
85414
86243
  }),
86244
+ ...(options.signal ? { signal: options.signal } : {}),
85415
86245
  });
85416
- console.info(colors__default["default"].green('Pre-coding tests passed.'));
85417
- return { isPassed: true, testOutput };
86246
+ (_e = options.signal) === null || _e === void 0 ? void 0 : _e.throwIfAborted();
86247
+ console.info(colors__default["default"].green('Pre-coding checks passed.'));
86248
+ return { isPassed: true, checkOutput };
85418
86249
  }
85419
86250
  catch (error) {
85420
- const testOutput = formatUnknownErrorMessage(error);
85421
- console.error(colors__default["default"].red('Pre-coding tests failed.'));
85422
- console.error(testOutput);
85423
- return { isPassed: false, testOutput };
86251
+ if (error instanceof CoderCheckSetupError || ((_f = options.signal) === null || _f === void 0 ? void 0 : _f.aborted))
86252
+ throw error;
86253
+ const checkOutput = formatUnknownErrorMessage(error);
86254
+ console.error(colors__default["default"].red('Pre-coding checks failed.'));
86255
+ console.error(checkOutput);
86256
+ return { isPassed: false, checkOutput };
85424
86257
  }
85425
86258
  }
85426
86259
 
85427
86260
  /**
85428
- * Commit message for files changed by a successful or failed pre-coding test in repair mode.
86261
+ * Commit message for files changed by a successful or failed pre-coding check in repair mode.
85429
86262
  */
85430
- const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-coding tests';
86263
+ const PRE_CODING_CHECK_CHANGES_COMMIT_MESSAGE = 'chore: Apply changes made by pre-coding checks';
85431
86264
  /**
85432
86265
  * Main entry point for running prompts with the selected agent.
85433
86266
  *
@@ -85436,10 +86269,15 @@
85436
86269
  * @public exported from `@promptbook/cli`
85437
86270
  */
85438
86271
  async function runCodexPrompts(providedOptions) {
85439
- var _a, _b, _c, _d;
85440
- const options = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
85441
- validateCoderRunOptions(options);
85442
- const projectPath = (_b = (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd();
86272
+ var _a, _b, _c, _d, _e, _f, _g, _h;
86273
+ const normalizedOptions = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
86274
+ validateCoderRunOptions(normalizedOptions);
86275
+ // CLI-preflighted workspaces are already canonical. For direct runs without an explicit project path, preserve
86276
+ // `process.cwd()` as written because macOS may expose the same temporary directory through `/var` and `/private/var`.
86277
+ const projectPath = (_b = (_a = normalizedOptions.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : ((providedOptions === null || providedOptions === void 0 ? void 0 : providedOptions.projectPath) === undefined
86278
+ ? normalizedOptions.projectPath
86279
+ : await resolveProjectDirectory(normalizedOptions.projectPath));
86280
+ const options = { ...normalizedOptions, projectPath };
85443
86281
  resetCoderRunControls();
85444
86282
  const runStartDate = moment__default["default"]();
85445
86283
  const { isRichUiEnabled, progressDisplay, uiHandle } = createRunDisplays(options, runStartDate);
@@ -85457,20 +86295,25 @@
85457
86295
  startPauseListenerIfNeeded(isRichUiEnabled);
85458
86296
  let subscriptionUsageRefresh;
85459
86297
  try {
85460
- const resolvedCoderContext = await resolveCoderContext(options.context, projectPath);
86298
+ const projectContext = (_d = options.projectContext) !== null && _d !== void 0 ? _d : (await resolveCoderProjectContext({
86299
+ ...options,
86300
+ projectPath,
86301
+ }));
86302
+ if (await runDryRunIfRequested(options, (_e = projectContext.agentBook) === null || _e === void 0 ? void 0 : _e.agentReferences)) {
86303
+ return;
86304
+ }
86305
+ await assertProjectCheckIsConfigured(options.checkCommand, projectPath);
86306
+ const resolvedCoderContext = projectContext.context;
85461
86307
  const resolvedCoderAgent = await resolveCoderAgent(options.agent, projectPath, {
85462
- defaultRole: 'developer',
86308
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
85463
86309
  isInitializationAllowed: !options.dryRun,
85464
86310
  });
85465
86311
  const resolvedAgentSystemMessage = resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.systemMessage;
85466
- if (await runDryRunIfRequested(options, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentReferences)) {
85467
- return;
85468
- }
85469
86312
  if (!options.noCommit && resolvedCoderAgent) {
85470
86313
  await commitInitializedAgentBooks(projectPath, resolvedCoderAgent.createdAgentBookPaths, options.workspace);
85471
86314
  }
85472
86315
  const { runner: harnessRunner, actualRunnerModel, runnerMetadata: harnessRunnerMetadata, } = resolvePromptRunner(options);
85473
- const runner = createCoderTeamPromptRunner(harnessRunner, options.agent);
86316
+ const runner = createCoderTeamPromptRunner(harnessRunner, options.agent, projectPath, (_f = options.workspace) === null || _f === void 0 ? void 0 : _f.repositoryRoot);
85474
86317
  // Note: The harness only knows itself, so the Book agent it runs as is joined here - this is the single
85475
86318
  // place where the whole run report of prompt status lines and run traces is put together
85476
86319
  const runnerMetadata = {
@@ -85501,7 +86344,7 @@
85501
86344
  let previousRoundStartTime;
85502
86345
  let previousRoundEndTime;
85503
86346
  let completedRunCount = 0;
85504
- let hasRunTestBefore = false;
86347
+ let hasRunCheckBefore = false;
85505
86348
  // Note: Only the very first round resumes the interrupted prompt, every later round starts from a clean tree
85506
86349
  let isContinuingInterruptedPrompt = options.gitChanges === 'continue';
85507
86350
  while (just(true)) {
@@ -85516,11 +86359,11 @@
85516
86359
  options,
85517
86360
  isRichUiEnabled,
85518
86361
  });
85519
- if (!hasRunTestBefore && options.testBefore !== 'no') {
86362
+ if (!hasRunCheckBefore && options.checkBefore !== 'no') {
85520
86363
  await waitForRequestedPause({
85521
- checkpointLabel: 'loading prompts before running initial tests',
86364
+ checkpointLabel: 'loading prompts before running initial checks',
85522
86365
  phase: 'loading',
85523
- statusMessage: 'Loading prompts before running initial tests...',
86366
+ statusMessage: 'Loading prompts before running initial checks...',
85524
86367
  });
85525
86368
  await loadPromptQueueSnapshot({
85526
86369
  options,
@@ -85531,8 +86374,8 @@
85531
86374
  isContinuingInterruptedPrompt,
85532
86375
  });
85533
86376
  }
85534
- if (!hasRunTestBefore) {
85535
- hasWaitedForStart = await runTestBeforeIfNeeded({
86377
+ if (!hasRunCheckBefore) {
86378
+ hasWaitedForStart = await runCheckBeforeIfNeeded({
85536
86379
  options,
85537
86380
  runner,
85538
86381
  runnerMetadata,
@@ -85545,7 +86388,7 @@
85545
86388
  hasWaitedForStart,
85546
86389
  isContinuingInterruptedPrompt,
85547
86390
  });
85548
- hasRunTestBefore = true;
86391
+ hasRunCheckBefore = true;
85549
86392
  }
85550
86393
  await waitForRequestedPause({
85551
86394
  checkpointLabel: 'loading prompts',
@@ -85561,6 +86404,7 @@
85561
86404
  isContinuingInterruptedPrompt,
85562
86405
  });
85563
86406
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
86407
+ projectPath,
85564
86408
  hasShownUpcomingTasks,
85565
86409
  promptFiles: promptQueueSnapshot.promptFiles,
85566
86410
  stats: promptQueueSnapshot.stats,
@@ -85591,7 +86435,7 @@
85591
86435
  return;
85592
86436
  }
85593
86437
  const nextPrompt = promptQueueSnapshot.nextPrompt;
85594
- const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
86438
+ const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section, projectPath);
85595
86439
  // Wait between prompt rounds (skipped for the first round)
85596
86440
  if (previousRoundStartTime !== undefined && previousRoundEndTime !== undefined) {
85597
86441
  await waitBetweenPromptRoundsIfNeeded({
@@ -85626,7 +86470,7 @@
85626
86470
  phase: 'loading',
85627
86471
  statusMessage: 'Checking the working tree...',
85628
86472
  });
85629
- await ensureWorkingTreeClean((_d = options.workspace) === null || _d === void 0 ? void 0 : _d.repositoryRoot);
86473
+ await ensureWorkingTreeClean((_h = (_g = options.workspace) === null || _g === void 0 ? void 0 : _g.repositoryRoot) !== null && _h !== void 0 ? _h : projectPath);
85630
86474
  }
85631
86475
  const currentRoundStartTime = Date.now();
85632
86476
  // Note: An isolated round implements the prompt in a temporary worktree and merges it back afterwards
@@ -85688,7 +86532,7 @@
85688
86532
  * Pulls the latest repository state before loading prompts when the feature is enabled.
85689
86533
  */
85690
86534
  async function pullLatestChangesIfEnabled(options) {
85691
- var _a;
86535
+ var _a, _b;
85692
86536
  const { options: runOptions, isRichUiEnabled } = options;
85693
86537
  if (!runOptions.autoPull || runOptions.dryRun) {
85694
86538
  return;
@@ -85696,7 +86540,7 @@
85696
86540
  if (!isRichUiEnabled) {
85697
86541
  console.info(colors__default["default"].gray('Pulling latest changes before the next prompt...'));
85698
86542
  }
85699
- await pullLatestChanges((_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot);
86543
+ await pullLatestChanges((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : runOptions.projectPath);
85700
86544
  }
85701
86545
  /**
85702
86546
  * Creates the progress display and rich UI handles used during the run.
@@ -85721,19 +86565,19 @@
85721
86565
  * Normalizes legacy and current priority options into one validated run option shape.
85722
86566
  */
85723
86567
  function normalizeRunOptions(options) {
85724
- var _a, _b, _c, _d, _e, _f, _g;
85725
- const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
85726
- const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
86568
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
86569
+ const checkBefore = (_a = options.checkBefore) !== null && _a !== void 0 ? _a : 'no';
85727
86570
  const priorityFilter = normalizePriorityFilter({
85728
86571
  priority: options.priority,
85729
- minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
85730
- maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
86572
+ minimumPriority: (_b = options.minimumPriority) !== null && _b !== void 0 ? _b : (_c = options.priorityFilter) === null || _c === void 0 ? void 0 : _c.minimumPriority,
86573
+ maximumPriority: (_d = options.maximumPriority) !== null && _d !== void 0 ? _d : (_e = options.priorityFilter) === null || _e === void 0 ? void 0 : _e.maximumPriority,
85731
86574
  });
85732
86575
  return {
85733
86576
  ...options,
85734
- testBefore,
85735
- testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
85736
- priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
86577
+ projectPath: (_h = (_g = (_f = options.workspace) === null || _f === void 0 ? void 0 : _f.projectPath) !== null && _g !== void 0 ? _g : options.projectPath) !== null && _h !== void 0 ? _h : process.cwd(),
86578
+ checkBefore,
86579
+ checkCommand: resolveCoderCheckCommand((_j = options.checkCommand) === null || _j === void 0 ? void 0 : _j.trim(), checkBefore),
86580
+ priority: (_k = priorityFilter.minimumPriority) !== null && _k !== void 0 ? _k : 0,
85737
86581
  minimumPriority: priorityFilter.minimumPriority,
85738
86582
  maximumPriority: priorityFilter.maximumPriority,
85739
86583
  priorityFilter,
@@ -85742,63 +86586,63 @@
85742
86586
  /**
85743
86587
  * Runs the optional pre-coding verification and, when requested, its one repair prompt.
85744
86588
  */
85745
- async function runTestBeforeIfNeeded(options) {
85746
- var _a, _b, _c, _d, _e;
86589
+ async function runCheckBeforeIfNeeded(options) {
86590
+ var _a, _b;
85747
86591
  const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, isContinuingInterruptedPrompt, } = options;
85748
- if (runOptions.testBefore === 'no') {
86592
+ if (runOptions.checkBefore === 'no') {
85749
86593
  return hasWaitedForStart;
85750
86594
  }
85751
- if (!runOptions.testCommand) {
86595
+ if (!runOptions.checkCommand) {
85752
86596
  throw new NotAllowed(_spaceTrim.spaceTrim(`
85753
- ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
86597
+ ${'`--check-before ' + runOptions.checkBefore + '`'} requires a verification command.
85754
86598
 
85755
- Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
86599
+ Pass one with ${'`--check <check-command>`'} or use the default ${'`npm run check`'} command.
85756
86600
  `));
85757
86601
  }
85758
86602
  if (isCleanWorkingTreeRequired(runOptions.gitChanges, isContinuingInterruptedPrompt)) {
85759
86603
  await waitForRequestedPause({
85760
- checkpointLabel: 'checking the git working tree before testing',
86604
+ checkpointLabel: 'checking the git working tree before checking',
85761
86605
  phase: 'loading',
85762
- statusMessage: 'Checking the working tree before testing...',
86606
+ statusMessage: 'Checking the working tree before checking...',
85763
86607
  });
85764
- await ensureWorkingTreeClean((_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot);
86608
+ await ensureWorkingTreeClean((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : runOptions.projectPath);
85765
86609
  }
85766
- const testBeforeCommitScope = await captureTestBeforeCommitScopeIfNeeded(runOptions);
86610
+ const checkBeforeCommitScope = await captureCheckBeforeCommitScopeIfNeeded(runOptions);
85767
86611
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
85768
- const testBeforeResult = await runTestBefore({
85769
- testCommand: runOptions.testCommand,
85770
- projectPath: (_c = (_b = runOptions.workspace) === null || _b === void 0 ? void 0 : _b.projectPath) !== null && _c !== void 0 ? _c : process.cwd(),
86612
+ const checkBeforeResult = await runCheckBefore({
86613
+ checkCommand: runOptions.checkCommand,
86614
+ projectPath: runOptions.projectPath,
85771
86615
  waitForPauseCheckpoint: waitForRequestedPause,
85772
86616
  }).finally(() => {
85773
86617
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
85774
86618
  });
85775
- await commitTestBeforeChangesIfNeeded({
86619
+ await commitCheckBeforeChangesIfNeeded({
85776
86620
  runOptions,
85777
- testBeforeCommitScope,
86621
+ checkBeforeCommitScope,
85778
86622
  waitForRequestedPause,
85779
86623
  });
85780
- if (testBeforeResult.isPassed) {
86624
+ if (checkBeforeResult.isPassed) {
85781
86625
  return hasWaitedForStart;
85782
86626
  }
85783
- const testOutput = limitTestOutput(testBeforeResult.testOutput);
85784
- if (runOptions.testBefore === 'yes-and-fail') {
86627
+ const checkOutput = limitCheckOutput(checkBeforeResult.checkOutput);
86628
+ if (runOptions.checkBefore === 'yes-and-fail') {
85785
86629
  throw new NotAllowed(_spaceTrim.spaceTrim((block) => `
85786
- Pre-coding verification command \`${runOptions.testCommand}\` failed.
86630
+ Pre-coding check command \`${runOptions.checkCommand}\` failed.
85787
86631
 
85788
86632
  The coding agent was not started because the project was already failing before the first queued prompt.
85789
86633
 
85790
- ### Test results
86634
+ ### Check results
85791
86635
  ${'```'}
85792
- ${block(testOutput)}
86636
+ ${block(checkOutput)}
85793
86637
  ${'```'}
85794
86638
  `));
85795
86639
  }
85796
- const repairPrompt = await createTestBeforeRepairPrompt({
85797
- projectPath: (_e = (_d = runOptions.workspace) === null || _d === void 0 ? void 0 : _d.projectPath) !== null && _e !== void 0 ? _e : process.cwd(),
85798
- testCommand: runOptions.testCommand,
85799
- testOutput,
86640
+ const repairPrompt = await createCheckBeforeRepairPrompt({
86641
+ projectPath: runOptions.projectPath,
86642
+ checkCommand: runOptions.checkCommand,
86643
+ checkOutput,
85800
86644
  });
85801
- const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
86645
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section, runOptions.projectPath);
85802
86646
  const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
85803
86647
  options: runOptions,
85804
86648
  nextPrompt: repairPrompt,
@@ -85826,36 +86670,36 @@
85826
86670
  return updatedHasWaitedForStart;
85827
86671
  }
85828
86672
  /**
85829
- * Captures the files present before a `yes-and-fix` pre-coding test, so only changes made by that test can be committed.
86673
+ * Captures the files present before a `yes-and-fix` pre-coding check, so only changes made by that check can be committed.
85830
86674
  */
85831
- async function captureTestBeforeCommitScopeIfNeeded(runOptions) {
86675
+ async function captureCheckBeforeCommitScopeIfNeeded(runOptions) {
85832
86676
  var _a;
85833
- if (runOptions.testBefore !== 'yes-and-fix' || runOptions.noCommit) {
86677
+ if (runOptions.checkBefore !== 'yes-and-fix' || runOptions.noCommit) {
85834
86678
  return undefined;
85835
86679
  }
85836
- return captureCoderCommitScope((_a = runOptions.workspace) !== null && _a !== void 0 ? _a : process.cwd());
86680
+ return captureCoderCommitScope((_a = runOptions.workspace) !== null && _a !== void 0 ? _a : runOptions.projectPath);
85837
86681
  }
85838
86682
  /**
85839
- * Commits files changed by a `yes-and-fix` pre-coding test before the coder continues to a queued or repair prompt.
86683
+ * Commits files changed by a `yes-and-fix` pre-coding check before the coder continues to a queued or repair prompt.
85840
86684
  */
85841
- async function commitTestBeforeChangesIfNeeded(options) {
86685
+ async function commitCheckBeforeChangesIfNeeded(options) {
85842
86686
  var _a;
85843
- const { runOptions, testBeforeCommitScope, waitForRequestedPause } = options;
85844
- if (!testBeforeCommitScope) {
86687
+ const { runOptions, checkBeforeCommitScope, waitForRequestedPause } = options;
86688
+ if (!checkBeforeCommitScope) {
85845
86689
  return;
85846
86690
  }
85847
- const relevantPaths = await resolveCoderCommitScopePaths(testBeforeCommitScope);
86691
+ const relevantPaths = await resolveCoderCommitScopePaths(checkBeforeCommitScope);
85848
86692
  if (relevantPaths.length === 0) {
85849
86693
  return;
85850
86694
  }
85851
86695
  await waitForRequestedPause({
85852
- checkpointLabel: 'committing changes made by pre-coding tests',
86696
+ checkpointLabel: 'committing changes made by pre-coding checks',
85853
86697
  phase: 'verifying',
85854
- statusMessage: 'Committing changes made by pre-coding tests...',
86698
+ statusMessage: 'Committing changes made by pre-coding checks...',
85855
86699
  });
85856
- await commitChanges(PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE, {
86700
+ await commitChanges(PRE_CODING_CHECK_CHANGES_COMMIT_MESSAGE, {
85857
86701
  autoPush: runOptions.autoPush,
85858
- projectPath: (_a = testBeforeCommitScope.repositoryRoot) !== null && _a !== void 0 ? _a : testBeforeCommitScope.projectPath,
86702
+ projectPath: (_a = checkBeforeCommitScope.repositoryRoot) !== null && _a !== void 0 ? _a : checkBeforeCommitScope.projectPath,
85859
86703
  relevantPaths,
85860
86704
  });
85861
86705
  }
@@ -85901,7 +86745,6 @@
85901
86745
  * Runs the dry-run reporting mode and returns whether the main execution should stop.
85902
86746
  */
85903
86747
  async function runDryRunIfRequested(options, agentReferences) {
85904
- var _a, _b;
85905
86748
  if (!options.dryRun) {
85906
86749
  return false;
85907
86750
  }
@@ -85910,7 +86753,7 @@
85910
86753
  modelName: options.agentName ? resolveRunnerModel(options.agentName, options.model) : options.model,
85911
86754
  agentReferences,
85912
86755
  };
85913
- const promptFiles = (await loadPromptFiles(path.join((_b = (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd(), 'prompts'), {
86756
+ const promptFiles = (await loadPromptFiles(path.join(options.projectPath, 'prompts'), {
85914
86757
  isMissingDirectoryAllowed: true,
85915
86758
  })).map((file) => ({
85916
86759
  ...file,
@@ -85919,7 +86762,7 @@
85919
86762
  const stats = summarizePrompts(promptFiles, options.priorityFilter);
85920
86763
  printStats(stats, options.priorityFilter);
85921
86764
  console.info(colors__default["default"].yellow('Following prompts need to be written:'));
85922
- printPromptsToBeWritten(promptFiles, options.priorityFilter);
86765
+ printPromptsToBeWritten(promptFiles, options.priorityFilter, options.projectPath);
85923
86766
  return true;
85924
86767
  }
85925
86768
  /**
@@ -85934,7 +86777,7 @@
85934
86777
  serverUrl: options.serverUrl,
85935
86778
  priorityFilter: options.priorityFilter,
85936
86779
  limit: options.limit,
85937
- testCommand: options.testCommand,
86780
+ checkCommand: options.checkCommand,
85938
86781
  });
85939
86782
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
85940
86783
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Running prompts with ${runnerName}`);
@@ -85957,10 +86800,9 @@
85957
86800
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
85958
86801
  */
85959
86802
  async function loadPromptQueueSnapshot(options) {
85960
- var _a, _b;
85961
86803
  const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity, isContinuingInterruptedPrompt, } = options;
85962
86804
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
85963
- const promptFiles = await loadPromptFiles(path.join((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd(), 'prompts'));
86805
+ const promptFiles = await loadPromptFiles(path.join(runOptions.projectPath, 'prompts'));
85964
86806
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
85965
86807
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.update(stats);
85966
86808
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(stats);
@@ -85987,10 +86829,10 @@
85987
86829
  }
85988
86830
  if (stats.toBeWritten > 0) {
85989
86831
  console.info(colors__default["default"].yellow('Following prompts need to be written:'));
85990
- printPromptsToBeWritten(promptFiles, priorityFilter);
86832
+ printPromptsToBeWritten(promptFiles, priorityFilter, options.projectPath);
85991
86833
  console.info('');
85992
86834
  }
85993
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
86835
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity, options.projectPath));
85994
86836
  return true;
85995
86837
  }
85996
86838
  /**
@@ -86187,12 +87029,14 @@
86187
87029
  return [
86188
87030
  ...options.promptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
86189
87031
  promptFile,
87032
+ projectPath: options.projectPath,
86190
87033
  isFinished: false,
86191
87034
  priorityFilter: options.priorityFilter,
86192
87035
  activePrompt,
86193
87036
  })),
86194
87037
  ...options.finishedPromptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
86195
87038
  promptFile,
87039
+ projectPath: options.projectPath,
86196
87040
  isFinished: true,
86197
87041
  priorityFilter: options.priorityFilter,
86198
87042
  activePrompt,
@@ -86203,13 +87047,15 @@
86203
87047
  * Builds one prompt-file response.
86204
87048
  */
86205
87049
  function buildCoderServerPromptFileResponse(options) {
87050
+ var _a;
86206
87051
  const { promptFile, isFinished, priorityFilter, activePrompt } = options;
86207
87052
  return {
86208
87053
  filePath: promptFile.path,
86209
87054
  fileName: promptFile.name,
86210
- relativeFilePath: path.relative(process.cwd(), promptFile.path).replace(/\\/gu, '/'),
87055
+ relativeFilePath: path.relative((_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd(), promptFile.path).replace(/\\/gu, '/'),
86211
87056
  isFinished,
86212
87057
  sections: promptFile.sections.map((section) => buildCoderServerPromptSectionResponse({
87058
+ projectPath: options.projectPath,
86213
87059
  promptFile,
86214
87060
  section,
86215
87061
  isFinished,
@@ -86224,7 +87070,7 @@
86224
87070
  function buildCoderServerPromptSectionResponse(options) {
86225
87071
  const { promptFile, section, isFinished, priorityFilter, activePrompt } = options;
86226
87072
  const isUnwritten = isPromptToBeWritten(promptFile, section);
86227
- const isActive = isPromptActive(promptFile, section, activePrompt);
87073
+ const isActive = isPromptActive(promptFile, section, activePrompt, options.projectPath);
86228
87074
  const column = getPromptColumn({
86229
87075
  section,
86230
87076
  isFinished,
@@ -86239,7 +87085,7 @@
86239
87085
  priority: section.priority,
86240
87086
  summary: buildPromptSummary(promptFile, section),
86241
87087
  content: buildCodexPrompt(promptFile, section),
86242
- label: buildPromptLabelForDisplay(promptFile, section),
87088
+ label: buildPromptLabelForDisplay(promptFile, section, options.projectPath),
86243
87089
  tags: buildPromptTags({
86244
87090
  section,
86245
87091
  isUnwritten,
@@ -86316,11 +87162,11 @@
86316
87162
  /**
86317
87163
  * Checks whether a parsed section is the prompt currently handled by the active agent.
86318
87164
  */
86319
- function isPromptActive(promptFile, section, activePrompt) {
87165
+ function isPromptActive(promptFile, section, activePrompt, projectPath) {
86320
87166
  if (!activePrompt || (section.status !== 'todo' && section.status !== 'in-progress')) {
86321
87167
  return false;
86322
87168
  }
86323
- return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
87169
+ return buildPromptLabelForDisplay(promptFile, section, projectPath) === activePrompt.label;
86324
87170
  }
86325
87171
  // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/buildCoderServerPromptResponse.ts) should never be published outside of `@promptbook/cli`
86326
87172
 
@@ -87241,6 +88087,7 @@
87241
88087
  const promptFiles = await loadPromptFilesSafely(promptsDir);
87242
88088
  const finishedPromptFiles = await loadPromptFilesSafely(path.join(promptsDir, FINISHED_PROMPTS_DIRECTORY_NAME));
87243
88089
  return buildCoderServerPromptFileResponses({
88090
+ projectPath: path.dirname(promptsDir),
87244
88091
  promptFiles,
87245
88092
  finishedPromptFiles,
87246
88093
  priorityFilter,
@@ -87327,18 +88174,20 @@
87327
88174
  * @private internal function of `ptbk coder server`
87328
88175
  */
87329
88176
  async function runCodexPromptsServer(options) {
87330
- var _a, _b, _c, _d;
87331
- const { port, ...runOptions } = options;
87332
- if (runOptions.dryRun) {
87333
- await runCodexPrompts(runOptions);
88177
+ var _a, _b, _c, _d, _e;
88178
+ const { port, ...providedOptions } = options;
88179
+ if (providedOptions.dryRun) {
88180
+ await runCodexPrompts(providedOptions);
87334
88181
  return;
87335
88182
  }
88183
+ const workspace = (_a = providedOptions.workspace) !== null && _a !== void 0 ? _a : await $resolveWorkspaceRepository(providedOptions.projectPath);
88184
+ const runOptions = { ...providedOptions, workspace, projectPath: workspace.projectPath };
87336
88185
  const serverUrl = `http://localhost:${port}`;
87337
88186
  const uiState = new CoderRunUiState(moment__default["default"]());
87338
88187
  const priorityFilter = normalizePriorityFilter({
87339
88188
  priority: runOptions.priority,
87340
- minimumPriority: (_a = runOptions.minimumPriority) !== null && _a !== void 0 ? _a : (_b = runOptions.priorityFilter) === null || _b === void 0 ? void 0 : _b.minimumPriority,
87341
- maximumPriority: (_c = runOptions.maximumPriority) !== null && _c !== void 0 ? _c : (_d = runOptions.priorityFilter) === null || _d === void 0 ? void 0 : _d.maximumPriority,
88189
+ minimumPriority: (_b = runOptions.minimumPriority) !== null && _b !== void 0 ? _b : (_c = runOptions.priorityFilter) === null || _c === void 0 ? void 0 : _c.minimumPriority,
88190
+ maximumPriority: (_d = runOptions.maximumPriority) !== null && _d !== void 0 ? _d : (_e = runOptions.priorityFilter) === null || _e === void 0 ? void 0 : _e.maximumPriority,
87342
88191
  });
87343
88192
  const serverHandle = startCoderHttpServer({
87344
88193
  port,