@promptbook/cli 0.113.0-4 → 0.113.0-8

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 (141) hide show
  1. package/apps/agents-server/src/app/admin/update/AdvancedOriginRepositoryPanel.tsx +76 -0
  2. package/apps/agents-server/src/app/admin/update/CurrentDeploymentCard.tsx +160 -0
  3. package/apps/agents-server/src/app/admin/update/PendingCommitsCard.tsx +86 -0
  4. package/apps/agents-server/src/app/admin/update/TargetEnvironmentCard.tsx +331 -0
  5. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +20 -873
  6. package/apps/agents-server/src/app/admin/update/UpdateJobCard.tsx +329 -0
  7. package/apps/agents-server/src/app/admin/update/UpdateOverview.ts +74 -0
  8. package/apps/agents-server/src/app/admin/update/buildDeploymentTimeBehindLabel.ts +29 -0
  9. package/apps/agents-server/src/app/admin/update/formatHumanReadableTimestamp.ts +18 -0
  10. package/apps/agents-server/src/app/admin/update/getUpdateJobFailureMessage.ts +13 -0
  11. package/apps/agents-server/src/app/admin/update/getUpdateJobSuccessMessage.ts +13 -0
  12. package/apps/agents-server/src/app/admin/update/useUpdateClientState.ts +441 -0
  13. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +13 -1
  14. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageClient.tsx +14 -559
  15. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageHeader.tsx +84 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageSection.tsx +52 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedCodePreview.tsx +138 -0
  18. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedHarnessSection.tsx +98 -0
  19. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/SourceBookSection.tsx +66 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeErrorBanner.tsx +30 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeExportWarningBanner.tsx +62 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/Transpiler.ts +9 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/useAgentCodeExportState.ts +478 -0
  24. package/apps/agents-server/src/app/system/utilities/mocked-chats/cloneMockedChatPreset.ts +17 -0
  25. package/apps/agents-server/src/app/system/utilities/mocked-chats/confirmDeleteMockedChat.ts +16 -0
  26. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedMessage.ts +81 -0
  27. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedParticipant.ts +146 -0
  28. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedSettings.ts +19 -0
  29. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDuplicatedDraft.ts +66 -0
  30. package/apps/agents-server/src/app/system/utilities/mocked-chats/findMockedChatById.ts +51 -0
  31. package/apps/agents-server/src/app/system/utilities/mocked-chats/persistMockedChats.ts +51 -0
  32. package/apps/agents-server/src/app/system/utilities/mocked-chats/useMockedChatsEditorState.ts +16 -426
  33. package/apps/agents-server/src/components/Homepage/buildFreeGraphBoxLayout.ts +446 -0
  34. package/apps/agents-server/src/components/Homepage/buildGraphLayoutNodes.ts +415 -157
  35. package/apps/agents-server/src/components/Homepage/useAgentsGraphCanvasState.ts +2 -1
  36. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +3 -1541
  37. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteQueryBuilder.ts +623 -0
  38. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteSupabaseClient.ts +64 -0
  39. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/ensureTable.ts +93 -0
  40. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteFilters.ts +202 -0
  41. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteMutationPayload.ts +131 -0
  42. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteRowOperations.ts +102 -0
  43. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteSql.ts +59 -0
  44. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTableSchema.ts +329 -0
  45. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTypes.ts +84 -0
  46. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteValueCodec.ts +63 -0
  47. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/normalizeSqliteError.ts +22 -0
  48. package/apps/agents-server/src/search/createDefaultServerSearchProviders/loadLocalOrganizationSearchDataset.ts +12 -1
  49. package/apps/agents-server/src/tools/send_email.ts +1 -2
  50. package/apps/agents-server/src/utils/agentOrganization/loadAgentOrganizationState.ts +13 -1
  51. package/apps/agents-server/src/utils/backup/createServerBackupZipStream.ts +7 -1382
  52. package/apps/agents-server/src/utils/backup/serverBackup/appendAgentBackupEntriesToZip.ts +27 -0
  53. package/apps/agents-server/src/utils/backup/serverBackup/appendConversationBackupEntriesToZip.ts +140 -0
  54. package/apps/agents-server/src/utils/backup/serverBackup/appendFileBackupEntriesToZip.ts +147 -0
  55. package/apps/agents-server/src/utils/backup/serverBackup/appendMessageBackupEntriesToZip.ts +65 -0
  56. package/apps/agents-server/src/utils/backup/serverBackup/appendMetadataBackupEntriesToZip.ts +39 -0
  57. package/apps/agents-server/src/utils/backup/serverBackup/appendSectionEntriesToZip.ts +92 -0
  58. package/apps/agents-server/src/utils/backup/serverBackup/appendUserBackupEntriesToZip.ts +80 -0
  59. package/apps/agents-server/src/utils/backup/serverBackup/createRedactedWalletBackupRecord.ts +34 -0
  60. package/apps/agents-server/src/utils/backup/serverBackup/createServerBackupManifest.ts +82 -0
  61. package/apps/agents-server/src/utils/backup/serverBackup/downloadBackupBinaryContent.ts +39 -0
  62. package/apps/agents-server/src/utils/backup/serverBackup/resolveFeedbackThreadMessages.ts +53 -0
  63. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupAttachments.ts +151 -0
  64. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupContext.ts +167 -0
  65. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupFilenames.ts +161 -0
  66. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupPreviews.ts +97 -0
  67. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupRowUtilities.ts +158 -0
  68. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupTypes.ts +59 -0
  69. package/apps/agents-server/src/utils/createLocalAgentSourceImporter.ts +159 -0
  70. package/apps/agents-server/src/utils/createMissingImportedAgentFallback.ts +60 -0
  71. package/apps/agents-server/src/utils/customDomainRouting.ts +157 -12
  72. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +1 -1
  73. package/apps/agents-server/src/utils/importAgentWithFallback.ts +1 -58
  74. package/apps/agents-server/src/utils/localAgentRouteReferences.ts +167 -0
  75. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +1 -1
  76. package/apps/agents-server/src/utils/managementApi/managementApiAgents.ts +17 -3
  77. package/apps/agents-server/src/utils/messages/sendMessage.ts +5 -3
  78. package/apps/agents-server/src/utils/resolveAgentStateFromSource.ts +7 -1
  79. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +54 -5
  80. package/apps/agents-server/src/utils/resolveServerAgentContext.ts +12 -1
  81. package/apps/agents-server/src/utils/resolveStoredAgentState.ts +2 -1
  82. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition/SpeechToTextFailoverRecognitionProviderRuntime.ts +0 -1
  83. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition.ts +34 -2
  84. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +39 -9
  85. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +5 -0
  86. package/esm/index.es.js +875 -60
  87. package/esm/index.es.js.map +1 -1
  88. package/esm/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  89. package/esm/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  90. package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +20 -0
  91. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +5 -0
  92. package/esm/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +29 -0
  93. package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +4 -0
  94. package/esm/src/avatars/renderAvatarVisualAsciiArt.d.ts +75 -0
  95. package/esm/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  96. package/esm/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  97. package/esm/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  98. package/esm/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  99. package/esm/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  100. package/esm/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
  101. package/esm/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
  102. package/esm/src/version.d.ts +1 -1
  103. package/package.json +1 -1
  104. package/src/avatars/avatarAnimationScheduler.ts +2 -1
  105. package/src/avatars/renderAvatarVisualAsciiArt.ts +138 -0
  106. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +6 -2
  107. package/src/avatars/visuals/octopus3d4AvatarVisual.ts +14 -18
  108. package/src/book-components/Chat/Chat/insertDictationChunk.ts +3 -3
  109. package/src/book-components/Chat/Chat/learnDictationDictionary.ts +1 -1
  110. package/src/book-components/Chat/Chat/useChatInputAreaDictation.ts +133 -26
  111. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +3 -1
  112. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +14 -21
  113. package/src/cli/cli-commands/coder/find-refactor-candidates.ts +9 -2
  114. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  115. package/src/cli/cli-commands/coder/run.ts +3 -27
  116. package/src/cli/cli-commands/common/createPositiveIntegerOptionParser.ts +31 -0
  117. package/src/commitments/TEAM/TEAM.ts +5 -5
  118. package/src/other/templates/getTemplatesPipelineCollection.ts +770 -1169
  119. package/src/utils/ascii-art/$detectTerminalAnsiColorDepth.ts +45 -0
  120. package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +396 -0
  121. package/src/version.ts +2 -2
  122. package/src/versions.txt +2 -0
  123. package/umd/index.umd.js +875 -60
  124. package/umd/index.umd.js.map +1 -1
  125. package/umd/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  126. package/umd/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  127. package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +20 -0
  128. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +5 -0
  129. package/umd/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +29 -0
  130. package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +4 -0
  131. package/umd/src/avatars/renderAvatarVisualAsciiArt.d.ts +75 -0
  132. package/umd/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  133. package/umd/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  134. package/umd/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  135. package/umd/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  136. package/umd/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  137. package/umd/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
  138. package/umd/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
  139. package/umd/src/version.d.ts +1 -1
  140. package/esm/scripts/run-codex-prompts/common/resolveAgentSystemMessage.d.ts +0 -6
  141. package/umd/scripts/run-codex-prompts/common/resolveAgentSystemMessage.d.ts +0 -6
@@ -38,6 +38,17 @@ type DictationChunk = {
38
38
  readonly start: number;
39
39
  };
40
40
 
41
+ /**
42
+ * Stable insertion range captured when the user starts dictation.
43
+ *
44
+ * @private function of `useChatInputAreaDictation`
45
+ */
46
+ type DictationInsertionSelection = {
47
+ readonly selectionStart: number;
48
+ readonly selectionEnd: number;
49
+ readonly isReplacingSelection: boolean;
50
+ };
51
+
41
52
  /**
42
53
  * Stable dictation error shape consumed by the correction panel UI.
43
54
  *
@@ -88,7 +99,7 @@ type UseChatInputAreaDictationFinalResultHandlerProps = {
88
99
  readonly applyMessageContent: (nextContent: string) => void;
89
100
  readonly dictationSettings: DictationRefinementSettings;
90
101
  readonly dictationDictionary: ReturnType<typeof useChatInputAreaDictationPersistence>['dictationDictionary'];
91
- readonly replaceSelectionOnNextFinalRef: MutableRefObject<boolean>;
102
+ readonly dictationInsertionSelectionRef: MutableRefObject<DictationInsertionSelection | null>;
92
103
  readonly focusTextareaSelection: (selectionStart: number, selectionEnd: number) => void;
93
104
  readonly state: UseChatInputAreaDictationState;
94
105
  };
@@ -126,7 +137,7 @@ type UseChatInputAreaDictationVoiceInputControlsProps = {
126
137
  readonly resolvedSpeechRecognitionLanguage: string;
127
138
  readonly whisperMode: boolean;
128
139
  readonly pendingStopFallbackRef: MutableRefObject<ReturnType<typeof setTimeout> | null>;
129
- readonly replaceSelectionOnNextFinalRef: MutableRefObject<boolean>;
140
+ readonly dictationInsertionSelectionRef: MutableRefObject<DictationInsertionSelection | null>;
130
141
  readonly clearPendingStopFallback: () => void;
131
142
  readonly state: Pick<
132
143
  UseChatInputAreaDictationState,
@@ -144,6 +155,7 @@ type UseChatInputAreaDictationCorrectionHandlersProps = {
144
155
  readonly dictationLastFinalChunk: string;
145
156
  readonly dictationEditableChunk: string;
146
157
  readonly dictationDictionary: ReturnType<typeof useChatInputAreaDictationPersistence>['dictationDictionary'];
158
+ readonly dictationInsertionSelectionRef: MutableRefObject<DictationInsertionSelection | null>;
147
159
  readonly messageContentRef: MutableRefObject<string>;
148
160
  readonly applyMessageContent: (nextContent: string) => void;
149
161
  readonly focusTextareaSelection: (selectionStart: number, selectionEnd: number) => void;
@@ -235,21 +247,96 @@ function clearPendingStopFallbackTimeout(
235
247
  }
236
248
 
237
249
  /**
238
- * Captures whether the next finalized chunk should replace the current selection.
250
+ * Captures where the next finalized chunk should be inserted.
239
251
  *
240
252
  * @private function of `useChatInputAreaDictation`
241
253
  */
242
- function captureSelectionReplacementIntent(
254
+ function captureDictationInsertionSelection(
243
255
  textareaRef: MutableRefObject<HTMLTextAreaElement | null>,
244
- replaceSelectionOnNextFinalRef: MutableRefObject<boolean>,
256
+ dictationInsertionSelectionRef: MutableRefObject<DictationInsertionSelection | null>,
245
257
  ): void {
246
258
  const textarea = textareaRef.current;
247
259
  if (!textarea) {
248
260
  return;
249
261
  }
250
262
 
251
- const { selectionStart, selectionEnd } = resolveTextareaSelection(textarea, 0);
252
- replaceSelectionOnNextFinalRef.current = selectionStart !== selectionEnd;
263
+ const { selectionStart, selectionEnd } = resolveTextareaSelection(textarea, textarea.value.length);
264
+ dictationInsertionSelectionRef.current = {
265
+ selectionStart,
266
+ selectionEnd,
267
+ isReplacingSelection: selectionStart !== selectionEnd,
268
+ };
269
+ }
270
+
271
+ /**
272
+ * Resolves the insertion range for one finalized dictation chunk.
273
+ *
274
+ * @private function of `useChatInputAreaDictation`
275
+ */
276
+ function resolveDictationInsertionSelection(
277
+ textareaElement: HTMLTextAreaElement,
278
+ messageContentLength: number,
279
+ dictationInsertionSelection: DictationInsertionSelection | null,
280
+ ): DictationInsertionSelection {
281
+ if (dictationInsertionSelection) {
282
+ return dictationInsertionSelection;
283
+ }
284
+
285
+ const { selectionStart, selectionEnd } = resolveTextareaSelection(textareaElement, messageContentLength);
286
+
287
+ return {
288
+ selectionStart,
289
+ selectionEnd,
290
+ isReplacingSelection: selectionStart !== selectionEnd,
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Removes one tracked dictated chunk from the current composer value.
296
+ *
297
+ * @private function of `useChatInputAreaDictation`
298
+ */
299
+ function removeTrackedDictationChunk(currentValue: string, chunk: DictationChunk): string {
300
+ const trackedText = currentValue.slice(chunk.start, chunk.start + chunk.finalText.length);
301
+
302
+ if (trackedText === chunk.finalText) {
303
+ return `${currentValue.slice(0, chunk.start)}${currentValue.slice(chunk.start + chunk.finalText.length)}`;
304
+ }
305
+
306
+ return replaceLastOccurrence(currentValue, chunk.finalText, '');
307
+ }
308
+
309
+ /**
310
+ * Replaces one tracked dictated chunk inside the current composer value.
311
+ *
312
+ * @private function of `useChatInputAreaDictation`
313
+ */
314
+ function replaceTrackedDictationChunk(currentValue: string, chunk: DictationChunk, correctedChunk: string): string {
315
+ const trackedText = currentValue.slice(chunk.start, chunk.start + chunk.finalText.length);
316
+
317
+ if (trackedText === chunk.finalText) {
318
+ return `${currentValue.slice(0, chunk.start)}${correctedChunk}${currentValue.slice(
319
+ chunk.start + chunk.finalText.length,
320
+ )}`;
321
+ }
322
+
323
+ return replaceLastOccurrence(currentValue, chunk.finalText, correctedChunk);
324
+ }
325
+
326
+ /**
327
+ * Stores the next append position after a programmatic dictation edit.
328
+ *
329
+ * @private function of `useChatInputAreaDictation`
330
+ */
331
+ function updateDictationInsertionCaret(
332
+ dictationInsertionSelectionRef: MutableRefObject<DictationInsertionSelection | null>,
333
+ caret: number,
334
+ ): void {
335
+ dictationInsertionSelectionRef.current = {
336
+ selectionStart: caret,
337
+ selectionEnd: caret,
338
+ isReplacingSelection: false,
339
+ };
253
340
  }
254
341
 
255
342
  /**
@@ -376,7 +463,7 @@ function useChatInputAreaDictationFinalResultHandler({
376
463
  applyMessageContent,
377
464
  dictationSettings,
378
465
  dictationDictionary,
379
- replaceSelectionOnNextFinalRef,
466
+ dictationInsertionSelectionRef,
380
467
  focusTextareaSelection,
381
468
  state,
382
469
  }: UseChatInputAreaDictationFinalResultHandlerProps) {
@@ -403,16 +490,20 @@ function useChatInputAreaDictationFinalResultHandler({
403
490
  return;
404
491
  }
405
492
 
406
- const { selectionStart, selectionEnd } = resolveTextareaSelection(textarea, previousMessageContent.length);
493
+ const { selectionStart, selectionEnd, isReplacingSelection } = resolveDictationInsertionSelection(
494
+ textarea,
495
+ previousMessageContent.length,
496
+ dictationInsertionSelectionRef.current,
497
+ );
407
498
  const insertion = insertDictationChunk({
408
499
  currentValue: previousMessageContent,
409
500
  dictatedText: refinedText,
410
501
  selectionStart,
411
502
  selectionEnd,
412
- shouldReplaceSelection: replaceSelectionOnNextFinalRef.current,
503
+ isReplacingSelection,
413
504
  });
414
505
 
415
- replaceSelectionOnNextFinalRef.current = false;
506
+ updateDictationInsertionCaret(dictationInsertionSelectionRef, insertion.caret);
416
507
  setDictationInterimText('');
417
508
  setDictationError(null);
418
509
  setDictationUiState('listening');
@@ -433,10 +524,10 @@ function useChatInputAreaDictationFinalResultHandler({
433
524
  [
434
525
  applyMessageContent,
435
526
  dictationDictionary,
527
+ dictationInsertionSelectionRef,
436
528
  dictationSettings,
437
529
  focusTextareaSelection,
438
530
  messageContentRef,
439
- replaceSelectionOnNextFinalRef,
440
531
  setDictationChunks,
441
532
  setDictationEditableChunk,
442
533
  setDictationError,
@@ -491,7 +582,9 @@ function useChatInputAreaDictationSpeechRecognitionEventHandler({
491
582
  return;
492
583
  case 'STOP':
493
584
  clearPendingStopFallback();
494
- setDictationUiState((currentState) => (currentState === 'disabled' ? currentState : 'idle'));
585
+ setDictationUiState((currentState) =>
586
+ currentState === 'disabled' || currentState === 'error' ? currentState : 'idle',
587
+ );
495
588
  setDictationInterimText('');
496
589
  return;
497
590
  }
@@ -549,7 +642,7 @@ function useChatInputAreaDictationVoiceInputControls({
549
642
  resolvedSpeechRecognitionLanguage,
550
643
  whisperMode,
551
644
  pendingStopFallbackRef,
552
- replaceSelectionOnNextFinalRef,
645
+ dictationInsertionSelectionRef,
553
646
  clearPendingStopFallback,
554
647
  state,
555
648
  }: UseChatInputAreaDictationVoiceInputControlsProps) {
@@ -560,7 +653,7 @@ function useChatInputAreaDictationVoiceInputControls({
560
653
  return;
561
654
  }
562
655
 
563
- captureSelectionReplacementIntent(textareaRef, replaceSelectionOnNextFinalRef);
656
+ captureDictationInsertionSelection(textareaRef, dictationInsertionSelectionRef);
564
657
  setDictationError(null);
565
658
  setDictationInterimText('');
566
659
  setDictationUiState('listening');
@@ -570,7 +663,7 @@ function useChatInputAreaDictationVoiceInputControls({
570
663
  whisperMode,
571
664
  });
572
665
  }, [
573
- replaceSelectionOnNextFinalRef,
666
+ dictationInsertionSelectionRef,
574
667
  resolvedSpeechRecognitionLanguage,
575
668
  setDictationError,
576
669
  setDictationInterimText,
@@ -635,6 +728,7 @@ function useChatInputAreaDictationCorrectionHandlers({
635
728
  dictationLastFinalChunk,
636
729
  dictationEditableChunk,
637
730
  dictationDictionary,
731
+ dictationInsertionSelectionRef,
638
732
  messageContentRef,
639
733
  applyMessageContent,
640
734
  focusTextareaSelection,
@@ -651,17 +745,23 @@ function useChatInputAreaDictationCorrectionHandlers({
651
745
  return;
652
746
  }
653
747
 
748
+ const nextMessageContent = removeTrackedDictationChunk(messageContentRef.current, lastChunk);
749
+ const nextCaret = Math.min(lastChunk.start, nextMessageContent.length);
750
+
654
751
  setDictationChunks(previousChunks);
655
- applyMessageContent(lastChunk.beforeValue);
752
+ applyMessageContent(nextMessageContent);
656
753
 
657
754
  const previousFinalChunk = previousChunks[previousChunks.length - 1]?.finalText || '';
658
755
  setDictationLastFinalChunk(previousFinalChunk);
659
756
  setDictationEditableChunk(previousFinalChunk);
660
- focusTextareaSelection(lastChunk.start, lastChunk.start);
757
+ updateDictationInsertionCaret(dictationInsertionSelectionRef, nextCaret);
758
+ focusTextareaSelection(nextCaret, nextCaret);
661
759
  }, [
662
760
  applyMessageContent,
761
+ dictationInsertionSelectionRef,
663
762
  dictationChunks,
664
763
  focusTextareaSelection,
764
+ messageContentRef,
665
765
  setDictationChunks,
666
766
  setDictationEditableChunk,
667
767
  setDictationLastFinalChunk,
@@ -673,21 +773,27 @@ function useChatInputAreaDictationCorrectionHandlers({
673
773
  return;
674
774
  }
675
775
 
676
- const nextMessageContent = replaceLastOccurrence(
677
- messageContentRef.current,
678
- dictationLastFinalChunk,
679
- correctedChunk,
680
- );
776
+ const lastChunk = dictationChunks[dictationChunks.length - 1];
777
+ const nextMessageContent = lastChunk
778
+ ? replaceTrackedDictationChunk(messageContentRef.current, lastChunk, correctedChunk)
779
+ : replaceLastOccurrence(messageContentRef.current, dictationLastFinalChunk, correctedChunk);
780
+ const correctionStart = lastChunk?.start ?? nextMessageContent.length;
781
+ const correctionCaret = Math.min(correctionStart + correctedChunk.length, nextMessageContent.length);
681
782
 
682
783
  applyMessageContent(nextMessageContent);
683
784
  setDictationLastFinalChunk(correctedChunk);
684
785
  setDictationChunks((previousChunks) => replaceLastDictationChunk(previousChunks, correctedChunk));
685
786
  setDictationDictionary(learnDictationDictionary(dictationLastFinalChunk, correctedChunk, dictationDictionary));
787
+ updateDictationInsertionCaret(dictationInsertionSelectionRef, correctionCaret);
788
+ focusTextareaSelection(correctionCaret, correctionCaret);
686
789
  }, [
687
790
  applyMessageContent,
688
791
  dictationDictionary,
689
792
  dictationEditableChunk,
793
+ dictationChunks,
794
+ dictationInsertionSelectionRef,
690
795
  dictationLastFinalChunk,
796
+ focusTextareaSelection,
691
797
  messageContentRef,
692
798
  setDictationChunks,
693
799
  setDictationDictionary,
@@ -713,7 +819,7 @@ export function useChatInputAreaDictation({
713
819
  applyMessageContent,
714
820
  }: UseChatInputAreaDictationProps) {
715
821
  const pendingStopFallbackRef = useRef<ReturnType<typeof setTimeout> | null>(null);
716
- const replaceSelectionOnNextFinalRef = useRef(false);
822
+ const dictationInsertionSelectionRef = useRef<DictationInsertionSelection | null>(null);
717
823
  const [dictationUiState, setDictationUiState] = useState<DictationUiState>('idle');
718
824
  const [dictationInterimText, setDictationInterimText] = useState('');
719
825
  const [dictationError, setDictationError] = useState<DictationError | null>(null);
@@ -753,7 +859,7 @@ export function useChatInputAreaDictation({
753
859
  applyMessageContent,
754
860
  dictationSettings,
755
861
  dictationDictionary,
756
- replaceSelectionOnNextFinalRef,
862
+ dictationInsertionSelectionRef,
757
863
  focusTextareaSelection,
758
864
  state: dictationState,
759
865
  });
@@ -775,7 +881,7 @@ export function useChatInputAreaDictation({
775
881
  resolvedSpeechRecognitionLanguage,
776
882
  whisperMode: dictationSettings.whisperMode,
777
883
  pendingStopFallbackRef,
778
- replaceSelectionOnNextFinalRef,
884
+ dictationInsertionSelectionRef,
779
885
  clearPendingStopFallback,
780
886
  state: {
781
887
  dictationUiState,
@@ -789,6 +895,7 @@ export function useChatInputAreaDictation({
789
895
  dictationLastFinalChunk,
790
896
  dictationEditableChunk,
791
897
  dictationDictionary,
898
+ dictationInsertionSelectionRef,
792
899
  messageContentRef,
793
900
  applyMessageContent,
794
901
  focusTextareaSelection,
@@ -739,7 +739,9 @@ function isNextBuildTerminationRetryable(exitStatus: NextBuildExitStatus): boole
739
739
  * Creates one visible retry message for a killed Next.js build attempt.
740
740
  */
741
741
  function createNextBuildRetryMessage(exitStatus: NextBuildExitStatus, nextAttempt: number): string {
742
- return `\nAgents Server Next build was terminated by ${describeNextBuildExitStatus(exitStatus)}. Retrying attempt ${nextAttempt}/${AGENTS_SERVER_BUILD_MAX_ATTEMPTS}.\n`;
742
+ return `\nAgents Server Next build was terminated by ${describeNextBuildExitStatus(
743
+ exitStatus,
744
+ )}. Retrying attempt ${nextAttempt}/${AGENTS_SERVER_BUILD_MAX_ATTEMPTS}.\n`;
743
745
  }
744
746
 
745
747
  /**
@@ -120,26 +120,26 @@ const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
120
120
  id: 'common',
121
121
  relativeFilePath: join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'common.md'),
122
122
  slugPrefix: null,
123
- content: buildCoderPromptTemplateContent([
124
- '- @@@',
125
- "- Keep in mind the DRY _(don't repeat yourself)_ principle.",
126
- '- Do a proper analysis of the current functionality before you start implementing.',
127
- '- Add the changes into the [changelog](changelog/_current-preversion.md)',
128
- ]),
123
+ content: spaceTrim(`
124
+ - @@@
125
+ - Keep in mind the DRY _(don't repeat yourself)_ principle.
126
+ - Do a proper analysis of the current functionality before you start implementing.
127
+ - Add the changes into the [changelog](changelog/_current-preversion.md)
128
+ `),
129
129
  isDefaultProjectTemplate: true,
130
130
  },
131
131
  {
132
132
  id: 'agents-server',
133
133
  relativeFilePath: join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'agents-server.md'),
134
134
  slugPrefix: 'agents-server',
135
- content: buildCoderPromptTemplateContent([
136
- '- @@@',
137
- "- Keep in mind the DRY _(don't repeat yourself)_ principle.",
138
- '- Do a proper analysis of the current functionality before you start implementing.',
139
- '- You are working with the [Agents Server](apps/agents-server)',
140
- '- If you need to do the database migration, do it',
141
- '- Add the changes into the [changelog](changelog/_current-preversion.md)',
142
- ]),
135
+ content: spaceTrim(`
136
+ - @@@
137
+ - Keep in mind the DRY _(don't repeat yourself)_ principle.
138
+ - Do a proper analysis of the current functionality before you start implementing.
139
+ - You are working with the [Agents Server](apps/agents-server)
140
+ - If you need to do the database migration, do it
141
+ - Add the changes into the [changelog](changelog/_current-preversion.md)
142
+ `),
143
143
  isDefaultProjectTemplate: false,
144
144
  },
145
145
  ] as const satisfies ReadonlyArray<CoderPromptTemplateDefinition>;
@@ -288,13 +288,6 @@ function getDefaultCoderPromptTemplateDefinitionOrUndefined(
288
288
  return DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS.find((definition) => definition.id === template);
289
289
  }
290
290
 
291
- /**
292
- * Builds stable markdown content for one coder prompt template without indentation drift.
293
- */
294
- function buildCoderPromptTemplateContent(lines: ReadonlyArray<string>): string {
295
- return lines.join('\n');
296
- }
297
-
298
291
  /**
299
292
  * Creates a fully resolved template payload from one built-in definition.
300
293
  */
@@ -12,6 +12,7 @@ import {
12
12
  } from '../../../../scripts/find-refactor-candidates/RefactorCandidateLevel';
13
13
  import { assertsError } from '../../../errors/assertsError';
14
14
  import type { $side_effect } from '../../../utils/organization/$side_effect';
15
+ import { createPositiveIntegerOptionParser } from '../common/createPositiveIntegerOptionParser';
15
16
  import { handleActionErrors } from '../common/handleActionErrors';
16
17
 
17
18
  /**
@@ -44,11 +45,17 @@ export function $initializeCoderFindRefactorCandidatesCommand(program: Program):
44
45
  .choices([...REFACTOR_CANDIDATE_LEVEL_VALUES])
45
46
  .default(DEFAULT_REFACTOR_CANDIDATE_LEVEL),
46
47
  );
48
+ command.option(
49
+ '--limit <candidate-count>',
50
+ 'Create at most this many refactor prompts, keeping the most important candidates',
51
+ createPositiveIntegerOptionParser('--limit'),
52
+ );
47
53
 
48
54
  command.action(
49
55
  handleActionErrors(async (cliOptions) => {
50
- const { level = DEFAULT_REFACTOR_CANDIDATE_LEVEL } = cliOptions as {
56
+ const { level = DEFAULT_REFACTOR_CANDIDATE_LEVEL, limit } = cliOptions as {
51
57
  readonly level?: RefactorCandidateLevel;
58
+ readonly limit?: number;
52
59
  };
53
60
 
54
61
  // Note: Import the function dynamically to avoid loading heavy dependencies until needed
@@ -57,7 +64,7 @@ export function $initializeCoderFindRefactorCandidatesCommand(program: Program):
57
64
  );
58
65
 
59
66
  try {
60
- await findRefactorCandidates({ level });
67
+ await findRefactorCandidates({ level, limit });
61
68
  } catch (error) {
62
69
  assertsError(error);
63
70
  console.error(colors.bgRed(`${error.name}`));
@@ -8,10 +8,7 @@ import {
8
8
  PROMPTS_TEMPLATES_DIRECTORY_PATH,
9
9
  } from './boilerplateTemplates';
10
10
  import { ensureCoderEnvFile } from './ensureCoderEnvFile';
11
- import {
12
- CODER_AGENTS_DIRECTORY_PATH,
13
- ensureCoderDeveloperAgentFile,
14
- } from './ensureCoderDeveloperAgentFile';
11
+ import { CODER_AGENTS_DIRECTORY_PATH, ensureCoderDeveloperAgentFile } from './ensureCoderDeveloperAgentFile';
15
12
  import { ensureCoderGitignoreFile } from './ensureCoderGitignoreFile';
16
13
  import { ensureCoderMarkdownFile } from './ensureCoderMarkdownFile';
17
14
  import { ensureCoderPackageJsonFile } from './ensureCoderPackageJsonFile';
@@ -1,9 +1,9 @@
1
1
  import colors from 'colors';
2
2
  import { Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */ } from 'commander';
3
3
  import { spaceTrim } from 'spacetrim';
4
- import { NotAllowed } from '../../../errors/NotAllowed';
5
4
  import { assertsError } from '../../../errors/assertsError';
6
5
  import type { $side_effect } from '../../../utils/organization/$side_effect';
6
+ import { createPositiveIntegerOptionParser } from '../common/createPositiveIntegerOptionParser';
7
7
  import { handleActionErrors } from '../common/handleActionErrors';
8
8
  import type { PromptRunnerCliOptions } from '../common/promptRunnerCliOptions';
9
9
  import {
@@ -12,10 +12,7 @@ import {
12
12
  normalizePromptRunnerCliOptions,
13
13
  PROMPT_RUNNER_DESCRIPTION,
14
14
  } from '../common/promptRunnerCliOptions';
15
- import {
16
- DEFAULT_WAIT_AFTER_ERROR_MS,
17
- parseOptionalWaitDuration,
18
- } from './waitOptions';
15
+ import { DEFAULT_WAIT_AFTER_ERROR_MS, parseOptionalWaitDuration } from './waitOptions';
19
16
 
20
17
  /**
21
18
  * Initializes `coder run` command for Promptbook CLI utilities
@@ -69,7 +66,7 @@ export function $initializeCoderRunCommand(program: Program): $side_effect {
69
66
  command.option(
70
67
  '--limit <run-count>',
71
68
  'Stop after processing this many prompt runs',
72
- parsePositiveIntegerOption,
69
+ createPositiveIntegerOptionParser('--limit'),
73
70
  );
74
71
  command.option(
75
72
  '--wait-after-prompt <duration>',
@@ -213,27 +210,6 @@ function parseIntOption(value: string): number {
213
210
  return parsed;
214
211
  }
215
212
 
216
- /**
217
- * Parses a positive integer option value.
218
- *
219
- * @private internal utility of `coder run` command
220
- */
221
- function parsePositiveIntegerOption(value: string): number {
222
- const parsed = Number(value);
223
-
224
- if (!Number.isInteger(parsed) || parsed <= 0) {
225
- throw new NotAllowed(
226
- spaceTrim(`
227
- Invalid value for \`--limit\`: \`${value}\`.
228
-
229
- Use a positive integer.
230
- `),
231
- );
232
- }
233
-
234
- return parsed;
235
- }
236
-
237
213
  /**
238
214
  * Joins one Commander option that may be parsed either as a single string or a variadic token array.
239
215
  *
@@ -0,0 +1,31 @@
1
+ import { spaceTrim } from 'spacetrim';
2
+ import { NotAllowed } from '../../../errors/NotAllowed';
3
+
4
+ /**
5
+ * Creates a Commander argument parser that accepts only positive integers.
6
+ *
7
+ * The returned parser is meant to be passed as the coercion callback of `command.option(...)`.
8
+ * It throws a branded `NotAllowed` error with a clear message referencing the given option name
9
+ * when the provided value is not a positive integer.
10
+ *
11
+ * @private internal utility of `promptbookCli`
12
+ */
13
+ export function createPositiveIntegerOptionParser(optionName: string): (value: string) => number {
14
+ return (value: string): number => {
15
+ const parsedValue = Number(value);
16
+
17
+ if (!Number.isInteger(parsedValue) || parsedValue <= 0) {
18
+ throw new NotAllowed(
19
+ spaceTrim(`
20
+ Invalid value for \`${optionName}\`: \`${value}\`.
21
+
22
+ Use a positive integer.
23
+ `),
24
+ );
25
+ }
26
+
27
+ return parsedValue;
28
+ };
29
+ }
30
+
31
+ // Note: [🟡] Code for CLI option parser [createPositiveIntegerOptionParser](src/cli/cli-commands/common/createPositiveIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
@@ -113,10 +113,10 @@ const teamToolTitles: Record<string_javascript_name, string> = {};
113
113
  *
114
114
  * @private
115
115
  */
116
- const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
117
- '- If a teammate is relevant to the request, consult that teammate using the matching tool.',
118
- '- Do not ask the user for information that a listed teammate can provide directly.',
119
- ] as const;
116
+ const TEAM_SYSTEM_MESSAGE_GUIDANCE = spaceTrim(`
117
+ - If a teammate is relevant to the request, consult that teammate using the matching tool.
118
+ - Do not ask the user for information that a listed teammate can provide directly.
119
+ `);
120
120
  /**
121
121
  * Constant for remote agents by Url.
122
122
  */
@@ -352,7 +352,7 @@ function buildTeamSystemMessageBody(teamEntries: ReadonlyArray<TeamToolEntry>):
352
352
 
353
353
  return spaceTrim(
354
354
  (block) => `
355
- ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
355
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE)}
356
356
 
357
357
  ${block(teammateSections.join('\n\n'))}
358
358
  `,