@promptbook/cli 0.114.0-4 → 0.114.0-6
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.
- package/README.md +3 -3
- package/agents/default/developer.book +1 -0
- package/apps/agents-server/next.config.ts +22 -0
- package/apps/agents-server/package.json +6 -6
- package/apps/agents-server/scripts/build-agents-server.js +43 -7
- package/apps/agents-server/scripts/build-e2e.js +12 -2
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +30 -16
- package/apps/agents-server/scripts/kill-port.js +163 -0
- package/apps/agents-server/scripts/run-e2e-tests.js +21 -8
- package/apps/agents-server/scripts/run-npm.js +81 -5
- package/apps/agents-server/src/app/actions.ts +3 -0
- package/apps/agents-server/src/app/admin/email-server/page.tsx +17 -18
- package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +2 -0
- package/apps/agents-server/src/app/admin/task-manager/[taskId]/TaskManagerTaskDetailClient.tsx +2 -0
- package/apps/agents-server/src/app/admin/task-manager/taskManagerTaskPresentation.tsx +23 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +7 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/[timeoutId]/route.ts +10 -153
- package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/route.ts +9 -4
- package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/route.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +2 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/resolveUserChatScope.ts +8 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/route.ts +15 -7
- package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorSaving.ts +18 -28
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistoryClient.tsx +19 -1
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatSidebarDefault.tsx +17 -5
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatNotice.tsx +23 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatPlannedMessages.tsx +153 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatPanel.tsx +6 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +13 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/ExternalUserChatAdminActions.tsx +51 -3
- package/apps/agents-server/src/app/agents/[agentName]/chat/TeamMemberFrozenChatPrimaryAgentLink.tsx +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatSidebarState.ts +26 -2
- package/apps/agents-server/src/app/agents/[agentName]/goal/page.tsx +35 -0
- package/apps/agents-server/src/app/agents/[agentName]/projects/[projectName]/page.tsx +2 -2
- package/apps/agents-server/src/app/api/admin/dns-records/cloudflare/route.ts +26 -16
- package/apps/agents-server/src/app/api/emails/incoming/stalwart/route.ts +9 -2
- package/apps/agents-server/src/app/superadmin/servers/ServersRegistryTable.tsx +31 -39
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +10 -3
- package/apps/agents-server/src/components/AgentProjects/AgentProjectIcon.tsx +137 -0
- package/apps/agents-server/src/components/AgentProjects/AgentProjectItem.tsx +26 -10
- package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsApiTokenImportStep.tsx +5 -6
- package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsCheckStep.tsx +6 -5
- package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsManualStep.tsx +10 -14
- package/apps/agents-server/src/components/CloudflareDnsWizard/CloudflareDnsWizard.tsx +11 -20
- package/apps/agents-server/src/components/CloudflareDnsWizard/useCloudflareDnsRecordImport.ts +5 -6
- package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsInstructions.tsx +91 -57
- package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsSectionPanel.tsx +63 -0
- package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsSectionVariantTabs.tsx +67 -0
- package/apps/agents-server/src/components/DnsRecordsInstructions/DnsRecordsTable.tsx +40 -0
- package/apps/agents-server/src/components/Header/buildActiveAgentViewItems.ts +5 -4
- package/apps/agents-server/src/components/Header/createAgentViewLabel.tsx +4 -4
- package/apps/agents-server/src/components/Header/resolveActiveAgentNavigation.ts +2 -2
- package/apps/agents-server/src/components/Homepage/useAgentsListImportExportState.ts +22 -10
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +1 -1
- package/apps/agents-server/src/database/migratePrefix.ts +13 -12
- package/apps/agents-server/src/database/migrations/2026-08-0100-agent-goal-chat-source.sql +10 -0
- package/apps/agents-server/src/generated/reservedPaths.ts +42 -42
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +9 -1
- package/apps/agents-server/src/languages/translations/czech.yaml +9 -1
- package/apps/agents-server/src/languages/translations/english.yaml +9 -1
- package/apps/agents-server/src/message-providers/email/stalwart/parseInboundStalwartEmail.ts +17 -2
- package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatConstants.ts +9 -0
- package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatIdentity.ts +28 -0
- package/apps/agents-server/src/utils/agentGoalChat/appendAgentGoalChatNote.ts +43 -0
- package/apps/agents-server/src/utils/agentGoalChat/canAccessAgentGoalChat.ts +14 -0
- package/apps/agents-server/src/utils/agentGoalChat/createAgentGoalChatNoteContent.ts +69 -0
- package/apps/agents-server/src/utils/agentGoalChat/ensureAgentGoalChat.ts +66 -0
- package/apps/agents-server/src/utils/agentGoalChat/prependAgentGoalChatSummarySeed.ts +40 -0
- package/apps/agents-server/src/utils/agentGoalChat/recordAgentGoalChatLifecycleNote.ts +36 -0
- package/apps/agents-server/src/utils/agentGoalChat/resolveAgentGoalChatOwnerUserId.ts +104 -0
- package/apps/agents-server/src/utils/agentGoalChat/scheduleAgentGoalChatModifiedNote.ts +82 -0
- package/apps/agents-server/src/utils/agentGoalChat.ts +19 -0
- package/apps/agents-server/src/utils/agentProjects/AgentProjectInfo.ts +10 -1
- package/apps/agents-server/src/utils/agentProjects/AgentProjectReferenceInfo.ts +5 -2
- package/apps/agents-server/src/utils/agentProjects/agentProjectFileNames.ts +21 -0
- package/apps/agents-server/src/utils/agentProjects/agentProjectHrefs.ts +20 -11
- package/apps/agents-server/src/utils/agentProjects/agentProjectsPaths.ts +15 -5
- package/apps/agents-server/src/utils/agentProjects/createAgentProjectInitials.ts +36 -0
- package/apps/agents-server/src/utils/agentProjects/createAgentProjectMarkdownReferences.ts +10 -9
- package/apps/agents-server/src/utils/agentProjects/createAgentProjectsDnsRecordsSection.ts +113 -0
- package/apps/agents-server/src/utils/agentProjects/createStaticAgentProjectServer.ts +2 -6
- package/apps/agents-server/src/utils/agentProjects/humanizeAgentProjectName.ts +25 -0
- package/apps/agents-server/src/utils/agentProjects/listAgentProjectChatReferences.ts +10 -3
- package/apps/agents-server/src/utils/agentProjects/parseAgentProjectIndexHtml.ts +167 -0
- package/apps/agents-server/src/utils/agentProjects/readAgentProjectIndexHtmlProfile.ts +24 -0
- package/apps/agents-server/src/utils/agentProjects/readAgentProjectReadme.ts +3 -53
- package/apps/agents-server/src/utils/agentProjects/readAgentProjectRootTextFile.ts +73 -0
- package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectFaviconRelativePath.ts +108 -0
- package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectInfo.ts +12 -3
- package/apps/agents-server/src/utils/agentProjects/{resolveAgentProjectReadmeProfile.ts → resolveAgentProjectProfile.ts} +33 -33
- package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectPublicUrls.ts +34 -0
- package/apps/agents-server/src/utils/bookLanguageDocumentation/createBookLanguageDocumentationPdfResponse.ts +19 -17
- package/apps/agents-server/src/utils/chatTasksAdmin.ts +9 -0
- package/apps/agents-server/src/utils/dnsRecords/DnsRecordInstruction.ts +20 -0
- package/apps/agents-server/src/utils/dnsRecords/DnsRecordsSection.ts +77 -0
- package/apps/agents-server/src/utils/dnsRecords/createServerDnsRecordsSections.ts +58 -0
- package/apps/agents-server/src/utils/dnsRecords/createServerDomainDnsRecordsSection.ts +48 -0
- package/apps/agents-server/src/utils/dnsRecords/dnsRecordGroups.ts +52 -0
- package/apps/agents-server/src/utils/dnsRecords/resolveDnsRecordBatchPlan.ts +47 -1
- package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/adminChatTaskSqlQuery.ts +41 -36
- package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/mapAdminChatTaskFallbackRows.ts +3 -0
- package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/mapAdminChatTaskSqlRows.ts +2 -0
- package/apps/agents-server/src/utils/localChatRunner/parseLocalTeamConversations.ts +281 -0
- package/apps/agents-server/src/utils/localChatRunner/persistLocalTeamConversations.ts +71 -0
- package/apps/agents-server/src/utils/localChatRunner/prepareLocalTeamConversationWorkspace.ts +233 -0
- package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +37 -6
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/insertManagedServerRegistryRow.ts +2 -2
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerCoreAgents.ts +7 -6
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +5 -4
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerMetadata.ts +3 -2
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerUsers.ts +3 -2
- package/apps/agents-server/src/utils/serverManagement/deleteManagedServer.ts +2 -2
- package/apps/agents-server/src/utils/stalwart/createEmailDnsRecordsSection.ts +53 -0
- package/apps/agents-server/src/utils/userChat/UserChatRecord.ts +13 -0
- package/apps/agents-server/src/utils/userChat/UserChatSource.ts +14 -0
- package/apps/agents-server/src/utils/userChat/createUserChatSummary.ts +10 -2
- package/apps/agents-server/src/utils/userChat/finalizeUserChatJob.ts +3 -2
- package/apps/agents-server/src/utils/userChat/getUserChat.ts +16 -1
- package/apps/agents-server/src/utils/userChat/getUserChatForJobRunner.ts +31 -0
- package/apps/agents-server/src/utils/userChat/listUserChats.ts +113 -110
- package/apps/agents-server/src/utils/userChat/persistFrozenUserChat.ts +21 -6
- package/apps/agents-server/src/utils/userChat/runImmediateUserChatAnswer.ts +2 -6
- package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +2 -6
- package/apps/agents-server/src/utils/userChat/teamMemberUserChatContext.ts +63 -0
- package/apps/agents-server/src/utils/userChat.ts +5 -1
- package/apps/agents-server/src/utils/userChatClient/cancelAgentUserTimeout.ts +2 -2
- package/apps/agents-server/src/utils/userChatClient/fetchAgentUserTimeouts.ts +2 -2
- package/apps/agents-server/src/utils/userChatClient.ts +9 -31
- package/apps/agents-server/src/utils/userChatTimeout/UserChatTimeoutRecord.ts +10 -2
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/claimNextDueUserChatTimeout.ts +28 -25
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/getAgentScopedUserChatTimeout.ts +8 -4
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/listAgentUserChatTimeouts.ts +4 -1
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/recoverExpiredRunningUserChatTimeouts.ts +3 -2
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutWorker.ts +53 -0
- package/esm/index.es.js +1801 -474
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +7 -1
- package/esm/scripts/run-agent-messages/messages/buildAgentTeamPromptSection.d.ts +12 -0
- package/esm/scripts/run-agent-messages/messages/finalizeAgentTeamConversationWorkspace.d.ts +16 -0
- package/esm/scripts/run-agent-messages/messages/loadAgentTeamConversationWorkspace.d.ts +6 -0
- package/esm/scripts/run-codex-prompts/common/createCoderRunStepTracker.d.ts +48 -0
- package/esm/scripts/run-codex-prompts/common/normalizeLineEndingsInChangedFiles.d.ts +2 -3
- package/esm/scripts/run-codex-prompts/git/coderCommitScope.d.ts +31 -0
- package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +15 -2
- package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +9 -3
- package/esm/scripts/run-codex-prompts/git/workingTreeChanges.d.ts +40 -0
- package/esm/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +43 -0
- package/esm/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +6 -3
- package/esm/scripts/run-codex-prompts/prompts/isPromptSectionUnfinished.d.ts +8 -0
- package/esm/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/prompts/markPromptInProgress.d.ts +27 -0
- package/esm/scripts/run-codex-prompts/prompts/resolvePromptStatusLine.d.ts +19 -0
- package/esm/scripts/run-codex-prompts/prompts/writePromptStatusLine.d.ts +9 -0
- package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +6 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +5 -5
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiTerminalFrameUpdate.d.ts +27 -0
- package/esm/scripts/verify-prompts/$orderPromptFiles.d.ts +9 -0
- package/esm/scripts/verify-prompts/VerifyPromptsOrder.d.ts +26 -0
- package/esm/scripts/verify-prompts/verify-prompts.d.ts +3 -2
- package/esm/src/avatars/renderAvatarVisualTerminalText.d.ts +80 -0
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +54 -0
- package/esm/src/book-3.0/AgentTeamConversationWorkspace.d.ts +85 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +3 -2
- package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +5 -3
- package/esm/src/cli/cli-commands/coder/boilerplateCount.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/verify.d.ts +1 -1
- package/{umd/src/cli/cli-commands/common/harness/$askForHarnessInstallationApproval.d.ts → esm/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts} +2 -2
- package/{umd/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.d.ts → esm/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.d.ts} +2 -3
- package/esm/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.d.ts +12 -0
- package/esm/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.d.ts +9 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +9 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +14 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +12 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +10 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.d.ts +39 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.d.ts +26 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.d.ts +7 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.d.ts +7 -0
- package/esm/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.d.ts +3 -3
- package/esm/src/utils/agents/terminalAgentAvatarVisual.d.ts +5 -2
- package/esm/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +6 -0
- package/esm/src/utils/ascii-art/createAnsiColorCode.d.ts +46 -0
- package/esm/src/utils/misc/debounce.d.ts +4 -2
- package/esm/src/utils/misc/debounce.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/avatars/renderAvatarVisualTerminalText.ts +249 -0
- package/src/avatars/types/AvatarVisualDefinition.ts +60 -0
- package/src/avatars/visuals/asciiOctopusAvatarVisual.ts +144 -22
- package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +4 -4
- package/src/book-2.0/book-language-documentation/renderCommitmentCatalogSection.ts +1 -4
- package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +4 -17
- package/src/book-3.0/AgentTeamConversationWorkspace.ts +172 -0
- package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +56 -55
- package/src/book-components/Chat/Chat/Chat.module.css +1 -8
- package/src/book-components/Chat/Chat/ChatMessageMap.tsx +8 -4
- package/src/book-components/Chat/Chat/ChatProps.tsx +3 -2
- package/src/book-components/Chat/save/react/exports/chat-preview-2025-10-13 (1).jsx +95 -5
- package/src/book-components/Chat/utils/renderMarkdown.ts +306 -87
- package/src/cli/cli-commands/coder/add.ts +3 -3
- package/src/cli/cli-commands/coder/boilerplateCount.ts +1 -1
- package/src/cli/cli-commands/coder/generate-boilerplates.ts +27 -27
- package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +5 -4
- package/src/cli/cli-commands/coder/init.ts +3 -3
- package/src/cli/cli-commands/coder/run.ts +10 -2
- package/src/cli/cli-commands/coder/verify.ts +29 -5
- package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +6 -2
- package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +2 -2
- package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +4 -4
- package/src/cli/cli-commands/common/harness/$resolveInstalledHarnessVersion.ts +2 -2
- package/src/cli/cli-commands/common/{harness/$askForHarnessInstallationApproval.ts → npm/$askForNpmPackageInstallationApproval.ts} +3 -3
- package/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.ts +49 -0
- package/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.ts +25 -0
- package/src/cli/cli-commands/common/{harness/isHarnessVersionOutdated.ts → npm/isNpmPackageVersionOutdated.ts} +6 -4
- package/src/cli/cli-commands/common/projectInitialization.ts +10 -1
- package/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.ts +73 -0
- package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +94 -0
- package/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.ts +223 -0
- package/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.ts +53 -0
- package/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.ts +46 -0
- package/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.ts +32 -0
- package/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.ts +22 -0
- package/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.ts +47 -0
- package/src/commands/FORMAT/formatCommandParser.ts +2 -2
- package/src/llm-providers/openai/utils/buildToolInvocationScript.ts +21 -18
- package/src/other/templates/getTemplatesPipelineCollection.ts +813 -768
- package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +80 -27
- package/src/utils/agents/terminalAgentAvatarVisual.ts +31 -3
- package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +9 -109
- package/src/utils/ascii-art/createAnsiColorCode.ts +132 -0
- package/src/utils/misc/debounce.ts +26 -5
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/index.umd.js +1800 -473
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +7 -1
- package/umd/scripts/run-agent-messages/messages/buildAgentTeamPromptSection.d.ts +12 -0
- package/umd/scripts/run-agent-messages/messages/finalizeAgentTeamConversationWorkspace.d.ts +16 -0
- package/umd/scripts/run-agent-messages/messages/loadAgentTeamConversationWorkspace.d.ts +6 -0
- package/umd/scripts/run-codex-prompts/common/createCoderRunStepTracker.d.ts +48 -0
- package/umd/scripts/run-codex-prompts/common/normalizeLineEndingsInChangedFiles.d.ts +2 -3
- package/umd/scripts/run-codex-prompts/git/coderCommitScope.d.ts +31 -0
- package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +15 -2
- package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +9 -3
- package/umd/scripts/run-codex-prompts/git/workingTreeChanges.d.ts +40 -0
- package/umd/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +43 -0
- package/umd/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +6 -3
- package/umd/scripts/run-codex-prompts/prompts/isPromptSectionUnfinished.d.ts +8 -0
- package/umd/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/prompts/markPromptInProgress.d.ts +27 -0
- package/umd/scripts/run-codex-prompts/prompts/resolvePromptStatusLine.d.ts +19 -0
- package/umd/scripts/run-codex-prompts/prompts/writePromptStatusLine.d.ts +9 -0
- package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +6 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +5 -5
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiTerminalFrameUpdate.d.ts +27 -0
- package/umd/scripts/verify-prompts/$orderPromptFiles.d.ts +9 -0
- package/umd/scripts/verify-prompts/VerifyPromptsOrder.d.ts +26 -0
- package/umd/scripts/verify-prompts/verify-prompts.d.ts +3 -2
- package/umd/src/avatars/renderAvatarVisualTerminalText.d.ts +80 -0
- package/umd/src/avatars/renderAvatarVisualTerminalText.test.d.ts +1 -0
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +54 -0
- package/umd/src/book-3.0/AgentTeamConversationWorkspace.d.ts +85 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +3 -2
- package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +5 -3
- package/umd/src/cli/cli-commands/coder/boilerplateCount.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/verify.d.ts +1 -1
- package/{esm/src/cli/cli-commands/common/harness/$askForHarnessInstallationApproval.d.ts → umd/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts} +2 -2
- package/{esm/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.d.ts → umd/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.d.ts} +2 -3
- package/umd/src/cli/cli-commands/common/npm/$resolveLatestNpmPackageVersion.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.d.ts +12 -0
- package/umd/src/cli/cli-commands/common/npm/extractNpmPackageVersionFromOutput.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.d.ts +9 -0
- package/umd/src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +9 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +14 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +12 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +10 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallation.d.ts +39 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/PromptbookCliInstallationStatus.d.ts +26 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.d.ts +7 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/buildPromptbookCliInstallCommand.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/formatPromptbookCliInstallationWarning.d.ts +7 -0
- package/umd/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.d.ts +3 -3
- package/umd/src/utils/agents/terminalAgentAvatarVisual.d.ts +5 -2
- package/umd/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +6 -0
- package/umd/src/utils/ascii-art/createAnsiColorCode.d.ts +46 -0
- package/umd/src/utils/misc/debounce.d.ts +4 -2
- package/umd/src/utils/misc/debounce.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
- package/apps/agents-server/scripts/ignore-kill-eperm.js +0 -31
- package/apps/agents-server/src/app/agents/[agentName]/api/timeouts/actions/route.ts +0 -103
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +0 -43
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsEditDialog.tsx +0 -98
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +0 -75
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsHeader.tsx +0 -53
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsSummaryMetrics.tsx +0 -70
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +0 -58
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +0 -242
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/loading.tsx +0 -15
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/page.tsx +0 -28
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/useAgentTimeoutsClientState.ts +0 -769
- package/apps/agents-server/src/components/AgentProjectDnsInstructions/AgentProjectDnsInstructions.tsx +0 -182
- package/apps/agents-server/src/utils/userChatClient/runAgentUserTimeoutBulkAction.ts +0 -24
- package/apps/agents-server/src/utils/userChatClient/updateAgentUserTimeout.ts +0 -25
- package/esm/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +0 -7
- package/esm/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.d.ts +0 -11
- package/esm/src/cli/cli-commands/common/harness/isHarnessVersionOutdated.d.ts +0 -9
- package/src/cli/cli-commands/common/harness/$resolveLatestHarnessVersion.ts +0 -34
- package/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.ts +0 -28
- package/umd/scripts/run-codex-prompts/prompts/replacePromptTodoStatusLine.d.ts +0 -7
- package/umd/src/cli/cli-commands/common/harness/extractHarnessVersionFromOutput.d.ts +0 -11
- package/umd/src/cli/cli-commands/common/harness/isHarnessVersionOutdated.d.ts +0 -9
- /package/esm/src/{cli/cli-commands/common/harness/extractHarnessVersionFromOutput.test.d.ts → avatars/renderAvatarVisualTerminalText.test.d.ts} +0 -0
- /package/esm/src/cli/cli-commands/common/{harness/isHarnessVersionOutdated.test.d.ts → npm/$resolveLatestNpmPackageVersion.test.d.ts} +0 -0
- /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
- /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-
|
|
61
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-6';
|
|
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 || '',
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
*
|
|
27403
|
+
* ANSI escape sequence that resets all colors and attributes.
|
|
27317
27404
|
*
|
|
27318
27405
|
* @private within the repository
|
|
27319
27406
|
*/
|
|
27320
|
-
const
|
|
27407
|
+
const ANSI_RESET = '\u001b[0m';
|
|
27321
27408
|
/**
|
|
27322
|
-
*
|
|
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
|
|
27413
|
+
const ANSI_256_ACHROMATIC_CHANNEL_SPREAD = 12;
|
|
27327
27414
|
/**
|
|
27328
|
-
*
|
|
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
|
|
27419
|
+
const ANSI_256_NEAR_WHITE_GRAY_LEVEL = 246;
|
|
27333
27420
|
/**
|
|
27334
|
-
*
|
|
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
|
|
27425
|
+
const ANSI_256_WHITE_INDEX = 231;
|
|
27339
27426
|
/**
|
|
27340
|
-
*
|
|
27427
|
+
* Brightness of the lightest entry of the ANSI 256 grayscale ramp.
|
|
27341
27428
|
*
|
|
27342
27429
|
* @private within the repository
|
|
27343
27430
|
*/
|
|
27344
|
-
const
|
|
27431
|
+
const ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL = 238;
|
|
27345
27432
|
/**
|
|
27346
|
-
*
|
|
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
|
|
27437
|
+
const ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN = 23;
|
|
27351
27438
|
/**
|
|
27352
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
27501
|
+
const DEFAULT_ALPHA_THRESHOLD = 32;
|
|
27369
27502
|
/**
|
|
27370
|
-
* Number of
|
|
27503
|
+
* Number of channels per pixel in an RGBA buffer.
|
|
27371
27504
|
*
|
|
27372
27505
|
* @private within the repository
|
|
27373
27506
|
*/
|
|
27374
|
-
const
|
|
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 =
|
|
27424
|
-
nextBackgroundCode =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
28637
|
-
|
|
28638
|
-
|
|
28639
|
-
|
|
28640
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
29106
|
-
if (!
|
|
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
|
-
*
|
|
29551
|
+
* Checks whether a path exists and is a regular file.
|
|
29168
29552
|
*/
|
|
29169
|
-
async function
|
|
29553
|
+
async function isRegularFile(path) {
|
|
29170
29554
|
try {
|
|
29171
|
-
|
|
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 (
|
|
29179
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
29425
|
-
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
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
|
|
29815
|
+
* Builds the git add command for either the whole tree or the relevant paths of the current operation.
|
|
29443
29816
|
*/
|
|
29444
|
-
function buildGitAddCommand(
|
|
29445
|
-
if (!
|
|
29817
|
+
function buildGitAddCommand(relevantPaths) {
|
|
29818
|
+
if (!relevantPaths || relevantPaths.length === 0) {
|
|
29446
29819
|
return 'git add .';
|
|
29447
29820
|
}
|
|
29448
|
-
return `git add --all -- ${
|
|
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.
|
|
29620
|
-
commandParts.push('--', ...options.
|
|
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
|
}
|
|
@@ -29758,6 +30147,32 @@
|
|
|
29758
30147
|
`);
|
|
29759
30148
|
}
|
|
29760
30149
|
|
|
30150
|
+
/**
|
|
30151
|
+
* Creates the step tracker of one prompt round.
|
|
30152
|
+
*
|
|
30153
|
+
* The tracker is the single place which knows which steps have finished and which one is running,
|
|
30154
|
+
* so both the finished `[x]` status line and the intermediate `[^]` in-progress status lines are
|
|
30155
|
+
* built from the very same data.
|
|
30156
|
+
*/
|
|
30157
|
+
function createCoderRunStepTracker(onStepStarted) {
|
|
30158
|
+
const steps = [];
|
|
30159
|
+
let reportedLoginMethod;
|
|
30160
|
+
return {
|
|
30161
|
+
steps,
|
|
30162
|
+
async startStep(kind) {
|
|
30163
|
+
await (onStepStarted === null || onStepStarted === void 0 ? void 0 : onStepStarted({
|
|
30164
|
+
startedStepKind: kind,
|
|
30165
|
+
finishedSteps: [...steps],
|
|
30166
|
+
loginMethod: reportedLoginMethod,
|
|
30167
|
+
}));
|
|
30168
|
+
},
|
|
30169
|
+
finishStep(step, loginMethod) {
|
|
30170
|
+
steps.push(step);
|
|
30171
|
+
reportedLoginMethod = loginMethod !== null && loginMethod !== void 0 ? loginMethod : reportedLoginMethod;
|
|
30172
|
+
},
|
|
30173
|
+
};
|
|
30174
|
+
}
|
|
30175
|
+
|
|
29761
30176
|
/**
|
|
29762
30177
|
* Maximum amount of test output embedded into an agent prompt.
|
|
29763
30178
|
*/
|
|
@@ -29803,7 +30218,7 @@
|
|
|
29803
30218
|
async function runPromptWithTestFeedback(options) {
|
|
29804
30219
|
var _a, _b, _c, _d;
|
|
29805
30220
|
const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
|
|
29806
|
-
const
|
|
30221
|
+
const stepTracker = createCoderRunStepTracker(options.onStepStarted);
|
|
29807
30222
|
if (!normalizedTestCommand) {
|
|
29808
30223
|
(_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
|
|
29809
30224
|
await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, 1);
|
|
@@ -29811,9 +30226,9 @@
|
|
|
29811
30226
|
runOptions: options,
|
|
29812
30227
|
prompt: options.prompt,
|
|
29813
30228
|
kind: 'implementation',
|
|
29814
|
-
|
|
30229
|
+
stepTracker,
|
|
29815
30230
|
});
|
|
29816
|
-
return { ...result, attemptCount: 1, steps };
|
|
30231
|
+
return { ...result, attemptCount: 1, steps: stepTracker.steps };
|
|
29817
30232
|
}
|
|
29818
30233
|
const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
|
|
29819
30234
|
let promptForCurrentAttempt = options.prompt;
|
|
@@ -29824,7 +30239,7 @@
|
|
|
29824
30239
|
runOptions: options,
|
|
29825
30240
|
prompt: promptForCurrentAttempt,
|
|
29826
30241
|
kind: attemptCount === 1 ? 'implementation' : 'fixing',
|
|
29827
|
-
|
|
30242
|
+
stepTracker,
|
|
29828
30243
|
});
|
|
29829
30244
|
await waitForVerificationPauseCheckpoint(options.waitForPauseCheckpoint, normalizedTestCommand, attemptCount);
|
|
29830
30245
|
console.info(colors__default["default"].gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
|
|
@@ -29832,10 +30247,10 @@
|
|
|
29832
30247
|
runPromptTestCommandExecutor,
|
|
29833
30248
|
testCommand: normalizedTestCommand,
|
|
29834
30249
|
runOptions: options,
|
|
29835
|
-
|
|
30250
|
+
stepTracker,
|
|
29836
30251
|
});
|
|
29837
30252
|
if (failedVerification === undefined) {
|
|
29838
|
-
return { ...result, attemptCount, steps };
|
|
30253
|
+
return { ...result, attemptCount, steps: stepTracker.steps };
|
|
29839
30254
|
}
|
|
29840
30255
|
const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
|
|
29841
30256
|
const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
|
|
@@ -29861,7 +30276,8 @@
|
|
|
29861
30276
|
* Runs one coding attempt through the runner, timing it and recording it as an implementation or fixing step.
|
|
29862
30277
|
*/
|
|
29863
30278
|
async function runRunnerPromptStep(options) {
|
|
29864
|
-
const { runOptions, prompt, kind,
|
|
30279
|
+
const { runOptions, prompt, kind, stepTracker } = options;
|
|
30280
|
+
await stepTracker.startStep(kind);
|
|
29865
30281
|
const stepStartedTimeMs = Date.now();
|
|
29866
30282
|
const result = await runOptions.runner.runPrompt({
|
|
29867
30283
|
prompt,
|
|
@@ -29871,7 +30287,7 @@
|
|
|
29871
30287
|
preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
|
|
29872
30288
|
waitForPauseCheckpoint: runOptions.waitForPauseCheckpoint,
|
|
29873
30289
|
});
|
|
29874
|
-
|
|
30290
|
+
stepTracker.finishStep({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs }, result.loginMethod);
|
|
29875
30291
|
return result;
|
|
29876
30292
|
}
|
|
29877
30293
|
/**
|
|
@@ -29879,7 +30295,8 @@
|
|
|
29879
30295
|
* returns the failure (or `undefined` when the verification passed).
|
|
29880
30296
|
*/
|
|
29881
30297
|
async function runVerificationStep(options) {
|
|
29882
|
-
const { runPromptTestCommandExecutor, testCommand, runOptions,
|
|
30298
|
+
const { runPromptTestCommandExecutor, testCommand, runOptions, stepTracker } = options;
|
|
30299
|
+
await stepTracker.startStep('testing');
|
|
29883
30300
|
const stepStartedTimeMs = Date.now();
|
|
29884
30301
|
try {
|
|
29885
30302
|
await runPromptTestCommandExecutor({
|
|
@@ -29895,7 +30312,7 @@
|
|
|
29895
30312
|
return { error };
|
|
29896
30313
|
}
|
|
29897
30314
|
finally {
|
|
29898
|
-
|
|
30315
|
+
stepTracker.finishStep({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
|
|
29899
30316
|
}
|
|
29900
30317
|
}
|
|
29901
30318
|
/**
|
|
@@ -30079,6 +30496,140 @@
|
|
|
30079
30496
|
`);
|
|
30080
30497
|
}
|
|
30081
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
|
+
|
|
30082
30633
|
/**
|
|
30083
30634
|
* Builds the prompt sent to the selected coding runner for one queued user-thread book.
|
|
30084
30635
|
*/
|
|
@@ -30088,7 +30639,7 @@
|
|
|
30088
30639
|
|
|
30089
30640
|
- Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE @User\`
|
|
30090
30641
|
- Only change the queued message file by appending one new \`MESSAGE @Agent\` block
|
|
30091
|
-
|
|
30642
|
+
${block(buildAllowedFileChangesPromptLine(options.teamWorkspace))}
|
|
30092
30643
|
|
|
30093
30644
|
## Rules for the answering
|
|
30094
30645
|
|
|
@@ -30126,11 +30677,22 @@
|
|
|
30126
30677
|
|
|
30127
30678
|
${block(buildAgentProjectsPromptSection(options))}
|
|
30128
30679
|
|
|
30680
|
+
${block(buildAgentTeamPromptSection(options.teamWorkspace))}
|
|
30681
|
+
|
|
30129
30682
|
## This is how you should behave
|
|
30130
30683
|
|
|
30131
30684
|
${block(agentSystemMessage)}
|
|
30132
30685
|
`);
|
|
30133
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
|
+
}
|
|
30134
30696
|
|
|
30135
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"}];
|
|
30136
30698
|
|
|
@@ -36830,6 +37392,148 @@
|
|
|
36830
37392
|
.join('\n\n');
|
|
36831
37393
|
}
|
|
36832
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
|
+
|
|
36833
37537
|
/**
|
|
36834
37538
|
* Resolves project-runtime prompt API details for one local agent runner folder.
|
|
36835
37539
|
*
|
|
@@ -37146,11 +37850,13 @@
|
|
|
37146
37850
|
* Runs the selected coding runner for one message and finalizes the answered file.
|
|
37147
37851
|
*/
|
|
37148
37852
|
async function runQueuedAgentMessage(options) {
|
|
37149
|
-
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard } = options;
|
|
37853
|
+
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard, } = options;
|
|
37150
37854
|
const agentSystemMessage = await loadLocalAgentSystemMessage(projectPath);
|
|
37855
|
+
const teamWorkspace = await loadAgentTeamConversationWorkspace(projectPath, queuedMessage);
|
|
37151
37856
|
const prompt = buildAgentMessagePrompt(queuedMessage.relativePath, agentSystemMessage, {
|
|
37152
37857
|
projectRuntimeApi: resolveAgentProjectRuntimePromptApi(projectPath),
|
|
37153
37858
|
projectsUrlPath: resolveAgentProjectsUrlPath(projectPath),
|
|
37859
|
+
teamWorkspace: teamWorkspace || undefined,
|
|
37154
37860
|
});
|
|
37155
37861
|
const scriptPath = buildAgentMessageScriptPath(projectPath, queuedMessage);
|
|
37156
37862
|
const runtimeLogPath = buildScriptLogPath(scriptPath);
|
|
@@ -37198,6 +37904,13 @@
|
|
|
37198
37904
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
37199
37905
|
}
|
|
37200
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.
|
|
37201
37914
|
const finishedMessage = await moveAgentMessageToFinished(projectPath, queuedMessage);
|
|
37202
37915
|
const writtenRunReport = await writeAgentMessageRunReport({
|
|
37203
37916
|
finishedMessageAbsolutePath: finishedMessage.absolutePath,
|
|
@@ -37214,6 +37927,7 @@
|
|
|
37214
37927
|
options: runOptions,
|
|
37215
37928
|
queuedMessage,
|
|
37216
37929
|
finishedMessage,
|
|
37930
|
+
finishedTeamWorkspace,
|
|
37217
37931
|
writtenRunReport,
|
|
37218
37932
|
isQueuedMessageTracked,
|
|
37219
37933
|
uiHandle,
|
|
@@ -37285,7 +37999,7 @@
|
|
|
37285
37999
|
* Commits the answered message move unless `--no-commit` was requested.
|
|
37286
38000
|
*/
|
|
37287
38001
|
async function commitAnsweredMessageIfEnabled(options) {
|
|
37288
|
-
const { options: runOptions, queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
|
|
38002
|
+
const { options: runOptions, queuedMessage, finishedMessage, finishedTeamWorkspace, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
|
|
37289
38003
|
if (runOptions.noCommit) {
|
|
37290
38004
|
if (!isSharedDashboard) {
|
|
37291
38005
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Leaving changes uncommitted');
|
|
@@ -37297,21 +38011,24 @@
|
|
|
37297
38011
|
}
|
|
37298
38012
|
await commitChanges(buildAgentMessageCommitMessage(queuedMessage), {
|
|
37299
38013
|
autoPush: runOptions.autoPush,
|
|
37300
|
-
|
|
38014
|
+
relevantPaths: buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace),
|
|
37301
38015
|
projectPath,
|
|
37302
38016
|
});
|
|
37303
38017
|
}
|
|
37304
38018
|
/**
|
|
37305
38019
|
* Builds the focused git path list for the answered-message commit.
|
|
37306
38020
|
*/
|
|
37307
|
-
function
|
|
37308
|
-
const
|
|
38021
|
+
function buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace) {
|
|
38022
|
+
const relevantPaths = isQueuedMessageTracked
|
|
37309
38023
|
? [queuedMessage.relativePath, finishedMessage.relativePath]
|
|
37310
38024
|
: [finishedMessage.relativePath];
|
|
37311
38025
|
if (writtenRunReport) {
|
|
37312
|
-
|
|
38026
|
+
relevantPaths.push(writtenRunReport.relativePath);
|
|
37313
38027
|
}
|
|
37314
|
-
|
|
38028
|
+
if (finishedTeamWorkspace) {
|
|
38029
|
+
relevantPaths.push(...finishedTeamWorkspace.relativePaths);
|
|
38030
|
+
}
|
|
38031
|
+
return relevantPaths;
|
|
37315
38032
|
}
|
|
37316
38033
|
/**
|
|
37317
38034
|
* Normalizes line endings in files changed during the current agent round.
|
|
@@ -38823,7 +39540,7 @@
|
|
|
38823
39540
|
const CODER_GIT_SYNC_DESCRIPTION = _spaceTrim.spaceTrim(`
|
|
38824
39541
|
Git synchronization:
|
|
38825
39542
|
- --auto-pull pulls the latest changes before this command changes anything
|
|
38826
|
-
- --commit commits the changes
|
|
39543
|
+
- --commit commits only the files this command has changed, unrelated changes stay in the working tree
|
|
38827
39544
|
- --auto-push pushes the created commit to the remote repository
|
|
38828
39545
|
`);
|
|
38829
39546
|
/**
|
|
@@ -38835,7 +39552,7 @@
|
|
|
38835
39552
|
* @private internal utility of `promptbookCli`
|
|
38836
39553
|
*/
|
|
38837
39554
|
function addCoderGitSyncOptions(command) {
|
|
38838
|
-
command.option('--commit', 'Commit the
|
|
39555
|
+
command.option('--commit', 'Commit the files changed by this command with the coding-agent git identity, leaving unrelated changes uncommitted', false);
|
|
38839
39556
|
command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
|
|
38840
39557
|
command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
|
|
38841
39558
|
}
|
|
@@ -39144,8 +39861,8 @@
|
|
|
39144
39861
|
const projectPath = process.cwd();
|
|
39145
39862
|
const description = await resolveCoderPromptDescription(descriptionArgument);
|
|
39146
39863
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
39147
|
-
const { $commitCoderChanges, $
|
|
39148
|
-
await $
|
|
39864
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
39865
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
39149
39866
|
const { /* filePath,*/ emojiTag } = await addCoderPrompt({
|
|
39150
39867
|
projectPath,
|
|
39151
39868
|
description,
|
|
@@ -39154,7 +39871,7 @@
|
|
|
39154
39871
|
});
|
|
39155
39872
|
await $commitCoderChanges({
|
|
39156
39873
|
gitSync,
|
|
39157
|
-
|
|
39874
|
+
commitScope,
|
|
39158
39875
|
commitMessage: `${emojiTag} Add prompt`,
|
|
39159
39876
|
});
|
|
39160
39877
|
}));
|
|
@@ -39748,7 +40465,7 @@
|
|
|
39748
40465
|
function $initializeCoderGenerateBoilerplatesCommand(program) {
|
|
39749
40466
|
const command = program.command('generate-boilerplates');
|
|
39750
40467
|
command.description(_spaceTrim.spaceTrim((block) => `
|
|
39751
|
-
Generate prompt boilerplate files with unique emoji
|
|
40468
|
+
Generate prompt boilerplate files with one unique emoji tag per file
|
|
39752
40469
|
|
|
39753
40470
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
39754
40471
|
`));
|
|
@@ -39767,8 +40484,8 @@
|
|
|
39767
40484
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
39768
40485
|
const projectPath = process.cwd();
|
|
39769
40486
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
39770
|
-
const { $commitCoderChanges, $
|
|
39771
|
-
await $
|
|
40487
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
40488
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
39772
40489
|
await generatePromptBoilerplate({
|
|
39773
40490
|
projectPath,
|
|
39774
40491
|
boilerplateCount,
|
|
@@ -39776,14 +40493,14 @@
|
|
|
39776
40493
|
});
|
|
39777
40494
|
await $commitCoderChanges({
|
|
39778
40495
|
gitSync,
|
|
39779
|
-
|
|
40496
|
+
commitScope,
|
|
39780
40497
|
commitMessage: `Prompts ${formatBoilerplateCount(boilerplateCount)}`,
|
|
39781
40498
|
});
|
|
39782
40499
|
return process.exit(0);
|
|
39783
40500
|
}));
|
|
39784
40501
|
}
|
|
39785
40502
|
/**
|
|
39786
|
-
* Generates boilerplate prompt files with unique emoji
|
|
40503
|
+
* Generates boilerplate prompt files with one unique emoji tag per file.
|
|
39787
40504
|
*
|
|
39788
40505
|
* @private internal function of `generatePromptBoilerplate` command
|
|
39789
40506
|
*/
|
|
@@ -39803,10 +40520,10 @@
|
|
|
39803
40520
|
const highestNumber = promptNumbering.startNumber === 0 ? 0 : promptNumbering.startNumber - promptNumbering.step;
|
|
39804
40521
|
const highestNumberFormatted = Math.max(0, highestNumber).toString().padStart(4, '0');
|
|
39805
40522
|
console.info(colors__default["default"].blue(`Highest existing number for ${promptNumbering.datePrefix} found: ${highestNumberFormatted}`));
|
|
39806
|
-
// Note: Every single generated prompt is one separate coding task, so each of them reserves its own fresh emoji tag
|
|
39807
40523
|
const promptsCount = filesCount * promptsPerFileCount;
|
|
39808
40524
|
const { availableCount, selectedEmojis } = await getFreshPromptEmojiTags({
|
|
39809
|
-
|
|
40525
|
+
// Note: Each generated file reserves one fresh emoji tag shared by all of its prompt sections
|
|
40526
|
+
count: filesCount,
|
|
39810
40527
|
rootDir: projectPath,
|
|
39811
40528
|
});
|
|
39812
40529
|
console.info(colors__default["default"].green(`Found ${availableCount} available fresh emojis`));
|
|
@@ -39818,14 +40535,13 @@
|
|
|
39818
40535
|
for (let i = 0; i < filesCount; i++) {
|
|
39819
40536
|
const number = promptNumbering.startNumber + i * promptNumbering.step;
|
|
39820
40537
|
const title = titles[i % titles.length];
|
|
39821
|
-
const
|
|
39822
|
-
.slice(i * promptsPerFileCount, (i + 1) * promptsPerFileCount)
|
|
39823
|
-
.map((emoji) => formatPromptEmojiTag(emoji));
|
|
40538
|
+
const emojiTag = formatPromptEmojiTag(selectedEmojis[i]);
|
|
39824
40539
|
const filename = buildPromptFilename(promptNumbering.datePrefix, number, buildPromptSlug$1(promptTemplate.slugPrefix, title));
|
|
39825
40540
|
const filepath = path.join(PROMPTS_DIRECTORY_PATH, filename);
|
|
39826
40541
|
const absoluteFilepath = path.join(projectPath, filepath);
|
|
39827
40542
|
const content = buildBoilerplatePromptFileContent({
|
|
39828
|
-
|
|
40543
|
+
emojiTag,
|
|
40544
|
+
promptsPerFileCount,
|
|
39829
40545
|
title,
|
|
39830
40546
|
body: promptTemplate.content,
|
|
39831
40547
|
});
|
|
@@ -39834,7 +40550,7 @@
|
|
|
39834
40550
|
absoluteFilepath,
|
|
39835
40551
|
filename,
|
|
39836
40552
|
content,
|
|
39837
|
-
|
|
40553
|
+
emojiTag,
|
|
39838
40554
|
number,
|
|
39839
40555
|
});
|
|
39840
40556
|
}
|
|
@@ -39842,26 +40558,25 @@
|
|
|
39842
40558
|
console.info(colors__default["default"].yellow(`Creating ${filesToCreate.length} files:`));
|
|
39843
40559
|
for (const file of filesToCreate) {
|
|
39844
40560
|
fs.writeFileSync(file.absoluteFilepath, file.content, 'utf-8');
|
|
39845
|
-
console.info(colors__default["default"].green(`✓ Created: ${file.filename} with ${file.
|
|
40561
|
+
console.info(colors__default["default"].green(`✓ Created: ${file.filename} with ${file.emojiTag}`));
|
|
39846
40562
|
}
|
|
39847
40563
|
console.info(colors__default["default"].bgGreen(` Successfully created ${promptsCount} prompts in ${filesToCreate.length} prompt boilerplate files! `));
|
|
39848
40564
|
}
|
|
39849
40565
|
/**
|
|
39850
|
-
* Builds the markdown content of one generated prompt file with one prompt section per
|
|
40566
|
+
* Builds the markdown content of one generated prompt file with one prompt section per prompt.
|
|
39851
40567
|
*
|
|
39852
|
-
* Multiple prompts in one file are separated by the `---` separator,
|
|
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.
|
|
39853
40570
|
*
|
|
39854
40571
|
* @private internal utility of `generatePromptBoilerplate` command
|
|
39855
40572
|
*/
|
|
39856
|
-
function buildBoilerplatePromptFileContent({
|
|
39857
|
-
return
|
|
39858
|
-
.map((emojiTag) => buildCoderPromptSection({
|
|
40573
|
+
function buildBoilerplatePromptFileContent({ emojiTag, promptsPerFileCount, title, body, }) {
|
|
40574
|
+
return Array.from({ length: promptsPerFileCount }, () => buildCoderPromptSection({
|
|
39859
40575
|
statusLine: '[-]',
|
|
39860
40576
|
emojiTag,
|
|
39861
40577
|
title,
|
|
39862
40578
|
body,
|
|
39863
|
-
}))
|
|
39864
|
-
.join('\n\n---\n\n');
|
|
40579
|
+
})).join('\n\n---\n\n');
|
|
39865
40580
|
}
|
|
39866
40581
|
/**
|
|
39867
40582
|
* Builds filename slug from template and placeholder title.
|
|
@@ -39878,14 +40593,14 @@
|
|
|
39878
40593
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
39879
40594
|
|
|
39880
40595
|
/**
|
|
39881
|
-
* Asks the user in the terminal whether
|
|
40596
|
+
* Asks the user in the terminal whether an npm package should be installed or updated now.
|
|
39882
40597
|
*
|
|
39883
40598
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
|
|
39884
40599
|
*
|
|
39885
40600
|
* @returns `true` when the user confirms, `false` when the user declines or the terminal is not interactive
|
|
39886
40601
|
* @private internal utility of `promptbookCli`
|
|
39887
40602
|
*/
|
|
39888
|
-
async function $
|
|
40603
|
+
async function $askForNpmPackageInstallationApproval(question) {
|
|
39889
40604
|
if (!process.stdin.isTTY) {
|
|
39890
40605
|
// Note: In non-interactive environments like CI there is nobody who could confirm the installation
|
|
39891
40606
|
return false;
|
|
@@ -39901,7 +40616,7 @@
|
|
|
39901
40616
|
readlineInterface.close();
|
|
39902
40617
|
}
|
|
39903
40618
|
}
|
|
39904
|
-
// Note: [🟡] Code for 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`
|
|
39905
40620
|
|
|
39906
40621
|
/**
|
|
39907
40622
|
* Builds the shell command which installs or updates one CLI coding harness globally.
|
|
@@ -39996,7 +40711,7 @@
|
|
|
39996
40711
|
return;
|
|
39997
40712
|
}
|
|
39998
40713
|
console.warn(colors__default["default"].yellow(formatHarnessInstallationWarning(status)));
|
|
39999
|
-
const isInstallationApproved = await $
|
|
40714
|
+
const isInstallationApproved = await $askForNpmPackageInstallationApproval(installationState === 'not-installed'
|
|
40000
40715
|
? `Install ${definition.label} globally now?`
|
|
40001
40716
|
: `Update ${definition.label} to ${latestVersion} now?`);
|
|
40002
40717
|
if (!isInstallationApproved) {
|
|
@@ -40008,30 +40723,25 @@
|
|
|
40008
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`
|
|
40009
40724
|
|
|
40010
40725
|
/**
|
|
40011
|
-
* Pattern matching one `major.minor.patch` version anywhere in
|
|
40726
|
+
* Pattern matching one `major.minor.patch` version anywhere in npm or CLI command output.
|
|
40012
40727
|
*/
|
|
40013
|
-
const
|
|
40728
|
+
const NPM_PACKAGE_VERSION_PATTERN = /\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/gu;
|
|
40014
40729
|
/**
|
|
40015
|
-
* Extracts
|
|
40730
|
+
* Extracts an npm package version from raw command output.
|
|
40016
40731
|
*
|
|
40017
|
-
*
|
|
40018
|
-
*
|
|
40019
|
-
* 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.
|
|
40020
40734
|
*
|
|
40021
40735
|
* @returns The parsed version or `null` when the output contains no version
|
|
40022
40736
|
* @private internal utility of `promptbookCli`
|
|
40023
40737
|
*/
|
|
40024
|
-
function
|
|
40738
|
+
function extractNpmPackageVersionFromOutput(output, { isLastMatchPreferred = false } = {}) {
|
|
40025
40739
|
var _a;
|
|
40026
|
-
|
|
40027
|
-
|
|
40028
|
-
|
|
40029
|
-
return (_a = versionMatch[1]) !== null && _a !== void 0 ? _a : null;
|
|
40030
|
-
}
|
|
40031
|
-
}
|
|
40032
|
-
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;
|
|
40033
40743
|
}
|
|
40034
|
-
// Note: [🟡] Code for 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`
|
|
40035
40745
|
|
|
40036
40746
|
/**
|
|
40037
40747
|
* Time limit for asking the globally installed harness command for its version.
|
|
@@ -40052,46 +40762,60 @@
|
|
|
40052
40762
|
timeout: HARNESS_VERSION_COMMAND_TIMEOUT_MS,
|
|
40053
40763
|
isVerbose: false,
|
|
40054
40764
|
}).catch(() => '');
|
|
40055
|
-
return
|
|
40765
|
+
return extractNpmPackageVersionFromOutput(output);
|
|
40056
40766
|
}
|
|
40057
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`
|
|
40058
40768
|
|
|
40059
40769
|
/**
|
|
40060
|
-
* Time limit for the whole `npm view` lookup of the newest published
|
|
40770
|
+
* Time limit for the whole `npm view` lookup of the newest published package version.
|
|
40061
40771
|
*/
|
|
40062
|
-
const
|
|
40772
|
+
const NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS = 60 * 1000;
|
|
40063
40773
|
/**
|
|
40064
40774
|
* Time limit for one npm registry request, so an unreachable registry never blocks the command.
|
|
40065
40775
|
*/
|
|
40066
40776
|
const NPM_REGISTRY_FETCH_TIMEOUT_MS = 20 * 1000;
|
|
40067
40777
|
/**
|
|
40068
|
-
*
|
|
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.
|
|
40069
40783
|
*
|
|
40070
40784
|
* Note: `$` is used to indicate that this function is not a pure function - it queries the npm registry
|
|
40071
40785
|
*
|
|
40072
40786
|
* @returns The newest published version or `null` when the registry could not be reached
|
|
40073
40787
|
* @private internal utility of `promptbookCli`
|
|
40074
40788
|
*/
|
|
40075
|
-
async function $
|
|
40789
|
+
async function $resolveLatestNpmPackageVersion(npmPackageName) {
|
|
40076
40790
|
const output = await $execCommand({
|
|
40077
|
-
command: `npm view ${
|
|
40791
|
+
command: `npm view ${npmPackageName} version --json --loglevel=error --fetch-timeout=${NPM_REGISTRY_FETCH_TIMEOUT_MS} --fetch-retries=1`,
|
|
40078
40792
|
crashOnError: true,
|
|
40079
|
-
timeout:
|
|
40793
|
+
timeout: NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS,
|
|
40080
40794
|
isVerbose: false,
|
|
40081
40795
|
}).catch(() => '');
|
|
40082
|
-
return
|
|
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 });
|
|
40083
40807
|
}
|
|
40084
|
-
// Note: [🟡] Code for 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`
|
|
40085
40809
|
|
|
40086
40810
|
/**
|
|
40087
|
-
* Compares
|
|
40811
|
+
* Compares an installed npm package version with the newest published version.
|
|
40088
40812
|
*
|
|
40089
40813
|
* Only the numeric `major.minor.patch` segments are compared and pre-release suffixes are ignored,
|
|
40090
|
-
* so a
|
|
40814
|
+
* so a package installed from a pre-release build of the newest version is not reported as outdated.
|
|
40091
40815
|
*
|
|
40092
40816
|
* @private internal utility of `promptbookCli`
|
|
40093
40817
|
*/
|
|
40094
|
-
function
|
|
40818
|
+
function isNpmPackageVersionOutdated(installedVersion, latestVersion) {
|
|
40095
40819
|
var _a, _b;
|
|
40096
40820
|
const installedSegments = parseVersionSegments(installedVersion);
|
|
40097
40821
|
const latestSegments = parseVersionSegments(latestVersion);
|
|
@@ -40107,6 +40831,8 @@
|
|
|
40107
40831
|
}
|
|
40108
40832
|
/**
|
|
40109
40833
|
* Splits one version into its numeric segments, dropping the pre-release and build suffix.
|
|
40834
|
+
*
|
|
40835
|
+
* @private internal utility of `isNpmPackageVersionOutdated`
|
|
40110
40836
|
*/
|
|
40111
40837
|
function parseVersionSegments(version) {
|
|
40112
40838
|
const [numericVersion = ''] = version.trim().split(/[-+]/u);
|
|
@@ -40115,7 +40841,7 @@
|
|
|
40115
40841
|
return Number.isNaN(segmentNumber) ? 0 : segmentNumber;
|
|
40116
40842
|
});
|
|
40117
40843
|
}
|
|
40118
|
-
// Note: [🟡] Code for 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`
|
|
40119
40845
|
|
|
40120
40846
|
/**
|
|
40121
40847
|
* Detects whether one CLI coding harness is installed globally and whether it is up to date.
|
|
@@ -40127,7 +40853,7 @@
|
|
|
40127
40853
|
async function $checkHarnessInstallation(definition) {
|
|
40128
40854
|
const [installedVersion, latestVersion] = await Promise.all([
|
|
40129
40855
|
$resolveInstalledHarnessVersion(definition),
|
|
40130
|
-
$
|
|
40856
|
+
$resolveLatestNpmPackageVersion(definition.npmPackageName),
|
|
40131
40857
|
]);
|
|
40132
40858
|
return {
|
|
40133
40859
|
definition,
|
|
@@ -40146,7 +40872,7 @@
|
|
|
40146
40872
|
if (latestVersion === null) {
|
|
40147
40873
|
return 'unknown';
|
|
40148
40874
|
}
|
|
40149
|
-
return
|
|
40875
|
+
return isNpmPackageVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
|
|
40150
40876
|
}
|
|
40151
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`
|
|
40152
40878
|
|
|
@@ -40378,11 +41104,12 @@
|
|
|
40378
41104
|
* Default npm scripts initialized by `ptbk coder init`.
|
|
40379
41105
|
*/
|
|
40380
41106
|
const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
|
|
40381
|
-
|
|
40382
|
-
'coder:
|
|
40383
|
-
'coder:
|
|
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',
|
|
40384
41111
|
// 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
|
|
40385
|
-
'coder:verify': 'ptbk coder verify',
|
|
41112
|
+
'coder:verify': 'npx ptbk coder verify',
|
|
40386
41113
|
};
|
|
40387
41114
|
/**
|
|
40388
41115
|
* Lists the default npm scripts initialized by `ptbk coder init`.
|
|
@@ -40752,14 +41479,14 @@
|
|
|
40752
41479
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
40753
41480
|
const projectPath = process.cwd();
|
|
40754
41481
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
40755
|
-
const { $commitCoderChanges, $
|
|
40756
|
-
await $
|
|
41482
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
41483
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
40757
41484
|
const summary = await initializeCoderProjectConfiguration(projectPath);
|
|
40758
41485
|
printInitializationSummary(summary);
|
|
40759
41486
|
await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
|
|
40760
41487
|
await $commitCoderChanges({
|
|
40761
41488
|
gitSync,
|
|
40762
|
-
|
|
41489
|
+
commitScope,
|
|
40763
41490
|
commitMessage: 'Initialize Promptbook Coder',
|
|
40764
41491
|
});
|
|
40765
41492
|
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
|
|
@@ -40848,6 +41575,370 @@
|
|
|
40848
41575
|
}
|
|
40849
41576
|
// Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
|
|
40850
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
|
+
|
|
40851
41942
|
/**
|
|
40852
41943
|
* Modes supported by `ptbk coder run --test-before`.
|
|
40853
41944
|
*/
|
|
@@ -40900,12 +41991,14 @@
|
|
|
40900
41991
|
|
|
40901
41992
|
Features:
|
|
40902
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
|
|
40903
41995
|
- Optional post-commit git push with explicit --auto-push opt-in
|
|
40904
41996
|
- Optional pre-prompt git pull with explicit --auto-pull opt-in
|
|
40905
41997
|
- Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
|
|
40906
41998
|
- Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
|
|
40907
41999
|
- Optional --no-ui keeps plain streaming console output for logging and debugging
|
|
40908
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
|
|
40909
42002
|
- Supports GPG signing of commits
|
|
40910
42003
|
- Optional pre-coding test run that can stop or repair pre-existing failures
|
|
40911
42004
|
- Optional post-prompt verification with test-feedback retries
|
|
@@ -40955,7 +42048,6 @@
|
|
|
40955
42048
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
40956
42049
|
isAgentRequired: !dryRun,
|
|
40957
42050
|
});
|
|
40958
|
-
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
40959
42051
|
// [1] Parse the wait options and --no-auto:
|
|
40960
42052
|
// default: run automatically through the queue (no waiting between prompts)
|
|
40961
42053
|
// --no-auto: wait for user confirmation before each prompt (interactive mode)
|
|
@@ -40963,6 +42055,10 @@
|
|
|
40963
42055
|
// --wait-between-prompts: pace from start of one prompt to start of next
|
|
40964
42056
|
// --wait-after-error: wait before retrying after an error (default 10m)
|
|
40965
42057
|
const waitForUser = !auto;
|
|
42058
|
+
if (waitForUser && (await $ensurePromptbookCliInstallations())) {
|
|
42059
|
+
return process.exit(0);
|
|
42060
|
+
}
|
|
42061
|
+
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
40966
42062
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
40967
42063
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
40968
42064
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -41182,6 +42278,45 @@
|
|
|
41182
42278
|
// Note: [🟡] Code for CLI command [server](src/cli/cli-commands/coder/server.ts) should never be published outside of `@promptbook/cli`
|
|
41183
42279
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
41184
42280
|
|
|
42281
|
+
/**
|
|
42282
|
+
* Orders supported by `ptbk coder verify --order`.
|
|
42283
|
+
*/
|
|
42284
|
+
const VERIFY_PROMPTS_ORDER_VALUES = ['from-earliest', 'from-latest', 'random'];
|
|
42285
|
+
/**
|
|
42286
|
+
* Order used when `--order` is not provided.
|
|
42287
|
+
*/
|
|
42288
|
+
const DEFAULT_VERIFY_PROMPTS_ORDER = 'from-earliest';
|
|
42289
|
+
/**
|
|
42290
|
+
* Human-readable description of each supported order, shared by the CLI help and the verification output.
|
|
42291
|
+
*/
|
|
42292
|
+
const VERIFY_PROMPTS_ORDER_DESCRIPTIONS = {
|
|
42293
|
+
'from-earliest': 'from the earliest prompt file',
|
|
42294
|
+
'from-latest': 'from the latest prompt file',
|
|
42295
|
+
random: 'in random order',
|
|
42296
|
+
};
|
|
42297
|
+
/**
|
|
42298
|
+
* Parses and validates one raw `--order` value.
|
|
42299
|
+
*
|
|
42300
|
+
* Note: `ptbk coder verify` lets Commander validate the value, this is used by the standalone script which parses the raw arguments itself
|
|
42301
|
+
*/
|
|
42302
|
+
function parseVerifyPromptsOrder(orderValue) {
|
|
42303
|
+
if (orderValue === undefined) {
|
|
42304
|
+
return DEFAULT_VERIFY_PROMPTS_ORDER;
|
|
42305
|
+
}
|
|
42306
|
+
if (VERIFY_PROMPTS_ORDER_VALUES.includes(orderValue)) {
|
|
42307
|
+
return orderValue;
|
|
42308
|
+
}
|
|
42309
|
+
throw new NotAllowed(_spaceTrim.spaceTrim((block) => `
|
|
42310
|
+
Invalid value for \`--order\`: \`${orderValue}\`.
|
|
42311
|
+
|
|
42312
|
+
Use one of the supported orders:
|
|
42313
|
+
${block(VERIFY_PROMPTS_ORDER_VALUES.map((value) => `- \`${value}\` processes the prompt files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[value]}`).join('\n'))}
|
|
42314
|
+
|
|
42315
|
+
Actionable hint:
|
|
42316
|
+
- The removed \`--reverse\` flag is now \`--order from-latest\`.
|
|
42317
|
+
`));
|
|
42318
|
+
}
|
|
42319
|
+
|
|
41185
42320
|
/**
|
|
41186
42321
|
* Initializes `coder verify` command for Promptbook CLI utilities
|
|
41187
42322
|
*
|
|
@@ -41201,21 +42336,27 @@
|
|
|
41201
42336
|
- Auto-appends repair prompts for incomplete work
|
|
41202
42337
|
- Processes files with all-done prompts first
|
|
41203
42338
|
- Supports ignoring matching prompt candidates for one verification run
|
|
42339
|
+
- Supports processing the prompt files from the earliest, from the latest or in random order
|
|
41204
42340
|
|
|
41205
42341
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
41206
42342
|
|
|
41207
42343
|
Note: The git synchronization is applied around each single verification, not once per run.
|
|
41208
42344
|
`));
|
|
41209
|
-
command.
|
|
42345
|
+
command.addOption(new commander.Option('--order <order>', _spaceTrim.spaceTrim((block) => `
|
|
42346
|
+
Order in which the prompt files are processed:
|
|
42347
|
+
${block(VERIFY_PROMPTS_ORDER_VALUES.map((order) => `- ${order}: ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[order]}`).join('\n'))}
|
|
42348
|
+
`))
|
|
42349
|
+
.choices([...VERIFY_PROMPTS_ORDER_VALUES])
|
|
42350
|
+
.default(DEFAULT_VERIFY_PROMPTS_ORDER));
|
|
41210
42351
|
command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
|
|
41211
42352
|
addCoderGitSyncOptions(command);
|
|
41212
42353
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
41213
|
-
const {
|
|
42354
|
+
const { order, ignore } = cliOptions;
|
|
41214
42355
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
41215
42356
|
// Note: Import the main function dynamically to avoid loading heavy dependencies until needed
|
|
41216
42357
|
const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
|
|
41217
42358
|
try {
|
|
41218
|
-
await verifyPrompts({
|
|
42359
|
+
await verifyPrompts({ order, ignore, gitSync });
|
|
41219
42360
|
}
|
|
41220
42361
|
catch (error) {
|
|
41221
42362
|
console.error(colors__default["default"].bgRed('Prompt verification failed:'), error);
|
|
@@ -43994,8 +45135,8 @@
|
|
|
43994
45135
|
*/
|
|
43995
45136
|
$applyToTaskJson(command, $taskJson) {
|
|
43996
45137
|
if ($taskJson.format !== undefined && command.format !== $taskJson.format) {
|
|
43997
|
-
throw new ParseError(`Format format is already defined to "${$taskJson.format}".
|
|
43998
|
-
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}"`));
|
|
43999
45140
|
}
|
|
44000
45141
|
$taskJson.format = command.format;
|
|
44001
45142
|
},
|
|
@@ -56838,28 +57979,28 @@
|
|
|
56838
57979
|
*/
|
|
56839
57980
|
function buildToolInvocationScript(options) {
|
|
56840
57981
|
const { functionName, functionArgsExpression } = options;
|
|
56841
|
-
return `
|
|
56842
|
-
|
|
56843
|
-
|
|
56844
|
-
|
|
56845
|
-
|
|
56846
|
-
|
|
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};
|
|
56847
57988
|
|
|
56848
|
-
|
|
56849
|
-
|
|
56850
|
-
|
|
57989
|
+
if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
|
|
57990
|
+
args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
|
|
57991
|
+
}
|
|
56851
57992
|
|
|
56852
|
-
|
|
56853
|
-
|
|
56854
|
-
|
|
56855
|
-
|
|
57993
|
+
const toolProgressTokenRaw =
|
|
57994
|
+
typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
|
|
57995
|
+
? undefined
|
|
57996
|
+
: ${TOOL_PROGRESS_TOKEN_PARAMETER};
|
|
56856
57997
|
|
|
56857
|
-
|
|
56858
|
-
|
|
56859
|
-
|
|
57998
|
+
if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
|
|
57999
|
+
args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
|
|
58000
|
+
}
|
|
56860
58001
|
|
|
56861
|
-
|
|
56862
|
-
|
|
58002
|
+
return await ${functionName}(args);
|
|
58003
|
+
`);
|
|
56863
58004
|
}
|
|
56864
58005
|
|
|
56865
58006
|
/**
|
|
@@ -68805,6 +69946,28 @@
|
|
|
68805
69946
|
RemoteAgent: RemoteAgent
|
|
68806
69947
|
});
|
|
68807
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
|
+
|
|
68808
69971
|
/**
|
|
68809
69972
|
* Git synchronization which leaves the repository completely untouched.
|
|
68810
69973
|
*
|
|
@@ -68815,6 +69978,24 @@
|
|
|
68815
69978
|
isAutoPushEnabled: false,
|
|
68816
69979
|
isAutoPullEnabled: false,
|
|
68817
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
|
+
}
|
|
68818
69999
|
/**
|
|
68819
70000
|
* Pulls the latest repository changes before a `ptbk coder` command changes the project.
|
|
68820
70001
|
*/
|
|
@@ -68829,38 +70010,31 @@
|
|
|
68829
70010
|
/**
|
|
68830
70011
|
* Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
|
|
68831
70012
|
*
|
|
68832
|
-
* Note:
|
|
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.
|
|
68833
70015
|
*/
|
|
68834
70016
|
async function $commitCoderChanges(options) {
|
|
68835
|
-
const { gitSync, commitMessage,
|
|
70017
|
+
const { gitSync, commitMessage, commitScope } = options;
|
|
68836
70018
|
if (!gitSync.isCommitEnabled) {
|
|
68837
70019
|
return;
|
|
68838
70020
|
}
|
|
68839
|
-
|
|
68840
|
-
|
|
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'));
|
|
68841
70024
|
return;
|
|
68842
70025
|
}
|
|
68843
70026
|
await commitChanges(commitMessage, {
|
|
68844
|
-
projectPath,
|
|
70027
|
+
projectPath: commitScope.projectPath,
|
|
70028
|
+
relevantPaths,
|
|
68845
70029
|
autoPush: gitSync.isAutoPushEnabled,
|
|
68846
70030
|
});
|
|
68847
70031
|
console.info(colors__default["default"].green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
|
|
68848
70032
|
}
|
|
68849
|
-
/**
|
|
68850
|
-
* Checks whether the repository holds any change which can be committed.
|
|
68851
|
-
*/
|
|
68852
|
-
async function hasChangesToCommit(projectPath) {
|
|
68853
|
-
const gitStatus = await runGitCommand({
|
|
68854
|
-
command: 'git status --porcelain',
|
|
68855
|
-
cwd: projectPath,
|
|
68856
|
-
isVerbose: false,
|
|
68857
|
-
});
|
|
68858
|
-
return gitStatus.trim() !== '';
|
|
68859
|
-
}
|
|
68860
70033
|
|
|
68861
70034
|
var coderGitSync = /*#__PURE__*/Object.freeze({
|
|
68862
70035
|
__proto__: null,
|
|
68863
70036
|
DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
|
|
70037
|
+
$startCoderGitSync: $startCoderGitSync,
|
|
68864
70038
|
$pullCoderChanges: $pullCoderChanges,
|
|
68865
70039
|
$commitCoderChanges: $commitCoderChanges
|
|
68866
70040
|
});
|
|
@@ -70360,7 +71534,7 @@
|
|
|
70360
71534
|
}
|
|
70361
71535
|
/**
|
|
70362
71536
|
* Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
|
|
70363
|
-
* For [x] done
|
|
71537
|
+
* For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
|
|
70364
71538
|
*/
|
|
70365
71539
|
function parseStatusLine(line) {
|
|
70366
71540
|
var _a, _b, _c, _d;
|
|
@@ -70374,6 +71548,11 @@
|
|
|
70374
71548
|
if (failedMatch) {
|
|
70375
71549
|
return { status: 'failed', priority: 0 };
|
|
70376
71550
|
}
|
|
71551
|
+
// For in-progress prompts [^], allow any content after (for the steps recorded so far)
|
|
71552
|
+
const inProgressMatch = line.match(/^\[(?<status>\^)\]/);
|
|
71553
|
+
if (inProgressMatch) {
|
|
71554
|
+
return { status: 'in-progress', priority: 0 };
|
|
71555
|
+
}
|
|
70377
71556
|
// For not-ready [-], keep the historical clean-line syntax.
|
|
70378
71557
|
if (/^\[-\]\s*!*\s*$/u.test(line)) {
|
|
70379
71558
|
return { status: 'not-ready', priority: 0 };
|
|
@@ -72193,34 +73372,34 @@
|
|
|
72193
73372
|
* @private function of runDatabaseMigrations
|
|
72194
73373
|
*/
|
|
72195
73374
|
async function ensureMigrationsTableSchema(options) {
|
|
72196
|
-
await options.client.query(`
|
|
73375
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72197
73376
|
CREATE TABLE IF NOT EXISTS ${options.migrationsTableIdentifier} (
|
|
72198
73377
|
"filename" TEXT PRIMARY KEY,
|
|
72199
73378
|
"appliedAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
72200
73379
|
"appliedBy" TEXT NOT NULL DEFAULT '${options.manualAppliedByDefault}'
|
|
72201
73380
|
);
|
|
72202
|
-
`);
|
|
72203
|
-
await options.client.query(`
|
|
73381
|
+
`));
|
|
73382
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72204
73383
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72205
73384
|
ADD COLUMN IF NOT EXISTS "appliedBy" TEXT;
|
|
72206
|
-
`);
|
|
72207
|
-
await options.client.query(`
|
|
73385
|
+
`));
|
|
73386
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72208
73387
|
UPDATE ${options.migrationsTableIdentifier}
|
|
72209
73388
|
SET "appliedBy" = $1
|
|
72210
73389
|
WHERE "appliedBy" IS NULL;
|
|
72211
|
-
|
|
72212
|
-
await options.client.query(`
|
|
73390
|
+
`), [options.manualAppliedByDefault]);
|
|
73391
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72213
73392
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72214
73393
|
ALTER COLUMN "appliedBy" SET DEFAULT '${options.manualAppliedByDefault}';
|
|
72215
|
-
`);
|
|
72216
|
-
await options.client.query(`
|
|
73394
|
+
`));
|
|
73395
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72217
73396
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72218
73397
|
ALTER COLUMN "appliedBy" SET NOT NULL;
|
|
72219
|
-
`);
|
|
72220
|
-
await options.client.query(`
|
|
73398
|
+
`));
|
|
73399
|
+
await options.client.query(_spaceTrim.spaceTrim(`
|
|
72221
73400
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72222
73401
|
ENABLE ROW LEVEL SECURITY;
|
|
72223
|
-
`);
|
|
73402
|
+
`));
|
|
72224
73403
|
}
|
|
72225
73404
|
/**
|
|
72226
73405
|
* Rolls back an opened transaction and logs failures without masking original errors.
|
|
@@ -72953,14 +74132,25 @@
|
|
|
72953
74132
|
fixing: 'Fixing',
|
|
72954
74133
|
};
|
|
72955
74134
|
/**
|
|
72956
|
-
*
|
|
74135
|
+
* Suffix marking the one step which has already started but has neither a price nor a duration yet.
|
|
74136
|
+
*/
|
|
74137
|
+
const IN_PROGRESS_STEP_SUFFIX = 'in progress';
|
|
74138
|
+
/**
|
|
74139
|
+
* Formats the per-step usage breakdown recorded for one prompt.
|
|
72957
74140
|
*
|
|
72958
74141
|
* Produces a `; `-separated summary such as
|
|
72959
74142
|
* `Implementation $8.01 6 hours; Testing 1 hour; Fixing $3.14 2 hours` where each coding step carries its
|
|
72960
74143
|
* price and duration and each verification step carries only its duration.
|
|
74144
|
+
*
|
|
74145
|
+
* @param steps - Steps which have already finished
|
|
74146
|
+
* @param inProgressStepKind - Step which has started but has not finished yet, appended as `Testing in progress`
|
|
72961
74147
|
*/
|
|
72962
|
-
function formatCoderRunSteps(steps) {
|
|
72963
|
-
|
|
74148
|
+
function formatCoderRunSteps(steps, inProgressStepKind) {
|
|
74149
|
+
const formattedSteps = steps.map(formatCoderRunStep);
|
|
74150
|
+
if (inProgressStepKind !== undefined) {
|
|
74151
|
+
formattedSteps.push(`${CODER_RUN_STEP_LABELS[inProgressStepKind]} ${IN_PROGRESS_STEP_SUFFIX}`);
|
|
74152
|
+
}
|
|
74153
|
+
return formattedSteps.join('; ');
|
|
72964
74154
|
}
|
|
72965
74155
|
/**
|
|
72966
74156
|
* Formats one coder run step as `Label $price duration`, omitting the price for steps without model usage.
|
|
@@ -72989,47 +74179,78 @@
|
|
|
72989
74179
|
}
|
|
72990
74180
|
|
|
72991
74181
|
/**
|
|
72992
|
-
*
|
|
74182
|
+
* Builds the shared body of a prompt status line, used by both the in-progress `[^]` and the done `[x]` status.
|
|
72993
74183
|
*
|
|
72994
|
-
*
|
|
72995
|
-
*
|
|
74184
|
+
* Produces details such as
|
|
74185
|
+
* ``by OpenAI Codex `gpt-5.6-luna` thinking `max` (ChatGPT account) - Implementation ~$0.2036 10 minutes``.
|
|
72996
74186
|
*/
|
|
72997
|
-
function
|
|
72998
|
-
|
|
74187
|
+
function buildPromptStatusDetails(options) {
|
|
74188
|
+
const { steps, inProgressStepKind, runnerName, modelName, attemptCount, loginMethod, thinkingLevel } = options;
|
|
74189
|
+
const runnerSignature = formatRunnerSignature(runnerName, modelName, thinkingLevel);
|
|
74190
|
+
const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
|
|
74191
|
+
const loginMethodLabel = formatCodexLoginMethod(loginMethod);
|
|
74192
|
+
const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
|
|
74193
|
+
const stepsSummary = formatCoderRunSteps(steps, inProgressStepKind);
|
|
74194
|
+
const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
|
|
74195
|
+
return `${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`;
|
|
72999
74196
|
}
|
|
73000
74197
|
|
|
73001
74198
|
/**
|
|
73002
|
-
*
|
|
74199
|
+
* Resolves the status line of one prompt section so it can be rewritten.
|
|
73003
74200
|
*/
|
|
73004
|
-
function
|
|
74201
|
+
function resolvePromptStatusLine(file, section) {
|
|
73005
74202
|
if (section.statusLineIndex === undefined) {
|
|
73006
|
-
throw new
|
|
74203
|
+
throw new UnexpectedError(_spaceTrim.spaceTrim(`
|
|
74204
|
+
Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
|
|
74205
|
+
`));
|
|
73007
74206
|
}
|
|
73008
74207
|
const line = file.lines[section.statusLineIndex];
|
|
73009
74208
|
if (line === undefined) {
|
|
73010
|
-
throw new
|
|
74209
|
+
throw new UnexpectedError(_spaceTrim.spaceTrim(`
|
|
74210
|
+
Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
|
|
74211
|
+
`));
|
|
73011
74212
|
}
|
|
73012
|
-
|
|
73013
|
-
|
|
73014
|
-
|
|
73015
|
-
|
|
73016
|
-
|
|
73017
|
-
|
|
73018
|
-
|
|
73019
|
-
|
|
74213
|
+
return { statusLineIndex: section.statusLineIndex, line };
|
|
74214
|
+
}
|
|
74215
|
+
|
|
74216
|
+
/**
|
|
74217
|
+
* Matches a complete todo `[ ]` or in-progress `[^]` status line, capturing its indentation.
|
|
74218
|
+
*
|
|
74219
|
+
* The complete line is replaced because a todo status can contain a required model/harness token
|
|
74220
|
+
* in addition to its priority markers, and an in-progress status already carries the metadata of
|
|
74221
|
+
* the steps recorded so far.
|
|
74222
|
+
*/
|
|
74223
|
+
const REWRITABLE_PROMPT_STATUS_LINE_PATTERN = /^(?<indentation>\s*)\[(?:\s*|\^)\].*$/u;
|
|
74224
|
+
/**
|
|
74225
|
+
* Rewrites the status line of one prompt section while preserving its indentation.
|
|
74226
|
+
*
|
|
74227
|
+
* Only a todo `[ ]` or an in-progress `[^]` status line is rewritten, so an already finalized
|
|
74228
|
+
* `[x]`, `[!]` or `[-]` status is never overwritten by accident.
|
|
74229
|
+
*/
|
|
74230
|
+
function writePromptStatusLine(file, section, replacementStatusLine) {
|
|
74231
|
+
const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
|
|
74232
|
+
file.lines[statusLineIndex] = line.replace(REWRITABLE_PROMPT_STATUS_LINE_PATTERN, `$<indentation>${replacementStatusLine}`);
|
|
74233
|
+
}
|
|
74234
|
+
|
|
74235
|
+
/**
|
|
74236
|
+
* Marks a prompt section as done and records the per-step usage pricing and runner details.
|
|
74237
|
+
*/
|
|
74238
|
+
function markPromptDone(file, section, steps, runnerName, modelName, attemptCount = 1, loginMethod, thinkingLevel) {
|
|
74239
|
+
const statusDetails = buildPromptStatusDetails({
|
|
74240
|
+
steps,
|
|
74241
|
+
runnerName,
|
|
74242
|
+
modelName,
|
|
74243
|
+
attemptCount,
|
|
74244
|
+
loginMethod,
|
|
74245
|
+
thinkingLevel,
|
|
74246
|
+
});
|
|
74247
|
+
writePromptStatusLine(file, section, `[x] ${statusDetails}`);
|
|
73020
74248
|
}
|
|
73021
74249
|
|
|
73022
74250
|
/**
|
|
73023
74251
|
* Marks a prompt section as failed and records runner details.
|
|
73024
74252
|
*/
|
|
73025
74253
|
function markPromptFailed(file, section, runnerName, modelName, promptExecutionStartedDate, attemptCount = 1) {
|
|
73026
|
-
if (section.statusLineIndex === undefined) {
|
|
73027
|
-
throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
|
|
73028
|
-
}
|
|
73029
|
-
const line = file.lines[section.statusLineIndex];
|
|
73030
|
-
if (line === undefined) {
|
|
73031
|
-
throw new Error(`Prompt ${section.index + 1} in ${file.name} points to a missing status line.`);
|
|
73032
|
-
}
|
|
73033
74254
|
const runnerSignature = formatRunnerSignature(runnerName, modelName);
|
|
73034
74255
|
const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
|
|
73035
74256
|
const duration = moment__default["default"]().diff(promptExecutionStartedDate);
|
|
@@ -73037,7 +74258,18 @@
|
|
|
73037
74258
|
const failureDetails = attemptMetadata === ''
|
|
73038
74259
|
? `failed after ${durationString} by ${runnerSignature}`
|
|
73039
74260
|
: `${attemptMetadata}${durationString} by ${runnerSignature}`;
|
|
73040
|
-
file
|
|
74261
|
+
writePromptStatusLine(file, section, `[!] ${failureDetails}`);
|
|
74262
|
+
}
|
|
74263
|
+
|
|
74264
|
+
/**
|
|
74265
|
+
* Marks a prompt section as being implemented right now and records the steps finished so far.
|
|
74266
|
+
*
|
|
74267
|
+
* The `[^]` status is deliberately never reverted: when the coder is killed or crashes, the status stays
|
|
74268
|
+
* in the prompt file as the signal that this task was left in the middle of its implementation.
|
|
74269
|
+
*/
|
|
74270
|
+
function markPromptInProgress(options) {
|
|
74271
|
+
const { file, section, ...statusDetailsOptions } = options;
|
|
74272
|
+
writePromptStatusLine(file, section, `[^] ${buildPromptStatusDetails(statusDetailsOptions)}`);
|
|
73041
74273
|
}
|
|
73042
74274
|
|
|
73043
74275
|
/**
|
|
@@ -73125,9 +74357,9 @@
|
|
|
73125
74357
|
});
|
|
73126
74358
|
const promptExecutionStartedDate = moment__default["default"]();
|
|
73127
74359
|
let attemptCount = 1;
|
|
73128
|
-
|
|
73129
|
-
|
|
73130
|
-
|
|
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);
|
|
73131
74363
|
await withPromptRuntimeLog(scriptPath, async (logPath) => {
|
|
73132
74364
|
let lastError;
|
|
73133
74365
|
for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
|
|
@@ -73146,6 +74378,13 @@
|
|
|
73146
74378
|
attemptCount = nextAttemptCount;
|
|
73147
74379
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
|
|
73148
74380
|
},
|
|
74381
|
+
onStepStarted: (progress) => recordPromptRoundInProgress({
|
|
74382
|
+
nextPrompt,
|
|
74383
|
+
runnerMetadata,
|
|
74384
|
+
thinkingLevel: options.thinkingLevel,
|
|
74385
|
+
attemptCount,
|
|
74386
|
+
progress,
|
|
74387
|
+
}),
|
|
73149
74388
|
waitForPauseCheckpoint: waitForRequestedPause,
|
|
73150
74389
|
});
|
|
73151
74390
|
await finalizeSuccessfulPromptRound({
|
|
@@ -73156,7 +74395,7 @@
|
|
|
73156
74395
|
result,
|
|
73157
74396
|
commitMessage,
|
|
73158
74397
|
logPath,
|
|
73159
|
-
|
|
74398
|
+
roundCommitScope,
|
|
73160
74399
|
isRichUiEnabled,
|
|
73161
74400
|
progressDisplay,
|
|
73162
74401
|
uiHandle,
|
|
@@ -73189,7 +74428,7 @@
|
|
|
73189
74428
|
attemptCount,
|
|
73190
74429
|
error: lastError,
|
|
73191
74430
|
options,
|
|
73192
|
-
|
|
74431
|
+
roundCommitScope,
|
|
73193
74432
|
uiHandle,
|
|
73194
74433
|
waitForRequestedPause,
|
|
73195
74434
|
roundProjectPath,
|
|
@@ -73197,6 +74436,30 @@
|
|
|
73197
74436
|
throw lastError;
|
|
73198
74437
|
}, { preserveArtifactsOnSuccess: options.preserveLogs });
|
|
73199
74438
|
}
|
|
74439
|
+
/**
|
|
74440
|
+
* Records into the prompt file that the prompt is being implemented right now.
|
|
74441
|
+
*
|
|
74442
|
+
* The `[^]` in-progress status is written before every single step, so it always names the step which is
|
|
74443
|
+
* running and the steps already finished. It is intentionally never reverted: a coder which is killed or
|
|
74444
|
+
* crashes leaves the `[^]` status behind as the signal that this task was left in the middle.
|
|
74445
|
+
*/
|
|
74446
|
+
async function recordPromptRoundInProgress(options) {
|
|
74447
|
+
const { nextPrompt, runnerMetadata, thinkingLevel, attemptCount, progress } = options;
|
|
74448
|
+
markPromptInProgress({
|
|
74449
|
+
file: nextPrompt.file,
|
|
74450
|
+
section: nextPrompt.section,
|
|
74451
|
+
steps: progress.finishedSteps,
|
|
74452
|
+
inProgressStepKind: progress.startedStepKind,
|
|
74453
|
+
runnerName: runnerMetadata.runnerName,
|
|
74454
|
+
modelName: runnerMetadata.modelName,
|
|
74455
|
+
attemptCount,
|
|
74456
|
+
loginMethod: progress.loginMethod,
|
|
74457
|
+
thinkingLevel,
|
|
74458
|
+
});
|
|
74459
|
+
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
74460
|
+
// its own changes back through the merge instead
|
|
74461
|
+
await writePromptFile(nextPrompt.file);
|
|
74462
|
+
}
|
|
73200
74463
|
/**
|
|
73201
74464
|
* Sleeps `options.waitAfterError` while keeping the rich UI and plain console in sync, then resets state for the retry.
|
|
73202
74465
|
*/
|
|
@@ -73246,7 +74509,7 @@
|
|
|
73246
74509
|
* Finalizes a successful prompt round, including prompt bookkeeping and commit flow.
|
|
73247
74510
|
*/
|
|
73248
74511
|
async function finalizeSuccessfulPromptRound(options) {
|
|
73249
|
-
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath,
|
|
74512
|
+
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath, roundCommitScope, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
73250
74513
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
73251
74514
|
await waitForRequestedPause({
|
|
73252
74515
|
checkpointLabel: 'recording the successful prompt result',
|
|
@@ -73257,7 +74520,7 @@
|
|
|
73257
74520
|
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
73258
74521
|
// its own changes back through the merge instead
|
|
73259
74522
|
await writePromptFile(nextPrompt.file);
|
|
73260
|
-
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath,
|
|
74523
|
+
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
73261
74524
|
await recordPromptDurationInEstimateCache({
|
|
73262
74525
|
options: runOptions,
|
|
73263
74526
|
runnerMetadata,
|
|
@@ -73278,6 +74541,9 @@
|
|
|
73278
74541
|
});
|
|
73279
74542
|
await commitChanges(commitMessage, {
|
|
73280
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)),
|
|
73281
74547
|
// Keep the live runtime log out of default commits because it is deleted after a successful round.
|
|
73282
74548
|
excludePaths: runOptions.preserveLogs ? undefined : [logPath],
|
|
73283
74549
|
projectPath: roundProjectPath,
|
|
@@ -73301,7 +74567,7 @@
|
|
|
73301
74567
|
* Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
|
|
73302
74568
|
*/
|
|
73303
74569
|
async function finalizeFailedPromptRound(options) {
|
|
73304
|
-
const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions,
|
|
74570
|
+
const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
73305
74571
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
73306
74572
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
|
|
73307
74573
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
|
|
@@ -73319,7 +74585,7 @@
|
|
|
73319
74585
|
modelName: runnerMetadata.modelName,
|
|
73320
74586
|
error,
|
|
73321
74587
|
});
|
|
73322
|
-
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath,
|
|
74588
|
+
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
73323
74589
|
}
|
|
73324
74590
|
/**
|
|
73325
74591
|
* Waits for the optional user confirmation immediately before creating the commit.
|
|
@@ -73385,17 +74651,29 @@
|
|
|
73385
74651
|
thinkingLevel: runOptions.thinkingLevel,
|
|
73386
74652
|
}, promptDurationMs);
|
|
73387
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
|
+
}
|
|
73388
74666
|
/**
|
|
73389
74667
|
* Normalizes line endings in files modified during the current coding round.
|
|
73390
74668
|
*/
|
|
73391
|
-
async function normalizeLineEndingsForCurrentRound(options, roundProjectPath,
|
|
73392
|
-
if (!options.normalizeLineEndings || !
|
|
74669
|
+
async function normalizeLineEndingsForCurrentRound(options, roundProjectPath, roundCommitScope) {
|
|
74670
|
+
if (!options.normalizeLineEndings || !roundCommitScope) {
|
|
73393
74671
|
return;
|
|
73394
74672
|
}
|
|
73395
74673
|
try {
|
|
73396
74674
|
const result = await normalizeLineEndingsInFilesChangedSinceSnapshot({
|
|
73397
74675
|
projectPath: roundProjectPath,
|
|
73398
|
-
snapshot:
|
|
74676
|
+
snapshot: roundCommitScope.snapshotBeforeOperation,
|
|
73399
74677
|
});
|
|
73400
74678
|
if (result.normalizedFiles > 0) {
|
|
73401
74679
|
console.info(colors__default["default"].gray(`Normalized line endings to LF in ${result.normalizedFiles} changed file(s).`));
|
|
@@ -73456,11 +74734,11 @@
|
|
|
73456
74734
|
* Builds the markdown list of commands which merge one isolated task manually and clean it up.
|
|
73457
74735
|
*/
|
|
73458
74736
|
function buildManualRecoveryHints(worktree) {
|
|
73459
|
-
return
|
|
73460
|
-
|
|
73461
|
-
|
|
73462
|
-
|
|
73463
|
-
|
|
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
|
+
`);
|
|
73464
74742
|
}
|
|
73465
74743
|
|
|
73466
74744
|
/**
|
|
@@ -73857,19 +75135,9 @@
|
|
|
73857
75135
|
* kept and only the status box turns from `[x]` into `[!]`, followed by the manual merge instructions.
|
|
73858
75136
|
*/
|
|
73859
75137
|
function markPromptIsolationMergeFailed(file, section, worktree) {
|
|
73860
|
-
|
|
73861
|
-
throw new UnexpectedError(_spaceTrim.spaceTrim(`
|
|
73862
|
-
Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
|
|
73863
|
-
`));
|
|
73864
|
-
}
|
|
73865
|
-
const line = file.lines[section.statusLineIndex];
|
|
73866
|
-
if (line === undefined) {
|
|
73867
|
-
throw new UnexpectedError(_spaceTrim.spaceTrim(`
|
|
73868
|
-
Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
|
|
73869
|
-
`));
|
|
73870
|
-
}
|
|
75138
|
+
const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
|
|
73871
75139
|
const failedStatusLine = line.replace(PROMPT_STATUS_BOX_PATTERN, '$<indentation>[!]');
|
|
73872
|
-
file.lines[
|
|
75140
|
+
file.lines[statusLineIndex] = `${failedStatusLine} - ${buildCoderIsolationMergeFailureStatusNote(worktree)}`;
|
|
73873
75141
|
}
|
|
73874
75142
|
|
|
73875
75143
|
/**
|
|
@@ -73926,6 +75194,9 @@
|
|
|
73926
75194
|
var _a;
|
|
73927
75195
|
const { nextPrompt, promptLabel, isRichUiEnabled, uiHandle, waitForRequestedPause } = options;
|
|
73928
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);
|
|
73929
75200
|
const worktree = await createCoderIsolationWorktree({
|
|
73930
75201
|
projectPath,
|
|
73931
75202
|
taskName: buildCoderIsolationTaskName(nextPrompt.file, nextPrompt.section),
|
|
@@ -73957,6 +75228,7 @@
|
|
|
73957
75228
|
// Note: The merge only stages the isolated changes, so this commit joins them with the prompt status update
|
|
73958
75229
|
await commitChanges(buildCommitMessage(nextPrompt.file, nextPrompt.section), {
|
|
73959
75230
|
autoPush: options.options.autoPush,
|
|
75231
|
+
relevantPaths: await resolveCoderCommitScopePaths(originalProjectCommitScope),
|
|
73960
75232
|
projectPath,
|
|
73961
75233
|
});
|
|
73962
75234
|
await removeCoderIsolationWorktree(worktree);
|
|
@@ -73980,7 +75252,7 @@
|
|
|
73980
75252
|
await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
|
|
73981
75253
|
autoPush: options.options.autoPush,
|
|
73982
75254
|
projectPath: worktree.projectPath,
|
|
73983
|
-
|
|
75255
|
+
relevantPaths: [nextPrompt.file.path, errorLogPath].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
|
|
73984
75256
|
});
|
|
73985
75257
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
|
|
73986
75258
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
|
|
@@ -74135,26 +75407,22 @@
|
|
|
74135
75407
|
* Builds the ANSI ASCII-art visual of the `--agent` book shown above the coder-run dashboard.
|
|
74136
75408
|
*
|
|
74137
75409
|
* The agent's avatar visual is resolved the same way as on the website - the `META AVATAR`
|
|
74138
|
-
* commitment wins, then the `META VISUAL` commitment, then the shared default visual
|
|
74139
|
-
*
|
|
74140
|
-
*
|
|
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.
|
|
74141
75414
|
*
|
|
74142
|
-
* The visual is decorative, so any failure
|
|
74143
|
-
* 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.
|
|
74144
75416
|
*
|
|
74145
75417
|
* @param agentSource Source of the `--agent` book file.
|
|
74146
75418
|
* @returns ANSI-colored ASCII-art renderer or `null` when the visual cannot be rendered.
|
|
74147
75419
|
*/
|
|
74148
75420
|
async function buildCoderRunAgentVisual(agentSource) {
|
|
74149
75421
|
try {
|
|
74150
|
-
// Note: `@napi-rs/canvas` is an optional native module, so it is imported dynamically and lazily
|
|
74151
|
-
const { createCanvas } = await import('@napi-rs/canvas');
|
|
74152
|
-
const colorDepth = $detectTerminalAnsiColorDepth();
|
|
74153
|
-
const createCanvasForAsciiArt = (width, height) => createCanvas(width, height);
|
|
74154
75422
|
const agentVisual = createTerminalAgentAvatarVisual({
|
|
74155
75423
|
agentSource,
|
|
74156
|
-
colorDepth,
|
|
74157
|
-
createCanvas:
|
|
75424
|
+
colorDepth: $detectTerminalAnsiColorDepth(),
|
|
75425
|
+
createCanvas: await createOptionalNodeCanvasFactory(),
|
|
74158
75426
|
});
|
|
74159
75427
|
return {
|
|
74160
75428
|
isAnimated: agentVisual.isAnimated,
|
|
@@ -74173,6 +75441,23 @@
|
|
|
74173
75441
|
return null;
|
|
74174
75442
|
}
|
|
74175
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
|
+
}
|
|
74176
75461
|
|
|
74177
75462
|
/**
|
|
74178
75463
|
* Creates the one queue prompt used to repair a pre-existing test failure.
|
|
@@ -75009,7 +76294,9 @@
|
|
|
75009
76294
|
if (section.status === 'not-ready' || isUnwritten) {
|
|
75010
76295
|
return 'backlog';
|
|
75011
76296
|
}
|
|
75012
|
-
|
|
76297
|
+
// Note: A prompt marked `[^]` stays in progress even when no agent is running it, because its
|
|
76298
|
+
// implementation was left in the middle
|
|
76299
|
+
if (isActive || section.status === 'in-progress') {
|
|
75013
76300
|
return 'in-progress';
|
|
75014
76301
|
}
|
|
75015
76302
|
if (!isPromptInPriorityFilter(section, priorityFilter)) {
|
|
@@ -75026,6 +76313,10 @@
|
|
|
75026
76313
|
if (options.section.status === 'not-ready') {
|
|
75027
76314
|
tags.push({ id: 'not-ready', label: '[-]' });
|
|
75028
76315
|
}
|
|
76316
|
+
// Note: A `[^]` prompt which no agent is running right now was left in the middle of its implementation
|
|
76317
|
+
if (options.section.status === 'in-progress' && !options.isActive) {
|
|
76318
|
+
tags.push({ id: 'left-in-progress', label: '[^]' });
|
|
76319
|
+
}
|
|
75029
76320
|
if (options.isUnwritten) {
|
|
75030
76321
|
tags.push({ id: 'unwritten', label: '@@@' });
|
|
75031
76322
|
}
|
|
@@ -75055,7 +76346,7 @@
|
|
|
75055
76346
|
* Checks whether a parsed section is the prompt currently handled by the active agent.
|
|
75056
76347
|
*/
|
|
75057
76348
|
function isPromptActive(promptFile, section, activePrompt) {
|
|
75058
|
-
if (!activePrompt || section.status !== 'todo') {
|
|
76349
|
+
if (!activePrompt || (section.status !== 'todo' && section.status !== 'in-progress')) {
|
|
75059
76350
|
return false;
|
|
75060
76351
|
}
|
|
75061
76352
|
return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
|
|
@@ -75087,7 +76378,7 @@
|
|
|
75087
76378
|
* Overwrites the body of one prompt section with new content, preserving the status line.
|
|
75088
76379
|
*
|
|
75089
76380
|
* The `newContent` string is the prompt text without the status marker.
|
|
75090
|
-
* The status line (`[ ]`, `[x]`, `[!]`, `[-]`) is kept intact.
|
|
76381
|
+
* The status line (`[ ]`, `[^]`, `[x]`, `[!]`, `[-]`) is kept intact.
|
|
75091
76382
|
*
|
|
75092
76383
|
* @private internal utility of `ptbk coder server`
|
|
75093
76384
|
*/
|
|
@@ -75145,7 +76436,7 @@
|
|
|
75145
76436
|
*
|
|
75146
76437
|
* @private internal constant of `ptbk coder server`
|
|
75147
76438
|
*/
|
|
75148
|
-
const CODER_SERVER_HTML = `<!DOCTYPE html>
|
|
76439
|
+
const CODER_SERVER_HTML = _spaceTrim.spaceTrim(`<!DOCTYPE html>
|
|
75149
76440
|
<html lang="en">
|
|
75150
76441
|
<head>
|
|
75151
76442
|
<meta charset="UTF-8">
|
|
@@ -75368,6 +76659,7 @@
|
|
|
75368
76659
|
line-height: 1.35;
|
|
75369
76660
|
}
|
|
75370
76661
|
.tag-not-ready { background: #e2e8f0; color: #475569; }
|
|
76662
|
+
.tag-left-in-progress { background: #ffe4e6; color: #9f1239; }
|
|
75371
76663
|
.tag-unwritten { background: #fff4cc; color: #8a5a00; }
|
|
75372
76664
|
.tag-implementing { background: #ede9fe; color: #5b21b6; }
|
|
75373
76665
|
.tag-verifying { background: #fae8ff; color: #86198f; }
|
|
@@ -75830,7 +77122,7 @@
|
|
|
75830
77122
|
</script>
|
|
75831
77123
|
</body>
|
|
75832
77124
|
</html>
|
|
75833
|
-
|
|
77125
|
+
`);
|
|
75834
77126
|
// Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/coderServerHtml.ts) should never be published outside of `@promptbook/cli`
|
|
75835
77127
|
// Note: Keep in sync with apps/coder-server/index.html
|
|
75836
77128
|
|
|
@@ -76022,8 +77314,7 @@
|
|
|
76022
77314
|
async function commitPromptEdit(promptFilePath, sectionIndex) {
|
|
76023
77315
|
const relativePromptFilePath = path.relative(process.cwd(), promptFilePath).replace(/\\/gu, '/');
|
|
76024
77316
|
await commitChanges(`Edit coder prompt ${relativePromptFilePath}#${sectionIndex + 1}`, {
|
|
76025
|
-
|
|
76026
|
-
onlyPaths: [relativePromptFilePath],
|
|
77317
|
+
relevantPaths: [relativePromptFilePath],
|
|
76027
77318
|
});
|
|
76028
77319
|
}
|
|
76029
77320
|
/**
|
|
@@ -76098,6 +77389,33 @@
|
|
|
76098
77389
|
runCodexPromptsServer: runCodexPromptsServer
|
|
76099
77390
|
});
|
|
76100
77391
|
|
|
77392
|
+
/**
|
|
77393
|
+
* Checks whether one prompt section still has open coding work.
|
|
77394
|
+
*
|
|
77395
|
+
* A `[ ]` prompt has not been started at all and a `[^]` prompt was left in the middle of its
|
|
77396
|
+
* implementation, so a file containing either of them is not finished yet.
|
|
77397
|
+
*/
|
|
77398
|
+
function isPromptSectionUnfinished(section) {
|
|
77399
|
+
return section.status === 'todo' || section.status === 'in-progress';
|
|
77400
|
+
}
|
|
77401
|
+
|
|
77402
|
+
/**
|
|
77403
|
+
* Orders the loaded prompt files for one verification pass.
|
|
77404
|
+
*
|
|
77405
|
+
* Note: `$` is used to indicate that this function is not a pure function - the `random` order is not deterministic
|
|
77406
|
+
* Note: This function does NOT mutate the given array
|
|
77407
|
+
*/
|
|
77408
|
+
function $orderPromptFiles(promptFiles, order) {
|
|
77409
|
+
switch (order) {
|
|
77410
|
+
case 'from-earliest':
|
|
77411
|
+
return [...promptFiles];
|
|
77412
|
+
case 'from-latest':
|
|
77413
|
+
return [...promptFiles].reverse();
|
|
77414
|
+
case 'random':
|
|
77415
|
+
return [...$shuffleItems(...promptFiles)];
|
|
77416
|
+
}
|
|
77417
|
+
}
|
|
77418
|
+
|
|
76101
77419
|
/**
|
|
76102
77420
|
* Path to the directory that holds the prompt markdown files.
|
|
76103
77421
|
*/
|
|
@@ -76126,8 +77444,8 @@
|
|
|
76126
77444
|
async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
|
|
76127
77445
|
const normalizedOptions = normalizeVerifyPromptsOptions(options);
|
|
76128
77446
|
console.info(colors__default["default"].cyan.bold('📋 Prompt verification helper'));
|
|
76129
|
-
if (normalizedOptions.
|
|
76130
|
-
console.info(colors__default["default"].gray(
|
|
77447
|
+
if (normalizedOptions.order !== DEFAULT_VERIFY_PROMPTS_ORDER) {
|
|
77448
|
+
console.info(colors__default["default"].gray(`Processing files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[normalizedOptions.order]}`));
|
|
76131
77449
|
}
|
|
76132
77450
|
if (normalizedOptions.ignore.length > 0) {
|
|
76133
77451
|
console.info(colors__default["default"].gray(`Ignoring candidates matching: ${normalizedOptions.ignore.join(', ')}`));
|
|
@@ -76141,8 +77459,9 @@
|
|
|
76141
77459
|
let promptFiles = initialFiles;
|
|
76142
77460
|
const skippedFiles = new Set();
|
|
76143
77461
|
while (true) {
|
|
76144
|
-
// Note: The git synchronization is applied around each single verification, not once per whole run
|
|
76145
|
-
|
|
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 });
|
|
76146
77465
|
if (normalizedOptions.gitSync.isAutoPullEnabled) {
|
|
76147
77466
|
// Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
|
|
76148
77467
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
@@ -76155,7 +77474,7 @@
|
|
|
76155
77474
|
if (outcome.wasSkipped) {
|
|
76156
77475
|
skippedFiles.add(fileWithAllDone.path);
|
|
76157
77476
|
}
|
|
76158
|
-
await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
|
|
77477
|
+
await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
|
|
76159
77478
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
76160
77479
|
continue;
|
|
76161
77480
|
}
|
|
@@ -76166,25 +77485,25 @@
|
|
|
76166
77485
|
break;
|
|
76167
77486
|
}
|
|
76168
77487
|
const outcome = await resolvePrompt(nextPrompt);
|
|
76169
|
-
await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
|
|
77488
|
+
await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
|
|
76170
77489
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
76171
77490
|
}
|
|
76172
77491
|
}
|
|
76173
77492
|
/**
|
|
76174
77493
|
* Commits and pushes one applied verification when the git synchronization is enabled.
|
|
76175
77494
|
*/
|
|
76176
|
-
async function $commitVerificationOutcome(gitSync, outcome) {
|
|
77495
|
+
async function $commitVerificationOutcome(gitSync, commitScope, outcome) {
|
|
76177
77496
|
if (outcome.commitMessage === null) {
|
|
76178
77497
|
return;
|
|
76179
77498
|
}
|
|
76180
|
-
await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
|
|
77499
|
+
await $commitCoderChanges({ gitSync, commitScope, commitMessage: outcome.commitMessage });
|
|
76181
77500
|
}
|
|
76182
77501
|
/**
|
|
76183
77502
|
* Parses supported command-line arguments for the standalone verification script.
|
|
76184
77503
|
*/
|
|
76185
77504
|
function parseVerifyPromptsCliOptions(args) {
|
|
76186
77505
|
return {
|
|
76187
|
-
|
|
77506
|
+
order: parseVerifyPromptsOrder(readStringOption(args, '--order')),
|
|
76188
77507
|
ignore: readRepeatableStringOption(args, '--ignore'),
|
|
76189
77508
|
gitSync: {
|
|
76190
77509
|
isCommitEnabled: args.includes('--commit'),
|
|
@@ -76199,10 +77518,7 @@
|
|
|
76199
77518
|
async function loadPromptFilesForVerification(options) {
|
|
76200
77519
|
const loadedPromptFiles = await loadPromptFiles(PROMPTS_DIR);
|
|
76201
77520
|
const { promptFiles, ignoredPromptFiles } = partitionPromptFilesByIgnore(loadedPromptFiles, options.ignore);
|
|
76202
|
-
|
|
76203
|
-
promptFiles.reverse();
|
|
76204
|
-
}
|
|
76205
|
-
return { promptFiles, ignoredPromptFiles };
|
|
77521
|
+
return { promptFiles: $orderPromptFiles(promptFiles, options.order), ignoredPromptFiles };
|
|
76206
77522
|
}
|
|
76207
77523
|
/**
|
|
76208
77524
|
* Splits prompt files into files that should be verified now and files ignored for this run.
|
|
@@ -76244,7 +77560,7 @@
|
|
|
76244
77560
|
function normalizeVerifyPromptsOptions(options) {
|
|
76245
77561
|
var _a, _b, _c;
|
|
76246
77562
|
return {
|
|
76247
|
-
|
|
77563
|
+
order: (_a = options.order) !== null && _a !== void 0 ? _a : DEFAULT_VERIFY_PROMPTS_ORDER,
|
|
76248
77564
|
ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
|
|
76249
77565
|
gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
|
|
76250
77566
|
};
|
|
@@ -76269,6 +77585,13 @@
|
|
|
76269
77585
|
}
|
|
76270
77586
|
return normalizedIgnoreValues;
|
|
76271
77587
|
}
|
|
77588
|
+
/**
|
|
77589
|
+
* Reads one single-value string option from raw CLI arguments, keeping the last occurrence.
|
|
77590
|
+
*/
|
|
77591
|
+
function readStringOption(args, flag) {
|
|
77592
|
+
const values = readRepeatableStringOption(args, flag);
|
|
77593
|
+
return values[values.length - 1];
|
|
77594
|
+
}
|
|
76272
77595
|
/**
|
|
76273
77596
|
* Reads one repeatable string option from raw CLI arguments.
|
|
76274
77597
|
*/
|
|
@@ -76330,8 +77653,9 @@
|
|
|
76330
77653
|
for (const file of promptFiles) {
|
|
76331
77654
|
const doneCount = file.sections.filter((section) => section.status === 'done').length;
|
|
76332
77655
|
const todoCount = file.sections.filter((section) => section.status === 'todo').length;
|
|
77656
|
+
const inProgressCount = file.sections.filter((section) => section.status === 'in-progress').length;
|
|
76333
77657
|
const notReadyCount = file.sections.filter((section) => section.status === 'not-ready').length;
|
|
76334
|
-
const needsVerification = file.sections.length > 0 &&
|
|
77658
|
+
const needsVerification = file.sections.length > 0 && !file.sections.some(isPromptSectionUnfinished) && doneCount > 0;
|
|
76335
77659
|
const statusParts = [];
|
|
76336
77660
|
if (needsVerification) {
|
|
76337
77661
|
statusParts.push(colors__default["default"].cyan.bold(`🔍 ${doneCount} done [x] - NEEDS VERIFICATION`));
|
|
@@ -76340,6 +77664,9 @@
|
|
|
76340
77664
|
if (todoCount > 0) {
|
|
76341
77665
|
statusParts.push(colors__default["default"].yellow(`${todoCount} todo [ ]`));
|
|
76342
77666
|
}
|
|
77667
|
+
if (inProgressCount > 0) {
|
|
77668
|
+
statusParts.push(colors__default["default"].magenta(`${inProgressCount} in-progress [^]`));
|
|
77669
|
+
}
|
|
76343
77670
|
if (doneCount > 0) {
|
|
76344
77671
|
statusParts.push(colors__default["default"].green(`${doneCount} done [x]`));
|
|
76345
77672
|
}
|
|
@@ -76355,7 +77682,7 @@
|
|
|
76355
77682
|
console.info(colors__default["default"].gray('Goal: verify all done prompts, then process remaining todo prompts.'));
|
|
76356
77683
|
}
|
|
76357
77684
|
/**
|
|
76358
|
-
* Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ].
|
|
77685
|
+
* Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ] or in-progress [^].
|
|
76359
77686
|
* Completely ignores not-ready prompts like [-], [.], [?], etc.
|
|
76360
77687
|
* Also excludes files that have been skipped in this session.
|
|
76361
77688
|
*/
|
|
@@ -76368,10 +77695,10 @@
|
|
|
76368
77695
|
if (skippedFiles.has(file.path)) {
|
|
76369
77696
|
return false;
|
|
76370
77697
|
}
|
|
76371
|
-
// File is ready for verification if it has at least one done prompt and no
|
|
76372
|
-
const
|
|
77698
|
+
// File is ready for verification if it has at least one done prompt and no unfinished prompts
|
|
77699
|
+
const hasUnfinishedPrompts = file.sections.some(isPromptSectionUnfinished);
|
|
76373
77700
|
const hasDonePrompts = file.sections.some((section) => section.status === 'done');
|
|
76374
|
-
return !
|
|
77701
|
+
return !hasUnfinishedPrompts && hasDonePrompts;
|
|
76375
77702
|
});
|
|
76376
77703
|
}
|
|
76377
77704
|
/**
|
|
@@ -76477,11 +77804,11 @@
|
|
|
76477
77804
|
const filesNeedingVerification = promptFiles.filter((file) => {
|
|
76478
77805
|
if (file.sections.length === 0)
|
|
76479
77806
|
return false;
|
|
76480
|
-
const
|
|
77807
|
+
const hasUnfinished = file.sections.some(isPromptSectionUnfinished);
|
|
76481
77808
|
const hasDone = file.sections.some((section) => section.status === 'done');
|
|
76482
|
-
return !
|
|
77809
|
+
return !hasUnfinished && hasDone;
|
|
76483
77810
|
});
|
|
76484
|
-
const pendingFiles = promptFiles.filter((file) => file.sections.some(
|
|
77811
|
+
const pendingFiles = promptFiles.filter((file) => file.sections.some(isPromptSectionUnfinished));
|
|
76485
77812
|
if (filesNeedingVerification.length) {
|
|
76486
77813
|
const formattedNames = formatPendingFileNames(filesNeedingVerification.map((file) => file.name));
|
|
76487
77814
|
console.info(colors__default["default"].cyan(` 🔍 Files needing verification (${filesNeedingVerification.length}): ${formattedNames}`));
|