@promptbook/cli 0.114.0-10 → 0.114.0-12

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 (75) hide show
  1. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +8 -1
  2. package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +193 -63
  3. package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +25 -28
  4. package/apps/agents-server/src/app/admin/messages/send-email/emailTesting.ts +153 -0
  5. package/apps/agents-server/src/app/admin/messages/send-email/emailTestingAccess.ts +30 -0
  6. package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +18 -5
  7. package/apps/agents-server/src/app/api/internal/agent-goal-chat-planned-messages/route.ts +34 -7
  8. package/apps/agents-server/src/message-providers/email/stalwart/StalwartMessageProvider.ts +2 -1
  9. package/apps/agents-server/src/utils/email/agentEmailAddress.ts +14 -0
  10. package/apps/agents-server/src/utils/email/sendEmailThroughStalwart.ts +2 -1
  11. package/esm/index.es.js +404 -115
  12. package/esm/index.es.js.map +1 -1
  13. package/esm/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  14. package/esm/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  15. package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  16. package/esm/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  17. package/esm/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  18. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  19. package/esm/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  20. package/esm/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  21. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  22. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  23. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  24. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  25. package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  26. package/esm/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  27. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  28. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  29. package/esm/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  30. package/esm/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
  31. package/esm/src/version.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/book-2.0/agent-source/AgentSourceParseResult.ts +6 -0
  34. package/src/book-2.0/agent-source/ParsedUnknownCommitment.ts +29 -0
  35. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +38 -1
  36. package/src/book-2.0/agent-source/getBookCommitmentLineType.ts +36 -0
  37. package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.ts +128 -61
  38. package/src/book-components/BookEditor/BookEditorMonaco.tsx +6 -1
  39. package/src/book-components/BookEditor/BookEditorMonacoTokenization.ts +3 -1
  40. package/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.ts +50 -0
  41. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +3 -15
  42. package/src/cli/cli-commands/coder/init.ts +11 -2
  43. package/src/cli/cli-commands/coder/ping.ts +11 -1
  44. package/src/cli/cli-commands/coder/run.ts +11 -2
  45. package/src/cli/cli-commands/coder/server.ts +11 -2
  46. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +5 -0
  47. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +17 -5
  48. package/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts +6 -3
  49. package/src/cli/cli-commands/common/harness/HarnessInstallationStatus.ts +3 -2
  50. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +49 -0
  51. package/src/executables/platforms/locateAppOnWindows.ts +12 -9
  52. package/src/other/templates/getTemplatesPipelineCollection.ts +881 -685
  53. package/src/version.ts +2 -2
  54. package/src/versions.txt +1 -0
  55. package/umd/index.umd.js +403 -114
  56. package/umd/index.umd.js.map +1 -1
  57. package/umd/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  58. package/umd/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  59. package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  60. package/umd/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  61. package/umd/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  62. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  63. package/umd/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  64. package/umd/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  65. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  66. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  67. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  68. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  69. package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  70. package/umd/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  71. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  72. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  73. package/umd/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  74. package/umd/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
  75. package/umd/src/version.d.ts +1 -1
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-10';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-12';
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
@@ -20317,6 +20317,30 @@
20317
20317
  // TODO: [🧠] Maybe create through standardized $register
20318
20318
  // Note: [💞] Ignore a discrepancy between file name and entity name
20319
20319
 
20320
+ /**
20321
+ * Pattern shared by Book parsing and editor highlighting for a commitment-like line.
20322
+ *
20323
+ * A Book commitment keyword consists of one or more all-uppercase words with at least
20324
+ * two characters each. This deliberately mirrors the Book editor syntax rule so an
20325
+ * unknown keyword starts a new source block everywhere it is interpreted.
20326
+ *
20327
+ * @private internal constant of `getBookCommitmentLineType`
20328
+ */
20329
+ const BOOK_COMMITMENT_LINE_TYPE_REGEX = /^\s*(?<type>[A-Z][A-Z0-9]+(?:\s+[A-Z][A-Z0-9]+)*)(?=\s|$)/;
20330
+ /**
20331
+ * Extracts the uppercase commitment type from a Book line, including types not registered by Promptbook.
20332
+ *
20333
+ * @param line - One raw Book source line.
20334
+ * @returns Matched commitment type or `null` when the line does not begin with commitment syntax.
20335
+ *
20336
+ * @private internal utility of Book parsing and `<BookEditor/>`
20337
+ */
20338
+ function getBookCommitmentLineType(line) {
20339
+ var _a;
20340
+ const match = BOOK_COMMITMENT_LINE_TYPE_REGEX.exec(line);
20341
+ return ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.type) || null;
20342
+ }
20343
+
20320
20344
  /**
20321
20345
  * Consumes the agent-name prelude from raw source.
20322
20346
  *
@@ -20374,13 +20398,15 @@
20374
20398
  return {
20375
20399
  agentName: null,
20376
20400
  commitments: [],
20401
+ unknownCommitments: [],
20377
20402
  nonCommitmentLines: [],
20378
20403
  };
20379
20404
  }
20380
20405
  const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
20381
20406
  const commitments = [];
20407
+ const unknownCommitments = [];
20382
20408
  const nonCommitmentLines = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]] : [];
20383
- // Parse commitments with multiline support
20409
+ // Parse commitments with multiline support.
20384
20410
  let currentCommitment = null;
20385
20411
  // Process lines starting from after the agent name line
20386
20412
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
@@ -20394,25 +20420,11 @@
20394
20420
  // Check if this line starts or ends a code block
20395
20421
  if (trimmedLine.startsWith('```')) {
20396
20422
  isInsideCodeBlock = !isInsideCodeBlock;
20397
- if (currentCommitment) {
20398
- // If we are inside a commitment, the code block is part of it
20399
- currentCommitment.contentLines.push(line);
20400
- }
20401
- else {
20402
- // If we are not inside a commitment, the code block is non-commitment
20403
- nonCommitmentLines.push(line);
20404
- }
20423
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
20405
20424
  continue;
20406
20425
  }
20407
20426
  if (isInsideCodeBlock) {
20408
- if (currentCommitment) {
20409
- // If we are inside a commitment and a code block, the line is part of the commitment
20410
- currentCommitment.contentLines.push(line);
20411
- }
20412
- else {
20413
- // If we are inside a code block but not a commitment, the line is non-commitment
20414
- nonCommitmentLines.push(line);
20415
- }
20427
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
20416
20428
  continue;
20417
20429
  }
20418
20430
  // Check if this line starts a new commitment
@@ -20421,22 +20433,14 @@
20421
20433
  const typeRegex = definition.createTypeRegex();
20422
20434
  const match = typeRegex.exec(line.trim());
20423
20435
  if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
20424
- // Save the previous commitment if it exists
20425
- if (currentCommitment) {
20426
- const fullContent = currentCommitment.contentLines.join('\n');
20427
- commitments.push({
20428
- type: currentCommitment.type,
20429
- content: _spaceTrim.spaceTrim(fullContent),
20430
- originalLine: currentCommitment.originalStartLine,
20431
- lineNumber: currentCommitment.startLineNumber,
20432
- });
20433
- }
20436
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
20434
20437
  // Extract the initial content from the commitment line
20435
20438
  const fullRegex = definition.createRegex();
20436
20439
  const fullMatch = fullRegex.exec(line.trim());
20437
20440
  const initialContent = ((_b = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _b === void 0 ? void 0 : _b.contents) || '';
20438
20441
  // Start a new commitment
20439
20442
  currentCommitment = {
20443
+ kind: 'known',
20440
20444
  type: definition.type,
20441
20445
  startLineNumber: i + 1,
20442
20446
  originalStartLine: line,
@@ -20446,52 +20450,90 @@
20446
20450
  break;
20447
20451
  }
20448
20452
  }
20453
+ if (foundNewCommitment) {
20454
+ continue;
20455
+ }
20456
+ const unknownCommitmentType = getBookCommitmentLineType(line);
20457
+ if (unknownCommitmentType) {
20458
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
20459
+ currentCommitment = {
20460
+ kind: 'unknown',
20461
+ type: unknownCommitmentType,
20462
+ startLineNumber: i + 1,
20463
+ originalStartLine: line,
20464
+ sourceLines: [line],
20465
+ };
20466
+ continue;
20467
+ }
20449
20468
  // Check if this is a horizontal line (ends any current commitment)
20450
20469
  const isHorizontalLine = HORIZONTAL_LINE_PATTERN$1.test(line);
20451
20470
  if (isHorizontalLine) {
20452
- // Save the current commitment if it exists
20453
- if (currentCommitment) {
20454
- const fullContent = currentCommitment.contentLines.join('\n');
20455
- commitments.push({
20456
- type: currentCommitment.type,
20457
- content: _spaceTrim.spaceTrim(fullContent),
20458
- originalLine: currentCommitment.originalStartLine,
20459
- lineNumber: currentCommitment.startLineNumber,
20460
- });
20461
- currentCommitment = null;
20462
- }
20471
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
20472
+ currentCommitment = null;
20463
20473
  // Add horizontal line to non-commitment lines
20464
20474
  nonCommitmentLines.push(line);
20465
20475
  continue;
20466
20476
  }
20467
- if (!foundNewCommitment) {
20468
- if (currentCommitment) {
20469
- // This line belongs to the current commitment
20470
- currentCommitment.contentLines.push(line);
20471
- }
20472
- else {
20473
- // This line is not part of any commitment
20474
- nonCommitmentLines.push(line);
20475
- }
20476
- }
20477
- }
20478
- // Don't forget to save the last commitment if it exists
20479
- if (currentCommitment) {
20480
- const fullContent = currentCommitment.contentLines.join('\n');
20481
- commitments.push({
20482
- type: currentCommitment.type,
20483
- content: _spaceTrim.spaceTrim(fullContent),
20484
- originalLine: currentCommitment.originalStartLine,
20485
- lineNumber: currentCommitment.startLineNumber,
20486
- });
20477
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
20487
20478
  }
20479
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
20488
20480
  return {
20489
20481
  agentName,
20490
20482
  agentNameLineNumber,
20491
20483
  commitments,
20484
+ unknownCommitments,
20492
20485
  nonCommitmentLines,
20493
20486
  };
20494
20487
  }
20488
+ /**
20489
+ * Stores the current known or unknown commitment in its appropriate parsed collection.
20490
+ *
20491
+ * @param currentCommitment - Commitment-like block collected so far.
20492
+ * @param commitments - Parsed registered commitments to append to.
20493
+ * @param unknownCommitments - Parsed unregistered commitments to append to.
20494
+ *
20495
+ * @private internal utility of `parseAgentSourceWithCommitments`
20496
+ */
20497
+ function appendCurrentCommitment(currentCommitment, commitments, unknownCommitments) {
20498
+ if (!currentCommitment) {
20499
+ return;
20500
+ }
20501
+ if (currentCommitment.kind === 'unknown') {
20502
+ unknownCommitments.push({
20503
+ type: currentCommitment.type,
20504
+ originalLine: currentCommitment.originalStartLine,
20505
+ lineNumber: currentCommitment.startLineNumber,
20506
+ source: _spaceTrim.spaceTrim(currentCommitment.sourceLines.join('\n')),
20507
+ });
20508
+ return;
20509
+ }
20510
+ commitments.push({
20511
+ type: currentCommitment.type,
20512
+ content: _spaceTrim.spaceTrim(currentCommitment.contentLines.join('\n')),
20513
+ originalLine: currentCommitment.originalStartLine,
20514
+ lineNumber: currentCommitment.startLineNumber,
20515
+ });
20516
+ }
20517
+ /**
20518
+ * Adds one source line to the active commitment-like block, or to plain source content when none is active.
20519
+ *
20520
+ * @param currentCommitment - Commitment-like block currently being collected.
20521
+ * @param line - Source line to append.
20522
+ * @param nonCommitmentLines - Plain source lines to append to when no block is active.
20523
+ *
20524
+ * @private internal utility of `parseAgentSourceWithCommitments`
20525
+ */
20526
+ function appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines) {
20527
+ if (!currentCommitment) {
20528
+ nonCommitmentLines.push(line);
20529
+ return;
20530
+ }
20531
+ if (currentCommitment.kind === 'unknown') {
20532
+ currentCommitment.sourceLines.push(line);
20533
+ return;
20534
+ }
20535
+ currentCommitment.contentLines.push(line);
20536
+ }
20495
20537
 
20496
20538
  /**
20497
20539
  * Parses parameters from text using both supported notations:
@@ -24761,14 +24803,90 @@
24761
24803
  * without the generated script and the prompt it embeds.
24762
24804
  */
24763
24805
  const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
24806
+ /**
24807
+ * Command which terminates the Bash process tree rooted at the running harness.
24808
+ *
24809
+ * Bash's POSIX process IDs differ from Windows process IDs in Git Bash, so the Windows branch resolves the native PID
24810
+ * before delegating to `taskkill`. Unix harnesses run in a dedicated Bash job process group, which can be terminated
24811
+ * with its negative process ID.
24812
+ */
24813
+ const TERMINATE_BASH_PROCESS_TREE_COMMAND = process.platform === 'win32'
24814
+ ? _spaceTrim.spaceTrim(`
24815
+ HARNESS_WINDOWS_PROCESS_ID="$(ps -l -p "$HARNESS_PROCESS_ID" | awk 'NR == 2 { print $4 }')"
24816
+ if [ -n "$HARNESS_WINDOWS_PROCESS_ID" ]; then
24817
+ MSYS_NO_PATHCONV=1 taskkill.exe /PID "$HARNESS_WINDOWS_PROCESS_ID" /T /F > /dev/null 2>&1 || true
24818
+ fi
24819
+ `)
24820
+ : 'kill -TERM -- "-$HARNESS_PROCESS_ID" 2>/dev/null || true';
24821
+ /**
24822
+ * Shell condition that detects whether the Node process which owns the harness is still running.
24823
+ *
24824
+ * Git Bash translates process IDs and command switches, so querying the native Windows PID needs both `tasklist` and
24825
+ * disabled MSYS path conversion there. Unix can use the native `kill -0` process existence check.
24826
+ */
24827
+ const IS_PARENT_PROCESS_RUNNING_CONDITION = process.platform === 'win32'
24828
+ ? 'MSYS_NO_PATHCONV=1 tasklist.exe /FI "PID eq $PARENT_CODER_PROCESS_ID" /NH | awk -v processId="$PARENT_CODER_PROCESS_ID" \'$2 == processId { isFound = 1 } END { exit isFound ? 0 : 1 }\''
24829
+ : 'kill -0 "$PARENT_CODER_PROCESS_ID" 2>/dev/null';
24830
+ /**
24831
+ * Environment variable that identifies the Node process responsible for a temporary harness shell.
24832
+ */
24833
+ const PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME = 'PTBK_CODER_PARENT_PROCESS_ID';
24764
24834
  /**
24765
24835
  * Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
24836
+ *
24837
+ * A watcher polls the owning Node process by its PID. If that process exits abruptly, the watcher stops the whole
24838
+ * Bash and harness process tree instead of letting it continue as an orphan.
24766
24839
  */
24767
24840
  const LOGGED_BASH_WRAPPER_COMMAND = _spaceTrim.spaceTrim(`
24841
+ PARENT_CODER_PROCESS_ID="\${${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}}"
24842
+ unset ${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}
24843
+
24844
+ terminate_harness_process_tree() {
24845
+ # Keep the EXIT cleanup intact so the wrapper can also stop its parent-process watcher.
24846
+ trap - HUP INT TERM
24847
+ ${TERMINATE_BASH_PROCESS_TREE_COMMAND}
24848
+ }
24849
+
24850
+ is_parent_process_running() {
24851
+ ${IS_PARENT_PROCESS_RUNNING_CONDITION}
24852
+ }
24853
+
24768
24854
  if [ -n "\${${PTBK_CODER_LOG_FILE_ENV_NAME}:-}" ]; then
24769
- exec > >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
24855
+ exec > >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
24770
24856
  fi
24771
- bash "$1"
24857
+
24858
+ watch_parent_process() {
24859
+ trap 'exit 0' HUP INT TERM
24860
+
24861
+ while is_parent_process_running; do
24862
+ sleep 1
24863
+ done
24864
+
24865
+ terminate_harness_process_tree
24866
+ }
24867
+
24868
+ # Background harness jobs need their own process group on Unix so termination cannot reach the parent coder.
24869
+ set -m
24870
+ if ! is_parent_process_running; then
24871
+ exit 1
24872
+ fi
24873
+ bash "$1" &
24874
+ HARNESS_PROCESS_ID=$!
24875
+
24876
+ cleanup_parent_process_watcher() {
24877
+ kill "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
24878
+ wait "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
24879
+ }
24880
+
24881
+ watch_parent_process &
24882
+ PARENT_PROCESS_WATCHER_PID=$!
24883
+
24884
+ trap cleanup_parent_process_watcher EXIT
24885
+ trap terminate_harness_process_tree HUP INT TERM
24886
+
24887
+ wait "$HARNESS_PROCESS_ID"
24888
+ SCRIPT_EXIT_CODE=$?
24889
+ exit "$SCRIPT_EXIT_CODE"
24772
24890
  `);
24773
24891
  /**
24774
24892
  * Shapes one bash invocation that optionally mirrors live script output into a temporary log file.
@@ -24824,6 +24942,38 @@
24824
24942
  return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
24825
24943
  }
24826
24944
 
24945
+ /**
24946
+ * Standard streams used by every temporary Bash runner.
24947
+ */
24948
+ const BASH_PROCESS_STDIO = ['pipe', 'pipe', 'pipe'];
24949
+ /**
24950
+ * Whether the current Node process is running on Windows.
24951
+ */
24952
+ const IS_WINDOWS$1 = process.platform === 'win32';
24953
+ /**
24954
+ * Starts one temporary Bash script in a process tree owned by the current Node process.
24955
+ *
24956
+ * The wrapper watches the supplied owning process ID. When that process exits abruptly, the wrapper terminates every
24957
+ * nested shell and harness process instead of leaving it orphaned.
24958
+ *
24959
+ * @private internal utility of the coding prompt runner
24960
+ */
24961
+ function $spawnLoggedBashScript(options) {
24962
+ var _a;
24963
+ const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
24964
+ const parentProcessId = (_a = options.parentProcessId) !== null && _a !== void 0 ? _a : process.pid;
24965
+ return child_process.spawn('bash', bashExecution.args, {
24966
+ detached: !IS_WINDOWS$1,
24967
+ env: {
24968
+ ...process.env,
24969
+ ...bashExecution.env,
24970
+ [PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME]: parentProcessId.toString(),
24971
+ },
24972
+ stdio: BASH_PROCESS_STDIO,
24973
+ windowsHide: true,
24974
+ });
24975
+ }
24976
+
24827
24977
  /**
24828
24978
  * Prints one live shell output chunk to the terminal when console mirroring is enabled.
24829
24979
  */
@@ -24846,12 +24996,12 @@
24846
24996
  async function runBashScriptWithOutput(options) {
24847
24997
  var _a;
24848
24998
  await appendScriptExecutionLogStart(options);
24849
- const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
24850
24999
  const scriptPathPosix = toPosixPath(options.scriptPath);
24851
25000
  const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
24852
25001
  return await new Promise((resolve, reject) => {
24853
- const commandProcess = child_process.spawn('bash', bashExecution.args, {
24854
- env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
25002
+ const commandProcess = $spawnLoggedBashScript({
25003
+ scriptPath: options.scriptPath,
25004
+ logPath: options.logPath,
24855
25005
  });
24856
25006
  let output = '';
24857
25007
  let settled = false;
@@ -24910,8 +25060,9 @@
24910
25060
  const failure = new Error(_spaceTrim.spaceTrim(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
24911
25061
  settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
24912
25062
  };
25063
+ // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
25064
+ // substitutions after the direct shell exits.
24913
25065
  commandProcess.on('close', handleExit);
24914
- commandProcess.on('exit', handleExit);
24915
25066
  commandProcess.on('disconnect', () => {
24916
25067
  const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
24917
25068
  settleWithLog('failed after disconnect', () => reject(failure), failure);
@@ -25772,6 +25923,35 @@
25772
25923
  error.message.includes('bin/node'));
25773
25924
  }
25774
25925
 
25926
+ /**
25927
+ * Whether the current coder process runs on Windows.
25928
+ */
25929
+ const IS_WINDOWS = process.platform === 'win32';
25930
+ /**
25931
+ * Stops one active temporary Bash shell together with the harness process tree it owns.
25932
+ *
25933
+ * Unix sends `SIGTERM` to the wrapper so its shell trap terminates the separately grouped harness job. Windows does
25934
+ * not reliably deliver that signal to Bash, so the native `taskkill` command terminates the direct shell tree instead.
25935
+ *
25936
+ * @private internal utility of the coding prompt runner
25937
+ */
25938
+ function $terminateLoggedBashProcessTree(commandProcess) {
25939
+ if (commandProcess.exitCode !== null || commandProcess.signalCode !== null) {
25940
+ return;
25941
+ }
25942
+ if (!IS_WINDOWS) {
25943
+ commandProcess.kill('SIGTERM');
25944
+ return;
25945
+ }
25946
+ if (!commandProcess.pid) {
25947
+ return;
25948
+ }
25949
+ child_process.spawnSync('taskkill.exe', ['/PID', commandProcess.pid.toString(), '/T', '/F'], {
25950
+ stdio: 'ignore',
25951
+ windowsHide: true,
25952
+ });
25953
+ }
25954
+
25775
25955
  /**
25776
25956
  * Maximum number of output characters included in thrown error messages.
25777
25957
  */
@@ -25814,15 +25994,16 @@
25814
25994
  const scriptPathPosix = toPosixPath(scriptPath);
25815
25995
  const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
25816
25996
  await appendScriptExecutionLogStart(options);
25817
- const bashExecution = buildLoggedBashExecution(scriptPath, options.logPath);
25818
25997
  return await new Promise((resolve, reject) => {
25819
- const commandProcess = child_process.spawn('bash', bashExecution.args, {
25820
- env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
25998
+ const commandProcess = $spawnLoggedBashScript({
25999
+ scriptPath,
26000
+ logPath: options.logPath,
25821
26001
  });
25822
26002
  let stdoutBuffer = '';
25823
26003
  let stderrBuffer = '';
25824
26004
  let fullOutput = '';
25825
26005
  let markerSeen = false;
26006
+ let isCompletedAfterIdleTimeout = false;
25826
26007
  let idleTimer;
25827
26008
  let settled = false;
25828
26009
  let isSettling = false;
@@ -25864,15 +26045,16 @@
25864
26045
  });
25865
26046
  };
25866
26047
  /**
25867
- * Finishes a marker-completed run and then asks bash to stop.
26048
+ * Stops a marker-completed process tree after its trailing output becomes idle.
25868
26049
  */
25869
26050
  const finishAfterIdleTimeout = () => {
25870
- settleWithLog('completed after idle timeout', () => resolve(fullOutput));
26051
+ isCompletedAfterIdleTimeout = true;
26052
+ idleTimer = undefined;
25871
26053
  try {
25872
- commandProcess.kill();
26054
+ $terminateLoggedBashProcessTree(commandProcess);
25873
26055
  }
25874
26056
  catch (_a) {
25875
- // Windows can report EPERM when bash exits before the idle timer fires.
26057
+ // The close handler below still settles normally when Bash already exited before the idle timer fired.
25876
26058
  }
25877
26059
  };
25878
26060
  /**
@@ -25931,7 +26113,11 @@
25931
26113
  const failure = code === 0 || markerSeen
25932
26114
  ? undefined
25933
26115
  : new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
25934
- const status = failure ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}` : 'succeeded';
26116
+ const status = isCompletedAfterIdleTimeout
26117
+ ? 'completed after idle timeout'
26118
+ : failure
26119
+ ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`
26120
+ : 'succeeded';
25935
26121
  settleWithLog(status, () => {
25936
26122
  if (!failure) {
25937
26123
  resolve(fullOutput);
@@ -25940,8 +26126,9 @@
25940
26126
  reject(failure);
25941
26127
  });
25942
26128
  };
26129
+ // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
26130
+ // substitutions after the direct shell exits.
25943
26131
  commandProcess.on('close', handleExit);
25944
- commandProcess.on('exit', handleExit);
25945
26132
  commandProcess.on('disconnect', () => {
25946
26133
  const failure = new Error(buildCommandFailureMessage(scriptPathPosix, null, fullOutput));
25947
26134
  settleWithLog('failed after disconnect', () => reject(failure), failure);
@@ -36811,7 +36998,8 @@
36811
36998
  requirements = await importReferencedFiles(requirements);
36812
36999
  requirements = appendMcpServers(requirements, agentSource);
36813
37000
  requirements = appendNonCommitmentContent(requirements, parseResult);
36814
- return appendExampleInteractions(requirements, parseResult);
37001
+ requirements = appendExampleInteractions(requirements, parseResult);
37002
+ return appendUnknownCommitmentContent(requirements, parseResult);
36815
37003
  }
36816
37004
  /**
36817
37005
  * Imports text files referenced by IMPORT commitments and appends their transformed content to the system message.
@@ -36933,6 +37121,36 @@
36933
37121
  }
36934
37122
  return appendSystemMessageSection(requirements, nonCommitmentContent);
36935
37123
  }
37124
+ /**
37125
+ * Appends unknown commitment blocks as final extra context without applying commitment side effects.
37126
+ *
37127
+ * @param requirements - Current requirements snapshot.
37128
+ * @param parseResult - Parsed source including unknown commitment blocks.
37129
+ * @returns Requirements with unknown commitment context appended at the end of the system message.
37130
+ *
37131
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
37132
+ */
37133
+ function appendUnknownCommitmentContent(requirements, parseResult) {
37134
+ const unknownCommitmentContent = getUnknownCommitmentContent(parseResult);
37135
+ if (!unknownCommitmentContent) {
37136
+ return requirements;
37137
+ }
37138
+ return appendSystemMessageSection(requirements, unknownCommitmentContent);
37139
+ }
37140
+ /**
37141
+ * Joins unknown commitment blocks into their final extra-context representation.
37142
+ *
37143
+ * @param parseResult - Parsed source including unknown commitment blocks.
37144
+ * @returns Unknown commitment source blocks in their original order.
37145
+ *
37146
+ * @private internal utility of `appendUnknownCommitmentContent`
37147
+ */
37148
+ function getUnknownCommitmentContent(parseResult) {
37149
+ return parseResult.unknownCommitments
37150
+ .map((commitment) => commitment.source)
37151
+ .filter(Boolean)
37152
+ .join('\n\n');
37153
+ }
36936
37154
  /**
36937
37155
  * Collects plain-text lines that were not parsed as commitments and should still become part of the final system message.
36938
37156
  *
@@ -39673,24 +39891,12 @@
39673
39891
  - @@@
39674
39892
  - Keep in mind the DRY _(don't repeat yourself)_ principle.
39675
39893
  - Do a proper analysis of the current functionality before you start implementing.
39676
- - Add the changes into the [changelog](changelog/_current-preversion.md)
39894
+ - Add the changes into the [changelog](CHANGELOG.md)
39895
+ - Update the [README](README.md) if needed.
39896
+ - Update the [AGENTS.md](AGENTS.md) for the next job to be done if it makes sense.
39677
39897
  `),
39678
39898
  isDefaultProjectTemplate: true,
39679
39899
  },
39680
- {
39681
- id: 'agents-server',
39682
- relativeFilePath: path.join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'agents-server.md'),
39683
- slugPrefix: 'agents-server',
39684
- content: _spaceTrim.spaceTrim(`
39685
- - @@@
39686
- - Keep in mind the DRY _(don't repeat yourself)_ principle.
39687
- - Do a proper analysis of the current functionality before you start implementing.
39688
- - You are working with the [Agents Server](apps/agents-server)
39689
- - If you need to do the database migration, do it
39690
- - Add the changes into the [changelog](changelog/_current-preversion.md)
39691
- `),
39692
- isDefaultProjectTemplate: false,
39693
- },
39694
39900
  ];
39695
39901
  /**
39696
39902
  * Project-agnostic coder templates that `ptbk coder init` should materialize in any repository.
@@ -40782,6 +40988,10 @@
40782
40988
  console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
40783
40989
  return;
40784
40990
  }
40991
+ if (installationState === 'installed') {
40992
+ console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is installed. Skipped checking for updates.`));
40993
+ return;
40994
+ }
40785
40995
  if (installationState === 'unknown') {
40786
40996
  console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is installed, the newest version could not be checked.`));
40787
40997
  return;
@@ -40995,20 +41205,20 @@
40995
41205
  // Note: [🟡] Code for CLI npm package version comparison [isNpmPackageVersionOutdated](src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.ts) should never be published outside of `@promptbook/cli`
40996
41206
 
40997
41207
  /**
40998
- * Detects whether one CLI coding harness is installed globally and whether it is up to date.
41208
+ * Detects whether one CLI coding harness is installed globally and, when enabled, whether it is up to date.
40999
41209
  *
41000
- * Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and queries npm
41210
+ * Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and may query npm
41001
41211
  *
41002
41212
  * @private internal utility of `promptbookCli`
41003
41213
  */
41004
- async function $checkHarnessInstallation(definition) {
41214
+ async function $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled = true) {
41005
41215
  const [installedVersion, latestVersion] = await Promise.all([
41006
41216
  $resolveInstalledHarnessVersion(definition),
41007
- $resolveLatestNpmPackageVersion(definition.npmPackageName),
41217
+ isHarnessUpdateCheckEnabled ? $resolveLatestNpmPackageVersion(definition.npmPackageName) : Promise.resolve(null),
41008
41218
  ]);
41009
41219
  return {
41010
41220
  definition,
41011
- installationState: resolveHarnessInstallationState(installedVersion, latestVersion),
41221
+ installationState: resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled),
41012
41222
  installedVersion,
41013
41223
  latestVersion,
41014
41224
  };
@@ -41016,10 +41226,13 @@
41016
41226
  /**
41017
41227
  * Derives the installation state from the detected versions.
41018
41228
  */
41019
- function resolveHarnessInstallationState(installedVersion, latestVersion) {
41229
+ function resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled) {
41020
41230
  if (installedVersion === null) {
41021
41231
  return 'not-installed';
41022
41232
  }
41233
+ if (!isHarnessUpdateCheckEnabled) {
41234
+ return 'installed';
41235
+ }
41023
41236
  if (latestVersion === null) {
41024
41237
  return 'unknown';
41025
41238
  }
@@ -41030,8 +41243,8 @@
41030
41243
  // Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
41031
41244
 
41032
41245
  /**
41033
- * Checks that every given CLI coding harness is installed globally and up to date and offers to
41034
- * install or update the ones which are not.
41246
+ * Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
41247
+ * Offers to install missing harnesses and update outdated ones.
41035
41248
  *
41036
41249
  * Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
41037
41250
  * are ignored so that every call site can pass its raw selection.
@@ -41040,13 +41253,13 @@
41040
41253
  *
41041
41254
  * @private internal utility of `promptbookCli`
41042
41255
  */
41043
- async function $ensureHarnessInstallations(harnessNames) {
41256
+ async function $ensureHarnessInstallations(harnessNames, isHarnessUpdateCheckEnabled = true) {
41044
41257
  const definitions = resolveCheckedHarnessDefinitions(harnessNames);
41045
41258
  if (definitions.length === 0) {
41046
41259
  return;
41047
41260
  }
41048
41261
  // Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
41049
- const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition)));
41262
+ const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)));
41050
41263
  for (const status of statuses) {
41051
41264
  await $applyHarnessInstallationStatus(status);
41052
41265
  }
@@ -41060,6 +41273,27 @@
41060
41273
  }
41061
41274
  // Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
41062
41275
 
41276
+ /**
41277
+ * Registers the shared `--no-harness-update` option on a `ptbk coder` command that uses a coding harness.
41278
+ *
41279
+ * @private internal utility of `promptbookCli`
41280
+ */
41281
+ function addHarnessUpdateOption(command) {
41282
+ command.option('--no-harness-update', 'Skip checking whether globally installed coding harnesses are up to date');
41283
+ }
41284
+ /**
41285
+ * Converts the Commander harness-update flag into the normalized harness-check option.
41286
+ *
41287
+ * @private internal utility of `promptbookCli`
41288
+ */
41289
+ function normalizeHarnessUpdateCliOptions(cliOptions) {
41290
+ return {
41291
+ isHarnessUpdateCheckEnabled: cliOptions.harnessUpdate,
41292
+ };
41293
+ }
41294
+ // Note: [🟡] Code for CLI harness update options [harnessUpdateCliOptions](src/cli/cli-commands/common/harnessUpdateCliOptions.ts) should never be published outside of `@promptbook/cli`
41295
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
41296
+
41063
41297
  /**
41064
41298
  * Relative path to the Promptbook Coder quick-reference file initialized in project roots.
41065
41299
  *
@@ -41622,14 +41856,16 @@
41622
41856
  - CODING_AGENT_GIT_EMAIL
41623
41857
  - CODING_AGENT_GIT_SIGNING_KEY
41624
41858
 
41625
- Checks that the coding harnesses are installed globally and up to date:
41859
+ Checks that the coding harnesses are installed globally and up to date unless \`--no-harness-update\` is used:
41626
41860
  ${block(listCheckedHarnessLabels())}
41627
41861
 
41628
41862
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
41629
41863
  `));
41630
41864
  addCoderGitSyncOptions(command);
41865
+ addHarnessUpdateOption(command);
41631
41866
  command.action(handleActionErrors(async (cliOptions) => {
41632
41867
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
41868
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
41633
41869
  const projectPath = process.cwd();
41634
41870
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
41635
41871
  const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
@@ -41642,7 +41878,7 @@
41642
41878
  commitScope,
41643
41879
  commitMessage: 'Initialize Promptbook Coder',
41644
41880
  });
41645
- await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
41881
+ await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, isHarnessUpdateCheckEnabled);
41646
41882
  }));
41647
41883
  }
41648
41884
  /**
@@ -41681,13 +41917,16 @@
41681
41917
  - Reports the answer of the harness, the response time and the reported usage
41682
41918
  - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
41683
41919
  - Leaves the project exactly as it was — nothing is read, written, changed or committed
41920
+ - Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
41684
41921
  - Use --no-ui to stream the raw harness output instead of only the compact result
41685
41922
  `));
41686
41923
  addPromptRunnerSelectionOptions(command);
41924
+ addHarnessUpdateOption(command);
41687
41925
  addPromptRunnerRuntimeOptions(command);
41688
41926
  command.action(handleActionErrors(async (cliOptions) => {
41689
41927
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
41690
- await $ensureHarnessInstallations([runnerOptions.agentName]);
41928
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
41929
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
41691
41930
  // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
41692
41931
  const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
41693
41932
  const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
@@ -42178,7 +42417,7 @@
42178
42417
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
42179
42418
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
42180
42419
  - Optional --no-ui keeps plain streaming console output for logging and debugging
42181
- - Checks that the selected harness is installed globally and up to date before the first prompt
42420
+ - Checks that the selected harness is installed globally and up to date before the first prompt unless --no-harness-update is used
42182
42421
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
42183
42422
  - Supports GPG signing of commits
42184
42423
  - Optional pre-coding test run that can stop or repair pre-existing failures
@@ -42188,6 +42427,7 @@
42188
42427
  `));
42189
42428
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
42190
42429
  addPromptRunnerSelectionOptions(command);
42430
+ addHarnessUpdateOption(command);
42191
42431
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
42192
42432
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
42193
42433
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
@@ -42229,6 +42469,7 @@
42229
42469
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
42230
42470
  isAgentRequired: !dryRun,
42231
42471
  });
42472
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
42232
42473
  // [1] Parse the wait options and --no-auto:
42233
42474
  // default: run automatically through the queue (no waiting between prompts)
42234
42475
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
@@ -42239,7 +42480,7 @@
42239
42480
  if (await $ensurePromptbookCliInstallations()) {
42240
42481
  return process.exit(0);
42241
42482
  }
42242
- await $ensureHarnessInstallations([runnerOptions.agentName]);
42483
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
42243
42484
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
42244
42485
  const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
42245
42486
  const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
@@ -42333,7 +42574,7 @@
42333
42574
 
42334
42575
  Features:
42335
42576
  - Runs the same prompt processing as \`ptbk coder run\`
42336
- - Checks that the selected harness is installed globally and up to date on startup
42577
+ - Checks that the selected harness is installed globally and up to date on startup unless --no-harness-update is used
42337
42578
  - Does not exit when all prompts are done; polls for new prompt files instead
42338
42579
  - Serves a kanban board at http://localhost:<port> for visual progress tracking
42339
42580
  - Allows editing prompt files directly from the browser (Trello-style)
@@ -42345,6 +42586,7 @@
42345
42586
  .default(DEFAULT_CODER_SERVER_PORT));
42346
42587
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
42347
42588
  addPromptRunnerSelectionOptions(command);
42589
+ addHarnessUpdateOption(command);
42348
42590
  command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
42349
42591
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
42350
42592
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
@@ -42376,7 +42618,8 @@
42376
42618
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
42377
42619
  isAgentRequired: !dryRun,
42378
42620
  });
42379
- await $ensureHarnessInstallations([runnerOptions.agentName]);
42621
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
42622
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
42380
42623
  // [1] Parse the wait options and --no-auto (same logic as `coder run`)
42381
42624
  const waitForUser = !auto;
42382
42625
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
@@ -44300,23 +44543,27 @@
44300
44543
  // TODO: [🧠][♿] Maybe export through `@promptbook/node`
44301
44544
 
44302
44545
  /**
44303
- * Attempts to locate the specified application on a Windows system by searching common installation directories.
44546
+ * Attempts to locate the specified application on a Windows system by searching PATH and common installation directories.
44304
44547
  * Returns the path to the executable if found, or null otherwise.
44305
44548
  *
44306
44549
  * @private within the repository
44307
44550
  */
44308
44551
  async function locateAppOnWindows({ appName, windowsSuffix, }) {
44309
44552
  try {
44310
- const prefixes = [
44553
+ const EXECUTABLE_FILENAME = windowsSuffix.replace(/^.*[\\/]/, '');
44554
+ const PATH_EXECUTABLE_PATHS = (process.env.PATH || '')
44555
+ .split(path.delimiter)
44556
+ .filter(Boolean)
44557
+ .map((pathPrefix) => path.join(pathPrefix, EXECUTABLE_FILENAME));
44558
+ const INSTALLATION_EXECUTABLE_PATHS = [
44311
44559
  process.env.LOCALAPPDATA,
44312
44560
  path.join(process.env.LOCALAPPDATA || '', 'Programs'),
44313
44561
  process.env.PROGRAMFILES,
44314
44562
  process.env['PROGRAMFILES(X86)'],
44315
- ];
44316
- for (const prefix of prefixes) {
44317
- const path = prefix + windowsSuffix;
44318
- if (await isExecutable(path, $provideFilesystemForNode())) {
44319
- return path;
44563
+ ].map((pathPrefix) => pathPrefix + windowsSuffix);
44564
+ for (const executablePath of [...PATH_EXECUTABLE_PATHS, ...INSTALLATION_EXECUTABLE_PATHS]) {
44565
+ if (await isExecutable(executablePath, $provideFilesystemForNode())) {
44566
+ return executablePath;
44320
44567
  }
44321
44568
  }
44322
44569
  throw new Error(`Can not locate app ${appName} on Windows.`);
@@ -75709,6 +75956,10 @@
75709
75956
  * Constant for prompts dir.
75710
75957
  */
75711
75958
  const PROMPTS_DIR$1 = path.join(process.cwd(), 'prompts');
75959
+ /**
75960
+ * Commit message for files changed by a successful or failed pre-coding test in repair mode.
75961
+ */
75962
+ const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-coding tests';
75712
75963
  /**
75713
75964
  * Main entry point for running prompts with the selected agent.
75714
75965
  *
@@ -76032,6 +76283,7 @@
76032
76283
  });
76033
76284
  await ensureWorkingTreeClean();
76034
76285
  }
76286
+ const testBeforeCommitScope = await captureTestBeforeCommitScopeIfNeeded(runOptions);
76035
76287
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
76036
76288
  const testBeforeResult = await runTestBefore({
76037
76289
  testCommand: runOptions.testCommand,
@@ -76040,6 +76292,11 @@
76040
76292
  }).finally(() => {
76041
76293
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
76042
76294
  });
76295
+ await commitTestBeforeChangesIfNeeded({
76296
+ runOptions,
76297
+ testBeforeCommitScope,
76298
+ waitForRequestedPause,
76299
+ });
76043
76300
  if (testBeforeResult.isPassed) {
76044
76301
  return hasWaitedForStart;
76045
76302
  }
@@ -76088,6 +76345,38 @@
76088
76345
  });
76089
76346
  return updatedHasWaitedForStart;
76090
76347
  }
76348
+ /**
76349
+ * Captures the files present before a `yes-and-fix` pre-coding test, so only changes made by that test can be committed.
76350
+ */
76351
+ async function captureTestBeforeCommitScopeIfNeeded(runOptions) {
76352
+ if (runOptions.testBefore !== 'yes-and-fix' || runOptions.noCommit) {
76353
+ return undefined;
76354
+ }
76355
+ return captureCoderCommitScope(process.cwd());
76356
+ }
76357
+ /**
76358
+ * Commits files changed by a `yes-and-fix` pre-coding test before the coder continues to a queued or repair prompt.
76359
+ */
76360
+ async function commitTestBeforeChangesIfNeeded(options) {
76361
+ const { runOptions, testBeforeCommitScope, waitForRequestedPause } = options;
76362
+ if (!testBeforeCommitScope) {
76363
+ return;
76364
+ }
76365
+ const relevantPaths = await resolveCoderCommitScopePaths(testBeforeCommitScope);
76366
+ if (relevantPaths.length === 0) {
76367
+ return;
76368
+ }
76369
+ await waitForRequestedPause({
76370
+ checkpointLabel: 'committing changes made by pre-coding tests',
76371
+ phase: 'verifying',
76372
+ statusMessage: 'Committing changes made by pre-coding tests...',
76373
+ });
76374
+ await commitChanges(PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE, {
76375
+ autoPush: runOptions.autoPush,
76376
+ projectPath: testBeforeCommitScope.projectPath,
76377
+ relevantPaths,
76378
+ });
76379
+ }
76091
76380
  /**
76092
76381
  * Creates a pause waiter that keeps the progress display and rich UI in sync.
76093
76382
  */