@promptbook/cli 0.114.0-5 → 0.114.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 (296) hide show
  1. package/agents/default/developer.book +1 -0
  2. package/apps/agents-server/next.config.ts +22 -0
  3. package/apps/agents-server/package.json +6 -6
  4. package/apps/agents-server/scripts/build-agents-server.js +43 -7
  5. package/apps/agents-server/scripts/build-e2e.js +12 -2
  6. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +30 -16
  7. package/apps/agents-server/scripts/kill-port.js +163 -0
  8. package/apps/agents-server/scripts/run-e2e-tests.js +21 -8
  9. package/apps/agents-server/scripts/run-npm.js +81 -5
  10. package/apps/agents-server/src/app/actions.ts +3 -0
  11. package/apps/agents-server/src/app/admin/email-server/page.tsx +17 -18
  12. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +2 -0
  13. package/apps/agents-server/src/app/admin/task-manager/[taskId]/TaskManagerTaskDetailClient.tsx +2 -0
  14. package/apps/agents-server/src/app/admin/task-manager/taskManagerTaskPresentation.tsx +23 -0
  15. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +7 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/[timeoutId]/route.ts +10 -153
  17. package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/route.ts +9 -4
  18. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/route.ts +3 -0
  19. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +2 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/resolveUserChatScope.ts +8 -1
  21. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/route.ts +15 -7
  22. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorSaving.ts +18 -28
  23. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistoryClient.tsx +19 -1
  24. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatSidebarDefault.tsx +17 -5
  25. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatNotice.tsx +23 -0
  26. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatPlannedMessages.tsx +153 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatPanel.tsx +6 -0
  28. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +13 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/chat/ExternalUserChatAdminActions.tsx +51 -3
  30. package/apps/agents-server/src/app/agents/[agentName]/chat/TeamMemberFrozenChatPrimaryAgentLink.tsx +31 -0
  31. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatSidebarState.ts +26 -2
  32. package/apps/agents-server/src/app/agents/[agentName]/goal/page.tsx +35 -0
  33. package/apps/agents-server/src/app/agents/[agentName]/projects/[projectName]/page.tsx +2 -2
  34. package/apps/agents-server/src/app/api/admin/dns-records/cloudflare/route.ts +26 -16
  35. package/apps/agents-server/src/app/api/emails/incoming/stalwart/route.ts +9 -2
  36. package/apps/agents-server/src/app/superadmin/servers/ServersRegistryTable.tsx +31 -39
  37. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +10 -3
  38. package/apps/agents-server/src/components/AgentProjects/AgentProjectIcon.tsx +137 -0
  39. package/apps/agents-server/src/components/AgentProjects/AgentProjectItem.tsx +26 -10
  40. package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsApiTokenImportStep.tsx +5 -6
  41. package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsCheckStep.tsx +6 -5
  42. package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsManualStep.tsx +10 -14
  43. package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsWizard.tsx +11 -20
  44. package/apps/agents-server/src/components/CloudflareDnsWizard/useCloudflareDnsRecordImport.ts +5 -6
  45. package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsInstructions.tsx +91 -57
  46. package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsSectionPanel.tsx +63 -0
  47. package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsSectionVariantTabs.tsx +67 -0
  48. package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsTable.tsx +40 -0
  49. package/apps/agents-server/src/components/Header/buildActiveAgentViewItems.ts +5 -4
  50. package/apps/agents-server/src/components/Header/createAgentViewLabel.tsx +4 -4
  51. package/apps/agents-server/src/components/Header/resolveActiveAgentNavigation.ts +2 -2
  52. package/apps/agents-server/src/components/Homepage/useAgentsListImportExportState.ts +22 -10
  53. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +1 -1
  54. package/apps/agents-server/src/database/migratePrefix.ts +13 -12
  55. package/apps/agents-server/src/database/migrations/2026-08-0100-agent-goal-chat-source.sql +10 -0
  56. package/apps/agents-server/src/generated/reservedPaths.ts +42 -42
  57. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +9 -1
  58. package/apps/agents-server/src/languages/translations/czech.yaml +9 -1
  59. package/apps/agents-server/src/languages/translations/english.yaml +9 -1
  60. package/apps/agents-server/src/message-providers/email/stalwart/parseInboundStalwartEmail.ts +17 -2
  61. package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatConstants.ts +9 -0
  62. package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatIdentity.ts +28 -0
  63. package/apps/agents-server/src/utils/agentGoalChat/appendAgentGoalChatNote.ts +43 -0
  64. package/apps/agents-server/src/utils/agentGoalChat/canAccessAgentGoalChat.ts +14 -0
  65. package/apps/agents-server/src/utils/agentGoalChat/createAgentGoalChatNoteContent.ts +69 -0
  66. package/apps/agents-server/src/utils/agentGoalChat/ensureAgentGoalChat.ts +66 -0
  67. package/apps/agents-server/src/utils/agentGoalChat/prependAgentGoalChatSummarySeed.ts +40 -0
  68. package/apps/agents-server/src/utils/agentGoalChat/recordAgentGoalChatLifecycleNote.ts +36 -0
  69. package/apps/agents-server/src/utils/agentGoalChat/resolveAgentGoalChatOwnerUserId.ts +104 -0
  70. package/apps/agents-server/src/utils/agentGoalChat/scheduleAgentGoalChatModifiedNote.ts +82 -0
  71. package/apps/agents-server/src/utils/agentGoalChat.ts +19 -0
  72. package/apps/agents-server/src/utils/agentProjects/AgentProjectInfo.ts +10 -1
  73. package/apps/agents-server/src/utils/agentProjects/AgentProjectReferenceInfo.ts +5 -2
  74. package/apps/agents-server/src/utils/agentProjects/agentProjectFileNames.ts +21 -0
  75. package/apps/agents-server/src/utils/agentProjects/agentProjectHrefs.ts +20 -11
  76. package/apps/agents-server/src/utils/agentProjects/agentProjectsPaths.ts +15 -5
  77. package/apps/agents-server/src/utils/agentProjects/createAgentProjectInitials.ts +36 -0
  78. package/apps/agents-server/src/utils/agentProjects/createAgentProjectMarkdownReferences.ts +11 -9
  79. package/apps/agents-server/src/utils/agentProjects/createAgentProjectsDnsRecordsSection.ts +113 -0
  80. package/apps/agents-server/src/utils/agentProjects/createStaticAgentProjectServer.ts +2 -6
  81. package/apps/agents-server/src/utils/agentProjects/humanizeAgentProjectName.ts +25 -0
  82. package/apps/agents-server/src/utils/agentProjects/listAgentProjectChatReferences.ts +10 -3
  83. package/apps/agents-server/src/utils/agentProjects/parseAgentProjectIndexHtml.ts +167 -0
  84. package/apps/agents-server/src/utils/agentProjects/readAgentProjectIndexHtmlProfile.ts +24 -0
  85. package/apps/agents-server/src/utils/agentProjects/readAgentProjectReadme.ts +3 -53
  86. package/apps/agents-server/src/utils/agentProjects/readAgentProjectRootTextFile.ts +73 -0
  87. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectFaviconRelativePath.ts +108 -0
  88. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectInfo.ts +12 -3
  89. package/apps/agents-server/src/utils/agentProjects/{resolveAgentProjectReadmeProfile.ts → resolveAgentProjectProfile.ts} +33 -33
  90. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectPublicUrls.ts +34 -0
  91. package/apps/agents-server/src/utils/bookLanguageDocumentation/createBookLanguageDocumentationPdfResponse.ts +19 -17
  92. package/apps/agents-server/src/utils/chatTasksAdmin.ts +9 -0
  93. package/apps/agents-server/src/utils/dnsRecords/DnsRecordInstruction.ts +20 -0
  94. package/apps/agents-server/src/utils/dnsRecords/DnsRecordsSection.ts +77 -0
  95. package/apps/agents-server/src/utils/dnsRecords/createServerDnsRecordsSections.ts +58 -0
  96. package/apps/agents-server/src/utils/dnsRecords/createServerDomainDnsRecordsSection.ts +48 -0
  97. package/apps/agents-server/src/utils/dnsRecords/dnsRecordGroups.ts +52 -0
  98. package/apps/agents-server/src/utils/dnsRecords/resolveDnsRecordBatchPlan.ts +47 -1
  99. package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/adminChatTaskSqlQuery.ts +41 -36
  100. package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/mapAdminChatTaskFallbackRows.ts +3 -0
  101. package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/mapAdminChatTaskSqlRows.ts +2 -0
  102. package/apps/agents-server/src/utils/localChatRunner/parseLocalTeamConversations.ts +281 -0
  103. package/apps/agents-server/src/utils/localChatRunner/persistLocalTeamConversations.ts +71 -0
  104. package/apps/agents-server/src/utils/localChatRunner/prepareLocalTeamConversationWorkspace.ts +233 -0
  105. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +37 -6
  106. package/apps/agents-server/src/utils/serverManagement/createManagedServer/insertManagedServerRegistryRow.ts +2 -2
  107. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerCoreAgents.ts +7 -6
  108. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +5 -4
  109. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerMetadata.ts +3 -2
  110. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerUsers.ts +3 -2
  111. package/apps/agents-server/src/utils/serverManagement/deleteManagedServer.ts +2 -2
  112. package/apps/agents-server/src/utils/stalwart/createEmailDnsRecordsSection.ts +53 -0
  113. package/apps/agents-server/src/utils/userChat/UserChatRecord.ts +13 -0
  114. package/apps/agents-server/src/utils/userChat/UserChatSource.ts +14 -0
  115. package/apps/agents-server/src/utils/userChat/createUserChatSummary.ts +10 -2
  116. package/apps/agents-server/src/utils/userChat/finalizeUserChatJob.ts +3 -2
  117. package/apps/agents-server/src/utils/userChat/getUserChat.ts +16 -1
  118. package/apps/agents-server/src/utils/userChat/getUserChatForJobRunner.ts +31 -0
  119. package/apps/agents-server/src/utils/userChat/listUserChats.ts +113 -110
  120. package/apps/agents-server/src/utils/userChat/persistFrozenUserChat.ts +21 -6
  121. package/apps/agents-server/src/utils/userChat/runImmediateUserChatAnswer.ts +2 -6
  122. package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +2 -6
  123. package/apps/agents-server/src/utils/userChat/teamMemberUserChatContext.ts +63 -0
  124. package/apps/agents-server/src/utils/userChat.ts +5 -1
  125. package/apps/agents-server/src/utils/userChatClient/cancelAgentUserTimeout.ts +2 -2
  126. package/apps/agents-server/src/utils/userChatClient/fetchAgentUserTimeouts.ts +2 -2
  127. package/apps/agents-server/src/utils/userChatClient.ts +9 -31
  128. package/apps/agents-server/src/utils/userChatTimeout/UserChatTimeoutRecord.ts +10 -2
  129. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/claimNextDueUserChatTimeout.ts +28 -25
  130. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/getAgentScopedUserChatTimeout.ts +8 -4
  131. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/listAgentUserChatTimeouts.ts +4 -1
  132. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/recoverExpiredRunningUserChatTimeouts.ts +3 -2
  133. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutWorker.ts +53 -0
  134. package/esm/index.es.js +1534 -401
  135. package/esm/index.es.js.map +1 -1
  136. package/esm/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +7 -1
  137. package/esm/scripts/run-agent-messages/messages/buildAgentTeamPromptSection.d.ts +12 -0
  138. package/esm/scripts/run-agent-messages/messages/finalizeAgentTeamConversationWorkspace.d.ts +16 -0
  139. package/esm/scripts/run-agent-messages/messages/loadAgentTeamConversationWorkspace.d.ts +6 -0
  140. package/esm/scripts/run-codex-prompts/common/normalizeLineEndingsInChangedFiles.d.ts +2 -3
  141. package/esm/scripts/run-codex-prompts/git/coderCommitScope.d.ts +31 -0
  142. package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +15 -2
  143. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +9 -3
  144. package/esm/scripts/run-codex-prompts/git/workingTreeChanges.d.ts +40 -0
  145. package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  146. package/esm/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +5 -5
  147. package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiTerminalFrameUpdate.d.ts +27 -0
  148. package/esm/src/avatars/renderAvatarVisualTerminalText.d.ts +80 -0
  149. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +54 -0
  150. package/esm/src/book-3.0/AgentTeamConversationWorkspace.d.ts +85 -0
  151. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +3 -2
  152. package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +12 -3
  153. package/esm/src/cli/cli-commands/coder/boilerplateCount.d.ts +1 -1
  154. package/esm/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
  155. package/esm/src/cli/cli-commands/common/{harness/$askForHarnessInstallationApproval.d.ts → npm/$askForNpmPackageInstallationApproval.d.ts} +2 -2
  156. package/{umd/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.d.ts → esm/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.d.ts} +2 -3
  157. package/esm/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.d.ts +12 -0
  158. package/esm/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.d.ts +9 -0
  159. package/esm/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +9 -0
  160. package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +14 -0
  161. package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.test.d.ts +1 -0
  162. package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +12 -0
  163. package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.test.d.ts +1 -0
  164. package/esm/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +10 -0
  165. package/esm/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.d.ts +39 -0
  166. package/esm/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.d.ts +26 -0
  167. package/esm/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.d.ts +7 -0
  168. package/esm/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.test.d.ts +1 -0
  169. package/esm/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.d.ts +7 -0
  170. package/esm/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.d.ts +3 -3
  171. package/esm/src/utils/agents/terminalAgentAvatarVisual.d.ts +5 -2
  172. package/esm/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +6 -0
  173. package/esm/src/utils/ascii-art/createAnsiColorCode.d.ts +46 -0
  174. package/esm/src/utils/misc/debounce.d.ts +4 -2
  175. package/esm/src/utils/misc/debounce.test.d.ts +1 -0
  176. package/esm/src/version.d.ts +1 -1
  177. package/package.json +1 -1
  178. package/src/avatars/renderAvatarVisualTerminalText.ts +249 -0
  179. package/src/avatars/types/AvatarVisualDefinition.ts +60 -0
  180. package/src/avatars/visuals/asciiOctopusAvatarVisual.ts +144 -22
  181. package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +4 -4
  182. package/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.ts +1 -4
  183. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +4 -17
  184. package/src/book-3.0/AgentTeamConversationWorkspace.ts +172 -0
  185. package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +56 -55
  186. package/src/book-components/Chat/Chat/Chat.module.css +1 -8
  187. package/src/book-components/Chat/Chat/ChatMessageMap.tsx +8 -4
  188. package/src/book-components/Chat/Chat/ChatProps.tsx +3 -2
  189. package/src/book-components/Chat/MarkdownContent/MarkdownContent.module.css +14 -14
  190. package/src/book-components/Chat/save/react/exports/chat-preview-2025-10-13 (1).jsx +95 -5
  191. package/src/book-components/Chat/utils/renderMarkdown.ts +535 -89
  192. package/src/cli/cli-commands/coder/add.ts +3 -3
  193. package/src/cli/cli-commands/coder/boilerplateCount.ts +1 -1
  194. package/src/cli/cli-commands/coder/generate-boilerplates.ts +27 -27
  195. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +5 -4
  196. package/src/cli/cli-commands/coder/init.ts +3 -3
  197. package/src/cli/cli-commands/coder/run.ts +10 -2
  198. package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +6 -2
  199. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +2 -2
  200. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +4 -4
  201. package/src/cli/cli-commands/common/harness/$resolveInstalledHarnessVersion.ts +2 -2
  202. package/src/cli/cli-commands/common/{harness/$askForHarnessInstallationApproval.ts → npm/$askForNpmPackageInstallationApproval.ts} +3 -3
  203. package/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.ts +49 -0
  204. package/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.ts +25 -0
  205. package/src/cli/cli-commands/common/{harness/isHarnessVersionOutdated.ts → npm/isNpmPackageVersionOutdated.ts} +6 -4
  206. package/src/cli/cli-commands/common/projectInitialization.ts +10 -1
  207. package/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.ts +73 -0
  208. package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +94 -0
  209. package/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.ts +223 -0
  210. package/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.ts +53 -0
  211. package/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.ts +46 -0
  212. package/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.ts +32 -0
  213. package/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.ts +22 -0
  214. package/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.ts +47 -0
  215. package/src/commands/FORMAT/formatCommandParser.ts +2 -2
  216. package/src/llm-providers/openai/utils/buildToolInvocationScript.ts +21 -18
  217. package/src/other/templates/getTemplatesPipelineCollection.ts +866 -686
  218. package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +80 -27
  219. package/src/utils/agents/terminalAgentAvatarVisual.ts +31 -3
  220. package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +9 -109
  221. package/src/utils/ascii-art/createAnsiColorCode.ts +132 -0
  222. package/src/utils/misc/debounce.ts +26 -5
  223. package/src/version.ts +2 -2
  224. package/src/versions.txt +2 -0
  225. package/umd/index.umd.js +1533 -400
  226. package/umd/index.umd.js.map +1 -1
  227. package/umd/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +7 -1
  228. package/umd/scripts/run-agent-messages/messages/buildAgentTeamPromptSection.d.ts +12 -0
  229. package/umd/scripts/run-agent-messages/messages/finalizeAgentTeamConversationWorkspace.d.ts +16 -0
  230. package/umd/scripts/run-agent-messages/messages/loadAgentTeamConversationWorkspace.d.ts +6 -0
  231. package/umd/scripts/run-codex-prompts/common/normalizeLineEndingsInChangedFiles.d.ts +2 -3
  232. package/umd/scripts/run-codex-prompts/git/coderCommitScope.d.ts +31 -0
  233. package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +15 -2
  234. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +9 -3
  235. package/umd/scripts/run-codex-prompts/git/workingTreeChanges.d.ts +40 -0
  236. package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  237. package/umd/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +5 -5
  238. package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiTerminalFrameUpdate.d.ts +27 -0
  239. package/umd/src/avatars/renderAvatarVisualTerminalText.d.ts +80 -0
  240. package/umd/src/avatars/renderAvatarVisualTerminalText.test.d.ts +1 -0
  241. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +54 -0
  242. package/umd/src/book-3.0/AgentTeamConversationWorkspace.d.ts +85 -0
  243. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +3 -2
  244. package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +12 -3
  245. package/umd/src/cli/cli-commands/coder/boilerplateCount.d.ts +1 -1
  246. package/umd/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
  247. package/umd/src/cli/cli-commands/common/{harness/$askForHarnessInstallationApproval.d.ts → npm/$askForNpmPackageInstallationApproval.d.ts} +2 -2
  248. package/{esm/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.d.ts → umd/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.d.ts} +2 -3
  249. package/umd/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.test.d.ts +1 -0
  250. package/umd/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.d.ts +12 -0
  251. package/umd/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.test.d.ts +1 -0
  252. package/umd/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.d.ts +9 -0
  253. package/umd/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.test.d.ts +1 -0
  254. package/umd/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +9 -0
  255. package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +14 -0
  256. package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.test.d.ts +1 -0
  257. package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +12 -0
  258. package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.test.d.ts +1 -0
  259. package/umd/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +10 -0
  260. package/umd/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.d.ts +39 -0
  261. package/umd/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.d.ts +26 -0
  262. package/umd/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.d.ts +7 -0
  263. package/umd/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.test.d.ts +1 -0
  264. package/umd/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.d.ts +7 -0
  265. package/umd/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.d.ts +3 -3
  266. package/umd/src/utils/agents/terminalAgentAvatarVisual.d.ts +5 -2
  267. package/umd/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +6 -0
  268. package/umd/src/utils/ascii-art/createAnsiColorCode.d.ts +46 -0
  269. package/umd/src/utils/misc/debounce.d.ts +4 -2
  270. package/umd/src/utils/misc/debounce.test.d.ts +1 -0
  271. package/umd/src/version.d.ts +1 -1
  272. package/apps/agents-server/scripts/ignore-kill-eperm.js +0 -31
  273. package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/actions/route.ts +0 -103
  274. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +0 -43
  275. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsEditDialog.tsx +0 -98
  276. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +0 -75
  277. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsHeader.tsx +0 -53
  278. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsSummaryMetrics.tsx +0 -70
  279. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +0 -58
  280. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +0 -242
  281. package/apps/agents-server/src/app/agents/[agentName]/timeouts/loading.tsx +0 -15
  282. package/apps/agents-server/src/app/agents/[agentName]/timeouts/page.tsx +0 -28
  283. package/apps/agents-server/src/app/agents/[agentName]/timeouts/useAgentTimeoutsClientState.ts +0 -769
  284. package/apps/agents-server/src/components/AgentProjectDnsInstructions/AgentProjectDnsInstructions.tsx +0 -182
  285. package/apps/agents-server/src/utils/userChatClient/runAgentUserTimeoutBulkAction.ts +0 -24
  286. package/apps/agents-server/src/utils/userChatClient/updateAgentUserTimeout.ts +0 -25
  287. package/esm/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.d.ts +0 -11
  288. package/esm/src/cli/cli-commands/common/harness/isHarnessVersionOutdated.d.ts +0 -9
  289. package/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.ts +0 -34
  290. package/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.ts +0 -28
  291. package/umd/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.d.ts +0 -11
  292. package/umd/src/cli/cli-commands/common/harness/isHarnessVersionOutdated.d.ts +0 -9
  293. /package/esm/src/{cli/cli-commands/common/harness/extractHarnessVersionFromOutput.test.d.ts → avatars/renderAvatarVisualTerminalText.test.d.ts} +0 -0
  294. /package/esm/src/cli/cli-commands/common/{harness/isHarnessVersionOutdated.test.d.ts → npm/$resolveLatestNpmPackageVersion.test.d.ts} +0 -0
  295. /package/{umd/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.test.d.ts → esm/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.test.d.ts} +0 -0
  296. /package/{umd/src/cli/cli-commands/common/harness/isHarnessVersionOutdated.test.d.ts → esm/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.test.d.ts} +0 -0
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-5';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-8';
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
@@ -2593,7 +2593,10 @@
2593
2593
  if (currentGitignoreContent !== undefined && missingRules.length === 0) {
2594
2594
  return 'unchanged';
2595
2595
  }
2596
- const nextGitignoreContent = appendBlock(currentGitignoreContent || '', [blockHeader, ...missingRules].join('\n'));
2596
+ const nextGitignoreContent = appendBlock(currentGitignoreContent || '', _spaceTrim.spaceTrim((block) => `
2597
+ ${block(blockHeader)}
2598
+ ${block(missingRules.join('\n'))}
2599
+ `));
2597
2600
  await promises.writeFile(gitignorePath, nextGitignoreContent, 'utf-8');
2598
2601
  return currentGitignoreContent === undefined ? 'created' : 'updated';
2599
2602
  }
@@ -7463,6 +7466,15 @@
7463
7466
  * @private helper of `asciiOctopusAvatarVisual`
7464
7467
  */
7465
7468
  const ATMOSPHERE_GLYPHS = ['.', ':', "'", '`'];
7469
+ /**
7470
+ * Coordinate space the octopus layout is generated in when it is painted into a terminal.
7471
+ *
7472
+ * The terminal renderer has its own character grid, so the geometry only needs one stable
7473
+ * square coordinate space which the grid is then sampled from.
7474
+ *
7475
+ * @private helper of `asciiOctopusAvatarVisual`
7476
+ */
7477
+ const TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE = DEFAULT_AVATAR_SIZE;
7466
7478
  /**
7467
7479
  * AsciiOctopus avatar visual.
7468
7480
  *
@@ -7479,41 +7491,115 @@
7479
7491
  const staticRandom = createRandom('ascii-octopus-static');
7480
7492
  const gridMetrics = createAsciiGridMetrics(size, gridRandom);
7481
7493
  const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction);
7494
+ const glyphGrid = createAsciiOctopusGlyphGrid({
7495
+ sampleGrid: gridMetrics,
7496
+ layout,
7497
+ palette,
7498
+ cellRandom: createRandom('ascii-octopus-cells'),
7499
+ timeMs,
7500
+ });
7482
7501
  drawAvatarFrame(context, size, palette);
7483
7502
  drawAsciiBackdrop(context, size, palette, layout, timeMs);
7484
7503
  context.save();
7485
7504
  context.font = `600 ${gridMetrics.fontSize}px monospace`;
7486
7505
  context.textAlign = 'center';
7487
7506
  context.textBaseline = 'middle';
7488
- // The ASCII renderer samples the morphing octopus field on a low-resolution grid so the shape stays organic
7489
- // while the glyph layout remains deterministic for the same avatar input.
7490
- const cellRandom = createRandom('ascii-octopus-cells');
7491
7507
  for (let rowIndex = 0; rowIndex < gridMetrics.rowCount; rowIndex++) {
7492
7508
  for (let columnIndex = 0; columnIndex < gridMetrics.columnCount; columnIndex++) {
7493
- const point = {
7494
- x: gridMetrics.offsetX + columnIndex * gridMetrics.cellWidth,
7495
- y: gridMetrics.offsetY + rowIndex * gridMetrics.cellHeight,
7496
- };
7497
- const noise = cellRandom();
7498
- const glyphDescriptor = resolveAsciiGlyph({
7499
- point,
7500
- layout,
7501
- palette,
7502
- cellWidth: gridMetrics.cellWidth,
7503
- cellHeight: gridMetrics.cellHeight,
7504
- noise,
7505
- timeMs,
7506
- });
7509
+ const glyphDescriptor = glyphGrid[rowIndex][columnIndex];
7507
7510
  if (!glyphDescriptor) {
7508
7511
  continue;
7509
7512
  }
7513
+ const point = resolveAsciiSampleGridPoint(gridMetrics, columnIndex, rowIndex);
7510
7514
  context.fillStyle = glyphDescriptor.color;
7511
7515
  context.fillText(glyphDescriptor.character, point.x, point.y);
7512
7516
  }
7513
7517
  }
7514
7518
  context.restore();
7515
7519
  },
7520
+ renderTerminalText({ columns, rows, palette, createRandom, timeMs, interaction }) {
7521
+ const staticRandom = createRandom('ascii-octopus-static');
7522
+ const layout = createAsciiOctopusLayout(TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE, timeMs, createRandom, staticRandom, interaction);
7523
+ return createAsciiOctopusGlyphGrid({
7524
+ sampleGrid: createAsciiTerminalSampleGrid(columns, rows),
7525
+ layout,
7526
+ palette,
7527
+ cellRandom: createRandom('ascii-octopus-cells'),
7528
+ timeMs,
7529
+ });
7530
+ },
7516
7531
  };
7532
+ /**
7533
+ * Resolves the geometry point sampled for one cell of the grid.
7534
+ *
7535
+ * @param sampleGrid Character-cell sampling grid.
7536
+ * @param columnIndex Zero-based cell column.
7537
+ * @param rowIndex Zero-based cell row.
7538
+ * @returns Sampled point in the octopus coordinate space.
7539
+ *
7540
+ * @private helper of `asciiOctopusAvatarVisual`
7541
+ */
7542
+ function resolveAsciiSampleGridPoint(sampleGrid, columnIndex, rowIndex) {
7543
+ return {
7544
+ x: sampleGrid.offsetX + columnIndex * sampleGrid.cellWidth,
7545
+ y: sampleGrid.offsetY + rowIndex * sampleGrid.cellHeight,
7546
+ };
7547
+ }
7548
+ /**
7549
+ * Builds the sampling grid used when the octopus is painted straight into terminal character cells.
7550
+ *
7551
+ * @param columns Available terminal columns.
7552
+ * @param rows Available terminal rows.
7553
+ * @returns Character-cell sampling grid covering the whole octopus coordinate space.
7554
+ *
7555
+ * @private helper of `asciiOctopusAvatarVisual`
7556
+ */
7557
+ function createAsciiTerminalSampleGrid(columns, rows) {
7558
+ const cellWidth = TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE / columns;
7559
+ const cellHeight = TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE / rows;
7560
+ return {
7561
+ cellWidth,
7562
+ cellHeight,
7563
+ columnCount: columns,
7564
+ rowCount: rows,
7565
+ offsetX: cellWidth / 2,
7566
+ offsetY: cellHeight / 2,
7567
+ };
7568
+ }
7569
+ /**
7570
+ * Samples the morphing octopus field into one deterministic grid of ASCII glyphs.
7571
+ *
7572
+ * The field is sampled on a low-resolution grid so the shape stays organic while the glyph
7573
+ * layout remains deterministic for the same avatar input. Both the canvas visual and the
7574
+ * terminal visual share this single sampling pass so they always describe the same octopus.
7575
+ *
7576
+ * @param options Sampling grid, prepared octopus layout, palette, per-cell noise, and animation time.
7577
+ * @returns Resolved glyph of every sampled cell, where `null` means an empty cell.
7578
+ *
7579
+ * @private helper of `asciiOctopusAvatarVisual`
7580
+ */
7581
+ function createAsciiOctopusGlyphGrid(options) {
7582
+ const { sampleGrid, layout, palette, cellRandom, timeMs } = options;
7583
+ const glyphGrid = [];
7584
+ for (let rowIndex = 0; rowIndex < sampleGrid.rowCount; rowIndex++) {
7585
+ const glyphRow = [];
7586
+ for (let columnIndex = 0; columnIndex < sampleGrid.columnCount; columnIndex++) {
7587
+ const point = resolveAsciiSampleGridPoint(sampleGrid, columnIndex, rowIndex);
7588
+ const noise = cellRandom();
7589
+ glyphRow.push(resolveAsciiGlyph({
7590
+ point,
7591
+ layout,
7592
+ palette,
7593
+ cellWidth: sampleGrid.cellWidth,
7594
+ cellHeight: sampleGrid.cellHeight,
7595
+ noise,
7596
+ timeMs,
7597
+ }));
7598
+ }
7599
+ glyphGrid.push(glyphRow);
7600
+ }
7601
+ return glyphGrid;
7602
+ }
7517
7603
  /**
7518
7604
  * Draws the dark terminal-like glow behind the ASCII octopus.
7519
7605
  *
@@ -21760,7 +21846,7 @@
21760
21846
  return agentBookStats.isFile();
21761
21847
  }
21762
21848
  catch (error) {
21763
- if (isFileNotFoundError$4(error)) {
21849
+ if (isFileNotFoundError$6(error)) {
21764
21850
  return false;
21765
21851
  }
21766
21852
  throw error;
@@ -21769,7 +21855,7 @@
21769
21855
  /**
21770
21856
  * Returns true when one filesystem error indicates a missing path.
21771
21857
  */
21772
- function isFileNotFoundError$4(error) {
21858
+ function isFileNotFoundError$6(error) {
21773
21859
  return Boolean(error &&
21774
21860
  typeof error === 'object' &&
21775
21861
  'code' in error &&
@@ -21799,7 +21885,7 @@
21799
21885
  return await promises.readdir(queuedMessagesDirectoryPath, { withFileTypes: true });
21800
21886
  }
21801
21887
  catch (error) {
21802
- if (isFileNotFoundError$3(error)) {
21888
+ if (isFileNotFoundError$5(error)) {
21803
21889
  return [];
21804
21890
  }
21805
21891
  throw error;
@@ -21820,7 +21906,7 @@
21820
21906
  /**
21821
21907
  * Returns true when an error is a missing-path filesystem error.
21822
21908
  */
21823
- function isFileNotFoundError$3(error) {
21909
+ function isFileNotFoundError$5(error) {
21824
21910
  return Boolean(error &&
21825
21911
  typeof error === 'object' &&
21826
21912
  'code' in error &&
@@ -22298,7 +22384,7 @@
22298
22384
  lastIndexLockState = await readGitIndexLockState(cwd, options.env);
22299
22385
  if ((lastIndexLockState === null || lastIndexLockState === void 0 ? void 0 : lastIndexLockState.isStale) && !isStaleIndexLockRemoved) {
22300
22386
  await promises.unlink(lastIndexLockState.path).catch((unlinkError) => {
22301
- if (isFileNotFoundError$2(unlinkError)) {
22387
+ if (isFileNotFoundError$4(unlinkError)) {
22302
22388
  return;
22303
22389
  }
22304
22390
  throw unlinkError;
@@ -22359,7 +22445,7 @@
22359
22445
  };
22360
22446
  }
22361
22447
  catch (error) {
22362
- if (isFileNotFoundError$2(error)) {
22448
+ if (isFileNotFoundError$4(error)) {
22363
22449
  return undefined;
22364
22450
  }
22365
22451
  throw error;
@@ -22416,7 +22502,7 @@
22416
22502
  /**
22417
22503
  * Detects missing-file errors while inspecting the lock file.
22418
22504
  */
22419
- function isFileNotFoundError$2(error) {
22505
+ function isFileNotFoundError$4(error) {
22420
22506
  return Boolean(error &&
22421
22507
  typeof error === 'object' &&
22422
22508
  'code' in error &&
@@ -27312,66 +27398,125 @@
27312
27398
  });
27313
27399
  }
27314
27400
 
27401
+ // Note: [💞] Ignore a discrepancy between file name and entity name
27315
27402
  /**
27316
- * Default alpha channel value below which a half-cell is rendered as terminal background.
27403
+ * ANSI escape sequence that resets all colors and attributes.
27317
27404
  *
27318
27405
  * @private within the repository
27319
27406
  */
27320
- const DEFAULT_ALPHA_THRESHOLD = 32;
27407
+ const ANSI_RESET = '\u001b[0m';
27321
27408
  /**
27322
- * Number of channels per pixel in an RGBA buffer.
27409
+ * Maximum spread between RGB channels for a color to be treated as (nearly) achromatic gray.
27323
27410
  *
27324
27411
  * @private within the repository
27325
27412
  */
27326
- const RGBA_CHANNEL_COUNT = 4;
27413
+ const ANSI_256_ACHROMATIC_CHANNEL_SPREAD = 12;
27327
27414
  /**
27328
- * Upper half block character - foreground paints the top pixel, background paints the bottom pixel.
27415
+ * Gray level above which an achromatic color maps to the pure white color-cube entry.
27329
27416
  *
27330
27417
  * @private within the repository
27331
27418
  */
27332
- const UPPER_HALF_BLOCK = '▀'; // <- ▀
27419
+ const ANSI_256_NEAR_WHITE_GRAY_LEVEL = 246;
27333
27420
  /**
27334
- * Lower half block character - foreground paints the bottom pixel while the top pixel stays transparent.
27421
+ * Index of pure white inside the 6×6×6 ANSI color cube.
27335
27422
  *
27336
27423
  * @private within the repository
27337
27424
  */
27338
- const LOWER_HALF_BLOCK = '▄'; // <- ▄
27425
+ const ANSI_256_WHITE_INDEX = 231;
27339
27426
  /**
27340
- * ANSI escape sequence that resets all colors and attributes.
27427
+ * Brightness of the lightest entry of the ANSI 256 grayscale ramp.
27341
27428
  *
27342
27429
  * @private within the repository
27343
27430
  */
27344
- const ANSI_RESET = '\u001b[0m';
27431
+ const ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL = 238;
27345
27432
  /**
27346
- * Maximum spread between RGB channels for a color to be treated as (nearly) achromatic gray.
27433
+ * Number of grayscale ramp steps above its first entry (ANSI indexes 232-255).
27347
27434
  *
27348
27435
  * @private within the repository
27349
27436
  */
27350
- const ANSI_256_ACHROMATIC_CHANNEL_SPREAD = 12;
27437
+ const ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN = 23;
27351
27438
  /**
27352
- * Gray level above which an achromatic color maps to the pure white color-cube entry.
27439
+ * Creates the ANSI escape code that sets the foreground color of following characters.
27440
+ *
27441
+ * @param color Color painted onto the characters.
27442
+ * @param colorDepth Color depth supported by the target terminal.
27443
+ * @returns ANSI escape sequence.
27353
27444
  *
27354
27445
  * @private within the repository
27355
27446
  */
27356
- const ANSI_256_NEAR_WHITE_GRAY_LEVEL = 246;
27447
+ function createAnsiForegroundColorCode(color, colorDepth) {
27448
+ if (colorDepth === 'TRUE_COLOR') {
27449
+ return `\u001b[38;2;${color.red};${color.green};${color.blue}m`;
27450
+ }
27451
+ return `\u001b[38;5;${mapColorToAnsi256(color)}m`;
27452
+ }
27357
27453
  /**
27358
- * Index of pure white inside the 6×6×6 ANSI color cube.
27454
+ * Creates the ANSI escape code that sets the background color of following characters.
27455
+ *
27456
+ * @param color Color painted behind the characters.
27457
+ * @param colorDepth Color depth supported by the target terminal.
27458
+ * @returns ANSI escape sequence.
27359
27459
  *
27360
27460
  * @private within the repository
27361
27461
  */
27362
- const ANSI_256_WHITE_INDEX = 231;
27462
+ function createAnsiBackgroundColorCode(color, colorDepth) {
27463
+ if (colorDepth === 'TRUE_COLOR') {
27464
+ return `\u001b[48;2;${color.red};${color.green};${color.blue}m`;
27465
+ }
27466
+ return `\u001b[48;5;${mapColorToAnsi256(color)}m`;
27467
+ }
27363
27468
  /**
27364
- * Brightness of the lightest entry of the ANSI 256 grayscale ramp.
27469
+ * Maps a 24-bit color onto the closest entry of the 256-color ANSI palette.
27470
+ *
27471
+ * Uses the 6×6×6 color cube (entries 16-231) and the grayscale ramp (entries 232-255).
27472
+ *
27473
+ * @private helper of `createAnsiForegroundColorCode` and `createAnsiBackgroundColorCode`
27474
+ */
27475
+ function mapColorToAnsi256(color) {
27476
+ const { red, green, blue } = color;
27477
+ // Note: Prefer the finer grayscale ramp when the color is (nearly) achromatic
27478
+ const maxChannel = Math.max(red, green, blue);
27479
+ const minChannel = Math.min(red, green, blue);
27480
+ if (maxChannel - minChannel < ANSI_256_ACHROMATIC_CHANNEL_SPREAD) {
27481
+ const gray = Math.round((red + green + blue) / 3);
27482
+ if (gray < 4) {
27483
+ return 16; // <- Note: Pure black lives in the color cube
27484
+ }
27485
+ if (gray > ANSI_256_NEAR_WHITE_GRAY_LEVEL) {
27486
+ return ANSI_256_WHITE_INDEX; // <- Note: Pure white lives in the color cube
27487
+ }
27488
+ return 232 + Math.round(((gray - 8) / ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL) * ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN);
27489
+ }
27490
+ const redIndex = Math.round((red / 255) * 5);
27491
+ const greenIndex = Math.round((green / 255) * 5);
27492
+ const blueIndex = Math.round((blue / 255) * 5);
27493
+ return 16 + 36 * redIndex + 6 * greenIndex + blueIndex;
27494
+ }
27495
+
27496
+ /**
27497
+ * Default alpha channel value below which a cell is rendered as terminal background.
27365
27498
  *
27366
27499
  * @private within the repository
27367
27500
  */
27368
- const ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL = 238;
27501
+ const DEFAULT_ALPHA_THRESHOLD = 32;
27369
27502
  /**
27370
- * Number of grayscale ramp steps above its first entry (ANSI indexes 232-255).
27503
+ * Number of channels per pixel in an RGBA buffer.
27371
27504
  *
27372
27505
  * @private within the repository
27373
27506
  */
27374
- const ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN = 23;
27507
+ const RGBA_CHANNEL_COUNT = 4;
27508
+ /**
27509
+ * Upper half block character - foreground paints the top pixel, background paints the bottom pixel.
27510
+ *
27511
+ * @private within the repository
27512
+ */
27513
+ const UPPER_HALF_BLOCK = '▀'; // <- ▀
27514
+ /**
27515
+ * Lower half block character - foreground paints the bottom pixel while the top pixel stays transparent.
27516
+ *
27517
+ * @private within the repository
27518
+ */
27519
+ const LOWER_HALF_BLOCK = '▄'; // <- ▄
27375
27520
  /**
27376
27521
  * Converts raw RGBA image pixels into colored ASCII art for ANSI terminals.
27377
27522
  *
@@ -27420,17 +27565,17 @@
27420
27565
  let nextBackgroundCode;
27421
27566
  if (topHalfColor.isOpaque && bottomHalfColor.isOpaque) {
27422
27567
  character = UPPER_HALF_BLOCK;
27423
- nextForegroundCode = createForegroundColorCode(topHalfColor, colorDepth);
27424
- nextBackgroundCode = createBackgroundColorCode(bottomHalfColor, colorDepth);
27568
+ nextForegroundCode = createAnsiForegroundColorCode(topHalfColor, colorDepth);
27569
+ nextBackgroundCode = createAnsiBackgroundColorCode(bottomHalfColor, colorDepth);
27425
27570
  }
27426
27571
  else if (topHalfColor.isOpaque) {
27427
27572
  character = UPPER_HALF_BLOCK;
27428
- nextForegroundCode = createForegroundColorCode(topHalfColor, colorDepth);
27573
+ nextForegroundCode = createAnsiForegroundColorCode(topHalfColor, colorDepth);
27429
27574
  nextBackgroundCode = undefined;
27430
27575
  }
27431
27576
  else if (bottomHalfColor.isOpaque) {
27432
27577
  character = LOWER_HALF_BLOCK;
27433
- nextForegroundCode = createForegroundColorCode(bottomHalfColor, colorDepth);
27578
+ nextForegroundCode = createAnsiForegroundColorCode(bottomHalfColor, colorDepth);
27434
27579
  nextBackgroundCode = undefined;
27435
27580
  }
27436
27581
  else {
@@ -27505,55 +27650,6 @@
27505
27650
  isOpaque: true,
27506
27651
  };
27507
27652
  }
27508
- /**
27509
- * Creates the ANSI escape code that sets the foreground color of following characters.
27510
- *
27511
- * @private helper of `convertImageDataToAsciiArt`
27512
- */
27513
- function createForegroundColorCode(color, colorDepth) {
27514
- if (colorDepth === 'TRUE_COLOR') {
27515
- return `\u001b[38;2;${color.red};${color.green};${color.blue}m`;
27516
- }
27517
- return `\u001b[38;5;${mapColorToAnsi256(color)}m`;
27518
- }
27519
- /**
27520
- * Creates the ANSI escape code that sets the background color of following characters.
27521
- *
27522
- * @private helper of `convertImageDataToAsciiArt`
27523
- */
27524
- function createBackgroundColorCode(color, colorDepth) {
27525
- if (colorDepth === 'TRUE_COLOR') {
27526
- return `\u001b[48;2;${color.red};${color.green};${color.blue}m`;
27527
- }
27528
- return `\u001b[48;5;${mapColorToAnsi256(color)}m`;
27529
- }
27530
- /**
27531
- * Maps a 24-bit color onto the closest entry of the 256-color ANSI palette.
27532
- *
27533
- * Uses the 6×6×6 color cube (entries 16-231) and the grayscale ramp (entries 232-255).
27534
- *
27535
- * @private helper of `convertImageDataToAsciiArt`
27536
- */
27537
- function mapColorToAnsi256(color) {
27538
- const { red, green, blue } = color;
27539
- // Note: Prefer the finer grayscale ramp when the color is (nearly) achromatic
27540
- const maxChannel = Math.max(red, green, blue);
27541
- const minChannel = Math.min(red, green, blue);
27542
- if (maxChannel - minChannel < ANSI_256_ACHROMATIC_CHANNEL_SPREAD) {
27543
- const gray = Math.round((red + green + blue) / 3);
27544
- if (gray < 4) {
27545
- return 16; // <- Note: Pure black lives in the color cube
27546
- }
27547
- if (gray > ANSI_256_NEAR_WHITE_GRAY_LEVEL) {
27548
- return ANSI_256_WHITE_INDEX; // <- Note: Pure white lives in the color cube
27549
- }
27550
- return 232 + Math.round(((gray - 8) / ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL) * ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN);
27551
- }
27552
- const redIndex = Math.round((red / 255) * 5);
27553
- const greenIndex = Math.round((green / 255) * 5);
27554
- const blueIndex = Math.round((blue / 255) * 5);
27555
- return 16 + 36 * redIndex + 6 * greenIndex + blueIndex;
27556
- }
27557
27653
 
27558
27654
  /**
27559
27655
  * Default output width of the ASCII avatar in terminal character cells.
@@ -27683,6 +27779,143 @@
27683
27779
  `));
27684
27780
  }
27685
27781
 
27782
+ // Note: [💞] Ignore a discrepancy between file name and entity name
27783
+ /**
27784
+ * Height of one terminal character cell relative to its width.
27785
+ *
27786
+ * Terminal fonts are roughly twice as tall as they are wide, so a square avatar needs
27787
+ * twice as many columns as rows to keep its proportions.
27788
+ *
27789
+ * @private within the repository
27790
+ */
27791
+ const TERMINAL_CHARACTER_CELL_ASPECT_RATIO = 2;
27792
+ /**
27793
+ * Renders one frame of a character-based avatar visual straight into terminal character cells.
27794
+ *
27795
+ * Visuals which are themselves made of characters, for example `AsciiOctopus`, lose their identity
27796
+ * when they are rasterized onto a canvas and converted back into half-block ASCII art - every glyph
27797
+ * is averaged away into a colored blob. Such visuals expose `renderTerminalText`, which paints the
27798
+ * terminal grid directly and is used instead of the raster pipeline.
27799
+ *
27800
+ * The square avatar is centered inside the requested grid the same way `renderAvatarVisualAsciiArt`
27801
+ * centers the avatar canvas inside a wider terminal frame.
27802
+ *
27803
+ * @param options Avatar identity, visual selection, and output grid size.
27804
+ * @returns Grid of `rows` rows of `columns` cells, or `null` when the visual has no terminal renderer.
27805
+ *
27806
+ * @private within the repository
27807
+ */
27808
+ function renderAvatarVisualTerminalTextGrid(options) {
27809
+ const resolvedRenderDefinition = options.resolvedAvatarRenderDefinition ||
27810
+ resolveAvatarRenderDefinition({
27811
+ avatarDefinition: options.avatarDefinition,
27812
+ visualId: options.visualId,
27813
+ surface: options.surface,
27814
+ });
27815
+ const { renderTerminalText } = resolvedRenderDefinition.avatarVisual;
27816
+ if (renderTerminalText === undefined) {
27817
+ return null;
27818
+ }
27819
+ const avatarColumnCount = Math.min(options.columns, options.rows * TERMINAL_CHARACTER_CELL_ASPECT_RATIO);
27820
+ const avatarRowCount = Math.min(options.rows, Math.round(options.columns / TERMINAL_CHARACTER_CELL_ASPECT_RATIO));
27821
+ if (avatarColumnCount <= 0 || avatarRowCount <= 0) {
27822
+ return null;
27823
+ }
27824
+ const avatarGrid = renderTerminalText({
27825
+ columns: avatarColumnCount,
27826
+ rows: avatarRowCount,
27827
+ timeMs: options.timeMs,
27828
+ avatarDefinition: resolvedRenderDefinition.avatarDefinition,
27829
+ palette: resolvedRenderDefinition.palette,
27830
+ createRandom: resolvedRenderDefinition.createRandom,
27831
+ interaction: createIdleAvatarInteractionState(),
27832
+ });
27833
+ return centerAvatarVisualTerminalTextGrid(avatarGrid, options.columns, options.rows);
27834
+ }
27835
+ /**
27836
+ * Renders one frame of a character-based avatar visual into ANSI-colored terminal lines.
27837
+ *
27838
+ * @param options Avatar identity, visual selection, output grid size, and ANSI color depth.
27839
+ * @returns One ANSI-colored string per output row, or `null` when the visual has no terminal renderer.
27840
+ *
27841
+ * @private within the repository
27842
+ */
27843
+ function renderAvatarVisualTerminalTextLines(options) {
27844
+ const terminalTextGrid = renderAvatarVisualTerminalTextGrid(options);
27845
+ if (terminalTextGrid === null) {
27846
+ return null;
27847
+ }
27848
+ return terminalTextGrid.map((terminalTextRow) => buildAvatarVisualTerminalTextLine(terminalTextRow, options.colorDepth || 'TRUE_COLOR'));
27849
+ }
27850
+ /**
27851
+ * Converts one avatar cell color into the opaque terminal color it is painted with.
27852
+ *
27853
+ * Terminal cells cannot be blended, so the cell alpha is composited onto the dark terminal
27854
+ * background and fully transparent cells keep the terminal background instead.
27855
+ *
27856
+ * @param color Cell color as a CSS color string, optionally with an alpha channel.
27857
+ * @returns Opaque terminal color or `null` when the cell should stay empty.
27858
+ *
27859
+ * @private helper of `renderAvatarVisualTerminalTextLines`
27860
+ */
27861
+ function resolveAvatarVisualTerminalTextColor(color) {
27862
+ const parsedColor = Color.fromSafe(color);
27863
+ if (parsedColor.alpha < DEFAULT_ALPHA_THRESHOLD) {
27864
+ return null;
27865
+ }
27866
+ const opacity = parsedColor.alpha / 255;
27867
+ return {
27868
+ red: Math.round(parsedColor.red * opacity),
27869
+ green: Math.round(parsedColor.green * opacity),
27870
+ blue: Math.round(parsedColor.blue * opacity),
27871
+ };
27872
+ }
27873
+ /**
27874
+ * Places the square avatar grid into the center of the requested terminal grid.
27875
+ *
27876
+ * @private helper of `renderAvatarVisualTerminalTextGrid`
27877
+ */
27878
+ function centerAvatarVisualTerminalTextGrid(avatarGrid, columns, rows) {
27879
+ var _a;
27880
+ const avatarRowCount = avatarGrid.length;
27881
+ const avatarColumnCount = ((_a = avatarGrid[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
27882
+ const leftCellCount = Math.floor((columns - avatarColumnCount) / 2);
27883
+ const topRowCount = Math.floor((rows - avatarRowCount) / 2);
27884
+ return Array.from({ length: rows }, (_, rowIndex) => {
27885
+ const avatarRow = avatarGrid[rowIndex - topRowCount];
27886
+ return Array.from({ length: columns }, (__, columnIndex) => (avatarRow === null || avatarRow === void 0 ? void 0 : avatarRow[columnIndex - leftCellCount]) || null);
27887
+ });
27888
+ }
27889
+ /**
27890
+ * Builds one ANSI-colored terminal line from resolved character cells.
27891
+ *
27892
+ * @private helper of `renderAvatarVisualTerminalTextLines`
27893
+ */
27894
+ function buildAvatarVisualTerminalTextLine(terminalTextRow, colorDepth) {
27895
+ let line = '';
27896
+ let currentForegroundCode = undefined;
27897
+ for (const terminalTextCell of terminalTextRow) {
27898
+ const cellColor = terminalTextCell === null ? null : resolveAvatarVisualTerminalTextColor(terminalTextCell.color);
27899
+ const nextForegroundCode = cellColor === null ? undefined : createAnsiForegroundColorCode(cellColor, colorDepth);
27900
+ if (nextForegroundCode !== currentForegroundCode) {
27901
+ // Note: A reset is required whenever a previously set color must be cleared,
27902
+ // otherwise a stale color would bleed into the following empty cells.
27903
+ if (nextForegroundCode === undefined) {
27904
+ line += ANSI_RESET;
27905
+ }
27906
+ else {
27907
+ line += nextForegroundCode;
27908
+ }
27909
+ currentForegroundCode = nextForegroundCode;
27910
+ }
27911
+ line += cellColor === null || terminalTextCell === null ? ' ' : terminalTextCell.character;
27912
+ }
27913
+ if (currentForegroundCode !== undefined) {
27914
+ line += ANSI_RESET;
27915
+ }
27916
+ return line;
27917
+ }
27918
+
27686
27919
  /**
27687
27920
  * Default built-in avatar visual used when an agent does not define `META IMAGE`, `META AVATAR`, or `META VISUAL`.
27688
27921
  *
@@ -27801,9 +28034,30 @@
27801
28034
  /**
27802
28035
  * Renders one terminal avatar frame through the shared avatar-to-ASCII pipeline.
27803
28036
  *
28037
+ * Visuals which are made of characters themselves, for example `AsciiOctopus`, paint the terminal
28038
+ * character grid directly, because rasterizing them into half-block ASCII art averages every glyph
28039
+ * away and makes them indistinguishable from the other blob-shaped visuals.
28040
+ *
27804
28041
  * @private shared helper for terminal avatar rendering
27805
28042
  */
27806
28043
  function renderTerminalAgentAvatarVisualFrame(options) {
28044
+ const terminalTextLines = renderAvatarVisualTerminalTextLines({
28045
+ avatarDefinition: options.avatarDefinition,
28046
+ visualId: options.avatarVisualId,
28047
+ surface: 'transparent',
28048
+ columns: TERMINAL_AGENT_AVATAR_VISUAL_COLUMNS,
28049
+ rows: TERMINAL_AGENT_AVATAR_VISUAL_ROWS,
28050
+ colorDepth: options.colorDepth,
28051
+ timeMs: options.animationTimeMs,
28052
+ resolvedAvatarRenderDefinition: options.resolvedAvatarRenderDefinition,
28053
+ });
28054
+ if (terminalTextLines !== null) {
28055
+ return terminalTextLines;
28056
+ }
28057
+ if (options.createCanvas === undefined) {
28058
+ // Note: A pixel-based visual cannot be rasterized without a canvas, so the caller keeps its default banner
28059
+ return [];
28060
+ }
27807
28061
  return renderAvatarVisualAsciiArt({
27808
28062
  avatarDefinition: options.avatarDefinition,
27809
28063
  visualId: options.avatarVisualId,
@@ -28056,6 +28310,134 @@
28056
28310
  return `${count} prompt run${count === 1 ? '' : 's'}`;
28057
28311
  }
28058
28312
 
28313
+ /**
28314
+ * ANSI sequence that clears the current terminal row.
28315
+ *
28316
+ * @private internal constant of coder run UI
28317
+ */
28318
+ const CLEAR_CURRENT_LINE = '\x1b[2K';
28319
+ /**
28320
+ * ANSI control character that moves the cursor to the start of its current row.
28321
+ *
28322
+ * @private internal constant of coder run UI
28323
+ */
28324
+ const MOVE_CURSOR_TO_LINE_START = '\r';
28325
+ /**
28326
+ * Builds one atomic ANSI update that changes the displayed coder-run terminal frame.
28327
+ *
28328
+ * A change in frame height requires a full rewrite to reserve or clear rows correctly. Frames with
28329
+ * the same height only rewrite contiguous groups of changed rows, returning to the frame bottom
28330
+ * between groups so each group can be assembled into the same output payload safely.
28331
+ *
28332
+ * @returns One terminal output payload, or `undefined` when the frame is already up to date.
28333
+ *
28334
+ * @private internal utility of coder run UI
28335
+ */
28336
+ function buildCoderRunUiTerminalFrameUpdate(options) {
28337
+ const { previousFrameLines, nextFrameLines } = options;
28338
+ if (previousFrameLines.length === 0 && nextFrameLines.length === 0) {
28339
+ return undefined;
28340
+ }
28341
+ if (previousFrameLines.length === 0 || previousFrameLines.length !== nextFrameLines.length) {
28342
+ return buildFullCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines);
28343
+ }
28344
+ return buildChangedCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines);
28345
+ }
28346
+ /**
28347
+ * Builds an ANSI update which completely rewrites the reserved terminal frame area.
28348
+ *
28349
+ * @private helper of `buildCoderRunUiTerminalFrameUpdate`
28350
+ */
28351
+ function buildFullCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines) {
28352
+ var _a;
28353
+ const previousFrameLineCount = previousFrameLines.length;
28354
+ const lineCountToRewrite = Math.max(previousFrameLineCount, nextFrameLines.length);
28355
+ const outputParts = [];
28356
+ if (previousFrameLineCount > 1) {
28357
+ outputParts.push(moveCursorUp(previousFrameLineCount - 1));
28358
+ }
28359
+ for (let lineIndex = 0; lineIndex < lineCountToRewrite; lineIndex++) {
28360
+ outputParts.push(CLEAR_CURRENT_LINE, MOVE_CURSOR_TO_LINE_START, (_a = nextFrameLines[lineIndex]) !== null && _a !== void 0 ? _a : '');
28361
+ if (lineIndex < lineCountToRewrite - 1) {
28362
+ outputParts.push('\n');
28363
+ }
28364
+ }
28365
+ const clearedTrailingLineCount = lineCountToRewrite - nextFrameLines.length;
28366
+ if (clearedTrailingLineCount > 0) {
28367
+ outputParts.push(moveCursorUp(clearedTrailingLineCount));
28368
+ }
28369
+ outputParts.push(MOVE_CURSOR_TO_LINE_START);
28370
+ return outputParts.join('');
28371
+ }
28372
+ /**
28373
+ * Builds an ANSI update that rewrites only contiguous groups of rows which changed.
28374
+ *
28375
+ * @private helper of `buildCoderRunUiTerminalFrameUpdate`
28376
+ */
28377
+ function buildChangedCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines) {
28378
+ const outputParts = [];
28379
+ let lineIndex = 0;
28380
+ while (lineIndex < nextFrameLines.length) {
28381
+ if (previousFrameLines[lineIndex] === nextFrameLines[lineIndex]) {
28382
+ lineIndex++;
28383
+ continue;
28384
+ }
28385
+ const changedRangeStartLineIndex = lineIndex;
28386
+ while (lineIndex + 1 < nextFrameLines.length &&
28387
+ previousFrameLines[lineIndex + 1] !== nextFrameLines[lineIndex + 1]) {
28388
+ lineIndex++;
28389
+ }
28390
+ appendChangedCoderRunUiTerminalFrameRange({
28391
+ outputParts,
28392
+ frameLineCount: nextFrameLines.length,
28393
+ changedRangeStartLineIndex,
28394
+ changedRangeEndLineIndex: lineIndex,
28395
+ nextFrameLines,
28396
+ });
28397
+ lineIndex++;
28398
+ }
28399
+ return outputParts.length === 0 ? undefined : outputParts.join('');
28400
+ }
28401
+ /**
28402
+ * Appends the cursor movements and row rewrites for one contiguous changed frame range.
28403
+ *
28404
+ * @private helper of `buildChangedCoderRunUiTerminalFrameUpdate`
28405
+ */
28406
+ function appendChangedCoderRunUiTerminalFrameRange(options) {
28407
+ const { outputParts, frameLineCount, changedRangeStartLineIndex, changedRangeEndLineIndex, nextFrameLines } = options;
28408
+ const linesUpFromFrameBottom = frameLineCount - 1 - changedRangeStartLineIndex;
28409
+ if (linesUpFromFrameBottom > 0) {
28410
+ outputParts.push(moveCursorUp(linesUpFromFrameBottom));
28411
+ }
28412
+ for (let lineIndex = changedRangeStartLineIndex; lineIndex <= changedRangeEndLineIndex; lineIndex++) {
28413
+ outputParts.push(CLEAR_CURRENT_LINE, MOVE_CURSOR_TO_LINE_START, nextFrameLines[lineIndex]);
28414
+ if (lineIndex < changedRangeEndLineIndex) {
28415
+ outputParts.push('\n');
28416
+ }
28417
+ }
28418
+ const linesDownToFrameBottom = frameLineCount - 1 - changedRangeEndLineIndex;
28419
+ if (linesDownToFrameBottom > 0) {
28420
+ outputParts.push(moveCursorDown(linesDownToFrameBottom));
28421
+ }
28422
+ outputParts.push(MOVE_CURSOR_TO_LINE_START);
28423
+ }
28424
+ /**
28425
+ * Builds an ANSI sequence which moves the terminal cursor up by a positive number of rows.
28426
+ *
28427
+ * @private helper of `buildCoderRunUiTerminalFrameUpdate`
28428
+ */
28429
+ function moveCursorUp(lineCount) {
28430
+ return `\x1b[${lineCount}A`;
28431
+ }
28432
+ /**
28433
+ * Builds an ANSI sequence which moves the terminal cursor down by a positive number of rows.
28434
+ *
28435
+ * @private helper of `buildCoderRunUiTerminalFrameUpdate`
28436
+ */
28437
+ function moveCursorDown(lineCount) {
28438
+ return `\x1b[${lineCount}B`;
28439
+ }
28440
+
28059
28441
  /**
28060
28442
  * Calendar formats used when displaying the estimated completion time.
28061
28443
  *
@@ -28540,58 +28922,6 @@
28540
28922
  scheduleRender();
28541
28923
  }, autoRefreshInterval);
28542
28924
  }
28543
- /**
28544
- * Moves the cursor relative to the bottom of the current frame and rewrites one line in place.
28545
- */
28546
- function rewriteFrameLine(frameLineCount, lineIndex, line) {
28547
- const linesUpFromBottom = Math.max(0, frameLineCount - 1 - lineIndex);
28548
- if (linesUpFromBottom > 0) {
28549
- process.stdout.write(`\x1b[${linesUpFromBottom}A`);
28550
- }
28551
- readline.clearLine(process.stdout, 0);
28552
- readline.cursorTo(process.stdout, 0);
28553
- process.stdout.write(line);
28554
- readline.cursorTo(process.stdout, 0);
28555
- if (linesUpFromBottom > 0) {
28556
- process.stdout.write(`\x1b[${linesUpFromBottom}B`);
28557
- readline.cursorTo(process.stdout, 0);
28558
- }
28559
- }
28560
- /**
28561
- * Fully rewrites the reserved frame area.
28562
- */
28563
- function renderFullFrame(lines) {
28564
- var _a;
28565
- const previousFrameLineCount = previousFrameLines.length;
28566
- const linesToRewriteCount = Math.max(previousFrameLineCount, lines.length);
28567
- if (previousFrameLineCount > 1) {
28568
- process.stdout.write(`\x1b[${previousFrameLineCount - 1}A`);
28569
- }
28570
- for (let i = 0; i < linesToRewriteCount; i++) {
28571
- readline.clearLine(process.stdout, 0);
28572
- readline.cursorTo(process.stdout, 0);
28573
- process.stdout.write((_a = lines[i]) !== null && _a !== void 0 ? _a : '');
28574
- if (i < linesToRewriteCount - 1) {
28575
- process.stdout.write('\n');
28576
- }
28577
- }
28578
- const clearedTrailingLines = linesToRewriteCount - lines.length;
28579
- if (clearedTrailingLines > 0) {
28580
- process.stdout.write(`\x1b[${clearedTrailingLines}A`);
28581
- }
28582
- readline.cursorTo(process.stdout, 0);
28583
- }
28584
- /**
28585
- * Updates only the frame rows whose visible content changed.
28586
- */
28587
- function renderChangedLines(lines) {
28588
- for (let i = 0; i < lines.length; i++) {
28589
- if (previousFrameLines[i] === lines[i]) {
28590
- continue;
28591
- }
28592
- rewriteFrameLine(lines.length, i, lines[i]);
28593
- }
28594
- }
28595
28925
  /**
28596
28926
  * Builds the current frame snapshot from the latest state.
28597
28927
  */
@@ -28633,11 +28963,12 @@
28633
28963
  isRendering = true;
28634
28964
  try {
28635
28965
  const lines = buildFrameLines();
28636
- if (previousFrameLines.length === 0 || previousFrameLines.length !== lines.length) {
28637
- renderFullFrame(lines);
28638
- }
28639
- else {
28640
- renderChangedLines(lines);
28966
+ const terminalFrameUpdate = buildCoderRunUiTerminalFrameUpdate({
28967
+ previousFrameLines,
28968
+ nextFrameLines: lines,
28969
+ });
28970
+ if (terminalFrameUpdate !== undefined) {
28971
+ process.stdout.write(terminalFrameUpdate);
28641
28972
  }
28642
28973
  previousFrameLines = [...lines];
28643
28974
  spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
@@ -29028,12 +29359,102 @@
29028
29359
 
29029
29360
  /**
29030
29361
  * Git commands used to list changed and untracked files in the working tree.
29362
+ *
29363
+ * Note: None of them ever lists a file ignored by `.gitignore`, so a path taken from here can always be staged.
29364
+ * Note: `--no-renames` is used because a detected rename would be reported as its destination path only, which
29365
+ * would leave the source path of a moved file out of the commit.
29031
29366
  */
29032
29367
  const GIT_CHANGED_FILE_COMMANDS = [
29033
- 'git diff --name-only --',
29034
- 'git diff --name-only --cached --',
29368
+ 'git diff --name-only --no-renames --',
29369
+ 'git diff --name-only --no-renames --cached --',
29035
29370
  'git ls-files --others --exclude-standard',
29036
29371
  ];
29372
+ /**
29373
+ * Captures hashes for files that are dirty before one operation starts.
29374
+ */
29375
+ async function captureWorkingTreeChangesSnapshot(projectPath) {
29376
+ const changedFiles = await listWorkingTreeChangedFiles(projectPath);
29377
+ const changedFileHashes = new Map();
29378
+ for (const relativePath of changedFiles) {
29379
+ changedFileHashes.set(relativePath, await readWorkingTreeFileHash(projectPath, relativePath));
29380
+ }
29381
+ return { changedFileHashes };
29382
+ }
29383
+ /**
29384
+ * Lists the repository-relative paths which really changed since the snapshot was taken.
29385
+ *
29386
+ * A file which was already dirty before and was not touched afterwards is not listed, so the result contains
29387
+ * exactly the files changed by the operation which the snapshot wraps.
29388
+ */
29389
+ async function listFilesChangedSinceSnapshot(projectPath, snapshot) {
29390
+ const changedFiles = await listWorkingTreeChangedFiles(projectPath);
29391
+ const changedFilesSinceSnapshot = [];
29392
+ for (const relativePath of changedFiles) {
29393
+ const currentFileHash = await readWorkingTreeFileHash(projectPath, relativePath);
29394
+ const hashBeforeOperation = snapshot.changedFileHashes.get(relativePath);
29395
+ const wasDirtyBeforeOperation = snapshot.changedFileHashes.has(relativePath);
29396
+ if (wasDirtyBeforeOperation && hashBeforeOperation === currentFileHash) {
29397
+ continue;
29398
+ }
29399
+ changedFilesSinceSnapshot.push(relativePath);
29400
+ }
29401
+ return changedFilesSinceSnapshot;
29402
+ }
29403
+ /**
29404
+ * Lists dirty tracked files and untracked files in the working tree.
29405
+ */
29406
+ async function listWorkingTreeChangedFiles(projectPath) {
29407
+ const changedFiles = new Set();
29408
+ for (const command of GIT_CHANGED_FILE_COMMANDS) {
29409
+ const output = await $execCommand({
29410
+ command,
29411
+ cwd: projectPath,
29412
+ isVerbose: false,
29413
+ });
29414
+ for (const filePath of output.split('\n').map(normalizeGitFilePath).filter(Boolean)) {
29415
+ changedFiles.add(filePath);
29416
+ }
29417
+ }
29418
+ return [...changedFiles.values()];
29419
+ }
29420
+ /**
29421
+ * Reads the content hash of one repository-relative working tree file.
29422
+ *
29423
+ * @returns Hash of a regular file, otherwise `null` for a deleted or non-regular file.
29424
+ */
29425
+ async function readWorkingTreeFileHash(projectPath, relativePath) {
29426
+ try {
29427
+ const absolutePath = path.resolve(projectPath, relativePath);
29428
+ const fileStats = await promises.stat(absolutePath);
29429
+ if (!fileStats.isFile()) {
29430
+ return null;
29431
+ }
29432
+ const content = await promises.readFile(absolutePath);
29433
+ return crypto.createHash('sha1').update(content).digest('hex');
29434
+ }
29435
+ catch (error) {
29436
+ if (isFileNotFoundError$3(error)) {
29437
+ return null;
29438
+ }
29439
+ throw error;
29440
+ }
29441
+ }
29442
+ /**
29443
+ * Normalizes Git output paths for internal matching.
29444
+ */
29445
+ function normalizeGitFilePath(filePath) {
29446
+ return filePath.trim().replace(/\\/g, '/');
29447
+ }
29448
+ /**
29449
+ * Returns true when an error is a missing-file filesystem error.
29450
+ */
29451
+ function isFileNotFoundError$3(error) {
29452
+ return Boolean(error &&
29453
+ typeof error === 'object' &&
29454
+ 'code' in error &&
29455
+ (error.code === 'ENOENT' || error.code === 'ENOTDIR'));
29456
+ }
29457
+
29037
29458
  /**
29038
29459
  * File extensions that should always be treated as binary.
29039
29460
  */
@@ -29081,34 +29502,20 @@
29081
29502
  * Captures hashes for files that are dirty before a coding round starts.
29082
29503
  */
29083
29504
  async function captureChangedFilesSnapshot(projectPath) {
29084
- const changedFiles = await listWorkingTreeChangedFiles(projectPath);
29085
- const changedFileHashes = new Map();
29086
- for (const relativePath of changedFiles) {
29087
- const absolutePath = resolveProjectPath(projectPath, relativePath);
29088
- const fileHash = await readFileHashIfRegularFile(absolutePath);
29089
- if (fileHash) {
29090
- changedFileHashes.set(relativePath, fileHash);
29091
- }
29092
- }
29093
- return { changedFileHashes };
29505
+ return captureWorkingTreeChangesSnapshot(projectPath);
29094
29506
  }
29095
29507
  /**
29096
29508
  * Normalizes CRLF to LF only in files that changed since the captured snapshot.
29097
29509
  */
29098
29510
  async function normalizeLineEndingsInFilesChangedSinceSnapshot(options) {
29099
- const changedFiles = await listWorkingTreeChangedFiles(options.projectPath);
29511
+ const changedFiles = await listFilesChangedSinceSnapshot(options.projectPath, options.snapshot);
29100
29512
  let scannedFiles = 0;
29101
29513
  let normalizedFiles = 0;
29102
29514
  let skippedBinaryFiles = 0;
29103
29515
  for (const relativePath of changedFiles) {
29104
29516
  const absolutePath = resolveProjectPath(options.projectPath, relativePath);
29105
- const currentFileHash = await readFileHashIfRegularFile(absolutePath);
29106
- if (!currentFileHash) {
29107
- continue;
29108
- }
29109
- const hashBeforeRound = options.snapshot.changedFileHashes.get(relativePath);
29110
- const hasChangedInRound = hashBeforeRound === undefined || hashBeforeRound !== currentFileHash;
29111
- if (!hasChangedInRound) {
29517
+ // Note: A file deleted or replaced by a directory during the round has no content to normalize
29518
+ if (!(await isRegularFile(absolutePath))) {
29112
29519
  continue;
29113
29520
  }
29114
29521
  scannedFiles++;
@@ -29134,29 +29541,6 @@
29134
29541
  skippedBinaryFiles,
29135
29542
  };
29136
29543
  }
29137
- /**
29138
- * Lists dirty tracked files and untracked files in the working tree.
29139
- */
29140
- async function listWorkingTreeChangedFiles(projectPath) {
29141
- const changedFiles = new Set();
29142
- for (const command of GIT_CHANGED_FILE_COMMANDS) {
29143
- const output = await $execCommand({
29144
- command,
29145
- cwd: projectPath,
29146
- isVerbose: false,
29147
- });
29148
- for (const filePath of output.split('\n').map(normalizeGitFilePath).filter(Boolean)) {
29149
- changedFiles.add(filePath);
29150
- }
29151
- }
29152
- return [...changedFiles.values()];
29153
- }
29154
- /**
29155
- * Normalizes Git output paths for internal matching.
29156
- */
29157
- function normalizeGitFilePath(filePath) {
29158
- return filePath.trim().replace(/\\/g, '/');
29159
- }
29160
29544
  /**
29161
29545
  * Resolves a repository-relative file path to an absolute path.
29162
29546
  */
@@ -29164,33 +29548,16 @@
29164
29548
  return path.resolve(projectPath, relativePath);
29165
29549
  }
29166
29550
  /**
29167
- * Reads file hash for a regular file and returns undefined for non-files/missing files.
29551
+ * Checks whether a path exists and is a regular file.
29168
29552
  */
29169
- async function readFileHashIfRegularFile(path) {
29553
+ async function isRegularFile(path) {
29170
29554
  try {
29171
- const fileStats = await promises.stat(path);
29172
- if (!fileStats.isFile()) {
29173
- return undefined;
29174
- }
29175
- const content = await promises.readFile(path);
29176
- return crypto.createHash('sha1').update(content).digest('hex');
29555
+ return (await promises.stat(path)).isFile();
29177
29556
  }
29178
- catch (error) {
29179
- if (isFileNotFoundError$1(error)) {
29180
- return undefined;
29181
- }
29182
- throw error;
29557
+ catch (_a) {
29558
+ return false;
29183
29559
  }
29184
29560
  }
29185
- /**
29186
- * Returns true when an error is a missing-file filesystem error.
29187
- */
29188
- function isFileNotFoundError$1(error) {
29189
- return Boolean(error &&
29190
- typeof error === 'object' &&
29191
- 'code' in error &&
29192
- ((error.code === 'ENOENT') || error.code === 'ENOTDIR'));
29193
- }
29194
29561
  /**
29195
29562
  * Detects binary files from extension.
29196
29563
  */
@@ -29380,7 +29747,7 @@
29380
29747
  /**
29381
29748
  * Commits staged changes with the provided message using the dedicated coding-agent identity when configured,
29382
29749
  * otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`,
29383
- * `options.includePaths` can restrict staging, `options.onlyPaths` can restrict the commit pathspec,
29750
+ * `options.relevantPaths` restricts both the staging and the commit to the files of the current operation,
29384
29751
  * `options.excludePaths` can keep temporary artifacts out of the created commit and
29385
29752
  * `options.isEmptyCommitAllowed` keeps a round without any file change from failing.
29386
29753
  *
@@ -29396,15 +29763,19 @@
29396
29763
  try {
29397
29764
  const agentEnv = buildAgentGitEnv();
29398
29765
  const signingFlag = buildAgentGitSigningFlag();
29399
- await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, [
29766
+ const excludedGitPaths = await normalizeExcludedGitPaths(projectPath, [
29400
29767
  commitMessagePath,
29401
29768
  ...((_a = options === null || options === void 0 ? void 0 : options.excludePaths) !== null && _a !== void 0 ? _a : []),
29402
29769
  ]);
29770
+ // Note: An excluded path must be dropped from the relevant paths as well, because a commit restricted by
29771
+ // a pathspec commits the working tree content of those paths and would ignore unstaging them
29772
+ const relevantPaths = excludeGitPaths(options === null || options === void 0 ? void 0 : options.relevantPaths, excludedGitPaths);
29773
+ await stageCommitChanges(projectPath, agentEnv, relevantPaths, excludedGitPaths);
29403
29774
  await runGitCommand({
29404
29775
  command: buildGitCommitCommand({
29405
29776
  commitMessagePath,
29406
29777
  signingFlag,
29407
- onlyPaths: options === null || options === void 0 ? void 0 : options.onlyPaths,
29778
+ relevantPaths,
29408
29779
  isEmptyCommitAllowed: options === null || options === void 0 ? void 0 : options.isEmptyCommitAllowed,
29409
29780
  }),
29410
29781
  cwd: projectPath,
@@ -29419,15 +29790,17 @@
29419
29790
  }
29420
29791
  }
29421
29792
  /**
29422
- * Stages repository changes and optionally unstages temporary files that should not end up inside the commit.
29793
+ * Stages the relevant repository changes and unstages temporary files that should not end up inside the commit.
29423
29794
  */
29424
- async function stageCommitChanges(projectPath, agentEnv, includePaths, excludePaths) {
29425
- await runGitCommand({
29426
- command: buildGitAddCommand(includePaths),
29427
- cwd: projectPath,
29428
- env: agentEnv,
29429
- });
29430
- const excludedGitPaths = await normalizeExcludedGitPaths(projectPath, excludePaths);
29795
+ async function stageCommitChanges(projectPath, agentEnv, relevantPaths, excludedGitPaths) {
29796
+ // Note: An operation which changed nothing relevant has nothing to stage
29797
+ if (relevantPaths === undefined || relevantPaths.length > 0) {
29798
+ await runGitCommand({
29799
+ command: buildGitAddCommand(relevantPaths),
29800
+ cwd: projectPath,
29801
+ env: agentEnv,
29802
+ });
29803
+ }
29431
29804
  if (excludedGitPaths.length === 0) {
29432
29805
  return;
29433
29806
  }
@@ -29439,13 +29812,29 @@
29439
29812
  });
29440
29813
  }
29441
29814
  /**
29442
- * Builds the git add command for either the whole tree or a focused set of paths.
29815
+ * Builds the git add command for either the whole tree or the relevant paths of the current operation.
29443
29816
  */
29444
- function buildGitAddCommand(includePaths) {
29445
- if (!includePaths || includePaths.length === 0) {
29817
+ function buildGitAddCommand(relevantPaths) {
29818
+ if (!relevantPaths || relevantPaths.length === 0) {
29446
29819
  return 'git add .';
29447
29820
  }
29448
- return `git add --all -- ${includePaths.map(quoteShellPath$1).join(' ')}`;
29821
+ return `git add --all -- ${relevantPaths.map(quoteShellPath$1).join(' ')}`;
29822
+ }
29823
+ /**
29824
+ * Removes the excluded repository paths from the relevant paths of the current operation.
29825
+ */
29826
+ function excludeGitPaths(relevantPaths, excludedGitPaths) {
29827
+ if (relevantPaths === undefined || excludedGitPaths.length === 0) {
29828
+ return relevantPaths;
29829
+ }
29830
+ const excludedGitPathSet = new Set(excludedGitPaths);
29831
+ return relevantPaths.filter((relevantPath) => !excludedGitPathSet.has(normalizeGitPathSeparators(relevantPath)));
29832
+ }
29833
+ /**
29834
+ * Normalizes path separators so a relevant path can be matched against a repository-relative Git path.
29835
+ */
29836
+ function normalizeGitPathSeparators(path) {
29837
+ return path.replace(/\\/gu, '/');
29449
29838
  }
29450
29839
  /**
29451
29840
  * Converts excluded filesystem paths into unique repository-relative Git paths.
@@ -29616,8 +30005,8 @@
29616
30005
  commandParts.push('--allow-empty');
29617
30006
  }
29618
30007
  commandParts.push(`--file "${options.commitMessagePath}"`);
29619
- if (options.onlyPaths && options.onlyPaths.length > 0) {
29620
- commandParts.push('--', ...options.onlyPaths.map(quoteShellPath$1));
30008
+ if (options.relevantPaths && options.relevantPaths.length > 0) {
30009
+ commandParts.push('--', ...options.relevantPaths.map(quoteShellPath$1));
29621
30010
  }
29622
30011
  return commandParts.join(' ');
29623
30012
  }
@@ -30107,6 +30496,140 @@
30107
30496
  `);
30108
30497
  }
30109
30498
 
30499
+ // Note: [💞] This file defines the shared TEAM workspace convention rather than one standalone entity.
30500
+ /**
30501
+ * Relative directory for an active TEAM conversation workspace.
30502
+ *
30503
+ * @private internal convention shared by the Agents Server and agent-folder runner
30504
+ */
30505
+ const AGENT_TEAM_CONVERSATIONS_DIRECTORY_PATH = path.join(AGENT_MESSAGES_DIRECTORY_PATH, 'team');
30506
+ /**
30507
+ * Relative directory where completed TEAM conversation transcripts are retained.
30508
+ *
30509
+ * @private internal convention shared by the Agents Server and agent-folder runner
30510
+ */
30511
+ const AGENT_FINISHED_TEAM_CONVERSATIONS_DIRECTORY_PATH = path.join(AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, 'team');
30512
+ /**
30513
+ * Name of the JSON manifest that describes one TEAM conversation workspace.
30514
+ *
30515
+ * @private internal convention shared by the Agents Server and agent-folder runner
30516
+ */
30517
+ const AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME = 'team.json';
30518
+ /**
30519
+ * Name of the read-only teammate-source directory inside one TEAM workspace.
30520
+ *
30521
+ * @private internal convention shared by the Agents Server and agent-folder runner
30522
+ */
30523
+ const AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME = 'teammates';
30524
+ /**
30525
+ * Creates the stable directory name that belongs to one queued `.book` message.
30526
+ *
30527
+ * @private internal convention shared by the Agents Server and agent-folder runner
30528
+ */
30529
+ function createAgentTeamConversationWorkspaceDirectoryName(messageFileName) {
30530
+ const rawBaseName = path.basename(messageFileName).replace(/\.book$/iu, '');
30531
+ const normalizedBaseName = rawBaseName.replace(/[^A-Za-z0-9._-]+/gu, '-').replace(/^[._-]+|[._-]+$/gu, '');
30532
+ return normalizedBaseName || 'message';
30533
+ }
30534
+ /**
30535
+ * Creates the relative active workspace path for one queued message.
30536
+ *
30537
+ * @private internal convention shared by the Agents Server and agent-folder runner
30538
+ */
30539
+ function createAgentTeamConversationWorkspacePath(messageFileName) {
30540
+ return path.join(AGENT_TEAM_CONVERSATIONS_DIRECTORY_PATH, createAgentTeamConversationWorkspaceDirectoryName(messageFileName));
30541
+ }
30542
+ /**
30543
+ * Creates the relative completed-workspace path for one queued message.
30544
+ *
30545
+ * @private internal convention shared by the Agents Server and agent-folder runner
30546
+ */
30547
+ function createFinishedAgentTeamConversationWorkspacePath(messageFileName) {
30548
+ return path.join(AGENT_FINISHED_TEAM_CONVERSATIONS_DIRECTORY_PATH, createAgentTeamConversationWorkspaceDirectoryName(messageFileName));
30549
+ }
30550
+ /**
30551
+ * Checks whether unknown JSON has the minimum shape required for a TEAM workspace manifest.
30552
+ *
30553
+ * @private internal convention shared by the Agents Server and agent-folder runner
30554
+ */
30555
+ function isAgentTeamConversationWorkspaceManifest(value) {
30556
+ if (!value || typeof value !== 'object') {
30557
+ return false;
30558
+ }
30559
+ const manifest = value;
30560
+ if (manifest.version !== 1 ||
30561
+ !isPrimaryAgent(manifest.primaryAgent) ||
30562
+ !Array.isArray(manifest.teammates) ||
30563
+ !manifest.teammates.every(isTeammate)) {
30564
+ return false;
30565
+ }
30566
+ return true;
30567
+ }
30568
+ /**
30569
+ * Checks the primary-agent part of an untrusted manifest.
30570
+ *
30571
+ * @private internal utility of `isAgentTeamConversationWorkspaceManifest`
30572
+ */
30573
+ function isPrimaryAgent(value) {
30574
+ return Boolean(value &&
30575
+ typeof value === 'object' &&
30576
+ typeof value.permanentId === 'string' &&
30577
+ typeof value.agentName === 'string');
30578
+ }
30579
+ /**
30580
+ * Checks one teammate part of an untrusted manifest.
30581
+ *
30582
+ * @private internal utility of `isAgentTeamConversationWorkspaceManifest`
30583
+ */
30584
+ function isTeammate(value) {
30585
+ return Boolean(value &&
30586
+ typeof value === 'object' &&
30587
+ typeof value.permanentId === 'string' &&
30588
+ typeof value.agentName === 'string' &&
30589
+ typeof value.url === 'string' &&
30590
+ typeof value.instructions === 'string' &&
30591
+ typeof value.sourceFileName === 'string');
30592
+ }
30593
+
30594
+ /**
30595
+ * Builds the optional single-run TEAM consultation instructions for a coding harness.
30596
+ */
30597
+ function buildAgentTeamPromptSection(workspace) {
30598
+ if (!workspace || workspace.manifest.teammates.length === 0) {
30599
+ return '';
30600
+ }
30601
+ const teammateSections = workspace.manifest.teammates.map((teammate) => {
30602
+ const sourcePath = toPromptPath(path.join(workspace.relativeWorkspacePath, AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME, teammate.sourceFileName));
30603
+ const transcriptPath = toPromptPath(path.join(workspace.relativeWorkspacePath, `${teammate.permanentId}--01.book`));
30604
+ const instructions = teammate.instructions.trim() || 'No additional TEAM instructions were provided.';
30605
+ return _spaceTrim.spaceTrim(`
30606
+ - ${teammate.agentName}
30607
+ - TEAM instructions: ${instructions}
30608
+ - Read-only source: \`${sourcePath}\`
30609
+ - First transcript file: \`${transcriptPath}\`
30610
+ `);
30611
+ });
30612
+ return _spaceTrim.spaceTrim((block) => `
30613
+ ## Team consultations
30614
+
30615
+ The following teammates are available for this user turn:
30616
+
30617
+ ${block(teammateSections.join('\n'))}
30618
+
30619
+ - The underlying coding harness is already executing this turn **exactly once**. Never start, queue, or invoke another coding harness, agent runner, or \`ptbk\` command to consult a teammate.
30620
+ - The primary agent remains responsible for the final user-facing \`MESSAGE @Agent\` answer.
30621
+ - Teammate source files are read-only context. When a teammate consultation occurs, record the actual exchange in a new top-level \`.book\` file in \`${toPromptPath(workspace.relativeWorkspacePath)}\`.
30622
+ - Name each transcript \`<teammate permanent id>--<sequence>.book\`, for example \`${workspace.manifest.teammates[0].permanentId}--01.book\`.
30623
+ - Every transcript must contain alternating \`MESSAGE @${workspace.manifest.primaryAgent.agentName}\` and \`MESSAGE @<teammate name>\` blocks. Do not create a transcript for a teammate that was not consulted.
30624
+ `);
30625
+ }
30626
+ /**
30627
+ * Converts a filesystem-relative path into the portable path notation used in prompts.
30628
+ */
30629
+ function toPromptPath(path) {
30630
+ return path.replace(/\\/gu, '/');
30631
+ }
30632
+
30110
30633
  /**
30111
30634
  * Builds the prompt sent to the selected coding runner for one queued user-thread book.
30112
30635
  */
@@ -30116,7 +30639,7 @@
30116
30639
 
30117
30640
  - Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE @User\`
30118
30641
  - Only change the queued message file by appending one new \`MESSAGE @Agent\` block
30119
- - Do not modify any other file in the repository, except files inside your own \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory
30642
+ ${block(buildAllowedFileChangesPromptLine(options.teamWorkspace))}
30120
30643
 
30121
30644
  ## Rules for the answering
30122
30645
 
@@ -30154,11 +30677,22 @@
30154
30677
 
30155
30678
  ${block(buildAgentProjectsPromptSection(options))}
30156
30679
 
30680
+ ${block(buildAgentTeamPromptSection(options.teamWorkspace))}
30681
+
30157
30682
  ## This is how you should behave
30158
30683
 
30159
30684
  ${block(agentSystemMessage)}
30160
30685
  `);
30161
30686
  }
30687
+ /**
30688
+ * Explains the exact files that one coding harness may change for a user turn.
30689
+ */
30690
+ function buildAllowedFileChangesPromptLine(teamWorkspace) {
30691
+ if (!teamWorkspace) {
30692
+ return `- Do not modify any other file in the repository, except files inside your own \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory`;
30693
+ }
30694
+ return `- Do not modify any other file in the repository, except files inside your own \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory and new consultation transcripts inside \`${teamWorkspace.relativeWorkspacePath.replace(/\\/gu, '/')}\``;
30695
+ }
30162
30696
 
30163
30697
  var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
30164
30698
 
@@ -36858,6 +37392,148 @@
36858
37392
  .join('\n\n');
36859
37393
  }
36860
37394
 
37395
+ /**
37396
+ * Moves harness-created TEAM conversation transcripts into the finished-message history.
37397
+ */
37398
+ async function finalizeAgentTeamConversationWorkspace(options) {
37399
+ if (!options.workspace) {
37400
+ return null;
37401
+ }
37402
+ const activeWorkspacePath = path.join(options.projectPath, createAgentTeamConversationWorkspacePath(options.queuedMessage.fileName));
37403
+ const activeManifestPath = path.join(activeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
37404
+ const transcriptFileNames = await listAgentTeamTranscriptFileNames(activeWorkspacePath);
37405
+ if (!(await isExistingPath$1(activeManifestPath))) {
37406
+ return null;
37407
+ }
37408
+ if (transcriptFileNames.length === 0) {
37409
+ await promises.rm(activeWorkspacePath, { recursive: true, force: true });
37410
+ return null;
37411
+ }
37412
+ const finishedRelativeWorkspacePath = createFinishedAgentTeamConversationWorkspacePath(options.queuedMessage.fileName);
37413
+ const finishedWorkspacePath = path.join(options.projectPath, finishedRelativeWorkspacePath);
37414
+ await promises.rm(finishedWorkspacePath, { recursive: true, force: true });
37415
+ await promises.mkdir(finishedWorkspacePath, { recursive: true });
37416
+ const relativePaths = [
37417
+ toPortablePath$1(path.join(finishedRelativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME)),
37418
+ ];
37419
+ await promises.rename(activeManifestPath, path.join(finishedWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME));
37420
+ for (const transcriptFileName of transcriptFileNames) {
37421
+ await promises.rename(path.join(activeWorkspacePath, transcriptFileName), path.join(finishedWorkspacePath, transcriptFileName));
37422
+ relativePaths.push(toPortablePath$1(path.join(finishedRelativeWorkspacePath, transcriptFileName)));
37423
+ }
37424
+ await promises.rm(activeWorkspacePath, { recursive: true, force: true });
37425
+ return { relativePaths };
37426
+ }
37427
+ /**
37428
+ * Lists only direct transcript `.book` files and deliberately excludes read-only teammate sources.
37429
+ */
37430
+ async function listAgentTeamTranscriptFileNames(activeWorkspacePath) {
37431
+ try {
37432
+ const entries = await promises.readdir(activeWorkspacePath, { withFileTypes: true });
37433
+ return entries
37434
+ .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.book'))
37435
+ .map((entry) => entry.name)
37436
+ .sort((firstFileName, secondFileName) => firstFileName.localeCompare(secondFileName));
37437
+ }
37438
+ catch (error) {
37439
+ if (isFileNotFoundError$2(error)) {
37440
+ return [];
37441
+ }
37442
+ throw error;
37443
+ }
37444
+ }
37445
+ /**
37446
+ * Checks whether one fixed workspace path exists.
37447
+ */
37448
+ async function isExistingPath$1(path) {
37449
+ try {
37450
+ await promises.stat(path);
37451
+ return true;
37452
+ }
37453
+ catch (error) {
37454
+ if (isFileNotFoundError$2(error)) {
37455
+ return false;
37456
+ }
37457
+ throw error;
37458
+ }
37459
+ }
37460
+ /**
37461
+ * Converts a filesystem-relative path into a portable Git path.
37462
+ */
37463
+ function toPortablePath$1(path) {
37464
+ return path.replace(/\\/gu, '/');
37465
+ }
37466
+ /**
37467
+ * Returns true when one filesystem error indicates a missing path.
37468
+ */
37469
+ function isFileNotFoundError$2(error) {
37470
+ return Boolean(error &&
37471
+ typeof error === 'object' &&
37472
+ 'code' in error &&
37473
+ (error.code === 'ENOENT' || error.code === 'ENOTDIR'));
37474
+ }
37475
+
37476
+ /**
37477
+ * Loads the optional TEAM roster snapshot for one queued message.
37478
+ */
37479
+ async function loadAgentTeamConversationWorkspace(projectPath, queuedMessage) {
37480
+ const relativeWorkspacePath = createAgentTeamConversationWorkspacePath(queuedMessage.fileName);
37481
+ const manifestPath = path.join(projectPath, relativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
37482
+ const manifestContent = await readOptionalTextFile(manifestPath);
37483
+ if (manifestContent === null) {
37484
+ return null;
37485
+ }
37486
+ const manifest = parseAgentTeamConversationWorkspaceManifest(manifestContent);
37487
+ if (!manifest || manifest.teammates.length === 0) {
37488
+ return null;
37489
+ }
37490
+ return {
37491
+ relativeWorkspacePath: toPortablePath(relativeWorkspacePath),
37492
+ manifest,
37493
+ };
37494
+ }
37495
+ /**
37496
+ * Parses one untrusted TEAM workspace manifest without making a queued message fail for stale sidecar data.
37497
+ */
37498
+ function parseAgentTeamConversationWorkspaceManifest(manifestContent) {
37499
+ try {
37500
+ const parsedManifest = JSON.parse(manifestContent);
37501
+ return isAgentTeamConversationWorkspaceManifest(parsedManifest) ? parsedManifest : null;
37502
+ }
37503
+ catch (_a) {
37504
+ return null;
37505
+ }
37506
+ }
37507
+ /**
37508
+ * Reads one text file and treats a missing team workspace as absent.
37509
+ */
37510
+ async function readOptionalTextFile(path) {
37511
+ try {
37512
+ return await promises.readFile(path, 'utf-8');
37513
+ }
37514
+ catch (error) {
37515
+ if (isFileNotFoundError$1(error)) {
37516
+ return null;
37517
+ }
37518
+ throw error;
37519
+ }
37520
+ }
37521
+ /**
37522
+ * Converts a filesystem-relative path to the portable form used by prompts and Git.
37523
+ */
37524
+ function toPortablePath(path) {
37525
+ return path.replace(/\\/gu, '/');
37526
+ }
37527
+ /**
37528
+ * Returns true when one filesystem error indicates a missing path.
37529
+ */
37530
+ function isFileNotFoundError$1(error) {
37531
+ return Boolean(error &&
37532
+ typeof error === 'object' &&
37533
+ 'code' in error &&
37534
+ (error.code === 'ENOENT' || error.code === 'ENOTDIR'));
37535
+ }
37536
+
36861
37537
  /**
36862
37538
  * Resolves project-runtime prompt API details for one local agent runner folder.
36863
37539
  *
@@ -37174,11 +37850,13 @@
37174
37850
  * Runs the selected coding runner for one message and finalizes the answered file.
37175
37851
  */
37176
37852
  async function runQueuedAgentMessage(options) {
37177
- const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard } = options;
37853
+ const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard, } = options;
37178
37854
  const agentSystemMessage = await loadLocalAgentSystemMessage(projectPath);
37855
+ const teamWorkspace = await loadAgentTeamConversationWorkspace(projectPath, queuedMessage);
37179
37856
  const prompt = buildAgentMessagePrompt(queuedMessage.relativePath, agentSystemMessage, {
37180
37857
  projectRuntimeApi: resolveAgentProjectRuntimePromptApi(projectPath),
37181
37858
  projectsUrlPath: resolveAgentProjectsUrlPath(projectPath),
37859
+ teamWorkspace: teamWorkspace || undefined,
37182
37860
  });
37183
37861
  const scriptPath = buildAgentMessageScriptPath(projectPath, queuedMessage);
37184
37862
  const runtimeLogPath = buildScriptLogPath(scriptPath);
@@ -37226,6 +37904,13 @@
37226
37904
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
37227
37905
  }
37228
37906
  await normalizeLineEndingsForAgentRound(projectPath, runOptions, roundChangedFilesSnapshot);
37907
+ const finishedTeamWorkspace = await finalizeAgentTeamConversationWorkspace({
37908
+ projectPath,
37909
+ queuedMessage,
37910
+ workspace: teamWorkspace,
37911
+ });
37912
+ // Note: The Agents Server observes the primary finished `.book` as its completion signal.
37913
+ // Retain TEAM transcripts first so that observer can parse every consultation atomically.
37229
37914
  const finishedMessage = await moveAgentMessageToFinished(projectPath, queuedMessage);
37230
37915
  const writtenRunReport = await writeAgentMessageRunReport({
37231
37916
  finishedMessageAbsolutePath: finishedMessage.absolutePath,
@@ -37242,6 +37927,7 @@
37242
37927
  options: runOptions,
37243
37928
  queuedMessage,
37244
37929
  finishedMessage,
37930
+ finishedTeamWorkspace,
37245
37931
  writtenRunReport,
37246
37932
  isQueuedMessageTracked,
37247
37933
  uiHandle,
@@ -37313,7 +37999,7 @@
37313
37999
  * Commits the answered message move unless `--no-commit` was requested.
37314
38000
  */
37315
38001
  async function commitAnsweredMessageIfEnabled(options) {
37316
- const { options: runOptions, queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
38002
+ const { options: runOptions, queuedMessage, finishedMessage, finishedTeamWorkspace, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
37317
38003
  if (runOptions.noCommit) {
37318
38004
  if (!isSharedDashboard) {
37319
38005
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Leaving changes uncommitted');
@@ -37325,21 +38011,24 @@
37325
38011
  }
37326
38012
  await commitChanges(buildAgentMessageCommitMessage(queuedMessage), {
37327
38013
  autoPush: runOptions.autoPush,
37328
- includePaths: buildCommitIncludePaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked),
38014
+ relevantPaths: buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace),
37329
38015
  projectPath,
37330
38016
  });
37331
38017
  }
37332
38018
  /**
37333
38019
  * Builds the focused git path list for the answered-message commit.
37334
38020
  */
37335
- function buildCommitIncludePaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked) {
37336
- const includePaths = isQueuedMessageTracked
38021
+ function buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace) {
38022
+ const relevantPaths = isQueuedMessageTracked
37337
38023
  ? [queuedMessage.relativePath, finishedMessage.relativePath]
37338
38024
  : [finishedMessage.relativePath];
37339
38025
  if (writtenRunReport) {
37340
- includePaths.push(writtenRunReport.relativePath);
38026
+ relevantPaths.push(writtenRunReport.relativePath);
38027
+ }
38028
+ if (finishedTeamWorkspace) {
38029
+ relevantPaths.push(...finishedTeamWorkspace.relativePaths);
37341
38030
  }
37342
- return includePaths;
38031
+ return relevantPaths;
37343
38032
  }
37344
38033
  /**
37345
38034
  * Normalizes line endings in files changed during the current agent round.
@@ -38851,7 +39540,7 @@
38851
39540
  const CODER_GIT_SYNC_DESCRIPTION = _spaceTrim.spaceTrim(`
38852
39541
  Git synchronization:
38853
39542
  - --auto-pull pulls the latest changes before this command changes anything
38854
- - --commit commits the changes made by this command
39543
+ - --commit commits only the files this command has changed, unrelated changes stay in the working tree
38855
39544
  - --auto-push pushes the created commit to the remote repository
38856
39545
  `);
38857
39546
  /**
@@ -38863,7 +39552,7 @@
38863
39552
  * @private internal utility of `promptbookCli`
38864
39553
  */
38865
39554
  function addCoderGitSyncOptions(command) {
38866
- command.option('--commit', 'Commit the changes made by this command with the coding-agent git identity', false);
39555
+ command.option('--commit', 'Commit the files changed by this command with the coding-agent git identity, leaving unrelated changes uncommitted', false);
38867
39556
  command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
38868
39557
  command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
38869
39558
  }
@@ -39172,8 +39861,8 @@
39172
39861
  const projectPath = process.cwd();
39173
39862
  const description = await resolveCoderPromptDescription(descriptionArgument);
39174
39863
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
39175
- const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
39176
- await $pullCoderChanges({ gitSync, projectPath });
39864
+ const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
39865
+ const commitScope = await $startCoderGitSync({ gitSync, projectPath });
39177
39866
  const { /* filePath,*/ emojiTag } = await addCoderPrompt({
39178
39867
  projectPath,
39179
39868
  description,
@@ -39182,7 +39871,7 @@
39182
39871
  });
39183
39872
  await $commitCoderChanges({
39184
39873
  gitSync,
39185
- projectPath,
39874
+ commitScope,
39186
39875
  commitMessage: `${emojiTag} Add prompt`,
39187
39876
  });
39188
39877
  }));
@@ -39776,7 +40465,7 @@
39776
40465
  function $initializeCoderGenerateBoilerplatesCommand(program) {
39777
40466
  const command = program.command('generate-boilerplates');
39778
40467
  command.description(_spaceTrim.spaceTrim((block) => `
39779
- Generate prompt boilerplate files with unique emoji tags
40468
+ Generate prompt boilerplate files with one unique emoji tag per file
39780
40469
 
39781
40470
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
39782
40471
  `));
@@ -39795,8 +40484,8 @@
39795
40484
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
39796
40485
  const projectPath = process.cwd();
39797
40486
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
39798
- const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
39799
- await $pullCoderChanges({ gitSync, projectPath });
40487
+ const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
40488
+ const commitScope = await $startCoderGitSync({ gitSync, projectPath });
39800
40489
  await generatePromptBoilerplate({
39801
40490
  projectPath,
39802
40491
  boilerplateCount,
@@ -39804,14 +40493,14 @@
39804
40493
  });
39805
40494
  await $commitCoderChanges({
39806
40495
  gitSync,
39807
- projectPath,
40496
+ commitScope,
39808
40497
  commitMessage: `Prompts ${formatBoilerplateCount(boilerplateCount)}`,
39809
40498
  });
39810
40499
  return process.exit(0);
39811
40500
  }));
39812
40501
  }
39813
40502
  /**
39814
- * Generates boilerplate prompt files with unique emoji tags.
40503
+ * Generates boilerplate prompt files with one unique emoji tag per file.
39815
40504
  *
39816
40505
  * @private internal function of `generatePromptBoilerplate` command
39817
40506
  */
@@ -39831,10 +40520,10 @@
39831
40520
  const highestNumber = promptNumbering.startNumber === 0 ? 0 : promptNumbering.startNumber - promptNumbering.step;
39832
40521
  const highestNumberFormatted = Math.max(0, highestNumber).toString().padStart(4, '0');
39833
40522
  console.info(colors__default["default"].blue(`Highest existing number for ${promptNumbering.datePrefix} found: ${highestNumberFormatted}`));
39834
- // Note: Every single generated prompt is one separate coding task, so each of them reserves its own fresh emoji tag
39835
40523
  const promptsCount = filesCount * promptsPerFileCount;
39836
40524
  const { availableCount, selectedEmojis } = await getFreshPromptEmojiTags({
39837
- count: promptsCount,
40525
+ // Note: Each generated file reserves one fresh emoji tag shared by all of its prompt sections
40526
+ count: filesCount,
39838
40527
  rootDir: projectPath,
39839
40528
  });
39840
40529
  console.info(colors__default["default"].green(`Found ${availableCount} available fresh emojis`));
@@ -39846,14 +40535,13 @@
39846
40535
  for (let i = 0; i < filesCount; i++) {
39847
40536
  const number = promptNumbering.startNumber + i * promptNumbering.step;
39848
40537
  const title = titles[i % titles.length];
39849
- const emojiTags = selectedEmojis
39850
- .slice(i * promptsPerFileCount, (i + 1) * promptsPerFileCount)
39851
- .map((emoji) => formatPromptEmojiTag(emoji));
40538
+ const emojiTag = formatPromptEmojiTag(selectedEmojis[i]);
39852
40539
  const filename = buildPromptFilename(promptNumbering.datePrefix, number, buildPromptSlug$1(promptTemplate.slugPrefix, title));
39853
40540
  const filepath = path.join(PROMPTS_DIRECTORY_PATH, filename);
39854
40541
  const absoluteFilepath = path.join(projectPath, filepath);
39855
40542
  const content = buildBoilerplatePromptFileContent({
39856
- emojiTags,
40543
+ emojiTag,
40544
+ promptsPerFileCount,
39857
40545
  title,
39858
40546
  body: promptTemplate.content,
39859
40547
  });
@@ -39862,7 +40550,7 @@
39862
40550
  absoluteFilepath,
39863
40551
  filename,
39864
40552
  content,
39865
- emojiTags,
40553
+ emojiTag,
39866
40554
  number,
39867
40555
  });
39868
40556
  }
@@ -39870,26 +40558,25 @@
39870
40558
  console.info(colors__default["default"].yellow(`Creating ${filesToCreate.length} files:`));
39871
40559
  for (const file of filesToCreate) {
39872
40560
  fs.writeFileSync(file.absoluteFilepath, file.content, 'utf-8');
39873
- console.info(colors__default["default"].green(`✓ Created: ${file.filename} with ${file.emojiTags.join(' ')}`));
40561
+ console.info(colors__default["default"].green(`✓ Created: ${file.filename} with ${file.emojiTag}`));
39874
40562
  }
39875
40563
  console.info(colors__default["default"].bgGreen(` Successfully created ${promptsCount} prompts in ${filesToCreate.length} prompt boilerplate files! `));
39876
40564
  }
39877
40565
  /**
39878
- * Builds the markdown content of one generated prompt file with one prompt section per emoji tag.
40566
+ * Builds the markdown content of one generated prompt file with one prompt section per prompt.
39879
40567
  *
39880
- * Multiple prompts in one file are separated by the `---` separator, exactly like the prompt runner expects them.
40568
+ * Multiple prompts in one file share the file emoji tag and are separated by the `---` separator,
40569
+ * exactly like the prompt runner expects them.
39881
40570
  *
39882
40571
  * @private internal utility of `generatePromptBoilerplate` command
39883
40572
  */
39884
- function buildBoilerplatePromptFileContent({ emojiTags, title, body, }) {
39885
- return emojiTags
39886
- .map((emojiTag) => buildCoderPromptSection({
40573
+ function buildBoilerplatePromptFileContent({ emojiTag, promptsPerFileCount, title, body, }) {
40574
+ return Array.from({ length: promptsPerFileCount }, () => buildCoderPromptSection({
39887
40575
  statusLine: '[-]',
39888
40576
  emojiTag,
39889
40577
  title,
39890
40578
  body,
39891
- }))
39892
- .join('\n\n---\n\n');
40579
+ })).join('\n\n---\n\n');
39893
40580
  }
39894
40581
  /**
39895
40582
  * Builds filename slug from template and placeholder title.
@@ -39906,14 +40593,14 @@
39906
40593
  // Note: [💞] Ignore a discrepancy between file name and entity name
39907
40594
 
39908
40595
  /**
39909
- * Asks the user in the terminal whether the harness should be installed or updated right now.
40596
+ * Asks the user in the terminal whether an npm package should be installed or updated now.
39910
40597
  *
39911
40598
  * Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
39912
40599
  *
39913
40600
  * @returns `true` when the user confirms, `false` when the user declines or the terminal is not interactive
39914
40601
  * @private internal utility of `promptbookCli`
39915
40602
  */
39916
- async function $askForHarnessInstallationApproval(question) {
40603
+ async function $askForNpmPackageInstallationApproval(question) {
39917
40604
  if (!process.stdin.isTTY) {
39918
40605
  // Note: In non-interactive environments like CI there is nobody who could confirm the installation
39919
40606
  return false;
@@ -39929,7 +40616,7 @@
39929
40616
  readlineInterface.close();
39930
40617
  }
39931
40618
  }
39932
- // Note: [🟡] Code for CLI harness installation approval [$askForHarnessInstallationApproval](src/cli/cli-commands/common/harness/$askForHarnessInstallationApproval.ts) should never be published outside of `@promptbook/cli`
40619
+ // Note: [🟡] Code for CLI npm package installation approval [$askForNpmPackageInstallationApproval](src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.ts) should never be published outside of `@promptbook/cli`
39933
40620
 
39934
40621
  /**
39935
40622
  * Builds the shell command which installs or updates one CLI coding harness globally.
@@ -40024,7 +40711,7 @@
40024
40711
  return;
40025
40712
  }
40026
40713
  console.warn(colors__default["default"].yellow(formatHarnessInstallationWarning(status)));
40027
- const isInstallationApproved = await $askForHarnessInstallationApproval(installationState === 'not-installed'
40714
+ const isInstallationApproved = await $askForNpmPackageInstallationApproval(installationState === 'not-installed'
40028
40715
  ? `Install ${definition.label} globally now?`
40029
40716
  : `Update ${definition.label} to ${latestVersion} now?`);
40030
40717
  if (!isInstallationApproved) {
@@ -40036,30 +40723,25 @@
40036
40723
  // Note: [🟡] Code for CLI harness installation handling [$applyHarnessInstallationStatus](src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts) should never be published outside of `@promptbook/cli`
40037
40724
 
40038
40725
  /**
40039
- * Pattern matching one `major.minor.patch` version anywhere in the output of a harness version command.
40726
+ * Pattern matching one `major.minor.patch` version anywhere in npm or CLI command output.
40040
40727
  */
40041
- const HARNESS_VERSION_PATTERN = /\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/u;
40728
+ const NPM_PACKAGE_VERSION_PATTERN = /\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/gu;
40042
40729
  /**
40043
- * Extracts the version from the raw output of a harness `--version` command.
40730
+ * Extracts an npm package version from raw command output.
40044
40731
  *
40045
- * Harness CLIs print their version in many shapes, for example `2.1.199 (Claude Code)`,
40046
- * `codex-cli 0.144.4`, `GitHub Copilot CLI 1.0.61.` or after unrelated deprecation warning lines,
40047
- * so the first version-looking token of the first matching line wins.
40732
+ * Both npm and CLI commands may emit unrelated warnings before the version. CLI commands use the first matching token;
40733
+ * npm registry commands can opt into the last matching token because npm warnings commonly precede their JSON output.
40048
40734
  *
40049
40735
  * @returns The parsed version or `null` when the output contains no version
40050
40736
  * @private internal utility of `promptbookCli`
40051
40737
  */
40052
- function extractHarnessVersionFromOutput(output) {
40738
+ function extractNpmPackageVersionFromOutput(output, { isLastMatchPreferred = false } = {}) {
40053
40739
  var _a;
40054
- for (const line of output.split('\n')) {
40055
- const versionMatch = HARNESS_VERSION_PATTERN.exec(line);
40056
- if (versionMatch !== null) {
40057
- return (_a = versionMatch[1]) !== null && _a !== void 0 ? _a : null;
40058
- }
40059
- }
40060
- return null;
40740
+ const versionMatches = Array.from(output.matchAll(NPM_PACKAGE_VERSION_PATTERN));
40741
+ const versionMatch = isLastMatchPreferred ? versionMatches[versionMatches.length - 1] : versionMatches[0];
40742
+ return (_a = versionMatch === null || versionMatch === void 0 ? void 0 : versionMatch[1]) !== null && _a !== void 0 ? _a : null;
40061
40743
  }
40062
- // Note: [🟡] Code for CLI harness version parsing [extractHarnessVersionFromOutput](src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.ts) should never be published outside of `@promptbook/cli`
40744
+ // Note: [🟡] Code for CLI npm package version parsing [extractNpmPackageVersionFromOutput](src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.ts) should never be published outside of `@promptbook/cli`
40063
40745
 
40064
40746
  /**
40065
40747
  * Time limit for asking the globally installed harness command for its version.
@@ -40080,46 +40762,60 @@
40080
40762
  timeout: HARNESS_VERSION_COMMAND_TIMEOUT_MS,
40081
40763
  isVerbose: false,
40082
40764
  }).catch(() => '');
40083
- return extractHarnessVersionFromOutput(output);
40765
+ return extractNpmPackageVersionFromOutput(output);
40084
40766
  }
40085
40767
  // Note: [🟡] Code for CLI harness version detection [$resolveInstalledHarnessVersion](src/cli/cli-commands/common/harness/$resolveInstalledHarnessVersion.ts) should never be published outside of `@promptbook/cli`
40086
40768
 
40087
40769
  /**
40088
- * Time limit for the whole `npm view` lookup of the newest published harness version.
40770
+ * Time limit for the whole `npm view` lookup of the newest published package version.
40089
40771
  */
40090
- const HARNESS_LATEST_VERSION_COMMAND_TIMEOUT_MS = 60 * 1000;
40772
+ const NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS = 60 * 1000;
40091
40773
  /**
40092
40774
  * Time limit for one npm registry request, so an unreachable registry never blocks the command.
40093
40775
  */
40094
40776
  const NPM_REGISTRY_FETCH_TIMEOUT_MS = 20 * 1000;
40095
40777
  /**
40096
- * Reads the newest version of the harness published to npm.
40778
+ * Pattern matching npm's JSON-encoded package version result.
40779
+ */
40780
+ const NPM_VIEW_JSON_VERSION_PATTERN = /"(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/u;
40781
+ /**
40782
+ * Reads the newest version of one npm package.
40097
40783
  *
40098
40784
  * Note: `$` is used to indicate that this function is not a pure function - it queries the npm registry
40099
40785
  *
40100
40786
  * @returns The newest published version or `null` when the registry could not be reached
40101
40787
  * @private internal utility of `promptbookCli`
40102
40788
  */
40103
- async function $resolveLatestHarnessVersion(definition) {
40789
+ async function $resolveLatestNpmPackageVersion(npmPackageName) {
40104
40790
  const output = await $execCommand({
40105
- command: `npm view ${definition.npmPackageName} version --fetch-timeout=${NPM_REGISTRY_FETCH_TIMEOUT_MS} --fetch-retries=1`,
40791
+ command: `npm view ${npmPackageName} version --json --loglevel=error --fetch-timeout=${NPM_REGISTRY_FETCH_TIMEOUT_MS} --fetch-retries=1`,
40106
40792
  crashOnError: true,
40107
- timeout: HARNESS_LATEST_VERSION_COMMAND_TIMEOUT_MS,
40793
+ timeout: NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS,
40108
40794
  isVerbose: false,
40109
40795
  }).catch(() => '');
40110
- return extractHarnessVersionFromOutput(output);
40796
+ return extractLatestNpmPackageVersionFromNpmViewOutput(output);
40797
+ }
40798
+ /**
40799
+ * Extracts the version from npm's JSON result while tolerating warnings emitted before or after that result.
40800
+ *
40801
+ * @private internal utility of `$resolveLatestNpmPackageVersion`
40802
+ */
40803
+ function extractLatestNpmPackageVersionFromNpmViewOutput(output) {
40804
+ var _a;
40805
+ const jsonVersionMatch = NPM_VIEW_JSON_VERSION_PATTERN.exec(output);
40806
+ return (_a = jsonVersionMatch === null || jsonVersionMatch === void 0 ? void 0 : jsonVersionMatch[1]) !== null && _a !== void 0 ? _a : extractNpmPackageVersionFromOutput(output, { isLastMatchPreferred: true });
40111
40807
  }
40112
- // Note: [🟡] Code for CLI harness registry lookup [$resolveLatestHarnessVersion](src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.ts) should never be published outside of `@promptbook/cli`
40808
+ // Note: [🟡] Code for CLI npm package registry lookup [$resolveLatestNpmPackageVersion](src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.ts) should never be published outside of `@promptbook/cli`
40113
40809
 
40114
40810
  /**
40115
- * Compares the installed harness version with the newest version published to npm.
40811
+ * Compares an installed npm package version with the newest published version.
40116
40812
  *
40117
40813
  * Only the numeric `major.minor.patch` segments are compared and pre-release suffixes are ignored,
40118
- * so a harness installed from a pre-release build of the newest version is not reported as outdated.
40814
+ * so a package installed from a pre-release build of the newest version is not reported as outdated.
40119
40815
  *
40120
40816
  * @private internal utility of `promptbookCli`
40121
40817
  */
40122
- function isHarnessVersionOutdated(installedVersion, latestVersion) {
40818
+ function isNpmPackageVersionOutdated(installedVersion, latestVersion) {
40123
40819
  var _a, _b;
40124
40820
  const installedSegments = parseVersionSegments(installedVersion);
40125
40821
  const latestSegments = parseVersionSegments(latestVersion);
@@ -40135,6 +40831,8 @@
40135
40831
  }
40136
40832
  /**
40137
40833
  * Splits one version into its numeric segments, dropping the pre-release and build suffix.
40834
+ *
40835
+ * @private internal utility of `isNpmPackageVersionOutdated`
40138
40836
  */
40139
40837
  function parseVersionSegments(version) {
40140
40838
  const [numericVersion = ''] = version.trim().split(/[-+]/u);
@@ -40143,7 +40841,7 @@
40143
40841
  return Number.isNaN(segmentNumber) ? 0 : segmentNumber;
40144
40842
  });
40145
40843
  }
40146
- // Note: [🟡] Code for CLI harness version comparison [isHarnessVersionOutdated](src/cli/cli-commands/common/harness/isHarnessVersionOutdated.ts) should never be published outside of `@promptbook/cli`
40844
+ // 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`
40147
40845
 
40148
40846
  /**
40149
40847
  * Detects whether one CLI coding harness is installed globally and whether it is up to date.
@@ -40155,7 +40853,7 @@
40155
40853
  async function $checkHarnessInstallation(definition) {
40156
40854
  const [installedVersion, latestVersion] = await Promise.all([
40157
40855
  $resolveInstalledHarnessVersion(definition),
40158
- $resolveLatestHarnessVersion(definition),
40856
+ $resolveLatestNpmPackageVersion(definition.npmPackageName),
40159
40857
  ]);
40160
40858
  return {
40161
40859
  definition,
@@ -40174,7 +40872,7 @@
40174
40872
  if (latestVersion === null) {
40175
40873
  return 'unknown';
40176
40874
  }
40177
- return isHarnessVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
40875
+ return isNpmPackageVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
40178
40876
  }
40179
40877
  // Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
40180
40878
 
@@ -40406,11 +41104,12 @@
40406
41104
  * Default npm scripts initialized by `ptbk coder init`.
40407
41105
  */
40408
41106
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
40409
- 'coder:generate-boilerplates': `ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ./prompts/templates/common.md`,
40410
- 'coder:add': 'ptbk coder add --template ./prompts/templates/common.md',
40411
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max --agent agents/developer.book --context AGENTS.md --test-before yes-and-fix',
41107
+ // Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
41108
+ 'coder:generate-boilerplates': `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ./prompts/templates/common.md`,
41109
+ 'coder:add': 'npx ptbk coder add --template ./prompts/templates/common.md',
41110
+ 'coder:run': 'npx ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max --agent agents/developer.book --context AGENTS.md --test-before yes-and-fix',
40412
41111
  // 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
40413
- 'coder:verify': 'ptbk coder verify',
41112
+ 'coder:verify': 'npx ptbk coder verify',
40414
41113
  };
40415
41114
  /**
40416
41115
  * Lists the default npm scripts initialized by `ptbk coder init`.
@@ -40780,14 +41479,14 @@
40780
41479
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
40781
41480
  const projectPath = process.cwd();
40782
41481
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
40783
- const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
40784
- await $pullCoderChanges({ gitSync, projectPath });
41482
+ const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
41483
+ const commitScope = await $startCoderGitSync({ gitSync, projectPath });
40785
41484
  const summary = await initializeCoderProjectConfiguration(projectPath);
40786
41485
  printInitializationSummary(summary);
40787
41486
  await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
40788
41487
  await $commitCoderChanges({
40789
41488
  gitSync,
40790
- projectPath,
41489
+ commitScope,
40791
41490
  commitMessage: 'Initialize Promptbook Coder',
40792
41491
  });
40793
41492
  await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
@@ -40876,6 +41575,370 @@
40876
41575
  }
40877
41576
  // Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
40878
41577
 
41578
+ /**
41579
+ * Npm packages which provide an executable Promptbook CLI.
41580
+ *
41581
+ * `ptbk` is the preferred package and forwards to `@promptbook/cli`; the latter can also be installed directly.
41582
+ *
41583
+ * @private internal utility of `promptbookCli`
41584
+ */
41585
+ const PROMPTBOOK_CLI_NPM_PACKAGE_NAMES = ['ptbk', '@promptbook/cli'];
41586
+ // Note: [🟡] Code for Promptbook CLI installation definitions [PromptbookCliInstallation](src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.ts) should never be published outside of `@promptbook/cli`
41587
+
41588
+ /**
41589
+ * Time limit for resolving npm's global `node_modules` directory.
41590
+ */
41591
+ const NPM_GLOBAL_NODE_MODULES_PATH_COMMAND_TIMEOUT_MS = 30 * 1000;
41592
+ /**
41593
+ * Finds every locally or globally installed Promptbook CLI package which can be updated.
41594
+ *
41595
+ * A local package must be declared directly in the current project's `package.json`; transitive packages are not
41596
+ * updated because this command must not change dependencies chosen by another package.
41597
+ *
41598
+ * Note: `$` is used to indicate that this function is not a pure function - it reads package manifests and runs npm
41599
+ *
41600
+ * @private internal utility of `promptbookCli`
41601
+ */
41602
+ async function $resolvePromptbookCliInstallations() {
41603
+ const [localInstallations, globalInstallations] = await Promise.all([
41604
+ $resolveLocalPromptbookCliInstallations(),
41605
+ $resolveGlobalPromptbookCliInstallations(),
41606
+ ]);
41607
+ return [...localInstallations, ...globalInstallations];
41608
+ }
41609
+ /**
41610
+ * Finds Promptbook CLI packages installed directly in the current project's dependencies.
41611
+ *
41612
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41613
+ */
41614
+ async function $resolveLocalPromptbookCliInstallations() {
41615
+ const projectPath = process.cwd();
41616
+ const projectPackageJson = await $readJsonObject(path.join(projectPath, 'package.json'));
41617
+ if (projectPackageJson === null) {
41618
+ return [];
41619
+ }
41620
+ const installations = await Promise.all(PROMPTBOOK_CLI_NPM_PACKAGE_NAMES.map(async (npmPackageName) => {
41621
+ const installationLocation = resolveLocalPromptbookCliInstallationLocation(projectPackageJson, npmPackageName);
41622
+ if (installationLocation === undefined) {
41623
+ return undefined;
41624
+ }
41625
+ const installedVersion = await $resolveInstalledNpmPackageVersion(path.join(projectPath, 'node_modules'), npmPackageName);
41626
+ if (installedVersion === undefined) {
41627
+ return undefined;
41628
+ }
41629
+ return { npmPackageName, installedVersion, installationLocation };
41630
+ }));
41631
+ return installations.filter(isDefined);
41632
+ }
41633
+ /**
41634
+ * Finds Promptbook CLI packages installed in npm's global `node_modules` directory.
41635
+ *
41636
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41637
+ */
41638
+ async function $resolveGlobalPromptbookCliInstallations() {
41639
+ const globalNodeModulesPath = await $resolveNpmGlobalNodeModulesPath();
41640
+ if (globalNodeModulesPath === null) {
41641
+ return [];
41642
+ }
41643
+ const installations = await Promise.all(PROMPTBOOK_CLI_NPM_PACKAGE_NAMES.map(async (npmPackageName) => {
41644
+ const installedVersion = await $resolveInstalledNpmPackageVersion(globalNodeModulesPath, npmPackageName);
41645
+ if (installedVersion === undefined) {
41646
+ return undefined;
41647
+ }
41648
+ return {
41649
+ npmPackageName,
41650
+ installedVersion,
41651
+ installationLocation: 'global',
41652
+ };
41653
+ }));
41654
+ return installations.filter(isDefined);
41655
+ }
41656
+ /**
41657
+ * Resolves the current project's manifest section that declares a Promptbook CLI package.
41658
+ *
41659
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41660
+ */
41661
+ function resolveLocalPromptbookCliInstallationLocation(packageJson, npmPackageName) {
41662
+ if (readNestedStringProperty(packageJson, 'dependencies', npmPackageName) !== undefined) {
41663
+ return 'local-dependency';
41664
+ }
41665
+ if (readNestedStringProperty(packageJson, 'devDependencies', npmPackageName) !== undefined) {
41666
+ return 'local-development-dependency';
41667
+ }
41668
+ return undefined;
41669
+ }
41670
+ /**
41671
+ * Reads an installed npm package version from a `node_modules` directory.
41672
+ *
41673
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41674
+ */
41675
+ async function $resolveInstalledNpmPackageVersion(nodeModulesPath, npmPackageName) {
41676
+ const installedPackageJson = await $readJsonObject(path.join(nodeModulesPath, npmPackageName, 'package.json'));
41677
+ return installedPackageJson === null ? undefined : readStringProperty(installedPackageJson, 'version');
41678
+ }
41679
+ /**
41680
+ * Resolves npm's global `node_modules` directory without querying the registry.
41681
+ *
41682
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41683
+ */
41684
+ async function $resolveNpmGlobalNodeModulesPath() {
41685
+ const output = await $execCommand({
41686
+ command: 'npm root --global',
41687
+ crashOnError: true,
41688
+ timeout: NPM_GLOBAL_NODE_MODULES_PATH_COMMAND_TIMEOUT_MS,
41689
+ isVerbose: false,
41690
+ }).catch(() => '');
41691
+ const globalNodeModulesPath = output.trim();
41692
+ return globalNodeModulesPath === '' ? null : globalNodeModulesPath;
41693
+ }
41694
+ /**
41695
+ * Reads a JSON object, returning `null` for missing or malformed files because update checks must stay advisory.
41696
+ *
41697
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41698
+ */
41699
+ async function $readJsonObject(filePath) {
41700
+ try {
41701
+ const parsedValue = JSON.parse(await promises.readFile(filePath, 'utf8'));
41702
+ return isJsonObject(parsedValue) ? parsedValue : null;
41703
+ }
41704
+ catch (_a) {
41705
+ return null;
41706
+ }
41707
+ }
41708
+ /**
41709
+ * Checks whether an unknown value can be read as a JSON object.
41710
+ *
41711
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41712
+ */
41713
+ function isJsonObject(value) {
41714
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
41715
+ }
41716
+ /**
41717
+ * Reads one string property from a JSON object.
41718
+ *
41719
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41720
+ */
41721
+ function readStringProperty(jsonObject, propertyName) {
41722
+ const value = jsonObject[propertyName];
41723
+ return typeof value === 'string' ? value : undefined;
41724
+ }
41725
+ /**
41726
+ * Reads one string property nested under another JSON object property.
41727
+ *
41728
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41729
+ */
41730
+ function readNestedStringProperty(jsonObject, parentPropertyName, propertyName) {
41731
+ const nestedValue = jsonObject[parentPropertyName];
41732
+ return isJsonObject(nestedValue) ? readStringProperty(nestedValue, propertyName) : undefined;
41733
+ }
41734
+ /**
41735
+ * Narrows an array after `undefined` values were removed.
41736
+ *
41737
+ * @private internal utility of `$resolvePromptbookCliInstallations`
41738
+ */
41739
+ function isDefined(value) {
41740
+ return value !== undefined;
41741
+ }
41742
+ // Note: [🟡] Code for Promptbook CLI installation detection [$resolvePromptbookCliInstallations](src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.ts) should never be published outside of `@promptbook/cli`
41743
+
41744
+ /**
41745
+ * Checks every local and global Promptbook CLI installation against the newest npm version.
41746
+ *
41747
+ * Note: `$` is used to indicate that this function is not a pure function - it reads installed packages and queries npm
41748
+ *
41749
+ * @private internal utility of `promptbookCli`
41750
+ */
41751
+ async function $checkPromptbookCliInstallations() {
41752
+ const installations = await $resolvePromptbookCliInstallations();
41753
+ if (installations.length === 0) {
41754
+ return [];
41755
+ }
41756
+ const latestVersionsByNpmPackageName = await $resolveLatestVersionsByNpmPackageName(installations);
41757
+ return installations.map((installation) => {
41758
+ var _a;
41759
+ const latestVersion = (_a = latestVersionsByNpmPackageName.get(installation.npmPackageName)) !== null && _a !== void 0 ? _a : null;
41760
+ return {
41761
+ installation,
41762
+ installationState: resolvePromptbookCliInstallationState(installation.installedVersion, latestVersion),
41763
+ latestVersion,
41764
+ };
41765
+ });
41766
+ }
41767
+ /**
41768
+ * Resolves each package's newest version once, even when it is installed locally and globally.
41769
+ *
41770
+ * @private internal utility of `$checkPromptbookCliInstallations`
41771
+ */
41772
+ async function $resolveLatestVersionsByNpmPackageName(installations) {
41773
+ const npmPackageNames = Array.from(new Set(installations.map(({ npmPackageName }) => npmPackageName)));
41774
+ const latestVersionEntries = await Promise.all(npmPackageNames.map(async (npmPackageName) => {
41775
+ const latestVersion = await $resolveLatestNpmPackageVersion(npmPackageName);
41776
+ return [npmPackageName, latestVersion];
41777
+ }));
41778
+ return new Map(latestVersionEntries);
41779
+ }
41780
+ /**
41781
+ * Derives the update state of one installed Promptbook CLI package.
41782
+ *
41783
+ * @private internal utility of `$checkPromptbookCliInstallations`
41784
+ */
41785
+ function resolvePromptbookCliInstallationState(installedVersion, latestVersion) {
41786
+ if (latestVersion === null) {
41787
+ return 'unknown';
41788
+ }
41789
+ return isNpmPackageVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
41790
+ }
41791
+ // Note: [🟡] Code for Promptbook CLI installation check [$checkPromptbookCliInstallations](src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.ts) should never be published outside of `@promptbook/cli`
41792
+
41793
+ /**
41794
+ * Builds the npm command that updates one existing Promptbook CLI installation.
41795
+ *
41796
+ * @private internal utility of `promptbookCli`
41797
+ */
41798
+ function buildPromptbookCliInstallCommand(installation) {
41799
+ const { npmPackageName, installationLocation } = installation;
41800
+ if (installationLocation === 'global') {
41801
+ return `npm install --global ${npmPackageName}@latest`;
41802
+ }
41803
+ if (installationLocation === 'local-development-dependency') {
41804
+ return `npm install --save-dev ${npmPackageName}@latest`;
41805
+ }
41806
+ return `npm install --save ${npmPackageName}@latest`;
41807
+ }
41808
+ // Note: [🟡] Code for Promptbook CLI installation command [buildPromptbookCliInstallCommand](src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.ts) should never be published outside of `@promptbook/cli`
41809
+
41810
+ /**
41811
+ * Updates one Promptbook CLI package in its existing local or global location.
41812
+ *
41813
+ * Note: `$` is used to indicate that this function is not a pure function - it installs an npm package
41814
+ *
41815
+ * @returns `true` when the package update succeeds, `false` when it fails
41816
+ * @private internal utility of `promptbookCli`
41817
+ */
41818
+ async function $updatePromptbookCliInstallation(status) {
41819
+ const { npmPackageName } = status.installation;
41820
+ const installCommand = buildPromptbookCliInstallCommand(status.installation);
41821
+ console.info(colors__default["default"].cyan(`Updating Promptbook CLI package \`${npmPackageName}\` with \`${installCommand}\`...`));
41822
+ try {
41823
+ await $execCommand({
41824
+ command: installCommand,
41825
+ crashOnError: true,
41826
+ isVerbose: true,
41827
+ });
41828
+ }
41829
+ catch (error) {
41830
+ assertsError(error);
41831
+ const updateErrorMessage = error.message;
41832
+ console.error(colors__default["default"].red(_spaceTrim.spaceTrim((block) => `
41833
+ Could not update **Promptbook CLI** package \`${npmPackageName}\`.
41834
+
41835
+ Update it manually with \`${installCommand}\` and run the command again.
41836
+
41837
+ ${block(updateErrorMessage)}
41838
+ `)));
41839
+ return false;
41840
+ }
41841
+ console.info(colors__default["default"].green(`Promptbook CLI package \`${npmPackageName}\` has been updated.`));
41842
+ return true;
41843
+ }
41844
+ // Note: [🟡] Code for Promptbook CLI installation update [$updatePromptbookCliInstallation](src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.ts) should never be published outside of `@promptbook/cli`
41845
+
41846
+ /**
41847
+ * Formats the warning shown when one or more Promptbook CLI installations are outdated.
41848
+ *
41849
+ * @private internal utility of `promptbookCli`
41850
+ */
41851
+ function formatPromptbookCliInstallationWarning(statuses) {
41852
+ const statusLines = statuses.map((status) => {
41853
+ const { npmPackageName, installedVersion, installationLocation } = status.installation;
41854
+ return `- ${formatPromptbookCliInstallationLocation(installationLocation)} \`${npmPackageName}\`: installed \`${installedVersion}\`, newest \`${status.latestVersion}\``;
41855
+ });
41856
+ return _spaceTrim.spaceTrim((block) => `
41857
+ **Promptbook CLI** is outdated.
41858
+
41859
+ ${block(statusLines.join('\n'))}
41860
+ `);
41861
+ }
41862
+ /**
41863
+ * Describes an installation location in text suitable for an update warning.
41864
+ *
41865
+ * @private internal utility of `formatPromptbookCliInstallationWarning`
41866
+ */
41867
+ function formatPromptbookCliInstallationLocation(installationLocation) {
41868
+ if (installationLocation === 'global') {
41869
+ return 'Globally installed';
41870
+ }
41871
+ if (installationLocation === 'local-development-dependency') {
41872
+ return 'Locally in `devDependencies`';
41873
+ }
41874
+ return 'Locally in `dependencies`';
41875
+ }
41876
+ // Note: [🟡] Code for Promptbook CLI installation warning [formatPromptbookCliInstallationWarning](src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.ts) should never be published outside of `@promptbook/cli`
41877
+
41878
+ /**
41879
+ * Checks local and global Promptbook CLI installations before an interactive coder run and offers to update them.
41880
+ *
41881
+ * Once an update is approved, the caller must stop the current run and let the user start a fresh process. A local
41882
+ * npm update can modify `package.json` and a lockfile, which would make the coder's working-tree preflight fail;
41883
+ * more importantly, this Node.js process has already loaded the old CLI implementation.
41884
+ *
41885
+ * Note: `$` is used to indicate that this function is not a pure function - it reads package manifests, queries npm,
41886
+ * asks the user, and may install npm packages
41887
+ *
41888
+ * @returns `true` when an update was approved and the current coder run should stop
41889
+ * @private internal utility of `promptbookCli`
41890
+ */
41891
+ async function $ensurePromptbookCliInstallations() {
41892
+ if (!process.stdin.isTTY) {
41893
+ // Note: Non-interactive runs cannot approve an update and should not wait for registry checks.
41894
+ return false;
41895
+ }
41896
+ const statuses = await $checkPromptbookCliInstallations();
41897
+ const outdatedStatuses = statuses.filter(({ installationState }) => installationState === 'outdated');
41898
+ if (outdatedStatuses.length === 0) {
41899
+ reportPromptbookCliInstallationStatuses(statuses);
41900
+ return false;
41901
+ }
41902
+ console.warn(colors__default["default"].yellow(formatPromptbookCliInstallationWarning(outdatedStatuses)));
41903
+ const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?');
41904
+ if (!isUpdateApproved) {
41905
+ const manualInstallCommands = outdatedStatuses
41906
+ .map((status) => `- \`${buildPromptbookCliInstallCommand(status.installation)}\``)
41907
+ .join('\n');
41908
+ console.info(colors__default["default"].gray(_spaceTrim.spaceTrim((block) => `
41909
+ Skipped updating Promptbook CLI.
41910
+
41911
+ Run one of these commands manually:
41912
+
41913
+ ${block(manualInstallCommands)}
41914
+ `)));
41915
+ return false;
41916
+ }
41917
+ for (const status of outdatedStatuses) {
41918
+ await $updatePromptbookCliInstallation(status);
41919
+ }
41920
+ console.info(colors__default["default"].green('Promptbook CLI update is complete. Run the command again to use the new version.'));
41921
+ return true;
41922
+ }
41923
+ /**
41924
+ * Reports successful and unverifiable Promptbook CLI checks when no update is required.
41925
+ *
41926
+ * @private internal utility of `$ensurePromptbookCliInstallations`
41927
+ */
41928
+ function reportPromptbookCliInstallationStatuses(statuses) {
41929
+ for (const status of statuses) {
41930
+ const { npmPackageName, installedVersion } = status.installation;
41931
+ if (status.installationState === 'up-to-date') {
41932
+ console.info(colors__default["default"].gray(`✔ Promptbook CLI \`${npmPackageName}\` ${installedVersion} is up to date.`));
41933
+ continue;
41934
+ }
41935
+ if (status.installationState === 'unknown') {
41936
+ console.info(colors__default["default"].gray(`✔ Promptbook CLI \`${npmPackageName}\` ${installedVersion} is installed; the newest version could not be checked.`));
41937
+ }
41938
+ }
41939
+ }
41940
+ // Note: [🟡] Code for Promptbook CLI installation orchestration [$ensurePromptbookCliInstallations](src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts) should never be published outside of `@promptbook/cli`
41941
+
40879
41942
  /**
40880
41943
  * Modes supported by `ptbk coder run --test-before`.
40881
41944
  */
@@ -40928,12 +41991,14 @@
40928
41991
 
40929
41992
  Features:
40930
41993
  - Automatically stages and commits changes with agent identity unless --no-commit is used
41994
+ - Commits only the prompt file and the files the coding agent has changed, leaving unrelated changes alone
40931
41995
  - Optional post-commit git push with explicit --auto-push opt-in
40932
41996
  - Optional pre-prompt git pull with explicit --auto-pull opt-in
40933
41997
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
40934
41998
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
40935
41999
  - Optional --no-ui keeps plain streaming console output for logging and debugging
40936
42000
  - Checks that the selected harness is installed globally and up to date before the first prompt
42001
+ - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
40937
42002
  - Supports GPG signing of commits
40938
42003
  - Optional pre-coding test run that can stop or repair pre-existing failures
40939
42004
  - Optional post-prompt verification with test-feedback retries
@@ -40983,7 +42048,6 @@
40983
42048
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
40984
42049
  isAgentRequired: !dryRun,
40985
42050
  });
40986
- await $ensureHarnessInstallations([runnerOptions.agentName]);
40987
42051
  // [1] Parse the wait options and --no-auto:
40988
42052
  // default: run automatically through the queue (no waiting between prompts)
40989
42053
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
@@ -40991,6 +42055,10 @@
40991
42055
  // --wait-between-prompts: pace from start of one prompt to start of next
40992
42056
  // --wait-after-error: wait before retrying after an error (default 10m)
40993
42057
  const waitForUser = !auto;
42058
+ if (waitForUser && (await $ensurePromptbookCliInstallations())) {
42059
+ return process.exit(0);
42060
+ }
42061
+ await $ensureHarnessInstallations([runnerOptions.agentName]);
40994
42062
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
40995
42063
  const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
40996
42064
  const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
@@ -44067,8 +45135,8 @@
44067
45135
  */
44068
45136
  $applyToTaskJson(command, $taskJson) {
44069
45137
  if ($taskJson.format !== undefined && command.format !== $taskJson.format) {
44070
- throw new ParseError(`Format format is already defined to "${$taskJson.format}".
44071
- Now you try to redefine it by "${command.format}"`);
45138
+ throw new ParseError(_spaceTrim.spaceTrim(`Format format is already defined to "${$taskJson.format}".
45139
+ Now you try to redefine it by "${command.format}"`));
44072
45140
  }
44073
45141
  $taskJson.format = command.format;
44074
45142
  },
@@ -56911,28 +57979,28 @@
56911
57979
  */
56912
57980
  function buildToolInvocationScript(options) {
56913
57981
  const { functionName, functionArgsExpression } = options;
56914
- return `
56915
- const args = ${functionArgsExpression};
56916
- const runtimeContextRaw =
56917
- typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
56918
- ? undefined
56919
- : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
57982
+ return _spaceTrim.spaceTrim((block) => `
57983
+ const args = ${block(functionArgsExpression)};
57984
+ const runtimeContextRaw =
57985
+ typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
57986
+ ? undefined
57987
+ : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
56920
57988
 
56921
- if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
56922
- args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
56923
- }
57989
+ if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
57990
+ args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
57991
+ }
56924
57992
 
56925
- const toolProgressTokenRaw =
56926
- typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
56927
- ? undefined
56928
- : ${TOOL_PROGRESS_TOKEN_PARAMETER};
57993
+ const toolProgressTokenRaw =
57994
+ typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
57995
+ ? undefined
57996
+ : ${TOOL_PROGRESS_TOKEN_PARAMETER};
56929
57997
 
56930
- if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
56931
- args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
56932
- }
57998
+ if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
57999
+ args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
58000
+ }
56933
58001
 
56934
- return await ${functionName}(args);
56935
- `;
58002
+ return await ${functionName}(args);
58003
+ `);
56936
58004
  }
56937
58005
 
56938
58006
  /**
@@ -68878,6 +69946,28 @@
68878
69946
  RemoteAgent: RemoteAgent
68879
69947
  });
68880
69948
 
69949
+ /**
69950
+ * Captures the working tree state before one `ptbk coder` operation changes anything.
69951
+ *
69952
+ * The captured scope is passed to the commit of the very same operation, which then commits exactly the files
69953
+ * this operation has created, changed, moved or deleted.
69954
+ */
69955
+ async function captureCoderCommitScope(projectPath) {
69956
+ return {
69957
+ projectPath,
69958
+ snapshotBeforeOperation: await captureWorkingTreeChangesSnapshot(projectPath),
69959
+ };
69960
+ }
69961
+ /**
69962
+ * Resolves the repository-relative paths which one `ptbk coder` operation has really changed.
69963
+ *
69964
+ * Files which were already changed before the operation started and which the operation did not touch are
69965
+ * never part of the result, so they stay in the working tree instead of being swept into the commit.
69966
+ */
69967
+ async function resolveCoderCommitScopePaths(scope) {
69968
+ return listFilesChangedSinceSnapshot(scope.projectPath, scope.snapshotBeforeOperation);
69969
+ }
69970
+
68881
69971
  /**
68882
69972
  * Git synchronization which leaves the repository completely untouched.
68883
69973
  *
@@ -68888,6 +69978,24 @@
68888
69978
  isAutoPushEnabled: false,
68889
69979
  isAutoPullEnabled: false,
68890
69980
  });
69981
+ /**
69982
+ * Pulls the latest repository changes and captures the working tree state before a `ptbk coder` command
69983
+ * changes the project.
69984
+ *
69985
+ * The returned scope is handed over to `$commitCoderChanges` of the very same command, which then commits
69986
+ * exactly the files this command has changed.
69987
+ */
69988
+ async function $startCoderGitSync(options) {
69989
+ const { gitSync, projectPath = process.cwd() } = options;
69990
+ await $pullCoderChanges({ gitSync, projectPath });
69991
+ if (!gitSync.isCommitEnabled) {
69992
+ // Note: A command which does not commit must not touch git at all, so that it also works in a project
69993
+ // which is not a git repository
69994
+ return { projectPath, snapshotBeforeOperation: { changedFileHashes: new Map() } };
69995
+ }
69996
+ // Note: The scope is captured after pulling, so files brought in by the pull are not committed again
69997
+ return captureCoderCommitScope(projectPath);
69998
+ }
68891
69999
  /**
68892
70000
  * Pulls the latest repository changes before a `ptbk coder` command changes the project.
68893
70001
  */
@@ -68902,38 +70010,31 @@
68902
70010
  /**
68903
70011
  * Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
68904
70012
  *
68905
- * Note: A repository without any change is left alone instead of creating an empty commit.
70013
+ * Note: Only the files this very command has changed are committed, everything else is left in the working tree.
70014
+ * Note: A command which changed nothing is left alone instead of creating an empty commit.
68906
70015
  */
68907
70016
  async function $commitCoderChanges(options) {
68908
- const { gitSync, commitMessage, projectPath = process.cwd() } = options;
70017
+ const { gitSync, commitMessage, commitScope } = options;
68909
70018
  if (!gitSync.isCommitEnabled) {
68910
70019
  return;
68911
70020
  }
68912
- if (!(await hasChangesToCommit(projectPath))) {
68913
- console.info(colors__default["default"].gray('Nothing to commit, the working tree is clean'));
70021
+ const relevantPaths = await resolveCoderCommitScopePaths(commitScope);
70022
+ if (relevantPaths.length === 0) {
70023
+ console.info(colors__default["default"].gray('Nothing to commit, this command has not changed any file'));
68914
70024
  return;
68915
70025
  }
68916
70026
  await commitChanges(commitMessage, {
68917
- projectPath,
70027
+ projectPath: commitScope.projectPath,
70028
+ relevantPaths,
68918
70029
  autoPush: gitSync.isAutoPushEnabled,
68919
70030
  });
68920
70031
  console.info(colors__default["default"].green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
68921
70032
  }
68922
- /**
68923
- * Checks whether the repository holds any change which can be committed.
68924
- */
68925
- async function hasChangesToCommit(projectPath) {
68926
- const gitStatus = await runGitCommand({
68927
- command: 'git status --porcelain',
68928
- cwd: projectPath,
68929
- isVerbose: false,
68930
- });
68931
- return gitStatus.trim() !== '';
68932
- }
68933
70033
 
68934
70034
  var coderGitSync = /*#__PURE__*/Object.freeze({
68935
70035
  __proto__: null,
68936
70036
  DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
70037
+ $startCoderGitSync: $startCoderGitSync,
68937
70038
  $pullCoderChanges: $pullCoderChanges,
68938
70039
  $commitCoderChanges: $commitCoderChanges
68939
70040
  });
@@ -72271,34 +73372,34 @@
72271
73372
  * @private function of runDatabaseMigrations
72272
73373
  */
72273
73374
  async function ensureMigrationsTableSchema(options) {
72274
- await options.client.query(`
73375
+ await options.client.query(_spaceTrim.spaceTrim(`
72275
73376
  CREATE TABLE IF NOT EXISTS ${options.migrationsTableIdentifier} (
72276
73377
  "filename" TEXT PRIMARY KEY,
72277
73378
  "appliedAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
72278
73379
  "appliedBy" TEXT NOT NULL DEFAULT '${options.manualAppliedByDefault}'
72279
73380
  );
72280
- `);
72281
- await options.client.query(`
73381
+ `));
73382
+ await options.client.query(_spaceTrim.spaceTrim(`
72282
73383
  ALTER TABLE ${options.migrationsTableIdentifier}
72283
73384
  ADD COLUMN IF NOT EXISTS "appliedBy" TEXT;
72284
- `);
72285
- await options.client.query(`
73385
+ `));
73386
+ await options.client.query(_spaceTrim.spaceTrim(`
72286
73387
  UPDATE ${options.migrationsTableIdentifier}
72287
73388
  SET "appliedBy" = $1
72288
73389
  WHERE "appliedBy" IS NULL;
72289
- `, [options.manualAppliedByDefault]);
72290
- await options.client.query(`
73390
+ `), [options.manualAppliedByDefault]);
73391
+ await options.client.query(_spaceTrim.spaceTrim(`
72291
73392
  ALTER TABLE ${options.migrationsTableIdentifier}
72292
73393
  ALTER COLUMN "appliedBy" SET DEFAULT '${options.manualAppliedByDefault}';
72293
- `);
72294
- await options.client.query(`
73394
+ `));
73395
+ await options.client.query(_spaceTrim.spaceTrim(`
72295
73396
  ALTER TABLE ${options.migrationsTableIdentifier}
72296
73397
  ALTER COLUMN "appliedBy" SET NOT NULL;
72297
- `);
72298
- await options.client.query(`
73398
+ `));
73399
+ await options.client.query(_spaceTrim.spaceTrim(`
72299
73400
  ALTER TABLE ${options.migrationsTableIdentifier}
72300
73401
  ENABLE ROW LEVEL SECURITY;
72301
- `);
73402
+ `));
72302
73403
  }
72303
73404
  /**
72304
73405
  * Rolls back an opened transaction and logs failures without masking original errors.
@@ -73256,9 +74357,9 @@
73256
74357
  });
73257
74358
  const promptExecutionStartedDate = moment__default["default"]();
73258
74359
  let attemptCount = 1;
73259
- const roundChangedFilesSnapshot = options.normalizeLineEndings
73260
- ? await captureChangedFilesSnapshot(roundProjectPath)
73261
- : undefined;
74360
+ // Note: The very same snapshot tells which files this round has changed, both for normalizing their line
74361
+ // endings and for committing only them instead of everything which is changed in the project
74362
+ const roundCommitScope = await captureRoundCommitScopeIfNeeded(options, roundProjectPath);
73262
74363
  await withPromptRuntimeLog(scriptPath, async (logPath) => {
73263
74364
  let lastError;
73264
74365
  for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
@@ -73294,7 +74395,7 @@
73294
74395
  result,
73295
74396
  commitMessage,
73296
74397
  logPath,
73297
- roundChangedFilesSnapshot,
74398
+ roundCommitScope,
73298
74399
  isRichUiEnabled,
73299
74400
  progressDisplay,
73300
74401
  uiHandle,
@@ -73327,7 +74428,7 @@
73327
74428
  attemptCount,
73328
74429
  error: lastError,
73329
74430
  options,
73330
- roundChangedFilesSnapshot,
74431
+ roundCommitScope,
73331
74432
  uiHandle,
73332
74433
  waitForRequestedPause,
73333
74434
  roundProjectPath,
@@ -73408,7 +74509,7 @@
73408
74509
  * Finalizes a successful prompt round, including prompt bookkeeping and commit flow.
73409
74510
  */
73410
74511
  async function finalizeSuccessfulPromptRound(options) {
73411
- const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath, roundChangedFilesSnapshot, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
74512
+ const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath, roundCommitScope, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
73412
74513
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
73413
74514
  await waitForRequestedPause({
73414
74515
  checkpointLabel: 'recording the successful prompt result',
@@ -73419,7 +74520,7 @@
73419
74520
  // Note: The prompt status is always written into the original project, an isolated round transports
73420
74521
  // its own changes back through the merge instead
73421
74522
  await writePromptFile(nextPrompt.file);
73422
- await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundChangedFilesSnapshot);
74523
+ await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
73423
74524
  await recordPromptDurationInEstimateCache({
73424
74525
  options: runOptions,
73425
74526
  runnerMetadata,
@@ -73440,6 +74541,9 @@
73440
74541
  });
73441
74542
  await commitChanges(commitMessage, {
73442
74543
  autoPush: runOptions.autoPush,
74544
+ // Note: Only the prompt file and the files the coding agent has changed belong to this round,
74545
+ // everything which was already changed before the round started stays in the working tree
74546
+ relevantPaths: roundCommitScope && (await resolveCoderCommitScopePaths(roundCommitScope)),
73443
74547
  // Keep the live runtime log out of default commits because it is deleted after a successful round.
73444
74548
  excludePaths: runOptions.preserveLogs ? undefined : [logPath],
73445
74549
  projectPath: roundProjectPath,
@@ -73463,7 +74567,7 @@
73463
74567
  * Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
73464
74568
  */
73465
74569
  async function finalizeFailedPromptRound(options) {
73466
- const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundChangedFilesSnapshot, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
74570
+ const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
73467
74571
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
73468
74572
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
73469
74573
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
@@ -73481,7 +74585,7 @@
73481
74585
  modelName: runnerMetadata.modelName,
73482
74586
  error,
73483
74587
  });
73484
- await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundChangedFilesSnapshot);
74588
+ await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
73485
74589
  }
73486
74590
  /**
73487
74591
  * Waits for the optional user confirmation immediately before creating the commit.
@@ -73547,17 +74651,29 @@
73547
74651
  thinkingLevel: runOptions.thinkingLevel,
73548
74652
  }, promptDurationMs);
73549
74653
  }
74654
+ /**
74655
+ * Captures which files are already changed before the round starts, when the round needs to know it later.
74656
+ *
74657
+ * The scope is needed to commit only the files of this round and to normalize the line endings of exactly
74658
+ * those files, so a round which does neither of them does not pay for hashing the working tree.
74659
+ */
74660
+ async function captureRoundCommitScopeIfNeeded(options, roundProjectPath) {
74661
+ if (options.noCommit && !options.normalizeLineEndings) {
74662
+ return undefined;
74663
+ }
74664
+ return captureCoderCommitScope(roundProjectPath);
74665
+ }
73550
74666
  /**
73551
74667
  * Normalizes line endings in files modified during the current coding round.
73552
74668
  */
73553
- async function normalizeLineEndingsForCurrentRound(options, roundProjectPath, roundChangedFilesSnapshot) {
73554
- if (!options.normalizeLineEndings || !roundChangedFilesSnapshot) {
74669
+ async function normalizeLineEndingsForCurrentRound(options, roundProjectPath, roundCommitScope) {
74670
+ if (!options.normalizeLineEndings || !roundCommitScope) {
73555
74671
  return;
73556
74672
  }
73557
74673
  try {
73558
74674
  const result = await normalizeLineEndingsInFilesChangedSinceSnapshot({
73559
74675
  projectPath: roundProjectPath,
73560
- snapshot: roundChangedFilesSnapshot,
74676
+ snapshot: roundCommitScope.snapshotBeforeOperation,
73561
74677
  });
73562
74678
  if (result.normalizedFiles > 0) {
73563
74679
  console.info(colors__default["default"].gray(`Normalized line endings to LF in ${result.normalizedFiles} changed file(s).`));
@@ -73618,11 +74734,11 @@
73618
74734
  * Builds the markdown list of commands which merge one isolated task manually and clean it up.
73619
74735
  */
73620
74736
  function buildManualRecoveryHints(worktree) {
73621
- return [
73622
- `- \`git merge ${worktree.branchName}\` and resolve the conflicts`,
73623
- `- \`git worktree remove ${worktree.worktreeDisplayPath}\``,
73624
- `- \`git branch -d ${worktree.branchName}\``,
73625
- ].join('\n');
74737
+ return _spaceTrim.spaceTrim(`
74738
+ - \`git merge ${worktree.branchName}\` and resolve the conflicts
74739
+ - \`git worktree remove ${worktree.worktreeDisplayPath}\`
74740
+ - \`git branch -d ${worktree.branchName}\`
74741
+ `);
73626
74742
  }
73627
74743
 
73628
74744
  /**
@@ -74078,6 +75194,9 @@
74078
75194
  var _a;
74079
75195
  const { nextPrompt, promptLabel, isRichUiEnabled, uiHandle, waitForRequestedPause } = options;
74080
75196
  const projectPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
75197
+ // Note: The original project is left untouched by the isolated round itself, so its scope covers exactly
75198
+ // the prompt status update and the changes the merge brings back from the worktree
75199
+ const originalProjectCommitScope = await captureCoderCommitScope(projectPath);
74081
75200
  const worktree = await createCoderIsolationWorktree({
74082
75201
  projectPath,
74083
75202
  taskName: buildCoderIsolationTaskName(nextPrompt.file, nextPrompt.section),
@@ -74109,6 +75228,7 @@
74109
75228
  // Note: The merge only stages the isolated changes, so this commit joins them with the prompt status update
74110
75229
  await commitChanges(buildCommitMessage(nextPrompt.file, nextPrompt.section), {
74111
75230
  autoPush: options.options.autoPush,
75231
+ relevantPaths: await resolveCoderCommitScopePaths(originalProjectCommitScope),
74112
75232
  projectPath,
74113
75233
  });
74114
75234
  await removeCoderIsolationWorktree(worktree);
@@ -74132,7 +75252,7 @@
74132
75252
  await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
74133
75253
  autoPush: options.options.autoPush,
74134
75254
  projectPath: worktree.projectPath,
74135
- includePaths: [nextPrompt.file.path, errorLogPath].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
75255
+ relevantPaths: [nextPrompt.file.path, errorLogPath].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
74136
75256
  });
74137
75257
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
74138
75258
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
@@ -74287,26 +75407,22 @@
74287
75407
  * Builds the ANSI ASCII-art visual of the `--agent` book shown above the coder-run dashboard.
74288
75408
  *
74289
75409
  * The agent's avatar visual is resolved the same way as on the website - the `META AVATAR`
74290
- * commitment wins, then the `META VISUAL` commitment, then the shared default visual - and is
74291
- * rendered dynamically through the shared canvas avatar pipeline into terminal ASCII art.
74292
- * The terminal variant uses a transparent horizontal canvas instead of the website's framed 1:1 surface.
75410
+ * commitment wins, then the `META VISUAL` commitment, then the shared default visual.
75411
+ * Character-based visuals such as `AsciiOctopus` paint the terminal grid directly, while pixel-based
75412
+ * visuals are rendered through the shared canvas avatar pipeline into terminal ASCII art, using a
75413
+ * transparent horizontal canvas instead of the website's framed 1:1 surface.
74293
75414
  *
74294
- * The visual is decorative, so any failure (for example when the optional `@napi-rs/canvas`
74295
- * module is not installed) returns `null` and the caller keeps the default brand banner.
75415
+ * The visual is decorative, so any failure returns `null` and the caller keeps the default brand banner.
74296
75416
  *
74297
75417
  * @param agentSource Source of the `--agent` book file.
74298
75418
  * @returns ANSI-colored ASCII-art renderer or `null` when the visual cannot be rendered.
74299
75419
  */
74300
75420
  async function buildCoderRunAgentVisual(agentSource) {
74301
75421
  try {
74302
- // Note: `@napi-rs/canvas` is an optional native module, so it is imported dynamically and lazily
74303
- const { createCanvas } = await import('@napi-rs/canvas');
74304
- const colorDepth = $detectTerminalAnsiColorDepth();
74305
- const createCanvasForAsciiArt = (width, height) => createCanvas(width, height);
74306
75422
  const agentVisual = createTerminalAgentAvatarVisual({
74307
75423
  agentSource,
74308
- colorDepth,
74309
- createCanvas: createCanvasForAsciiArt,
75424
+ colorDepth: $detectTerminalAnsiColorDepth(),
75425
+ createCanvas: await createOptionalNodeCanvasFactory(),
74310
75426
  });
74311
75427
  return {
74312
75428
  isAnimated: agentVisual.isAnimated,
@@ -74325,6 +75441,23 @@
74325
75441
  return null;
74326
75442
  }
74327
75443
  }
75444
+ /**
75445
+ * Loads the optional Node.js canvas factory which rasterizes pixel-based avatar visuals.
75446
+ *
75447
+ * @returns Canvas factory or `undefined` when the optional native module is not installed.
75448
+ *
75449
+ * @private helper of `buildCoderRunAgentVisual`
75450
+ */
75451
+ async function createOptionalNodeCanvasFactory() {
75452
+ try {
75453
+ // Note: `@napi-rs/canvas` is an optional native module, so it is imported dynamically and lazily
75454
+ const { createCanvas } = await import('@napi-rs/canvas');
75455
+ return (width, height) => createCanvas(width, height);
75456
+ }
75457
+ catch (error) {
75458
+ return undefined;
75459
+ }
75460
+ }
74328
75461
 
74329
75462
  /**
74330
75463
  * Creates the one queue prompt used to repair a pre-existing test failure.
@@ -75303,7 +76436,7 @@
75303
76436
  *
75304
76437
  * @private internal constant of `ptbk coder server`
75305
76438
  */
75306
- const CODER_SERVER_HTML = `<!DOCTYPE html>
76439
+ const CODER_SERVER_HTML = _spaceTrim.spaceTrim(`<!DOCTYPE html>
75307
76440
  <html lang="en">
75308
76441
  <head>
75309
76442
  <meta charset="UTF-8">
@@ -75989,7 +77122,7 @@
75989
77122
  </script>
75990
77123
  </body>
75991
77124
  </html>
75992
- `;
77125
+ `);
75993
77126
  // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/coderServerHtml.ts) should never be published outside of `@promptbook/cli`
75994
77127
  // Note: Keep in sync with apps/coder-server/index.html
75995
77128
 
@@ -76181,8 +77314,7 @@
76181
77314
  async function commitPromptEdit(promptFilePath, sectionIndex) {
76182
77315
  const relativePromptFilePath = path.relative(process.cwd(), promptFilePath).replace(/\\/gu, '/');
76183
77316
  await commitChanges(`Edit coder prompt ${relativePromptFilePath}#${sectionIndex + 1}`, {
76184
- includePaths: [relativePromptFilePath],
76185
- onlyPaths: [relativePromptFilePath],
77317
+ relevantPaths: [relativePromptFilePath],
76186
77318
  });
76187
77319
  }
76188
77320
  /**
@@ -76327,8 +77459,9 @@
76327
77459
  let promptFiles = initialFiles;
76328
77460
  const skippedFiles = new Set();
76329
77461
  while (true) {
76330
- // Note: The git synchronization is applied around each single verification, not once per whole run
76331
- await $pullCoderChanges({ gitSync: normalizedOptions.gitSync });
77462
+ // Note: The git synchronization is applied around each single verification, not once per whole run,
77463
+ // so each verification commits only the prompt file it has archived or repaired
77464
+ const commitScope = await $startCoderGitSync({ gitSync: normalizedOptions.gitSync });
76332
77465
  if (normalizedOptions.gitSync.isAutoPullEnabled) {
76333
77466
  // Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
76334
77467
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
@@ -76341,7 +77474,7 @@
76341
77474
  if (outcome.wasSkipped) {
76342
77475
  skippedFiles.add(fileWithAllDone.path);
76343
77476
  }
76344
- await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
77477
+ await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
76345
77478
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
76346
77479
  continue;
76347
77480
  }
@@ -76352,18 +77485,18 @@
76352
77485
  break;
76353
77486
  }
76354
77487
  const outcome = await resolvePrompt(nextPrompt);
76355
- await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
77488
+ await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
76356
77489
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
76357
77490
  }
76358
77491
  }
76359
77492
  /**
76360
77493
  * Commits and pushes one applied verification when the git synchronization is enabled.
76361
77494
  */
76362
- async function $commitVerificationOutcome(gitSync, outcome) {
77495
+ async function $commitVerificationOutcome(gitSync, commitScope, outcome) {
76363
77496
  if (outcome.commitMessage === null) {
76364
77497
  return;
76365
77498
  }
76366
- await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
77499
+ await $commitCoderChanges({ gitSync, commitScope, commitMessage: outcome.commitMessage });
76367
77500
  }
76368
77501
  /**
76369
77502
  * Parses supported command-line arguments for the standalone verification script.