@promptbook/cli 0.114.0-47 → 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 +1060 -917
  96. package/src/version.ts +2 -2
  97. package/src/versions.txt +2 -1
  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/esm/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import colors from 'colors';
2
2
  import commander, { Option } from 'commander';
3
3
  import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { writeFile, stat, readdir, mkdir, readFile, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, statfs, access, constants as constants$1, watch, rmdir, mkdtemp } from 'fs/promises';
5
- import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
4
+ import { realpath, stat, access, writeFile, readdir, mkdir, readFile, rm, cp, lstat, symlink, rename, unlink, appendFile, copyFile, statfs, constants as constants$1, watch, rmdir, mkdtemp } from 'fs/promises';
5
+ import { resolve, join, delimiter, relative, basename, dirname, isAbsolute, extname } from 'path';
6
6
  import { createHash, randomBytes, randomUUID, timingSafeEqual } from 'crypto';
7
7
  import { spawn, spawnSync, execFile } from 'child_process';
8
8
  import moment from 'moment';
9
9
  import * as fs from 'fs';
10
- import { createWriteStream, mkdirSync, writeFileSync, constants, statSync, readFileSync, existsSync, realpathSync, lstatSync, linkSync, unlinkSync, renameSync, openSync, fsyncSync, closeSync, promises } from 'fs';
10
+ import { constants, createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, realpathSync, lstatSync, linkSync, unlinkSync, renameSync, openSync, fsyncSync, closeSync, promises } from 'fs';
11
11
  import * as dotenv from 'dotenv';
12
12
  import * as readline from 'readline';
13
13
  import { createInterface, emitKeypressEvents, cursorTo, clearLine } from 'readline';
@@ -51,7 +51,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
51
51
  * @generated
52
52
  * @see https://github.com/webgptorg/promptbook
53
53
  */
54
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-47';
54
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-49';
55
55
  /**
56
56
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
57
57
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1746,6 +1746,56 @@ function $reportMissingCliSubcommand(command) {
1746
1746
  return command.help();
1747
1747
  }
1748
1748
 
1749
+ /**
1750
+ * Registers project selection without capturing a directory during command registration.
1751
+ * @private internal CLI registration helper
1752
+ */
1753
+ function addProjectPathOption(command) {
1754
+ command.option('--path <directory>', 'Project directory (default: invocation current directory); relative paths start there');
1755
+ }
1756
+ /**
1757
+ * Registers additional context independently of primary-agent selection.
1758
+ * @private internal CLI registration helper
1759
+ */
1760
+ function addProjectContextOption(command) {
1761
+ 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');
1762
+ }
1763
+ /**
1764
+ * Captures an invocation's directory at action time. No process-global state is changed.
1765
+ * @private internal CLI normalization helper
1766
+ */
1767
+ function normalizeProjectCliOptions(options, invocationDirectory = process.cwd()) {
1768
+ const selectedPath = options.path;
1769
+ if (selectedPath !== undefined && !selectedPath.trim()) {
1770
+ throw new EnvironmentMismatchError(spaceTrim('Pass a non-empty project directory in `--path`, or omit it to use the current directory.'));
1771
+ }
1772
+ return { projectDirectory: resolve(invocationDirectory, selectedPath !== null && selectedPath !== void 0 ? selectedPath : '.') };
1773
+ }
1774
+ /**
1775
+ * Validates the selected directory before any setup writes, without selecting an enclosing Git root.
1776
+ * @private shared project resolution for CLI services
1777
+ */
1778
+ async function resolveProjectDirectory(projectDirectory) {
1779
+ const requestedPath = resolve(projectDirectory);
1780
+ try {
1781
+ const projectPath = await realpath(requestedPath);
1782
+ if (!(await stat(projectPath)).isDirectory()) {
1783
+ throw new EnvironmentMismatchError(spaceTrim(`Project path \`${requestedPath}\` is not a directory.`));
1784
+ }
1785
+ await access(projectPath, constants.R_OK | constants.X_OK);
1786
+ return projectPath;
1787
+ }
1788
+ catch (error) {
1789
+ throw new EnvironmentMismatchError(spaceTrim(`
1790
+ Cannot access project directory \`${requestedPath}\`.
1791
+ Check that it exists, is a directory, and is readable and searchable; correct \`--path\` before retrying.
1792
+
1793
+ ${error instanceof Error ? error.message : String(error)}
1794
+ `));
1795
+ }
1796
+ }
1797
+ // Note: [💞] Shared project registration and resolution helpers.
1798
+
1749
1799
  // Note: [💞] Ignore a discrepancy between file name and entity name
1750
1800
  /**
1751
1801
  * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
@@ -2105,15 +2155,18 @@ function $initializeAgentChatCommand(program) {
2105
2155
  command.requiredOption('--agent <agent-book-path>', 'Path to the agent .book file');
2106
2156
  addPromptRunnerSelectionOptions(command);
2107
2157
  addPromptRunnerRuntimeOptions(command);
2108
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
2158
+ addProjectPathOption(command);
2159
+ addProjectContextOption(command);
2109
2160
  command.action(handleActionErrors(async (cliOptions, commandProgram) => {
2161
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2162
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2110
2163
  const options = cliOptions;
2111
2164
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram);
2112
2165
  const { runAgentChat } = await Promise.resolve().then(function () { return runAgentChat$1; });
2113
2166
  await runAgentChat({
2114
2167
  agentPath: resolveRequiredAgentPath(options),
2115
2168
  context: options.context,
2116
- currentWorkingDirectory: process.cwd(),
2169
+ currentWorkingDirectory: projectPath,
2117
2170
  ...runnerOptions,
2118
2171
  });
2119
2172
  }));
@@ -2139,8 +2192,11 @@ function $initializeAgentExecCommand(program) {
2139
2192
  command.requiredOption('--message <message>', 'User message to send to the agent');
2140
2193
  addPromptRunnerSelectionOptions(command);
2141
2194
  addPromptRunnerRuntimeOptions(command);
2142
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
2195
+ addProjectPathOption(command);
2196
+ addProjectContextOption(command);
2143
2197
  command.action(handleActionErrors(async (cliOptions, commandProgram) => {
2198
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2199
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2144
2200
  const options = cliOptions;
2145
2201
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram);
2146
2202
  const { runAgentExec } = await Promise.resolve().then(function () { return runAgentExec$1; });
@@ -2148,7 +2204,7 @@ function $initializeAgentExecCommand(program) {
2148
2204
  agentPath: resolveRequiredAgentPath(options),
2149
2205
  context: options.context,
2150
2206
  message: resolveRequiredAgentMessage(options),
2151
- currentWorkingDirectory: process.cwd(),
2207
+ currentWorkingDirectory: projectPath,
2152
2208
  ...runnerOptions,
2153
2209
  });
2154
2210
  }));
@@ -2452,8 +2508,11 @@ function $initializeAgentInitCommand(program) {
2452
2508
  - ${AGENT_BOOK_FILE_PATH}
2453
2509
  - ${AGENT_BOOK_LANGUAGE_MANUAL_FILE_PATH.replace(/\\/gu, '/')}
2454
2510
  `));
2455
- command.action(handleActionErrors(async () => {
2456
- const summary = await initializeAgentProjectConfiguration(process.cwd());
2511
+ addProjectPathOption(command);
2512
+ command.action(handleActionErrors(async (cliOptions) => {
2513
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2514
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2515
+ const summary = await initializeAgentProjectConfiguration(projectPath);
2457
2516
  printAgentInitializationSummary(summary);
2458
2517
  }));
2459
2518
  }
@@ -2508,15 +2567,21 @@ function $initializeAgentRunnerCommand(program, options) {
2508
2567
  `));
2509
2568
  addPromptRunnerSelectionOptions(command);
2510
2569
  addPromptRunnerExecutionOptions(command);
2570
+ addProjectPathOption(command);
2511
2571
  (_a = options.configureCommand) === null || _a === void 0 ? void 0 : _a.call(options, command);
2512
2572
  command.action(handleActionErrors(async (cliOptions) => {
2513
- const runOptions = createAgentRunOptionsFromCliOptions(cliOptions);
2573
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
2574
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
2575
+ const runOptions = {
2576
+ ...createAgentRunOptionsFromCliOptions(cliOptions),
2577
+ projectPath,
2578
+ };
2514
2579
  const execute = await options.loadExecutor();
2515
2580
  if (options.executionMode === 'watch') {
2516
2581
  const { runPersistentAgentWatch } = await Promise.resolve().then(function () { return runPersistentAgentWatch$1; });
2517
2582
  await runPersistentAgentWatch({
2518
2583
  commandDisplayName: `ptbk agent-folder ${options.commandName}`,
2519
- logDirectoryPath: process.cwd(),
2584
+ logDirectoryPath: projectPath,
2520
2585
  runWatch: async () => {
2521
2586
  await execute(runOptions);
2522
2587
  },
@@ -24750,7 +24815,7 @@ async function appendScriptExecutionLogFinish({ scriptPath, logPath, status, det
24750
24815
  * Distinguishes prompt-runner and verification temp shells in the shared runtime log.
24751
24816
  */
24752
24817
  function describeTempScriptKind(scriptPath) {
24753
- return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
24818
+ return scriptPath.toLowerCase().endsWith('.check.sh') ? 'check shell' : 'runner shell';
24754
24819
  }
24755
24820
 
24756
24821
  /**
@@ -24953,28 +25018,44 @@ async function runBashScriptWithOutput(options) {
24953
25018
  /**
24954
25019
  * Handles process exit and resolves or rejects accordingly.
24955
25020
  */
24956
- const handleExit = (code) => {
25021
+ const handleExit = (code, signal) => {
24957
25022
  var _a;
24958
25023
  if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
24959
25024
  settleWithLog('cancelled', () => reject(options.signal.reason), options.signal.reason);
24960
25025
  return;
24961
25026
  }
24962
- if (code === 0) {
24963
- settleWithLog('succeeded', () => resolve(spaceTrim$1(output)));
25027
+ if (code === 0 && !signal) {
25028
+ settleWithLog('succeeded', () => {
25029
+ var _a;
25030
+ // Cancellation can arrive while the runtime log footer is being flushed.
25031
+ if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
25032
+ reject(options.signal.reason);
25033
+ else
25034
+ resolve(spaceTrim$1(output));
25035
+ });
24964
25036
  return;
24965
25037
  }
24966
- const failure = new Error(spaceTrim$1(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
24967
- settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
25038
+ const failure = new NotAllowed(spaceTrim$1((block) => `
25039
+ Command "bash ${scriptPathPosix}" exited with code ${code !== null && code !== void 0 ? code : 'unknown'}${signal ? ` and signal ${signal}` : ''}.
25040
+ ${block(output)}
25041
+ `));
25042
+ settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}${signal ? ` and signal ${signal}` : ''}`, () => reject(failure), failure);
24968
25043
  };
24969
25044
  // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
24970
25045
  // substitutions after the direct shell exits.
24971
25046
  commandProcess.on('close', handleExit);
24972
25047
  commandProcess.on('disconnect', () => {
24973
- const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
25048
+ const failure = new NotAllowed(spaceTrim$1((block) => `
25049
+ Command "bash ${scriptPathPosix}" disconnected.
25050
+ ${block(output)}
25051
+ `));
24974
25052
  settleWithLog('failed after disconnect', () => reject(failure), failure);
24975
25053
  });
24976
25054
  commandProcess.on('error', (error) => {
24977
- const failure = new Error(`Command "bash ${scriptPathPosix}" failed: ${error.message}`);
25055
+ const failure = new NotAllowed(spaceTrim$1((block) => `
25056
+ Command "bash ${scriptPathPosix}" failed: ${error.message}
25057
+ ${block(output)}
25058
+ `));
24978
25059
  settleWithLog('failed before completion', () => reject(failure), failure);
24979
25060
  });
24980
25061
  /** Reuses the process-tree terminator; settlement waits for stream closure and the trace footer. */
@@ -26430,6 +26511,11 @@ class GeminiRunner {
26430
26511
  }
26431
26512
  }
26432
26513
 
26514
+ /** Quotes one literal argument, including spaces and shell metacharacters, for a generated Bash script. */
26515
+ function quoteBashArgument(value) {
26516
+ return `'${value.replace(/'/gu, "'\\''")}'`;
26517
+ }
26518
+
26433
26519
  /**
26434
26520
  * Builds the shell script that runs GitHub Copilot CLI with the prompt and coding context.
26435
26521
  */
@@ -26439,7 +26525,7 @@ function buildGitHubCopilotScript(options) {
26439
26525
  const modelArgument = options.model ? ` --model ${options.model}` : '';
26440
26526
  const thinkingLevelArgument = options.thinkingLevel ? ` --reasoning-effort ${options.thinkingLevel}` : '';
26441
26527
  return spaceTrim((block) => `
26442
- cd "${projectPath}"
26528
+ cd ${quoteBashArgument(projectPath)}
26443
26529
 
26444
26530
  if [ -f .env ]; then
26445
26531
  set -a
@@ -26880,7 +26966,7 @@ function buildCodexScript(options) {
26880
26966
  ...(options.isMachineReadableProgressEnabled ? [' --json \\'] : []),
26881
26967
  ' --local-provider none \\',
26882
26968
  ` --sandbox ${options.sandbox} \\`,
26883
- ` -C ${projectPath} \\`,
26969
+ ` -C ${quoteBashArgument(projectPath)} \\`,
26884
26970
  ' --skip-git-repo-check \\',
26885
26971
  ` <<'${delimiter}'`,
26886
26972
  '',
@@ -29216,7 +29302,7 @@ function buildSessionRows(options, bodyWidth, pausePresentation) {
29216
29302
  ...buildOptionalSessionRow('Server', options.config.serverUrl
29217
29303
  ? buildTerminalUrlLink(options.config.serverUrl, bodyWidth - SESSION_LABEL_WIDTH - 1)
29218
29304
  : undefined),
29219
- ...buildOptionalSessionRow('Test', options.config.testCommand),
29305
+ ...buildOptionalSessionRow('Check', options.config.checkCommand),
29220
29306
  ];
29221
29307
  return [
29222
29308
  {
@@ -31710,7 +31796,7 @@ function createCoderRunOptionsForAgent(options) {
31710
31796
  return {
31711
31797
  dryRun: false,
31712
31798
  context: undefined,
31713
- testCommand: undefined,
31799
+ checkCommand: undefined,
31714
31800
  preserveLogs: false,
31715
31801
  noUi: options.noUi,
31716
31802
  thinkingLevel: options.thinkingLevel,
@@ -32812,40 +32898,207 @@ function createCoderRunStepTracker(onStepStarted) {
32812
32898
  }
32813
32899
 
32814
32900
  /**
32815
- * Maximum amount of test output embedded into an agent prompt.
32901
+ * Maximum amount of check output embedded into an agent prompt.
32902
+ */
32903
+ const MAX_CHECK_OUTPUT_CHARS = 12000;
32904
+ /**
32905
+ * Limits check output while keeping the end of the output, where check tools usually print the failure summary.
32906
+ */
32907
+ function limitCheckOutput(checkOutput) {
32908
+ const normalizedCheckOutput = checkOutput.trim();
32909
+ if (normalizedCheckOutput.length <= MAX_CHECK_OUTPUT_CHARS) {
32910
+ return normalizedCheckOutput;
32911
+ }
32912
+ return `[..., check output truncated to the last ${MAX_CHECK_OUTPUT_CHARS} characters...]\n${normalizedCheckOutput.slice(-MAX_CHECK_OUTPUT_CHARS)}`;
32913
+ }
32914
+
32915
+ /**
32916
+ * Modes supported by `ptbk coder run --check-before`.
32917
+ */
32918
+ const CHECK_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
32919
+ /**
32920
+ * Default check command used when a pre-coding mode is enabled without an explicit `--check` command.
32921
+ */
32922
+ const DEFAULT_CODER_CHECK_COMMAND = 'npm run check';
32923
+ /**
32924
+ * Resolves the shared command without enabling checks when both command and preflight mode are absent.
32925
+ */
32926
+ function resolveCoderCheckCommand(checkCommand, checkBefore = 'no') {
32927
+ return checkCommand !== null && checkCommand !== void 0 ? checkCommand : (checkBefore === 'no' ? undefined : DEFAULT_CODER_CHECK_COMMAND);
32928
+ }
32929
+ /**
32930
+ * Checks whether a value is a supported `--check-before` mode.
32931
+ */
32932
+ function isCheckBeforeMode(value) {
32933
+ return CHECK_BEFORE_MODE_VALUES.includes(value);
32934
+ }
32935
+
32936
+ /** Conventional root command boundaries, including subshells and environment assignments. */
32937
+ const SCRIPT_COMMAND_PREFIX = String.raw `(?:^|&&|\|\||;|\n|[()])\s*(?:(?:env\s+)?[A-Za-z_]\w*=(?:[^\s'";&|()]+|"[^"]*"|'[^']*')\s+)*`;
32938
+ /** Package-manager options which do not select a different directory or workspace. */
32939
+ const QUIET_OPTIONS = String.raw `(?:(?:--silent|--quiet|-s|-q)\s+)*`;
32940
+ /** Quoted script names preserve spaces, while unquoted names use conventional npm identifier characters. */
32941
+ const SCRIPT_NAME = String.raw `(?:(["'])([^"'\r\n]+)\1|([\w:.-]+))(?=\s|$|[;&|()])`;
32942
+ /** Quoted payloads and escaped characters are opaque to this configuration inspection. */
32943
+ const OPAQUE_ARGUMENT = String.raw `"(?:\\.|[^"\\])*"|'[^']*'|\\[\s\S]`;
32944
+ /** Only references before a conventional directory change can confidently belong to the root package. */
32945
+ const DIRECTORY_CHANGE_COMMAND = new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:cd|pushd|popd)(?=\\s|$)|(?<argument>${OPAQUE_ARGUMENT})`, 'gu');
32946
+ /** Arguments up to the next conventional command boundary, preserving quoted payloads as opaque text. */
32947
+ const SCRIPT_ARGUMENTS = new RegExp(String.raw `^(?:${OPAQUE_ARGUMENT}|[^;&|\n()'"\\])*`, 'u');
32948
+ /** Package-manager options that select validation from a different directory or workspace. */
32949
+ const PROJECT_OVERRIDE_OPTION = /\s(?:--prefix|--dir|--cwd|-C|--workspace|--workspaces|-w|--filter)(?:\s|=|$)/u;
32950
+ /**
32951
+ * Reads conventional npm/pnpm/yarn script references for configuration inspection.
32952
+ * Commands are still executed verbatim by the existing shell runner; this helper neither parses nor rewrites shells.
32953
+ * Options selecting other workspaces remain opaque so their scripts are not mistaken for root validation.
32954
+ * Inspection stops at the first directory change; quoted examples do not change the inspected workspace.
32816
32955
  */
32817
- const MAX_TEST_OUTPUT_CHARS = 12000;
32956
+ function projectScriptReferences(command) {
32957
+ var _a;
32958
+ const directoryChange = [...command.matchAll(DIRECTORY_CHANGE_COMMAND)].find((match) => { var _a; return ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.argument) === undefined; });
32959
+ if (directoryChange)
32960
+ command = command.slice(0, directoryChange.index);
32961
+ const references = [
32962
+ ...command.matchAll(new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:npm|pnpm|yarn)\\s+${QUIET_OPTIONS}(?:run(?:-script)?\\s+${QUIET_OPTIONS}${SCRIPT_NAME}|(?:test|t)(?=\\s|$|[;&|()]))` +
32963
+ `|(?<argument>${OPAQUE_ARGUMENT})`, 'gu')),
32964
+ ]
32965
+ .filter((match) => isRootProjectScriptReference(command, match))
32966
+ .map((match) => { var _a, _b; return (_b = (_a = match[2]) !== null && _a !== void 0 ? _a : match[3]) !== null && _b !== void 0 ? _b : 'test'; });
32967
+ for (const match of command.matchAll(new RegExp(`${SCRIPT_COMMAND_PREFIX}(?:pnpm|yarn)\\s+${QUIET_OPTIONS}(?!run\\b|-)${SCRIPT_NAME}` +
32968
+ `|(?<argument>${OPAQUE_ARGUMENT})`, 'gu')))
32969
+ if (isRootProjectScriptReference(command, match))
32970
+ references.push(((_a = match[2]) !== null && _a !== void 0 ? _a : match[3]));
32971
+ return [...new Set(references)];
32972
+ }
32973
+ /** Excludes quoted examples and package-manager directory overrides without altering the executed command. */
32974
+ function isRootProjectScriptReference(command, match) {
32975
+ var _a, _b, _c, _d;
32976
+ if (((_a = match.groups) === null || _a === void 0 ? void 0 : _a.argument) !== undefined)
32977
+ return false;
32978
+ 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 : '';
32979
+ // Flags after npm's `--` separator belong to the leaf tool, rather than the package manager.
32980
+ const packageManagerArguments = (_d = argumentsText.split(/\s--(?:\s|$)/u)[0]) !== null && _d !== void 0 ? _d : '';
32981
+ return !PROJECT_OVERRIDE_OPTION.test(packageManagerArguments);
32982
+ }
32983
+
32818
32984
  /**
32819
- * Limits test output while keeping the end of the output, where test runners usually print the failure summary.
32985
+ * Failing setup placeholder used only when initialization finds no usable project validation scripts.
32820
32986
  */
32821
- function limitTestOutput(testOutput) {
32822
- const normalizedTestOutput = testOutput.trim();
32823
- if (normalizedTestOutput.length <= MAX_TEST_OUTPUT_CHARS) {
32824
- return normalizedTestOutput;
32987
+ const CHECK_SETUP_PLACEHOLDER_COMMAND = `node -e "console.error('Configure package.json scripts.check with project validation commands before running Coder.'); process.exit(1)"`;
32988
+ /** Retired generated script names inspected only to report stale caller configuration. */
32989
+ const LEGACY_CHECK_SCRIPT_NAMES$1 = new Set(['test-for-ptbk-coder', 'check-for-ptbk-coder']);
32990
+ /**
32991
+ * A missing or unconfigured project check needs project-owner setup, rather than model repair retries.
32992
+ */
32993
+ class CoderCheckSetupError extends NotAllowed {
32994
+ /** Creates an actionable project check setup error. */
32995
+ constructor(details) {
32996
+ super(spaceTrim$1(`
32997
+ Project check is not configured: ${details}
32998
+
32999
+ Configure the selected validation scripts in \`package.json\`. The canonical aggregate is \`scripts.check\`, invoked as \`${DEFAULT_CODER_CHECK_COMMAND}\`.
33000
+ Coder will not replace missing validation with a successful placeholder or tests alone.
33001
+ `));
33002
+ Object.setPrototypeOf(this, CoderCheckSetupError.prototype);
32825
33003
  }
32826
- return `[..., test output truncated to the last ${MAX_TEST_OUTPUT_CHARS} characters...]\n${normalizedTestOutput.slice(-MAX_TEST_OUTPUT_CHARS)}`;
32827
33004
  }
33005
+ /**
33006
+ * Detects the generated setup placeholder and the conventional npm unconfigured-test placeholder.
33007
+ */
33008
+ function isCheckSetupRequired(command) {
33009
+ return (!command.trim() ||
33010
+ command.includes('Configure package.json scripts.check') ||
33011
+ /no test specified/iu.test(command));
33012
+ }
33013
+ /**
33014
+ * Inspects root project check references before an initial check or repair loop can start.
33015
+ * Flags and shell composition do not bypass setup validation; execution still uses the existing shell runner.
33016
+ */
33017
+ async function assertProjectCheckIsConfigured(command, projectPath) {
33018
+ var _a, _b;
33019
+ if (!command)
33020
+ return;
33021
+ const references = projectScriptReferences(command);
33022
+ if (references.length === 0)
33023
+ return;
33024
+ let packageJson;
33025
+ try {
33026
+ packageJson = JSON.parse(await readFile(join(projectPath, 'package.json'), 'utf-8'));
33027
+ }
33028
+ catch (error) {
33029
+ throw new CoderCheckSetupError(`cannot read package.json (${error instanceof Error ? error.message : String(error)}).`);
33030
+ }
33031
+ for (const reference of references) {
33032
+ 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) {
33033
+ 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.`);
33034
+ }
33035
+ assertProjectCheckScriptsAreConfigured((_b = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) !== null && _b !== void 0 ? _b : {}, reference);
33036
+ }
33037
+ }
33038
+ /**
33039
+ * Validates the project-owned check definition without running commands or changing its scope.
33040
+ * Initialization and execution use this same setup diagnostic for missing leaves and recursive callers.
33041
+ */
33042
+ function assertProjectCheckScriptsAreConfigured(scripts, name = 'check') {
33043
+ assertReferencedScriptsAreConfigured(name, scripts, new Set());
33044
+ }
33045
+ /** Detects missing/placeholder leaf validation and recursive npm callers before a model repair loop can start. */
33046
+ function assertReferencedScriptsAreConfigured(name, scripts, ancestors) {
33047
+ if (ancestors.has(name))
33048
+ throw new CoderCheckSetupError(`scripts.${name} recursively calls the check pipeline.`);
33049
+ const command = scripts[name];
33050
+ if (typeof command !== 'string' || isCheckSetupRequired(command)) {
33051
+ throw new CoderCheckSetupError(`scripts.${name} is missing, empty, or contains a setup placeholder.`);
33052
+ }
33053
+ if (/^coder:(?:run|fix)$/u.test(name) || /\b(?:ptbk|promptbook)\s+coder\s+(?:run|fix)\b/u.test(command)) {
33054
+ throw new CoderCheckSetupError(`scripts.${name} calls Coder execution instead of project validation.`);
33055
+ }
33056
+ const nextAncestors = new Set([...ancestors, name]);
33057
+ // The shared inspector keeps root references before a directory change and leaves child workspaces opaque.
33058
+ for (const reference of projectScriptReferences(command)) {
33059
+ assertReferencedScriptsAreConfigured(reference, scripts, nextAncestors);
33060
+ }
33061
+ for (const prefix of ['pre', 'post']) {
33062
+ const lifecycleName = `${prefix}${name}`;
33063
+ if (scripts[lifecycleName] !== undefined)
33064
+ assertReferencedScriptsAreConfigured(lifecycleName, scripts, nextAncestors);
33065
+ }
33066
+ }
33067
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
32828
33068
 
32829
33069
  /**
32830
- * Runs the configured verification command inside the project root and returns its output.
33070
+ * Runs the configured check command inside the project root and returns its output.
32831
33071
  */
32832
- async function runPromptTestCommand(options) {
33072
+ async function runPromptCheckCommand(options) {
33073
+ await assertProjectCheckIsConfigured(options.command, options.projectPath);
32833
33074
  const projectPath = toPosixPath(options.projectPath);
32834
33075
  return await $runGoScriptWithOutput({
32835
33076
  scriptPath: options.scriptPath,
33077
+ projectPath: options.projectPath,
32836
33078
  scriptContent: spaceTrim(`
32837
- cd "${projectPath}"
33079
+ cd ${quoteBashArgument(projectPath)} || exit 1
32838
33080
  ${options.command}
32839
33081
  `),
32840
33082
  logPath: options.logPath,
32841
33083
  preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
33084
+ signal: options.signal,
32842
33085
  });
32843
33086
  }
32844
33087
 
32845
33088
  /**
32846
- * Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
33089
+ * Shared constraints for initial repair PRDs and post-prompt check feedback.
33090
+ */
33091
+ const CHECK_REPAIR_INSTRUCTIONS = spaceTrim$1(`
33092
+ Fix the underlying lint, typechecking, build, generated-code consistency, or test failure without weakening validation.
33093
+ Do not delete assertions, disable lint rules, remove failing checks from the aggregate, lower quality thresholds,
33094
+ skip a build, or force exit code zero merely to obtain a pass. Keep the project's chosen check scope intact.
33095
+ Missing or unconfigured validation requires project-owner setup; never replace it with a meaningless green result.
33096
+ `);
33097
+
33098
+ /**
33099
+ * Maximum number of coding attempts allowed for the same prompt when check keeps failing.
32847
33100
  */
32848
- const MAX_PROMPT_TEST_ATTEMPTS = 3;
33101
+ const MAX_PROMPT_CHECK_ATTEMPTS = 3;
32849
33102
  /**
32850
33103
  * File extension used by generated shell scripts.
32851
33104
  */
@@ -32853,12 +33106,18 @@ const SHELL_SCRIPT_EXTENSION = '.sh';
32853
33106
  /**
32854
33107
  * Runs one coding prompt and, when configured, verifies it with a shell command that can feed failures back.
32855
33108
  */
32856
- async function runPromptWithTestFeedback(options) {
32857
- var _a, _b, _c, _d;
32858
- const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
33109
+ async function runPromptWithCheckFeedback(options) {
33110
+ var _a, _b, _c, _d, _e;
33111
+ (_a = options.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
33112
+ const normalizedCheckCommand = (_b = options.checkCommand) === null || _b === void 0 ? void 0 : _b.trim();
33113
+ // Injected executors own their setup validation, just as they own command execution. The shared real executor
33114
+ // validates again after each agent attempt in case it changed the project's validation configuration.
33115
+ if (!options.runPromptCheckCommandExecutor) {
33116
+ await assertProjectCheckIsConfigured(normalizedCheckCommand, options.projectPath);
33117
+ }
32859
33118
  const stepTracker = createCoderRunStepTracker(options.onStepStarted);
32860
- if (!normalizedTestCommand) {
32861
- (_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
33119
+ if (!normalizedCheckCommand) {
33120
+ (_c = options.onAttemptStarted) === null || _c === void 0 ? void 0 : _c.call(options, 1);
32862
33121
  await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, 1);
32863
33122
  const result = await runRunnerPromptStep({
32864
33123
  runOptions: options,
@@ -32868,10 +33127,10 @@ async function runPromptWithTestFeedback(options) {
32868
33127
  });
32869
33128
  return { ...result, attemptCount: 1, steps: stepTracker.steps };
32870
33129
  }
32871
- const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
33130
+ const runPromptCheckCommandExecutor = (_d = options.runPromptCheckCommandExecutor) !== null && _d !== void 0 ? _d : runPromptCheckCommand;
32872
33131
  let promptForCurrentAttempt = options.prompt;
32873
- for (let attemptCount = 1; attemptCount <= MAX_PROMPT_TEST_ATTEMPTS; attemptCount++) {
32874
- (_d = options.onAttemptStarted) === null || _d === void 0 ? void 0 : _d.call(options, attemptCount);
33132
+ for (let attemptCount = 1; attemptCount <= MAX_PROMPT_CHECK_ATTEMPTS; attemptCount++) {
33133
+ (_e = options.onAttemptStarted) === null || _e === void 0 ? void 0 : _e.call(options, attemptCount);
32875
33134
  await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, attemptCount);
32876
33135
  const result = await runRunnerPromptStep({
32877
33136
  runOptions: options,
@@ -32879,42 +33138,44 @@ async function runPromptWithTestFeedback(options) {
32879
33138
  kind: attemptCount === 1 ? 'implementation' : 'fixing',
32880
33139
  stepTracker,
32881
33140
  });
32882
- await waitForVerificationPauseCheckpoint(options.waitForPauseCheckpoint, normalizedTestCommand, attemptCount);
32883
- console.info(colors.gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
32884
- const failedVerification = await runVerificationStep({
32885
- runPromptTestCommandExecutor,
32886
- testCommand: normalizedTestCommand,
33141
+ await waitForCheckPauseCheckpoint(options.waitForPauseCheckpoint, normalizedCheckCommand, attemptCount);
33142
+ console.info(colors.gray(`Running check command after attempt #${attemptCount}: ${normalizedCheckCommand}`));
33143
+ const failedCheck = await runCheckStep({
33144
+ runPromptCheckCommandExecutor,
33145
+ checkCommand: normalizedCheckCommand,
32887
33146
  runOptions: options,
32888
33147
  stepTracker,
32889
33148
  });
32890
- if (failedVerification === undefined) {
33149
+ if (failedCheck === undefined) {
32891
33150
  return { ...result, attemptCount, steps: stepTracker.steps };
32892
33151
  }
32893
- const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
32894
- const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
32895
- if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
32896
- console.error(colors.red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
32897
- throw new Error(buildFinalVerificationFailureMessage({
33152
+ const fullCheckOutput = formatUnknownErrorDetails(failedCheck.error);
33153
+ const feedbackCheckOutput = limitCheckOutput(fullCheckOutput);
33154
+ if (attemptCount >= MAX_PROMPT_CHECK_ATTEMPTS) {
33155
+ console.error(colors.red(`Check failed for ${options.promptLabel} after ${attemptCount} attempts.`));
33156
+ throw new NotAllowed(buildFinalCheckFailureMessage({
32898
33157
  promptLabel: options.promptLabel,
32899
- testCommand: normalizedTestCommand,
33158
+ checkCommand: normalizedCheckCommand,
32900
33159
  attemptCount,
32901
- verificationOutput: fullVerificationOutput,
33160
+ checkOutput: fullCheckOutput,
32902
33161
  }));
32903
33162
  }
32904
- console.warn(colors.yellow(`Verification failed for ${options.promptLabel} on attempt #${attemptCount}. Sending feedback to ${options.runner.name} and retrying...`));
32905
- promptForCurrentAttempt = appendCoderContext(options.prompt, buildVerificationFeedback({
32906
- testCommand: normalizedTestCommand,
33163
+ console.warn(colors.yellow(`Check failed for ${options.promptLabel} on attempt #${attemptCount}. Sending feedback to ${options.runner.name} and retrying...`));
33164
+ promptForCurrentAttempt = appendCoderContext(options.prompt, buildCheckFeedback({
33165
+ checkCommand: normalizedCheckCommand,
32907
33166
  failedAttemptCount: attemptCount,
32908
- verificationOutput: feedbackVerificationOutput,
33167
+ checkOutput: feedbackCheckOutput,
32909
33168
  }));
32910
33169
  }
32911
- throw new Error('Unexpected prompt verification state.');
33170
+ throw new UnexpectedError('Unexpected prompt check state.');
32912
33171
  }
32913
33172
  /**
32914
33173
  * Runs one coding attempt through the runner, timing it and recording it as an implementation or fixing step.
32915
33174
  */
32916
33175
  async function runRunnerPromptStep(options) {
33176
+ var _a, _b;
32917
33177
  const { runOptions, prompt, kind, stepTracker } = options;
33178
+ (_a = runOptions.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
32918
33179
  await stepTracker.startStep(kind);
32919
33180
  const stepStartedTimeMs = Date.now();
32920
33181
  const result = await runOptions.runner.runPrompt({
@@ -32924,33 +33185,41 @@ async function runRunnerPromptStep(options) {
32924
33185
  logPath: runOptions.logPath,
32925
33186
  preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
32926
33187
  waitForPauseCheckpoint: runOptions.waitForPauseCheckpoint,
33188
+ ...(runOptions.signal ? { signal: runOptions.signal } : {}),
32927
33189
  });
33190
+ (_b = runOptions.signal) === null || _b === void 0 ? void 0 : _b.throwIfAborted();
32928
33191
  stepTracker.finishStep({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs }, result.loginMethod);
32929
33192
  return result;
32930
33193
  }
32931
33194
  /**
32932
- * Runs the verification command, timing it and recording it as a testing step regardless of the outcome, and
32933
- * returns the failure (or `undefined` when the verification passed).
33195
+ * Runs the check command, timing it and recording it as a checking step regardless of the outcome, and
33196
+ * returns the failure (or `undefined` when the check passed).
32934
33197
  */
32935
- async function runVerificationStep(options) {
32936
- const { runPromptTestCommandExecutor, testCommand, runOptions, stepTracker } = options;
32937
- await stepTracker.startStep('testing');
33198
+ async function runCheckStep(options) {
33199
+ var _a, _b, _c;
33200
+ const { runPromptCheckCommandExecutor, checkCommand, runOptions, stepTracker } = options;
33201
+ (_a = runOptions.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
33202
+ await stepTracker.startStep('checking');
32938
33203
  const stepStartedTimeMs = Date.now();
32939
33204
  try {
32940
- await runPromptTestCommandExecutor({
32941
- command: testCommand,
33205
+ await runPromptCheckCommandExecutor({
33206
+ command: checkCommand,
32942
33207
  projectPath: runOptions.projectPath,
32943
- scriptPath: buildPromptTestScriptPath(runOptions.scriptPath),
33208
+ scriptPath: buildPromptCheckScriptPath(runOptions.scriptPath),
32944
33209
  logPath: runOptions.logPath,
32945
33210
  preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
33211
+ ...(runOptions.signal ? { signal: runOptions.signal } : {}),
32946
33212
  });
33213
+ (_b = runOptions.signal) === null || _b === void 0 ? void 0 : _b.throwIfAborted();
32947
33214
  return undefined;
32948
33215
  }
32949
33216
  catch (error) {
33217
+ if (error instanceof CoderCheckSetupError || ((_c = runOptions.signal) === null || _c === void 0 ? void 0 : _c.aborted))
33218
+ throw error;
32950
33219
  return { error };
32951
33220
  }
32952
33221
  finally {
32953
- stepTracker.finishStep({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
33222
+ stepTracker.finishStep({ kind: 'checking', usage: null, durationMs: Date.now() - stepStartedTimeMs });
32954
33223
  }
32955
33224
  }
32956
33225
  /**
@@ -32964,13 +33233,13 @@ async function waitForPromptAttemptPauseCheckpoint(waitForPauseCheckpoint, runne
32964
33233
  }));
32965
33234
  }
32966
33235
  /**
32967
- * Waits for a pause checkpoint immediately before one verification command begins.
33236
+ * Waits for a pause checkpoint immediately before one check command begins.
32968
33237
  */
32969
- async function waitForVerificationPauseCheckpoint(waitForPauseCheckpoint, testCommand, attemptCount) {
33238
+ async function waitForCheckPauseCheckpoint(waitForPauseCheckpoint, checkCommand, attemptCount) {
32970
33239
  await (waitForPauseCheckpoint === null || waitForPauseCheckpoint === void 0 ? void 0 : waitForPauseCheckpoint({
32971
- checkpointLabel: buildVerificationPauseLabel(attemptCount),
33240
+ checkpointLabel: buildCheckPauseLabel(attemptCount),
32972
33241
  phase: 'verifying',
32973
- statusMessage: `Running verification after attempt #${attemptCount}: ${testCommand}`,
33242
+ statusMessage: `Running check after attempt #${attemptCount}: ${checkCommand}`,
32974
33243
  }));
32975
33244
  }
32976
33245
  /**
@@ -32986,51 +33255,53 @@ function buildPromptAttemptStatusMessage(runnerName, attemptCount) {
32986
33255
  return `Calling ${runnerName} (attempt ${attemptCount})`;
32987
33256
  }
32988
33257
  /**
32989
- * Builds the human-readable pause label used before one verification command begins.
33258
+ * Builds the human-readable pause label used before one check command begins.
32990
33259
  */
32991
- function buildVerificationPauseLabel(attemptCount) {
32992
- return `running verification after attempt #${attemptCount}`;
33260
+ function buildCheckPauseLabel(attemptCount) {
33261
+ return `running check after attempt #${attemptCount}`;
32993
33262
  }
32994
33263
  /**
32995
- * Builds one feedback block appended to the next coding attempt after tests fail.
33264
+ * Builds one feedback block appended to the next coding attempt after checks fail.
32996
33265
  */
32997
- function buildVerificationFeedback({ testCommand, failedAttemptCount, verificationOutput, }) {
33266
+ function buildCheckFeedback({ checkCommand, failedAttemptCount, checkOutput, }) {
32998
33267
  const nextAttemptCount = failedAttemptCount + 1;
32999
33268
  return spaceTrim((block) => `
33000
- The previous implementation did not pass the required verification command.
33269
+ The previous implementation did not pass the required check command.
33001
33270
 
33002
- ## Automated verification feedback
33003
- - Retry attempt: ${nextAttemptCount} of ${MAX_PROMPT_TEST_ATTEMPTS}
33004
- - Verification command: \`${testCommand}\`
33005
- - Update the current implementation so the verification command passes without breaking the original task requirements.
33271
+ ## Automated check feedback
33272
+ - Retry attempt: ${nextAttemptCount} of ${MAX_PROMPT_CHECK_ATTEMPTS}
33273
+ - Check command: \`${checkCommand}\`
33274
+ - Update the current implementation so the check command passes without breaking the original task requirements.
33006
33275
 
33007
- ### Verification output
33276
+ ${block(CHECK_REPAIR_INSTRUCTIONS)}
33277
+
33278
+ ### Check output
33008
33279
  \`\`\`
33009
- ${block(verificationOutput)}
33280
+ ${block(checkOutput)}
33010
33281
  \`\`\`
33011
33282
  `);
33012
33283
  }
33013
33284
  /**
33014
- * Builds the final error message written when verification still fails after all retries.
33285
+ * Builds the final error message written when check still fails after all retries.
33015
33286
  */
33016
- function buildFinalVerificationFailureMessage({ promptLabel, testCommand, attemptCount, verificationOutput, }) {
33287
+ function buildFinalCheckFailureMessage({ promptLabel, checkCommand, attemptCount, checkOutput, }) {
33017
33288
  return spaceTrim((block) => `
33018
- Verification command \`${testCommand}\` failed for \`${promptLabel}\` after ${attemptCount} attempts.
33289
+ Check command \`${checkCommand}\` failed for \`${promptLabel}\` after ${attemptCount} attempts.
33019
33290
 
33020
- ### Verification output
33291
+ ### Check output
33021
33292
  \`\`\`
33022
- ${block(verificationOutput)}
33293
+ ${block(checkOutput)}
33023
33294
  \`\`\`
33024
33295
  `);
33025
33296
  }
33026
33297
  /**
33027
- * Derives a dedicated temp-script path for verification commands.
33298
+ * Derives a dedicated temp-script path for check commands.
33028
33299
  */
33029
- function buildPromptTestScriptPath(scriptPath) {
33300
+ function buildPromptCheckScriptPath(scriptPath) {
33030
33301
  if (scriptPath.toLowerCase().endsWith(SHELL_SCRIPT_EXTENSION)) {
33031
- return `${scriptPath.slice(0, -SHELL_SCRIPT_EXTENSION.length)}.test.sh`;
33302
+ return `${scriptPath.slice(0, -SHELL_SCRIPT_EXTENSION.length)}.check.sh`;
33032
33303
  }
33033
- return `${scriptPath}.test.sh`;
33304
+ return `${scriptPath}.check.sh`;
33034
33305
  }
33035
33306
 
33036
33307
  /**
@@ -41930,16 +42201,16 @@ function validateAgentRunOptions(options) {
41930
42201
  * Answers one queued `.book` message thread and moves it to `messages/finished`.
41931
42202
  */
41932
42203
  async function tickAgentMessages(options, tickOptions = {}) {
41933
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
42204
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
41934
42205
  validateAgentRunOptions(options);
41935
- const projectPath = tickOptions.projectPath || process.cwd();
42206
+ const projectPath = (_b = (_a = tickOptions.projectPath) !== null && _a !== void 0 ? _a : options.projectPath) !== null && _b !== void 0 ? _b : process.cwd();
41936
42207
  let queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
41937
42208
  let queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
41938
42209
  if (!queuedMessage) {
41939
42210
  announceNoQueuedMessages(tickOptions);
41940
42211
  return { isMessageProcessed: false };
41941
42212
  }
41942
- if (tickOptions.uiHandle && !((_a = tickOptions.uiPresentation) === null || _a === void 0 ? void 0 : _a.isSharedDashboard)) {
42213
+ if (tickOptions.uiHandle && !((_c = tickOptions.uiPresentation) === null || _c === void 0 ? void 0 : _c.isSharedDashboard)) {
41943
42214
  updateAgentRunUiForPulling(tickOptions.uiHandle, queueSnapshot, 'Pulling latest changes before answering the next message');
41944
42215
  }
41945
42216
  const autoPullTimestamp = await pullLatestChangesForAgentQueueIfEnabled({
@@ -41951,7 +42222,7 @@ async function tickAgentMessages(options, tickOptions = {}) {
41951
42222
  queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
41952
42223
  queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
41953
42224
  if (!queuedMessage) {
41954
- if (tickOptions.uiHandle && !((_b = tickOptions.uiPresentation) === null || _b === void 0 ? void 0 : _b.isSharedDashboard)) {
42225
+ if (tickOptions.uiHandle && !((_d = tickOptions.uiPresentation) === null || _d === void 0 ? void 0 : _d.isSharedDashboard)) {
41955
42226
  updateAgentRunUiForWatching(tickOptions.uiHandle, queueSnapshot);
41956
42227
  }
41957
42228
  announceNoQueuedMessages(tickOptions);
@@ -41970,24 +42241,24 @@ async function tickAgentMessages(options, tickOptions = {}) {
41970
42241
  actualRunnerModel,
41971
42242
  queuedMessage,
41972
42243
  uiHandle,
41973
- isSharedDashboard: (_c = tickOptions.uiPresentation) === null || _c === void 0 ? void 0 : _c.isSharedDashboard,
42244
+ isSharedDashboard: (_e = tickOptions.uiPresentation) === null || _e === void 0 ? void 0 : _e.isSharedDashboard,
41974
42245
  });
41975
- if (!((_d = tickOptions.uiPresentation) === null || _d === void 0 ? void 0 : _d.isSharedDashboard)) {
41976
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(((_e = tickOptions.uiPresentation) === null || _e === void 0 ? void 0 : _e.completedProgressStats) ||
42246
+ if (!((_f = tickOptions.uiPresentation) === null || _f === void 0 ? void 0 : _f.isSharedDashboard)) {
42247
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(((_g = tickOptions.uiPresentation) === null || _g === void 0 ? void 0 : _g.completedProgressStats) ||
41977
42248
  createAgentQueueProgressSnapshot({
41978
42249
  finishedMessageCount: queueSnapshot.finishedMessageCount + 1,
41979
42250
  queuedMessages: removeQueuedMessageFromSnapshot(queueSnapshot, queuedMessage),
41980
42251
  }));
41981
- if ((_f = tickOptions.uiPresentation) === null || _f === void 0 ? void 0 : _f.completedAgentStatusLines) {
42252
+ if ((_h = tickOptions.uiPresentation) === null || _h === void 0 ? void 0 : _h.completedAgentStatusLines) {
41982
42253
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAgentStatusLines([...tickOptions.uiPresentation.completedAgentStatusLines]);
41983
42254
  }
41984
- if ((_g = tickOptions.uiPresentation) === null || _g === void 0 ? void 0 : _g.completedAgentStatusTableRows) {
42255
+ if ((_j = tickOptions.uiPresentation) === null || _j === void 0 ? void 0 : _j.completedAgentStatusTableRows) {
41985
42256
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAgentStatusTableRows([...tickOptions.uiPresentation.completedAgentStatusTableRows]);
41986
42257
  }
41987
- if ((_h = tickOptions.uiPresentation) === null || _h === void 0 ? void 0 : _h.completedMessagePreviewLines) {
42258
+ if ((_k = tickOptions.uiPresentation) === null || _k === void 0 ? void 0 : _k.completedMessagePreviewLines) {
41988
42259
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewLines([...tickOptions.uiPresentation.completedMessagePreviewLines]);
41989
42260
  }
41990
- if ((_j = tickOptions.uiPresentation) === null || _j === void 0 ? void 0 : _j.completedMessagePreviewSections) {
42261
+ if ((_l = tickOptions.uiPresentation) === null || _l === void 0 ? void 0 : _l.completedMessagePreviewSections) {
41991
42262
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewSections([
41992
42263
  ...tickOptions.uiPresentation.completedMessagePreviewSections,
41993
42264
  ]);
@@ -42071,7 +42342,7 @@ async function runQueuedAgentMessage(options) {
42071
42342
  try {
42072
42343
  try {
42073
42344
  promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) => {
42074
- const runResult = await runPromptWithTestFeedback({
42345
+ const runResult = await runPromptWithCheckFeedback({
42075
42346
  runner,
42076
42347
  prompt,
42077
42348
  scriptPath,
@@ -42498,12 +42769,12 @@ const MULTI_AGENT_QUEUE_POLL_INTERVAL_MS = 2000;
42498
42769
  * Watches all direct child agent repositories from the current directory in one shared session.
42499
42770
  */
42500
42771
  async function runMultipleAgentMessages(options, controls = {}) {
42501
- var _a, _b;
42772
+ var _a, _b, _c;
42502
42773
  validateAgentRunOptions(options);
42503
42774
  validateAgentWatchOptions('ptbk agent-folder run-multiple', options);
42504
- const rootPath = process.cwd();
42775
+ const rootPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
42505
42776
  const shouldContinue = controls.shouldContinue || (() => just(true));
42506
- const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : MULTI_AGENT_QUEUE_POLL_INTERVAL_MS;
42777
+ const queuePollIntervalMs = (_b = controls.queuePollIntervalMs) !== null && _b !== void 0 ? _b : MULTI_AGENT_QUEUE_POLL_INTERVAL_MS;
42507
42778
  const watchErrorLogDirectoryPath = controls.watchErrorLogDirectoryPath || rootPath;
42508
42779
  const ignoreMatcher = createAgentIgnoreMatcher(options.ignorePatterns);
42509
42780
  const githubSynchronizer = new MultipleAgentGithubSynchronizer();
@@ -42527,7 +42798,7 @@ async function runMultipleAgentMessages(options, controls = {}) {
42527
42798
  watchErrorLogDirectoryPath,
42528
42799
  });
42529
42800
  isWatchSessionInitialized = true;
42530
- (_b = controls.onUiInitialized) === null || _b === void 0 ? void 0 : _b.call(controls, uiPresenter.uiHandle);
42801
+ (_c = controls.onUiInitialized) === null || _c === void 0 ? void 0 : _c.call(controls, uiPresenter.uiHandle);
42531
42802
  if (!uiPresenter.uiHandle) {
42532
42803
  console.info(colors.green('Watching direct child agent repositories for queued messages.'));
42533
42804
  }
@@ -43786,6 +44057,7 @@ const WORKSPACE_REPOSITORY_DESCRIPTION = spaceTrim$1(`
43786
44057
  * @private internal registration helper for workspace CLI commands
43787
44058
  */
43788
44059
  function addWorkspaceRepositoryOptions(command) {
44060
+ addProjectPathOption(command);
43789
44061
  if (!command.options.some((option) => option.long === '--no-questions'))
43790
44062
  addQuestionsOption(command);
43791
44063
  command.addHelpText('after', `\n${WORKSPACE_REPOSITORY_DESCRIPTION}\n`);
@@ -43887,17 +44159,7 @@ const EXECUTE_FILE = promisify(execFile);
43887
44159
  * @private shared workspace discovery for CLI commands
43888
44160
  */
43889
44161
  async function $resolveWorkspaceRepository(projectDirectory = process.cwd()) {
43890
- const requestedPath = resolve(projectDirectory);
43891
- let projectPath;
43892
- try {
43893
- projectPath = await realpath(requestedPath);
43894
- if (!(await stat(projectPath)).isDirectory()) {
43895
- throw new EnvironmentMismatchError(`The project path \`${requestedPath}\` is not a directory.`);
43896
- }
43897
- }
43898
- catch (error) {
43899
- throw new EnvironmentMismatchError(spaceTrim$1(`Cannot access project directory \`${requestedPath}\`. Check that it exists and is accessible.\n\n${describeWorkspaceError(error)}`));
43900
- }
44162
+ const projectPath = await resolveProjectDirectory(projectDirectory);
43901
44163
  return $inspectWorkspaceRepository(projectPath);
43902
44164
  }
43903
44165
  /**
@@ -44009,7 +44271,7 @@ async function $inspectWorkspaceRepository(projectPath) {
44009
44271
  // Git may skip damaged inner metadata and find a valid parent. Do not silently use that parent
44010
44272
  // for a project whose own checkout needs repair.
44011
44273
  await $assertNoBrokenRepositoryMetadata(projectPath, repositoryRoot);
44012
- const gitDirectory = (await $runWorkspaceGit(projectPath, ['rev-parse', '--absolute-git-dir'])).trim();
44274
+ const gitDirectory = await realpath((await $runWorkspaceGit(projectPath, ['rev-parse', '--absolute-git-dir'])).trim());
44013
44275
  // Also validate the index and metadata, without refreshing the index or depending on an existing commit.
44014
44276
  await $runWorkspaceGit(projectPath, ['status', '--porcelain', '--untracked-files=no']);
44015
44277
  return { projectPath, repositoryRoot, gitDirectory, repositoryStatus: 'reused' };
@@ -44337,10 +44599,15 @@ function $initializeCoderAddCommand(program) {
44337
44599
  addQuestionsOption(command);
44338
44600
  addWorkspaceRepositoryOptions(command);
44339
44601
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
44602
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44340
44603
  const { priority, template: templateOption } = cliOptions;
44341
44604
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44342
44605
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
44343
- const workspace = await $preflightWorkspaceRepository({ policy: 'mutate', ...questionsOptions });
44606
+ const workspace = await $preflightWorkspaceRepository({
44607
+ ...projectOptions,
44608
+ policy: 'mutate',
44609
+ ...questionsOptions,
44610
+ });
44344
44611
  const { projectPath } = workspace;
44345
44612
  const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
44346
44613
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
@@ -44563,7 +44830,9 @@ function $initializeCoderFindFreshEmojiTagCommand(program) {
44563
44830
  `));
44564
44831
  addWorkspaceRepositoryOptions(command);
44565
44832
  command.action(handleActionErrors(async (cliOptions) => {
44833
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44566
44834
  const workspace = await $preflightWorkspaceRepository({
44835
+ ...projectOptions,
44567
44836
  policy: 'read-only',
44568
44837
  isAskingQuestionsEnabled: cliOptions.questions,
44569
44838
  });
@@ -44804,8 +45073,10 @@ function $initializeCoderFindRefactorCandidatesCommand(program) {
44804
45073
  command.option('--limit <candidate-count>', 'Create at most this many refactor prompts, keeping the most important candidates', createPositiveIntegerOptionParser('--limit'));
44805
45074
  addWorkspaceRepositoryOptions(command);
44806
45075
  command.action(handleActionErrors(async (cliOptions) => {
45076
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44807
45077
  const { level = DEFAULT_REFACTOR_CANDIDATE_LEVEL, limit } = cliOptions;
44808
45078
  const workspace = await $preflightWorkspaceRepository({
45079
+ ...projectOptions,
44809
45080
  policy: 'mutate',
44810
45081
  isAskingQuestionsEnabled: cliOptions.questions,
44811
45082
  });
@@ -44839,8 +45110,10 @@ function $initializeCoderFindUnwrittenCommand(program) {
44839
45110
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
44840
45111
  addWorkspaceRepositoryOptions(command);
44841
45112
  command.action(handleActionErrors(async (cliOptions) => {
45113
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44842
45114
  const { priority = 0 } = cliOptions;
44843
45115
  const workspace = await $preflightWorkspaceRepository({
45116
+ ...projectOptions,
44844
45117
  policy: 'read-only',
44845
45118
  isAskingQuestionsEnabled: cliOptions.questions,
44846
45119
  });
@@ -44994,10 +45267,12 @@ function $initializeCoderGenerateBoilerplatesCommand(program) {
44994
45267
  addCoderGitSyncOptions(command);
44995
45268
  addWorkspaceRepositoryOptions(command);
44996
45269
  command.action(handleActionErrors(async (cliOptions) => {
45270
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44997
45271
  const { count: countOption, template: templateOption } = cliOptions;
44998
45272
  const boilerplateCount = parseBoilerplateCount(countOption);
44999
45273
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
45000
45274
  const workspace = await $preflightWorkspaceRepository({
45275
+ ...projectOptions,
45001
45276
  policy: 'mutate',
45002
45277
  isAskingQuestionsEnabled: cliOptions.questions,
45003
45278
  });
@@ -46036,16 +46311,16 @@ const CODER_REFERENCED_ARTIFACT_DEFINITIONS = [
46036
46311
  ...getDefaultCoderProjectPromptTemplateDefinitions().map(createCoderPromptTemplateArtifactDefinition),
46037
46312
  ];
46038
46313
  /**
46039
- * Initializes artifacts referenced by freshly added scripts. Default Books are ensured separately.
46314
+ * Initializes essential context and artifacts referenced by freshly added scripts. Books are ensured separately.
46040
46315
  *
46041
- * For artifacts other than role Books, an existing project script does not cause default files to be created.
46316
+ * AGENTS.md is essential even when every script already exists or uses implicit defaults.
46042
46317
  *
46043
46318
  * @private function of `initializeCoderProjectConfiguration`
46044
46319
  */
46045
46320
  async function ensureCoderReferencedArtifacts(projectPath, referencedArtifactPaths) {
46046
46321
  const ensuredArtifacts = [];
46047
46322
  for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
46048
- if (!referencedArtifactPaths.has(relativeFilePath)) {
46323
+ if (relativeFilePath !== AGENTS_FILE_PATH && !referencedArtifactPaths.has(relativeFilePath)) {
46049
46324
  ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
46050
46325
  continue;
46051
46326
  }
@@ -46664,7 +46939,7 @@ async function ensureCoderGitignoreRules(projectPath, rules) {
46664
46939
  /**
46665
46940
  * Name of the npm script which `ptbk coder run` uses to verify the project.
46666
46941
  */
46667
- const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
46942
+ const CHECK_SCRIPT_NAME = 'check';
46668
46943
  /**
46669
46944
  * Default npm scripts initialized by `ptbk coder init`.
46670
46945
  *
@@ -46684,14 +46959,13 @@ const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS = [
46684
46959
  {
46685
46960
  scriptName: 'coder:plan',
46686
46961
  scriptCommand: 'npx ptbk coder plan --harness openai-codex',
46687
- referencedArtifactPaths: [CODER_PLANNER_AGENT_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
46962
+ referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
46688
46963
  },
46689
46964
  {
46690
46965
  scriptName: 'coder:run',
46691
46966
  scriptCommand: [
46692
46967
  'npx ptbk coder run --harness openai-codex --thinking-level max',
46693
- `--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
46694
- `--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
46968
+ `--check "npm run ${CHECK_SCRIPT_NAME}" --check-before yes-and-fix`,
46695
46969
  ].join(' '),
46696
46970
  referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
46697
46971
  },
@@ -46704,8 +46978,8 @@ const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS = [
46704
46978
  {
46705
46979
  // Note: The verification command of `coder:run` is a project-owned script, so every project can decide
46706
46980
  // what "verified" means without touching the `coder:run` script itself
46707
- scriptName: CODER_TEST_SCRIPT_NAME,
46708
- scriptCommand: 'npm test',
46981
+ scriptName: CHECK_SCRIPT_NAME,
46982
+ scriptCommand: CHECK_SETUP_PLACEHOLDER_COMMAND,
46709
46983
  referencedArtifactPaths: [],
46710
46984
  },
46711
46985
  ];
@@ -46770,12 +47044,13 @@ const DEFAULT_JSON_FILE_NEWLINE = '\n';
46770
47044
  /**
46771
47045
  * Ensures one JSON object field contains the provided string-record entries.
46772
47046
  *
46773
- * Entries which the project already defines are **never** overridden - only missing keys are added,
46774
- * so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
47047
+ * Entries are additive unless a caller explicitly supplies a scoped migration callback.
47048
+ * Hand-tuned scripts and settings survive repeated `ptbk coder init`; migrations recognize generated entries only.
46775
47049
  *
46776
47050
  * @private function of `initializeCoderProjectConfiguration`
46777
47051
  */
46778
- async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, }) {
47052
+ async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, transformExistingEntries, }) {
47053
+ var _a;
46779
47054
  if (ensureParentDirectoryPath) {
46780
47055
  await mkdir(join(projectPath, ensureParentDirectoryPath), { recursive: true });
46781
47056
  }
@@ -46785,7 +47060,7 @@ async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldP
46785
47060
  const jsonObject = fileContent === undefined ? {} : await parseJsonObjectFile(relativeFilePath, fileContent);
46786
47061
  const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
46787
47062
  const addedEntryKeys = [];
46788
- const mergedEntries = { ...existingEntries };
47063
+ const mergedEntries = { ...((_a = (await (transformExistingEntries === null || transformExistingEntries === void 0 ? void 0 : transformExistingEntries(existingEntries)))) !== null && _a !== void 0 ? _a : existingEntries) };
46789
47064
  for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
46790
47065
  if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
46791
47066
  // Note: The project already defines this entry, keep its own value untouched
@@ -46794,7 +47069,12 @@ async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldP
46794
47069
  mergedEntries[entryKey] = entryValue;
46795
47070
  addedEntryKeys.push(entryKey);
46796
47071
  }
46797
- const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
47072
+ for (const entryKey of Object.keys(mergedEntries)) {
47073
+ if (!Object.prototype.hasOwnProperty.call(existingEntries, entryKey) && !addedEntryKeys.includes(entryKey)) {
47074
+ addedEntryKeys.push(entryKey);
47075
+ }
47076
+ }
47077
+ const hasChanges = fileContent === undefined || JSON.stringify(mergedEntries) !== JSON.stringify(existingEntries);
46798
47078
  if (!hasChanges) {
46799
47079
  return { status: 'unchanged', addedEntryKeys };
46800
47080
  }
@@ -46883,6 +47163,301 @@ function isPlainObject$1(value) {
46883
47163
  }
46884
47164
  // Note: [🟡] Code for coder init JSON merging [mergeStringRecordJsonFile](src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts) should never be published outside of `@promptbook/cli`
46885
47165
 
47166
+ /** Old initialized aggregate entries, used only at this migration boundary. */
47167
+ const LEGACY_CHECK_SCRIPT_NAMES = ['test-for-ptbk-coder', 'check-for-ptbk-coder'];
47168
+ /** Conventional project validation scripts, ordered from lightweight checks to tests. */
47169
+ const VALIDATION_SCRIPT_GROUPS = [
47170
+ ['lint', 'test-lint'],
47171
+ ['typecheck', 'type-check', 'type-checking', 'check-types', 'test-types'],
47172
+ ['build', 'test-build'],
47173
+ ];
47174
+ /** Conventional leaf test families used when the project has no usable test aggregate. */
47175
+ const TEST_SCRIPT_GROUPS = [
47176
+ ['test-unit', 'test:unit'],
47177
+ ['test-integration', 'test:integration'],
47178
+ ['test-e2e', 'test:e2e'],
47179
+ ];
47180
+ /** Script names that must never be reached by a generated aggregate. */
47181
+ const NON_VALIDATION_SCRIPT_PATTERN = /^(?:check$|coder:|(?:dev|start|serve|watch|deploy|publish|install|preinstall|postinstall|prepare|reset|clean)(?:$|[:-]))/iu;
47182
+ /** Clearly unsuitable shell bodies are excluded even under a conventional validation name. */
47183
+ 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;
47184
+ /** Npm lifecycle hooks that run implicitly before and after a selected project script. */
47185
+ const SCRIPT_LIFECYCLE_PREFIXES = ['pre', 'post'];
47186
+ /**
47187
+ * Preserves project scripts, creates one canonical check, and migrates only exact generated caller forms.
47188
+ * @private shared package-script preparation for Coder initialization
47189
+ */
47190
+ function prepareCoderPackageJsonScripts(existing, externalCallers = {}) {
47191
+ var _a;
47192
+ const scripts = { ...existing };
47193
+ const migrationInstructions = [];
47194
+ const legacyNames = LEGACY_CHECK_SCRIPT_NAMES.filter((name) => Object.prototype.hasOwnProperty.call(existing, name));
47195
+ let checkScriptSummary;
47196
+ if (Object.prototype.hasOwnProperty.call(existing, 'check')) {
47197
+ checkScriptSummary = 'Preserved project scripts.check exactly; Coder executes npm run check.';
47198
+ }
47199
+ else if (legacyNames.length > 0) {
47200
+ const sourceName = legacyNames[0];
47201
+ const isRecursive = scriptReaches(sourceName, 'check', existing, new Set()) ||
47202
+ scriptReaches(sourceName, sourceName, existing, new Set()) ||
47203
+ scriptReaches(sourceName, 'coder:run', existing, new Set()) ||
47204
+ scriptReaches(sourceName, 'coder:fix', existing, new Set());
47205
+ const conflictingLifecycleNames = SCRIPT_LIFECYCLE_PREFIXES.filter((prefix) => existing[`${prefix}check`] !== undefined &&
47206
+ existing[`${prefix}${sourceName}`] !== existing[`${prefix}check`]).map((prefix) => `${prefix}check`);
47207
+ scripts.check =
47208
+ isRecursive || conflictingLifecycleNames.length > 0
47209
+ ? CHECK_SETUP_PLACEHOLDER_COMMAND
47210
+ : existing[sourceName];
47211
+ if (!isRecursive && conflictingLifecycleNames.length === 0) {
47212
+ // npm runs these hooks implicitly. Preserve their bodies as well as the main script's scope.
47213
+ for (const prefix of SCRIPT_LIFECYCLE_PREFIXES) {
47214
+ const legacyHook = existing[`${prefix}${sourceName}`];
47215
+ if (legacyHook !== undefined)
47216
+ scripts[`${prefix}check`] = legacyHook;
47217
+ }
47218
+ }
47219
+ checkScriptSummary = isRecursive
47220
+ ? `Kept recursive scripts.${sourceName} unchanged; configure scripts.check with validation that does not call check or Coder recursively.`
47221
+ : conflictingLifecycleNames.length > 0
47222
+ ? `Kept scripts.${sourceName} and conflicting ${conflictingLifecycleNames.join(', ')} hooks unchanged; configure scripts.check and its lifecycle hooks to preserve the required validation.`
47223
+ : `Preserved scripts.${sourceName}'s validation command and npm lifecycle hooks in scripts.check: ${scripts.check}`;
47224
+ }
47225
+ else {
47226
+ const selectedScripts = selectValidationScripts(existing);
47227
+ scripts.check =
47228
+ selectedScripts.length > 0
47229
+ ? selectedScripts.map((name) => (name === 'test' ? 'npm test' : `npm run ${name}`)).join(' && ')
47230
+ : CHECK_SETUP_PLACEHOLDER_COMMAND;
47231
+ checkScriptSummary =
47232
+ selectedScripts.length > 0
47233
+ ? `Generated scripts.check from the available validation scripts (${selectedScripts.join(', ')}): ${scripts.check}`
47234
+ : 'No usable validation scripts found. scripts.check contains a failing setup placeholder; configure it before running Coder.';
47235
+ }
47236
+ for (const [name, command] of Object.entries(existing)) {
47237
+ if (['check', ...legacyNames].some((verificationName) => name === verificationName ||
47238
+ SCRIPT_LIFECYCLE_PREFIXES.some((prefix) => name === `${prefix}${verificationName}`)))
47239
+ continue;
47240
+ const migratedCaller = migrateGeneratedCoderRunScript(command);
47241
+ if (migratedCaller !== undefined) {
47242
+ // A different existing check is authoritative, but changing a custom legacy caller's scope is not safe.
47243
+ const referencedLegacyName = legacyNames.find((legacyName) => command.includes(legacyName));
47244
+ const isConflictingCustomCheck = referencedLegacyName !== undefined &&
47245
+ !isLegacyCheckScopePreserved(referencedLegacyName, scripts) &&
47246
+ !isUnchangedLegacyCheckDefault(referencedLegacyName, scripts);
47247
+ if (!isConflictingCustomCheck)
47248
+ scripts[name] = migratedCaller;
47249
+ }
47250
+ }
47251
+ for (const legacyName of legacyNames) {
47252
+ const callers = Object.entries(scripts)
47253
+ .filter(([name, command]) => name !== legacyName && command.includes(legacyName))
47254
+ .map(([name]) => name);
47255
+ const isPreservedScope = isLegacyCheckScopePreserved(legacyName, scripts);
47256
+ const isObsoleteGeneratedDefault = isUnchangedLegacyCheckDefault(legacyName, scripts) && !isCheckSetupRequired(scripts.check);
47257
+ const files = (_a = externalCallers[legacyName]) !== null && _a !== void 0 ? _a : [];
47258
+ if (callers.length === 0 && files.length === 0 && (isPreservedScope || isObsoleteGeneratedDefault)) {
47259
+ delete scripts[legacyName];
47260
+ for (const prefix of SCRIPT_LIFECYCLE_PREFIXES)
47261
+ delete scripts[`${prefix}${legacyName}`];
47262
+ }
47263
+ else {
47264
+ migrationInstructions.push(`Kept scripts.${legacyName}${callers.length ? `; referenced by scripts.${callers.join(', scripts.')}` : ''}${files.length ? `; referenced by ${files.join(', ')}` : ''}${!isPreservedScope && !isObsoleteGeneratedDefault
47265
+ ? '; its custom command or npm lifecycle hooks differ from scripts.check'
47266
+ : ''}. 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.`);
47267
+ }
47268
+ }
47269
+ for (const [name, command] of Object.entries(scripts)) {
47270
+ if (/\b(?:ptbk|promptbook)\s+coder\s+(?:run|server)\b|\brun-codex-prompts\b/u.test(command) &&
47271
+ /--test(?:-before)?(?:\s|=|$)/u.test(command)) {
47272
+ 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}`);
47273
+ }
47274
+ }
47275
+ let isCheckConfigured = true;
47276
+ try {
47277
+ assertProjectCheckScriptsAreConfigured(scripts);
47278
+ }
47279
+ catch (error) {
47280
+ if (!(error instanceof CoderCheckSetupError))
47281
+ throw error;
47282
+ isCheckConfigured = false;
47283
+ checkScriptSummary += ` Additional setup required: ${error.message}`;
47284
+ }
47285
+ return {
47286
+ scripts,
47287
+ checkScriptSummary,
47288
+ migrationInstructions,
47289
+ isCheckConfigured,
47290
+ };
47291
+ }
47292
+ /** Determines whether both the legacy command and its implicit npm validation hooks have been preserved. */
47293
+ function isLegacyCheckScopePreserved(name, scripts) {
47294
+ return (scripts[name] === scripts.check &&
47295
+ SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => scripts[`${prefix}${name}`] === scripts[`${prefix}check`]));
47296
+ }
47297
+ /** Recognizes the generated default only when no custom lifecycle hooks extend its validation scope. */
47298
+ function isUnchangedLegacyCheckDefault(name, scripts) {
47299
+ return (scripts[name] === 'npm test' &&
47300
+ SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => scripts[`${prefix}${name}`] === undefined));
47301
+ }
47302
+ /**
47303
+ * Recognizes unchanged generated run scripts, including the previous explicit Developer/context defaults.
47304
+ * Unknown shell expressions and custom harness configuration are preserved for manual migration.
47305
+ */
47306
+ function migrateGeneratedCoderRunScript(command) {
47307
+ const prefix = 'npx ptbk coder run --harness openai-codex --thinking-level max';
47308
+ const defaultArguments = ['', ' --agent agents/developer.book --context AGENTS.md'];
47309
+ const verificationArguments = [
47310
+ ' --test-before yes-and-fix',
47311
+ ...LEGACY_CHECK_SCRIPT_NAMES.map((name) => ` --test "npm run ${name}" --test-before yes-and-fix`),
47312
+ ...LEGACY_CHECK_SCRIPT_NAMES.map((name) => ` --check "npm run ${name}" --check-before yes-and-fix`),
47313
+ ];
47314
+ const isGenerated = defaultArguments.some((defaults) => verificationArguments.some((verification) => command === prefix + defaults + verification));
47315
+ return isGenerated ? getDefaultCoderPackageJsonScripts()['coder:run'] : undefined;
47316
+ }
47317
+ /** Selects safe conventional scripts, avoiding duplicates already reached by another selected aggregate. */
47318
+ function selectValidationScripts(scripts) {
47319
+ if (SCRIPT_LIFECYCLE_PREFIXES.some((prefix) => scripts[`${prefix}check`] !== undefined &&
47320
+ !isUsableValidationScript(`${prefix}check`, scripts, new Set())))
47321
+ return [];
47322
+ const selected = [];
47323
+ for (const group of VALIDATION_SCRIPT_GROUPS) {
47324
+ const name = group.find((candidate) => isUsableValidationScript(candidate, scripts, new Set()));
47325
+ if (name !== undefined)
47326
+ selected.push(name);
47327
+ }
47328
+ if (isUsableValidationScript('test', scripts, new Set()))
47329
+ selected.push('test');
47330
+ else
47331
+ for (const group of TEST_SCRIPT_GROUPS) {
47332
+ const name = group.find((candidate) => isUsableValidationScript(candidate, scripts, new Set()));
47333
+ if (name !== undefined)
47334
+ selected.push(name);
47335
+ }
47336
+ return selected.filter((name) => !selected.some((other) => other !== name && scriptReaches(other, name, scripts, new Set())));
47337
+ }
47338
+ /** Inspects each body, referenced script, and implicit lifecycle hook before including it in an aggregate. */
47339
+ function isUsableValidationScript(name, scripts, visited) {
47340
+ const command = scripts[name];
47341
+ if (!command ||
47342
+ visited.has(name) ||
47343
+ NON_VALIDATION_SCRIPT_PATTERN.test(name) ||
47344
+ isCheckSetupRequired(command) ||
47345
+ UNSUITABLE_VALIDATION_BODY_PATTERN.test(command))
47346
+ return false;
47347
+ if (command.split('&&').every((part) => /^\s*(?:echo|printf|true|:)(?:\s|$)/iu.test(part)) ||
47348
+ /\bprocess\.exit\(\s*0\s*\)/u.test(command))
47349
+ return false;
47350
+ if (/\b(?:vite|next|astro|webpack-dev-server)\s+(?:dev|start|serve)\b/iu.test(command) ||
47351
+ /(?:^|&&)\s*(?:vite|webpack-dev-server)(?:\s*$|\s+--)/iu.test(command) ||
47352
+ (/\bvitest\b/iu.test(command) && !/\bvitest\s+run\b/iu.test(command)))
47353
+ return false;
47354
+ const nextVisited = new Set([...visited, name]);
47355
+ const references = projectScriptReferences(command);
47356
+ // Ambiguous package-manager flags and cwd switches cannot be composed confidently as root validation.
47357
+ if (/\b(?:npm|pnpm|yarn)\s+(?:--|run(?:-script)?\s+--)/u.test(command) ||
47358
+ (/\b(?:npm|pnpm|yarn)\b/u.test(command) && references.length === 0))
47359
+ return false;
47360
+ if (references.some((reference) => !isUsableValidationScript(reference, scripts, nextVisited)))
47361
+ return false;
47362
+ return SCRIPT_LIFECYCLE_PREFIXES.every((prefix) => {
47363
+ const lifecycleName = `${prefix}${name}`;
47364
+ return scripts[lifecycleName] === undefined || isUsableValidationScript(lifecycleName, scripts, nextVisited);
47365
+ });
47366
+ }
47367
+ /** Checks whether a selected aggregate already reaches another selected script. */
47368
+ function scriptReaches(name, target, scripts, visited) {
47369
+ var _a;
47370
+ if (visited.has(name))
47371
+ return false;
47372
+ const nextVisited = new Set([...visited, name]);
47373
+ const command = (_a = scripts[name]) !== null && _a !== void 0 ? _a : '';
47374
+ if ((target === 'coder:run' && /\b(?:ptbk|promptbook)\s+coder\s+run\b/u.test(command)) ||
47375
+ (target === 'coder:fix' && /\b(?:ptbk|promptbook)\s+coder\s+fix\b/u.test(command)))
47376
+ return true;
47377
+ const references = [
47378
+ ...projectScriptReferences(command),
47379
+ ...SCRIPT_LIFECYCLE_PREFIXES.map((prefix) => `${prefix}${name}`).filter((lifecycleName) => scripts[lifecycleName] !== undefined),
47380
+ ];
47381
+ return references.some((reference) => reference === target || scriptReaches(reference, target, scripts, nextVisited));
47382
+ }
47383
+
47384
+ // cspell:ignore Procfile
47385
+ /** Runtime/dependency/history directories do not contain maintained project callers. */
47386
+ const IGNORED_CALLER_DIRECTORIES = new Set([
47387
+ 'node_modules',
47388
+ '.git',
47389
+ '.promptbook',
47390
+ '.next',
47391
+ '.tmp',
47392
+ 'dist',
47393
+ 'build',
47394
+ 'coverage',
47395
+ ]);
47396
+ /** Only Coder's known task-history paths are excluded; the same names elsewhere can hold active callers. */
47397
+ const IGNORED_CALLER_HISTORY_PATHS = new Set(['prompts/done', 'prompts/traces']);
47398
+ /** Source and workflow files whose references must be preserved during npm script migration. */
47399
+ const CALLER_FILE_PATTERN = /\.(?:[cm]?jsx?|tsx?|json|ya?ml|md|sh|bash|ps1|cmd|bat|py|toml|xml|groovy)$/iu;
47400
+ /** Conventional workflow files which have no filename extension. */
47401
+ const CALLER_FILE_NAMES = new Set(['Makefile', 'Dockerfile', 'Jenkinsfile', 'Procfile']);
47402
+ /**
47403
+ * Finds external project callers conservatively; it never rewrites their shell expressions.
47404
+ * @private migration safety check for the Coder initializer
47405
+ */
47406
+ async function findLegacyCoderCheckCallers(projectPath, legacyNames) {
47407
+ const callers = Object.fromEntries(legacyNames.map((name) => [name, []]));
47408
+ if (legacyNames.length === 0)
47409
+ return callers;
47410
+ /** Scans maintained text files without following symlinks or inspecting runtime state. */
47411
+ async function visit(relativeDirectory) {
47412
+ let entries;
47413
+ try {
47414
+ entries = await readdir(join(projectPath, relativeDirectory), { withFileTypes: true });
47415
+ }
47416
+ catch (_a) {
47417
+ for (const legacyName of legacyNames)
47418
+ callers[legacyName].push(`${relativeDirectory} (unreadable directory; review manually)`);
47419
+ return;
47420
+ }
47421
+ for (const entry of entries) {
47422
+ const relativePath = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
47423
+ if (entry.isSymbolicLink()) {
47424
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name)) {
47425
+ for (const legacyName of legacyNames)
47426
+ callers[legacyName].push(`${relativePath} (symbolic link; review manually)`);
47427
+ }
47428
+ continue;
47429
+ }
47430
+ if (entry.isDirectory()) {
47431
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name) && !IGNORED_CALLER_HISTORY_PATHS.has(relativePath))
47432
+ await visit(relativePath);
47433
+ }
47434
+ else if (entry.isFile() && (CALLER_FILE_PATTERN.test(entry.name) || CALLER_FILE_NAMES.has(entry.name))) {
47435
+ try {
47436
+ let content = await readFile(join(projectPath, relativePath), 'utf-8');
47437
+ if (relativePath === 'package.json') {
47438
+ // Script callers are already handled by the merge. Other project-owned metadata may also
47439
+ // reference a script, so it must be included when deciding whether removal is safe.
47440
+ const packageMetadata = JSON.parse(content);
47441
+ delete packageMetadata.scripts;
47442
+ content = JSON.stringify(packageMetadata);
47443
+ }
47444
+ for (const legacyName of legacyNames) {
47445
+ if (content.includes(legacyName))
47446
+ callers[legacyName].push(relativePath === 'package.json' ? 'package.json (outside scripts)' : relativePath);
47447
+ }
47448
+ }
47449
+ catch (_b) {
47450
+ // An unreadable file cannot prove that deletion is safe. Keep the entry and explain why.
47451
+ for (const legacyName of legacyNames)
47452
+ callers[legacyName].push(`${relativePath} (unreadable; review manually)`);
47453
+ }
47454
+ }
47455
+ }
47456
+ }
47457
+ await visit('');
47458
+ return callers;
47459
+ }
47460
+
46886
47461
  /**
46887
47462
  * Relative path to `package.json` in the initialized project.
46888
47463
  */
@@ -46890,17 +47465,30 @@ const PACKAGE_JSON_FILE_PATH = 'package.json';
46890
47465
  /**
46891
47466
  * Ensures `package.json` contains the standalone Promptbook coder helper scripts.
46892
47467
  *
46893
- * Scripts which the project already defines are kept as they are, only missing ones are added.
47468
+ * Project definitions are preserved; missing scripts and narrowly recognized generated migrations are applied.
46894
47469
  *
46895
47470
  * @private function of `initializeCoderProjectConfiguration`
46896
47471
  */
46897
47472
  async function ensureCoderPackageJsonFile(projectPath) {
46898
- return mergeStringRecordJsonFile({
47473
+ let preparation;
47474
+ const mergedFile = await mergeStringRecordJsonFile({
46899
47475
  projectPath,
46900
47476
  relativeFilePath: PACKAGE_JSON_FILE_PATH,
46901
47477
  fieldPath: 'scripts',
46902
47478
  nextEntries: getDefaultCoderPackageJsonScripts(),
47479
+ transformExistingEntries: async (entries) => {
47480
+ const legacyNames = Object.keys(entries).filter((name) => name === 'test-for-ptbk-coder' || name === 'check-for-ptbk-coder');
47481
+ const externalCallers = await findLegacyCoderCheckCallers(projectPath, legacyNames);
47482
+ preparation = prepareCoderPackageJsonScripts(entries, externalCallers);
47483
+ return preparation.scripts;
47484
+ },
46903
47485
  });
47486
+ return {
47487
+ ...mergedFile,
47488
+ checkScriptSummary: preparation.checkScriptSummary,
47489
+ migrationInstructions: preparation.migrationInstructions,
47490
+ isCheckConfigured: preparation.isCheckConfigured,
47491
+ };
46904
47492
  }
46905
47493
  // Note: [🟡] Code for coder init package.json bootstrapping [ensureCoderPackageJsonFile](src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts) should never be published outside of `@promptbook/cli`
46906
47494
 
@@ -47194,9 +47782,9 @@ const PROMPTS_README_TEMPLATE = spaceTrim$1(`
47194
47782
 
47195
47783
  Runner-written completion lines may attribute implementation to a harness and model, include
47196
47784
  reasoning/authentication labels, attempts or interrupted/continued history, and report implementation,
47197
- testing, fixing, or total cost/time information. Those are observed run metadata, not instructions, budgets,
47198
- or a frozen list of supported models. A completed status alone does not prove tests ran: direct runs only
47199
- execute an automated verification command when configured. Preserve actual measurements and record unknown
47785
+ checking, fixing, or total cost/time information. Those are observed run metadata, not instructions, budgets,
47786
+ or a frozen list of supported models. A completed status alone does not prove checks ran: direct runs only
47787
+ execute the project check command when configured. Preserve actual measurements and record unknown
47200
47788
  results or checks that were not run honestly. Manual users must not invent cost/time figures, copy another
47201
47789
  run's attribution, or mark unverified work as verified. The textual metadata rows above demonstrate syntax
47202
47790
  only; use them only when the described events actually happened.
@@ -47247,9 +47835,9 @@ const PROMPTS_README_TEMPLATE = spaceTrim$1(`
47247
47835
  | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47248
47836
  | \`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. |
47249
47837
  | \`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. |
47250
- | \`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. |
47838
+ | \`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. |
47251
47839
  | \`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. |
47252
- | \`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. |
47840
+ | \`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. |
47253
47841
  | \`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. |
47254
47842
  | \`ptbk coder find-unwritten\` | Lists pending sections still containing authoring placeholders; not-ready drafts are not included. |
47255
47843
  | \`ptbk coder generate-boilerplates\` | Adds not-ready starter sections using reusable templates; author and review them before making them pending. |
@@ -47261,11 +47849,18 @@ const PROMPTS_README_TEMPLATE = spaceTrim$1(`
47261
47849
  pulling require the corresponding explicit options. Consult \`ptbk coder --help\` and the relevant subcommand's
47262
47850
  \`--help\` for the installed version's full options.
47263
47851
 
47264
- The initialized \`npm run coder:run\` script supplies \`AGENTS.md\` as context, selects the Codex harness, and
47265
- uses \`npm run test-for-ptbk-coder\` for checks (including pre-run repair mode). The new check script delegates
47266
- to \`npm test\`; configure real tests for your project before running it. Existing npm scripts are preserved, so
47267
- read your \`package.json\` to see your actual commands. Direct \`ptbk coder run\` does not inherit npm-script
47268
- flags.
47852
+ The initialized \`npm run coder:run\` script selects the Codex harness and uses
47853
+ \`--check "npm run check" --check-before yes-and-fix\`. Developer, the current project directory, and its
47854
+ \`AGENTS.md\` are shared CLI defaults. A **check** covers the project's chosen tests, linting, typechecking,
47855
+ builds, generated-code consistency, and other validation. Tests remain one kind of check.
47856
+ Init preserves an existing \`scripts.check\` exactly, or composes applicable existing validation scripts and
47857
+ reports the selected scope. If none are usable, it creates a failing setup placeholder: configure real
47858
+ validation before running Coder. Missing checks or setup placeholders stop with setup guidance, without model repairs.
47859
+ Direct \`ptbk coder run\` defaults to \`--check-before no\`; checks after prompts remain opt-in. Enabled
47860
+ preflight without an explicit command uses \`npm run check\`, including during repair verification.
47861
+ Replace retired \`--test\` / \`--test-before\` with \`--check\` / \`--check-before\`. Init migrates recognized
47862
+ generated scripts and preserves custom legacy validation in \`check\`. Conflicting definitions or custom callers
47863
+ are preserved with manual migration instructions; review them before removing a legacy script.
47269
47864
 
47270
47865
  ### Book agent, harness, and model
47271
47866
 
@@ -47277,9 +47872,20 @@ const PROMPTS_README_TEMPLATE = spaceTrim$1(`
47277
47872
  Init creates local Developer and Planner Books in \`agents/developer.book\` and \`agents/planner.book\`, their
47278
47873
  Lawyer and Copywriter TEAM advisers, and shared \`agents/.core/adam.book\` instructions. Books can
47279
47874
  inherit/import instructions; TEAM entries expose advisers for relevant consultations, not an obligation to
47280
- invoke everyone. \`run\` and \`server\` default to Developer; \`plan\` defaults to Planner.
47281
- \`--agent agents/my-developer.book\` overrides the Book only. \`list\` has no default Book filter. Selecting
47282
- Developer for \`plan\` still permits planning only.
47875
+ invoke everyone. \`run\`, \`server\` and \`plan\` default to Developer.
47876
+ \`--agent ./agents/planner.book\` selects Planner explicitly; another Book path also works. \`list\` has no
47877
+ default Book filter. Every Book used in \`plan\`, including Developer, still permits planning only.
47878
+
47879
+ Project commands default to the current directory captured when invoked. \`--path\` selects another existing
47880
+ directory, relative to that invocation or absolute; an enclosing Git repository never relocates the project's
47881
+ Books, PRDs or context. Additional context defaults to the actual UTF-8 contents of that project's \`AGENTS.md\`.
47882
+ A missing implicit file warns and continues; an unreadable file fails. \`--context\` replaces the default with
47883
+ inline instructions or a file resolved relative to the selected project; \`--context ""\` disables it.
47884
+ Invalid explicit Books and missing/unreadable explicit context files fail without falling back.
47885
+ Thus \`ptbk coder run --harness openai-codex\` and
47886
+ \`ptbk coder run --harness openai-codex --agent ./agents/developer.book --path . --context ./AGENTS.md\` select
47887
+ the same inputs. New npm scripts rely on these defaults; init still creates missing Books and AGENTS.md even
47888
+ when every script already exists, preserving existing custom scripts, settings and context.
47283
47889
 
47284
47890
  Edit local Books to reflect this project's rules; repeat init preserves customizations and adds missing
47285
47891
  defaults/helper references. An invalid explicit Book selection fails rather than silently changing roles.
@@ -47350,7 +47956,7 @@ async function initializeCoderProjectConfiguration(projectPath, onStepCompleted)
47350
47956
  const adamArtifact = defaultAgentArtifacts.find(({ relativeFilePath }) => relativeFilePath === adamRelativeFilePath);
47351
47957
  const { envFileStatus, initializedEnvVariableNames } = await completeStep('.env', ensureCoderEnvFile(projectPath));
47352
47958
  const gitignoreFileStatus = await completeStep('.gitignore', ensureCoderGitignoreFile(projectPath));
47353
- const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await completeStep('package.json', ensureCoderPackageJsonFile(projectPath));
47959
+ const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames, checkScriptSummary, migrationInstructions, isCheckConfigured, } = await completeStep('package.json', ensureCoderPackageJsonFile(projectPath));
47354
47960
  const vscodeSettingsFileStatus = await completeStep('.vscode/settings.json', ensureCoderVscodeSettingsFile(projectPath));
47355
47961
  const referencedArtifactStatuses = [
47356
47962
  ...defaultAgentArtifacts.filter(({ relativeFilePath }) => relativeFilePath !== adamRelativeFilePath),
@@ -47371,6 +47977,9 @@ async function initializeCoderProjectConfiguration(projectPath, onStepCompleted)
47371
47977
  addedPackageJsonScriptNames,
47372
47978
  referencedArtifactStatuses,
47373
47979
  initializedEnvVariableNames,
47980
+ checkScriptSummary,
47981
+ migrationInstructions,
47982
+ isCheckConfigured,
47374
47983
  };
47375
47984
  }
47376
47985
  // Note: [🟡] Code for coder init project bootstrapping [initializeCoderProjectConfiguration](src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts) should never be published outside of `@promptbook/cli`
@@ -47382,9 +47991,11 @@ async function initializeCoderProjectConfiguration(projectPath, onStepCompleted)
47382
47991
  */
47383
47992
  function printInitializationSummary(summary, workspace) {
47384
47993
  const isUnresolved = summary.adamAgentFileStatus === 'unresolved' ||
47994
+ !summary.isCheckConfigured ||
47995
+ summary.migrationInstructions.length > 0 ||
47385
47996
  summary.referencedArtifactStatuses.some(({ status }) => status === 'unresolved');
47386
47997
  console.info(isUnresolved
47387
- ? colors.yellow('Promptbook coder initialization has unresolved artifacts.')
47998
+ ? colors.yellow('Promptbook coder configuration setup: additional setup is required.')
47388
47999
  : colors.green('Promptbook coder configuration setup:'));
47389
48000
  if (workspace)
47390
48001
  console.info(colors.gray(`✔ Git repository: ${workspace.repositoryStatus} (${workspace.repositoryRoot})`));
@@ -47413,6 +48024,9 @@ function printInitializationSummary(summary, workspace) {
47413
48024
  else {
47414
48025
  printInitializationNote('Required coder env variables are already present.', colors.gray);
47415
48026
  }
48027
+ console.info((summary.isCheckConfigured ? colors.cyan : colors.yellow)(summary.checkScriptSummary));
48028
+ for (const instruction of summary.migrationInstructions)
48029
+ console.info(colors.yellow(`! ${instruction}`));
47416
48030
  }
47417
48031
  /**
47418
48032
  * Formats one initialization status into a human-readable label.
@@ -47494,7 +48108,7 @@ function $initializeCoderInitCommand(program) {
47494
48108
  - Adds missing local Lawyer and Copywriter TEAM references to Developer and Planner, preserving existing content
47495
48109
  - Helpers advise on relevant tasks; declaring TEAM does not run them for every task
47496
48110
  - Reports created, augmented, unchanged and unresolved Books; invalid or conflicting files are left untouched
47497
- - Other referenced files, like coder:run context, are created with their newly added scripts
48111
+ - Missing AGENTS.md is initialized independently of scripts; existing content is preserved
47498
48112
 
47499
48113
  Ensures required coding-agent environment variables in .env:
47500
48114
  - CODING_AGENT_GIT_NAME
@@ -47512,9 +48126,14 @@ function $initializeCoderInitCommand(program) {
47512
48126
  addQuestionsOption(command);
47513
48127
  addWorkspaceRepositoryOptions(command);
47514
48128
  command.action(handleActionErrors(async (cliOptions) => {
48129
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47515
48130
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
47516
48131
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
47517
- const workspace = await $preflightWorkspaceRepository({ policy: 'initialize', ...questionsOptions });
48132
+ const workspace = await $preflightWorkspaceRepository({
48133
+ ...projectOptions,
48134
+ policy: 'initialize',
48135
+ ...questionsOptions,
48136
+ });
47518
48137
  const { projectPath } = workspace;
47519
48138
  const completedSteps = [`Git repository ${workspace.repositoryStatus}: ${workspace.repositoryRoot}`];
47520
48139
  try {
@@ -47628,6 +48247,11 @@ const CODER_DEFAULT_AGENT_BOOK_PATHS = {
47628
48247
  developer: CODER_DEVELOPER_AGENT_FILE_PATH,
47629
48248
  planner: CODER_PLANNER_AGENT_FILE_PATH,
47630
48249
  };
48250
+ /**
48251
+ * Shared persona for optional single-agent execution and authoring; capabilities belong to the command.
48252
+ * @private internal constant of project CLI commands
48253
+ */
48254
+ const DEFAULT_CODER_AGENT_ROLE = 'developer';
47631
48255
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
47632
48256
 
47633
48257
  /**
@@ -47647,6 +48271,14 @@ function addCoderAgentOption(command, defaultRole) {
47647
48271
  : 'No default Book filter; omitting --agent includes tasks for all agents';
47648
48272
  command.option('--agent <agent-book-path>', `${CODER_AGENT_OPTION_DESCRIPTION}. ${defaultDescription}`);
47649
48273
  }
48274
+ /**
48275
+ * Registers the shared optional persona and context for single-agent execution or authoring.
48276
+ * @private internal CLI registration helper
48277
+ */
48278
+ function addCoderExecutionOptions(command) {
48279
+ addCoderAgentOption(command, DEFAULT_CODER_AGENT_ROLE);
48280
+ addProjectContextOption(command);
48281
+ }
47650
48282
  // Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
47651
48283
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
47652
48284
 
@@ -47675,9 +48307,13 @@ function $initializeCoderListCommand(program) {
47675
48307
  addPromptPriorityOptions(command);
47676
48308
  addWorkspaceRepositoryOptions(command);
47677
48309
  command.action(handleActionErrors(async (cliOptions) => {
48310
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47678
48311
  const { agent, priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
47679
48312
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
47680
- const workspace = await $preflightWorkspaceRepository({ policy: 'read-only' });
48313
+ const workspace = await $preflightWorkspaceRepository({
48314
+ ...projectOptions,
48315
+ policy: 'read-only',
48316
+ });
47681
48317
  const resolvedCoderAgentBook = agent === undefined
47682
48318
  ? undefined
47683
48319
  : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; })).resolveCoderAgentBook(agent, workspace.projectPath);
@@ -47818,12 +48454,17 @@ function $initializeCoderPingCommand(program) {
47818
48454
  `));
47819
48455
  addWorkspaceRepositoryOptions(command);
47820
48456
  command.action(handleActionErrors(async (cliOptions) => {
48457
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47821
48458
  const { period: periodValue } = cliOptions;
47822
48459
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
47823
48460
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
47824
48461
  // Note: The period is validated before the harness installation check, so a mistyped duration fails fast
47825
48462
  const periodMs = parseOptionalPeriodDuration('--period', periodValue);
47826
- const workspace = await $preflightWorkspaceRepository({ policy: 'mutate', ...questionsOptions });
48463
+ const workspace = await $preflightWorkspaceRepository({
48464
+ ...projectOptions,
48465
+ policy: 'mutate',
48466
+ ...questionsOptions,
48467
+ });
47827
48468
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
47828
48469
  await $ensureCoderHarnessGitignoreRules(workspace.projectPath, runnerOptions.agentName, questionsOptions);
47829
48470
  const pingOptions = {
@@ -47855,10 +48496,10 @@ function $initializeCoderPingCommand(program) {
47855
48496
  */
47856
48497
  function $initializeCoderPlanCommand(program) {
47857
48498
  const command = program.command('plan').description(spaceTrim(`
47858
- Discuss features with the project's Planner and review PRDs before saving them.
48499
+ Discuss features with the project's Developer Book and review PRDs before saving them.
47859
48500
 
47860
- Run ptbk coder init first. Default Book: agents/planner.book; --agent overrides the Book,
47861
- independently of --harness and --model. Currently supports openai-codex with restricted tools.
48501
+ Run ptbk coder init first. Default Book: agents/developer.book; --agent overrides the Book,
48502
+ independently of --harness and --model. Use --agent ./agents/planner.book for Planner. Currently supports openai-codex with restricted tools.
47862
48503
  Requires an interactive terminal and a Codex version supporting --ignore-user-config and --ignore-rules.
47863
48504
  Codex configuration, plugins, hooks and native tools are isolated; --model default uses Codex's built-in default.
47864
48505
  /save applies reviewed proposals, /draft saves [-] drafts, /discard drops proposals, /exit ends.
@@ -47867,11 +48508,12 @@ function $initializeCoderPlanCommand(program) {
47867
48508
  `));
47868
48509
  addPromptRunnerSelectionOptions(command);
47869
48510
  addPromptRunnerRuntimeOptions(command);
47870
- addCoderAgentOption(command, 'planner');
48511
+ addCoderExecutionOptions(command);
47871
48512
  addCoderGitSyncOptions(command);
47872
48513
  command.option('--template <path>', 'PRD template (defaults to the project-owned prompts/templates/common.md when present)');
47873
48514
  addWorkspaceRepositoryOptions(command);
47874
48515
  command.action(handleActionErrors(async (cliOptions) => {
48516
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47875
48517
  const { createPlanningTerminal } = await Promise.resolve().then(function () { return createPlanningTerminal$1; });
47876
48518
  const { runPlanningSession } = await Promise.resolve().then(function () { return runPlanningSession$1; });
47877
48519
  const { resolvePlanningPath } = await Promise.resolve().then(function () { return resolvePlanningPath$1; });
@@ -47882,12 +48524,18 @@ function $initializeCoderPlanCommand(program) {
47882
48524
  assertPlanningHarnessSupported(options.agentName);
47883
48525
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
47884
48526
  const workspace = await $preflightWorkspaceRepository({
48527
+ ...projectOptions,
47885
48528
  policy: 'mutate',
47886
48529
  isAskingQuestionsEnabled: cliOptions.questions,
47887
48530
  });
47888
48531
  if (cliOptions.questions === false) {
47889
48532
  throw new NotAllowed(spaceTrim('`ptbk coder plan` requires an interactive conversation and review. Run it without `--no-questions` in a terminal.'));
47890
48533
  }
48534
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
48535
+ const projectContext = await resolveCoderProjectContext({
48536
+ ...cliOptions,
48537
+ projectPath: workspace.projectPath,
48538
+ });
47891
48539
  const terminal = createPlanningTerminal();
47892
48540
  try {
47893
48541
  const { projectPath } = workspace;
@@ -47900,6 +48548,8 @@ function $initializeCoderPlanCommand(program) {
47900
48548
  ...options,
47901
48549
  projectPath,
47902
48550
  agent: cliOptions.agent,
48551
+ context: cliOptions.context,
48552
+ projectContext,
47903
48553
  template: cliOptions.template,
47904
48554
  preexistingChangedPaths: gitSync.isCommitEnabled
47905
48555
  ? new Set(Array.from(commitScope.snapshotBeforeOperation.changedFileHashes.keys(), (path) => relative(projectPath, join(workspace.repositoryRoot, path)).replace(/\\/gu, '/')))
@@ -48089,30 +48739,20 @@ async function $assertSufficientFreeDiskSpace(inspectedPath) {
48089
48739
  }
48090
48740
  // 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`
48091
48741
 
48092
- /**
48093
- * Modes supported by `ptbk coder run --test-before`.
48094
- */
48095
- const TEST_BEFORE_MODE_VALUES = ['no', 'yes-and-fail', 'yes-and-fix'];
48096
- /**
48097
- * Default verification command used when a pre-coding mode is enabled without an explicit `--test` command.
48098
- */
48099
- const DEFAULT_CODER_TEST_COMMAND = 'npm test';
48100
- /**
48101
- * Checks whether a value is a supported `--test-before` mode.
48102
- */
48103
- function isTestBeforeMode(value) {
48104
- return TEST_BEFORE_MODE_VALUES.includes(value);
48105
- }
48106
-
48107
48742
  /**
48108
48743
  * Validates cross-flag constraints before workspace setup or the run starts.
48109
48744
  * @private shared validation for Coder execution commands
48110
48745
  */
48111
48746
  function validateCoderRunOptions(options) {
48112
48747
  var _a;
48113
- if (!isTestBeforeMode((_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no')) {
48748
+ if (options.checkCommand !== undefined && !options.checkCommand.trim()) {
48114
48749
  throw new NotAllowed(spaceTrim$1(`
48115
- Invalid ${'`--test-before`'} mode: \`${String(options.testBefore)}\`.
48750
+ Option \`--check\` requires a non-empty shell command, for example \`npm run check\`.
48751
+ `));
48752
+ }
48753
+ if (!isCheckBeforeMode((_a = options.checkBefore) !== null && _a !== void 0 ? _a : 'no')) {
48754
+ throw new NotAllowed(spaceTrim$1(`
48755
+ Invalid ${'`--check-before`'} mode: \`${String(options.checkBefore)}\`.
48116
48756
 
48117
48757
  Use one of: \`no\`, \`yes-and-fail\`, \`yes-and-fix\`.
48118
48758
  `));
@@ -48150,9 +48790,9 @@ function validateCoderRunOptions(options) {
48150
48790
  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.
48151
48791
  `));
48152
48792
  }
48153
- if (options.gitChanges === 'continue' && options.testBefore === 'yes-and-fix') {
48793
+ if (options.gitChanges === 'continue' && options.checkBefore === 'yes-and-fix') {
48154
48794
  throw new NotAllowed(spaceTrim$1(`
48155
- Flag \`--git-changes continue\` cannot be combined with \`--test-before yes-and-fix\`.
48795
+ Flag \`--git-changes continue\` cannot be combined with \`--check-before yes-and-fix\`.
48156
48796
 
48157
48797
  An interrupted prompt already has changes in progress, so there is no unmodified project state for pre-coding verification to repair.
48158
48798
  `));
@@ -48199,9 +48839,9 @@ const NODE_MODULES_DIRECTORY_NAME = 'node_modules';
48199
48839
  *
48200
48840
  * @private internal utility of `promptbookCli`
48201
48841
  */
48202
- async function $resolvePromptbookCliInstallations() {
48842
+ async function $resolvePromptbookCliInstallations(projectPath = process.cwd()) {
48203
48843
  const [localInstallations, globalInstallations] = await Promise.all([
48204
- $resolveLocalPromptbookCliInstallations(),
48844
+ $resolveLocalPromptbookCliInstallations(projectPath),
48205
48845
  $resolveGlobalPromptbookCliInstallations(),
48206
48846
  ]);
48207
48847
  return [...localInstallations, ...globalInstallations];
@@ -48209,8 +48849,8 @@ async function $resolvePromptbookCliInstallations() {
48209
48849
  /**
48210
48850
  * Finds the closest ancestor `package.json` which directly declares a Promptbook CLI package.
48211
48851
  */
48212
- async function $resolveLocalPromptbookCliInstallations() {
48213
- let candidateProjectPath = resolve(process.cwd());
48852
+ async function $resolveLocalPromptbookCliInstallations(projectPath) {
48853
+ let candidateProjectPath = resolve(projectPath);
48214
48854
  let isFileSystemRootReached = false;
48215
48855
  while (!isFileSystemRootReached) {
48216
48856
  const projectPackageJson = await $readJsonObject(join(candidateProjectPath, PACKAGE_JSON_FILENAME));
@@ -48366,8 +49006,8 @@ function isDefined(value) {
48366
49006
  *
48367
49007
  * @private internal utility of `promptbookCli`
48368
49008
  */
48369
- async function $checkPromptbookCliInstallations() {
48370
- const installations = await $resolvePromptbookCliInstallations();
49009
+ async function $checkPromptbookCliInstallations(projectPath = process.cwd()) {
49010
+ const installations = await $resolvePromptbookCliInstallations(projectPath);
48371
49011
  if (installations.length === 0) {
48372
49012
  return [];
48373
49013
  }
@@ -48422,10 +49062,10 @@ function buildPromptbookCliInstallCommand(installation) {
48422
49062
  * @returns `true` when the package update succeeds, `false` when it fails
48423
49063
  * @private internal utility of `promptbookCli`
48424
49064
  */
48425
- async function $updatePromptbookCliInstallation(status) {
49065
+ async function $updatePromptbookCliInstallation(status, projectPath = process.cwd()) {
48426
49066
  const { npmPackageName, installationLocation } = status.installation;
48427
49067
  const installCommand = buildPromptbookCliInstallCommand(status.installation);
48428
- const updateWorkingDirectory = installationLocation === 'global' ? process.cwd() : status.installation.projectPath;
49068
+ const updateWorkingDirectory = installationLocation === 'global' ? projectPath : status.installation.projectPath;
48429
49069
  console.info(colors.cyan(`Updating Promptbook CLI package \`${npmPackageName}\` with \`${installCommand}\`...`));
48430
49070
  try {
48431
49071
  await $execCommand({
@@ -48495,7 +49135,7 @@ function formatPromptbookCliInstallationLocation(installationLocation) {
48495
49135
  * @returns `true` when at least one installation was updated and the current coder run should stop
48496
49136
  * @private internal utility of `promptbookCli`
48497
49137
  */
48498
- async function $ensurePromptbookCliInstallations(questionsOptions) {
49138
+ async function $ensurePromptbookCliInstallations(questionsOptions, projectPath = process.cwd()) {
48499
49139
  if (!questionsOptions.isAskingQuestionsEnabled) {
48500
49140
  // Note: `--no-questions` forbids asking for the approval, so the registry checks would be useless
48501
49141
  return false;
@@ -48504,7 +49144,7 @@ async function $ensurePromptbookCliInstallations(questionsOptions) {
48504
49144
  // Note: Non-interactive runs cannot approve an update and should not wait for registry checks
48505
49145
  return false;
48506
49146
  }
48507
- const statuses = await $checkPromptbookCliInstallations();
49147
+ const statuses = await $checkPromptbookCliInstallations(projectPath);
48508
49148
  const outdatedStatuses = statuses.filter(({ installationState }) => installationState === 'outdated');
48509
49149
  if (outdatedStatuses.length === 0) {
48510
49150
  reportPromptbookCliInstallationStatuses(statuses);
@@ -48518,7 +49158,7 @@ async function $ensurePromptbookCliInstallations(questionsOptions) {
48518
49158
  }
48519
49159
  const updateResults = [];
48520
49160
  for (const status of outdatedStatuses) {
48521
- updateResults.push(await $updatePromptbookCliInstallation(status));
49161
+ updateResults.push(await $updatePromptbookCliInstallation(status, projectPath));
48522
49162
  }
48523
49163
  const isAnyInstallationUpdated = updateResults.some(Boolean);
48524
49164
  const isEveryInstallationUpdated = updateResults.every(Boolean);
@@ -48563,6 +49203,89 @@ function reportManualPromptbookCliUpdateCommands(outdatedStatuses) {
48563
49203
  }
48564
49204
  // 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`
48565
49205
 
49206
+ /** Standalone shell composition tokens remain operators when a command is supplied as several arguments. */
49207
+ const SHELL_COMPOSITION_TOKENS = new Set([
49208
+ '&&',
49209
+ '||',
49210
+ '|',
49211
+ '|&',
49212
+ '&',
49213
+ ';',
49214
+ '(',
49215
+ ')',
49216
+ '>',
49217
+ '>>',
49218
+ '<',
49219
+ '2>',
49220
+ '2>>',
49221
+ '2>&1',
49222
+ ]);
49223
+ /**
49224
+ * Retired aggregate option spellings and their canonical replacements, used only for migration diagnostics.
49225
+ * @private shared CLI migration definitions for Coder execution
49226
+ */
49227
+ const LEGACY_CODER_CHECK_OPTIONS = [
49228
+ ['--test', '--check'],
49229
+ ['--test-before', '--check-before'],
49230
+ ];
49231
+ /**
49232
+ * Registers the shared project check command and rejects retired aggregate-test spellings.
49233
+ * @private shared CLI registration for Coder execution
49234
+ */
49235
+ function addCoderCheckOptions(command, isCheckBeforeSupported) {
49236
+ command.option('--check <check-command...>', 'Run project checks after each prompt; quote the full command when it contains flags or shell composition');
49237
+ if (isCheckBeforeSupported) {
49238
+ command.addOption(new Option('--check-before <mode>', `Check before coding: ${CHECK_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm run check when --check is omitted)`)
49239
+ .choices([...CHECK_BEFORE_MODE_VALUES])
49240
+ .default('no'));
49241
+ }
49242
+ // These diagnostic-only options never forward a value or start execution.
49243
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
49244
+ command.addOption(new Option(`${legacyFlag} [value...]`).hideHelp());
49245
+ command.on(`option:${legacyFlag.slice(2)}`, () => {
49246
+ command.error(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
49247
+ });
49248
+ }
49249
+ }
49250
+ /**
49251
+ * Builds the same actionable migration diagnostic for registered and direct-script entrypoints.
49252
+ * @private shared CLI diagnostic for Coder execution
49253
+ */
49254
+ function formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag) {
49255
+ return `Aggregate verification option \`${legacyFlag}\` was renamed to \`${replacementFlag}\`. Update your command and package.json scripts.`;
49256
+ }
49257
+ /**
49258
+ * Normalizes shell command tokens without parsing a quoted command again or losing spaced argument boundaries.
49259
+ * @private shared command normalization for Coder execution
49260
+ */
49261
+ function normalizeCheckCommandOption(value) {
49262
+ var _a, _b;
49263
+ if (value === undefined)
49264
+ return undefined;
49265
+ const parts = typeof value === 'string' ? [value] : value;
49266
+ const firstPart = (_b = (_a = parts[0]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
49267
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
49268
+ if (firstPart === legacyFlag || firstPart.startsWith(`${legacyFlag}=`)) {
49269
+ throw new NotAllowed(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
49270
+ }
49271
+ }
49272
+ const command = parts.length === 1
49273
+ ? parts[0].trim()
49274
+ : parts
49275
+ .map((part) => !SHELL_COMPOSITION_TOKENS.has(part) && (!part || /[\s'"\\$`;&|<>()*?]/u.test(part))
49276
+ ? quoteBashArgument(part)
49277
+ : part)
49278
+ .join(' ')
49279
+ .trim();
49280
+ if (!command || !firstPart || firstPart.startsWith('-')) {
49281
+ throw new NotAllowed(spaceTrim$1(`
49282
+ Option \`--check\` requires a non-empty shell command, for example \`--check "npm run check"\`.
49283
+ `));
49284
+ }
49285
+ return command;
49286
+ }
49287
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
49288
+
48566
49289
  /**
48567
49290
  * Prints why a coder run has ended to the terminal.
48568
49291
  *
@@ -48609,20 +49332,16 @@ function $initializeCoderRunCommand(program) {
48609
49332
  - Offers to add missing project-local ignore rules for the selected harness
48610
49333
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
48611
49334
  - Supports GPG signing of commits
48612
- - Optional pre-coding test run that can stop or repair pre-existing failures
48613
- - Optional post-prompt verification with test-feedback retries
49335
+ - Optional pre-coding check run that can stop or repair pre-existing failures
49336
+ - Optional post-prompt checks with check-feedback retries
48614
49337
  - Progress tracking and interactive P/S/X terminal controls; O changes only the dashboard output view
48615
49338
  - Dry-run mode to preview prompts
48616
49339
  `));
48617
49340
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
48618
49341
  addPromptRunnerSelectionOptions(command);
48619
49342
  addQuestionsOption(command);
48620
- addCoderAgentOption(command, 'developer');
48621
- command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
48622
- command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
48623
- command.addOption(new Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
48624
- .choices([...TEST_BEFORE_MODE_VALUES])
48625
- .default('no'));
49343
+ addCoderExecutionOptions(command);
49344
+ addCoderCheckOptions(command, true);
48626
49345
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
48627
49346
  addPromptRunnerExecutionOptions(command);
48628
49347
  command.option('--isolate', spaceTrim$1(`
@@ -48651,9 +49370,9 @@ function $initializeCoderRunCommand(program) {
48651
49370
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
48652
49371
  addWorkspaceRepositoryOptions(command);
48653
49372
  command.action(handleActionErrors(async (cliOptions) => {
48654
- 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;
48655
- const configuredTestCommand = normalizeCommandOptionValue$1(test);
48656
- const testCommand = configuredTestCommand !== null && configuredTestCommand !== void 0 ? configuredTestCommand : (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND);
49373
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49374
+ 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;
49375
+ const checkCommand = resolveCoderCheckCommand(normalizeCheckCommandOption(check), checkBefore);
48657
49376
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
48658
49377
  isAgentRequired: !dryRun,
48659
49378
  });
@@ -48682,8 +49401,8 @@ function $initializeCoderRunCommand(program) {
48682
49401
  model: runnerOptions.model,
48683
49402
  agent,
48684
49403
  context,
48685
- testCommand,
48686
- testBefore,
49404
+ checkCommand,
49405
+ checkBefore,
48687
49406
  preserveLogs,
48688
49407
  isIsolated,
48689
49408
  noUi: runnerOptions.noUi,
@@ -48702,16 +49421,20 @@ function $initializeCoderRunCommand(program) {
48702
49421
  };
48703
49422
  validateCoderRunOptions(runOptions);
48704
49423
  const workspace = await $preflightWorkspaceRepository({
49424
+ ...projectOptions,
48705
49425
  policy: dryRun ? 'read-only' : 'mutate',
48706
49426
  ...questionsOptions,
48707
49427
  });
49428
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
49429
+ const projectContext = await resolveCoderProjectContext({
49430
+ projectPath: workspace.projectPath,
49431
+ agent,
49432
+ context,
49433
+ });
48708
49434
  if (!dryRun) {
48709
- // Reject missing or unreadable Books before offering installations or project configuration writes.
48710
- const { resolveCoderAgentBook } = await Promise.resolve().then(function () { return resolveCoderAgent$1; });
48711
- await resolveCoderAgentBook(agent, workspace.projectPath, { defaultRole: 'developer' });
48712
49435
  // Check disk space before installations and repository writes; previews require no setup.
48713
49436
  await $assertSufficientFreeDiskSpace(workspace.projectPath);
48714
- if (await $ensurePromptbookCliInstallations(questionsOptions)) {
49437
+ if (await $ensurePromptbookCliInstallations(questionsOptions, workspace.projectPath)) {
48715
49438
  return process.exit(0);
48716
49439
  }
48717
49440
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
@@ -48720,8 +49443,7 @@ function $initializeCoderRunCommand(program) {
48720
49443
  // Note: Import the function dynamically to avoid loading heavy dependencies until needed
48721
49444
  const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
48722
49445
  try {
48723
- // Override process.argv to pass options to the legacy parseRunOptions if needed
48724
- await runCodexPrompts({ ...runOptions, workspace });
49446
+ await runCodexPrompts({ ...runOptions, workspace, projectContext });
48725
49447
  }
48726
49448
  catch (error) {
48727
49449
  assertsError(error);
@@ -48731,23 +49453,6 @@ function $initializeCoderRunCommand(program) {
48731
49453
  return process.exit(0);
48732
49454
  }));
48733
49455
  }
48734
- /**
48735
- * Joins one Commander option that may be parsed either as a single string or a variadic token array.
48736
- *
48737
- * @private internal utility of `coder run` command
48738
- */
48739
- function normalizeCommandOptionValue$1(value) {
48740
- if (value === undefined) {
48741
- return undefined;
48742
- }
48743
- const parts = Array.isArray(value) ? value : [value];
48744
- const normalizedValue = parts
48745
- .map((part) => part.trim())
48746
- .filter(Boolean)
48747
- .join(' ')
48748
- .trim();
48749
- return normalizedValue === '' ? undefined : normalizedValue;
48750
- }
48751
49456
  // Note: [🟡] Code for CLI command [run](src/cli/cli-commands/coder/run.ts) should never be published outside of `@promptbook/cli`
48752
49457
  // Note: [💞] Ignore a discrepancy between file name and entity name
48753
49458
 
@@ -48793,9 +49498,8 @@ function $initializeCoderServerCommand(program) {
48793
49498
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
48794
49499
  addPromptRunnerSelectionOptions(command);
48795
49500
  addQuestionsOption(command);
48796
- addCoderAgentOption(command, 'developer');
48797
- command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
48798
- command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
49501
+ addCoderExecutionOptions(command);
49502
+ addCoderCheckOptions(command, false);
48799
49503
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
48800
49504
  addPromptRunnerExecutionOptions(command);
48801
49505
  addPromptPriorityOptions(command);
@@ -48817,9 +49521,10 @@ function $initializeCoderServerCommand(program) {
48817
49521
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
48818
49522
  addWorkspaceRepositoryOptions(command);
48819
49523
  command.action(handleActionErrors(async (cliOptions) => {
48820
- const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, minPriority: minimumPriority, maxPriority: maximumPriority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
49524
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49525
+ const { port: rawPort, dryRun, agent, context, check, preserveLogs, priority, minPriority: minimumPriority, maxPriority: maximumPriority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
48821
49526
  const port = parseCoderServerPort(rawPort);
48822
- const testCommand = normalizeCommandOptionValue(test);
49527
+ const checkCommand = normalizeCheckCommandOption(check);
48823
49528
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
48824
49529
  isAgentRequired: !dryRun,
48825
49530
  });
@@ -48843,7 +49548,7 @@ function $initializeCoderServerCommand(program) {
48843
49548
  model: runnerOptions.model,
48844
49549
  agent,
48845
49550
  context,
48846
- testCommand,
49551
+ checkCommand,
48847
49552
  preserveLogs,
48848
49553
  noUi: runnerOptions.noUi,
48849
49554
  thinkingLevel: runnerOptions.thinkingLevel,
@@ -48860,13 +49565,17 @@ function $initializeCoderServerCommand(program) {
48860
49565
  };
48861
49566
  validateCoderRunOptions(runOptions);
48862
49567
  const workspace = await $preflightWorkspaceRepository({
49568
+ ...projectOptions,
48863
49569
  policy: dryRun ? 'read-only' : 'mutate',
48864
49570
  ...questionsOptions,
48865
49571
  });
49572
+ const { resolveCoderProjectContext } = await Promise.resolve().then(function () { return resolveCoderProjectContext$1; });
49573
+ const projectContext = await resolveCoderProjectContext({
49574
+ projectPath: workspace.projectPath,
49575
+ agent,
49576
+ context,
49577
+ });
48866
49578
  if (!dryRun) {
48867
- // Reject missing or unreadable Books before offering installations or project configuration writes.
48868
- const { resolveCoderAgentBook } = await Promise.resolve().then(function () { return resolveCoderAgent$1; });
48869
- await resolveCoderAgentBook(agent, workspace.projectPath, { defaultRole: 'developer' });
48870
49579
  // Check disk space before installations and repository writes; previews require no setup.
48871
49580
  await $assertSufficientFreeDiskSpace(workspace.projectPath);
48872
49581
  await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
@@ -48875,7 +49584,7 @@ function $initializeCoderServerCommand(program) {
48875
49584
  // Note: Import dynamically to avoid loading heavy dependencies until needed
48876
49585
  const { runCodexPromptsServer } = await Promise.resolve().then(function () { return runCodexPromptsServer$1; });
48877
49586
  try {
48878
- await runCodexPromptsServer({ ...runOptions, workspace });
49587
+ await runCodexPromptsServer({ ...runOptions, workspace, projectContext });
48879
49588
  }
48880
49589
  catch (error) {
48881
49590
  assertsError(error);
@@ -48901,23 +49610,6 @@ function parseCoderServerPort(rawPort) {
48901
49610
  }
48902
49611
  return port;
48903
49612
  }
48904
- /**
48905
- * Joins one Commander option that may be parsed either as a single string or a variadic token array.
48906
- *
48907
- * @private internal utility of `coder server` command
48908
- */
48909
- function normalizeCommandOptionValue(value) {
48910
- if (value === undefined) {
48911
- return undefined;
48912
- }
48913
- const parts = Array.isArray(value) ? value : [value];
48914
- const normalizedValue = parts
48915
- .map((part) => part.trim())
48916
- .filter(Boolean)
48917
- .join(' ')
48918
- .trim();
48919
- return normalizedValue === '' ? undefined : normalizedValue;
48920
- }
48921
49613
  // Note: [🟡] Code for CLI command [server](src/cli/cli-commands/coder/server.ts) should never be published outside of `@promptbook/cli`
48922
49614
  // Note: [💞] Ignore a discrepancy between file name and entity name
48923
49615
 
@@ -48995,9 +49687,11 @@ function $initializeCoderVerifyCommand(program) {
48995
49687
  addCoderGitSyncOptions(command);
48996
49688
  addWorkspaceRepositoryOptions(command);
48997
49689
  command.action(handleActionErrors(async (cliOptions) => {
49690
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
48998
49691
  const { order, ignore } = cliOptions;
48999
49692
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
49000
49693
  const workspace = await $preflightWorkspaceRepository({
49694
+ ...projectOptions,
49001
49695
  policy: 'mutate',
49002
49696
  isAskingQuestionsEnabled: cliOptions.questions,
49003
49697
  });
@@ -49033,7 +49727,7 @@ function collectStringOption(value, previousValues) {
49033
49727
  * The coder command provides utilities for automated coding:
49034
49728
  * - init: Initialize coder configuration in current project
49035
49729
  * - add: Add one ready-to-run prompt file to the queue
49036
- * - plan: Discuss features with Planner and author reviewed PRDs
49730
+ * - plan: Discuss features with Developer and author reviewed PRDs
49037
49731
  * - generate-boilerplates: Generate prompt boilerplate files
49038
49732
  * - find-refactor-candidates: Find files that need refactoring
49039
49733
  * - list: List ready prompts in priority order without running them
@@ -49054,7 +49748,7 @@ function $initializeCoderCommand(program) {
49054
49748
  Subcommands:
49055
49749
  - init: Initialize coder configuration in current project
49056
49750
  - add: Add one ready-to-run prompt file to the queue
49057
- - plan: Discuss features with Planner and author reviewed PRDs
49751
+ - plan: Discuss features with Developer and author reviewed PRDs
49058
49752
  - generate-boilerplates: Generate prompt boilerplate files
49059
49753
  - find-refactor-candidates: Find files that need refactoring
49060
49754
  - find-unwritten: List prompt sections that still need to be authored
@@ -72175,12 +72869,21 @@ async function resolveInlineOrFileText(options) {
72175
72869
  }
72176
72870
  const textPath = resolve(options.currentWorkingDirectory, normalizedTextReference);
72177
72871
  const textPathStats = await stat(textPath).catch((error) => {
72178
- if (error.code === 'ENOENT') {
72872
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
72179
72873
  return undefined;
72180
72874
  }
72181
- throw error;
72875
+ if (['ENAMETOOLONG', 'EINVAL', 'ERR_INVALID_ARG_VALUE'].includes(error.code || '') && !isContextFileReference(normalizedTextReference)) {
72876
+ return undefined;
72877
+ }
72878
+ throw contextReadError(options, textPath, error);
72182
72879
  });
72183
72880
  if (!textPathStats) {
72881
+ if (options.isMissingFileAnError && isContextFileReference(normalizedTextReference)) {
72882
+ throw new NotFoundError(spaceTrim(`
72883
+ ${options.contextLabel} file \`${textPath}\` does not exist.
72884
+ Correct the file path in \`${options.optionName}\` or pass inline instructions.
72885
+ `));
72886
+ }
72184
72887
  return normalizedTextReference;
72185
72888
  }
72186
72889
  if (!textPathStats.isFile()) {
@@ -72190,7 +72893,55 @@ async function resolveInlineOrFileText(options) {
72190
72893
  Pass a file path or inline text in \`${options.optionName}\`.
72191
72894
  `));
72192
72895
  }
72193
- return readFile(textPath, 'utf-8');
72896
+ return readFile(textPath, 'utf-8').catch((error) => {
72897
+ throw contextReadError(options, textPath, error);
72898
+ });
72899
+ }
72900
+ /** Distinguishes explicit file spellings from ordinary inline instructions, including native Windows paths. */
72901
+ function isContextFileReference(reference) {
72902
+ return isAbsolute(reference) || /^(?:\.{1,2}[/\\]|[a-z]:[/\\]|\\\\)/iu.test(reference) ||
72903
+ (!/[\r\n]/u.test(reference) && /\.(?:md|markdown|txt|book|json|ya?ml)$/iu.test(reference)) ||
72904
+ (!/\s/u.test(reference) && /[/\\]/u.test(reference) && !/^[a-z]+:\/\//iu.test(reference));
72905
+ }
72906
+ /** Adds the selected path and recovery guidance to filesystem failures. */
72907
+ function contextReadError(options, path, error) {
72908
+ return new NotAllowed(spaceTrim(`
72909
+ Cannot read ${options.contextLabel.toLowerCase()} file \`${path}\`: ${error.message}
72910
+ Check file permissions and the path in \`${options.optionName}\`.
72911
+ `));
72912
+ }
72913
+
72914
+ /**
72915
+ * Resolves project AGENTS.md on omission; explicit text, files and intentional empty values replace it.
72916
+ */
72917
+ async function resolveCoderContext(contextReference, currentWorkingDirectory) {
72918
+ if (contextReference === undefined) {
72919
+ const contextPath = join(currentWorkingDirectory, AGENTS_FILE_PATH);
72920
+ try {
72921
+ return await readFile(contextPath, 'utf-8');
72922
+ }
72923
+ catch (error) {
72924
+ if (error.code === 'ENOENT') {
72925
+ // A broken symlink is an existing, unreadable default, not an absent optional file.
72926
+ const isMissing = await lstat(contextPath).then(() => false, (failure) => failure.code === 'ENOENT');
72927
+ if (isMissing) {
72928
+ console.warn(`No additional context: \`${contextPath}\` is missing.`);
72929
+ return undefined;
72930
+ }
72931
+ }
72932
+ throw new NotAllowed(spaceTrim(`
72933
+ Cannot read default context file \`${contextPath}\`: ${error instanceof Error ? error.message : String(error)}
72934
+ Check its permissions, or replace it with \`--context\` (an empty value disables additional context).
72935
+ `));
72936
+ }
72937
+ }
72938
+ return resolveInlineOrFileText({
72939
+ textReference: contextReference,
72940
+ currentWorkingDirectory,
72941
+ contextLabel: 'Coding context',
72942
+ optionName: '--context',
72943
+ isMissingFileAnError: true,
72944
+ });
72194
72945
  }
72195
72946
 
72196
72947
  /**
@@ -72269,11 +73020,7 @@ function createAgentChatWorkspacePath(options) {
72269
73020
  .toLowerCase()
72270
73021
  .replace(/[^a-z0-9._-]+/gu, '-')
72271
73022
  .replace(/^[._-]+|[._-]+$/gu, '');
72272
- const sessionName = [
72273
- safeAgentName || 'agent',
72274
- Date.now().toString(36),
72275
- $randomToken(4),
72276
- ].join('-');
73023
+ const sessionName = [safeAgentName || 'agent', Date.now().toString(36), $randomToken(4)].join('-');
72277
73024
  return resolvePromptbookTemporaryPath(options.currentWorkingDirectory, AGENT_CHAT_TEMPORARY_DIRECTORY_NAME, AGENT_CHAT_SESSIONS_DIRECTORY_NAME, sessionName);
72278
73025
  }
72279
73026
  /**
@@ -72328,7 +73075,7 @@ function createPromptRunnerOptions(options) {
72328
73075
  return {
72329
73076
  dryRun: false,
72330
73077
  context: undefined,
72331
- testCommand: undefined,
73078
+ checkCommand: undefined,
72332
73079
  preserveLogs: false,
72333
73080
  noUi: options.noUi,
72334
73081
  thinkingLevel: options.thinkingLevel,
@@ -72394,12 +73141,7 @@ function normalizeRelativePath(relativePath) {
72394
73141
  */
72395
73142
  async function runAgentChat(options) {
72396
73143
  const currentWorkingDirectory = options.currentWorkingDirectory || process.cwd();
72397
- const context = await resolveInlineOrFileText({
72398
- textReference: options.context,
72399
- currentWorkingDirectory,
72400
- contextLabel: 'Agent context',
72401
- optionName: '--context',
72402
- });
73144
+ const context = await resolveCoderContext(options.context, currentWorkingDirectory);
72403
73145
  const workspacePath = createAgentChatWorkspacePath({
72404
73146
  currentWorkingDirectory,
72405
73147
  agentPath: options.agentPath,
@@ -72462,12 +73204,7 @@ async function runAgentExec(options) {
72462
73204
  throw new NotAllowed('Pass a non-empty user message in `--message`.');
72463
73205
  }
72464
73206
  const currentWorkingDirectory = options.currentWorkingDirectory || process.cwd();
72465
- const context = await resolveInlineOrFileText({
72466
- textReference: options.context,
72467
- currentWorkingDirectory,
72468
- contextLabel: 'Agent context',
72469
- optionName: '--context',
72470
- });
73207
+ const context = await resolveCoderContext(options.context, currentWorkingDirectory);
72471
73208
  const result = await executeAgentChatTurn({
72472
73209
  ...options,
72473
73210
  currentWorkingDirectory,
@@ -72553,13 +73290,13 @@ const AGENT_IDLE_AUTO_PULL_INTERVAL_MS = 30000;
72553
73290
  * Watches the queued message directory and answers messages one by one.
72554
73291
  */
72555
73292
  async function runAgentMessages(options, controls = {}) {
72556
- var _a, _b;
73293
+ var _a, _b, _c;
72557
73294
  validateAgentRunOptions(options);
72558
73295
  validateAgentWatchOptions('ptbk agent-folder run-agent', options);
72559
- const projectPath = process.cwd();
73296
+ const projectPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
72560
73297
  let autoPullTimestamp = options.autoPull ? Date.now() : undefined;
72561
73298
  const shouldContinue = controls.shouldContinue || (() => just(true));
72562
- const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : AGENT_QUEUE_POLL_INTERVAL_MS;
73299
+ const queuePollIntervalMs = (_b = controls.queuePollIntervalMs) !== null && _b !== void 0 ? _b : AGENT_QUEUE_POLL_INTERVAL_MS;
72563
73300
  let isWatchSessionInitialized = false;
72564
73301
  let uiHandle;
72565
73302
  const messageFailureTracker = new AgentMessageFailureTracker({
@@ -72576,7 +73313,7 @@ async function runAgentMessages(options, controls = {}) {
72576
73313
  }
72577
73314
  }
72578
73315
  const result = await tickAgentMessages(options, { isQuietWhenIdle: true, uiHandle });
72579
- autoPullTimestamp = (_b = result.autoPullTimestamp) !== null && _b !== void 0 ? _b : autoPullTimestamp;
73316
+ autoPullTimestamp = (_c = result.autoPullTimestamp) !== null && _c !== void 0 ? _c : autoPullTimestamp;
72580
73317
  if (result.isMessageProcessed) {
72581
73318
  messageFailureTracker.clearMessageFailure(projectPath, result.queuedMessage);
72582
73319
  continue;
@@ -78429,8 +79166,8 @@ async function loadPromptFiles(promptsDir, options = {}) {
78429
79166
  /**
78430
79167
  * Builds a display label using the prompt line number for easier navigation.
78431
79168
  */
78432
- function buildPromptLabelForDisplay(file, section) {
78433
- return `${relative(process.cwd(), file.path).replace(/\\/g, '/')}#${section.startLine + 1}`;
79169
+ function buildPromptLabelForDisplay(file, section, projectPath = process.cwd()) {
79170
+ return `${relative(projectPath, file.path).replace(/\\/g, '/')}#${section.startLine + 1}`;
78434
79171
  }
78435
79172
 
78436
79173
  /**
@@ -78445,11 +79182,11 @@ function buildPromptSummary(file, section) {
78445
79182
  /**
78446
79183
  * Prints the list of prompts that still need to be written.
78447
79184
  */
78448
- function printPromptsToBeWritten(files, priorityFilter = {}) {
79185
+ function printPromptsToBeWritten(files, priorityFilter = {}, projectPath) {
78449
79186
  const promptsToWrite = listPromptsToBeWritten(files, priorityFilter);
78450
79187
  let i = 0;
78451
79188
  for (const { file, section } of promptsToWrite) {
78452
- const label = buildPromptLabelForDisplay(file, section);
79189
+ const label = buildPromptLabelForDisplay(file, section, projectPath);
78453
79190
  const summary = buildPromptSummary(file, section);
78454
79191
  console.info(` ${++i}) ${label}: ${summary}`);
78455
79192
  }
@@ -78519,7 +79256,7 @@ async function findUnwrittenPrompts(options = {}) {
78519
79256
  }
78520
79257
  else {
78521
79258
  console.info(colors.yellow(`Found ${unwrittenPrompts.length} prompt(s) that need to be written:`));
78522
- printPromptsToBeWritten(promptFiles, priorityFilter);
79259
+ printPromptsToBeWritten(promptFiles, priorityFilter, options.projectPath);
78523
79260
  }
78524
79261
  return unwrittenPrompts.length;
78525
79262
  }
@@ -79363,9 +80100,9 @@ function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
79363
80100
  /**
79364
80101
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
79365
80102
  */
79366
- function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
80103
+ function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity, projectPath) {
79367
80104
  return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
79368
- label: buildPromptLabelForDisplay(file, section),
80105
+ label: buildPromptLabelForDisplay(file, section, projectPath),
79369
80106
  summary: buildPromptSummary(file, section),
79370
80107
  priority: section.priority,
79371
80108
  }));
@@ -79420,7 +80157,7 @@ async function listCoderPrompts(options = {}) {
79420
80157
  const promptFiles = await loadPromptFiles(join((_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd(), 'prompts'), {
79421
80158
  isMissingDirectoryAllowed: true,
79422
80159
  });
79423
- const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity);
80160
+ const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity, options.projectPath);
79424
80161
  printUpcomingTasks(upcomingTasks);
79425
80162
  return upcomingTasks.length;
79426
80163
  }
@@ -79800,6 +80537,24 @@ var createPlanningTerminal$1 = /*#__PURE__*/Object.freeze({
79800
80537
  createPlanningTerminal: createPlanningTerminal
79801
80538
  });
79802
80539
 
80540
+ /**
80541
+ * Resolves single-agent execution/authoring defaults without compiling Books or creating artifacts.
80542
+ * Execution previews use the same identity and routing. Utilities and all-agent discovery resolve
80543
+ * only the project directory and must not call this service to manufacture a primary-agent filter.
80544
+ */
80545
+ async function resolveCoderProjectContext(options) {
80546
+ const agentBook = await resolveCoderAgentBook(options.agent, options.projectPath, {
80547
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
80548
+ });
80549
+ const context = await resolveCoderContext(options.context, options.projectPath);
80550
+ return { projectPath: options.projectPath, agentBook, context };
80551
+ }
80552
+
80553
+ var resolveCoderProjectContext$1 = /*#__PURE__*/Object.freeze({
80554
+ __proto__: null,
80555
+ resolveCoderProjectContext: resolveCoderProjectContext
80556
+ });
80557
+
79803
80558
  /** Maximum question length accepted by both command and planning tools. */
79804
80559
  const MAX_MESSAGE_LENGTH = 32000;
79805
80560
  /** Maximum context length accepted by both command and planning tools. */
@@ -80018,6 +80773,19 @@ class CoderTeamRuntime {
80018
80773
  }
80019
80774
  // Note: [💞] Runtime contract types accompany their implementation.
80020
80775
 
80776
+ /**
80777
+ * Rebinds an absolute reference inside a project/repository to its worktree copy. Relative references
80778
+ * already resolve against the execution project; explicit references outside that checkout remain explicit.
80779
+ */
80780
+ function mapProjectReferenceToWorktree(reference, originalProjectPath, worktreeProjectPath) {
80781
+ if (reference === undefined || !isAbsolute(reference.trim()))
80782
+ return reference;
80783
+ const relativePath = relative(originalProjectPath, reference.trim());
80784
+ if (isAbsolute(relativePath) || relativePath === '..' || /^\.\.[/\\]/u.test(relativePath))
80785
+ return reference;
80786
+ return join(worktreeProjectPath, relativePath);
80787
+ }
80788
+
80021
80789
  /** Limits apply before JSON parsing, even when a client bypasses the generated command. */
80022
80790
  const MAX_REQUEST_BYTES = 131072;
80023
80791
  /** Each invocation receives an independent 256-bit bearer credential. */
@@ -80204,7 +80972,7 @@ function buildBridgeClient(url, token) {
80204
80972
  * Adds a scoped consultation transport around an existing adapter. Both primary and advisers keep exactly
80205
80973
  * that adapter's permissions and authentication. Only the enclosing Coder round owns tests, queues and Git operations.
80206
80974
  */
80207
- function createCoderTeamPromptRunner(runner, agentBookReference) {
80975
+ function createCoderTeamPromptRunner(runner, agentBookReference, originalProjectPath, originalRepositoryRoot = originalProjectPath) {
80208
80976
  var _a;
80209
80977
  return {
80210
80978
  name: runner.name,
@@ -80220,8 +80988,11 @@ function createCoderTeamPromptRunner(runner, agentBookReference) {
80220
80988
  let runtime;
80221
80989
  try {
80222
80990
  // Resolve against this invocation's actual checkout, including an isolated worktree.
80223
- const agent = await resolveCoderAgent(agentBookReference, options.projectPath, {
80224
- defaultRole: 'developer',
80991
+ const reference = originalProjectPath
80992
+ ? mapProjectReferenceToWorktree(agentBookReference, originalRepositoryRoot, resolve(options.projectPath, relative(originalProjectPath, originalRepositoryRoot)))
80993
+ : agentBookReference;
80994
+ const agent = await resolveCoderAgent(reference, options.projectPath, {
80995
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
80225
80996
  isInitializationAllowed: false,
80226
80997
  signal: controller.signal,
80227
80998
  });
@@ -81102,11 +81873,12 @@ function writeDurableFile(path, content) {
81102
81873
  * @private internal utility of `coder plan`
81103
81874
  */
81104
81875
  async function runPlanningSession(options, io, harness = runPlanningHarness) {
81105
- var _a, _b;
81876
+ var _a, _b, _c;
81106
81877
  const projectPath = await realpath(options.projectPath);
81107
81878
  assertPlanningHarnessSupported(options.agentName);
81879
+ const projectContext = (_a = options.projectContext) !== null && _a !== void 0 ? _a : (await resolveCoderProjectContext({ ...options, projectPath }));
81108
81880
  const agent = await resolveCoderAgent(options.agent, projectPath, {
81109
- defaultRole: 'planner',
81881
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
81110
81882
  isInitializationAllowed: false,
81111
81883
  signal: io.signal,
81112
81884
  });
@@ -81114,9 +81886,9 @@ async function runPlanningSession(options, io, harness = runPlanningHarness) {
81114
81886
  throw new NotFoundError(spaceTrim('Planning requires `prompts/`. Run `ptbk coder init` first.'));
81115
81887
  }
81116
81888
  if (!agent)
81117
- throw new NotFoundError(spaceTrim('Planner Book is missing. Run `ptbk coder init`.'));
81889
+ throw new NotFoundError(spaceTrim('Primary Book is missing. Run `ptbk coder init`.'));
81118
81890
  resolvePlanningPath(projectPath, 'prompts');
81119
- const templateOption = (_a = options.template) !== null && _a !== void 0 ? _a : (existsSync(join(projectPath, COMMON_PROMPT_TEMPLATE_FILE_PATH))
81891
+ const templateOption = (_b = options.template) !== null && _b !== void 0 ? _b : (existsSync(join(projectPath, COMMON_PROMPT_TEMPLATE_FILE_PATH))
81120
81892
  ? COMMON_PROMPT_TEMPLATE_FILE_PATH
81121
81893
  : undefined);
81122
81894
  const template = await resolveCoderPromptTemplate({ projectPath, templateOption });
@@ -81160,7 +81932,10 @@ async function runPlanningSession(options, io, harness = runPlanningHarness) {
81160
81932
  },
81161
81933
  { role: 'context', content: await readPlanningContext(projectPath, { kind: 'list', path: '.' }) },
81162
81934
  ];
81163
- for (const path of ['README.md', 'AGENTS.md']) {
81935
+ if (projectContext.context !== undefined) {
81936
+ history.push({ role: 'context', content: projectContext.context });
81937
+ }
81938
+ for (const path of ['README.md']) {
81164
81939
  if (existsSync(join(projectPath, path))) {
81165
81940
  history.push({
81166
81941
  role: 'context',
@@ -81177,7 +81952,7 @@ async function runPlanningSession(options, io, harness = runPlanningHarness) {
81177
81952
  let pending = [];
81178
81953
  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`);
81179
81954
  while (!io.signal.aborted) {
81180
- const message = (_b = (await io.readMessage())) === null || _b === void 0 ? void 0 : _b.trim();
81955
+ const message = (_c = (await io.readMessage())) === null || _c === void 0 ? void 0 : _c.trim();
81181
81956
  if (message === undefined || ['/exit', 'exit', 'quit'].includes(message))
81182
81957
  break;
81183
81958
  if (!message)
@@ -81314,7 +82089,7 @@ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
81314
82089
  /**
81315
82090
  * CLI usage text for this script.
81316
82091
  */
81317
- 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]';
82092
+ 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]';
81318
82093
  /**
81319
82094
  * Top-level flags supported by this command.
81320
82095
  */
@@ -81324,8 +82099,8 @@ const KNOWN_OPTION_FLAGS = new Set([
81324
82099
  '--model',
81325
82100
  '--agent',
81326
82101
  '--context',
81327
- '--test',
81328
- '--test-before',
82102
+ '--check',
82103
+ '--check-before',
81329
82104
  '--preserve-logs',
81330
82105
  '--isolate',
81331
82106
  '--no-ui',
@@ -81351,6 +82126,15 @@ const KNOWN_OPTION_FLAGS = new Set([
81351
82126
  * Parses CLI arguments into runner options.
81352
82127
  */
81353
82128
  function parseRunOptions(args) {
82129
+ args = args.flatMap((argument) => {
82130
+ const match = argument.match(/^(--check(?:-before)?)=([\s\S]*)$/u);
82131
+ return match ? [match[1], match[2]] : [argument];
82132
+ });
82133
+ for (const [legacyFlag, replacementFlag] of LEGACY_CODER_CHECK_OPTIONS) {
82134
+ if (args.some((argument) => argument === legacyFlag || argument.startsWith(`${legacyFlag}=`))) {
82135
+ exitWithUsageError(formatLegacyCoderCheckOptionError(legacyFlag, replacementFlag));
82136
+ }
82137
+ }
81354
82138
  let agentName = undefined;
81355
82139
  const dryRun = args.includes('--dry-run');
81356
82140
  const harnessValue = readOptionValue(args, '--harness');
@@ -81366,10 +82150,10 @@ function parseRunOptions(args) {
81366
82150
  exitWithUsageError('Pass a non-empty Book path after `--agent`, or omit it to use `agents/developer.book`.');
81367
82151
  }
81368
82152
  const context = readOptionValue(args, '--context');
81369
- const hasTestCommandFlag = args.includes('--test');
81370
- const testCommand = readVariadicOptionValue(args, '--test');
81371
- const hasTestBeforeFlag = args.includes('--test-before');
81372
- const testBefore = parseTestBeforeOption(readOptionValue(args, '--test-before'), hasTestBeforeFlag);
82153
+ const hasCheckCommandFlag = args.includes('--check');
82154
+ const checkCommand = readVariadicOptionValue(args, '--check');
82155
+ const hasCheckBeforeFlag = args.includes('--check-before');
82156
+ const checkBefore = parseCheckBeforeOption(readOptionValue(args, '--check-before'), hasCheckBeforeFlag);
81373
82157
  const preserveLogs = args.includes('--preserve-logs');
81374
82158
  const isIsolated = args.includes('--isolate');
81375
82159
  const noUi = args.includes('--no-ui');
@@ -81407,8 +82191,8 @@ function parseRunOptions(args) {
81407
82191
  const waitBetweenPrompts = parseWaitOption(args, '--wait-between-prompts', 0);
81408
82192
  const waitAfterError = parseWaitOption(args, '--wait-after-error', DEFAULT_WAIT_AFTER_ERROR_MS);
81409
82193
  let thinkingLevel;
81410
- if (hasTestCommandFlag && testCommand === undefined) {
81411
- exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
82194
+ if (hasCheckCommandFlag && checkCommand === undefined) {
82195
+ exitWithUsageError('Missing value for --check. Use a shell command such as `npm run check` and quote it when it contains top-level CLI flags.');
81412
82196
  }
81413
82197
  if (hasThinkingLevelFlag && thinkingLevelValue === undefined) {
81414
82198
  exitWithUsageError(`Missing value for --thinking-level. Use one of: ${THINKING_LEVEL_VALUES.join(', ')}.`);
@@ -81443,8 +82227,8 @@ function parseRunOptions(args) {
81443
82227
  model,
81444
82228
  agent,
81445
82229
  context,
81446
- testCommand,
81447
- testBefore,
82230
+ checkCommand: resolveCoderCheckCommand(checkCommand, checkBefore),
82231
+ checkBefore,
81448
82232
  thinkingLevel,
81449
82233
  priority: minimumPriority !== null && minimumPriority !== void 0 ? minimumPriority : 0,
81450
82234
  minimumPriority,
@@ -81456,15 +82240,15 @@ function parseRunOptions(args) {
81456
82240
  /**
81457
82241
  * Parses and validates the optional pre-coding verification mode.
81458
82242
  */
81459
- function parseTestBeforeOption(value, hasTestBeforeFlag) {
82243
+ function parseCheckBeforeOption(value, hasCheckBeforeFlag) {
81460
82244
  if (value === undefined) {
81461
- if (hasTestBeforeFlag) {
81462
- exitWithUsageError(`Missing value for --test-before. Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
82245
+ if (hasCheckBeforeFlag) {
82246
+ exitWithUsageError(`Missing value for --check-before. Use one of: ${CHECK_BEFORE_MODE_VALUES.join(', ')}.`);
81463
82247
  }
81464
82248
  return 'no';
81465
82249
  }
81466
- if (!isTestBeforeMode(value)) {
81467
- exitWithUsageError(`Invalid value for --test-before: "${value}". Use one of: ${TEST_BEFORE_MODE_VALUES.join(', ')}.`);
82250
+ if (!isCheckBeforeMode(value)) {
82251
+ exitWithUsageError(`Invalid value for --check-before: "${value}". Use one of: ${CHECK_BEFORE_MODE_VALUES.join(', ')}.`);
81468
82252
  }
81469
82253
  return value;
81470
82254
  }
@@ -81525,8 +82309,14 @@ function readVariadicOptionValue(args, flag) {
81525
82309
  }
81526
82310
  valueParts.push(valuePart);
81527
82311
  }
81528
- const normalizedValue = valueParts.join(' ').trim();
81529
- return normalizedValue === '' ? undefined : normalizedValue;
82312
+ if (valueParts.length === 0)
82313
+ return undefined;
82314
+ try {
82315
+ return normalizeCheckCommandOption(valueParts);
82316
+ }
82317
+ catch (error) {
82318
+ exitWithUsageError(error instanceof Error ? error.message : String(error));
82319
+ }
81530
82320
  }
81531
82321
  /**
81532
82322
  * Parses and validates one priority boundary.
@@ -81968,18 +82758,6 @@ async function sleepWithCountdown(options) {
81968
82758
  });
81969
82759
  }
81970
82760
 
81971
- /**
81972
- * Resolves optional coding context provided inline or via a file path.
81973
- */
81974
- async function resolveCoderContext(contextReference, currentWorkingDirectory) {
81975
- return resolveInlineOrFileText({
81976
- textReference: contextReference,
81977
- currentWorkingDirectory,
81978
- contextLabel: 'Coding context',
81979
- optionName: '--context',
81980
- });
81981
- }
81982
-
81983
82761
  /**
81984
82762
  * Listens for the terminal control keys while the plain console output is used.
81985
82763
  *
@@ -83574,7 +84352,7 @@ function buildScriptPath(file, section, projectPath = process.cwd()) {
83574
84352
  */
83575
84353
  const CODER_RUN_STEP_LABELS = {
83576
84354
  implementation: 'Implementation',
83577
- testing: 'Testing',
84355
+ checking: 'Checking',
83578
84356
  fixing: 'Fixing',
83579
84357
  };
83580
84358
  /**
@@ -83585,16 +84363,16 @@ const IN_PROGRESS_STEP_SUFFIX = 'in progress';
83585
84363
  * Formats the per-step usage breakdown recorded for one prompt.
83586
84364
  *
83587
84365
  * Produces a `; `-separated summary such as
83588
- * `Implementation $8.01 6 hours; Testing 1 hour; Fixing $3.14 2 hours` where each coding step carries its
84366
+ * `Implementation $8.01 6 hours; Checking 1 hour; Fixing $3.14 2 hours` where each coding step carries its
83589
84367
  * price and duration and each verification step carries only its duration.
83590
84368
  *
83591
84369
  * @param steps - Steps which have already finished
83592
- * @param inProgressStepKind - Step which has started but has not finished yet, appended as `Testing in progress`
84370
+ * @param inProgressStepKind - Step which has started but has not finished yet, appended as `Checking in progress`
83593
84371
  */
83594
84372
  function formatCoderRunSteps(steps, inProgressStepKind) {
83595
84373
  const formattedSteps = steps.map(formatCoderRunStep);
83596
84374
  if (inProgressStepKind !== undefined) {
83597
- formattedSteps.push(`${CODER_RUN_STEP_LABELS[inProgressStepKind]} ${IN_PROGRESS_STEP_SUFFIX}`);
84375
+ formattedSteps.push(`${CODER_RUN_STEP_LABELS[decodeCoderRunStepKind(inProgressStepKind)]} ${IN_PROGRESS_STEP_SUFFIX}`);
83598
84376
  }
83599
84377
  return formattedSteps.join('; ');
83600
84378
  }
@@ -83602,7 +84380,7 @@ function formatCoderRunSteps(steps, inProgressStepKind) {
83602
84380
  * Formats one coder run step as `Label $price duration`, omitting the price for steps without model usage.
83603
84381
  */
83604
84382
  function formatCoderRunStep(step) {
83605
- const label = CODER_RUN_STEP_LABELS[step.kind];
84383
+ const label = CODER_RUN_STEP_LABELS[decodeCoderRunStepKind(step.kind)];
83606
84384
  const durationString = moment.duration(step.durationMs).humanize();
83607
84385
  const priceString = step.usage === null ? undefined : formatUsagePrice(step.usage);
83608
84386
  if (priceString === undefined) {
@@ -83610,6 +84388,10 @@ function formatCoderRunStep(step) {
83610
84388
  }
83611
84389
  return `${label} ${priceString} ${durationString}`;
83612
84390
  }
84391
+ /** Decodes the old aggregate step name at the display boundary; new execution only records checking. */
84392
+ function decodeCoderRunStepKind(kind) {
84393
+ return kind === 'testing' ? 'checking' : kind;
84394
+ }
83613
84395
 
83614
84396
  /**
83615
84397
  * Formats optional attempt metadata stored in prompt status lines.
@@ -83950,13 +84732,13 @@ function buildPromptRunTraceSummarySection(options) {
83950
84732
  `- **Runner:** ${runnerSignature}${loginMethodSuffix}`,
83951
84733
  `- **Attempts:** ${options.attemptCount}`,
83952
84734
  ...buildPromptRunTraceStepsLines(outcome),
83953
- ...buildPromptRunTraceTestCommandLines(options.testCommand),
84735
+ ...buildPromptRunTraceCheckCommandLines(options.checkCommand),
83954
84736
  `- **Started:** ${options.startedDate.toISOString()}`,
83955
84737
  `- **Finished:** ${options.finishedDate.toISOString()}`,
83956
84738
  `- **Duration:** ${moment.duration(options.finishedDate.diff(options.startedDate)).humanize()}`,
83957
84739
  ];
83958
84740
  return spaceTrim$1((block) => `
83959
- # Run trace of \`${buildPromptLabelForDisplay(file, section)}\`
84741
+ # Run trace of \`${buildPromptLabelForDisplay(file, section, options.projectPath)}\`
83960
84742
 
83961
84743
  ${block(detailLines.join('\n'))}
83962
84744
  `);
@@ -83989,11 +84771,11 @@ function buildPromptRunTraceStepsLines(outcome) {
83989
84771
  /**
83990
84772
  * Renders the verification command of the round, omitted when the round ran without one.
83991
84773
  */
83992
- function buildPromptRunTraceTestCommandLines(testCommand) {
83993
- if (!testCommand) {
84774
+ function buildPromptRunTraceCheckCommandLines(checkCommand) {
84775
+ if (!checkCommand) {
83994
84776
  return [];
83995
84777
  }
83996
- return [`- **Verification command:** \`${testCommand}\``];
84778
+ return [`- **Check command:** \`${checkCommand}\``];
83997
84779
  }
83998
84780
  /**
83999
84781
  * Renders the failure details of a failed round, omitted for a successful one.
@@ -84080,9 +84862,9 @@ const MAX_RETRY_ATTEMPTS_AFTER_ERROR = 3;
84080
84862
  *
84081
84863
  * @private function of runCodexPrompts
84082
84864
  */
84083
- async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, promptLabel, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, projectPath, }) {
84084
- var _a, _b;
84085
- 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();
84865
+ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, promptLabel, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, projectPath, artifactsProjectPath, }) {
84866
+ var _a, _b, _c;
84867
+ 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();
84086
84868
  const commitMessage = buildCommitMessage(nextPrompt.file, nextPrompt.section);
84087
84869
  const taskPrompt = buildCodexPrompt(nextPrompt.file, nextPrompt.section);
84088
84870
  // Prepend agent system message before the task so the harness sees agent instructions first
@@ -84099,8 +84881,7 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
84099
84881
  `)
84100
84882
  : taskPrompt;
84101
84883
  const codexPrompt = appendCoderContext(promptWithAgent, resolvedCoderContext);
84102
- // Note: Temporary scripts and runtime logs stay in the original project so they outlive an isolated worktree
84103
- const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
84884
+ const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section, artifactsProjectPath !== null && artifactsProjectPath !== void 0 ? artifactsProjectPath : roundProjectPath);
84104
84885
  // Note: Read before the first `[^]` rewrite of this round, which would overwrite the report recorded so far
84105
84886
  const previousRunnerSignatures = resolvePreviousRunnerSignatures(nextPrompt);
84106
84887
  setPromptRoundRunningState({ isRichUiEnabled, promptLabel, scriptPath, uiHandle });
@@ -84119,13 +84900,13 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
84119
84900
  for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
84120
84901
  try {
84121
84902
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
84122
- const result = await runPromptWithTestFeedback({
84903
+ const result = await runPromptWithCheckFeedback({
84123
84904
  runner,
84124
84905
  prompt: codexPrompt,
84125
84906
  scriptPath,
84126
84907
  projectPath: roundProjectPath,
84127
84908
  promptLabel,
84128
- testCommand: options.testCommand,
84909
+ checkCommand: options.checkCommand,
84129
84910
  preserveArtifactsOnSuccess: options.preserveLogs,
84130
84911
  logPath,
84131
84912
  onAttemptStarted: (nextAttemptCount) => {
@@ -84165,7 +84946,9 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
84165
84946
  lastError = error;
84166
84947
  // Note: A harness which is not logged in answers every retry the same way, so the user gets
84167
84948
  // the sign-in instructions right away instead of after every retry has waited its delay
84168
- if (error instanceof AuthenticationError || errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
84949
+ if (error instanceof AuthenticationError ||
84950
+ error instanceof CoderCheckSetupError ||
84951
+ errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
84169
84952
  break;
84170
84953
  }
84171
84954
  await waitAfterErrorBeforeRetry({
@@ -84407,13 +85190,15 @@ async function finalizeFailedPromptRound(options) {
84407
85190
  * harness, model and thinking level did it and everything that harness has written while doing it.
84408
85191
  */
84409
85192
  async function recordPromptRoundTrace(options) {
85193
+ var _a, _b;
84410
85194
  const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
84411
85195
  await writePromptRunTrace({
85196
+ projectPath: (_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : runOptions.projectPath,
84412
85197
  file: nextPrompt.file,
84413
85198
  section: nextPrompt.section,
84414
85199
  ...runnerMetadata,
84415
85200
  thinkingLevel: runOptions.thinkingLevel,
84416
- testCommand: runOptions.testCommand,
85201
+ checkCommand: runOptions.checkCommand,
84417
85202
  attemptCount,
84418
85203
  startedDate: promptExecutionStartedDate,
84419
85204
  finishedDate: moment(),
@@ -85011,12 +85796,12 @@ async function abortSquashMerge(worktree) {
85011
85796
  * @private function of runCodexPrompts
85012
85797
  */
85013
85798
  async function runIsolatedPromptRound(options) {
85014
- var _a, _b, _c, _d, _e, _f;
85799
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
85015
85800
  const { nextPrompt, promptLabel, isRichUiEnabled, uiHandle, waitForRequestedPause } = options;
85016
- 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();
85801
+ 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();
85017
85802
  // Note: The original project is left untouched by the isolated round itself, so its scope covers exactly
85018
85803
  // the prompt status update and the changes the merge brings back from the worktree
85019
- const originalProjectCommitScope = await captureCoderCommitScope((_d = options.options.workspace) !== null && _d !== void 0 ? _d : projectPath);
85804
+ const originalProjectCommitScope = await captureCoderCommitScope((_e = options.options.workspace) !== null && _e !== void 0 ? _e : projectPath);
85020
85805
  const worktree = await createCoderIsolationWorktree({
85021
85806
  projectPath,
85022
85807
  repositoryRoot: originalProjectCommitScope.repositoryRoot,
@@ -85024,16 +85809,44 @@ async function runIsolatedPromptRound(options) {
85024
85809
  });
85025
85810
  announceIsolatedRoundStart(worktree, promptLabel, isRichUiEnabled);
85026
85811
  try {
85027
- const isolatedProjectPath = join(worktree.worktreePath, relative((_e = originalProjectCommitScope.repositoryRoot) !== null && _e !== void 0 ? _e : projectPath, projectPath));
85812
+ const isolatedProjectPath = join(worktree.worktreePath, relative((_f = originalProjectCommitScope.repositoryRoot) !== null && _f !== void 0 ? _f : projectPath, projectPath));
85028
85813
  // This newly created working tree has its own metadata. Keep the project's repository-relative
85029
85814
  // location so its harness and verification use the same project as the original invocation.
85030
85815
  const isolatedWorkspace = await $resolveWorkspaceRepository(isolatedProjectPath);
85816
+ const isolatedOptions = {
85817
+ ...options.options,
85818
+ workspace: isolatedWorkspace,
85819
+ projectPath: isolatedWorkspace.projectPath,
85820
+ agent: mapProjectReferenceToWorktree(options.options.agent, (_g = originalProjectCommitScope.repositoryRoot) !== null && _g !== void 0 ? _g : projectPath, worktree.worktreePath),
85821
+ context: mapProjectReferenceToWorktree(options.options.context, (_h = originalProjectCommitScope.repositoryRoot) !== null && _h !== void 0 ? _h : projectPath, worktree.worktreePath),
85822
+ autoPush: false,
85823
+ };
85824
+ const projectContext = await resolveCoderProjectContext(isolatedOptions);
85825
+ const agent = await resolveCoderAgent(isolatedOptions.agent, isolatedWorkspace.projectPath, {
85826
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
85827
+ isInitializationAllowed: false,
85828
+ });
85829
+ // Status updates, traces and commits belong to the same checkout as the harness and checks.
85830
+ // Copy the parsed data too, so a failed isolated round does not mutate the original queue in memory.
85831
+ const isolatedFile = {
85832
+ ...nextPrompt.file,
85833
+ path: join(isolatedWorkspace.projectPath, relative(projectPath, nextPrompt.file.path)),
85834
+ lines: [...nextPrompt.file.lines],
85835
+ sections: nextPrompt.file.sections.map((section) => ({ ...section })),
85836
+ };
85031
85837
  await runPromptRound({
85032
85838
  ...options,
85839
+ nextPrompt: { file: isolatedFile, section: isolatedFile.sections[nextPrompt.section.index] },
85033
85840
  projectPath: isolatedWorkspace.projectPath,
85841
+ // Temporary scripts and raw logs deliberately outlive the worktree; PRDs and traces are merged.
85842
+ artifactsProjectPath: projectPath,
85843
+ resolvedCoderContext: projectContext.context,
85844
+ resolvedAgentSystemMessage: agent === null || agent === void 0 ? void 0 : agent.systemMessage,
85034
85845
  // Note: The isolated commit must never reach the remote, the merged commit on the original branch is pushed instead
85035
- options: { ...options.options, workspace: isolatedWorkspace, autoPush: false },
85846
+ options: { ...isolatedOptions, projectContext },
85036
85847
  });
85848
+ // Keep the display/commit identity in sync without writing to the original checkout before merging.
85849
+ Object.assign(nextPrompt.section, isolatedFile.sections[nextPrompt.section.index]);
85037
85850
  }
85038
85851
  catch (error) {
85039
85852
  // Note: The worktree keeps whatever the failed round produced, which would be lost by deleting it here
@@ -85054,7 +85867,7 @@ async function runIsolatedPromptRound(options) {
85054
85867
  await commitChanges(buildCommitMessage(nextPrompt.file, nextPrompt.section), {
85055
85868
  autoPush: options.options.autoPush,
85056
85869
  relevantPaths: await resolveCoderCommitScopePaths(originalProjectCommitScope),
85057
- projectPath: (_f = originalProjectCommitScope.repositoryRoot) !== null && _f !== void 0 ? _f : projectPath,
85870
+ projectPath: (_j = originalProjectCommitScope.repositoryRoot) !== null && _j !== void 0 ? _j : projectPath,
85058
85871
  });
85059
85872
  await removeCoderIsolationWorktree(worktree);
85060
85873
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merged \`${worktree.taskName}\` into \`${worktree.baseBranchName}\``);
@@ -85063,9 +85876,23 @@ async function runIsolatedPromptRound(options) {
85063
85876
  * Records a failed isolation merge in the original project and keeps the worktree for a manual merge.
85064
85877
  */
85065
85878
  async function recordIsolationMergeFailure(options, worktree, failureDetails) {
85066
- var _a, _b;
85879
+ var _a, _b, _c, _d;
85067
85880
  const { nextPrompt, runnerMetadata, isRichUiEnabled, uiHandle } = options;
85068
85881
  const mergeFailureError = buildCoderIsolationMergeFailureError(worktree, failureDetails);
85882
+ const repositoryRoot = (_b = (_a = options.options.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : worktree.projectPath;
85883
+ const tracePath = buildPromptRunTracePath(nextPrompt.file, nextPrompt.section);
85884
+ const isolatedTracePath = join(worktree.worktreePath, relative(repositoryRoot, tracePath));
85885
+ // The failed merge did not bring back the worktree's trace. Preserve it beside the failure report.
85886
+ await mkdir(dirname(tracePath), { recursive: true });
85887
+ let isTraceCopied = false;
85888
+ try {
85889
+ await copyFile(isolatedTracePath, tracePath);
85890
+ isTraceCopied = true;
85891
+ }
85892
+ catch (error) {
85893
+ if (error.code !== 'ENOENT')
85894
+ throw error;
85895
+ }
85069
85896
  markPromptIsolationMergeFailed(nextPrompt.file, nextPrompt.section, worktree);
85070
85897
  await writePromptFile(nextPrompt.file);
85071
85898
  const errorLogPath = await writePromptErrorLog({
@@ -85077,13 +85904,11 @@ async function recordIsolationMergeFailure(options, worktree, failureDetails) {
85077
85904
  });
85078
85905
  await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
85079
85906
  autoPush: options.options.autoPush,
85080
- projectPath: (_b = (_a = options.options.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : worktree.projectPath,
85081
- // Note: The round itself has already succeeded, so it has left its run trace in the original project.
85082
- // It belongs to this commit, which is the only one this task still gets.
85907
+ projectPath: (_d = (_c = options.options.workspace) === null || _c === void 0 ? void 0 : _c.repositoryRoot) !== null && _d !== void 0 ? _d : worktree.projectPath,
85083
85908
  relevantPaths: [
85084
85909
  nextPrompt.file.path,
85085
85910
  errorLogPath,
85086
- buildPromptRunTracePath(nextPrompt.file, nextPrompt.section),
85911
+ ...(isTraceCopied ? [tracePath] : []),
85087
85912
  ].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); }),
85088
85913
  });
85089
85914
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
@@ -85124,8 +85949,8 @@ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
85124
85949
  /**
85125
85950
  * Builds a label that identifies the prompt section by index.
85126
85951
  */
85127
- function buildPromptLabel(file, section) {
85128
- return `${relative(process.cwd(), file.path).replace(/\\/g, '/')}#${section.index + 1}`;
85952
+ function buildPromptLabel(file, section, projectPath = process.cwd()) {
85953
+ return `${relative(projectPath, file.path).replace(/\\/g, '/')}#${section.index + 1}`;
85129
85954
  }
85130
85955
 
85131
85956
  /**
@@ -85353,18 +86178,20 @@ function startCoderRunUiSubscriptionUsageRefresh(options) {
85353
86178
  }
85354
86179
 
85355
86180
  /**
85356
- * Creates the one queue prompt used to repair a pre-existing test failure.
86181
+ * Creates the one queue prompt used to repair a pre-existing project check failure.
85357
86182
  */
85358
- async function createTestBeforeRepairPrompt(options) {
86183
+ async function createCheckBeforeRepairPrompt(options) {
85359
86184
  const description = spaceTrim((block) => `
85360
- Fix the existing test failures before implementing any queued coding tasks.
86185
+ Fix the existing check failures before implementing any queued coding tasks.
86186
+
86187
+ The check command \`${options.checkCommand}\` failed before coding started. Leave the project ready for the remaining coding prompts.
85361
86188
 
85362
- 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.
86189
+ ${block(CHECK_REPAIR_INSTRUCTIONS)}
85363
86190
 
85364
- ## Verification output
86191
+ ## Check output
85365
86192
 
85366
86193
  \`\`\`
85367
- ${block(limitTestOutput(options.testOutput))}
86194
+ ${block(limitCheckOutput(options.checkOutput))}
85368
86195
  \`\`\`
85369
86196
  `);
85370
86197
  const createdPrompt = await addCoderPrompt({
@@ -85377,50 +86204,56 @@ async function createTestBeforeRepairPrompt(options) {
85377
86204
  const section = promptFile.sections[0];
85378
86205
  if (!section) {
85379
86206
  throw new UnexpectedError(spaceTrim(`
85380
- The pre-coding test repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
86207
+ The pre-coding check repair prompt was created at \`${createdPrompt.filePath}\` without a runnable section.
85381
86208
  `));
85382
86209
  }
85383
86210
  return { file: promptFile, section };
85384
86211
  }
85385
86212
 
85386
86213
  /**
85387
- * Runs the configured verification command before the first coding prompt.
86214
+ * Runs the configured check command before the first coding prompt.
85388
86215
  */
85389
- async function runTestBefore(options) {
85390
- var _a, _b;
85391
- const runPromptTestCommandExecutor = (_a = options.runPromptTestCommandExecutor) !== null && _a !== void 0 ? _a : runPromptTestCommand;
85392
- const initialTestStatusMessage = `Running initial tests before the agent coding starts: ${options.testCommand}`;
85393
- await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
85394
- checkpointLabel: 'running initial tests before the agent coding starts',
86216
+ async function runCheckBefore(options) {
86217
+ var _a, _b, _c, _d, _e, _f;
86218
+ (_a = options.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
86219
+ const runPromptCheckCommandExecutor = (_b = options.runPromptCheckCommandExecutor) !== null && _b !== void 0 ? _b : runPromptCheckCommand;
86220
+ const initialCheckStatusMessage = `Running initial checks before the agent coding starts: ${options.checkCommand}`;
86221
+ await ((_c = options.waitForPauseCheckpoint) === null || _c === void 0 ? void 0 : _c.call(options, {
86222
+ checkpointLabel: 'running initial checks before the agent coding starts',
85395
86223
  phase: 'verifying',
85396
- statusMessage: initialTestStatusMessage,
86224
+ statusMessage: initialCheckStatusMessage,
85397
86225
  }));
85398
- console.info(colors.gray(initialTestStatusMessage));
86226
+ (_d = options.signal) === null || _d === void 0 ? void 0 : _d.throwIfAborted();
86227
+ console.info(colors.gray(initialCheckStatusMessage));
85399
86228
  try {
85400
- const testOutput = await runPromptTestCommandExecutor({
85401
- command: options.testCommand,
86229
+ const checkOutput = await runPromptCheckCommandExecutor({
86230
+ command: options.checkCommand,
85402
86231
  projectPath: options.projectPath,
85403
86232
  scriptPath: buildTemporaryPromptScriptPath({
85404
86233
  projectPath: options.projectPath,
85405
86234
  scriptDirectoryName: 'coder-prompts',
85406
- sourceFileName: 'test-before',
86235
+ sourceFileName: 'check-before',
85407
86236
  }),
86237
+ ...(options.signal ? { signal: options.signal } : {}),
85408
86238
  });
85409
- console.info(colors.green('Pre-coding tests passed.'));
85410
- return { isPassed: true, testOutput };
86239
+ (_e = options.signal) === null || _e === void 0 ? void 0 : _e.throwIfAborted();
86240
+ console.info(colors.green('Pre-coding checks passed.'));
86241
+ return { isPassed: true, checkOutput };
85411
86242
  }
85412
86243
  catch (error) {
85413
- const testOutput = formatUnknownErrorMessage(error);
85414
- console.error(colors.red('Pre-coding tests failed.'));
85415
- console.error(testOutput);
85416
- return { isPassed: false, testOutput };
86244
+ if (error instanceof CoderCheckSetupError || ((_f = options.signal) === null || _f === void 0 ? void 0 : _f.aborted))
86245
+ throw error;
86246
+ const checkOutput = formatUnknownErrorMessage(error);
86247
+ console.error(colors.red('Pre-coding checks failed.'));
86248
+ console.error(checkOutput);
86249
+ return { isPassed: false, checkOutput };
85417
86250
  }
85418
86251
  }
85419
86252
 
85420
86253
  /**
85421
- * Commit message for files changed by a successful or failed pre-coding test in repair mode.
86254
+ * Commit message for files changed by a successful or failed pre-coding check in repair mode.
85422
86255
  */
85423
- const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-coding tests';
86256
+ const PRE_CODING_CHECK_CHANGES_COMMIT_MESSAGE = 'chore: Apply changes made by pre-coding checks';
85424
86257
  /**
85425
86258
  * Main entry point for running prompts with the selected agent.
85426
86259
  *
@@ -85429,10 +86262,15 @@ const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-
85429
86262
  * @public exported from `@promptbook/cli`
85430
86263
  */
85431
86264
  async function runCodexPrompts(providedOptions) {
85432
- var _a, _b, _c, _d;
85433
- const options = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
85434
- validateCoderRunOptions(options);
85435
- const projectPath = (_b = (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd();
86265
+ var _a, _b, _c, _d, _e, _f, _g, _h;
86266
+ const normalizedOptions = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
86267
+ validateCoderRunOptions(normalizedOptions);
86268
+ // CLI-preflighted workspaces are already canonical. For direct runs without an explicit project path, preserve
86269
+ // `process.cwd()` as written because macOS may expose the same temporary directory through `/var` and `/private/var`.
86270
+ 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
86271
+ ? normalizedOptions.projectPath
86272
+ : await resolveProjectDirectory(normalizedOptions.projectPath));
86273
+ const options = { ...normalizedOptions, projectPath };
85436
86274
  resetCoderRunControls();
85437
86275
  const runStartDate = moment();
85438
86276
  const { isRichUiEnabled, progressDisplay, uiHandle } = createRunDisplays(options, runStartDate);
@@ -85450,20 +86288,25 @@ async function runCodexPrompts(providedOptions) {
85450
86288
  startPauseListenerIfNeeded(isRichUiEnabled);
85451
86289
  let subscriptionUsageRefresh;
85452
86290
  try {
85453
- const resolvedCoderContext = await resolveCoderContext(options.context, projectPath);
86291
+ const projectContext = (_d = options.projectContext) !== null && _d !== void 0 ? _d : (await resolveCoderProjectContext({
86292
+ ...options,
86293
+ projectPath,
86294
+ }));
86295
+ if (await runDryRunIfRequested(options, (_e = projectContext.agentBook) === null || _e === void 0 ? void 0 : _e.agentReferences)) {
86296
+ return;
86297
+ }
86298
+ await assertProjectCheckIsConfigured(options.checkCommand, projectPath);
86299
+ const resolvedCoderContext = projectContext.context;
85454
86300
  const resolvedCoderAgent = await resolveCoderAgent(options.agent, projectPath, {
85455
- defaultRole: 'developer',
86301
+ defaultRole: DEFAULT_CODER_AGENT_ROLE,
85456
86302
  isInitializationAllowed: !options.dryRun,
85457
86303
  });
85458
86304
  const resolvedAgentSystemMessage = resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.systemMessage;
85459
- if (await runDryRunIfRequested(options, resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentReferences)) {
85460
- return;
85461
- }
85462
86305
  if (!options.noCommit && resolvedCoderAgent) {
85463
86306
  await commitInitializedAgentBooks(projectPath, resolvedCoderAgent.createdAgentBookPaths, options.workspace);
85464
86307
  }
85465
86308
  const { runner: harnessRunner, actualRunnerModel, runnerMetadata: harnessRunnerMetadata, } = resolvePromptRunner(options);
85466
- const runner = createCoderTeamPromptRunner(harnessRunner, options.agent);
86309
+ const runner = createCoderTeamPromptRunner(harnessRunner, options.agent, projectPath, (_f = options.workspace) === null || _f === void 0 ? void 0 : _f.repositoryRoot);
85467
86310
  // Note: The harness only knows itself, so the Book agent it runs as is joined here - this is the single
85468
86311
  // place where the whole run report of prompt status lines and run traces is put together
85469
86312
  const runnerMetadata = {
@@ -85494,7 +86337,7 @@ async function runCodexPrompts(providedOptions) {
85494
86337
  let previousRoundStartTime;
85495
86338
  let previousRoundEndTime;
85496
86339
  let completedRunCount = 0;
85497
- let hasRunTestBefore = false;
86340
+ let hasRunCheckBefore = false;
85498
86341
  // Note: Only the very first round resumes the interrupted prompt, every later round starts from a clean tree
85499
86342
  let isContinuingInterruptedPrompt = options.gitChanges === 'continue';
85500
86343
  while (just(true)) {
@@ -85509,11 +86352,11 @@ async function runCodexPrompts(providedOptions) {
85509
86352
  options,
85510
86353
  isRichUiEnabled,
85511
86354
  });
85512
- if (!hasRunTestBefore && options.testBefore !== 'no') {
86355
+ if (!hasRunCheckBefore && options.checkBefore !== 'no') {
85513
86356
  await waitForRequestedPause({
85514
- checkpointLabel: 'loading prompts before running initial tests',
86357
+ checkpointLabel: 'loading prompts before running initial checks',
85515
86358
  phase: 'loading',
85516
- statusMessage: 'Loading prompts before running initial tests...',
86359
+ statusMessage: 'Loading prompts before running initial checks...',
85517
86360
  });
85518
86361
  await loadPromptQueueSnapshot({
85519
86362
  options,
@@ -85524,8 +86367,8 @@ async function runCodexPrompts(providedOptions) {
85524
86367
  isContinuingInterruptedPrompt,
85525
86368
  });
85526
86369
  }
85527
- if (!hasRunTestBefore) {
85528
- hasWaitedForStart = await runTestBeforeIfNeeded({
86370
+ if (!hasRunCheckBefore) {
86371
+ hasWaitedForStart = await runCheckBeforeIfNeeded({
85529
86372
  options,
85530
86373
  runner,
85531
86374
  runnerMetadata,
@@ -85538,7 +86381,7 @@ async function runCodexPrompts(providedOptions) {
85538
86381
  hasWaitedForStart,
85539
86382
  isContinuingInterruptedPrompt,
85540
86383
  });
85541
- hasRunTestBefore = true;
86384
+ hasRunCheckBefore = true;
85542
86385
  }
85543
86386
  await waitForRequestedPause({
85544
86387
  checkpointLabel: 'loading prompts',
@@ -85554,6 +86397,7 @@ async function runCodexPrompts(providedOptions) {
85554
86397
  isContinuingInterruptedPrompt,
85555
86398
  });
85556
86399
  hasShownUpcomingTasks || (hasShownUpcomingTasks = showUpcomingTasksOnce({
86400
+ projectPath,
85557
86401
  hasShownUpcomingTasks,
85558
86402
  promptFiles: promptQueueSnapshot.promptFiles,
85559
86403
  stats: promptQueueSnapshot.stats,
@@ -85584,7 +86428,7 @@ async function runCodexPrompts(providedOptions) {
85584
86428
  return;
85585
86429
  }
85586
86430
  const nextPrompt = promptQueueSnapshot.nextPrompt;
85587
- const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
86431
+ const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section, projectPath);
85588
86432
  // Wait between prompt rounds (skipped for the first round)
85589
86433
  if (previousRoundStartTime !== undefined && previousRoundEndTime !== undefined) {
85590
86434
  await waitBetweenPromptRoundsIfNeeded({
@@ -85619,7 +86463,7 @@ async function runCodexPrompts(providedOptions) {
85619
86463
  phase: 'loading',
85620
86464
  statusMessage: 'Checking the working tree...',
85621
86465
  });
85622
- await ensureWorkingTreeClean((_d = options.workspace) === null || _d === void 0 ? void 0 : _d.repositoryRoot);
86466
+ await ensureWorkingTreeClean((_h = (_g = options.workspace) === null || _g === void 0 ? void 0 : _g.repositoryRoot) !== null && _h !== void 0 ? _h : projectPath);
85623
86467
  }
85624
86468
  const currentRoundStartTime = Date.now();
85625
86469
  // Note: An isolated round implements the prompt in a temporary worktree and merges it back afterwards
@@ -85681,7 +86525,7 @@ function isCleanWorkingTreeRequired(gitChanges, isContinuingInterruptedPrompt) {
85681
86525
  * Pulls the latest repository state before loading prompts when the feature is enabled.
85682
86526
  */
85683
86527
  async function pullLatestChangesIfEnabled(options) {
85684
- var _a;
86528
+ var _a, _b;
85685
86529
  const { options: runOptions, isRichUiEnabled } = options;
85686
86530
  if (!runOptions.autoPull || runOptions.dryRun) {
85687
86531
  return;
@@ -85689,7 +86533,7 @@ async function pullLatestChangesIfEnabled(options) {
85689
86533
  if (!isRichUiEnabled) {
85690
86534
  console.info(colors.gray('Pulling latest changes before the next prompt...'));
85691
86535
  }
85692
- await pullLatestChanges((_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot);
86536
+ await pullLatestChanges((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : runOptions.projectPath);
85693
86537
  }
85694
86538
  /**
85695
86539
  * Creates the progress display and rich UI handles used during the run.
@@ -85714,19 +86558,19 @@ function createRunDisplays(options, runStartDate) {
85714
86558
  * Normalizes legacy and current priority options into one validated run option shape.
85715
86559
  */
85716
86560
  function normalizeRunOptions(options) {
85717
- var _a, _b, _c, _d, _e, _f, _g;
85718
- const testBefore = (_a = options.testBefore) !== null && _a !== void 0 ? _a : 'no';
85719
- const normalizedTestCommand = (_b = options.testCommand) === null || _b === void 0 ? void 0 : _b.trim();
86561
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
86562
+ const checkBefore = (_a = options.checkBefore) !== null && _a !== void 0 ? _a : 'no';
85720
86563
  const priorityFilter = normalizePriorityFilter({
85721
86564
  priority: options.priority,
85722
- minimumPriority: (_c = options.minimumPriority) !== null && _c !== void 0 ? _c : (_d = options.priorityFilter) === null || _d === void 0 ? void 0 : _d.minimumPriority,
85723
- maximumPriority: (_e = options.maximumPriority) !== null && _e !== void 0 ? _e : (_f = options.priorityFilter) === null || _f === void 0 ? void 0 : _f.maximumPriority,
86565
+ minimumPriority: (_b = options.minimumPriority) !== null && _b !== void 0 ? _b : (_c = options.priorityFilter) === null || _c === void 0 ? void 0 : _c.minimumPriority,
86566
+ maximumPriority: (_d = options.maximumPriority) !== null && _d !== void 0 ? _d : (_e = options.priorityFilter) === null || _e === void 0 ? void 0 : _e.maximumPriority,
85724
86567
  });
85725
86568
  return {
85726
86569
  ...options,
85727
- testBefore,
85728
- testCommand: normalizedTestCommand || (testBefore === 'no' ? undefined : DEFAULT_CODER_TEST_COMMAND),
85729
- priority: (_g = priorityFilter.minimumPriority) !== null && _g !== void 0 ? _g : 0,
86570
+ 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(),
86571
+ checkBefore,
86572
+ checkCommand: resolveCoderCheckCommand((_j = options.checkCommand) === null || _j === void 0 ? void 0 : _j.trim(), checkBefore),
86573
+ priority: (_k = priorityFilter.minimumPriority) !== null && _k !== void 0 ? _k : 0,
85730
86574
  minimumPriority: priorityFilter.minimumPriority,
85731
86575
  maximumPriority: priorityFilter.maximumPriority,
85732
86576
  priorityFilter,
@@ -85735,63 +86579,63 @@ function normalizeRunOptions(options) {
85735
86579
  /**
85736
86580
  * Runs the optional pre-coding verification and, when requested, its one repair prompt.
85737
86581
  */
85738
- async function runTestBeforeIfNeeded(options) {
85739
- var _a, _b, _c, _d, _e;
86582
+ async function runCheckBeforeIfNeeded(options) {
86583
+ var _a, _b;
85740
86584
  const { options: runOptions, runner, runnerMetadata, resolvedCoderContext, resolvedAgentSystemMessage, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, hasWaitedForStart, isContinuingInterruptedPrompt, } = options;
85741
- if (runOptions.testBefore === 'no') {
86585
+ if (runOptions.checkBefore === 'no') {
85742
86586
  return hasWaitedForStart;
85743
86587
  }
85744
- if (!runOptions.testCommand) {
86588
+ if (!runOptions.checkCommand) {
85745
86589
  throw new NotAllowed(spaceTrim$1(`
85746
- ${'`--test-before ' + runOptions.testBefore + '`'} requires a verification command.
86590
+ ${'`--check-before ' + runOptions.checkBefore + '`'} requires a verification command.
85747
86591
 
85748
- Pass one with ${'`--test <test-command>`'} or use the default ${'`npm test`'} command by providing the mode through the CLI.
86592
+ Pass one with ${'`--check <check-command>`'} or use the default ${'`npm run check`'} command.
85749
86593
  `));
85750
86594
  }
85751
86595
  if (isCleanWorkingTreeRequired(runOptions.gitChanges, isContinuingInterruptedPrompt)) {
85752
86596
  await waitForRequestedPause({
85753
- checkpointLabel: 'checking the git working tree before testing',
86597
+ checkpointLabel: 'checking the git working tree before checking',
85754
86598
  phase: 'loading',
85755
- statusMessage: 'Checking the working tree before testing...',
86599
+ statusMessage: 'Checking the working tree before checking...',
85756
86600
  });
85757
- await ensureWorkingTreeClean((_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot);
86601
+ await ensureWorkingTreeClean((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.repositoryRoot) !== null && _b !== void 0 ? _b : runOptions.projectPath);
85758
86602
  }
85759
- const testBeforeCommitScope = await captureTestBeforeCommitScopeIfNeeded(runOptions);
86603
+ const checkBeforeCommitScope = await captureCheckBeforeCommitScopeIfNeeded(runOptions);
85760
86604
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
85761
- const testBeforeResult = await runTestBefore({
85762
- testCommand: runOptions.testCommand,
85763
- projectPath: (_c = (_b = runOptions.workspace) === null || _b === void 0 ? void 0 : _b.projectPath) !== null && _c !== void 0 ? _c : process.cwd(),
86605
+ const checkBeforeResult = await runCheckBefore({
86606
+ checkCommand: runOptions.checkCommand,
86607
+ projectPath: runOptions.projectPath,
85764
86608
  waitForPauseCheckpoint: waitForRequestedPause,
85765
86609
  }).finally(() => {
85766
86610
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
85767
86611
  });
85768
- await commitTestBeforeChangesIfNeeded({
86612
+ await commitCheckBeforeChangesIfNeeded({
85769
86613
  runOptions,
85770
- testBeforeCommitScope,
86614
+ checkBeforeCommitScope,
85771
86615
  waitForRequestedPause,
85772
86616
  });
85773
- if (testBeforeResult.isPassed) {
86617
+ if (checkBeforeResult.isPassed) {
85774
86618
  return hasWaitedForStart;
85775
86619
  }
85776
- const testOutput = limitTestOutput(testBeforeResult.testOutput);
85777
- if (runOptions.testBefore === 'yes-and-fail') {
86620
+ const checkOutput = limitCheckOutput(checkBeforeResult.checkOutput);
86621
+ if (runOptions.checkBefore === 'yes-and-fail') {
85778
86622
  throw new NotAllowed(spaceTrim$1((block) => `
85779
- Pre-coding verification command \`${runOptions.testCommand}\` failed.
86623
+ Pre-coding check command \`${runOptions.checkCommand}\` failed.
85780
86624
 
85781
86625
  The coding agent was not started because the project was already failing before the first queued prompt.
85782
86626
 
85783
- ### Test results
86627
+ ### Check results
85784
86628
  ${'```'}
85785
- ${block(testOutput)}
86629
+ ${block(checkOutput)}
85786
86630
  ${'```'}
85787
86631
  `));
85788
86632
  }
85789
- const repairPrompt = await createTestBeforeRepairPrompt({
85790
- projectPath: (_e = (_d = runOptions.workspace) === null || _d === void 0 ? void 0 : _d.projectPath) !== null && _e !== void 0 ? _e : process.cwd(),
85791
- testCommand: runOptions.testCommand,
85792
- testOutput,
86633
+ const repairPrompt = await createCheckBeforeRepairPrompt({
86634
+ projectPath: runOptions.projectPath,
86635
+ checkCommand: runOptions.checkCommand,
86636
+ checkOutput,
85793
86637
  });
85794
- const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section);
86638
+ const repairPromptLabel = buildPromptLabelForDisplay(repairPrompt.file, repairPrompt.section, runOptions.projectPath);
85795
86639
  const updatedHasWaitedForStart = await waitForPromptConfirmationIfNeeded({
85796
86640
  options: runOptions,
85797
86641
  nextPrompt: repairPrompt,
@@ -85819,36 +86663,36 @@ async function runTestBeforeIfNeeded(options) {
85819
86663
  return updatedHasWaitedForStart;
85820
86664
  }
85821
86665
  /**
85822
- * Captures the files present before a `yes-and-fix` pre-coding test, so only changes made by that test can be committed.
86666
+ * Captures the files present before a `yes-and-fix` pre-coding check, so only changes made by that check can be committed.
85823
86667
  */
85824
- async function captureTestBeforeCommitScopeIfNeeded(runOptions) {
86668
+ async function captureCheckBeforeCommitScopeIfNeeded(runOptions) {
85825
86669
  var _a;
85826
- if (runOptions.testBefore !== 'yes-and-fix' || runOptions.noCommit) {
86670
+ if (runOptions.checkBefore !== 'yes-and-fix' || runOptions.noCommit) {
85827
86671
  return undefined;
85828
86672
  }
85829
- return captureCoderCommitScope((_a = runOptions.workspace) !== null && _a !== void 0 ? _a : process.cwd());
86673
+ return captureCoderCommitScope((_a = runOptions.workspace) !== null && _a !== void 0 ? _a : runOptions.projectPath);
85830
86674
  }
85831
86675
  /**
85832
- * Commits files changed by a `yes-and-fix` pre-coding test before the coder continues to a queued or repair prompt.
86676
+ * Commits files changed by a `yes-and-fix` pre-coding check before the coder continues to a queued or repair prompt.
85833
86677
  */
85834
- async function commitTestBeforeChangesIfNeeded(options) {
86678
+ async function commitCheckBeforeChangesIfNeeded(options) {
85835
86679
  var _a;
85836
- const { runOptions, testBeforeCommitScope, waitForRequestedPause } = options;
85837
- if (!testBeforeCommitScope) {
86680
+ const { runOptions, checkBeforeCommitScope, waitForRequestedPause } = options;
86681
+ if (!checkBeforeCommitScope) {
85838
86682
  return;
85839
86683
  }
85840
- const relevantPaths = await resolveCoderCommitScopePaths(testBeforeCommitScope);
86684
+ const relevantPaths = await resolveCoderCommitScopePaths(checkBeforeCommitScope);
85841
86685
  if (relevantPaths.length === 0) {
85842
86686
  return;
85843
86687
  }
85844
86688
  await waitForRequestedPause({
85845
- checkpointLabel: 'committing changes made by pre-coding tests',
86689
+ checkpointLabel: 'committing changes made by pre-coding checks',
85846
86690
  phase: 'verifying',
85847
- statusMessage: 'Committing changes made by pre-coding tests...',
86691
+ statusMessage: 'Committing changes made by pre-coding checks...',
85848
86692
  });
85849
- await commitChanges(PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE, {
86693
+ await commitChanges(PRE_CODING_CHECK_CHANGES_COMMIT_MESSAGE, {
85850
86694
  autoPush: runOptions.autoPush,
85851
- projectPath: (_a = testBeforeCommitScope.repositoryRoot) !== null && _a !== void 0 ? _a : testBeforeCommitScope.projectPath,
86695
+ projectPath: (_a = checkBeforeCommitScope.repositoryRoot) !== null && _a !== void 0 ? _a : checkBeforeCommitScope.projectPath,
85852
86696
  relevantPaths,
85853
86697
  });
85854
86698
  }
@@ -85894,7 +86738,6 @@ function startPauseListenerIfNeeded(isRichUiEnabled) {
85894
86738
  * Runs the dry-run reporting mode and returns whether the main execution should stop.
85895
86739
  */
85896
86740
  async function runDryRunIfRequested(options, agentReferences) {
85897
- var _a, _b;
85898
86741
  if (!options.dryRun) {
85899
86742
  return false;
85900
86743
  }
@@ -85903,7 +86746,7 @@ async function runDryRunIfRequested(options, agentReferences) {
85903
86746
  modelName: options.agentName ? resolveRunnerModel(options.agentName, options.model) : options.model,
85904
86747
  agentReferences,
85905
86748
  };
85906
- const promptFiles = (await loadPromptFiles(join((_b = (_a = options.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd(), 'prompts'), {
86749
+ const promptFiles = (await loadPromptFiles(join(options.projectPath, 'prompts'), {
85907
86750
  isMissingDirectoryAllowed: true,
85908
86751
  })).map((file) => ({
85909
86752
  ...file,
@@ -85912,7 +86755,7 @@ async function runDryRunIfRequested(options, agentReferences) {
85912
86755
  const stats = summarizePrompts(promptFiles, options.priorityFilter);
85913
86756
  printStats(stats, options.priorityFilter);
85914
86757
  console.info(colors.yellow('Following prompts need to be written:'));
85915
- printPromptsToBeWritten(promptFiles, options.priorityFilter);
86758
+ printPromptsToBeWritten(promptFiles, options.priorityFilter, options.projectPath);
85916
86759
  return true;
85917
86760
  }
85918
86761
  /**
@@ -85927,7 +86770,7 @@ function initializeRunUi(uiHandle, runnerName, actualRunnerModel, options) {
85927
86770
  serverUrl: options.serverUrl,
85928
86771
  priorityFilter: options.priorityFilter,
85929
86772
  limit: options.limit,
85930
- testCommand: options.testCommand,
86773
+ checkCommand: options.checkCommand,
85931
86774
  });
85932
86775
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
85933
86776
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Running prompts with ${runnerName}`);
@@ -85950,10 +86793,9 @@ async function initializeRunUiAgentVisual(uiHandle, agentSource) {
85950
86793
  * Loads prompt files, updates progress displays, and selects the next runnable prompt.
85951
86794
  */
85952
86795
  async function loadPromptQueueSnapshot(options) {
85953
- var _a, _b;
85954
86796
  const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle, promptRunnerIdentity, isContinuingInterruptedPrompt, } = options;
85955
86797
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(undefined);
85956
- const promptFiles = await loadPromptFiles(join((_b = (_a = runOptions.workspace) === null || _a === void 0 ? void 0 : _a.projectPath) !== null && _b !== void 0 ? _b : process.cwd(), 'prompts'));
86798
+ const promptFiles = await loadPromptFiles(join(runOptions.projectPath, 'prompts'));
85957
86799
  const stats = summarizePrompts(promptFiles, runOptions.priorityFilter);
85958
86800
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.update(stats);
85959
86801
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(stats);
@@ -85980,10 +86822,10 @@ function showUpcomingTasksOnce(options) {
85980
86822
  }
85981
86823
  if (stats.toBeWritten > 0) {
85982
86824
  console.info(colors.yellow('Following prompts need to be written:'));
85983
- printPromptsToBeWritten(promptFiles, priorityFilter);
86825
+ printPromptsToBeWritten(promptFiles, priorityFilter, options.projectPath);
85984
86826
  console.info('');
85985
86827
  }
85986
- printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity));
86828
+ printUpcomingTasks(listUpcomingTasks(promptFiles, priorityFilter, promptRunnerIdentity, options.projectPath));
85987
86829
  return true;
85988
86830
  }
85989
86831
  /**
@@ -86180,12 +87022,14 @@ function buildCoderServerPromptFileResponses(options) {
86180
87022
  return [
86181
87023
  ...options.promptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
86182
87024
  promptFile,
87025
+ projectPath: options.projectPath,
86183
87026
  isFinished: false,
86184
87027
  priorityFilter: options.priorityFilter,
86185
87028
  activePrompt,
86186
87029
  })),
86187
87030
  ...options.finishedPromptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
86188
87031
  promptFile,
87032
+ projectPath: options.projectPath,
86189
87033
  isFinished: true,
86190
87034
  priorityFilter: options.priorityFilter,
86191
87035
  activePrompt,
@@ -86196,13 +87040,15 @@ function buildCoderServerPromptFileResponses(options) {
86196
87040
  * Builds one prompt-file response.
86197
87041
  */
86198
87042
  function buildCoderServerPromptFileResponse(options) {
87043
+ var _a;
86199
87044
  const { promptFile, isFinished, priorityFilter, activePrompt } = options;
86200
87045
  return {
86201
87046
  filePath: promptFile.path,
86202
87047
  fileName: promptFile.name,
86203
- relativeFilePath: relative(process.cwd(), promptFile.path).replace(/\\/gu, '/'),
87048
+ relativeFilePath: relative((_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd(), promptFile.path).replace(/\\/gu, '/'),
86204
87049
  isFinished,
86205
87050
  sections: promptFile.sections.map((section) => buildCoderServerPromptSectionResponse({
87051
+ projectPath: options.projectPath,
86206
87052
  promptFile,
86207
87053
  section,
86208
87054
  isFinished,
@@ -86217,7 +87063,7 @@ function buildCoderServerPromptFileResponse(options) {
86217
87063
  function buildCoderServerPromptSectionResponse(options) {
86218
87064
  const { promptFile, section, isFinished, priorityFilter, activePrompt } = options;
86219
87065
  const isUnwritten = isPromptToBeWritten(promptFile, section);
86220
- const isActive = isPromptActive(promptFile, section, activePrompt);
87066
+ const isActive = isPromptActive(promptFile, section, activePrompt, options.projectPath);
86221
87067
  const column = getPromptColumn({
86222
87068
  section,
86223
87069
  isFinished,
@@ -86232,7 +87078,7 @@ function buildCoderServerPromptSectionResponse(options) {
86232
87078
  priority: section.priority,
86233
87079
  summary: buildPromptSummary(promptFile, section),
86234
87080
  content: buildCodexPrompt(promptFile, section),
86235
- label: buildPromptLabelForDisplay(promptFile, section),
87081
+ label: buildPromptLabelForDisplay(promptFile, section, options.projectPath),
86236
87082
  tags: buildPromptTags({
86237
87083
  section,
86238
87084
  isUnwritten,
@@ -86309,11 +87155,11 @@ function getActivePrompt(uiState) {
86309
87155
  /**
86310
87156
  * Checks whether a parsed section is the prompt currently handled by the active agent.
86311
87157
  */
86312
- function isPromptActive(promptFile, section, activePrompt) {
87158
+ function isPromptActive(promptFile, section, activePrompt, projectPath) {
86313
87159
  if (!activePrompt || (section.status !== 'todo' && section.status !== 'in-progress')) {
86314
87160
  return false;
86315
87161
  }
86316
- return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
87162
+ return buildPromptLabelForDisplay(promptFile, section, projectPath) === activePrompt.label;
86317
87163
  }
86318
87164
  // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/buildCoderServerPromptResponse.ts) should never be published outside of `@promptbook/cli`
86319
87165
 
@@ -87234,6 +88080,7 @@ async function loadPromptsForApi(options) {
87234
88080
  const promptFiles = await loadPromptFilesSafely(promptsDir);
87235
88081
  const finishedPromptFiles = await loadPromptFilesSafely(join(promptsDir, FINISHED_PROMPTS_DIRECTORY_NAME));
87236
88082
  return buildCoderServerPromptFileResponses({
88083
+ projectPath: dirname(promptsDir),
87237
88084
  promptFiles,
87238
88085
  finishedPromptFiles,
87239
88086
  priorityFilter,
@@ -87320,18 +88167,20 @@ function jsonHeaders() {
87320
88167
  * @private internal function of `ptbk coder server`
87321
88168
  */
87322
88169
  async function runCodexPromptsServer(options) {
87323
- var _a, _b, _c, _d;
87324
- const { port, ...runOptions } = options;
87325
- if (runOptions.dryRun) {
87326
- await runCodexPrompts(runOptions);
88170
+ var _a, _b, _c, _d, _e;
88171
+ const { port, ...providedOptions } = options;
88172
+ if (providedOptions.dryRun) {
88173
+ await runCodexPrompts(providedOptions);
87327
88174
  return;
87328
88175
  }
88176
+ const workspace = (_a = providedOptions.workspace) !== null && _a !== void 0 ? _a : await $resolveWorkspaceRepository(providedOptions.projectPath);
88177
+ const runOptions = { ...providedOptions, workspace, projectPath: workspace.projectPath };
87329
88178
  const serverUrl = `http://localhost:${port}`;
87330
88179
  const uiState = new CoderRunUiState(moment());
87331
88180
  const priorityFilter = normalizePriorityFilter({
87332
88181
  priority: runOptions.priority,
87333
- minimumPriority: (_a = runOptions.minimumPriority) !== null && _a !== void 0 ? _a : (_b = runOptions.priorityFilter) === null || _b === void 0 ? void 0 : _b.minimumPriority,
87334
- maximumPriority: (_c = runOptions.maximumPriority) !== null && _c !== void 0 ? _c : (_d = runOptions.priorityFilter) === null || _d === void 0 ? void 0 : _d.maximumPriority,
88182
+ minimumPriority: (_b = runOptions.minimumPriority) !== null && _b !== void 0 ? _b : (_c = runOptions.priorityFilter) === null || _c === void 0 ? void 0 : _c.minimumPriority,
88183
+ maximumPriority: (_d = runOptions.maximumPriority) !== null && _d !== void 0 ? _d : (_e = runOptions.priorityFilter) === null || _e === void 0 ? void 0 : _e.maximumPriority,
87335
88184
  });
87336
88185
  const serverHandle = startCoderHttpServer({
87337
88186
  port,