@promptbook/cli 0.114.0-4 → 0.114.0-5

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 (46) hide show
  1. package/README.md +3 -3
  2. package/esm/index.es.js +268 -74
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/scripts/run-codex-prompts/common/createCoderRunStepTracker.d.ts +48 -0
  5. package/esm/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +43 -0
  6. package/esm/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +6 -3
  7. package/esm/scripts/run-codex-prompts/prompts/isPromptSectionUnfinished.d.ts +8 -0
  8. package/esm/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
  9. package/esm/scripts/run-codex-prompts/prompts/markPromptInProgress.d.ts +27 -0
  10. package/esm/scripts/run-codex-prompts/prompts/resolvePromptStatusLine.d.ts +19 -0
  11. package/esm/scripts/run-codex-prompts/prompts/writePromptStatusLine.d.ts +9 -0
  12. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +1 -1
  13. package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  14. package/esm/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  15. package/esm/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +6 -0
  16. package/esm/scripts/verify-prompts/$orderPromptFiles.d.ts +9 -0
  17. package/esm/scripts/verify-prompts/VerifyPromptsOrder.d.ts +26 -0
  18. package/esm/scripts/verify-prompts/verify-prompts.d.ts +3 -2
  19. package/esm/src/cli/cli-commands/coder/verify.d.ts +1 -1
  20. package/esm/src/version.d.ts +1 -1
  21. package/package.json +1 -1
  22. package/src/cli/cli-commands/coder/verify.ts +29 -5
  23. package/src/other/templates/getTemplatesPipelineCollection.ts +704 -843
  24. package/src/version.ts +2 -2
  25. package/src/versions.txt +1 -0
  26. package/umd/index.umd.js +268 -74
  27. package/umd/index.umd.js.map +1 -1
  28. package/umd/scripts/run-codex-prompts/common/createCoderRunStepTracker.d.ts +48 -0
  29. package/umd/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +43 -0
  30. package/umd/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +6 -3
  31. package/umd/scripts/run-codex-prompts/prompts/isPromptSectionUnfinished.d.ts +8 -0
  32. package/umd/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
  33. package/umd/scripts/run-codex-prompts/prompts/markPromptInProgress.d.ts +27 -0
  34. package/umd/scripts/run-codex-prompts/prompts/resolvePromptStatusLine.d.ts +19 -0
  35. package/umd/scripts/run-codex-prompts/prompts/writePromptStatusLine.d.ts +9 -0
  36. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +1 -1
  37. package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  38. package/umd/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  39. package/umd/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +6 -0
  40. package/umd/scripts/verify-prompts/$orderPromptFiles.d.ts +9 -0
  41. package/umd/scripts/verify-prompts/VerifyPromptsOrder.d.ts +26 -0
  42. package/umd/scripts/verify-prompts/verify-prompts.d.ts +3 -2
  43. package/umd/src/cli/cli-commands/coder/verify.d.ts +1 -1
  44. package/umd/src/version.d.ts +1 -1
  45. package/esm/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +0 -7
  46. package/umd/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +0 -7
package/src/version.ts CHANGED
@@ -16,11 +16,11 @@ export const BOOK_LANGUAGE_VERSION: string_semantic_version = '2.0.0';
16
16
  * @generated
17
17
  * @see https://github.com/webgptorg/promptbook
18
18
  */
19
- export const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version = '0.114.0-4';
19
+ export const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version = '0.114.0-5';
20
20
 
21
21
  /**
22
22
  * Represents the version string of the Promptbook engine.
23
- * It follows semantic versioning (e.g., `0.114.0-3`).
23
+ * It follows semantic versioning (e.g., `0.114.0-4`).
24
24
  *
25
25
  * @generated
26
26
  */
package/src/versions.txt CHANGED
@@ -1182,3 +1182,4 @@
1182
1182
  0.114.0-2
1183
1183
  0.114.0-3
1184
1184
  0.114.0-4
1185
+ 0.114.0-5
package/umd/index.umd.js CHANGED
@@ -58,7 +58,7 @@
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-4';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-5';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -29758,6 +29758,32 @@
29758
29758
  `);
29759
29759
  }
29760
29760
 
29761
+ /**
29762
+ * Creates the step tracker of one prompt round.
29763
+ *
29764
+ * The tracker is the single place which knows which steps have finished and which one is running,
29765
+ * so both the finished `[x]` status line and the intermediate `[^]` in-progress status lines are
29766
+ * built from the very same data.
29767
+ */
29768
+ function createCoderRunStepTracker(onStepStarted) {
29769
+ const steps = [];
29770
+ let reportedLoginMethod;
29771
+ return {
29772
+ steps,
29773
+ async startStep(kind) {
29774
+ await (onStepStarted === null || onStepStarted === void 0 ? void 0 : onStepStarted({
29775
+ startedStepKind: kind,
29776
+ finishedSteps: [...steps],
29777
+ loginMethod: reportedLoginMethod,
29778
+ }));
29779
+ },
29780
+ finishStep(step, loginMethod) {
29781
+ steps.push(step);
29782
+ reportedLoginMethod = loginMethod !== null && loginMethod !== void 0 ? loginMethod : reportedLoginMethod;
29783
+ },
29784
+ };
29785
+ }
29786
+
29761
29787
  /**
29762
29788
  * Maximum amount of test output embedded into an agent prompt.
29763
29789
  */
@@ -29803,7 +29829,7 @@
29803
29829
  async function runPromptWithTestFeedback(options) {
29804
29830
  var _a, _b, _c, _d;
29805
29831
  const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
29806
- const steps = [];
29832
+ const stepTracker = createCoderRunStepTracker(options.onStepStarted);
29807
29833
  if (!normalizedTestCommand) {
29808
29834
  (_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
29809
29835
  await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, 1);
@@ -29811,9 +29837,9 @@
29811
29837
  runOptions: options,
29812
29838
  prompt: options.prompt,
29813
29839
  kind: 'implementation',
29814
- steps,
29840
+ stepTracker,
29815
29841
  });
29816
- return { ...result, attemptCount: 1, steps };
29842
+ return { ...result, attemptCount: 1, steps: stepTracker.steps };
29817
29843
  }
29818
29844
  const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
29819
29845
  let promptForCurrentAttempt = options.prompt;
@@ -29824,7 +29850,7 @@
29824
29850
  runOptions: options,
29825
29851
  prompt: promptForCurrentAttempt,
29826
29852
  kind: attemptCount === 1 ? 'implementation' : 'fixing',
29827
- steps,
29853
+ stepTracker,
29828
29854
  });
29829
29855
  await waitForVerificationPauseCheckpoint(options.waitForPauseCheckpoint, normalizedTestCommand, attemptCount);
29830
29856
  console.info(colors__default["default"].gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
@@ -29832,10 +29858,10 @@
29832
29858
  runPromptTestCommandExecutor,
29833
29859
  testCommand: normalizedTestCommand,
29834
29860
  runOptions: options,
29835
- steps,
29861
+ stepTracker,
29836
29862
  });
29837
29863
  if (failedVerification === undefined) {
29838
- return { ...result, attemptCount, steps };
29864
+ return { ...result, attemptCount, steps: stepTracker.steps };
29839
29865
  }
29840
29866
  const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
29841
29867
  const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
@@ -29861,7 +29887,8 @@
29861
29887
  * Runs one coding attempt through the runner, timing it and recording it as an implementation or fixing step.
29862
29888
  */
29863
29889
  async function runRunnerPromptStep(options) {
29864
- const { runOptions, prompt, kind, steps } = options;
29890
+ const { runOptions, prompt, kind, stepTracker } = options;
29891
+ await stepTracker.startStep(kind);
29865
29892
  const stepStartedTimeMs = Date.now();
29866
29893
  const result = await runOptions.runner.runPrompt({
29867
29894
  prompt,
@@ -29871,7 +29898,7 @@
29871
29898
  preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
29872
29899
  waitForPauseCheckpoint: runOptions.waitForPauseCheckpoint,
29873
29900
  });
29874
- steps.push({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs });
29901
+ stepTracker.finishStep({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs }, result.loginMethod);
29875
29902
  return result;
29876
29903
  }
29877
29904
  /**
@@ -29879,7 +29906,8 @@
29879
29906
  * returns the failure (or `undefined` when the verification passed).
29880
29907
  */
29881
29908
  async function runVerificationStep(options) {
29882
- const { runPromptTestCommandExecutor, testCommand, runOptions, steps } = options;
29909
+ const { runPromptTestCommandExecutor, testCommand, runOptions, stepTracker } = options;
29910
+ await stepTracker.startStep('testing');
29883
29911
  const stepStartedTimeMs = Date.now();
29884
29912
  try {
29885
29913
  await runPromptTestCommandExecutor({
@@ -29895,7 +29923,7 @@
29895
29923
  return { error };
29896
29924
  }
29897
29925
  finally {
29898
- steps.push({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
29926
+ stepTracker.finishStep({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
29899
29927
  }
29900
29928
  }
29901
29929
  /**
@@ -41182,6 +41210,45 @@
41182
41210
  // Note: [🟡] Code for CLI command [server](src/cli/cli-commands/coder/server.ts) should never be published outside of `@promptbook/cli`
41183
41211
  // Note: [💞] Ignore a discrepancy between file name and entity name
41184
41212
 
41213
+ /**
41214
+ * Orders supported by `ptbk coder verify --order`.
41215
+ */
41216
+ const VERIFY_PROMPTS_ORDER_VALUES = ['from-earliest', 'from-latest', 'random'];
41217
+ /**
41218
+ * Order used when `--order` is not provided.
41219
+ */
41220
+ const DEFAULT_VERIFY_PROMPTS_ORDER = 'from-earliest';
41221
+ /**
41222
+ * Human-readable description of each supported order, shared by the CLI help and the verification output.
41223
+ */
41224
+ const VERIFY_PROMPTS_ORDER_DESCRIPTIONS = {
41225
+ 'from-earliest': 'from the earliest prompt file',
41226
+ 'from-latest': 'from the latest prompt file',
41227
+ random: 'in random order',
41228
+ };
41229
+ /**
41230
+ * Parses and validates one raw `--order` value.
41231
+ *
41232
+ * Note: `ptbk coder verify` lets Commander validate the value, this is used by the standalone script which parses the raw arguments itself
41233
+ */
41234
+ function parseVerifyPromptsOrder(orderValue) {
41235
+ if (orderValue === undefined) {
41236
+ return DEFAULT_VERIFY_PROMPTS_ORDER;
41237
+ }
41238
+ if (VERIFY_PROMPTS_ORDER_VALUES.includes(orderValue)) {
41239
+ return orderValue;
41240
+ }
41241
+ throw new NotAllowed(_spaceTrim.spaceTrim((block) => `
41242
+ Invalid value for \`--order\`: \`${orderValue}\`.
41243
+
41244
+ Use one of the supported orders:
41245
+ ${block(VERIFY_PROMPTS_ORDER_VALUES.map((value) => `- \`${value}\` processes the prompt files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[value]}`).join('\n'))}
41246
+
41247
+ Actionable hint:
41248
+ - The removed \`--reverse\` flag is now \`--order from-latest\`.
41249
+ `));
41250
+ }
41251
+
41185
41252
  /**
41186
41253
  * Initializes `coder verify` command for Promptbook CLI utilities
41187
41254
  *
@@ -41201,21 +41268,27 @@
41201
41268
  - Auto-appends repair prompts for incomplete work
41202
41269
  - Processes files with all-done prompts first
41203
41270
  - Supports ignoring matching prompt candidates for one verification run
41271
+ - Supports processing the prompt files from the earliest, from the latest or in random order
41204
41272
 
41205
41273
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
41206
41274
 
41207
41275
  Note: The git synchronization is applied around each single verification, not once per run.
41208
41276
  `));
41209
- command.option('--reverse', 'Process prompt files in reverse order', false);
41277
+ command.addOption(new commander.Option('--order <order>', _spaceTrim.spaceTrim((block) => `
41278
+ Order in which the prompt files are processed:
41279
+ ${block(VERIFY_PROMPTS_ORDER_VALUES.map((order) => `- ${order}: ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[order]}`).join('\n'))}
41280
+ `))
41281
+ .choices([...VERIFY_PROMPTS_ORDER_VALUES])
41282
+ .default(DEFAULT_VERIFY_PROMPTS_ORDER));
41210
41283
  command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
41211
41284
  addCoderGitSyncOptions(command);
41212
41285
  command.action(handleActionErrors(async (cliOptions) => {
41213
- const { reverse, ignore } = cliOptions;
41286
+ const { order, ignore } = cliOptions;
41214
41287
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
41215
41288
  // Note: Import the main function dynamically to avoid loading heavy dependencies until needed
41216
41289
  const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
41217
41290
  try {
41218
- await verifyPrompts({ reverse, ignore, gitSync });
41291
+ await verifyPrompts({ order, ignore, gitSync });
41219
41292
  }
41220
41293
  catch (error) {
41221
41294
  console.error(colors__default["default"].bgRed('Prompt verification failed:'), error);
@@ -70360,7 +70433,7 @@
70360
70433
  }
70361
70434
  /**
70362
70435
  * Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
70363
- * For [x] done and [!] failed prompts, allow metadata after the status marker.
70436
+ * For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
70364
70437
  */
70365
70438
  function parseStatusLine(line) {
70366
70439
  var _a, _b, _c, _d;
@@ -70374,6 +70447,11 @@
70374
70447
  if (failedMatch) {
70375
70448
  return { status: 'failed', priority: 0 };
70376
70449
  }
70450
+ // For in-progress prompts [^], allow any content after (for the steps recorded so far)
70451
+ const inProgressMatch = line.match(/^\[(?<status>\^)\]/);
70452
+ if (inProgressMatch) {
70453
+ return { status: 'in-progress', priority: 0 };
70454
+ }
70377
70455
  // For not-ready [-], keep the historical clean-line syntax.
70378
70456
  if (/^\[-\]\s*!*\s*$/u.test(line)) {
70379
70457
  return { status: 'not-ready', priority: 0 };
@@ -72953,14 +73031,25 @@
72953
73031
  fixing: 'Fixing',
72954
73032
  };
72955
73033
  /**
72956
- * Formats the per-step usage breakdown recorded for one finished prompt.
73034
+ * Suffix marking the one step which has already started but has neither a price nor a duration yet.
73035
+ */
73036
+ const IN_PROGRESS_STEP_SUFFIX = 'in progress';
73037
+ /**
73038
+ * Formats the per-step usage breakdown recorded for one prompt.
72957
73039
  *
72958
73040
  * Produces a `; `-separated summary such as
72959
73041
  * `Implementation $8.01 6 hours; Testing 1 hour; Fixing $3.14 2 hours` where each coding step carries its
72960
73042
  * price and duration and each verification step carries only its duration.
73043
+ *
73044
+ * @param steps - Steps which have already finished
73045
+ * @param inProgressStepKind - Step which has started but has not finished yet, appended as `Testing in progress`
72961
73046
  */
72962
- function formatCoderRunSteps(steps) {
72963
- return steps.map(formatCoderRunStep).join('; ');
73047
+ function formatCoderRunSteps(steps, inProgressStepKind) {
73048
+ const formattedSteps = steps.map(formatCoderRunStep);
73049
+ if (inProgressStepKind !== undefined) {
73050
+ formattedSteps.push(`${CODER_RUN_STEP_LABELS[inProgressStepKind]} ${IN_PROGRESS_STEP_SUFFIX}`);
73051
+ }
73052
+ return formattedSteps.join('; ');
72964
73053
  }
72965
73054
  /**
72966
73055
  * Formats one coder run step as `Label $price duration`, omitting the price for steps without model usage.
@@ -72989,47 +73078,78 @@
72989
73078
  }
72990
73079
 
72991
73080
  /**
72992
- * Replaces the complete todo status line while preserving its indentation.
73081
+ * Builds the shared body of a prompt status line, used by both the in-progress `[^]` and the done `[x]` status.
72993
73082
  *
72994
- * The complete line is replaced because a todo status can contain a required
72995
- * model/harness token in addition to priority markers.
73083
+ * Produces details such as
73084
+ * ``by OpenAI Codex `gpt-5.6-luna` thinking `max` (ChatGPT account) - Implementation ~$0.2036 10 minutes``.
72996
73085
  */
72997
- function replacePromptTodoStatusLine(line, replacementStatusLine) {
72998
- return line.replace(/^(?<indentation>\s*)\[\s*\].*$/u, `$<indentation>${replacementStatusLine}`);
73086
+ function buildPromptStatusDetails(options) {
73087
+ const { steps, inProgressStepKind, runnerName, modelName, attemptCount, loginMethod, thinkingLevel } = options;
73088
+ const runnerSignature = formatRunnerSignature(runnerName, modelName, thinkingLevel);
73089
+ const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
73090
+ const loginMethodLabel = formatCodexLoginMethod(loginMethod);
73091
+ const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
73092
+ const stepsSummary = formatCoderRunSteps(steps, inProgressStepKind);
73093
+ const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
73094
+ return `${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`;
72999
73095
  }
73000
73096
 
73001
73097
  /**
73002
- * Marks a prompt section as done and records the per-step usage pricing and runner details.
73098
+ * Resolves the status line of one prompt section so it can be rewritten.
73003
73099
  */
73004
- function markPromptDone(file, section, steps, runnerName, modelName, attemptCount = 1, loginMethod, thinkingLevel) {
73100
+ function resolvePromptStatusLine(file, section) {
73005
73101
  if (section.statusLineIndex === undefined) {
73006
- throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
73102
+ throw new UnexpectedError(_spaceTrim.spaceTrim(`
73103
+ Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
73104
+ `));
73007
73105
  }
73008
73106
  const line = file.lines[section.statusLineIndex];
73009
73107
  if (line === undefined) {
73010
- throw new Error(`Prompt ${section.index + 1} in ${file.name} points to a missing status line.`);
73108
+ throw new UnexpectedError(_spaceTrim.spaceTrim(`
73109
+ Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
73110
+ `));
73011
73111
  }
73012
- const runnerSignature = formatRunnerSignature(runnerName, modelName, thinkingLevel);
73013
- const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
73014
- const loginMethodLabel = formatCodexLoginMethod(loginMethod);
73015
- const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
73016
- const stepsSummary = formatCoderRunSteps(steps);
73017
- const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
73018
- // Replace the complete todo status, including any required model/harness token.
73019
- file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[x] ${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`);
73112
+ return { statusLineIndex: section.statusLineIndex, line };
73113
+ }
73114
+
73115
+ /**
73116
+ * Matches a complete todo `[ ]` or in-progress `[^]` status line, capturing its indentation.
73117
+ *
73118
+ * The complete line is replaced because a todo status can contain a required model/harness token
73119
+ * in addition to its priority markers, and an in-progress status already carries the metadata of
73120
+ * the steps recorded so far.
73121
+ */
73122
+ const REWRITABLE_PROMPT_STATUS_LINE_PATTERN = /^(?<indentation>\s*)\[(?:\s*|\^)\].*$/u;
73123
+ /**
73124
+ * Rewrites the status line of one prompt section while preserving its indentation.
73125
+ *
73126
+ * Only a todo `[ ]` or an in-progress `[^]` status line is rewritten, so an already finalized
73127
+ * `[x]`, `[!]` or `[-]` status is never overwritten by accident.
73128
+ */
73129
+ function writePromptStatusLine(file, section, replacementStatusLine) {
73130
+ const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
73131
+ file.lines[statusLineIndex] = line.replace(REWRITABLE_PROMPT_STATUS_LINE_PATTERN, `$<indentation>${replacementStatusLine}`);
73132
+ }
73133
+
73134
+ /**
73135
+ * Marks a prompt section as done and records the per-step usage pricing and runner details.
73136
+ */
73137
+ function markPromptDone(file, section, steps, runnerName, modelName, attemptCount = 1, loginMethod, thinkingLevel) {
73138
+ const statusDetails = buildPromptStatusDetails({
73139
+ steps,
73140
+ runnerName,
73141
+ modelName,
73142
+ attemptCount,
73143
+ loginMethod,
73144
+ thinkingLevel,
73145
+ });
73146
+ writePromptStatusLine(file, section, `[x] ${statusDetails}`);
73020
73147
  }
73021
73148
 
73022
73149
  /**
73023
73150
  * Marks a prompt section as failed and records runner details.
73024
73151
  */
73025
73152
  function markPromptFailed(file, section, runnerName, modelName, promptExecutionStartedDate, attemptCount = 1) {
73026
- if (section.statusLineIndex === undefined) {
73027
- throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
73028
- }
73029
- const line = file.lines[section.statusLineIndex];
73030
- if (line === undefined) {
73031
- throw new Error(`Prompt ${section.index + 1} in ${file.name} points to a missing status line.`);
73032
- }
73033
73153
  const runnerSignature = formatRunnerSignature(runnerName, modelName);
73034
73154
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
73035
73155
  const duration = moment__default["default"]().diff(promptExecutionStartedDate);
@@ -73037,7 +73157,18 @@
73037
73157
  const failureDetails = attemptMetadata === ''
73038
73158
  ? `failed after ${durationString} by ${runnerSignature}`
73039
73159
  : `${attemptMetadata}${durationString} by ${runnerSignature}`;
73040
- file.lines[section.statusLineIndex] = replacePromptTodoStatusLine(line, `[!] ${failureDetails}`);
73160
+ writePromptStatusLine(file, section, `[!] ${failureDetails}`);
73161
+ }
73162
+
73163
+ /**
73164
+ * Marks a prompt section as being implemented right now and records the steps finished so far.
73165
+ *
73166
+ * The `[^]` status is deliberately never reverted: when the coder is killed or crashes, the status stays
73167
+ * in the prompt file as the signal that this task was left in the middle of its implementation.
73168
+ */
73169
+ function markPromptInProgress(options) {
73170
+ const { file, section, ...statusDetailsOptions } = options;
73171
+ writePromptStatusLine(file, section, `[^] ${buildPromptStatusDetails(statusDetailsOptions)}`);
73041
73172
  }
73042
73173
 
73043
73174
  /**
@@ -73146,6 +73277,13 @@
73146
73277
  attemptCount = nextAttemptCount;
73147
73278
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
73148
73279
  },
73280
+ onStepStarted: (progress) => recordPromptRoundInProgress({
73281
+ nextPrompt,
73282
+ runnerMetadata,
73283
+ thinkingLevel: options.thinkingLevel,
73284
+ attemptCount,
73285
+ progress,
73286
+ }),
73149
73287
  waitForPauseCheckpoint: waitForRequestedPause,
73150
73288
  });
73151
73289
  await finalizeSuccessfulPromptRound({
@@ -73197,6 +73335,30 @@
73197
73335
  throw lastError;
73198
73336
  }, { preserveArtifactsOnSuccess: options.preserveLogs });
73199
73337
  }
73338
+ /**
73339
+ * Records into the prompt file that the prompt is being implemented right now.
73340
+ *
73341
+ * The `[^]` in-progress status is written before every single step, so it always names the step which is
73342
+ * running and the steps already finished. It is intentionally never reverted: a coder which is killed or
73343
+ * crashes leaves the `[^]` status behind as the signal that this task was left in the middle.
73344
+ */
73345
+ async function recordPromptRoundInProgress(options) {
73346
+ const { nextPrompt, runnerMetadata, thinkingLevel, attemptCount, progress } = options;
73347
+ markPromptInProgress({
73348
+ file: nextPrompt.file,
73349
+ section: nextPrompt.section,
73350
+ steps: progress.finishedSteps,
73351
+ inProgressStepKind: progress.startedStepKind,
73352
+ runnerName: runnerMetadata.runnerName,
73353
+ modelName: runnerMetadata.modelName,
73354
+ attemptCount,
73355
+ loginMethod: progress.loginMethod,
73356
+ thinkingLevel,
73357
+ });
73358
+ // Note: The prompt status is always written into the original project, an isolated round transports
73359
+ // its own changes back through the merge instead
73360
+ await writePromptFile(nextPrompt.file);
73361
+ }
73200
73362
  /**
73201
73363
  * Sleeps `options.waitAfterError` while keeping the rich UI and plain console in sync, then resets state for the retry.
73202
73364
  */
@@ -73857,19 +74019,9 @@
73857
74019
  * kept and only the status box turns from `[x]` into `[!]`, followed by the manual merge instructions.
73858
74020
  */
73859
74021
  function markPromptIsolationMergeFailed(file, section, worktree) {
73860
- if (section.statusLineIndex === undefined) {
73861
- throw new UnexpectedError(_spaceTrim.spaceTrim(`
73862
- Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
73863
- `));
73864
- }
73865
- const line = file.lines[section.statusLineIndex];
73866
- if (line === undefined) {
73867
- throw new UnexpectedError(_spaceTrim.spaceTrim(`
73868
- Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
73869
- `));
73870
- }
74022
+ const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
73871
74023
  const failedStatusLine = line.replace(PROMPT_STATUS_BOX_PATTERN, '$<indentation>[!]');
73872
- file.lines[section.statusLineIndex] = `${failedStatusLine} - ${buildCoderIsolationMergeFailureStatusNote(worktree)}`;
74024
+ file.lines[statusLineIndex] = `${failedStatusLine} - ${buildCoderIsolationMergeFailureStatusNote(worktree)}`;
73873
74025
  }
73874
74026
 
73875
74027
  /**
@@ -75009,7 +75161,9 @@
75009
75161
  if (section.status === 'not-ready' || isUnwritten) {
75010
75162
  return 'backlog';
75011
75163
  }
75012
- if (isActive) {
75164
+ // Note: A prompt marked `[^]` stays in progress even when no agent is running it, because its
75165
+ // implementation was left in the middle
75166
+ if (isActive || section.status === 'in-progress') {
75013
75167
  return 'in-progress';
75014
75168
  }
75015
75169
  if (!isPromptInPriorityFilter(section, priorityFilter)) {
@@ -75026,6 +75180,10 @@
75026
75180
  if (options.section.status === 'not-ready') {
75027
75181
  tags.push({ id: 'not-ready', label: '[-]' });
75028
75182
  }
75183
+ // Note: A `[^]` prompt which no agent is running right now was left in the middle of its implementation
75184
+ if (options.section.status === 'in-progress' && !options.isActive) {
75185
+ tags.push({ id: 'left-in-progress', label: '[^]' });
75186
+ }
75029
75187
  if (options.isUnwritten) {
75030
75188
  tags.push({ id: 'unwritten', label: '@@@' });
75031
75189
  }
@@ -75055,7 +75213,7 @@
75055
75213
  * Checks whether a parsed section is the prompt currently handled by the active agent.
75056
75214
  */
75057
75215
  function isPromptActive(promptFile, section, activePrompt) {
75058
- if (!activePrompt || section.status !== 'todo') {
75216
+ if (!activePrompt || (section.status !== 'todo' && section.status !== 'in-progress')) {
75059
75217
  return false;
75060
75218
  }
75061
75219
  return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
@@ -75087,7 +75245,7 @@
75087
75245
  * Overwrites the body of one prompt section with new content, preserving the status line.
75088
75246
  *
75089
75247
  * The `newContent` string is the prompt text without the status marker.
75090
- * The status line (`[ ]`, `[x]`, `[!]`, `[-]`) is kept intact.
75248
+ * The status line (`[ ]`, `[^]`, `[x]`, `[!]`, `[-]`) is kept intact.
75091
75249
  *
75092
75250
  * @private internal utility of `ptbk coder server`
75093
75251
  */
@@ -75368,6 +75526,7 @@
75368
75526
  line-height: 1.35;
75369
75527
  }
75370
75528
  .tag-not-ready { background: #e2e8f0; color: #475569; }
75529
+ .tag-left-in-progress { background: #ffe4e6; color: #9f1239; }
75371
75530
  .tag-unwritten { background: #fff4cc; color: #8a5a00; }
75372
75531
  .tag-implementing { background: #ede9fe; color: #5b21b6; }
75373
75532
  .tag-verifying { background: #fae8ff; color: #86198f; }
@@ -76098,6 +76257,33 @@
76098
76257
  runCodexPromptsServer: runCodexPromptsServer
76099
76258
  });
76100
76259
 
76260
+ /**
76261
+ * Checks whether one prompt section still has open coding work.
76262
+ *
76263
+ * A `[ ]` prompt has not been started at all and a `[^]` prompt was left in the middle of its
76264
+ * implementation, so a file containing either of them is not finished yet.
76265
+ */
76266
+ function isPromptSectionUnfinished(section) {
76267
+ return section.status === 'todo' || section.status === 'in-progress';
76268
+ }
76269
+
76270
+ /**
76271
+ * Orders the loaded prompt files for one verification pass.
76272
+ *
76273
+ * Note: `$` is used to indicate that this function is not a pure function - the `random` order is not deterministic
76274
+ * Note: This function does NOT mutate the given array
76275
+ */
76276
+ function $orderPromptFiles(promptFiles, order) {
76277
+ switch (order) {
76278
+ case 'from-earliest':
76279
+ return [...promptFiles];
76280
+ case 'from-latest':
76281
+ return [...promptFiles].reverse();
76282
+ case 'random':
76283
+ return [...$shuffleItems(...promptFiles)];
76284
+ }
76285
+ }
76286
+
76101
76287
  /**
76102
76288
  * Path to the directory that holds the prompt markdown files.
76103
76289
  */
@@ -76126,8 +76312,8 @@
76126
76312
  async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
76127
76313
  const normalizedOptions = normalizeVerifyPromptsOptions(options);
76128
76314
  console.info(colors__default["default"].cyan.bold('📋 Prompt verification helper'));
76129
- if (normalizedOptions.reverse) {
76130
- console.info(colors__default["default"].gray('Processing files in reverse order'));
76315
+ if (normalizedOptions.order !== DEFAULT_VERIFY_PROMPTS_ORDER) {
76316
+ console.info(colors__default["default"].gray(`Processing files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[normalizedOptions.order]}`));
76131
76317
  }
76132
76318
  if (normalizedOptions.ignore.length > 0) {
76133
76319
  console.info(colors__default["default"].gray(`Ignoring candidates matching: ${normalizedOptions.ignore.join(', ')}`));
@@ -76184,7 +76370,7 @@
76184
76370
  */
76185
76371
  function parseVerifyPromptsCliOptions(args) {
76186
76372
  return {
76187
- reverse: args.includes('--reverse'),
76373
+ order: parseVerifyPromptsOrder(readStringOption(args, '--order')),
76188
76374
  ignore: readRepeatableStringOption(args, '--ignore'),
76189
76375
  gitSync: {
76190
76376
  isCommitEnabled: args.includes('--commit'),
@@ -76199,10 +76385,7 @@
76199
76385
  async function loadPromptFilesForVerification(options) {
76200
76386
  const loadedPromptFiles = await loadPromptFiles(PROMPTS_DIR);
76201
76387
  const { promptFiles, ignoredPromptFiles } = partitionPromptFilesByIgnore(loadedPromptFiles, options.ignore);
76202
- if (options.reverse) {
76203
- promptFiles.reverse();
76204
- }
76205
- return { promptFiles, ignoredPromptFiles };
76388
+ return { promptFiles: $orderPromptFiles(promptFiles, options.order), ignoredPromptFiles };
76206
76389
  }
76207
76390
  /**
76208
76391
  * Splits prompt files into files that should be verified now and files ignored for this run.
@@ -76244,7 +76427,7 @@
76244
76427
  function normalizeVerifyPromptsOptions(options) {
76245
76428
  var _a, _b, _c;
76246
76429
  return {
76247
- reverse: (_a = options.reverse) !== null && _a !== void 0 ? _a : false,
76430
+ order: (_a = options.order) !== null && _a !== void 0 ? _a : DEFAULT_VERIFY_PROMPTS_ORDER,
76248
76431
  ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
76249
76432
  gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
76250
76433
  };
@@ -76269,6 +76452,13 @@
76269
76452
  }
76270
76453
  return normalizedIgnoreValues;
76271
76454
  }
76455
+ /**
76456
+ * Reads one single-value string option from raw CLI arguments, keeping the last occurrence.
76457
+ */
76458
+ function readStringOption(args, flag) {
76459
+ const values = readRepeatableStringOption(args, flag);
76460
+ return values[values.length - 1];
76461
+ }
76272
76462
  /**
76273
76463
  * Reads one repeatable string option from raw CLI arguments.
76274
76464
  */
@@ -76330,8 +76520,9 @@
76330
76520
  for (const file of promptFiles) {
76331
76521
  const doneCount = file.sections.filter((section) => section.status === 'done').length;
76332
76522
  const todoCount = file.sections.filter((section) => section.status === 'todo').length;
76523
+ const inProgressCount = file.sections.filter((section) => section.status === 'in-progress').length;
76333
76524
  const notReadyCount = file.sections.filter((section) => section.status === 'not-ready').length;
76334
- const needsVerification = file.sections.length > 0 && todoCount === 0 && doneCount > 0;
76525
+ const needsVerification = file.sections.length > 0 && !file.sections.some(isPromptSectionUnfinished) && doneCount > 0;
76335
76526
  const statusParts = [];
76336
76527
  if (needsVerification) {
76337
76528
  statusParts.push(colors__default["default"].cyan.bold(`🔍 ${doneCount} done [x] - NEEDS VERIFICATION`));
@@ -76340,6 +76531,9 @@
76340
76531
  if (todoCount > 0) {
76341
76532
  statusParts.push(colors__default["default"].yellow(`${todoCount} todo [ ]`));
76342
76533
  }
76534
+ if (inProgressCount > 0) {
76535
+ statusParts.push(colors__default["default"].magenta(`${inProgressCount} in-progress [^]`));
76536
+ }
76343
76537
  if (doneCount > 0) {
76344
76538
  statusParts.push(colors__default["default"].green(`${doneCount} done [x]`));
76345
76539
  }
@@ -76355,7 +76549,7 @@
76355
76549
  console.info(colors__default["default"].gray('Goal: verify all done prompts, then process remaining todo prompts.'));
76356
76550
  }
76357
76551
  /**
76358
- * Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ].
76552
+ * Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ] or in-progress [^].
76359
76553
  * Completely ignores not-ready prompts like [-], [.], [?], etc.
76360
76554
  * Also excludes files that have been skipped in this session.
76361
76555
  */
@@ -76368,10 +76562,10 @@
76368
76562
  if (skippedFiles.has(file.path)) {
76369
76563
  return false;
76370
76564
  }
76371
- // File is ready for verification if it has at least one done prompt and no todo prompts
76372
- const hasTodoPrompts = file.sections.some((section) => section.status === 'todo');
76565
+ // File is ready for verification if it has at least one done prompt and no unfinished prompts
76566
+ const hasUnfinishedPrompts = file.sections.some(isPromptSectionUnfinished);
76373
76567
  const hasDonePrompts = file.sections.some((section) => section.status === 'done');
76374
- return !hasTodoPrompts && hasDonePrompts;
76568
+ return !hasUnfinishedPrompts && hasDonePrompts;
76375
76569
  });
76376
76570
  }
76377
76571
  /**
@@ -76477,11 +76671,11 @@
76477
76671
  const filesNeedingVerification = promptFiles.filter((file) => {
76478
76672
  if (file.sections.length === 0)
76479
76673
  return false;
76480
- const hasTodo = file.sections.some((section) => section.status === 'todo');
76674
+ const hasUnfinished = file.sections.some(isPromptSectionUnfinished);
76481
76675
  const hasDone = file.sections.some((section) => section.status === 'done');
76482
- return !hasTodo && hasDone;
76676
+ return !hasUnfinished && hasDone;
76483
76677
  });
76484
- const pendingFiles = promptFiles.filter((file) => file.sections.some((section) => section.status === 'todo'));
76678
+ const pendingFiles = promptFiles.filter((file) => file.sections.some(isPromptSectionUnfinished));
76485
76679
  if (filesNeedingVerification.length) {
76486
76680
  const formattedNames = formatPendingFileNames(filesNeedingVerification.map((file) => file.name));
76487
76681
  console.info(colors__default["default"].cyan(` 🔍 Files needing verification (${filesNeedingVerification.length}): ${formattedNames}`));