@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/esm/index.es.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as fs from 'fs';
|
|
|
10
10
|
import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
|
|
11
11
|
import * as dotenv from 'dotenv';
|
|
12
12
|
import * as readline from 'readline';
|
|
13
|
-
import { emitKeypressEvents,
|
|
13
|
+
import { emitKeypressEvents, createInterface, cursorTo, clearLine } from 'readline';
|
|
14
14
|
import { forTime, forEver } from 'waitasecond';
|
|
15
15
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
16
16
|
import sha256 from 'crypto-js/sha256';
|
|
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
48
48
|
* @generated
|
|
49
49
|
* @see https://github.com/webgptorg/promptbook
|
|
50
50
|
*/
|
|
51
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-6';
|
|
52
52
|
/**
|
|
53
53
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
54
54
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2583,7 +2583,10 @@ async function ensureProjectGitignoreFile({ projectPath, blockHeader, rules, })
|
|
|
2583
2583
|
if (currentGitignoreContent !== undefined && missingRules.length === 0) {
|
|
2584
2584
|
return 'unchanged';
|
|
2585
2585
|
}
|
|
2586
|
-
const nextGitignoreContent = appendBlock(currentGitignoreContent || '',
|
|
2586
|
+
const nextGitignoreContent = appendBlock(currentGitignoreContent || '', spaceTrim$1((block) => `
|
|
2587
|
+
${block(blockHeader)}
|
|
2588
|
+
${block(missingRules.join('\n'))}
|
|
2589
|
+
`));
|
|
2587
2590
|
await writeFile(gitignorePath, nextGitignoreContent, 'utf-8');
|
|
2588
2591
|
return currentGitignoreContent === undefined ? 'created' : 'updated';
|
|
2589
2592
|
}
|
|
@@ -7453,6 +7456,15 @@ const OUTLINE_GLYPHS = ['#', '%', '@'];
|
|
|
7453
7456
|
* @private helper of `asciiOctopusAvatarVisual`
|
|
7454
7457
|
*/
|
|
7455
7458
|
const ATMOSPHERE_GLYPHS = ['.', ':', "'", '`'];
|
|
7459
|
+
/**
|
|
7460
|
+
* Coordinate space the octopus layout is generated in when it is painted into a terminal.
|
|
7461
|
+
*
|
|
7462
|
+
* The terminal renderer has its own character grid, so the geometry only needs one stable
|
|
7463
|
+
* square coordinate space which the grid is then sampled from.
|
|
7464
|
+
*
|
|
7465
|
+
* @private helper of `asciiOctopusAvatarVisual`
|
|
7466
|
+
*/
|
|
7467
|
+
const TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE = DEFAULT_AVATAR_SIZE;
|
|
7456
7468
|
/**
|
|
7457
7469
|
* AsciiOctopus avatar visual.
|
|
7458
7470
|
*
|
|
@@ -7469,41 +7481,115 @@ const asciiOctopusAvatarVisual = {
|
|
|
7469
7481
|
const staticRandom = createRandom('ascii-octopus-static');
|
|
7470
7482
|
const gridMetrics = createAsciiGridMetrics(size, gridRandom);
|
|
7471
7483
|
const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction);
|
|
7484
|
+
const glyphGrid = createAsciiOctopusGlyphGrid({
|
|
7485
|
+
sampleGrid: gridMetrics,
|
|
7486
|
+
layout,
|
|
7487
|
+
palette,
|
|
7488
|
+
cellRandom: createRandom('ascii-octopus-cells'),
|
|
7489
|
+
timeMs,
|
|
7490
|
+
});
|
|
7472
7491
|
drawAvatarFrame(context, size, palette);
|
|
7473
7492
|
drawAsciiBackdrop(context, size, palette, layout, timeMs);
|
|
7474
7493
|
context.save();
|
|
7475
7494
|
context.font = `600 ${gridMetrics.fontSize}px monospace`;
|
|
7476
7495
|
context.textAlign = 'center';
|
|
7477
7496
|
context.textBaseline = 'middle';
|
|
7478
|
-
// The ASCII renderer samples the morphing octopus field on a low-resolution grid so the shape stays organic
|
|
7479
|
-
// while the glyph layout remains deterministic for the same avatar input.
|
|
7480
|
-
const cellRandom = createRandom('ascii-octopus-cells');
|
|
7481
7497
|
for (let rowIndex = 0; rowIndex < gridMetrics.rowCount; rowIndex++) {
|
|
7482
7498
|
for (let columnIndex = 0; columnIndex < gridMetrics.columnCount; columnIndex++) {
|
|
7483
|
-
const
|
|
7484
|
-
x: gridMetrics.offsetX + columnIndex * gridMetrics.cellWidth,
|
|
7485
|
-
y: gridMetrics.offsetY + rowIndex * gridMetrics.cellHeight,
|
|
7486
|
-
};
|
|
7487
|
-
const noise = cellRandom();
|
|
7488
|
-
const glyphDescriptor = resolveAsciiGlyph({
|
|
7489
|
-
point,
|
|
7490
|
-
layout,
|
|
7491
|
-
palette,
|
|
7492
|
-
cellWidth: gridMetrics.cellWidth,
|
|
7493
|
-
cellHeight: gridMetrics.cellHeight,
|
|
7494
|
-
noise,
|
|
7495
|
-
timeMs,
|
|
7496
|
-
});
|
|
7499
|
+
const glyphDescriptor = glyphGrid[rowIndex][columnIndex];
|
|
7497
7500
|
if (!glyphDescriptor) {
|
|
7498
7501
|
continue;
|
|
7499
7502
|
}
|
|
7503
|
+
const point = resolveAsciiSampleGridPoint(gridMetrics, columnIndex, rowIndex);
|
|
7500
7504
|
context.fillStyle = glyphDescriptor.color;
|
|
7501
7505
|
context.fillText(glyphDescriptor.character, point.x, point.y);
|
|
7502
7506
|
}
|
|
7503
7507
|
}
|
|
7504
7508
|
context.restore();
|
|
7505
7509
|
},
|
|
7510
|
+
renderTerminalText({ columns, rows, palette, createRandom, timeMs, interaction }) {
|
|
7511
|
+
const staticRandom = createRandom('ascii-octopus-static');
|
|
7512
|
+
const layout = createAsciiOctopusLayout(TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE, timeMs, createRandom, staticRandom, interaction);
|
|
7513
|
+
return createAsciiOctopusGlyphGrid({
|
|
7514
|
+
sampleGrid: createAsciiTerminalSampleGrid(columns, rows),
|
|
7515
|
+
layout,
|
|
7516
|
+
palette,
|
|
7517
|
+
cellRandom: createRandom('ascii-octopus-cells'),
|
|
7518
|
+
timeMs,
|
|
7519
|
+
});
|
|
7520
|
+
},
|
|
7506
7521
|
};
|
|
7522
|
+
/**
|
|
7523
|
+
* Resolves the geometry point sampled for one cell of the grid.
|
|
7524
|
+
*
|
|
7525
|
+
* @param sampleGrid Character-cell sampling grid.
|
|
7526
|
+
* @param columnIndex Zero-based cell column.
|
|
7527
|
+
* @param rowIndex Zero-based cell row.
|
|
7528
|
+
* @returns Sampled point in the octopus coordinate space.
|
|
7529
|
+
*
|
|
7530
|
+
* @private helper of `asciiOctopusAvatarVisual`
|
|
7531
|
+
*/
|
|
7532
|
+
function resolveAsciiSampleGridPoint(sampleGrid, columnIndex, rowIndex) {
|
|
7533
|
+
return {
|
|
7534
|
+
x: sampleGrid.offsetX + columnIndex * sampleGrid.cellWidth,
|
|
7535
|
+
y: sampleGrid.offsetY + rowIndex * sampleGrid.cellHeight,
|
|
7536
|
+
};
|
|
7537
|
+
}
|
|
7538
|
+
/**
|
|
7539
|
+
* Builds the sampling grid used when the octopus is painted straight into terminal character cells.
|
|
7540
|
+
*
|
|
7541
|
+
* @param columns Available terminal columns.
|
|
7542
|
+
* @param rows Available terminal rows.
|
|
7543
|
+
* @returns Character-cell sampling grid covering the whole octopus coordinate space.
|
|
7544
|
+
*
|
|
7545
|
+
* @private helper of `asciiOctopusAvatarVisual`
|
|
7546
|
+
*/
|
|
7547
|
+
function createAsciiTerminalSampleGrid(columns, rows) {
|
|
7548
|
+
const cellWidth = TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE / columns;
|
|
7549
|
+
const cellHeight = TERMINAL_ASCII_OCTOPUS_LAYOUT_SIZE / rows;
|
|
7550
|
+
return {
|
|
7551
|
+
cellWidth,
|
|
7552
|
+
cellHeight,
|
|
7553
|
+
columnCount: columns,
|
|
7554
|
+
rowCount: rows,
|
|
7555
|
+
offsetX: cellWidth / 2,
|
|
7556
|
+
offsetY: cellHeight / 2,
|
|
7557
|
+
};
|
|
7558
|
+
}
|
|
7559
|
+
/**
|
|
7560
|
+
* Samples the morphing octopus field into one deterministic grid of ASCII glyphs.
|
|
7561
|
+
*
|
|
7562
|
+
* The field is sampled on a low-resolution grid so the shape stays organic while the glyph
|
|
7563
|
+
* layout remains deterministic for the same avatar input. Both the canvas visual and the
|
|
7564
|
+
* terminal visual share this single sampling pass so they always describe the same octopus.
|
|
7565
|
+
*
|
|
7566
|
+
* @param options Sampling grid, prepared octopus layout, palette, per-cell noise, and animation time.
|
|
7567
|
+
* @returns Resolved glyph of every sampled cell, where `null` means an empty cell.
|
|
7568
|
+
*
|
|
7569
|
+
* @private helper of `asciiOctopusAvatarVisual`
|
|
7570
|
+
*/
|
|
7571
|
+
function createAsciiOctopusGlyphGrid(options) {
|
|
7572
|
+
const { sampleGrid, layout, palette, cellRandom, timeMs } = options;
|
|
7573
|
+
const glyphGrid = [];
|
|
7574
|
+
for (let rowIndex = 0; rowIndex < sampleGrid.rowCount; rowIndex++) {
|
|
7575
|
+
const glyphRow = [];
|
|
7576
|
+
for (let columnIndex = 0; columnIndex < sampleGrid.columnCount; columnIndex++) {
|
|
7577
|
+
const point = resolveAsciiSampleGridPoint(sampleGrid, columnIndex, rowIndex);
|
|
7578
|
+
const noise = cellRandom();
|
|
7579
|
+
glyphRow.push(resolveAsciiGlyph({
|
|
7580
|
+
point,
|
|
7581
|
+
layout,
|
|
7582
|
+
palette,
|
|
7583
|
+
cellWidth: sampleGrid.cellWidth,
|
|
7584
|
+
cellHeight: sampleGrid.cellHeight,
|
|
7585
|
+
noise,
|
|
7586
|
+
timeMs,
|
|
7587
|
+
}));
|
|
7588
|
+
}
|
|
7589
|
+
glyphGrid.push(glyphRow);
|
|
7590
|
+
}
|
|
7591
|
+
return glyphGrid;
|
|
7592
|
+
}
|
|
7507
7593
|
/**
|
|
7508
7594
|
* Draws the dark terminal-like glow behind the ASCII octopus.
|
|
7509
7595
|
*
|
|
@@ -21750,7 +21836,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21750
21836
|
return agentBookStats.isFile();
|
|
21751
21837
|
}
|
|
21752
21838
|
catch (error) {
|
|
21753
|
-
if (isFileNotFoundError$
|
|
21839
|
+
if (isFileNotFoundError$6(error)) {
|
|
21754
21840
|
return false;
|
|
21755
21841
|
}
|
|
21756
21842
|
throw error;
|
|
@@ -21759,7 +21845,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21759
21845
|
/**
|
|
21760
21846
|
* Returns true when one filesystem error indicates a missing path.
|
|
21761
21847
|
*/
|
|
21762
|
-
function isFileNotFoundError$
|
|
21848
|
+
function isFileNotFoundError$6(error) {
|
|
21763
21849
|
return Boolean(error &&
|
|
21764
21850
|
typeof error === 'object' &&
|
|
21765
21851
|
'code' in error &&
|
|
@@ -21789,7 +21875,7 @@ async function readQueuedMessageDirectoryEntries(queuedMessagesDirectoryPath) {
|
|
|
21789
21875
|
return await readdir(queuedMessagesDirectoryPath, { withFileTypes: true });
|
|
21790
21876
|
}
|
|
21791
21877
|
catch (error) {
|
|
21792
|
-
if (isFileNotFoundError$
|
|
21878
|
+
if (isFileNotFoundError$5(error)) {
|
|
21793
21879
|
return [];
|
|
21794
21880
|
}
|
|
21795
21881
|
throw error;
|
|
@@ -21810,7 +21896,7 @@ function normalizeRelativePath$2(relativePath) {
|
|
|
21810
21896
|
/**
|
|
21811
21897
|
* Returns true when an error is a missing-path filesystem error.
|
|
21812
21898
|
*/
|
|
21813
|
-
function isFileNotFoundError$
|
|
21899
|
+
function isFileNotFoundError$5(error) {
|
|
21814
21900
|
return Boolean(error &&
|
|
21815
21901
|
typeof error === 'object' &&
|
|
21816
21902
|
'code' in error &&
|
|
@@ -22288,7 +22374,7 @@ async function runGitCommand(options) {
|
|
|
22288
22374
|
lastIndexLockState = await readGitIndexLockState(cwd, options.env);
|
|
22289
22375
|
if ((lastIndexLockState === null || lastIndexLockState === void 0 ? void 0 : lastIndexLockState.isStale) && !isStaleIndexLockRemoved) {
|
|
22290
22376
|
await unlink(lastIndexLockState.path).catch((unlinkError) => {
|
|
22291
|
-
if (isFileNotFoundError$
|
|
22377
|
+
if (isFileNotFoundError$4(unlinkError)) {
|
|
22292
22378
|
return;
|
|
22293
22379
|
}
|
|
22294
22380
|
throw unlinkError;
|
|
@@ -22349,7 +22435,7 @@ async function readGitIndexLockState(cwd, env) {
|
|
|
22349
22435
|
};
|
|
22350
22436
|
}
|
|
22351
22437
|
catch (error) {
|
|
22352
|
-
if (isFileNotFoundError$
|
|
22438
|
+
if (isFileNotFoundError$4(error)) {
|
|
22353
22439
|
return undefined;
|
|
22354
22440
|
}
|
|
22355
22441
|
throw error;
|
|
@@ -22406,7 +22492,7 @@ function formatDelay$1(delayMs) {
|
|
|
22406
22492
|
/**
|
|
22407
22493
|
* Detects missing-file errors while inspecting the lock file.
|
|
22408
22494
|
*/
|
|
22409
|
-
function isFileNotFoundError$
|
|
22495
|
+
function isFileNotFoundError$4(error) {
|
|
22410
22496
|
return Boolean(error &&
|
|
22411
22497
|
typeof error === 'object' &&
|
|
22412
22498
|
'code' in error &&
|
|
@@ -27302,66 +27388,125 @@ function renderAvatarVisual(options, resolvedAvatarRenderDefinition) {
|
|
|
27302
27388
|
});
|
|
27303
27389
|
}
|
|
27304
27390
|
|
|
27391
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
27305
27392
|
/**
|
|
27306
|
-
*
|
|
27393
|
+
* ANSI escape sequence that resets all colors and attributes.
|
|
27307
27394
|
*
|
|
27308
27395
|
* @private within the repository
|
|
27309
27396
|
*/
|
|
27310
|
-
const
|
|
27397
|
+
const ANSI_RESET = '\u001b[0m';
|
|
27311
27398
|
/**
|
|
27312
|
-
*
|
|
27399
|
+
* Maximum spread between RGB channels for a color to be treated as (nearly) achromatic gray.
|
|
27313
27400
|
*
|
|
27314
27401
|
* @private within the repository
|
|
27315
27402
|
*/
|
|
27316
|
-
const
|
|
27403
|
+
const ANSI_256_ACHROMATIC_CHANNEL_SPREAD = 12;
|
|
27317
27404
|
/**
|
|
27318
|
-
*
|
|
27405
|
+
* Gray level above which an achromatic color maps to the pure white color-cube entry.
|
|
27319
27406
|
*
|
|
27320
27407
|
* @private within the repository
|
|
27321
27408
|
*/
|
|
27322
|
-
const
|
|
27409
|
+
const ANSI_256_NEAR_WHITE_GRAY_LEVEL = 246;
|
|
27323
27410
|
/**
|
|
27324
|
-
*
|
|
27411
|
+
* Index of pure white inside the 6×6×6 ANSI color cube.
|
|
27325
27412
|
*
|
|
27326
27413
|
* @private within the repository
|
|
27327
27414
|
*/
|
|
27328
|
-
const
|
|
27415
|
+
const ANSI_256_WHITE_INDEX = 231;
|
|
27329
27416
|
/**
|
|
27330
|
-
*
|
|
27417
|
+
* Brightness of the lightest entry of the ANSI 256 grayscale ramp.
|
|
27331
27418
|
*
|
|
27332
27419
|
* @private within the repository
|
|
27333
27420
|
*/
|
|
27334
|
-
const
|
|
27421
|
+
const ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL = 238;
|
|
27335
27422
|
/**
|
|
27336
|
-
*
|
|
27423
|
+
* Number of grayscale ramp steps above its first entry (ANSI indexes 232-255).
|
|
27337
27424
|
*
|
|
27338
27425
|
* @private within the repository
|
|
27339
27426
|
*/
|
|
27340
|
-
const
|
|
27427
|
+
const ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN = 23;
|
|
27341
27428
|
/**
|
|
27342
|
-
*
|
|
27429
|
+
* Creates the ANSI escape code that sets the foreground color of following characters.
|
|
27430
|
+
*
|
|
27431
|
+
* @param color Color painted onto the characters.
|
|
27432
|
+
* @param colorDepth Color depth supported by the target terminal.
|
|
27433
|
+
* @returns ANSI escape sequence.
|
|
27343
27434
|
*
|
|
27344
27435
|
* @private within the repository
|
|
27345
27436
|
*/
|
|
27346
|
-
|
|
27437
|
+
function createAnsiForegroundColorCode(color, colorDepth) {
|
|
27438
|
+
if (colorDepth === 'TRUE_COLOR') {
|
|
27439
|
+
return `\u001b[38;2;${color.red};${color.green};${color.blue}m`;
|
|
27440
|
+
}
|
|
27441
|
+
return `\u001b[38;5;${mapColorToAnsi256(color)}m`;
|
|
27442
|
+
}
|
|
27347
27443
|
/**
|
|
27348
|
-
*
|
|
27444
|
+
* Creates the ANSI escape code that sets the background color of following characters.
|
|
27445
|
+
*
|
|
27446
|
+
* @param color Color painted behind the characters.
|
|
27447
|
+
* @param colorDepth Color depth supported by the target terminal.
|
|
27448
|
+
* @returns ANSI escape sequence.
|
|
27349
27449
|
*
|
|
27350
27450
|
* @private within the repository
|
|
27351
27451
|
*/
|
|
27352
|
-
|
|
27452
|
+
function createAnsiBackgroundColorCode(color, colorDepth) {
|
|
27453
|
+
if (colorDepth === 'TRUE_COLOR') {
|
|
27454
|
+
return `\u001b[48;2;${color.red};${color.green};${color.blue}m`;
|
|
27455
|
+
}
|
|
27456
|
+
return `\u001b[48;5;${mapColorToAnsi256(color)}m`;
|
|
27457
|
+
}
|
|
27353
27458
|
/**
|
|
27354
|
-
*
|
|
27459
|
+
* Maps a 24-bit color onto the closest entry of the 256-color ANSI palette.
|
|
27460
|
+
*
|
|
27461
|
+
* Uses the 6×6×6 color cube (entries 16-231) and the grayscale ramp (entries 232-255).
|
|
27462
|
+
*
|
|
27463
|
+
* @private helper of `createAnsiForegroundColorCode` and `createAnsiBackgroundColorCode`
|
|
27464
|
+
*/
|
|
27465
|
+
function mapColorToAnsi256(color) {
|
|
27466
|
+
const { red, green, blue } = color;
|
|
27467
|
+
// Note: Prefer the finer grayscale ramp when the color is (nearly) achromatic
|
|
27468
|
+
const maxChannel = Math.max(red, green, blue);
|
|
27469
|
+
const minChannel = Math.min(red, green, blue);
|
|
27470
|
+
if (maxChannel - minChannel < ANSI_256_ACHROMATIC_CHANNEL_SPREAD) {
|
|
27471
|
+
const gray = Math.round((red + green + blue) / 3);
|
|
27472
|
+
if (gray < 4) {
|
|
27473
|
+
return 16; // <- Note: Pure black lives in the color cube
|
|
27474
|
+
}
|
|
27475
|
+
if (gray > ANSI_256_NEAR_WHITE_GRAY_LEVEL) {
|
|
27476
|
+
return ANSI_256_WHITE_INDEX; // <- Note: Pure white lives in the color cube
|
|
27477
|
+
}
|
|
27478
|
+
return 232 + Math.round(((gray - 8) / ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL) * ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN);
|
|
27479
|
+
}
|
|
27480
|
+
const redIndex = Math.round((red / 255) * 5);
|
|
27481
|
+
const greenIndex = Math.round((green / 255) * 5);
|
|
27482
|
+
const blueIndex = Math.round((blue / 255) * 5);
|
|
27483
|
+
return 16 + 36 * redIndex + 6 * greenIndex + blueIndex;
|
|
27484
|
+
}
|
|
27485
|
+
|
|
27486
|
+
/**
|
|
27487
|
+
* Default alpha channel value below which a cell is rendered as terminal background.
|
|
27355
27488
|
*
|
|
27356
27489
|
* @private within the repository
|
|
27357
27490
|
*/
|
|
27358
|
-
const
|
|
27491
|
+
const DEFAULT_ALPHA_THRESHOLD = 32;
|
|
27359
27492
|
/**
|
|
27360
|
-
* Number of
|
|
27493
|
+
* Number of channels per pixel in an RGBA buffer.
|
|
27361
27494
|
*
|
|
27362
27495
|
* @private within the repository
|
|
27363
27496
|
*/
|
|
27364
|
-
const
|
|
27497
|
+
const RGBA_CHANNEL_COUNT = 4;
|
|
27498
|
+
/**
|
|
27499
|
+
* Upper half block character - foreground paints the top pixel, background paints the bottom pixel.
|
|
27500
|
+
*
|
|
27501
|
+
* @private within the repository
|
|
27502
|
+
*/
|
|
27503
|
+
const UPPER_HALF_BLOCK = '▀'; // <- ▀
|
|
27504
|
+
/**
|
|
27505
|
+
* Lower half block character - foreground paints the bottom pixel while the top pixel stays transparent.
|
|
27506
|
+
*
|
|
27507
|
+
* @private within the repository
|
|
27508
|
+
*/
|
|
27509
|
+
const LOWER_HALF_BLOCK = '▄'; // <- ▄
|
|
27365
27510
|
/**
|
|
27366
27511
|
* Converts raw RGBA image pixels into colored ASCII art for ANSI terminals.
|
|
27367
27512
|
*
|
|
@@ -27410,17 +27555,17 @@ function convertImageDataToAsciiArt(options) {
|
|
|
27410
27555
|
let nextBackgroundCode;
|
|
27411
27556
|
if (topHalfColor.isOpaque && bottomHalfColor.isOpaque) {
|
|
27412
27557
|
character = UPPER_HALF_BLOCK;
|
|
27413
|
-
nextForegroundCode =
|
|
27414
|
-
nextBackgroundCode =
|
|
27558
|
+
nextForegroundCode = createAnsiForegroundColorCode(topHalfColor, colorDepth);
|
|
27559
|
+
nextBackgroundCode = createAnsiBackgroundColorCode(bottomHalfColor, colorDepth);
|
|
27415
27560
|
}
|
|
27416
27561
|
else if (topHalfColor.isOpaque) {
|
|
27417
27562
|
character = UPPER_HALF_BLOCK;
|
|
27418
|
-
nextForegroundCode =
|
|
27563
|
+
nextForegroundCode = createAnsiForegroundColorCode(topHalfColor, colorDepth);
|
|
27419
27564
|
nextBackgroundCode = undefined;
|
|
27420
27565
|
}
|
|
27421
27566
|
else if (bottomHalfColor.isOpaque) {
|
|
27422
27567
|
character = LOWER_HALF_BLOCK;
|
|
27423
|
-
nextForegroundCode =
|
|
27568
|
+
nextForegroundCode = createAnsiForegroundColorCode(bottomHalfColor, colorDepth);
|
|
27424
27569
|
nextBackgroundCode = undefined;
|
|
27425
27570
|
}
|
|
27426
27571
|
else {
|
|
@@ -27495,55 +27640,6 @@ function computeHalfCellColor(imageData, columnIndex, halfCellRowIndex, columns,
|
|
|
27495
27640
|
isOpaque: true,
|
|
27496
27641
|
};
|
|
27497
27642
|
}
|
|
27498
|
-
/**
|
|
27499
|
-
* Creates the ANSI escape code that sets the foreground color of following characters.
|
|
27500
|
-
*
|
|
27501
|
-
* @private helper of `convertImageDataToAsciiArt`
|
|
27502
|
-
*/
|
|
27503
|
-
function createForegroundColorCode(color, colorDepth) {
|
|
27504
|
-
if (colorDepth === 'TRUE_COLOR') {
|
|
27505
|
-
return `\u001b[38;2;${color.red};${color.green};${color.blue}m`;
|
|
27506
|
-
}
|
|
27507
|
-
return `\u001b[38;5;${mapColorToAnsi256(color)}m`;
|
|
27508
|
-
}
|
|
27509
|
-
/**
|
|
27510
|
-
* Creates the ANSI escape code that sets the background color of following characters.
|
|
27511
|
-
*
|
|
27512
|
-
* @private helper of `convertImageDataToAsciiArt`
|
|
27513
|
-
*/
|
|
27514
|
-
function createBackgroundColorCode(color, colorDepth) {
|
|
27515
|
-
if (colorDepth === 'TRUE_COLOR') {
|
|
27516
|
-
return `\u001b[48;2;${color.red};${color.green};${color.blue}m`;
|
|
27517
|
-
}
|
|
27518
|
-
return `\u001b[48;5;${mapColorToAnsi256(color)}m`;
|
|
27519
|
-
}
|
|
27520
|
-
/**
|
|
27521
|
-
* Maps a 24-bit color onto the closest entry of the 256-color ANSI palette.
|
|
27522
|
-
*
|
|
27523
|
-
* Uses the 6×6×6 color cube (entries 16-231) and the grayscale ramp (entries 232-255).
|
|
27524
|
-
*
|
|
27525
|
-
* @private helper of `convertImageDataToAsciiArt`
|
|
27526
|
-
*/
|
|
27527
|
-
function mapColorToAnsi256(color) {
|
|
27528
|
-
const { red, green, blue } = color;
|
|
27529
|
-
// Note: Prefer the finer grayscale ramp when the color is (nearly) achromatic
|
|
27530
|
-
const maxChannel = Math.max(red, green, blue);
|
|
27531
|
-
const minChannel = Math.min(red, green, blue);
|
|
27532
|
-
if (maxChannel - minChannel < ANSI_256_ACHROMATIC_CHANNEL_SPREAD) {
|
|
27533
|
-
const gray = Math.round((red + green + blue) / 3);
|
|
27534
|
-
if (gray < 4) {
|
|
27535
|
-
return 16; // <- Note: Pure black lives in the color cube
|
|
27536
|
-
}
|
|
27537
|
-
if (gray > ANSI_256_NEAR_WHITE_GRAY_LEVEL) {
|
|
27538
|
-
return ANSI_256_WHITE_INDEX; // <- Note: Pure white lives in the color cube
|
|
27539
|
-
}
|
|
27540
|
-
return 232 + Math.round(((gray - 8) / ANSI_256_GRAYSCALE_RAMP_MAX_LEVEL) * ANSI_256_GRAYSCALE_RAMP_INDEX_SPAN);
|
|
27541
|
-
}
|
|
27542
|
-
const redIndex = Math.round((red / 255) * 5);
|
|
27543
|
-
const greenIndex = Math.round((green / 255) * 5);
|
|
27544
|
-
const blueIndex = Math.round((blue / 255) * 5);
|
|
27545
|
-
return 16 + 36 * redIndex + 6 * greenIndex + blueIndex;
|
|
27546
|
-
}
|
|
27547
27643
|
|
|
27548
27644
|
/**
|
|
27549
27645
|
* Default output width of the ASCII avatar in terminal character cells.
|
|
@@ -27673,6 +27769,143 @@ function assertPositiveCanvasDimension(value, dimensionName) {
|
|
|
27673
27769
|
`));
|
|
27674
27770
|
}
|
|
27675
27771
|
|
|
27772
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
27773
|
+
/**
|
|
27774
|
+
* Height of one terminal character cell relative to its width.
|
|
27775
|
+
*
|
|
27776
|
+
* Terminal fonts are roughly twice as tall as they are wide, so a square avatar needs
|
|
27777
|
+
* twice as many columns as rows to keep its proportions.
|
|
27778
|
+
*
|
|
27779
|
+
* @private within the repository
|
|
27780
|
+
*/
|
|
27781
|
+
const TERMINAL_CHARACTER_CELL_ASPECT_RATIO = 2;
|
|
27782
|
+
/**
|
|
27783
|
+
* Renders one frame of a character-based avatar visual straight into terminal character cells.
|
|
27784
|
+
*
|
|
27785
|
+
* Visuals which are themselves made of characters, for example `AsciiOctopus`, lose their identity
|
|
27786
|
+
* when they are rasterized onto a canvas and converted back into half-block ASCII art - every glyph
|
|
27787
|
+
* is averaged away into a colored blob. Such visuals expose `renderTerminalText`, which paints the
|
|
27788
|
+
* terminal grid directly and is used instead of the raster pipeline.
|
|
27789
|
+
*
|
|
27790
|
+
* The square avatar is centered inside the requested grid the same way `renderAvatarVisualAsciiArt`
|
|
27791
|
+
* centers the avatar canvas inside a wider terminal frame.
|
|
27792
|
+
*
|
|
27793
|
+
* @param options Avatar identity, visual selection, and output grid size.
|
|
27794
|
+
* @returns Grid of `rows` rows of `columns` cells, or `null` when the visual has no terminal renderer.
|
|
27795
|
+
*
|
|
27796
|
+
* @private within the repository
|
|
27797
|
+
*/
|
|
27798
|
+
function renderAvatarVisualTerminalTextGrid(options) {
|
|
27799
|
+
const resolvedRenderDefinition = options.resolvedAvatarRenderDefinition ||
|
|
27800
|
+
resolveAvatarRenderDefinition({
|
|
27801
|
+
avatarDefinition: options.avatarDefinition,
|
|
27802
|
+
visualId: options.visualId,
|
|
27803
|
+
surface: options.surface,
|
|
27804
|
+
});
|
|
27805
|
+
const { renderTerminalText } = resolvedRenderDefinition.avatarVisual;
|
|
27806
|
+
if (renderTerminalText === undefined) {
|
|
27807
|
+
return null;
|
|
27808
|
+
}
|
|
27809
|
+
const avatarColumnCount = Math.min(options.columns, options.rows * TERMINAL_CHARACTER_CELL_ASPECT_RATIO);
|
|
27810
|
+
const avatarRowCount = Math.min(options.rows, Math.round(options.columns / TERMINAL_CHARACTER_CELL_ASPECT_RATIO));
|
|
27811
|
+
if (avatarColumnCount <= 0 || avatarRowCount <= 0) {
|
|
27812
|
+
return null;
|
|
27813
|
+
}
|
|
27814
|
+
const avatarGrid = renderTerminalText({
|
|
27815
|
+
columns: avatarColumnCount,
|
|
27816
|
+
rows: avatarRowCount,
|
|
27817
|
+
timeMs: options.timeMs,
|
|
27818
|
+
avatarDefinition: resolvedRenderDefinition.avatarDefinition,
|
|
27819
|
+
palette: resolvedRenderDefinition.palette,
|
|
27820
|
+
createRandom: resolvedRenderDefinition.createRandom,
|
|
27821
|
+
interaction: createIdleAvatarInteractionState(),
|
|
27822
|
+
});
|
|
27823
|
+
return centerAvatarVisualTerminalTextGrid(avatarGrid, options.columns, options.rows);
|
|
27824
|
+
}
|
|
27825
|
+
/**
|
|
27826
|
+
* Renders one frame of a character-based avatar visual into ANSI-colored terminal lines.
|
|
27827
|
+
*
|
|
27828
|
+
* @param options Avatar identity, visual selection, output grid size, and ANSI color depth.
|
|
27829
|
+
* @returns One ANSI-colored string per output row, or `null` when the visual has no terminal renderer.
|
|
27830
|
+
*
|
|
27831
|
+
* @private within the repository
|
|
27832
|
+
*/
|
|
27833
|
+
function renderAvatarVisualTerminalTextLines(options) {
|
|
27834
|
+
const terminalTextGrid = renderAvatarVisualTerminalTextGrid(options);
|
|
27835
|
+
if (terminalTextGrid === null) {
|
|
27836
|
+
return null;
|
|
27837
|
+
}
|
|
27838
|
+
return terminalTextGrid.map((terminalTextRow) => buildAvatarVisualTerminalTextLine(terminalTextRow, options.colorDepth || 'TRUE_COLOR'));
|
|
27839
|
+
}
|
|
27840
|
+
/**
|
|
27841
|
+
* Converts one avatar cell color into the opaque terminal color it is painted with.
|
|
27842
|
+
*
|
|
27843
|
+
* Terminal cells cannot be blended, so the cell alpha is composited onto the dark terminal
|
|
27844
|
+
* background and fully transparent cells keep the terminal background instead.
|
|
27845
|
+
*
|
|
27846
|
+
* @param color Cell color as a CSS color string, optionally with an alpha channel.
|
|
27847
|
+
* @returns Opaque terminal color or `null` when the cell should stay empty.
|
|
27848
|
+
*
|
|
27849
|
+
* @private helper of `renderAvatarVisualTerminalTextLines`
|
|
27850
|
+
*/
|
|
27851
|
+
function resolveAvatarVisualTerminalTextColor(color) {
|
|
27852
|
+
const parsedColor = Color.fromSafe(color);
|
|
27853
|
+
if (parsedColor.alpha < DEFAULT_ALPHA_THRESHOLD) {
|
|
27854
|
+
return null;
|
|
27855
|
+
}
|
|
27856
|
+
const opacity = parsedColor.alpha / 255;
|
|
27857
|
+
return {
|
|
27858
|
+
red: Math.round(parsedColor.red * opacity),
|
|
27859
|
+
green: Math.round(parsedColor.green * opacity),
|
|
27860
|
+
blue: Math.round(parsedColor.blue * opacity),
|
|
27861
|
+
};
|
|
27862
|
+
}
|
|
27863
|
+
/**
|
|
27864
|
+
* Places the square avatar grid into the center of the requested terminal grid.
|
|
27865
|
+
*
|
|
27866
|
+
* @private helper of `renderAvatarVisualTerminalTextGrid`
|
|
27867
|
+
*/
|
|
27868
|
+
function centerAvatarVisualTerminalTextGrid(avatarGrid, columns, rows) {
|
|
27869
|
+
var _a;
|
|
27870
|
+
const avatarRowCount = avatarGrid.length;
|
|
27871
|
+
const avatarColumnCount = ((_a = avatarGrid[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
27872
|
+
const leftCellCount = Math.floor((columns - avatarColumnCount) / 2);
|
|
27873
|
+
const topRowCount = Math.floor((rows - avatarRowCount) / 2);
|
|
27874
|
+
return Array.from({ length: rows }, (_, rowIndex) => {
|
|
27875
|
+
const avatarRow = avatarGrid[rowIndex - topRowCount];
|
|
27876
|
+
return Array.from({ length: columns }, (__, columnIndex) => (avatarRow === null || avatarRow === void 0 ? void 0 : avatarRow[columnIndex - leftCellCount]) || null);
|
|
27877
|
+
});
|
|
27878
|
+
}
|
|
27879
|
+
/**
|
|
27880
|
+
* Builds one ANSI-colored terminal line from resolved character cells.
|
|
27881
|
+
*
|
|
27882
|
+
* @private helper of `renderAvatarVisualTerminalTextLines`
|
|
27883
|
+
*/
|
|
27884
|
+
function buildAvatarVisualTerminalTextLine(terminalTextRow, colorDepth) {
|
|
27885
|
+
let line = '';
|
|
27886
|
+
let currentForegroundCode = undefined;
|
|
27887
|
+
for (const terminalTextCell of terminalTextRow) {
|
|
27888
|
+
const cellColor = terminalTextCell === null ? null : resolveAvatarVisualTerminalTextColor(terminalTextCell.color);
|
|
27889
|
+
const nextForegroundCode = cellColor === null ? undefined : createAnsiForegroundColorCode(cellColor, colorDepth);
|
|
27890
|
+
if (nextForegroundCode !== currentForegroundCode) {
|
|
27891
|
+
// Note: A reset is required whenever a previously set color must be cleared,
|
|
27892
|
+
// otherwise a stale color would bleed into the following empty cells.
|
|
27893
|
+
if (nextForegroundCode === undefined) {
|
|
27894
|
+
line += ANSI_RESET;
|
|
27895
|
+
}
|
|
27896
|
+
else {
|
|
27897
|
+
line += nextForegroundCode;
|
|
27898
|
+
}
|
|
27899
|
+
currentForegroundCode = nextForegroundCode;
|
|
27900
|
+
}
|
|
27901
|
+
line += cellColor === null || terminalTextCell === null ? ' ' : terminalTextCell.character;
|
|
27902
|
+
}
|
|
27903
|
+
if (currentForegroundCode !== undefined) {
|
|
27904
|
+
line += ANSI_RESET;
|
|
27905
|
+
}
|
|
27906
|
+
return line;
|
|
27907
|
+
}
|
|
27908
|
+
|
|
27676
27909
|
/**
|
|
27677
27910
|
* Default built-in avatar visual used when an agent does not define `META IMAGE`, `META AVATAR`, or `META VISUAL`.
|
|
27678
27911
|
*
|
|
@@ -27791,9 +28024,30 @@ function resolveTerminalAgentAvatarVisualIdFromAgentBasicInformation(agentBasicI
|
|
|
27791
28024
|
/**
|
|
27792
28025
|
* Renders one terminal avatar frame through the shared avatar-to-ASCII pipeline.
|
|
27793
28026
|
*
|
|
28027
|
+
* Visuals which are made of characters themselves, for example `AsciiOctopus`, paint the terminal
|
|
28028
|
+
* character grid directly, because rasterizing them into half-block ASCII art averages every glyph
|
|
28029
|
+
* away and makes them indistinguishable from the other blob-shaped visuals.
|
|
28030
|
+
*
|
|
27794
28031
|
* @private shared helper for terminal avatar rendering
|
|
27795
28032
|
*/
|
|
27796
28033
|
function renderTerminalAgentAvatarVisualFrame(options) {
|
|
28034
|
+
const terminalTextLines = renderAvatarVisualTerminalTextLines({
|
|
28035
|
+
avatarDefinition: options.avatarDefinition,
|
|
28036
|
+
visualId: options.avatarVisualId,
|
|
28037
|
+
surface: 'transparent',
|
|
28038
|
+
columns: TERMINAL_AGENT_AVATAR_VISUAL_COLUMNS,
|
|
28039
|
+
rows: TERMINAL_AGENT_AVATAR_VISUAL_ROWS,
|
|
28040
|
+
colorDepth: options.colorDepth,
|
|
28041
|
+
timeMs: options.animationTimeMs,
|
|
28042
|
+
resolvedAvatarRenderDefinition: options.resolvedAvatarRenderDefinition,
|
|
28043
|
+
});
|
|
28044
|
+
if (terminalTextLines !== null) {
|
|
28045
|
+
return terminalTextLines;
|
|
28046
|
+
}
|
|
28047
|
+
if (options.createCanvas === undefined) {
|
|
28048
|
+
// Note: A pixel-based visual cannot be rasterized without a canvas, so the caller keeps its default banner
|
|
28049
|
+
return [];
|
|
28050
|
+
}
|
|
27797
28051
|
return renderAvatarVisualAsciiArt({
|
|
27798
28052
|
avatarDefinition: options.avatarDefinition,
|
|
27799
28053
|
visualId: options.avatarVisualId,
|
|
@@ -28046,6 +28300,134 @@ function formatPromptRunCount(count) {
|
|
|
28046
28300
|
return `${count} prompt run${count === 1 ? '' : 's'}`;
|
|
28047
28301
|
}
|
|
28048
28302
|
|
|
28303
|
+
/**
|
|
28304
|
+
* ANSI sequence that clears the current terminal row.
|
|
28305
|
+
*
|
|
28306
|
+
* @private internal constant of coder run UI
|
|
28307
|
+
*/
|
|
28308
|
+
const CLEAR_CURRENT_LINE = '\x1b[2K';
|
|
28309
|
+
/**
|
|
28310
|
+
* ANSI control character that moves the cursor to the start of its current row.
|
|
28311
|
+
*
|
|
28312
|
+
* @private internal constant of coder run UI
|
|
28313
|
+
*/
|
|
28314
|
+
const MOVE_CURSOR_TO_LINE_START = '\r';
|
|
28315
|
+
/**
|
|
28316
|
+
* Builds one atomic ANSI update that changes the displayed coder-run terminal frame.
|
|
28317
|
+
*
|
|
28318
|
+
* A change in frame height requires a full rewrite to reserve or clear rows correctly. Frames with
|
|
28319
|
+
* the same height only rewrite contiguous groups of changed rows, returning to the frame bottom
|
|
28320
|
+
* between groups so each group can be assembled into the same output payload safely.
|
|
28321
|
+
*
|
|
28322
|
+
* @returns One terminal output payload, or `undefined` when the frame is already up to date.
|
|
28323
|
+
*
|
|
28324
|
+
* @private internal utility of coder run UI
|
|
28325
|
+
*/
|
|
28326
|
+
function buildCoderRunUiTerminalFrameUpdate(options) {
|
|
28327
|
+
const { previousFrameLines, nextFrameLines } = options;
|
|
28328
|
+
if (previousFrameLines.length === 0 && nextFrameLines.length === 0) {
|
|
28329
|
+
return undefined;
|
|
28330
|
+
}
|
|
28331
|
+
if (previousFrameLines.length === 0 || previousFrameLines.length !== nextFrameLines.length) {
|
|
28332
|
+
return buildFullCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines);
|
|
28333
|
+
}
|
|
28334
|
+
return buildChangedCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines);
|
|
28335
|
+
}
|
|
28336
|
+
/**
|
|
28337
|
+
* Builds an ANSI update which completely rewrites the reserved terminal frame area.
|
|
28338
|
+
*
|
|
28339
|
+
* @private helper of `buildCoderRunUiTerminalFrameUpdate`
|
|
28340
|
+
*/
|
|
28341
|
+
function buildFullCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines) {
|
|
28342
|
+
var _a;
|
|
28343
|
+
const previousFrameLineCount = previousFrameLines.length;
|
|
28344
|
+
const lineCountToRewrite = Math.max(previousFrameLineCount, nextFrameLines.length);
|
|
28345
|
+
const outputParts = [];
|
|
28346
|
+
if (previousFrameLineCount > 1) {
|
|
28347
|
+
outputParts.push(moveCursorUp(previousFrameLineCount - 1));
|
|
28348
|
+
}
|
|
28349
|
+
for (let lineIndex = 0; lineIndex < lineCountToRewrite; lineIndex++) {
|
|
28350
|
+
outputParts.push(CLEAR_CURRENT_LINE, MOVE_CURSOR_TO_LINE_START, (_a = nextFrameLines[lineIndex]) !== null && _a !== void 0 ? _a : '');
|
|
28351
|
+
if (lineIndex < lineCountToRewrite - 1) {
|
|
28352
|
+
outputParts.push('\n');
|
|
28353
|
+
}
|
|
28354
|
+
}
|
|
28355
|
+
const clearedTrailingLineCount = lineCountToRewrite - nextFrameLines.length;
|
|
28356
|
+
if (clearedTrailingLineCount > 0) {
|
|
28357
|
+
outputParts.push(moveCursorUp(clearedTrailingLineCount));
|
|
28358
|
+
}
|
|
28359
|
+
outputParts.push(MOVE_CURSOR_TO_LINE_START);
|
|
28360
|
+
return outputParts.join('');
|
|
28361
|
+
}
|
|
28362
|
+
/**
|
|
28363
|
+
* Builds an ANSI update that rewrites only contiguous groups of rows which changed.
|
|
28364
|
+
*
|
|
28365
|
+
* @private helper of `buildCoderRunUiTerminalFrameUpdate`
|
|
28366
|
+
*/
|
|
28367
|
+
function buildChangedCoderRunUiTerminalFrameUpdate(previousFrameLines, nextFrameLines) {
|
|
28368
|
+
const outputParts = [];
|
|
28369
|
+
let lineIndex = 0;
|
|
28370
|
+
while (lineIndex < nextFrameLines.length) {
|
|
28371
|
+
if (previousFrameLines[lineIndex] === nextFrameLines[lineIndex]) {
|
|
28372
|
+
lineIndex++;
|
|
28373
|
+
continue;
|
|
28374
|
+
}
|
|
28375
|
+
const changedRangeStartLineIndex = lineIndex;
|
|
28376
|
+
while (lineIndex + 1 < nextFrameLines.length &&
|
|
28377
|
+
previousFrameLines[lineIndex + 1] !== nextFrameLines[lineIndex + 1]) {
|
|
28378
|
+
lineIndex++;
|
|
28379
|
+
}
|
|
28380
|
+
appendChangedCoderRunUiTerminalFrameRange({
|
|
28381
|
+
outputParts,
|
|
28382
|
+
frameLineCount: nextFrameLines.length,
|
|
28383
|
+
changedRangeStartLineIndex,
|
|
28384
|
+
changedRangeEndLineIndex: lineIndex,
|
|
28385
|
+
nextFrameLines,
|
|
28386
|
+
});
|
|
28387
|
+
lineIndex++;
|
|
28388
|
+
}
|
|
28389
|
+
return outputParts.length === 0 ? undefined : outputParts.join('');
|
|
28390
|
+
}
|
|
28391
|
+
/**
|
|
28392
|
+
* Appends the cursor movements and row rewrites for one contiguous changed frame range.
|
|
28393
|
+
*
|
|
28394
|
+
* @private helper of `buildChangedCoderRunUiTerminalFrameUpdate`
|
|
28395
|
+
*/
|
|
28396
|
+
function appendChangedCoderRunUiTerminalFrameRange(options) {
|
|
28397
|
+
const { outputParts, frameLineCount, changedRangeStartLineIndex, changedRangeEndLineIndex, nextFrameLines } = options;
|
|
28398
|
+
const linesUpFromFrameBottom = frameLineCount - 1 - changedRangeStartLineIndex;
|
|
28399
|
+
if (linesUpFromFrameBottom > 0) {
|
|
28400
|
+
outputParts.push(moveCursorUp(linesUpFromFrameBottom));
|
|
28401
|
+
}
|
|
28402
|
+
for (let lineIndex = changedRangeStartLineIndex; lineIndex <= changedRangeEndLineIndex; lineIndex++) {
|
|
28403
|
+
outputParts.push(CLEAR_CURRENT_LINE, MOVE_CURSOR_TO_LINE_START, nextFrameLines[lineIndex]);
|
|
28404
|
+
if (lineIndex < changedRangeEndLineIndex) {
|
|
28405
|
+
outputParts.push('\n');
|
|
28406
|
+
}
|
|
28407
|
+
}
|
|
28408
|
+
const linesDownToFrameBottom = frameLineCount - 1 - changedRangeEndLineIndex;
|
|
28409
|
+
if (linesDownToFrameBottom > 0) {
|
|
28410
|
+
outputParts.push(moveCursorDown(linesDownToFrameBottom));
|
|
28411
|
+
}
|
|
28412
|
+
outputParts.push(MOVE_CURSOR_TO_LINE_START);
|
|
28413
|
+
}
|
|
28414
|
+
/**
|
|
28415
|
+
* Builds an ANSI sequence which moves the terminal cursor up by a positive number of rows.
|
|
28416
|
+
*
|
|
28417
|
+
* @private helper of `buildCoderRunUiTerminalFrameUpdate`
|
|
28418
|
+
*/
|
|
28419
|
+
function moveCursorUp(lineCount) {
|
|
28420
|
+
return `\x1b[${lineCount}A`;
|
|
28421
|
+
}
|
|
28422
|
+
/**
|
|
28423
|
+
* Builds an ANSI sequence which moves the terminal cursor down by a positive number of rows.
|
|
28424
|
+
*
|
|
28425
|
+
* @private helper of `buildCoderRunUiTerminalFrameUpdate`
|
|
28426
|
+
*/
|
|
28427
|
+
function moveCursorDown(lineCount) {
|
|
28428
|
+
return `\x1b[${lineCount}B`;
|
|
28429
|
+
}
|
|
28430
|
+
|
|
28049
28431
|
/**
|
|
28050
28432
|
* Calendar formats used when displaying the estimated completion time.
|
|
28051
28433
|
*
|
|
@@ -28530,58 +28912,6 @@ function renderCoderRunUi(startTime, options = {}) {
|
|
|
28530
28912
|
scheduleRender();
|
|
28531
28913
|
}, autoRefreshInterval);
|
|
28532
28914
|
}
|
|
28533
|
-
/**
|
|
28534
|
-
* Moves the cursor relative to the bottom of the current frame and rewrites one line in place.
|
|
28535
|
-
*/
|
|
28536
|
-
function rewriteFrameLine(frameLineCount, lineIndex, line) {
|
|
28537
|
-
const linesUpFromBottom = Math.max(0, frameLineCount - 1 - lineIndex);
|
|
28538
|
-
if (linesUpFromBottom > 0) {
|
|
28539
|
-
process.stdout.write(`\x1b[${linesUpFromBottom}A`);
|
|
28540
|
-
}
|
|
28541
|
-
clearLine(process.stdout, 0);
|
|
28542
|
-
cursorTo(process.stdout, 0);
|
|
28543
|
-
process.stdout.write(line);
|
|
28544
|
-
cursorTo(process.stdout, 0);
|
|
28545
|
-
if (linesUpFromBottom > 0) {
|
|
28546
|
-
process.stdout.write(`\x1b[${linesUpFromBottom}B`);
|
|
28547
|
-
cursorTo(process.stdout, 0);
|
|
28548
|
-
}
|
|
28549
|
-
}
|
|
28550
|
-
/**
|
|
28551
|
-
* Fully rewrites the reserved frame area.
|
|
28552
|
-
*/
|
|
28553
|
-
function renderFullFrame(lines) {
|
|
28554
|
-
var _a;
|
|
28555
|
-
const previousFrameLineCount = previousFrameLines.length;
|
|
28556
|
-
const linesToRewriteCount = Math.max(previousFrameLineCount, lines.length);
|
|
28557
|
-
if (previousFrameLineCount > 1) {
|
|
28558
|
-
process.stdout.write(`\x1b[${previousFrameLineCount - 1}A`);
|
|
28559
|
-
}
|
|
28560
|
-
for (let i = 0; i < linesToRewriteCount; i++) {
|
|
28561
|
-
clearLine(process.stdout, 0);
|
|
28562
|
-
cursorTo(process.stdout, 0);
|
|
28563
|
-
process.stdout.write((_a = lines[i]) !== null && _a !== void 0 ? _a : '');
|
|
28564
|
-
if (i < linesToRewriteCount - 1) {
|
|
28565
|
-
process.stdout.write('\n');
|
|
28566
|
-
}
|
|
28567
|
-
}
|
|
28568
|
-
const clearedTrailingLines = linesToRewriteCount - lines.length;
|
|
28569
|
-
if (clearedTrailingLines > 0) {
|
|
28570
|
-
process.stdout.write(`\x1b[${clearedTrailingLines}A`);
|
|
28571
|
-
}
|
|
28572
|
-
cursorTo(process.stdout, 0);
|
|
28573
|
-
}
|
|
28574
|
-
/**
|
|
28575
|
-
* Updates only the frame rows whose visible content changed.
|
|
28576
|
-
*/
|
|
28577
|
-
function renderChangedLines(lines) {
|
|
28578
|
-
for (let i = 0; i < lines.length; i++) {
|
|
28579
|
-
if (previousFrameLines[i] === lines[i]) {
|
|
28580
|
-
continue;
|
|
28581
|
-
}
|
|
28582
|
-
rewriteFrameLine(lines.length, i, lines[i]);
|
|
28583
|
-
}
|
|
28584
|
-
}
|
|
28585
28915
|
/**
|
|
28586
28916
|
* Builds the current frame snapshot from the latest state.
|
|
28587
28917
|
*/
|
|
@@ -28623,11 +28953,12 @@ function renderCoderRunUi(startTime, options = {}) {
|
|
|
28623
28953
|
isRendering = true;
|
|
28624
28954
|
try {
|
|
28625
28955
|
const lines = buildFrameLines();
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28956
|
+
const terminalFrameUpdate = buildCoderRunUiTerminalFrameUpdate({
|
|
28957
|
+
previousFrameLines,
|
|
28958
|
+
nextFrameLines: lines,
|
|
28959
|
+
});
|
|
28960
|
+
if (terminalFrameUpdate !== undefined) {
|
|
28961
|
+
process.stdout.write(terminalFrameUpdate);
|
|
28631
28962
|
}
|
|
28632
28963
|
previousFrameLines = [...lines];
|
|
28633
28964
|
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
@@ -29018,12 +29349,102 @@ class MultipleAgentRunUiPresenter {
|
|
|
29018
29349
|
|
|
29019
29350
|
/**
|
|
29020
29351
|
* Git commands used to list changed and untracked files in the working tree.
|
|
29352
|
+
*
|
|
29353
|
+
* Note: None of them ever lists a file ignored by `.gitignore`, so a path taken from here can always be staged.
|
|
29354
|
+
* Note: `--no-renames` is used because a detected rename would be reported as its destination path only, which
|
|
29355
|
+
* would leave the source path of a moved file out of the commit.
|
|
29021
29356
|
*/
|
|
29022
29357
|
const GIT_CHANGED_FILE_COMMANDS = [
|
|
29023
|
-
'git diff --name-only --',
|
|
29024
|
-
'git diff --name-only --cached --',
|
|
29358
|
+
'git diff --name-only --no-renames --',
|
|
29359
|
+
'git diff --name-only --no-renames --cached --',
|
|
29025
29360
|
'git ls-files --others --exclude-standard',
|
|
29026
29361
|
];
|
|
29362
|
+
/**
|
|
29363
|
+
* Captures hashes for files that are dirty before one operation starts.
|
|
29364
|
+
*/
|
|
29365
|
+
async function captureWorkingTreeChangesSnapshot(projectPath) {
|
|
29366
|
+
const changedFiles = await listWorkingTreeChangedFiles(projectPath);
|
|
29367
|
+
const changedFileHashes = new Map();
|
|
29368
|
+
for (const relativePath of changedFiles) {
|
|
29369
|
+
changedFileHashes.set(relativePath, await readWorkingTreeFileHash(projectPath, relativePath));
|
|
29370
|
+
}
|
|
29371
|
+
return { changedFileHashes };
|
|
29372
|
+
}
|
|
29373
|
+
/**
|
|
29374
|
+
* Lists the repository-relative paths which really changed since the snapshot was taken.
|
|
29375
|
+
*
|
|
29376
|
+
* A file which was already dirty before and was not touched afterwards is not listed, so the result contains
|
|
29377
|
+
* exactly the files changed by the operation which the snapshot wraps.
|
|
29378
|
+
*/
|
|
29379
|
+
async function listFilesChangedSinceSnapshot(projectPath, snapshot) {
|
|
29380
|
+
const changedFiles = await listWorkingTreeChangedFiles(projectPath);
|
|
29381
|
+
const changedFilesSinceSnapshot = [];
|
|
29382
|
+
for (const relativePath of changedFiles) {
|
|
29383
|
+
const currentFileHash = await readWorkingTreeFileHash(projectPath, relativePath);
|
|
29384
|
+
const hashBeforeOperation = snapshot.changedFileHashes.get(relativePath);
|
|
29385
|
+
const wasDirtyBeforeOperation = snapshot.changedFileHashes.has(relativePath);
|
|
29386
|
+
if (wasDirtyBeforeOperation && hashBeforeOperation === currentFileHash) {
|
|
29387
|
+
continue;
|
|
29388
|
+
}
|
|
29389
|
+
changedFilesSinceSnapshot.push(relativePath);
|
|
29390
|
+
}
|
|
29391
|
+
return changedFilesSinceSnapshot;
|
|
29392
|
+
}
|
|
29393
|
+
/**
|
|
29394
|
+
* Lists dirty tracked files and untracked files in the working tree.
|
|
29395
|
+
*/
|
|
29396
|
+
async function listWorkingTreeChangedFiles(projectPath) {
|
|
29397
|
+
const changedFiles = new Set();
|
|
29398
|
+
for (const command of GIT_CHANGED_FILE_COMMANDS) {
|
|
29399
|
+
const output = await $execCommand({
|
|
29400
|
+
command,
|
|
29401
|
+
cwd: projectPath,
|
|
29402
|
+
isVerbose: false,
|
|
29403
|
+
});
|
|
29404
|
+
for (const filePath of output.split('\n').map(normalizeGitFilePath).filter(Boolean)) {
|
|
29405
|
+
changedFiles.add(filePath);
|
|
29406
|
+
}
|
|
29407
|
+
}
|
|
29408
|
+
return [...changedFiles.values()];
|
|
29409
|
+
}
|
|
29410
|
+
/**
|
|
29411
|
+
* Reads the content hash of one repository-relative working tree file.
|
|
29412
|
+
*
|
|
29413
|
+
* @returns Hash of a regular file, otherwise `null` for a deleted or non-regular file.
|
|
29414
|
+
*/
|
|
29415
|
+
async function readWorkingTreeFileHash(projectPath, relativePath) {
|
|
29416
|
+
try {
|
|
29417
|
+
const absolutePath = resolve(projectPath, relativePath);
|
|
29418
|
+
const fileStats = await stat(absolutePath);
|
|
29419
|
+
if (!fileStats.isFile()) {
|
|
29420
|
+
return null;
|
|
29421
|
+
}
|
|
29422
|
+
const content = await readFile(absolutePath);
|
|
29423
|
+
return createHash('sha1').update(content).digest('hex');
|
|
29424
|
+
}
|
|
29425
|
+
catch (error) {
|
|
29426
|
+
if (isFileNotFoundError$3(error)) {
|
|
29427
|
+
return null;
|
|
29428
|
+
}
|
|
29429
|
+
throw error;
|
|
29430
|
+
}
|
|
29431
|
+
}
|
|
29432
|
+
/**
|
|
29433
|
+
* Normalizes Git output paths for internal matching.
|
|
29434
|
+
*/
|
|
29435
|
+
function normalizeGitFilePath(filePath) {
|
|
29436
|
+
return filePath.trim().replace(/\\/g, '/');
|
|
29437
|
+
}
|
|
29438
|
+
/**
|
|
29439
|
+
* Returns true when an error is a missing-file filesystem error.
|
|
29440
|
+
*/
|
|
29441
|
+
function isFileNotFoundError$3(error) {
|
|
29442
|
+
return Boolean(error &&
|
|
29443
|
+
typeof error === 'object' &&
|
|
29444
|
+
'code' in error &&
|
|
29445
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR'));
|
|
29446
|
+
}
|
|
29447
|
+
|
|
29027
29448
|
/**
|
|
29028
29449
|
* File extensions that should always be treated as binary.
|
|
29029
29450
|
*/
|
|
@@ -29071,34 +29492,20 @@ const BINARY_FILE_EXTENSIONS = new Set([
|
|
|
29071
29492
|
* Captures hashes for files that are dirty before a coding round starts.
|
|
29072
29493
|
*/
|
|
29073
29494
|
async function captureChangedFilesSnapshot(projectPath) {
|
|
29074
|
-
|
|
29075
|
-
const changedFileHashes = new Map();
|
|
29076
|
-
for (const relativePath of changedFiles) {
|
|
29077
|
-
const absolutePath = resolveProjectPath(projectPath, relativePath);
|
|
29078
|
-
const fileHash = await readFileHashIfRegularFile(absolutePath);
|
|
29079
|
-
if (fileHash) {
|
|
29080
|
-
changedFileHashes.set(relativePath, fileHash);
|
|
29081
|
-
}
|
|
29082
|
-
}
|
|
29083
|
-
return { changedFileHashes };
|
|
29495
|
+
return captureWorkingTreeChangesSnapshot(projectPath);
|
|
29084
29496
|
}
|
|
29085
29497
|
/**
|
|
29086
29498
|
* Normalizes CRLF to LF only in files that changed since the captured snapshot.
|
|
29087
29499
|
*/
|
|
29088
29500
|
async function normalizeLineEndingsInFilesChangedSinceSnapshot(options) {
|
|
29089
|
-
const changedFiles = await
|
|
29501
|
+
const changedFiles = await listFilesChangedSinceSnapshot(options.projectPath, options.snapshot);
|
|
29090
29502
|
let scannedFiles = 0;
|
|
29091
29503
|
let normalizedFiles = 0;
|
|
29092
29504
|
let skippedBinaryFiles = 0;
|
|
29093
29505
|
for (const relativePath of changedFiles) {
|
|
29094
29506
|
const absolutePath = resolveProjectPath(options.projectPath, relativePath);
|
|
29095
|
-
|
|
29096
|
-
if (!
|
|
29097
|
-
continue;
|
|
29098
|
-
}
|
|
29099
|
-
const hashBeforeRound = options.snapshot.changedFileHashes.get(relativePath);
|
|
29100
|
-
const hasChangedInRound = hashBeforeRound === undefined || hashBeforeRound !== currentFileHash;
|
|
29101
|
-
if (!hasChangedInRound) {
|
|
29507
|
+
// Note: A file deleted or replaced by a directory during the round has no content to normalize
|
|
29508
|
+
if (!(await isRegularFile(absolutePath))) {
|
|
29102
29509
|
continue;
|
|
29103
29510
|
}
|
|
29104
29511
|
scannedFiles++;
|
|
@@ -29124,29 +29531,6 @@ async function normalizeLineEndingsInFilesChangedSinceSnapshot(options) {
|
|
|
29124
29531
|
skippedBinaryFiles,
|
|
29125
29532
|
};
|
|
29126
29533
|
}
|
|
29127
|
-
/**
|
|
29128
|
-
* Lists dirty tracked files and untracked files in the working tree.
|
|
29129
|
-
*/
|
|
29130
|
-
async function listWorkingTreeChangedFiles(projectPath) {
|
|
29131
|
-
const changedFiles = new Set();
|
|
29132
|
-
for (const command of GIT_CHANGED_FILE_COMMANDS) {
|
|
29133
|
-
const output = await $execCommand({
|
|
29134
|
-
command,
|
|
29135
|
-
cwd: projectPath,
|
|
29136
|
-
isVerbose: false,
|
|
29137
|
-
});
|
|
29138
|
-
for (const filePath of output.split('\n').map(normalizeGitFilePath).filter(Boolean)) {
|
|
29139
|
-
changedFiles.add(filePath);
|
|
29140
|
-
}
|
|
29141
|
-
}
|
|
29142
|
-
return [...changedFiles.values()];
|
|
29143
|
-
}
|
|
29144
|
-
/**
|
|
29145
|
-
* Normalizes Git output paths for internal matching.
|
|
29146
|
-
*/
|
|
29147
|
-
function normalizeGitFilePath(filePath) {
|
|
29148
|
-
return filePath.trim().replace(/\\/g, '/');
|
|
29149
|
-
}
|
|
29150
29534
|
/**
|
|
29151
29535
|
* Resolves a repository-relative file path to an absolute path.
|
|
29152
29536
|
*/
|
|
@@ -29154,33 +29538,16 @@ function resolveProjectPath(projectPath, relativePath) {
|
|
|
29154
29538
|
return resolve(projectPath, relativePath);
|
|
29155
29539
|
}
|
|
29156
29540
|
/**
|
|
29157
|
-
*
|
|
29541
|
+
* Checks whether a path exists and is a regular file.
|
|
29158
29542
|
*/
|
|
29159
|
-
async function
|
|
29543
|
+
async function isRegularFile(path) {
|
|
29160
29544
|
try {
|
|
29161
|
-
|
|
29162
|
-
if (!fileStats.isFile()) {
|
|
29163
|
-
return undefined;
|
|
29164
|
-
}
|
|
29165
|
-
const content = await readFile(path);
|
|
29166
|
-
return createHash('sha1').update(content).digest('hex');
|
|
29545
|
+
return (await stat(path)).isFile();
|
|
29167
29546
|
}
|
|
29168
|
-
catch (
|
|
29169
|
-
|
|
29170
|
-
return undefined;
|
|
29171
|
-
}
|
|
29172
|
-
throw error;
|
|
29547
|
+
catch (_a) {
|
|
29548
|
+
return false;
|
|
29173
29549
|
}
|
|
29174
29550
|
}
|
|
29175
|
-
/**
|
|
29176
|
-
* Returns true when an error is a missing-file filesystem error.
|
|
29177
|
-
*/
|
|
29178
|
-
function isFileNotFoundError$1(error) {
|
|
29179
|
-
return Boolean(error &&
|
|
29180
|
-
typeof error === 'object' &&
|
|
29181
|
-
'code' in error &&
|
|
29182
|
-
((error.code === 'ENOENT') || error.code === 'ENOTDIR'));
|
|
29183
|
-
}
|
|
29184
29551
|
/**
|
|
29185
29552
|
* Detects binary files from extension.
|
|
29186
29553
|
*/
|
|
@@ -29370,7 +29737,7 @@ function readOptionalSigningKeyValue() {
|
|
|
29370
29737
|
/**
|
|
29371
29738
|
* Commits staged changes with the provided message using the dedicated coding-agent identity when configured,
|
|
29372
29739
|
* otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`,
|
|
29373
|
-
* `options.
|
|
29740
|
+
* `options.relevantPaths` restricts both the staging and the commit to the files of the current operation,
|
|
29374
29741
|
* `options.excludePaths` can keep temporary artifacts out of the created commit and
|
|
29375
29742
|
* `options.isEmptyCommitAllowed` keeps a round without any file change from failing.
|
|
29376
29743
|
*
|
|
@@ -29386,15 +29753,19 @@ async function commitChanges(message, options) {
|
|
|
29386
29753
|
try {
|
|
29387
29754
|
const agentEnv = buildAgentGitEnv();
|
|
29388
29755
|
const signingFlag = buildAgentGitSigningFlag();
|
|
29389
|
-
await
|
|
29756
|
+
const excludedGitPaths = await normalizeExcludedGitPaths(projectPath, [
|
|
29390
29757
|
commitMessagePath,
|
|
29391
29758
|
...((_a = options === null || options === void 0 ? void 0 : options.excludePaths) !== null && _a !== void 0 ? _a : []),
|
|
29392
29759
|
]);
|
|
29760
|
+
// Note: An excluded path must be dropped from the relevant paths as well, because a commit restricted by
|
|
29761
|
+
// a pathspec commits the working tree content of those paths and would ignore unstaging them
|
|
29762
|
+
const relevantPaths = excludeGitPaths(options === null || options === void 0 ? void 0 : options.relevantPaths, excludedGitPaths);
|
|
29763
|
+
await stageCommitChanges(projectPath, agentEnv, relevantPaths, excludedGitPaths);
|
|
29393
29764
|
await runGitCommand({
|
|
29394
29765
|
command: buildGitCommitCommand({
|
|
29395
29766
|
commitMessagePath,
|
|
29396
29767
|
signingFlag,
|
|
29397
|
-
|
|
29768
|
+
relevantPaths,
|
|
29398
29769
|
isEmptyCommitAllowed: options === null || options === void 0 ? void 0 : options.isEmptyCommitAllowed,
|
|
29399
29770
|
}),
|
|
29400
29771
|
cwd: projectPath,
|
|
@@ -29409,15 +29780,17 @@ async function commitChanges(message, options) {
|
|
|
29409
29780
|
}
|
|
29410
29781
|
}
|
|
29411
29782
|
/**
|
|
29412
|
-
* Stages repository changes and
|
|
29783
|
+
* Stages the relevant repository changes and unstages temporary files that should not end up inside the commit.
|
|
29413
29784
|
*/
|
|
29414
|
-
async function stageCommitChanges(projectPath, agentEnv,
|
|
29415
|
-
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
|
|
29419
|
-
|
|
29420
|
-
|
|
29785
|
+
async function stageCommitChanges(projectPath, agentEnv, relevantPaths, excludedGitPaths) {
|
|
29786
|
+
// Note: An operation which changed nothing relevant has nothing to stage
|
|
29787
|
+
if (relevantPaths === undefined || relevantPaths.length > 0) {
|
|
29788
|
+
await runGitCommand({
|
|
29789
|
+
command: buildGitAddCommand(relevantPaths),
|
|
29790
|
+
cwd: projectPath,
|
|
29791
|
+
env: agentEnv,
|
|
29792
|
+
});
|
|
29793
|
+
}
|
|
29421
29794
|
if (excludedGitPaths.length === 0) {
|
|
29422
29795
|
return;
|
|
29423
29796
|
}
|
|
@@ -29429,13 +29802,29 @@ async function stageCommitChanges(projectPath, agentEnv, includePaths, excludePa
|
|
|
29429
29802
|
});
|
|
29430
29803
|
}
|
|
29431
29804
|
/**
|
|
29432
|
-
* Builds the git add command for either the whole tree or
|
|
29805
|
+
* Builds the git add command for either the whole tree or the relevant paths of the current operation.
|
|
29433
29806
|
*/
|
|
29434
|
-
function buildGitAddCommand(
|
|
29435
|
-
if (!
|
|
29807
|
+
function buildGitAddCommand(relevantPaths) {
|
|
29808
|
+
if (!relevantPaths || relevantPaths.length === 0) {
|
|
29436
29809
|
return 'git add .';
|
|
29437
29810
|
}
|
|
29438
|
-
return `git add --all -- ${
|
|
29811
|
+
return `git add --all -- ${relevantPaths.map(quoteShellPath$1).join(' ')}`;
|
|
29812
|
+
}
|
|
29813
|
+
/**
|
|
29814
|
+
* Removes the excluded repository paths from the relevant paths of the current operation.
|
|
29815
|
+
*/
|
|
29816
|
+
function excludeGitPaths(relevantPaths, excludedGitPaths) {
|
|
29817
|
+
if (relevantPaths === undefined || excludedGitPaths.length === 0) {
|
|
29818
|
+
return relevantPaths;
|
|
29819
|
+
}
|
|
29820
|
+
const excludedGitPathSet = new Set(excludedGitPaths);
|
|
29821
|
+
return relevantPaths.filter((relevantPath) => !excludedGitPathSet.has(normalizeGitPathSeparators(relevantPath)));
|
|
29822
|
+
}
|
|
29823
|
+
/**
|
|
29824
|
+
* Normalizes path separators so a relevant path can be matched against a repository-relative Git path.
|
|
29825
|
+
*/
|
|
29826
|
+
function normalizeGitPathSeparators(path) {
|
|
29827
|
+
return path.replace(/\\/gu, '/');
|
|
29439
29828
|
}
|
|
29440
29829
|
/**
|
|
29441
29830
|
* Converts excluded filesystem paths into unique repository-relative Git paths.
|
|
@@ -29606,8 +29995,8 @@ function buildGitCommitCommand(options) {
|
|
|
29606
29995
|
commandParts.push('--allow-empty');
|
|
29607
29996
|
}
|
|
29608
29997
|
commandParts.push(`--file "${options.commitMessagePath}"`);
|
|
29609
|
-
if (options.
|
|
29610
|
-
commandParts.push('--', ...options.
|
|
29998
|
+
if (options.relevantPaths && options.relevantPaths.length > 0) {
|
|
29999
|
+
commandParts.push('--', ...options.relevantPaths.map(quoteShellPath$1));
|
|
29611
30000
|
}
|
|
29612
30001
|
return commandParts.join(' ');
|
|
29613
30002
|
}
|
|
@@ -29748,6 +30137,32 @@ function appendCoderContext(prompt, context) {
|
|
|
29748
30137
|
`);
|
|
29749
30138
|
}
|
|
29750
30139
|
|
|
30140
|
+
/**
|
|
30141
|
+
* Creates the step tracker of one prompt round.
|
|
30142
|
+
*
|
|
30143
|
+
* The tracker is the single place which knows which steps have finished and which one is running,
|
|
30144
|
+
* so both the finished `[x]` status line and the intermediate `[^]` in-progress status lines are
|
|
30145
|
+
* built from the very same data.
|
|
30146
|
+
*/
|
|
30147
|
+
function createCoderRunStepTracker(onStepStarted) {
|
|
30148
|
+
const steps = [];
|
|
30149
|
+
let reportedLoginMethod;
|
|
30150
|
+
return {
|
|
30151
|
+
steps,
|
|
30152
|
+
async startStep(kind) {
|
|
30153
|
+
await (onStepStarted === null || onStepStarted === void 0 ? void 0 : onStepStarted({
|
|
30154
|
+
startedStepKind: kind,
|
|
30155
|
+
finishedSteps: [...steps],
|
|
30156
|
+
loginMethod: reportedLoginMethod,
|
|
30157
|
+
}));
|
|
30158
|
+
},
|
|
30159
|
+
finishStep(step, loginMethod) {
|
|
30160
|
+
steps.push(step);
|
|
30161
|
+
reportedLoginMethod = loginMethod !== null && loginMethod !== void 0 ? loginMethod : reportedLoginMethod;
|
|
30162
|
+
},
|
|
30163
|
+
};
|
|
30164
|
+
}
|
|
30165
|
+
|
|
29751
30166
|
/**
|
|
29752
30167
|
* Maximum amount of test output embedded into an agent prompt.
|
|
29753
30168
|
*/
|
|
@@ -29793,7 +30208,7 @@ const SHELL_SCRIPT_EXTENSION = '.sh';
|
|
|
29793
30208
|
async function runPromptWithTestFeedback(options) {
|
|
29794
30209
|
var _a, _b, _c, _d;
|
|
29795
30210
|
const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
|
|
29796
|
-
const
|
|
30211
|
+
const stepTracker = createCoderRunStepTracker(options.onStepStarted);
|
|
29797
30212
|
if (!normalizedTestCommand) {
|
|
29798
30213
|
(_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
|
|
29799
30214
|
await waitForPromptAttemptPauseCheckpoint(options.waitForPauseCheckpoint, options.runner.name, 1);
|
|
@@ -29801,9 +30216,9 @@ async function runPromptWithTestFeedback(options) {
|
|
|
29801
30216
|
runOptions: options,
|
|
29802
30217
|
prompt: options.prompt,
|
|
29803
30218
|
kind: 'implementation',
|
|
29804
|
-
|
|
30219
|
+
stepTracker,
|
|
29805
30220
|
});
|
|
29806
|
-
return { ...result, attemptCount: 1, steps };
|
|
30221
|
+
return { ...result, attemptCount: 1, steps: stepTracker.steps };
|
|
29807
30222
|
}
|
|
29808
30223
|
const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
|
|
29809
30224
|
let promptForCurrentAttempt = options.prompt;
|
|
@@ -29814,7 +30229,7 @@ async function runPromptWithTestFeedback(options) {
|
|
|
29814
30229
|
runOptions: options,
|
|
29815
30230
|
prompt: promptForCurrentAttempt,
|
|
29816
30231
|
kind: attemptCount === 1 ? 'implementation' : 'fixing',
|
|
29817
|
-
|
|
30232
|
+
stepTracker,
|
|
29818
30233
|
});
|
|
29819
30234
|
await waitForVerificationPauseCheckpoint(options.waitForPauseCheckpoint, normalizedTestCommand, attemptCount);
|
|
29820
30235
|
console.info(colors.gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
|
|
@@ -29822,10 +30237,10 @@ async function runPromptWithTestFeedback(options) {
|
|
|
29822
30237
|
runPromptTestCommandExecutor,
|
|
29823
30238
|
testCommand: normalizedTestCommand,
|
|
29824
30239
|
runOptions: options,
|
|
29825
|
-
|
|
30240
|
+
stepTracker,
|
|
29826
30241
|
});
|
|
29827
30242
|
if (failedVerification === undefined) {
|
|
29828
|
-
return { ...result, attemptCount, steps };
|
|
30243
|
+
return { ...result, attemptCount, steps: stepTracker.steps };
|
|
29829
30244
|
}
|
|
29830
30245
|
const fullVerificationOutput = formatUnknownErrorDetails(failedVerification.error);
|
|
29831
30246
|
const feedbackVerificationOutput = limitTestOutput(fullVerificationOutput);
|
|
@@ -29851,7 +30266,8 @@ async function runPromptWithTestFeedback(options) {
|
|
|
29851
30266
|
* Runs one coding attempt through the runner, timing it and recording it as an implementation or fixing step.
|
|
29852
30267
|
*/
|
|
29853
30268
|
async function runRunnerPromptStep(options) {
|
|
29854
|
-
const { runOptions, prompt, kind,
|
|
30269
|
+
const { runOptions, prompt, kind, stepTracker } = options;
|
|
30270
|
+
await stepTracker.startStep(kind);
|
|
29855
30271
|
const stepStartedTimeMs = Date.now();
|
|
29856
30272
|
const result = await runOptions.runner.runPrompt({
|
|
29857
30273
|
prompt,
|
|
@@ -29861,7 +30277,7 @@ async function runRunnerPromptStep(options) {
|
|
|
29861
30277
|
preserveArtifactsOnSuccess: runOptions.preserveArtifactsOnSuccess,
|
|
29862
30278
|
waitForPauseCheckpoint: runOptions.waitForPauseCheckpoint,
|
|
29863
30279
|
});
|
|
29864
|
-
|
|
30280
|
+
stepTracker.finishStep({ kind, usage: result.usage, durationMs: Date.now() - stepStartedTimeMs }, result.loginMethod);
|
|
29865
30281
|
return result;
|
|
29866
30282
|
}
|
|
29867
30283
|
/**
|
|
@@ -29869,7 +30285,8 @@ async function runRunnerPromptStep(options) {
|
|
|
29869
30285
|
* returns the failure (or `undefined` when the verification passed).
|
|
29870
30286
|
*/
|
|
29871
30287
|
async function runVerificationStep(options) {
|
|
29872
|
-
const { runPromptTestCommandExecutor, testCommand, runOptions,
|
|
30288
|
+
const { runPromptTestCommandExecutor, testCommand, runOptions, stepTracker } = options;
|
|
30289
|
+
await stepTracker.startStep('testing');
|
|
29873
30290
|
const stepStartedTimeMs = Date.now();
|
|
29874
30291
|
try {
|
|
29875
30292
|
await runPromptTestCommandExecutor({
|
|
@@ -29885,7 +30302,7 @@ async function runVerificationStep(options) {
|
|
|
29885
30302
|
return { error };
|
|
29886
30303
|
}
|
|
29887
30304
|
finally {
|
|
29888
|
-
|
|
30305
|
+
stepTracker.finishStep({ kind: 'testing', usage: null, durationMs: Date.now() - stepStartedTimeMs });
|
|
29889
30306
|
}
|
|
29890
30307
|
}
|
|
29891
30308
|
/**
|
|
@@ -30069,6 +30486,140 @@ function buildProjectRuntimeInstructions(projectRuntimeApi) {
|
|
|
30069
30486
|
`);
|
|
30070
30487
|
}
|
|
30071
30488
|
|
|
30489
|
+
// Note: [💞] This file defines the shared TEAM workspace convention rather than one standalone entity.
|
|
30490
|
+
/**
|
|
30491
|
+
* Relative directory for an active TEAM conversation workspace.
|
|
30492
|
+
*
|
|
30493
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30494
|
+
*/
|
|
30495
|
+
const AGENT_TEAM_CONVERSATIONS_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH, 'team');
|
|
30496
|
+
/**
|
|
30497
|
+
* Relative directory where completed TEAM conversation transcripts are retained.
|
|
30498
|
+
*
|
|
30499
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30500
|
+
*/
|
|
30501
|
+
const AGENT_FINISHED_TEAM_CONVERSATIONS_DIRECTORY_PATH = join(AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, 'team');
|
|
30502
|
+
/**
|
|
30503
|
+
* Name of the JSON manifest that describes one TEAM conversation workspace.
|
|
30504
|
+
*
|
|
30505
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30506
|
+
*/
|
|
30507
|
+
const AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME = 'team.json';
|
|
30508
|
+
/**
|
|
30509
|
+
* Name of the read-only teammate-source directory inside one TEAM workspace.
|
|
30510
|
+
*
|
|
30511
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30512
|
+
*/
|
|
30513
|
+
const AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME = 'teammates';
|
|
30514
|
+
/**
|
|
30515
|
+
* Creates the stable directory name that belongs to one queued `.book` message.
|
|
30516
|
+
*
|
|
30517
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30518
|
+
*/
|
|
30519
|
+
function createAgentTeamConversationWorkspaceDirectoryName(messageFileName) {
|
|
30520
|
+
const rawBaseName = basename(messageFileName).replace(/\.book$/iu, '');
|
|
30521
|
+
const normalizedBaseName = rawBaseName.replace(/[^A-Za-z0-9._-]+/gu, '-').replace(/^[._-]+|[._-]+$/gu, '');
|
|
30522
|
+
return normalizedBaseName || 'message';
|
|
30523
|
+
}
|
|
30524
|
+
/**
|
|
30525
|
+
* Creates the relative active workspace path for one queued message.
|
|
30526
|
+
*
|
|
30527
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30528
|
+
*/
|
|
30529
|
+
function createAgentTeamConversationWorkspacePath(messageFileName) {
|
|
30530
|
+
return join(AGENT_TEAM_CONVERSATIONS_DIRECTORY_PATH, createAgentTeamConversationWorkspaceDirectoryName(messageFileName));
|
|
30531
|
+
}
|
|
30532
|
+
/**
|
|
30533
|
+
* Creates the relative completed-workspace path for one queued message.
|
|
30534
|
+
*
|
|
30535
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30536
|
+
*/
|
|
30537
|
+
function createFinishedAgentTeamConversationWorkspacePath(messageFileName) {
|
|
30538
|
+
return join(AGENT_FINISHED_TEAM_CONVERSATIONS_DIRECTORY_PATH, createAgentTeamConversationWorkspaceDirectoryName(messageFileName));
|
|
30539
|
+
}
|
|
30540
|
+
/**
|
|
30541
|
+
* Checks whether unknown JSON has the minimum shape required for a TEAM workspace manifest.
|
|
30542
|
+
*
|
|
30543
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30544
|
+
*/
|
|
30545
|
+
function isAgentTeamConversationWorkspaceManifest(value) {
|
|
30546
|
+
if (!value || typeof value !== 'object') {
|
|
30547
|
+
return false;
|
|
30548
|
+
}
|
|
30549
|
+
const manifest = value;
|
|
30550
|
+
if (manifest.version !== 1 ||
|
|
30551
|
+
!isPrimaryAgent(manifest.primaryAgent) ||
|
|
30552
|
+
!Array.isArray(manifest.teammates) ||
|
|
30553
|
+
!manifest.teammates.every(isTeammate)) {
|
|
30554
|
+
return false;
|
|
30555
|
+
}
|
|
30556
|
+
return true;
|
|
30557
|
+
}
|
|
30558
|
+
/**
|
|
30559
|
+
* Checks the primary-agent part of an untrusted manifest.
|
|
30560
|
+
*
|
|
30561
|
+
* @private internal utility of `isAgentTeamConversationWorkspaceManifest`
|
|
30562
|
+
*/
|
|
30563
|
+
function isPrimaryAgent(value) {
|
|
30564
|
+
return Boolean(value &&
|
|
30565
|
+
typeof value === 'object' &&
|
|
30566
|
+
typeof value.permanentId === 'string' &&
|
|
30567
|
+
typeof value.agentName === 'string');
|
|
30568
|
+
}
|
|
30569
|
+
/**
|
|
30570
|
+
* Checks one teammate part of an untrusted manifest.
|
|
30571
|
+
*
|
|
30572
|
+
* @private internal utility of `isAgentTeamConversationWorkspaceManifest`
|
|
30573
|
+
*/
|
|
30574
|
+
function isTeammate(value) {
|
|
30575
|
+
return Boolean(value &&
|
|
30576
|
+
typeof value === 'object' &&
|
|
30577
|
+
typeof value.permanentId === 'string' &&
|
|
30578
|
+
typeof value.agentName === 'string' &&
|
|
30579
|
+
typeof value.url === 'string' &&
|
|
30580
|
+
typeof value.instructions === 'string' &&
|
|
30581
|
+
typeof value.sourceFileName === 'string');
|
|
30582
|
+
}
|
|
30583
|
+
|
|
30584
|
+
/**
|
|
30585
|
+
* Builds the optional single-run TEAM consultation instructions for a coding harness.
|
|
30586
|
+
*/
|
|
30587
|
+
function buildAgentTeamPromptSection(workspace) {
|
|
30588
|
+
if (!workspace || workspace.manifest.teammates.length === 0) {
|
|
30589
|
+
return '';
|
|
30590
|
+
}
|
|
30591
|
+
const teammateSections = workspace.manifest.teammates.map((teammate) => {
|
|
30592
|
+
const sourcePath = toPromptPath(join(workspace.relativeWorkspacePath, AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME, teammate.sourceFileName));
|
|
30593
|
+
const transcriptPath = toPromptPath(join(workspace.relativeWorkspacePath, `${teammate.permanentId}--01.book`));
|
|
30594
|
+
const instructions = teammate.instructions.trim() || 'No additional TEAM instructions were provided.';
|
|
30595
|
+
return spaceTrim$1(`
|
|
30596
|
+
- ${teammate.agentName}
|
|
30597
|
+
- TEAM instructions: ${instructions}
|
|
30598
|
+
- Read-only source: \`${sourcePath}\`
|
|
30599
|
+
- First transcript file: \`${transcriptPath}\`
|
|
30600
|
+
`);
|
|
30601
|
+
});
|
|
30602
|
+
return spaceTrim$1((block) => `
|
|
30603
|
+
## Team consultations
|
|
30604
|
+
|
|
30605
|
+
The following teammates are available for this user turn:
|
|
30606
|
+
|
|
30607
|
+
${block(teammateSections.join('\n'))}
|
|
30608
|
+
|
|
30609
|
+
- 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.
|
|
30610
|
+
- The primary agent remains responsible for the final user-facing \`MESSAGE @Agent\` answer.
|
|
30611
|
+
- 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)}\`.
|
|
30612
|
+
- Name each transcript \`<teammate permanent id>--<sequence>.book\`, for example \`${workspace.manifest.teammates[0].permanentId}--01.book\`.
|
|
30613
|
+
- 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.
|
|
30614
|
+
`);
|
|
30615
|
+
}
|
|
30616
|
+
/**
|
|
30617
|
+
* Converts a filesystem-relative path into the portable path notation used in prompts.
|
|
30618
|
+
*/
|
|
30619
|
+
function toPromptPath(path) {
|
|
30620
|
+
return path.replace(/\\/gu, '/');
|
|
30621
|
+
}
|
|
30622
|
+
|
|
30072
30623
|
/**
|
|
30073
30624
|
* Builds the prompt sent to the selected coding runner for one queued user-thread book.
|
|
30074
30625
|
*/
|
|
@@ -30078,7 +30629,7 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30078
30629
|
|
|
30079
30630
|
- Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE @User\`
|
|
30080
30631
|
- Only change the queued message file by appending one new \`MESSAGE @Agent\` block
|
|
30081
|
-
|
|
30632
|
+
${block(buildAllowedFileChangesPromptLine(options.teamWorkspace))}
|
|
30082
30633
|
|
|
30083
30634
|
## Rules for the answering
|
|
30084
30635
|
|
|
@@ -30116,11 +30667,22 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30116
30667
|
|
|
30117
30668
|
${block(buildAgentProjectsPromptSection(options))}
|
|
30118
30669
|
|
|
30670
|
+
${block(buildAgentTeamPromptSection(options.teamWorkspace))}
|
|
30671
|
+
|
|
30119
30672
|
## This is how you should behave
|
|
30120
30673
|
|
|
30121
30674
|
${block(agentSystemMessage)}
|
|
30122
30675
|
`);
|
|
30123
30676
|
}
|
|
30677
|
+
/**
|
|
30678
|
+
* Explains the exact files that one coding harness may change for a user turn.
|
|
30679
|
+
*/
|
|
30680
|
+
function buildAllowedFileChangesPromptLine(teamWorkspace) {
|
|
30681
|
+
if (!teamWorkspace) {
|
|
30682
|
+
return `- Do not modify any other file in the repository, except files inside your own \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory`;
|
|
30683
|
+
}
|
|
30684
|
+
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, '/')}\``;
|
|
30685
|
+
}
|
|
30124
30686
|
|
|
30125
30687
|
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"}];
|
|
30126
30688
|
|
|
@@ -36820,6 +37382,148 @@ function formatAgentModelRequirementsForRunner(modelRequirements) {
|
|
|
36820
37382
|
.join('\n\n');
|
|
36821
37383
|
}
|
|
36822
37384
|
|
|
37385
|
+
/**
|
|
37386
|
+
* Moves harness-created TEAM conversation transcripts into the finished-message history.
|
|
37387
|
+
*/
|
|
37388
|
+
async function finalizeAgentTeamConversationWorkspace(options) {
|
|
37389
|
+
if (!options.workspace) {
|
|
37390
|
+
return null;
|
|
37391
|
+
}
|
|
37392
|
+
const activeWorkspacePath = join(options.projectPath, createAgentTeamConversationWorkspacePath(options.queuedMessage.fileName));
|
|
37393
|
+
const activeManifestPath = join(activeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
|
|
37394
|
+
const transcriptFileNames = await listAgentTeamTranscriptFileNames(activeWorkspacePath);
|
|
37395
|
+
if (!(await isExistingPath$1(activeManifestPath))) {
|
|
37396
|
+
return null;
|
|
37397
|
+
}
|
|
37398
|
+
if (transcriptFileNames.length === 0) {
|
|
37399
|
+
await rm(activeWorkspacePath, { recursive: true, force: true });
|
|
37400
|
+
return null;
|
|
37401
|
+
}
|
|
37402
|
+
const finishedRelativeWorkspacePath = createFinishedAgentTeamConversationWorkspacePath(options.queuedMessage.fileName);
|
|
37403
|
+
const finishedWorkspacePath = join(options.projectPath, finishedRelativeWorkspacePath);
|
|
37404
|
+
await rm(finishedWorkspacePath, { recursive: true, force: true });
|
|
37405
|
+
await mkdir(finishedWorkspacePath, { recursive: true });
|
|
37406
|
+
const relativePaths = [
|
|
37407
|
+
toPortablePath$1(join(finishedRelativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME)),
|
|
37408
|
+
];
|
|
37409
|
+
await rename(activeManifestPath, join(finishedWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME));
|
|
37410
|
+
for (const transcriptFileName of transcriptFileNames) {
|
|
37411
|
+
await rename(join(activeWorkspacePath, transcriptFileName), join(finishedWorkspacePath, transcriptFileName));
|
|
37412
|
+
relativePaths.push(toPortablePath$1(join(finishedRelativeWorkspacePath, transcriptFileName)));
|
|
37413
|
+
}
|
|
37414
|
+
await rm(activeWorkspacePath, { recursive: true, force: true });
|
|
37415
|
+
return { relativePaths };
|
|
37416
|
+
}
|
|
37417
|
+
/**
|
|
37418
|
+
* Lists only direct transcript `.book` files and deliberately excludes read-only teammate sources.
|
|
37419
|
+
*/
|
|
37420
|
+
async function listAgentTeamTranscriptFileNames(activeWorkspacePath) {
|
|
37421
|
+
try {
|
|
37422
|
+
const entries = await readdir(activeWorkspacePath, { withFileTypes: true });
|
|
37423
|
+
return entries
|
|
37424
|
+
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.book'))
|
|
37425
|
+
.map((entry) => entry.name)
|
|
37426
|
+
.sort((firstFileName, secondFileName) => firstFileName.localeCompare(secondFileName));
|
|
37427
|
+
}
|
|
37428
|
+
catch (error) {
|
|
37429
|
+
if (isFileNotFoundError$2(error)) {
|
|
37430
|
+
return [];
|
|
37431
|
+
}
|
|
37432
|
+
throw error;
|
|
37433
|
+
}
|
|
37434
|
+
}
|
|
37435
|
+
/**
|
|
37436
|
+
* Checks whether one fixed workspace path exists.
|
|
37437
|
+
*/
|
|
37438
|
+
async function isExistingPath$1(path) {
|
|
37439
|
+
try {
|
|
37440
|
+
await stat(path);
|
|
37441
|
+
return true;
|
|
37442
|
+
}
|
|
37443
|
+
catch (error) {
|
|
37444
|
+
if (isFileNotFoundError$2(error)) {
|
|
37445
|
+
return false;
|
|
37446
|
+
}
|
|
37447
|
+
throw error;
|
|
37448
|
+
}
|
|
37449
|
+
}
|
|
37450
|
+
/**
|
|
37451
|
+
* Converts a filesystem-relative path into a portable Git path.
|
|
37452
|
+
*/
|
|
37453
|
+
function toPortablePath$1(path) {
|
|
37454
|
+
return path.replace(/\\/gu, '/');
|
|
37455
|
+
}
|
|
37456
|
+
/**
|
|
37457
|
+
* Returns true when one filesystem error indicates a missing path.
|
|
37458
|
+
*/
|
|
37459
|
+
function isFileNotFoundError$2(error) {
|
|
37460
|
+
return Boolean(error &&
|
|
37461
|
+
typeof error === 'object' &&
|
|
37462
|
+
'code' in error &&
|
|
37463
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR'));
|
|
37464
|
+
}
|
|
37465
|
+
|
|
37466
|
+
/**
|
|
37467
|
+
* Loads the optional TEAM roster snapshot for one queued message.
|
|
37468
|
+
*/
|
|
37469
|
+
async function loadAgentTeamConversationWorkspace(projectPath, queuedMessage) {
|
|
37470
|
+
const relativeWorkspacePath = createAgentTeamConversationWorkspacePath(queuedMessage.fileName);
|
|
37471
|
+
const manifestPath = join(projectPath, relativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
|
|
37472
|
+
const manifestContent = await readOptionalTextFile(manifestPath);
|
|
37473
|
+
if (manifestContent === null) {
|
|
37474
|
+
return null;
|
|
37475
|
+
}
|
|
37476
|
+
const manifest = parseAgentTeamConversationWorkspaceManifest(manifestContent);
|
|
37477
|
+
if (!manifest || manifest.teammates.length === 0) {
|
|
37478
|
+
return null;
|
|
37479
|
+
}
|
|
37480
|
+
return {
|
|
37481
|
+
relativeWorkspacePath: toPortablePath(relativeWorkspacePath),
|
|
37482
|
+
manifest,
|
|
37483
|
+
};
|
|
37484
|
+
}
|
|
37485
|
+
/**
|
|
37486
|
+
* Parses one untrusted TEAM workspace manifest without making a queued message fail for stale sidecar data.
|
|
37487
|
+
*/
|
|
37488
|
+
function parseAgentTeamConversationWorkspaceManifest(manifestContent) {
|
|
37489
|
+
try {
|
|
37490
|
+
const parsedManifest = JSON.parse(manifestContent);
|
|
37491
|
+
return isAgentTeamConversationWorkspaceManifest(parsedManifest) ? parsedManifest : null;
|
|
37492
|
+
}
|
|
37493
|
+
catch (_a) {
|
|
37494
|
+
return null;
|
|
37495
|
+
}
|
|
37496
|
+
}
|
|
37497
|
+
/**
|
|
37498
|
+
* Reads one text file and treats a missing team workspace as absent.
|
|
37499
|
+
*/
|
|
37500
|
+
async function readOptionalTextFile(path) {
|
|
37501
|
+
try {
|
|
37502
|
+
return await readFile(path, 'utf-8');
|
|
37503
|
+
}
|
|
37504
|
+
catch (error) {
|
|
37505
|
+
if (isFileNotFoundError$1(error)) {
|
|
37506
|
+
return null;
|
|
37507
|
+
}
|
|
37508
|
+
throw error;
|
|
37509
|
+
}
|
|
37510
|
+
}
|
|
37511
|
+
/**
|
|
37512
|
+
* Converts a filesystem-relative path to the portable form used by prompts and Git.
|
|
37513
|
+
*/
|
|
37514
|
+
function toPortablePath(path) {
|
|
37515
|
+
return path.replace(/\\/gu, '/');
|
|
37516
|
+
}
|
|
37517
|
+
/**
|
|
37518
|
+
* Returns true when one filesystem error indicates a missing path.
|
|
37519
|
+
*/
|
|
37520
|
+
function isFileNotFoundError$1(error) {
|
|
37521
|
+
return Boolean(error &&
|
|
37522
|
+
typeof error === 'object' &&
|
|
37523
|
+
'code' in error &&
|
|
37524
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR'));
|
|
37525
|
+
}
|
|
37526
|
+
|
|
36823
37527
|
/**
|
|
36824
37528
|
* Resolves project-runtime prompt API details for one local agent runner folder.
|
|
36825
37529
|
*
|
|
@@ -37136,11 +37840,13 @@ function removeQueuedMessageFromSnapshot(queueSnapshot, processedQueuedMessage)
|
|
|
37136
37840
|
* Runs the selected coding runner for one message and finalizes the answered file.
|
|
37137
37841
|
*/
|
|
37138
37842
|
async function runQueuedAgentMessage(options) {
|
|
37139
|
-
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard } = options;
|
|
37843
|
+
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard, } = options;
|
|
37140
37844
|
const agentSystemMessage = await loadLocalAgentSystemMessage(projectPath);
|
|
37845
|
+
const teamWorkspace = await loadAgentTeamConversationWorkspace(projectPath, queuedMessage);
|
|
37141
37846
|
const prompt = buildAgentMessagePrompt(queuedMessage.relativePath, agentSystemMessage, {
|
|
37142
37847
|
projectRuntimeApi: resolveAgentProjectRuntimePromptApi(projectPath),
|
|
37143
37848
|
projectsUrlPath: resolveAgentProjectsUrlPath(projectPath),
|
|
37849
|
+
teamWorkspace: teamWorkspace || undefined,
|
|
37144
37850
|
});
|
|
37145
37851
|
const scriptPath = buildAgentMessageScriptPath(projectPath, queuedMessage);
|
|
37146
37852
|
const runtimeLogPath = buildScriptLogPath(scriptPath);
|
|
@@ -37188,6 +37894,13 @@ async function runQueuedAgentMessage(options) {
|
|
|
37188
37894
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
37189
37895
|
}
|
|
37190
37896
|
await normalizeLineEndingsForAgentRound(projectPath, runOptions, roundChangedFilesSnapshot);
|
|
37897
|
+
const finishedTeamWorkspace = await finalizeAgentTeamConversationWorkspace({
|
|
37898
|
+
projectPath,
|
|
37899
|
+
queuedMessage,
|
|
37900
|
+
workspace: teamWorkspace,
|
|
37901
|
+
});
|
|
37902
|
+
// Note: The Agents Server observes the primary finished `.book` as its completion signal.
|
|
37903
|
+
// Retain TEAM transcripts first so that observer can parse every consultation atomically.
|
|
37191
37904
|
const finishedMessage = await moveAgentMessageToFinished(projectPath, queuedMessage);
|
|
37192
37905
|
const writtenRunReport = await writeAgentMessageRunReport({
|
|
37193
37906
|
finishedMessageAbsolutePath: finishedMessage.absolutePath,
|
|
@@ -37204,6 +37917,7 @@ async function runQueuedAgentMessage(options) {
|
|
|
37204
37917
|
options: runOptions,
|
|
37205
37918
|
queuedMessage,
|
|
37206
37919
|
finishedMessage,
|
|
37920
|
+
finishedTeamWorkspace,
|
|
37207
37921
|
writtenRunReport,
|
|
37208
37922
|
isQueuedMessageTracked,
|
|
37209
37923
|
uiHandle,
|
|
@@ -37275,7 +37989,7 @@ function seedAgentRunUiHandle(uiHandle, options, runner, actualRunnerModel, queu
|
|
|
37275
37989
|
* Commits the answered message move unless `--no-commit` was requested.
|
|
37276
37990
|
*/
|
|
37277
37991
|
async function commitAnsweredMessageIfEnabled(options) {
|
|
37278
|
-
const { options: runOptions, queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
|
|
37992
|
+
const { options: runOptions, queuedMessage, finishedMessage, finishedTeamWorkspace, writtenRunReport, isQueuedMessageTracked, uiHandle, isSharedDashboard, projectPath, } = options;
|
|
37279
37993
|
if (runOptions.noCommit) {
|
|
37280
37994
|
if (!isSharedDashboard) {
|
|
37281
37995
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Leaving changes uncommitted');
|
|
@@ -37287,21 +38001,24 @@ async function commitAnsweredMessageIfEnabled(options) {
|
|
|
37287
38001
|
}
|
|
37288
38002
|
await commitChanges(buildAgentMessageCommitMessage(queuedMessage), {
|
|
37289
38003
|
autoPush: runOptions.autoPush,
|
|
37290
|
-
|
|
38004
|
+
relevantPaths: buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace),
|
|
37291
38005
|
projectPath,
|
|
37292
38006
|
});
|
|
37293
38007
|
}
|
|
37294
38008
|
/**
|
|
37295
38009
|
* Builds the focused git path list for the answered-message commit.
|
|
37296
38010
|
*/
|
|
37297
|
-
function
|
|
37298
|
-
const
|
|
38011
|
+
function buildCommitRelevantPaths(queuedMessage, finishedMessage, writtenRunReport, isQueuedMessageTracked, finishedTeamWorkspace) {
|
|
38012
|
+
const relevantPaths = isQueuedMessageTracked
|
|
37299
38013
|
? [queuedMessage.relativePath, finishedMessage.relativePath]
|
|
37300
38014
|
: [finishedMessage.relativePath];
|
|
37301
38015
|
if (writtenRunReport) {
|
|
37302
|
-
|
|
38016
|
+
relevantPaths.push(writtenRunReport.relativePath);
|
|
37303
38017
|
}
|
|
37304
|
-
|
|
38018
|
+
if (finishedTeamWorkspace) {
|
|
38019
|
+
relevantPaths.push(...finishedTeamWorkspace.relativePaths);
|
|
38020
|
+
}
|
|
38021
|
+
return relevantPaths;
|
|
37305
38022
|
}
|
|
37306
38023
|
/**
|
|
37307
38024
|
* Normalizes line endings in files changed during the current agent round.
|
|
@@ -38813,7 +39530,7 @@ const loadPromptsModule = createLazyModuleLoader(() => import('prompts'));
|
|
|
38813
39530
|
const CODER_GIT_SYNC_DESCRIPTION = spaceTrim$1(`
|
|
38814
39531
|
Git synchronization:
|
|
38815
39532
|
- --auto-pull pulls the latest changes before this command changes anything
|
|
38816
|
-
- --commit commits the changes
|
|
39533
|
+
- --commit commits only the files this command has changed, unrelated changes stay in the working tree
|
|
38817
39534
|
- --auto-push pushes the created commit to the remote repository
|
|
38818
39535
|
`);
|
|
38819
39536
|
/**
|
|
@@ -38825,7 +39542,7 @@ const CODER_GIT_SYNC_DESCRIPTION = spaceTrim$1(`
|
|
|
38825
39542
|
* @private internal utility of `promptbookCli`
|
|
38826
39543
|
*/
|
|
38827
39544
|
function addCoderGitSyncOptions(command) {
|
|
38828
|
-
command.option('--commit', 'Commit the
|
|
39545
|
+
command.option('--commit', 'Commit the files changed by this command with the coding-agent git identity, leaving unrelated changes uncommitted', false);
|
|
38829
39546
|
command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
|
|
38830
39547
|
command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
|
|
38831
39548
|
}
|
|
@@ -39134,8 +39851,8 @@ function $initializeCoderAddCommand(program) {
|
|
|
39134
39851
|
const projectPath = process.cwd();
|
|
39135
39852
|
const description = await resolveCoderPromptDescription(descriptionArgument);
|
|
39136
39853
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
39137
|
-
const { $commitCoderChanges, $
|
|
39138
|
-
await $
|
|
39854
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
39855
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
39139
39856
|
const { /* filePath,*/ emojiTag } = await addCoderPrompt({
|
|
39140
39857
|
projectPath,
|
|
39141
39858
|
description,
|
|
@@ -39144,7 +39861,7 @@ function $initializeCoderAddCommand(program) {
|
|
|
39144
39861
|
});
|
|
39145
39862
|
await $commitCoderChanges({
|
|
39146
39863
|
gitSync,
|
|
39147
|
-
|
|
39864
|
+
commitScope,
|
|
39148
39865
|
commitMessage: `${emojiTag} Add prompt`,
|
|
39149
39866
|
});
|
|
39150
39867
|
}));
|
|
@@ -39738,7 +40455,7 @@ function assertsPositiveBoilerplateCountPart(countPart, countOption, countPartDe
|
|
|
39738
40455
|
function $initializeCoderGenerateBoilerplatesCommand(program) {
|
|
39739
40456
|
const command = program.command('generate-boilerplates');
|
|
39740
40457
|
command.description(spaceTrim$1((block) => `
|
|
39741
|
-
Generate prompt boilerplate files with unique emoji
|
|
40458
|
+
Generate prompt boilerplate files with one unique emoji tag per file
|
|
39742
40459
|
|
|
39743
40460
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
39744
40461
|
`));
|
|
@@ -39757,8 +40474,8 @@ function $initializeCoderGenerateBoilerplatesCommand(program) {
|
|
|
39757
40474
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
39758
40475
|
const projectPath = process.cwd();
|
|
39759
40476
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
39760
|
-
const { $commitCoderChanges, $
|
|
39761
|
-
await $
|
|
40477
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
40478
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
39762
40479
|
await generatePromptBoilerplate({
|
|
39763
40480
|
projectPath,
|
|
39764
40481
|
boilerplateCount,
|
|
@@ -39766,14 +40483,14 @@ function $initializeCoderGenerateBoilerplatesCommand(program) {
|
|
|
39766
40483
|
});
|
|
39767
40484
|
await $commitCoderChanges({
|
|
39768
40485
|
gitSync,
|
|
39769
|
-
|
|
40486
|
+
commitScope,
|
|
39770
40487
|
commitMessage: `Prompts ${formatBoilerplateCount(boilerplateCount)}`,
|
|
39771
40488
|
});
|
|
39772
40489
|
return process.exit(0);
|
|
39773
40490
|
}));
|
|
39774
40491
|
}
|
|
39775
40492
|
/**
|
|
39776
|
-
* Generates boilerplate prompt files with unique emoji
|
|
40493
|
+
* Generates boilerplate prompt files with one unique emoji tag per file.
|
|
39777
40494
|
*
|
|
39778
40495
|
* @private internal function of `generatePromptBoilerplate` command
|
|
39779
40496
|
*/
|
|
@@ -39793,10 +40510,10 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
39793
40510
|
const highestNumber = promptNumbering.startNumber === 0 ? 0 : promptNumbering.startNumber - promptNumbering.step;
|
|
39794
40511
|
const highestNumberFormatted = Math.max(0, highestNumber).toString().padStart(4, '0');
|
|
39795
40512
|
console.info(colors.blue(`Highest existing number for ${promptNumbering.datePrefix} found: ${highestNumberFormatted}`));
|
|
39796
|
-
// Note: Every single generated prompt is one separate coding task, so each of them reserves its own fresh emoji tag
|
|
39797
40513
|
const promptsCount = filesCount * promptsPerFileCount;
|
|
39798
40514
|
const { availableCount, selectedEmojis } = await getFreshPromptEmojiTags({
|
|
39799
|
-
|
|
40515
|
+
// Note: Each generated file reserves one fresh emoji tag shared by all of its prompt sections
|
|
40516
|
+
count: filesCount,
|
|
39800
40517
|
rootDir: projectPath,
|
|
39801
40518
|
});
|
|
39802
40519
|
console.info(colors.green(`Found ${availableCount} available fresh emojis`));
|
|
@@ -39808,14 +40525,13 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
39808
40525
|
for (let i = 0; i < filesCount; i++) {
|
|
39809
40526
|
const number = promptNumbering.startNumber + i * promptNumbering.step;
|
|
39810
40527
|
const title = titles[i % titles.length];
|
|
39811
|
-
const
|
|
39812
|
-
.slice(i * promptsPerFileCount, (i + 1) * promptsPerFileCount)
|
|
39813
|
-
.map((emoji) => formatPromptEmojiTag(emoji));
|
|
40528
|
+
const emojiTag = formatPromptEmojiTag(selectedEmojis[i]);
|
|
39814
40529
|
const filename = buildPromptFilename(promptNumbering.datePrefix, number, buildPromptSlug$1(promptTemplate.slugPrefix, title));
|
|
39815
40530
|
const filepath = join(PROMPTS_DIRECTORY_PATH, filename);
|
|
39816
40531
|
const absoluteFilepath = join(projectPath, filepath);
|
|
39817
40532
|
const content = buildBoilerplatePromptFileContent({
|
|
39818
|
-
|
|
40533
|
+
emojiTag,
|
|
40534
|
+
promptsPerFileCount,
|
|
39819
40535
|
title,
|
|
39820
40536
|
body: promptTemplate.content,
|
|
39821
40537
|
});
|
|
@@ -39824,7 +40540,7 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
39824
40540
|
absoluteFilepath,
|
|
39825
40541
|
filename,
|
|
39826
40542
|
content,
|
|
39827
|
-
|
|
40543
|
+
emojiTag,
|
|
39828
40544
|
number,
|
|
39829
40545
|
});
|
|
39830
40546
|
}
|
|
@@ -39832,26 +40548,25 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
39832
40548
|
console.info(colors.yellow(`Creating ${filesToCreate.length} files:`));
|
|
39833
40549
|
for (const file of filesToCreate) {
|
|
39834
40550
|
writeFileSync(file.absoluteFilepath, file.content, 'utf-8');
|
|
39835
|
-
console.info(colors.green(`✓ Created: ${file.filename} with ${file.
|
|
40551
|
+
console.info(colors.green(`✓ Created: ${file.filename} with ${file.emojiTag}`));
|
|
39836
40552
|
}
|
|
39837
40553
|
console.info(colors.bgGreen(` Successfully created ${promptsCount} prompts in ${filesToCreate.length} prompt boilerplate files! `));
|
|
39838
40554
|
}
|
|
39839
40555
|
/**
|
|
39840
|
-
* Builds the markdown content of one generated prompt file with one prompt section per
|
|
40556
|
+
* Builds the markdown content of one generated prompt file with one prompt section per prompt.
|
|
39841
40557
|
*
|
|
39842
|
-
* Multiple prompts in one file are separated by the `---` separator,
|
|
40558
|
+
* Multiple prompts in one file share the file emoji tag and are separated by the `---` separator,
|
|
40559
|
+
* exactly like the prompt runner expects them.
|
|
39843
40560
|
*
|
|
39844
40561
|
* @private internal utility of `generatePromptBoilerplate` command
|
|
39845
40562
|
*/
|
|
39846
|
-
function buildBoilerplatePromptFileContent({
|
|
39847
|
-
return
|
|
39848
|
-
.map((emojiTag) => buildCoderPromptSection({
|
|
40563
|
+
function buildBoilerplatePromptFileContent({ emojiTag, promptsPerFileCount, title, body, }) {
|
|
40564
|
+
return Array.from({ length: promptsPerFileCount }, () => buildCoderPromptSection({
|
|
39849
40565
|
statusLine: '[-]',
|
|
39850
40566
|
emojiTag,
|
|
39851
40567
|
title,
|
|
39852
40568
|
body,
|
|
39853
|
-
}))
|
|
39854
|
-
.join('\n\n---\n\n');
|
|
40569
|
+
})).join('\n\n---\n\n');
|
|
39855
40570
|
}
|
|
39856
40571
|
/**
|
|
39857
40572
|
* Builds filename slug from template and placeholder title.
|
|
@@ -39868,14 +40583,14 @@ function buildPromptSlug$1(templateSlugPrefix, title) {
|
|
|
39868
40583
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
39869
40584
|
|
|
39870
40585
|
/**
|
|
39871
|
-
* Asks the user in the terminal whether
|
|
40586
|
+
* Asks the user in the terminal whether an npm package should be installed or updated now.
|
|
39872
40587
|
*
|
|
39873
40588
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
|
|
39874
40589
|
*
|
|
39875
40590
|
* @returns `true` when the user confirms, `false` when the user declines or the terminal is not interactive
|
|
39876
40591
|
* @private internal utility of `promptbookCli`
|
|
39877
40592
|
*/
|
|
39878
|
-
async function $
|
|
40593
|
+
async function $askForNpmPackageInstallationApproval(question) {
|
|
39879
40594
|
if (!process.stdin.isTTY) {
|
|
39880
40595
|
// Note: In non-interactive environments like CI there is nobody who could confirm the installation
|
|
39881
40596
|
return false;
|
|
@@ -39891,7 +40606,7 @@ async function $askForHarnessInstallationApproval(question) {
|
|
|
39891
40606
|
readlineInterface.close();
|
|
39892
40607
|
}
|
|
39893
40608
|
}
|
|
39894
|
-
// Note: [🟡] Code for CLI
|
|
40609
|
+
// 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`
|
|
39895
40610
|
|
|
39896
40611
|
/**
|
|
39897
40612
|
* Builds the shell command which installs or updates one CLI coding harness globally.
|
|
@@ -39986,7 +40701,7 @@ async function $applyHarnessInstallationStatus(status) {
|
|
|
39986
40701
|
return;
|
|
39987
40702
|
}
|
|
39988
40703
|
console.warn(colors.yellow(formatHarnessInstallationWarning(status)));
|
|
39989
|
-
const isInstallationApproved = await $
|
|
40704
|
+
const isInstallationApproved = await $askForNpmPackageInstallationApproval(installationState === 'not-installed'
|
|
39990
40705
|
? `Install ${definition.label} globally now?`
|
|
39991
40706
|
: `Update ${definition.label} to ${latestVersion} now?`);
|
|
39992
40707
|
if (!isInstallationApproved) {
|
|
@@ -39998,30 +40713,25 @@ async function $applyHarnessInstallationStatus(status) {
|
|
|
39998
40713
|
// Note: [🟡] Code for CLI harness installation handling [$applyHarnessInstallationStatus](src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts) should never be published outside of `@promptbook/cli`
|
|
39999
40714
|
|
|
40000
40715
|
/**
|
|
40001
|
-
* Pattern matching one `major.minor.patch` version anywhere in
|
|
40716
|
+
* Pattern matching one `major.minor.patch` version anywhere in npm or CLI command output.
|
|
40002
40717
|
*/
|
|
40003
|
-
const
|
|
40718
|
+
const NPM_PACKAGE_VERSION_PATTERN = /\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/gu;
|
|
40004
40719
|
/**
|
|
40005
|
-
* Extracts
|
|
40720
|
+
* Extracts an npm package version from raw command output.
|
|
40006
40721
|
*
|
|
40007
|
-
*
|
|
40008
|
-
*
|
|
40009
|
-
* so the first version-looking token of the first matching line wins.
|
|
40722
|
+
* Both npm and CLI commands may emit unrelated warnings before the version. CLI commands use the first matching token;
|
|
40723
|
+
* npm registry commands can opt into the last matching token because npm warnings commonly precede their JSON output.
|
|
40010
40724
|
*
|
|
40011
40725
|
* @returns The parsed version or `null` when the output contains no version
|
|
40012
40726
|
* @private internal utility of `promptbookCli`
|
|
40013
40727
|
*/
|
|
40014
|
-
function
|
|
40728
|
+
function extractNpmPackageVersionFromOutput(output, { isLastMatchPreferred = false } = {}) {
|
|
40015
40729
|
var _a;
|
|
40016
|
-
|
|
40017
|
-
|
|
40018
|
-
|
|
40019
|
-
return (_a = versionMatch[1]) !== null && _a !== void 0 ? _a : null;
|
|
40020
|
-
}
|
|
40021
|
-
}
|
|
40022
|
-
return null;
|
|
40730
|
+
const versionMatches = Array.from(output.matchAll(NPM_PACKAGE_VERSION_PATTERN));
|
|
40731
|
+
const versionMatch = isLastMatchPreferred ? versionMatches[versionMatches.length - 1] : versionMatches[0];
|
|
40732
|
+
return (_a = versionMatch === null || versionMatch === void 0 ? void 0 : versionMatch[1]) !== null && _a !== void 0 ? _a : null;
|
|
40023
40733
|
}
|
|
40024
|
-
// Note: [🟡] Code for CLI
|
|
40734
|
+
// 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`
|
|
40025
40735
|
|
|
40026
40736
|
/**
|
|
40027
40737
|
* Time limit for asking the globally installed harness command for its version.
|
|
@@ -40042,46 +40752,60 @@ async function $resolveInstalledHarnessVersion(definition) {
|
|
|
40042
40752
|
timeout: HARNESS_VERSION_COMMAND_TIMEOUT_MS,
|
|
40043
40753
|
isVerbose: false,
|
|
40044
40754
|
}).catch(() => '');
|
|
40045
|
-
return
|
|
40755
|
+
return extractNpmPackageVersionFromOutput(output);
|
|
40046
40756
|
}
|
|
40047
40757
|
// Note: [🟡] Code for CLI harness version detection [$resolveInstalledHarnessVersion](src/cli/cli-commands/common/harness/$resolveInstalledHarnessVersion.ts) should never be published outside of `@promptbook/cli`
|
|
40048
40758
|
|
|
40049
40759
|
/**
|
|
40050
|
-
* Time limit for the whole `npm view` lookup of the newest published
|
|
40760
|
+
* Time limit for the whole `npm view` lookup of the newest published package version.
|
|
40051
40761
|
*/
|
|
40052
|
-
const
|
|
40762
|
+
const NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS = 60 * 1000;
|
|
40053
40763
|
/**
|
|
40054
40764
|
* Time limit for one npm registry request, so an unreachable registry never blocks the command.
|
|
40055
40765
|
*/
|
|
40056
40766
|
const NPM_REGISTRY_FETCH_TIMEOUT_MS = 20 * 1000;
|
|
40057
40767
|
/**
|
|
40058
|
-
*
|
|
40768
|
+
* Pattern matching npm's JSON-encoded package version result.
|
|
40769
|
+
*/
|
|
40770
|
+
const NPM_VIEW_JSON_VERSION_PATTERN = /"(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/u;
|
|
40771
|
+
/**
|
|
40772
|
+
* Reads the newest version of one npm package.
|
|
40059
40773
|
*
|
|
40060
40774
|
* Note: `$` is used to indicate that this function is not a pure function - it queries the npm registry
|
|
40061
40775
|
*
|
|
40062
40776
|
* @returns The newest published version or `null` when the registry could not be reached
|
|
40063
40777
|
* @private internal utility of `promptbookCli`
|
|
40064
40778
|
*/
|
|
40065
|
-
async function $
|
|
40779
|
+
async function $resolveLatestNpmPackageVersion(npmPackageName) {
|
|
40066
40780
|
const output = await $execCommand({
|
|
40067
|
-
command: `npm view ${
|
|
40781
|
+
command: `npm view ${npmPackageName} version --json --loglevel=error --fetch-timeout=${NPM_REGISTRY_FETCH_TIMEOUT_MS} --fetch-retries=1`,
|
|
40068
40782
|
crashOnError: true,
|
|
40069
|
-
timeout:
|
|
40783
|
+
timeout: NPM_PACKAGE_LATEST_VERSION_COMMAND_TIMEOUT_MS,
|
|
40070
40784
|
isVerbose: false,
|
|
40071
40785
|
}).catch(() => '');
|
|
40072
|
-
return
|
|
40786
|
+
return extractLatestNpmPackageVersionFromNpmViewOutput(output);
|
|
40787
|
+
}
|
|
40788
|
+
/**
|
|
40789
|
+
* Extracts the version from npm's JSON result while tolerating warnings emitted before or after that result.
|
|
40790
|
+
*
|
|
40791
|
+
* @private internal utility of `$resolveLatestNpmPackageVersion`
|
|
40792
|
+
*/
|
|
40793
|
+
function extractLatestNpmPackageVersionFromNpmViewOutput(output) {
|
|
40794
|
+
var _a;
|
|
40795
|
+
const jsonVersionMatch = NPM_VIEW_JSON_VERSION_PATTERN.exec(output);
|
|
40796
|
+
return (_a = jsonVersionMatch === null || jsonVersionMatch === void 0 ? void 0 : jsonVersionMatch[1]) !== null && _a !== void 0 ? _a : extractNpmPackageVersionFromOutput(output, { isLastMatchPreferred: true });
|
|
40073
40797
|
}
|
|
40074
|
-
// Note: [🟡] Code for CLI
|
|
40798
|
+
// 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`
|
|
40075
40799
|
|
|
40076
40800
|
/**
|
|
40077
|
-
* Compares
|
|
40801
|
+
* Compares an installed npm package version with the newest published version.
|
|
40078
40802
|
*
|
|
40079
40803
|
* Only the numeric `major.minor.patch` segments are compared and pre-release suffixes are ignored,
|
|
40080
|
-
* so a
|
|
40804
|
+
* so a package installed from a pre-release build of the newest version is not reported as outdated.
|
|
40081
40805
|
*
|
|
40082
40806
|
* @private internal utility of `promptbookCli`
|
|
40083
40807
|
*/
|
|
40084
|
-
function
|
|
40808
|
+
function isNpmPackageVersionOutdated(installedVersion, latestVersion) {
|
|
40085
40809
|
var _a, _b;
|
|
40086
40810
|
const installedSegments = parseVersionSegments(installedVersion);
|
|
40087
40811
|
const latestSegments = parseVersionSegments(latestVersion);
|
|
@@ -40097,6 +40821,8 @@ function isHarnessVersionOutdated(installedVersion, latestVersion) {
|
|
|
40097
40821
|
}
|
|
40098
40822
|
/**
|
|
40099
40823
|
* Splits one version into its numeric segments, dropping the pre-release and build suffix.
|
|
40824
|
+
*
|
|
40825
|
+
* @private internal utility of `isNpmPackageVersionOutdated`
|
|
40100
40826
|
*/
|
|
40101
40827
|
function parseVersionSegments(version) {
|
|
40102
40828
|
const [numericVersion = ''] = version.trim().split(/[-+]/u);
|
|
@@ -40105,7 +40831,7 @@ function parseVersionSegments(version) {
|
|
|
40105
40831
|
return Number.isNaN(segmentNumber) ? 0 : segmentNumber;
|
|
40106
40832
|
});
|
|
40107
40833
|
}
|
|
40108
|
-
// Note: [🟡] Code for CLI
|
|
40834
|
+
// 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`
|
|
40109
40835
|
|
|
40110
40836
|
/**
|
|
40111
40837
|
* Detects whether one CLI coding harness is installed globally and whether it is up to date.
|
|
@@ -40117,7 +40843,7 @@ function parseVersionSegments(version) {
|
|
|
40117
40843
|
async function $checkHarnessInstallation(definition) {
|
|
40118
40844
|
const [installedVersion, latestVersion] = await Promise.all([
|
|
40119
40845
|
$resolveInstalledHarnessVersion(definition),
|
|
40120
|
-
$
|
|
40846
|
+
$resolveLatestNpmPackageVersion(definition.npmPackageName),
|
|
40121
40847
|
]);
|
|
40122
40848
|
return {
|
|
40123
40849
|
definition,
|
|
@@ -40136,7 +40862,7 @@ function resolveHarnessInstallationState(installedVersion, latestVersion) {
|
|
|
40136
40862
|
if (latestVersion === null) {
|
|
40137
40863
|
return 'unknown';
|
|
40138
40864
|
}
|
|
40139
|
-
return
|
|
40865
|
+
return isNpmPackageVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
|
|
40140
40866
|
}
|
|
40141
40867
|
// Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
|
|
40142
40868
|
|
|
@@ -40368,11 +41094,12 @@ async function ensureCoderGitignoreFile(projectPath) {
|
|
|
40368
41094
|
* Default npm scripts initialized by `ptbk coder init`.
|
|
40369
41095
|
*/
|
|
40370
41096
|
const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
|
|
40371
|
-
|
|
40372
|
-
'coder:
|
|
40373
|
-
'coder:
|
|
41097
|
+
// Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
|
|
41098
|
+
'coder:generate-boilerplates': `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ./prompts/templates/common.md`,
|
|
41099
|
+
'coder:add': 'npx ptbk coder add --template ./prompts/templates/common.md',
|
|
41100
|
+
'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',
|
|
40374
41101
|
// 'coder:find-refactor-candidates': 'npx ptbk coder find-refactor-candidates',
|
|
40375
|
-
'coder:verify': 'ptbk coder verify',
|
|
41102
|
+
'coder:verify': 'npx ptbk coder verify',
|
|
40376
41103
|
};
|
|
40377
41104
|
/**
|
|
40378
41105
|
* Lists the default npm scripts initialized by `ptbk coder init`.
|
|
@@ -40742,14 +41469,14 @@ function $initializeCoderInitCommand(program) {
|
|
|
40742
41469
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
40743
41470
|
const projectPath = process.cwd();
|
|
40744
41471
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
40745
|
-
const { $commitCoderChanges, $
|
|
40746
|
-
await $
|
|
41472
|
+
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
41473
|
+
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
40747
41474
|
const summary = await initializeCoderProjectConfiguration(projectPath);
|
|
40748
41475
|
printInitializationSummary(summary);
|
|
40749
41476
|
await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
|
|
40750
41477
|
await $commitCoderChanges({
|
|
40751
41478
|
gitSync,
|
|
40752
|
-
|
|
41479
|
+
commitScope,
|
|
40753
41480
|
commitMessage: 'Initialize Promptbook Coder',
|
|
40754
41481
|
});
|
|
40755
41482
|
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
|
|
@@ -40838,6 +41565,370 @@ function createNonNegativeIntegerOptionParser(optionName) {
|
|
|
40838
41565
|
}
|
|
40839
41566
|
// Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
|
|
40840
41567
|
|
|
41568
|
+
/**
|
|
41569
|
+
* Npm packages which provide an executable Promptbook CLI.
|
|
41570
|
+
*
|
|
41571
|
+
* `ptbk` is the preferred package and forwards to `@promptbook/cli`; the latter can also be installed directly.
|
|
41572
|
+
*
|
|
41573
|
+
* @private internal utility of `promptbookCli`
|
|
41574
|
+
*/
|
|
41575
|
+
const PROMPTBOOK_CLI_NPM_PACKAGE_NAMES = ['ptbk', '@promptbook/cli'];
|
|
41576
|
+
// 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`
|
|
41577
|
+
|
|
41578
|
+
/**
|
|
41579
|
+
* Time limit for resolving npm's global `node_modules` directory.
|
|
41580
|
+
*/
|
|
41581
|
+
const NPM_GLOBAL_NODE_MODULES_PATH_COMMAND_TIMEOUT_MS = 30 * 1000;
|
|
41582
|
+
/**
|
|
41583
|
+
* Finds every locally or globally installed Promptbook CLI package which can be updated.
|
|
41584
|
+
*
|
|
41585
|
+
* A local package must be declared directly in the current project's `package.json`; transitive packages are not
|
|
41586
|
+
* updated because this command must not change dependencies chosen by another package.
|
|
41587
|
+
*
|
|
41588
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads package manifests and runs npm
|
|
41589
|
+
*
|
|
41590
|
+
* @private internal utility of `promptbookCli`
|
|
41591
|
+
*/
|
|
41592
|
+
async function $resolvePromptbookCliInstallations() {
|
|
41593
|
+
const [localInstallations, globalInstallations] = await Promise.all([
|
|
41594
|
+
$resolveLocalPromptbookCliInstallations(),
|
|
41595
|
+
$resolveGlobalPromptbookCliInstallations(),
|
|
41596
|
+
]);
|
|
41597
|
+
return [...localInstallations, ...globalInstallations];
|
|
41598
|
+
}
|
|
41599
|
+
/**
|
|
41600
|
+
* Finds Promptbook CLI packages installed directly in the current project's dependencies.
|
|
41601
|
+
*
|
|
41602
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41603
|
+
*/
|
|
41604
|
+
async function $resolveLocalPromptbookCliInstallations() {
|
|
41605
|
+
const projectPath = process.cwd();
|
|
41606
|
+
const projectPackageJson = await $readJsonObject(join(projectPath, 'package.json'));
|
|
41607
|
+
if (projectPackageJson === null) {
|
|
41608
|
+
return [];
|
|
41609
|
+
}
|
|
41610
|
+
const installations = await Promise.all(PROMPTBOOK_CLI_NPM_PACKAGE_NAMES.map(async (npmPackageName) => {
|
|
41611
|
+
const installationLocation = resolveLocalPromptbookCliInstallationLocation(projectPackageJson, npmPackageName);
|
|
41612
|
+
if (installationLocation === undefined) {
|
|
41613
|
+
return undefined;
|
|
41614
|
+
}
|
|
41615
|
+
const installedVersion = await $resolveInstalledNpmPackageVersion(join(projectPath, 'node_modules'), npmPackageName);
|
|
41616
|
+
if (installedVersion === undefined) {
|
|
41617
|
+
return undefined;
|
|
41618
|
+
}
|
|
41619
|
+
return { npmPackageName, installedVersion, installationLocation };
|
|
41620
|
+
}));
|
|
41621
|
+
return installations.filter(isDefined);
|
|
41622
|
+
}
|
|
41623
|
+
/**
|
|
41624
|
+
* Finds Promptbook CLI packages installed in npm's global `node_modules` directory.
|
|
41625
|
+
*
|
|
41626
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41627
|
+
*/
|
|
41628
|
+
async function $resolveGlobalPromptbookCliInstallations() {
|
|
41629
|
+
const globalNodeModulesPath = await $resolveNpmGlobalNodeModulesPath();
|
|
41630
|
+
if (globalNodeModulesPath === null) {
|
|
41631
|
+
return [];
|
|
41632
|
+
}
|
|
41633
|
+
const installations = await Promise.all(PROMPTBOOK_CLI_NPM_PACKAGE_NAMES.map(async (npmPackageName) => {
|
|
41634
|
+
const installedVersion = await $resolveInstalledNpmPackageVersion(globalNodeModulesPath, npmPackageName);
|
|
41635
|
+
if (installedVersion === undefined) {
|
|
41636
|
+
return undefined;
|
|
41637
|
+
}
|
|
41638
|
+
return {
|
|
41639
|
+
npmPackageName,
|
|
41640
|
+
installedVersion,
|
|
41641
|
+
installationLocation: 'global',
|
|
41642
|
+
};
|
|
41643
|
+
}));
|
|
41644
|
+
return installations.filter(isDefined);
|
|
41645
|
+
}
|
|
41646
|
+
/**
|
|
41647
|
+
* Resolves the current project's manifest section that declares a Promptbook CLI package.
|
|
41648
|
+
*
|
|
41649
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41650
|
+
*/
|
|
41651
|
+
function resolveLocalPromptbookCliInstallationLocation(packageJson, npmPackageName) {
|
|
41652
|
+
if (readNestedStringProperty(packageJson, 'dependencies', npmPackageName) !== undefined) {
|
|
41653
|
+
return 'local-dependency';
|
|
41654
|
+
}
|
|
41655
|
+
if (readNestedStringProperty(packageJson, 'devDependencies', npmPackageName) !== undefined) {
|
|
41656
|
+
return 'local-development-dependency';
|
|
41657
|
+
}
|
|
41658
|
+
return undefined;
|
|
41659
|
+
}
|
|
41660
|
+
/**
|
|
41661
|
+
* Reads an installed npm package version from a `node_modules` directory.
|
|
41662
|
+
*
|
|
41663
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41664
|
+
*/
|
|
41665
|
+
async function $resolveInstalledNpmPackageVersion(nodeModulesPath, npmPackageName) {
|
|
41666
|
+
const installedPackageJson = await $readJsonObject(join(nodeModulesPath, npmPackageName, 'package.json'));
|
|
41667
|
+
return installedPackageJson === null ? undefined : readStringProperty(installedPackageJson, 'version');
|
|
41668
|
+
}
|
|
41669
|
+
/**
|
|
41670
|
+
* Resolves npm's global `node_modules` directory without querying the registry.
|
|
41671
|
+
*
|
|
41672
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41673
|
+
*/
|
|
41674
|
+
async function $resolveNpmGlobalNodeModulesPath() {
|
|
41675
|
+
const output = await $execCommand({
|
|
41676
|
+
command: 'npm root --global',
|
|
41677
|
+
crashOnError: true,
|
|
41678
|
+
timeout: NPM_GLOBAL_NODE_MODULES_PATH_COMMAND_TIMEOUT_MS,
|
|
41679
|
+
isVerbose: false,
|
|
41680
|
+
}).catch(() => '');
|
|
41681
|
+
const globalNodeModulesPath = output.trim();
|
|
41682
|
+
return globalNodeModulesPath === '' ? null : globalNodeModulesPath;
|
|
41683
|
+
}
|
|
41684
|
+
/**
|
|
41685
|
+
* Reads a JSON object, returning `null` for missing or malformed files because update checks must stay advisory.
|
|
41686
|
+
*
|
|
41687
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41688
|
+
*/
|
|
41689
|
+
async function $readJsonObject(filePath) {
|
|
41690
|
+
try {
|
|
41691
|
+
const parsedValue = JSON.parse(await readFile(filePath, 'utf8'));
|
|
41692
|
+
return isJsonObject(parsedValue) ? parsedValue : null;
|
|
41693
|
+
}
|
|
41694
|
+
catch (_a) {
|
|
41695
|
+
return null;
|
|
41696
|
+
}
|
|
41697
|
+
}
|
|
41698
|
+
/**
|
|
41699
|
+
* Checks whether an unknown value can be read as a JSON object.
|
|
41700
|
+
*
|
|
41701
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41702
|
+
*/
|
|
41703
|
+
function isJsonObject(value) {
|
|
41704
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
41705
|
+
}
|
|
41706
|
+
/**
|
|
41707
|
+
* Reads one string property from a JSON object.
|
|
41708
|
+
*
|
|
41709
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41710
|
+
*/
|
|
41711
|
+
function readStringProperty(jsonObject, propertyName) {
|
|
41712
|
+
const value = jsonObject[propertyName];
|
|
41713
|
+
return typeof value === 'string' ? value : undefined;
|
|
41714
|
+
}
|
|
41715
|
+
/**
|
|
41716
|
+
* Reads one string property nested under another JSON object property.
|
|
41717
|
+
*
|
|
41718
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41719
|
+
*/
|
|
41720
|
+
function readNestedStringProperty(jsonObject, parentPropertyName, propertyName) {
|
|
41721
|
+
const nestedValue = jsonObject[parentPropertyName];
|
|
41722
|
+
return isJsonObject(nestedValue) ? readStringProperty(nestedValue, propertyName) : undefined;
|
|
41723
|
+
}
|
|
41724
|
+
/**
|
|
41725
|
+
* Narrows an array after `undefined` values were removed.
|
|
41726
|
+
*
|
|
41727
|
+
* @private internal utility of `$resolvePromptbookCliInstallations`
|
|
41728
|
+
*/
|
|
41729
|
+
function isDefined(value) {
|
|
41730
|
+
return value !== undefined;
|
|
41731
|
+
}
|
|
41732
|
+
// 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`
|
|
41733
|
+
|
|
41734
|
+
/**
|
|
41735
|
+
* Checks every local and global Promptbook CLI installation against the newest npm version.
|
|
41736
|
+
*
|
|
41737
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads installed packages and queries npm
|
|
41738
|
+
*
|
|
41739
|
+
* @private internal utility of `promptbookCli`
|
|
41740
|
+
*/
|
|
41741
|
+
async function $checkPromptbookCliInstallations() {
|
|
41742
|
+
const installations = await $resolvePromptbookCliInstallations();
|
|
41743
|
+
if (installations.length === 0) {
|
|
41744
|
+
return [];
|
|
41745
|
+
}
|
|
41746
|
+
const latestVersionsByNpmPackageName = await $resolveLatestVersionsByNpmPackageName(installations);
|
|
41747
|
+
return installations.map((installation) => {
|
|
41748
|
+
var _a;
|
|
41749
|
+
const latestVersion = (_a = latestVersionsByNpmPackageName.get(installation.npmPackageName)) !== null && _a !== void 0 ? _a : null;
|
|
41750
|
+
return {
|
|
41751
|
+
installation,
|
|
41752
|
+
installationState: resolvePromptbookCliInstallationState(installation.installedVersion, latestVersion),
|
|
41753
|
+
latestVersion,
|
|
41754
|
+
};
|
|
41755
|
+
});
|
|
41756
|
+
}
|
|
41757
|
+
/**
|
|
41758
|
+
* Resolves each package's newest version once, even when it is installed locally and globally.
|
|
41759
|
+
*
|
|
41760
|
+
* @private internal utility of `$checkPromptbookCliInstallations`
|
|
41761
|
+
*/
|
|
41762
|
+
async function $resolveLatestVersionsByNpmPackageName(installations) {
|
|
41763
|
+
const npmPackageNames = Array.from(new Set(installations.map(({ npmPackageName }) => npmPackageName)));
|
|
41764
|
+
const latestVersionEntries = await Promise.all(npmPackageNames.map(async (npmPackageName) => {
|
|
41765
|
+
const latestVersion = await $resolveLatestNpmPackageVersion(npmPackageName);
|
|
41766
|
+
return [npmPackageName, latestVersion];
|
|
41767
|
+
}));
|
|
41768
|
+
return new Map(latestVersionEntries);
|
|
41769
|
+
}
|
|
41770
|
+
/**
|
|
41771
|
+
* Derives the update state of one installed Promptbook CLI package.
|
|
41772
|
+
*
|
|
41773
|
+
* @private internal utility of `$checkPromptbookCliInstallations`
|
|
41774
|
+
*/
|
|
41775
|
+
function resolvePromptbookCliInstallationState(installedVersion, latestVersion) {
|
|
41776
|
+
if (latestVersion === null) {
|
|
41777
|
+
return 'unknown';
|
|
41778
|
+
}
|
|
41779
|
+
return isNpmPackageVersionOutdated(installedVersion, latestVersion) ? 'outdated' : 'up-to-date';
|
|
41780
|
+
}
|
|
41781
|
+
// 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`
|
|
41782
|
+
|
|
41783
|
+
/**
|
|
41784
|
+
* Builds the npm command that updates one existing Promptbook CLI installation.
|
|
41785
|
+
*
|
|
41786
|
+
* @private internal utility of `promptbookCli`
|
|
41787
|
+
*/
|
|
41788
|
+
function buildPromptbookCliInstallCommand(installation) {
|
|
41789
|
+
const { npmPackageName, installationLocation } = installation;
|
|
41790
|
+
if (installationLocation === 'global') {
|
|
41791
|
+
return `npm install --global ${npmPackageName}@latest`;
|
|
41792
|
+
}
|
|
41793
|
+
if (installationLocation === 'local-development-dependency') {
|
|
41794
|
+
return `npm install --save-dev ${npmPackageName}@latest`;
|
|
41795
|
+
}
|
|
41796
|
+
return `npm install --save ${npmPackageName}@latest`;
|
|
41797
|
+
}
|
|
41798
|
+
// 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`
|
|
41799
|
+
|
|
41800
|
+
/**
|
|
41801
|
+
* Updates one Promptbook CLI package in its existing local or global location.
|
|
41802
|
+
*
|
|
41803
|
+
* Note: `$` is used to indicate that this function is not a pure function - it installs an npm package
|
|
41804
|
+
*
|
|
41805
|
+
* @returns `true` when the package update succeeds, `false` when it fails
|
|
41806
|
+
* @private internal utility of `promptbookCli`
|
|
41807
|
+
*/
|
|
41808
|
+
async function $updatePromptbookCliInstallation(status) {
|
|
41809
|
+
const { npmPackageName } = status.installation;
|
|
41810
|
+
const installCommand = buildPromptbookCliInstallCommand(status.installation);
|
|
41811
|
+
console.info(colors.cyan(`Updating Promptbook CLI package \`${npmPackageName}\` with \`${installCommand}\`...`));
|
|
41812
|
+
try {
|
|
41813
|
+
await $execCommand({
|
|
41814
|
+
command: installCommand,
|
|
41815
|
+
crashOnError: true,
|
|
41816
|
+
isVerbose: true,
|
|
41817
|
+
});
|
|
41818
|
+
}
|
|
41819
|
+
catch (error) {
|
|
41820
|
+
assertsError(error);
|
|
41821
|
+
const updateErrorMessage = error.message;
|
|
41822
|
+
console.error(colors.red(spaceTrim$1((block) => `
|
|
41823
|
+
Could not update **Promptbook CLI** package \`${npmPackageName}\`.
|
|
41824
|
+
|
|
41825
|
+
Update it manually with \`${installCommand}\` and run the command again.
|
|
41826
|
+
|
|
41827
|
+
${block(updateErrorMessage)}
|
|
41828
|
+
`)));
|
|
41829
|
+
return false;
|
|
41830
|
+
}
|
|
41831
|
+
console.info(colors.green(`Promptbook CLI package \`${npmPackageName}\` has been updated.`));
|
|
41832
|
+
return true;
|
|
41833
|
+
}
|
|
41834
|
+
// 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`
|
|
41835
|
+
|
|
41836
|
+
/**
|
|
41837
|
+
* Formats the warning shown when one or more Promptbook CLI installations are outdated.
|
|
41838
|
+
*
|
|
41839
|
+
* @private internal utility of `promptbookCli`
|
|
41840
|
+
*/
|
|
41841
|
+
function formatPromptbookCliInstallationWarning(statuses) {
|
|
41842
|
+
const statusLines = statuses.map((status) => {
|
|
41843
|
+
const { npmPackageName, installedVersion, installationLocation } = status.installation;
|
|
41844
|
+
return `- ${formatPromptbookCliInstallationLocation(installationLocation)} \`${npmPackageName}\`: installed \`${installedVersion}\`, newest \`${status.latestVersion}\``;
|
|
41845
|
+
});
|
|
41846
|
+
return spaceTrim$1((block) => `
|
|
41847
|
+
**Promptbook CLI** is outdated.
|
|
41848
|
+
|
|
41849
|
+
${block(statusLines.join('\n'))}
|
|
41850
|
+
`);
|
|
41851
|
+
}
|
|
41852
|
+
/**
|
|
41853
|
+
* Describes an installation location in text suitable for an update warning.
|
|
41854
|
+
*
|
|
41855
|
+
* @private internal utility of `formatPromptbookCliInstallationWarning`
|
|
41856
|
+
*/
|
|
41857
|
+
function formatPromptbookCliInstallationLocation(installationLocation) {
|
|
41858
|
+
if (installationLocation === 'global') {
|
|
41859
|
+
return 'Globally installed';
|
|
41860
|
+
}
|
|
41861
|
+
if (installationLocation === 'local-development-dependency') {
|
|
41862
|
+
return 'Locally in `devDependencies`';
|
|
41863
|
+
}
|
|
41864
|
+
return 'Locally in `dependencies`';
|
|
41865
|
+
}
|
|
41866
|
+
// 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`
|
|
41867
|
+
|
|
41868
|
+
/**
|
|
41869
|
+
* Checks local and global Promptbook CLI installations before an interactive coder run and offers to update them.
|
|
41870
|
+
*
|
|
41871
|
+
* Once an update is approved, the caller must stop the current run and let the user start a fresh process. A local
|
|
41872
|
+
* npm update can modify `package.json` and a lockfile, which would make the coder's working-tree preflight fail;
|
|
41873
|
+
* more importantly, this Node.js process has already loaded the old CLI implementation.
|
|
41874
|
+
*
|
|
41875
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads package manifests, queries npm,
|
|
41876
|
+
* asks the user, and may install npm packages
|
|
41877
|
+
*
|
|
41878
|
+
* @returns `true` when an update was approved and the current coder run should stop
|
|
41879
|
+
* @private internal utility of `promptbookCli`
|
|
41880
|
+
*/
|
|
41881
|
+
async function $ensurePromptbookCliInstallations() {
|
|
41882
|
+
if (!process.stdin.isTTY) {
|
|
41883
|
+
// Note: Non-interactive runs cannot approve an update and should not wait for registry checks.
|
|
41884
|
+
return false;
|
|
41885
|
+
}
|
|
41886
|
+
const statuses = await $checkPromptbookCliInstallations();
|
|
41887
|
+
const outdatedStatuses = statuses.filter(({ installationState }) => installationState === 'outdated');
|
|
41888
|
+
if (outdatedStatuses.length === 0) {
|
|
41889
|
+
reportPromptbookCliInstallationStatuses(statuses);
|
|
41890
|
+
return false;
|
|
41891
|
+
}
|
|
41892
|
+
console.warn(colors.yellow(formatPromptbookCliInstallationWarning(outdatedStatuses)));
|
|
41893
|
+
const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?');
|
|
41894
|
+
if (!isUpdateApproved) {
|
|
41895
|
+
const manualInstallCommands = outdatedStatuses
|
|
41896
|
+
.map((status) => `- \`${buildPromptbookCliInstallCommand(status.installation)}\``)
|
|
41897
|
+
.join('\n');
|
|
41898
|
+
console.info(colors.gray(spaceTrim$1((block) => `
|
|
41899
|
+
Skipped updating Promptbook CLI.
|
|
41900
|
+
|
|
41901
|
+
Run one of these commands manually:
|
|
41902
|
+
|
|
41903
|
+
${block(manualInstallCommands)}
|
|
41904
|
+
`)));
|
|
41905
|
+
return false;
|
|
41906
|
+
}
|
|
41907
|
+
for (const status of outdatedStatuses) {
|
|
41908
|
+
await $updatePromptbookCliInstallation(status);
|
|
41909
|
+
}
|
|
41910
|
+
console.info(colors.green('Promptbook CLI update is complete. Run the command again to use the new version.'));
|
|
41911
|
+
return true;
|
|
41912
|
+
}
|
|
41913
|
+
/**
|
|
41914
|
+
* Reports successful and unverifiable Promptbook CLI checks when no update is required.
|
|
41915
|
+
*
|
|
41916
|
+
* @private internal utility of `$ensurePromptbookCliInstallations`
|
|
41917
|
+
*/
|
|
41918
|
+
function reportPromptbookCliInstallationStatuses(statuses) {
|
|
41919
|
+
for (const status of statuses) {
|
|
41920
|
+
const { npmPackageName, installedVersion } = status.installation;
|
|
41921
|
+
if (status.installationState === 'up-to-date') {
|
|
41922
|
+
console.info(colors.gray(`✔ Promptbook CLI \`${npmPackageName}\` ${installedVersion} is up to date.`));
|
|
41923
|
+
continue;
|
|
41924
|
+
}
|
|
41925
|
+
if (status.installationState === 'unknown') {
|
|
41926
|
+
console.info(colors.gray(`✔ Promptbook CLI \`${npmPackageName}\` ${installedVersion} is installed; the newest version could not be checked.`));
|
|
41927
|
+
}
|
|
41928
|
+
}
|
|
41929
|
+
}
|
|
41930
|
+
// 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`
|
|
41931
|
+
|
|
40841
41932
|
/**
|
|
40842
41933
|
* Modes supported by `ptbk coder run --test-before`.
|
|
40843
41934
|
*/
|
|
@@ -40890,12 +41981,14 @@ function $initializeCoderRunCommand(program) {
|
|
|
40890
41981
|
|
|
40891
41982
|
Features:
|
|
40892
41983
|
- Automatically stages and commits changes with agent identity unless --no-commit is used
|
|
41984
|
+
- Commits only the prompt file and the files the coding agent has changed, leaving unrelated changes alone
|
|
40893
41985
|
- Optional post-commit git push with explicit --auto-push opt-in
|
|
40894
41986
|
- Optional pre-prompt git pull with explicit --auto-pull opt-in
|
|
40895
41987
|
- Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
|
|
40896
41988
|
- Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
|
|
40897
41989
|
- Optional --no-ui keeps plain streaming console output for logging and debugging
|
|
40898
41990
|
- Checks that the selected harness is installed globally and up to date before the first prompt
|
|
41991
|
+
- In interactive mode, checks local and global Promptbook CLI installations and offers to update them
|
|
40899
41992
|
- Supports GPG signing of commits
|
|
40900
41993
|
- Optional pre-coding test run that can stop or repair pre-existing failures
|
|
40901
41994
|
- Optional post-prompt verification with test-feedback retries
|
|
@@ -40945,7 +42038,6 @@ function $initializeCoderRunCommand(program) {
|
|
|
40945
42038
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
40946
42039
|
isAgentRequired: !dryRun,
|
|
40947
42040
|
});
|
|
40948
|
-
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
40949
42041
|
// [1] Parse the wait options and --no-auto:
|
|
40950
42042
|
// default: run automatically through the queue (no waiting between prompts)
|
|
40951
42043
|
// --no-auto: wait for user confirmation before each prompt (interactive mode)
|
|
@@ -40953,6 +42045,10 @@ function $initializeCoderRunCommand(program) {
|
|
|
40953
42045
|
// --wait-between-prompts: pace from start of one prompt to start of next
|
|
40954
42046
|
// --wait-after-error: wait before retrying after an error (default 10m)
|
|
40955
42047
|
const waitForUser = !auto;
|
|
42048
|
+
if (waitForUser && (await $ensurePromptbookCliInstallations())) {
|
|
42049
|
+
return process.exit(0);
|
|
42050
|
+
}
|
|
42051
|
+
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
40956
42052
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
40957
42053
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
40958
42054
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -41172,6 +42268,45 @@ function normalizeCommandOptionValue(value) {
|
|
|
41172
42268
|
// Note: [🟡] Code for CLI command [server](src/cli/cli-commands/coder/server.ts) should never be published outside of `@promptbook/cli`
|
|
41173
42269
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
41174
42270
|
|
|
42271
|
+
/**
|
|
42272
|
+
* Orders supported by `ptbk coder verify --order`.
|
|
42273
|
+
*/
|
|
42274
|
+
const VERIFY_PROMPTS_ORDER_VALUES = ['from-earliest', 'from-latest', 'random'];
|
|
42275
|
+
/**
|
|
42276
|
+
* Order used when `--order` is not provided.
|
|
42277
|
+
*/
|
|
42278
|
+
const DEFAULT_VERIFY_PROMPTS_ORDER = 'from-earliest';
|
|
42279
|
+
/**
|
|
42280
|
+
* Human-readable description of each supported order, shared by the CLI help and the verification output.
|
|
42281
|
+
*/
|
|
42282
|
+
const VERIFY_PROMPTS_ORDER_DESCRIPTIONS = {
|
|
42283
|
+
'from-earliest': 'from the earliest prompt file',
|
|
42284
|
+
'from-latest': 'from the latest prompt file',
|
|
42285
|
+
random: 'in random order',
|
|
42286
|
+
};
|
|
42287
|
+
/**
|
|
42288
|
+
* Parses and validates one raw `--order` value.
|
|
42289
|
+
*
|
|
42290
|
+
* Note: `ptbk coder verify` lets Commander validate the value, this is used by the standalone script which parses the raw arguments itself
|
|
42291
|
+
*/
|
|
42292
|
+
function parseVerifyPromptsOrder(orderValue) {
|
|
42293
|
+
if (orderValue === undefined) {
|
|
42294
|
+
return DEFAULT_VERIFY_PROMPTS_ORDER;
|
|
42295
|
+
}
|
|
42296
|
+
if (VERIFY_PROMPTS_ORDER_VALUES.includes(orderValue)) {
|
|
42297
|
+
return orderValue;
|
|
42298
|
+
}
|
|
42299
|
+
throw new NotAllowed(spaceTrim$1((block) => `
|
|
42300
|
+
Invalid value for \`--order\`: \`${orderValue}\`.
|
|
42301
|
+
|
|
42302
|
+
Use one of the supported orders:
|
|
42303
|
+
${block(VERIFY_PROMPTS_ORDER_VALUES.map((value) => `- \`${value}\` processes the prompt files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[value]}`).join('\n'))}
|
|
42304
|
+
|
|
42305
|
+
Actionable hint:
|
|
42306
|
+
- The removed \`--reverse\` flag is now \`--order from-latest\`.
|
|
42307
|
+
`));
|
|
42308
|
+
}
|
|
42309
|
+
|
|
41175
42310
|
/**
|
|
41176
42311
|
* Initializes `coder verify` command for Promptbook CLI utilities
|
|
41177
42312
|
*
|
|
@@ -41191,21 +42326,27 @@ function $initializeCoderVerifyCommand(program) {
|
|
|
41191
42326
|
- Auto-appends repair prompts for incomplete work
|
|
41192
42327
|
- Processes files with all-done prompts first
|
|
41193
42328
|
- Supports ignoring matching prompt candidates for one verification run
|
|
42329
|
+
- Supports processing the prompt files from the earliest, from the latest or in random order
|
|
41194
42330
|
|
|
41195
42331
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
41196
42332
|
|
|
41197
42333
|
Note: The git synchronization is applied around each single verification, not once per run.
|
|
41198
42334
|
`));
|
|
41199
|
-
command.
|
|
42335
|
+
command.addOption(new Option('--order <order>', spaceTrim$1((block) => `
|
|
42336
|
+
Order in which the prompt files are processed:
|
|
42337
|
+
${block(VERIFY_PROMPTS_ORDER_VALUES.map((order) => `- ${order}: ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[order]}`).join('\n'))}
|
|
42338
|
+
`))
|
|
42339
|
+
.choices([...VERIFY_PROMPTS_ORDER_VALUES])
|
|
42340
|
+
.default(DEFAULT_VERIFY_PROMPTS_ORDER));
|
|
41200
42341
|
command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
|
|
41201
42342
|
addCoderGitSyncOptions(command);
|
|
41202
42343
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
41203
|
-
const {
|
|
42344
|
+
const { order, ignore } = cliOptions;
|
|
41204
42345
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
41205
42346
|
// Note: Import the main function dynamically to avoid loading heavy dependencies until needed
|
|
41206
42347
|
const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
|
|
41207
42348
|
try {
|
|
41208
|
-
await verifyPrompts({
|
|
42349
|
+
await verifyPrompts({ order, ignore, gitSync });
|
|
41209
42350
|
}
|
|
41210
42351
|
catch (error) {
|
|
41211
42352
|
console.error(colors.bgRed('Prompt verification failed:'), error);
|
|
@@ -43984,8 +45125,8 @@ const formatCommandParser = {
|
|
|
43984
45125
|
*/
|
|
43985
45126
|
$applyToTaskJson(command, $taskJson) {
|
|
43986
45127
|
if ($taskJson.format !== undefined && command.format !== $taskJson.format) {
|
|
43987
|
-
throw new ParseError(`Format format is already defined to "${$taskJson.format}".
|
|
43988
|
-
Now you try to redefine it by "${command.format}"`);
|
|
45128
|
+
throw new ParseError(spaceTrim$1(`Format format is already defined to "${$taskJson.format}".
|
|
45129
|
+
Now you try to redefine it by "${command.format}"`));
|
|
43989
45130
|
}
|
|
43990
45131
|
$taskJson.format = command.format;
|
|
43991
45132
|
},
|
|
@@ -56828,28 +57969,28 @@ function parseToolExecutionEnvelope(rawValue) {
|
|
|
56828
57969
|
*/
|
|
56829
57970
|
function buildToolInvocationScript(options) {
|
|
56830
57971
|
const { functionName, functionArgsExpression } = options;
|
|
56831
|
-
return `
|
|
56832
|
-
|
|
56833
|
-
|
|
56834
|
-
|
|
56835
|
-
|
|
56836
|
-
|
|
57972
|
+
return spaceTrim$1((block) => `
|
|
57973
|
+
const args = ${block(functionArgsExpression)};
|
|
57974
|
+
const runtimeContextRaw =
|
|
57975
|
+
typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
|
|
57976
|
+
? undefined
|
|
57977
|
+
: ${TOOL_RUNTIME_CONTEXT_PARAMETER};
|
|
56837
57978
|
|
|
56838
|
-
|
|
56839
|
-
|
|
56840
|
-
|
|
57979
|
+
if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
|
|
57980
|
+
args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
|
|
57981
|
+
}
|
|
56841
57982
|
|
|
56842
|
-
|
|
56843
|
-
|
|
56844
|
-
|
|
56845
|
-
|
|
57983
|
+
const toolProgressTokenRaw =
|
|
57984
|
+
typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
|
|
57985
|
+
? undefined
|
|
57986
|
+
: ${TOOL_PROGRESS_TOKEN_PARAMETER};
|
|
56846
57987
|
|
|
56847
|
-
|
|
56848
|
-
|
|
56849
|
-
|
|
57988
|
+
if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
|
|
57989
|
+
args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
|
|
57990
|
+
}
|
|
56850
57991
|
|
|
56851
|
-
|
|
56852
|
-
|
|
57992
|
+
return await ${functionName}(args);
|
|
57993
|
+
`);
|
|
56853
57994
|
}
|
|
56854
57995
|
|
|
56855
57996
|
/**
|
|
@@ -68795,6 +69936,28 @@ var RemoteAgent$1 = /*#__PURE__*/Object.freeze({
|
|
|
68795
69936
|
RemoteAgent: RemoteAgent
|
|
68796
69937
|
});
|
|
68797
69938
|
|
|
69939
|
+
/**
|
|
69940
|
+
* Captures the working tree state before one `ptbk coder` operation changes anything.
|
|
69941
|
+
*
|
|
69942
|
+
* The captured scope is passed to the commit of the very same operation, which then commits exactly the files
|
|
69943
|
+
* this operation has created, changed, moved or deleted.
|
|
69944
|
+
*/
|
|
69945
|
+
async function captureCoderCommitScope(projectPath) {
|
|
69946
|
+
return {
|
|
69947
|
+
projectPath,
|
|
69948
|
+
snapshotBeforeOperation: await captureWorkingTreeChangesSnapshot(projectPath),
|
|
69949
|
+
};
|
|
69950
|
+
}
|
|
69951
|
+
/**
|
|
69952
|
+
* Resolves the repository-relative paths which one `ptbk coder` operation has really changed.
|
|
69953
|
+
*
|
|
69954
|
+
* Files which were already changed before the operation started and which the operation did not touch are
|
|
69955
|
+
* never part of the result, so they stay in the working tree instead of being swept into the commit.
|
|
69956
|
+
*/
|
|
69957
|
+
async function resolveCoderCommitScopePaths(scope) {
|
|
69958
|
+
return listFilesChangedSinceSnapshot(scope.projectPath, scope.snapshotBeforeOperation);
|
|
69959
|
+
}
|
|
69960
|
+
|
|
68798
69961
|
/**
|
|
68799
69962
|
* Git synchronization which leaves the repository completely untouched.
|
|
68800
69963
|
*
|
|
@@ -68805,6 +69968,24 @@ const DISABLED_CODER_GIT_SYNC_OPTIONS = Object.freeze({
|
|
|
68805
69968
|
isAutoPushEnabled: false,
|
|
68806
69969
|
isAutoPullEnabled: false,
|
|
68807
69970
|
});
|
|
69971
|
+
/**
|
|
69972
|
+
* Pulls the latest repository changes and captures the working tree state before a `ptbk coder` command
|
|
69973
|
+
* changes the project.
|
|
69974
|
+
*
|
|
69975
|
+
* The returned scope is handed over to `$commitCoderChanges` of the very same command, which then commits
|
|
69976
|
+
* exactly the files this command has changed.
|
|
69977
|
+
*/
|
|
69978
|
+
async function $startCoderGitSync(options) {
|
|
69979
|
+
const { gitSync, projectPath = process.cwd() } = options;
|
|
69980
|
+
await $pullCoderChanges({ gitSync, projectPath });
|
|
69981
|
+
if (!gitSync.isCommitEnabled) {
|
|
69982
|
+
// Note: A command which does not commit must not touch git at all, so that it also works in a project
|
|
69983
|
+
// which is not a git repository
|
|
69984
|
+
return { projectPath, snapshotBeforeOperation: { changedFileHashes: new Map() } };
|
|
69985
|
+
}
|
|
69986
|
+
// Note: The scope is captured after pulling, so files brought in by the pull are not committed again
|
|
69987
|
+
return captureCoderCommitScope(projectPath);
|
|
69988
|
+
}
|
|
68808
69989
|
/**
|
|
68809
69990
|
* Pulls the latest repository changes before a `ptbk coder` command changes the project.
|
|
68810
69991
|
*/
|
|
@@ -68819,38 +70000,31 @@ async function $pullCoderChanges(options) {
|
|
|
68819
70000
|
/**
|
|
68820
70001
|
* Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
|
|
68821
70002
|
*
|
|
68822
|
-
* Note:
|
|
70003
|
+
* Note: Only the files this very command has changed are committed, everything else is left in the working tree.
|
|
70004
|
+
* Note: A command which changed nothing is left alone instead of creating an empty commit.
|
|
68823
70005
|
*/
|
|
68824
70006
|
async function $commitCoderChanges(options) {
|
|
68825
|
-
const { gitSync, commitMessage,
|
|
70007
|
+
const { gitSync, commitMessage, commitScope } = options;
|
|
68826
70008
|
if (!gitSync.isCommitEnabled) {
|
|
68827
70009
|
return;
|
|
68828
70010
|
}
|
|
68829
|
-
|
|
68830
|
-
|
|
70011
|
+
const relevantPaths = await resolveCoderCommitScopePaths(commitScope);
|
|
70012
|
+
if (relevantPaths.length === 0) {
|
|
70013
|
+
console.info(colors.gray('Nothing to commit, this command has not changed any file'));
|
|
68831
70014
|
return;
|
|
68832
70015
|
}
|
|
68833
70016
|
await commitChanges(commitMessage, {
|
|
68834
|
-
projectPath,
|
|
70017
|
+
projectPath: commitScope.projectPath,
|
|
70018
|
+
relevantPaths,
|
|
68835
70019
|
autoPush: gitSync.isAutoPushEnabled,
|
|
68836
70020
|
});
|
|
68837
70021
|
console.info(colors.green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
|
|
68838
70022
|
}
|
|
68839
|
-
/**
|
|
68840
|
-
* Checks whether the repository holds any change which can be committed.
|
|
68841
|
-
*/
|
|
68842
|
-
async function hasChangesToCommit(projectPath) {
|
|
68843
|
-
const gitStatus = await runGitCommand({
|
|
68844
|
-
command: 'git status --porcelain',
|
|
68845
|
-
cwd: projectPath,
|
|
68846
|
-
isVerbose: false,
|
|
68847
|
-
});
|
|
68848
|
-
return gitStatus.trim() !== '';
|
|
68849
|
-
}
|
|
68850
70023
|
|
|
68851
70024
|
var coderGitSync = /*#__PURE__*/Object.freeze({
|
|
68852
70025
|
__proto__: null,
|
|
68853
70026
|
DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
|
|
70027
|
+
$startCoderGitSync: $startCoderGitSync,
|
|
68854
70028
|
$pullCoderChanges: $pullCoderChanges,
|
|
68855
70029
|
$commitCoderChanges: $commitCoderChanges
|
|
68856
70030
|
});
|
|
@@ -70350,7 +71524,7 @@ function parsePromptFile(filePath, content) {
|
|
|
70350
71524
|
}
|
|
70351
71525
|
/**
|
|
70352
71526
|
* Parses a status line like "[ ] !!", "[ ] use `gpt` !!!!!" or "[-]" into status and priority.
|
|
70353
|
-
* For [x] done
|
|
71527
|
+
* For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
|
|
70354
71528
|
*/
|
|
70355
71529
|
function parseStatusLine(line) {
|
|
70356
71530
|
var _a, _b, _c, _d;
|
|
@@ -70364,6 +71538,11 @@ function parseStatusLine(line) {
|
|
|
70364
71538
|
if (failedMatch) {
|
|
70365
71539
|
return { status: 'failed', priority: 0 };
|
|
70366
71540
|
}
|
|
71541
|
+
// For in-progress prompts [^], allow any content after (for the steps recorded so far)
|
|
71542
|
+
const inProgressMatch = line.match(/^\[(?<status>\^)\]/);
|
|
71543
|
+
if (inProgressMatch) {
|
|
71544
|
+
return { status: 'in-progress', priority: 0 };
|
|
71545
|
+
}
|
|
70367
71546
|
// For not-ready [-], keep the historical clean-line syntax.
|
|
70368
71547
|
if (/^\[-\]\s*!*\s*$/u.test(line)) {
|
|
70369
71548
|
return { status: 'not-ready', priority: 0 };
|
|
@@ -72183,34 +73362,34 @@ async function migratePrefix(options) {
|
|
|
72183
73362
|
* @private function of runDatabaseMigrations
|
|
72184
73363
|
*/
|
|
72185
73364
|
async function ensureMigrationsTableSchema(options) {
|
|
72186
|
-
await options.client.query(`
|
|
73365
|
+
await options.client.query(spaceTrim$1(`
|
|
72187
73366
|
CREATE TABLE IF NOT EXISTS ${options.migrationsTableIdentifier} (
|
|
72188
73367
|
"filename" TEXT PRIMARY KEY,
|
|
72189
73368
|
"appliedAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
72190
73369
|
"appliedBy" TEXT NOT NULL DEFAULT '${options.manualAppliedByDefault}'
|
|
72191
73370
|
);
|
|
72192
|
-
`);
|
|
72193
|
-
await options.client.query(`
|
|
73371
|
+
`));
|
|
73372
|
+
await options.client.query(spaceTrim$1(`
|
|
72194
73373
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72195
73374
|
ADD COLUMN IF NOT EXISTS "appliedBy" TEXT;
|
|
72196
|
-
`);
|
|
72197
|
-
await options.client.query(`
|
|
73375
|
+
`));
|
|
73376
|
+
await options.client.query(spaceTrim$1(`
|
|
72198
73377
|
UPDATE ${options.migrationsTableIdentifier}
|
|
72199
73378
|
SET "appliedBy" = $1
|
|
72200
73379
|
WHERE "appliedBy" IS NULL;
|
|
72201
|
-
|
|
72202
|
-
await options.client.query(`
|
|
73380
|
+
`), [options.manualAppliedByDefault]);
|
|
73381
|
+
await options.client.query(spaceTrim$1(`
|
|
72203
73382
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72204
73383
|
ALTER COLUMN "appliedBy" SET DEFAULT '${options.manualAppliedByDefault}';
|
|
72205
|
-
`);
|
|
72206
|
-
await options.client.query(`
|
|
73384
|
+
`));
|
|
73385
|
+
await options.client.query(spaceTrim$1(`
|
|
72207
73386
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72208
73387
|
ALTER COLUMN "appliedBy" SET NOT NULL;
|
|
72209
|
-
`);
|
|
72210
|
-
await options.client.query(`
|
|
73388
|
+
`));
|
|
73389
|
+
await options.client.query(spaceTrim$1(`
|
|
72211
73390
|
ALTER TABLE ${options.migrationsTableIdentifier}
|
|
72212
73391
|
ENABLE ROW LEVEL SECURITY;
|
|
72213
|
-
`);
|
|
73392
|
+
`));
|
|
72214
73393
|
}
|
|
72215
73394
|
/**
|
|
72216
73395
|
* Rolls back an opened transaction and logs failures without masking original errors.
|
|
@@ -72943,14 +74122,25 @@ const CODER_RUN_STEP_LABELS = {
|
|
|
72943
74122
|
fixing: 'Fixing',
|
|
72944
74123
|
};
|
|
72945
74124
|
/**
|
|
72946
|
-
*
|
|
74125
|
+
* Suffix marking the one step which has already started but has neither a price nor a duration yet.
|
|
74126
|
+
*/
|
|
74127
|
+
const IN_PROGRESS_STEP_SUFFIX = 'in progress';
|
|
74128
|
+
/**
|
|
74129
|
+
* Formats the per-step usage breakdown recorded for one prompt.
|
|
72947
74130
|
*
|
|
72948
74131
|
* Produces a `; `-separated summary such as
|
|
72949
74132
|
* `Implementation $8.01 6 hours; Testing 1 hour; Fixing $3.14 2 hours` where each coding step carries its
|
|
72950
74133
|
* price and duration and each verification step carries only its duration.
|
|
74134
|
+
*
|
|
74135
|
+
* @param steps - Steps which have already finished
|
|
74136
|
+
* @param inProgressStepKind - Step which has started but has not finished yet, appended as `Testing in progress`
|
|
72951
74137
|
*/
|
|
72952
|
-
function formatCoderRunSteps(steps) {
|
|
72953
|
-
|
|
74138
|
+
function formatCoderRunSteps(steps, inProgressStepKind) {
|
|
74139
|
+
const formattedSteps = steps.map(formatCoderRunStep);
|
|
74140
|
+
if (inProgressStepKind !== undefined) {
|
|
74141
|
+
formattedSteps.push(`${CODER_RUN_STEP_LABELS[inProgressStepKind]} ${IN_PROGRESS_STEP_SUFFIX}`);
|
|
74142
|
+
}
|
|
74143
|
+
return formattedSteps.join('; ');
|
|
72954
74144
|
}
|
|
72955
74145
|
/**
|
|
72956
74146
|
* Formats one coder run step as `Label $price duration`, omitting the price for steps without model usage.
|
|
@@ -72979,47 +74169,78 @@ function formatPromptAttemptMetadata(status, attemptCount) {
|
|
|
72979
74169
|
}
|
|
72980
74170
|
|
|
72981
74171
|
/**
|
|
72982
|
-
*
|
|
74172
|
+
* Builds the shared body of a prompt status line, used by both the in-progress `[^]` and the done `[x]` status.
|
|
72983
74173
|
*
|
|
72984
|
-
*
|
|
72985
|
-
*
|
|
74174
|
+
* Produces details such as
|
|
74175
|
+
* ``by OpenAI Codex `gpt-5.6-luna` thinking `max` (ChatGPT account) - Implementation ~$0.2036 10 minutes``.
|
|
72986
74176
|
*/
|
|
72987
|
-
function
|
|
72988
|
-
|
|
74177
|
+
function buildPromptStatusDetails(options) {
|
|
74178
|
+
const { steps, inProgressStepKind, runnerName, modelName, attemptCount, loginMethod, thinkingLevel } = options;
|
|
74179
|
+
const runnerSignature = formatRunnerSignature(runnerName, modelName, thinkingLevel);
|
|
74180
|
+
const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
|
|
74181
|
+
const loginMethodLabel = formatCodexLoginMethod(loginMethod);
|
|
74182
|
+
const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
|
|
74183
|
+
const stepsSummary = formatCoderRunSteps(steps, inProgressStepKind);
|
|
74184
|
+
const stepsSuffix = stepsSummary === '' ? '' : ` - ${stepsSummary}`;
|
|
74185
|
+
return `${attemptMetadata}by ${runnerSignature}${loginMethodSuffix}${stepsSuffix}`;
|
|
72989
74186
|
}
|
|
72990
74187
|
|
|
72991
74188
|
/**
|
|
72992
|
-
*
|
|
74189
|
+
* Resolves the status line of one prompt section so it can be rewritten.
|
|
72993
74190
|
*/
|
|
72994
|
-
function
|
|
74191
|
+
function resolvePromptStatusLine(file, section) {
|
|
72995
74192
|
if (section.statusLineIndex === undefined) {
|
|
72996
|
-
throw new
|
|
74193
|
+
throw new UnexpectedError(spaceTrim$1(`
|
|
74194
|
+
Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
|
|
74195
|
+
`));
|
|
72997
74196
|
}
|
|
72998
74197
|
const line = file.lines[section.statusLineIndex];
|
|
72999
74198
|
if (line === undefined) {
|
|
73000
|
-
throw new
|
|
74199
|
+
throw new UnexpectedError(spaceTrim$1(`
|
|
74200
|
+
Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
|
|
74201
|
+
`));
|
|
73001
74202
|
}
|
|
73002
|
-
|
|
73003
|
-
|
|
73004
|
-
|
|
73005
|
-
|
|
73006
|
-
|
|
73007
|
-
|
|
73008
|
-
|
|
73009
|
-
|
|
74203
|
+
return { statusLineIndex: section.statusLineIndex, line };
|
|
74204
|
+
}
|
|
74205
|
+
|
|
74206
|
+
/**
|
|
74207
|
+
* Matches a complete todo `[ ]` or in-progress `[^]` status line, capturing its indentation.
|
|
74208
|
+
*
|
|
74209
|
+
* The complete line is replaced because a todo status can contain a required model/harness token
|
|
74210
|
+
* in addition to its priority markers, and an in-progress status already carries the metadata of
|
|
74211
|
+
* the steps recorded so far.
|
|
74212
|
+
*/
|
|
74213
|
+
const REWRITABLE_PROMPT_STATUS_LINE_PATTERN = /^(?<indentation>\s*)\[(?:\s*|\^)\].*$/u;
|
|
74214
|
+
/**
|
|
74215
|
+
* Rewrites the status line of one prompt section while preserving its indentation.
|
|
74216
|
+
*
|
|
74217
|
+
* Only a todo `[ ]` or an in-progress `[^]` status line is rewritten, so an already finalized
|
|
74218
|
+
* `[x]`, `[!]` or `[-]` status is never overwritten by accident.
|
|
74219
|
+
*/
|
|
74220
|
+
function writePromptStatusLine(file, section, replacementStatusLine) {
|
|
74221
|
+
const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
|
|
74222
|
+
file.lines[statusLineIndex] = line.replace(REWRITABLE_PROMPT_STATUS_LINE_PATTERN, `$<indentation>${replacementStatusLine}`);
|
|
74223
|
+
}
|
|
74224
|
+
|
|
74225
|
+
/**
|
|
74226
|
+
* Marks a prompt section as done and records the per-step usage pricing and runner details.
|
|
74227
|
+
*/
|
|
74228
|
+
function markPromptDone(file, section, steps, runnerName, modelName, attemptCount = 1, loginMethod, thinkingLevel) {
|
|
74229
|
+
const statusDetails = buildPromptStatusDetails({
|
|
74230
|
+
steps,
|
|
74231
|
+
runnerName,
|
|
74232
|
+
modelName,
|
|
74233
|
+
attemptCount,
|
|
74234
|
+
loginMethod,
|
|
74235
|
+
thinkingLevel,
|
|
74236
|
+
});
|
|
74237
|
+
writePromptStatusLine(file, section, `[x] ${statusDetails}`);
|
|
73010
74238
|
}
|
|
73011
74239
|
|
|
73012
74240
|
/**
|
|
73013
74241
|
* Marks a prompt section as failed and records runner details.
|
|
73014
74242
|
*/
|
|
73015
74243
|
function markPromptFailed(file, section, runnerName, modelName, promptExecutionStartedDate, attemptCount = 1) {
|
|
73016
|
-
if (section.statusLineIndex === undefined) {
|
|
73017
|
-
throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
|
|
73018
|
-
}
|
|
73019
|
-
const line = file.lines[section.statusLineIndex];
|
|
73020
|
-
if (line === undefined) {
|
|
73021
|
-
throw new Error(`Prompt ${section.index + 1} in ${file.name} points to a missing status line.`);
|
|
73022
|
-
}
|
|
73023
74244
|
const runnerSignature = formatRunnerSignature(runnerName, modelName);
|
|
73024
74245
|
const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
|
|
73025
74246
|
const duration = moment().diff(promptExecutionStartedDate);
|
|
@@ -73027,7 +74248,18 @@ function markPromptFailed(file, section, runnerName, modelName, promptExecutionS
|
|
|
73027
74248
|
const failureDetails = attemptMetadata === ''
|
|
73028
74249
|
? `failed after ${durationString} by ${runnerSignature}`
|
|
73029
74250
|
: `${attemptMetadata}${durationString} by ${runnerSignature}`;
|
|
73030
|
-
file
|
|
74251
|
+
writePromptStatusLine(file, section, `[!] ${failureDetails}`);
|
|
74252
|
+
}
|
|
74253
|
+
|
|
74254
|
+
/**
|
|
74255
|
+
* Marks a prompt section as being implemented right now and records the steps finished so far.
|
|
74256
|
+
*
|
|
74257
|
+
* The `[^]` status is deliberately never reverted: when the coder is killed or crashes, the status stays
|
|
74258
|
+
* in the prompt file as the signal that this task was left in the middle of its implementation.
|
|
74259
|
+
*/
|
|
74260
|
+
function markPromptInProgress(options) {
|
|
74261
|
+
const { file, section, ...statusDetailsOptions } = options;
|
|
74262
|
+
writePromptStatusLine(file, section, `[^] ${buildPromptStatusDetails(statusDetailsOptions)}`);
|
|
73031
74263
|
}
|
|
73032
74264
|
|
|
73033
74265
|
/**
|
|
@@ -73115,9 +74347,9 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
73115
74347
|
});
|
|
73116
74348
|
const promptExecutionStartedDate = moment();
|
|
73117
74349
|
let attemptCount = 1;
|
|
73118
|
-
|
|
73119
|
-
|
|
73120
|
-
|
|
74350
|
+
// Note: The very same snapshot tells which files this round has changed, both for normalizing their line
|
|
74351
|
+
// endings and for committing only them instead of everything which is changed in the project
|
|
74352
|
+
const roundCommitScope = await captureRoundCommitScopeIfNeeded(options, roundProjectPath);
|
|
73121
74353
|
await withPromptRuntimeLog(scriptPath, async (logPath) => {
|
|
73122
74354
|
let lastError;
|
|
73123
74355
|
for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
|
|
@@ -73136,6 +74368,13 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
73136
74368
|
attemptCount = nextAttemptCount;
|
|
73137
74369
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
|
|
73138
74370
|
},
|
|
74371
|
+
onStepStarted: (progress) => recordPromptRoundInProgress({
|
|
74372
|
+
nextPrompt,
|
|
74373
|
+
runnerMetadata,
|
|
74374
|
+
thinkingLevel: options.thinkingLevel,
|
|
74375
|
+
attemptCount,
|
|
74376
|
+
progress,
|
|
74377
|
+
}),
|
|
73139
74378
|
waitForPauseCheckpoint: waitForRequestedPause,
|
|
73140
74379
|
});
|
|
73141
74380
|
await finalizeSuccessfulPromptRound({
|
|
@@ -73146,7 +74385,7 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
73146
74385
|
result,
|
|
73147
74386
|
commitMessage,
|
|
73148
74387
|
logPath,
|
|
73149
|
-
|
|
74388
|
+
roundCommitScope,
|
|
73150
74389
|
isRichUiEnabled,
|
|
73151
74390
|
progressDisplay,
|
|
73152
74391
|
uiHandle,
|
|
@@ -73179,7 +74418,7 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
73179
74418
|
attemptCount,
|
|
73180
74419
|
error: lastError,
|
|
73181
74420
|
options,
|
|
73182
|
-
|
|
74421
|
+
roundCommitScope,
|
|
73183
74422
|
uiHandle,
|
|
73184
74423
|
waitForRequestedPause,
|
|
73185
74424
|
roundProjectPath,
|
|
@@ -73187,6 +74426,30 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
73187
74426
|
throw lastError;
|
|
73188
74427
|
}, { preserveArtifactsOnSuccess: options.preserveLogs });
|
|
73189
74428
|
}
|
|
74429
|
+
/**
|
|
74430
|
+
* Records into the prompt file that the prompt is being implemented right now.
|
|
74431
|
+
*
|
|
74432
|
+
* The `[^]` in-progress status is written before every single step, so it always names the step which is
|
|
74433
|
+
* running and the steps already finished. It is intentionally never reverted: a coder which is killed or
|
|
74434
|
+
* crashes leaves the `[^]` status behind as the signal that this task was left in the middle.
|
|
74435
|
+
*/
|
|
74436
|
+
async function recordPromptRoundInProgress(options) {
|
|
74437
|
+
const { nextPrompt, runnerMetadata, thinkingLevel, attemptCount, progress } = options;
|
|
74438
|
+
markPromptInProgress({
|
|
74439
|
+
file: nextPrompt.file,
|
|
74440
|
+
section: nextPrompt.section,
|
|
74441
|
+
steps: progress.finishedSteps,
|
|
74442
|
+
inProgressStepKind: progress.startedStepKind,
|
|
74443
|
+
runnerName: runnerMetadata.runnerName,
|
|
74444
|
+
modelName: runnerMetadata.modelName,
|
|
74445
|
+
attemptCount,
|
|
74446
|
+
loginMethod: progress.loginMethod,
|
|
74447
|
+
thinkingLevel,
|
|
74448
|
+
});
|
|
74449
|
+
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
74450
|
+
// its own changes back through the merge instead
|
|
74451
|
+
await writePromptFile(nextPrompt.file);
|
|
74452
|
+
}
|
|
73190
74453
|
/**
|
|
73191
74454
|
* Sleeps `options.waitAfterError` while keeping the rich UI and plain console in sync, then resets state for the retry.
|
|
73192
74455
|
*/
|
|
@@ -73236,7 +74499,7 @@ function setPromptRoundRunningState(options) {
|
|
|
73236
74499
|
* Finalizes a successful prompt round, including prompt bookkeeping and commit flow.
|
|
73237
74500
|
*/
|
|
73238
74501
|
async function finalizeSuccessfulPromptRound(options) {
|
|
73239
|
-
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath,
|
|
74502
|
+
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, result, commitMessage, logPath, roundCommitScope, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
73240
74503
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
73241
74504
|
await waitForRequestedPause({
|
|
73242
74505
|
checkpointLabel: 'recording the successful prompt result',
|
|
@@ -73247,7 +74510,7 @@ async function finalizeSuccessfulPromptRound(options) {
|
|
|
73247
74510
|
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
73248
74511
|
// its own changes back through the merge instead
|
|
73249
74512
|
await writePromptFile(nextPrompt.file);
|
|
73250
|
-
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath,
|
|
74513
|
+
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
73251
74514
|
await recordPromptDurationInEstimateCache({
|
|
73252
74515
|
options: runOptions,
|
|
73253
74516
|
runnerMetadata,
|
|
@@ -73268,6 +74531,9 @@ async function finalizeSuccessfulPromptRound(options) {
|
|
|
73268
74531
|
});
|
|
73269
74532
|
await commitChanges(commitMessage, {
|
|
73270
74533
|
autoPush: runOptions.autoPush,
|
|
74534
|
+
// Note: Only the prompt file and the files the coding agent has changed belong to this round,
|
|
74535
|
+
// everything which was already changed before the round started stays in the working tree
|
|
74536
|
+
relevantPaths: roundCommitScope && (await resolveCoderCommitScopePaths(roundCommitScope)),
|
|
73271
74537
|
// Keep the live runtime log out of default commits because it is deleted after a successful round.
|
|
73272
74538
|
excludePaths: runOptions.preserveLogs ? undefined : [logPath],
|
|
73273
74539
|
projectPath: roundProjectPath,
|
|
@@ -73291,7 +74557,7 @@ async function finalizeSuccessfulPromptRound(options) {
|
|
|
73291
74557
|
* Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
|
|
73292
74558
|
*/
|
|
73293
74559
|
async function finalizeFailedPromptRound(options) {
|
|
73294
|
-
const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions,
|
|
74560
|
+
const { nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
73295
74561
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
73296
74562
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
|
|
73297
74563
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
|
|
@@ -73309,7 +74575,7 @@ async function finalizeFailedPromptRound(options) {
|
|
|
73309
74575
|
modelName: runnerMetadata.modelName,
|
|
73310
74576
|
error,
|
|
73311
74577
|
});
|
|
73312
|
-
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath,
|
|
74578
|
+
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
73313
74579
|
}
|
|
73314
74580
|
/**
|
|
73315
74581
|
* Waits for the optional user confirmation immediately before creating the commit.
|
|
@@ -73375,17 +74641,29 @@ async function recordPromptDurationInEstimateCache(options) {
|
|
|
73375
74641
|
thinkingLevel: runOptions.thinkingLevel,
|
|
73376
74642
|
}, promptDurationMs);
|
|
73377
74643
|
}
|
|
74644
|
+
/**
|
|
74645
|
+
* Captures which files are already changed before the round starts, when the round needs to know it later.
|
|
74646
|
+
*
|
|
74647
|
+
* The scope is needed to commit only the files of this round and to normalize the line endings of exactly
|
|
74648
|
+
* those files, so a round which does neither of them does not pay for hashing the working tree.
|
|
74649
|
+
*/
|
|
74650
|
+
async function captureRoundCommitScopeIfNeeded(options, roundProjectPath) {
|
|
74651
|
+
if (options.noCommit && !options.normalizeLineEndings) {
|
|
74652
|
+
return undefined;
|
|
74653
|
+
}
|
|
74654
|
+
return captureCoderCommitScope(roundProjectPath);
|
|
74655
|
+
}
|
|
73378
74656
|
/**
|
|
73379
74657
|
* Normalizes line endings in files modified during the current coding round.
|
|
73380
74658
|
*/
|
|
73381
|
-
async function normalizeLineEndingsForCurrentRound(options, roundProjectPath,
|
|
73382
|
-
if (!options.normalizeLineEndings || !
|
|
74659
|
+
async function normalizeLineEndingsForCurrentRound(options, roundProjectPath, roundCommitScope) {
|
|
74660
|
+
if (!options.normalizeLineEndings || !roundCommitScope) {
|
|
73383
74661
|
return;
|
|
73384
74662
|
}
|
|
73385
74663
|
try {
|
|
73386
74664
|
const result = await normalizeLineEndingsInFilesChangedSinceSnapshot({
|
|
73387
74665
|
projectPath: roundProjectPath,
|
|
73388
|
-
snapshot:
|
|
74666
|
+
snapshot: roundCommitScope.snapshotBeforeOperation,
|
|
73389
74667
|
});
|
|
73390
74668
|
if (result.normalizedFiles > 0) {
|
|
73391
74669
|
console.info(colors.gray(`Normalized line endings to LF in ${result.normalizedFiles} changed file(s).`));
|
|
@@ -73446,11 +74724,11 @@ function buildCoderIsolationMergeFailureError(worktree, failureDetails) {
|
|
|
73446
74724
|
* Builds the markdown list of commands which merge one isolated task manually and clean it up.
|
|
73447
74725
|
*/
|
|
73448
74726
|
function buildManualRecoveryHints(worktree) {
|
|
73449
|
-
return
|
|
73450
|
-
|
|
73451
|
-
|
|
73452
|
-
|
|
73453
|
-
|
|
74727
|
+
return spaceTrim$1(`
|
|
74728
|
+
- \`git merge ${worktree.branchName}\` and resolve the conflicts
|
|
74729
|
+
- \`git worktree remove ${worktree.worktreeDisplayPath}\`
|
|
74730
|
+
- \`git branch -d ${worktree.branchName}\`
|
|
74731
|
+
`);
|
|
73454
74732
|
}
|
|
73455
74733
|
|
|
73456
74734
|
/**
|
|
@@ -73847,19 +75125,9 @@ const PROMPT_STATUS_BOX_PATTERN = /^(?<indentation>\s*)\[[^\]]*\]/u;
|
|
|
73847
75125
|
* kept and only the status box turns from `[x]` into `[!]`, followed by the manual merge instructions.
|
|
73848
75126
|
*/
|
|
73849
75127
|
function markPromptIsolationMergeFailed(file, section, worktree) {
|
|
73850
|
-
|
|
73851
|
-
throw new UnexpectedError(spaceTrim$1(`
|
|
73852
|
-
Prompt ${section.index + 1} in \`${file.name}\` does not have a status line.
|
|
73853
|
-
`));
|
|
73854
|
-
}
|
|
73855
|
-
const line = file.lines[section.statusLineIndex];
|
|
73856
|
-
if (line === undefined) {
|
|
73857
|
-
throw new UnexpectedError(spaceTrim$1(`
|
|
73858
|
-
Prompt ${section.index + 1} in \`${file.name}\` points to a missing status line.
|
|
73859
|
-
`));
|
|
73860
|
-
}
|
|
75128
|
+
const { statusLineIndex, line } = resolvePromptStatusLine(file, section);
|
|
73861
75129
|
const failedStatusLine = line.replace(PROMPT_STATUS_BOX_PATTERN, '$<indentation>[!]');
|
|
73862
|
-
file.lines[
|
|
75130
|
+
file.lines[statusLineIndex] = `${failedStatusLine} - ${buildCoderIsolationMergeFailureStatusNote(worktree)}`;
|
|
73863
75131
|
}
|
|
73864
75132
|
|
|
73865
75133
|
/**
|
|
@@ -73916,6 +75184,9 @@ async function runIsolatedPromptRound(options) {
|
|
|
73916
75184
|
var _a;
|
|
73917
75185
|
const { nextPrompt, promptLabel, isRichUiEnabled, uiHandle, waitForRequestedPause } = options;
|
|
73918
75186
|
const projectPath = (_a = options.projectPath) !== null && _a !== void 0 ? _a : process.cwd();
|
|
75187
|
+
// Note: The original project is left untouched by the isolated round itself, so its scope covers exactly
|
|
75188
|
+
// the prompt status update and the changes the merge brings back from the worktree
|
|
75189
|
+
const originalProjectCommitScope = await captureCoderCommitScope(projectPath);
|
|
73919
75190
|
const worktree = await createCoderIsolationWorktree({
|
|
73920
75191
|
projectPath,
|
|
73921
75192
|
taskName: buildCoderIsolationTaskName(nextPrompt.file, nextPrompt.section),
|
|
@@ -73947,6 +75218,7 @@ async function runIsolatedPromptRound(options) {
|
|
|
73947
75218
|
// Note: The merge only stages the isolated changes, so this commit joins them with the prompt status update
|
|
73948
75219
|
await commitChanges(buildCommitMessage(nextPrompt.file, nextPrompt.section), {
|
|
73949
75220
|
autoPush: options.options.autoPush,
|
|
75221
|
+
relevantPaths: await resolveCoderCommitScopePaths(originalProjectCommitScope),
|
|
73950
75222
|
projectPath,
|
|
73951
75223
|
});
|
|
73952
75224
|
await removeCoderIsolationWorktree(worktree);
|
|
@@ -73970,7 +75242,7 @@ async function recordIsolationMergeFailure(options, worktree, failureDetails) {
|
|
|
73970
75242
|
await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
|
|
73971
75243
|
autoPush: options.options.autoPush,
|
|
73972
75244
|
projectPath: worktree.projectPath,
|
|
73973
|
-
|
|
75245
|
+
relevantPaths: [nextPrompt.file.path, errorLogPath].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
|
|
73974
75246
|
});
|
|
73975
75247
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
|
|
73976
75248
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
|
|
@@ -74125,26 +75397,22 @@ function $detectTerminalAnsiColorDepth() {
|
|
|
74125
75397
|
* Builds the ANSI ASCII-art visual of the `--agent` book shown above the coder-run dashboard.
|
|
74126
75398
|
*
|
|
74127
75399
|
* The agent's avatar visual is resolved the same way as on the website - the `META AVATAR`
|
|
74128
|
-
* commitment wins, then the `META VISUAL` commitment, then the shared default visual
|
|
74129
|
-
*
|
|
74130
|
-
*
|
|
75400
|
+
* commitment wins, then the `META VISUAL` commitment, then the shared default visual.
|
|
75401
|
+
* Character-based visuals such as `AsciiOctopus` paint the terminal grid directly, while pixel-based
|
|
75402
|
+
* visuals are rendered through the shared canvas avatar pipeline into terminal ASCII art, using a
|
|
75403
|
+
* transparent horizontal canvas instead of the website's framed 1:1 surface.
|
|
74131
75404
|
*
|
|
74132
|
-
* The visual is decorative, so any failure
|
|
74133
|
-
* module is not installed) returns `null` and the caller keeps the default brand banner.
|
|
75405
|
+
* The visual is decorative, so any failure returns `null` and the caller keeps the default brand banner.
|
|
74134
75406
|
*
|
|
74135
75407
|
* @param agentSource Source of the `--agent` book file.
|
|
74136
75408
|
* @returns ANSI-colored ASCII-art renderer or `null` when the visual cannot be rendered.
|
|
74137
75409
|
*/
|
|
74138
75410
|
async function buildCoderRunAgentVisual(agentSource) {
|
|
74139
75411
|
try {
|
|
74140
|
-
// Note: `@napi-rs/canvas` is an optional native module, so it is imported dynamically and lazily
|
|
74141
|
-
const { createCanvas } = await import('@napi-rs/canvas');
|
|
74142
|
-
const colorDepth = $detectTerminalAnsiColorDepth();
|
|
74143
|
-
const createCanvasForAsciiArt = (width, height) => createCanvas(width, height);
|
|
74144
75412
|
const agentVisual = createTerminalAgentAvatarVisual({
|
|
74145
75413
|
agentSource,
|
|
74146
|
-
colorDepth,
|
|
74147
|
-
createCanvas:
|
|
75414
|
+
colorDepth: $detectTerminalAnsiColorDepth(),
|
|
75415
|
+
createCanvas: await createOptionalNodeCanvasFactory(),
|
|
74148
75416
|
});
|
|
74149
75417
|
return {
|
|
74150
75418
|
isAnimated: agentVisual.isAnimated,
|
|
@@ -74163,6 +75431,23 @@ async function buildCoderRunAgentVisual(agentSource) {
|
|
|
74163
75431
|
return null;
|
|
74164
75432
|
}
|
|
74165
75433
|
}
|
|
75434
|
+
/**
|
|
75435
|
+
* Loads the optional Node.js canvas factory which rasterizes pixel-based avatar visuals.
|
|
75436
|
+
*
|
|
75437
|
+
* @returns Canvas factory or `undefined` when the optional native module is not installed.
|
|
75438
|
+
*
|
|
75439
|
+
* @private helper of `buildCoderRunAgentVisual`
|
|
75440
|
+
*/
|
|
75441
|
+
async function createOptionalNodeCanvasFactory() {
|
|
75442
|
+
try {
|
|
75443
|
+
// Note: `@napi-rs/canvas` is an optional native module, so it is imported dynamically and lazily
|
|
75444
|
+
const { createCanvas } = await import('@napi-rs/canvas');
|
|
75445
|
+
return (width, height) => createCanvas(width, height);
|
|
75446
|
+
}
|
|
75447
|
+
catch (error) {
|
|
75448
|
+
return undefined;
|
|
75449
|
+
}
|
|
75450
|
+
}
|
|
74166
75451
|
|
|
74167
75452
|
/**
|
|
74168
75453
|
* Creates the one queue prompt used to repair a pre-existing test failure.
|
|
@@ -74999,7 +76284,9 @@ function getPromptColumn(options) {
|
|
|
74999
76284
|
if (section.status === 'not-ready' || isUnwritten) {
|
|
75000
76285
|
return 'backlog';
|
|
75001
76286
|
}
|
|
75002
|
-
|
|
76287
|
+
// Note: A prompt marked `[^]` stays in progress even when no agent is running it, because its
|
|
76288
|
+
// implementation was left in the middle
|
|
76289
|
+
if (isActive || section.status === 'in-progress') {
|
|
75003
76290
|
return 'in-progress';
|
|
75004
76291
|
}
|
|
75005
76292
|
if (!isPromptInPriorityFilter(section, priorityFilter)) {
|
|
@@ -75016,6 +76303,10 @@ function buildPromptTags(options) {
|
|
|
75016
76303
|
if (options.section.status === 'not-ready') {
|
|
75017
76304
|
tags.push({ id: 'not-ready', label: '[-]' });
|
|
75018
76305
|
}
|
|
76306
|
+
// Note: A `[^]` prompt which no agent is running right now was left in the middle of its implementation
|
|
76307
|
+
if (options.section.status === 'in-progress' && !options.isActive) {
|
|
76308
|
+
tags.push({ id: 'left-in-progress', label: '[^]' });
|
|
76309
|
+
}
|
|
75019
76310
|
if (options.isUnwritten) {
|
|
75020
76311
|
tags.push({ id: 'unwritten', label: '@@@' });
|
|
75021
76312
|
}
|
|
@@ -75045,7 +76336,7 @@ function getActivePrompt(uiState) {
|
|
|
75045
76336
|
* Checks whether a parsed section is the prompt currently handled by the active agent.
|
|
75046
76337
|
*/
|
|
75047
76338
|
function isPromptActive(promptFile, section, activePrompt) {
|
|
75048
|
-
if (!activePrompt || section.status !== 'todo') {
|
|
76339
|
+
if (!activePrompt || (section.status !== 'todo' && section.status !== 'in-progress')) {
|
|
75049
76340
|
return false;
|
|
75050
76341
|
}
|
|
75051
76342
|
return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
|
|
@@ -75077,7 +76368,7 @@ function buildCoderServerRunState(uiState) {
|
|
|
75077
76368
|
* Overwrites the body of one prompt section with new content, preserving the status line.
|
|
75078
76369
|
*
|
|
75079
76370
|
* The `newContent` string is the prompt text without the status marker.
|
|
75080
|
-
* The status line (`[ ]`, `[x]`, `[!]`, `[-]`) is kept intact.
|
|
76371
|
+
* The status line (`[ ]`, `[^]`, `[x]`, `[!]`, `[-]`) is kept intact.
|
|
75081
76372
|
*
|
|
75082
76373
|
* @private internal utility of `ptbk coder server`
|
|
75083
76374
|
*/
|
|
@@ -75135,7 +76426,7 @@ async function updatePromptSection(filePath, sectionIndex, newContent) {
|
|
|
75135
76426
|
*
|
|
75136
76427
|
* @private internal constant of `ptbk coder server`
|
|
75137
76428
|
*/
|
|
75138
|
-
const CODER_SERVER_HTML = `<!DOCTYPE html>
|
|
76429
|
+
const CODER_SERVER_HTML = spaceTrim$1(`<!DOCTYPE html>
|
|
75139
76430
|
<html lang="en">
|
|
75140
76431
|
<head>
|
|
75141
76432
|
<meta charset="UTF-8">
|
|
@@ -75358,6 +76649,7 @@ const CODER_SERVER_HTML = `<!DOCTYPE html>
|
|
|
75358
76649
|
line-height: 1.35;
|
|
75359
76650
|
}
|
|
75360
76651
|
.tag-not-ready { background: #e2e8f0; color: #475569; }
|
|
76652
|
+
.tag-left-in-progress { background: #ffe4e6; color: #9f1239; }
|
|
75361
76653
|
.tag-unwritten { background: #fff4cc; color: #8a5a00; }
|
|
75362
76654
|
.tag-implementing { background: #ede9fe; color: #5b21b6; }
|
|
75363
76655
|
.tag-verifying { background: #fae8ff; color: #86198f; }
|
|
@@ -75820,7 +77112,7 @@ const CODER_SERVER_HTML = `<!DOCTYPE html>
|
|
|
75820
77112
|
</script>
|
|
75821
77113
|
</body>
|
|
75822
77114
|
</html>
|
|
75823
|
-
|
|
77115
|
+
`);
|
|
75824
77116
|
// Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/coderServerHtml.ts) should never be published outside of `@promptbook/cli`
|
|
75825
77117
|
// Note: Keep in sync with apps/coder-server/index.html
|
|
75826
77118
|
|
|
@@ -76012,8 +77304,7 @@ function resolveEditablePromptFilePath(filePath, promptsDir) {
|
|
|
76012
77304
|
async function commitPromptEdit(promptFilePath, sectionIndex) {
|
|
76013
77305
|
const relativePromptFilePath = relative(process.cwd(), promptFilePath).replace(/\\/gu, '/');
|
|
76014
77306
|
await commitChanges(`Edit coder prompt ${relativePromptFilePath}#${sectionIndex + 1}`, {
|
|
76015
|
-
|
|
76016
|
-
onlyPaths: [relativePromptFilePath],
|
|
77307
|
+
relevantPaths: [relativePromptFilePath],
|
|
76017
77308
|
});
|
|
76018
77309
|
}
|
|
76019
77310
|
/**
|
|
@@ -76088,6 +77379,33 @@ var runCodexPromptsServer$1 = /*#__PURE__*/Object.freeze({
|
|
|
76088
77379
|
runCodexPromptsServer: runCodexPromptsServer
|
|
76089
77380
|
});
|
|
76090
77381
|
|
|
77382
|
+
/**
|
|
77383
|
+
* Checks whether one prompt section still has open coding work.
|
|
77384
|
+
*
|
|
77385
|
+
* A `[ ]` prompt has not been started at all and a `[^]` prompt was left in the middle of its
|
|
77386
|
+
* implementation, so a file containing either of them is not finished yet.
|
|
77387
|
+
*/
|
|
77388
|
+
function isPromptSectionUnfinished(section) {
|
|
77389
|
+
return section.status === 'todo' || section.status === 'in-progress';
|
|
77390
|
+
}
|
|
77391
|
+
|
|
77392
|
+
/**
|
|
77393
|
+
* Orders the loaded prompt files for one verification pass.
|
|
77394
|
+
*
|
|
77395
|
+
* Note: `$` is used to indicate that this function is not a pure function - the `random` order is not deterministic
|
|
77396
|
+
* Note: This function does NOT mutate the given array
|
|
77397
|
+
*/
|
|
77398
|
+
function $orderPromptFiles(promptFiles, order) {
|
|
77399
|
+
switch (order) {
|
|
77400
|
+
case 'from-earliest':
|
|
77401
|
+
return [...promptFiles];
|
|
77402
|
+
case 'from-latest':
|
|
77403
|
+
return [...promptFiles].reverse();
|
|
77404
|
+
case 'random':
|
|
77405
|
+
return [...$shuffleItems(...promptFiles)];
|
|
77406
|
+
}
|
|
77407
|
+
}
|
|
77408
|
+
|
|
76091
77409
|
/**
|
|
76092
77410
|
* Path to the directory that holds the prompt markdown files.
|
|
76093
77411
|
*/
|
|
@@ -76116,8 +77434,8 @@ const DEFAULT_VERIFY_PROMPTS_OPTIONS = normalizeVerifyPromptsOptions(parseVerify
|
|
|
76116
77434
|
async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
|
|
76117
77435
|
const normalizedOptions = normalizeVerifyPromptsOptions(options);
|
|
76118
77436
|
console.info(colors.cyan.bold('📋 Prompt verification helper'));
|
|
76119
|
-
if (normalizedOptions.
|
|
76120
|
-
console.info(colors.gray(
|
|
77437
|
+
if (normalizedOptions.order !== DEFAULT_VERIFY_PROMPTS_ORDER) {
|
|
77438
|
+
console.info(colors.gray(`Processing files ${VERIFY_PROMPTS_ORDER_DESCRIPTIONS[normalizedOptions.order]}`));
|
|
76121
77439
|
}
|
|
76122
77440
|
if (normalizedOptions.ignore.length > 0) {
|
|
76123
77441
|
console.info(colors.gray(`Ignoring candidates matching: ${normalizedOptions.ignore.join(', ')}`));
|
|
@@ -76131,8 +77449,9 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
|
|
|
76131
77449
|
let promptFiles = initialFiles;
|
|
76132
77450
|
const skippedFiles = new Set();
|
|
76133
77451
|
while (true) {
|
|
76134
|
-
// Note: The git synchronization is applied around each single verification, not once per whole run
|
|
76135
|
-
|
|
77452
|
+
// Note: The git synchronization is applied around each single verification, not once per whole run,
|
|
77453
|
+
// so each verification commits only the prompt file it has archived or repaired
|
|
77454
|
+
const commitScope = await $startCoderGitSync({ gitSync: normalizedOptions.gitSync });
|
|
76136
77455
|
if (normalizedOptions.gitSync.isAutoPullEnabled) {
|
|
76137
77456
|
// Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
|
|
76138
77457
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
@@ -76145,7 +77464,7 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
|
|
|
76145
77464
|
if (outcome.wasSkipped) {
|
|
76146
77465
|
skippedFiles.add(fileWithAllDone.path);
|
|
76147
77466
|
}
|
|
76148
|
-
await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
|
|
77467
|
+
await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
|
|
76149
77468
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
76150
77469
|
continue;
|
|
76151
77470
|
}
|
|
@@ -76156,25 +77475,25 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
|
|
|
76156
77475
|
break;
|
|
76157
77476
|
}
|
|
76158
77477
|
const outcome = await resolvePrompt(nextPrompt);
|
|
76159
|
-
await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
|
|
77478
|
+
await $commitVerificationOutcome(normalizedOptions.gitSync, commitScope, outcome);
|
|
76160
77479
|
promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
|
|
76161
77480
|
}
|
|
76162
77481
|
}
|
|
76163
77482
|
/**
|
|
76164
77483
|
* Commits and pushes one applied verification when the git synchronization is enabled.
|
|
76165
77484
|
*/
|
|
76166
|
-
async function $commitVerificationOutcome(gitSync, outcome) {
|
|
77485
|
+
async function $commitVerificationOutcome(gitSync, commitScope, outcome) {
|
|
76167
77486
|
if (outcome.commitMessage === null) {
|
|
76168
77487
|
return;
|
|
76169
77488
|
}
|
|
76170
|
-
await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
|
|
77489
|
+
await $commitCoderChanges({ gitSync, commitScope, commitMessage: outcome.commitMessage });
|
|
76171
77490
|
}
|
|
76172
77491
|
/**
|
|
76173
77492
|
* Parses supported command-line arguments for the standalone verification script.
|
|
76174
77493
|
*/
|
|
76175
77494
|
function parseVerifyPromptsCliOptions(args) {
|
|
76176
77495
|
return {
|
|
76177
|
-
|
|
77496
|
+
order: parseVerifyPromptsOrder(readStringOption(args, '--order')),
|
|
76178
77497
|
ignore: readRepeatableStringOption(args, '--ignore'),
|
|
76179
77498
|
gitSync: {
|
|
76180
77499
|
isCommitEnabled: args.includes('--commit'),
|
|
@@ -76189,10 +77508,7 @@ function parseVerifyPromptsCliOptions(args) {
|
|
|
76189
77508
|
async function loadPromptFilesForVerification(options) {
|
|
76190
77509
|
const loadedPromptFiles = await loadPromptFiles(PROMPTS_DIR);
|
|
76191
77510
|
const { promptFiles, ignoredPromptFiles } = partitionPromptFilesByIgnore(loadedPromptFiles, options.ignore);
|
|
76192
|
-
|
|
76193
|
-
promptFiles.reverse();
|
|
76194
|
-
}
|
|
76195
|
-
return { promptFiles, ignoredPromptFiles };
|
|
77511
|
+
return { promptFiles: $orderPromptFiles(promptFiles, options.order), ignoredPromptFiles };
|
|
76196
77512
|
}
|
|
76197
77513
|
/**
|
|
76198
77514
|
* Splits prompt files into files that should be verified now and files ignored for this run.
|
|
@@ -76234,7 +77550,7 @@ async function prepareArchiveDirectory() {
|
|
|
76234
77550
|
function normalizeVerifyPromptsOptions(options) {
|
|
76235
77551
|
var _a, _b, _c;
|
|
76236
77552
|
return {
|
|
76237
|
-
|
|
77553
|
+
order: (_a = options.order) !== null && _a !== void 0 ? _a : DEFAULT_VERIFY_PROMPTS_ORDER,
|
|
76238
77554
|
ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
|
|
76239
77555
|
gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
|
|
76240
77556
|
};
|
|
@@ -76259,6 +77575,13 @@ function normalizeIgnoreValues(ignoreValues) {
|
|
|
76259
77575
|
}
|
|
76260
77576
|
return normalizedIgnoreValues;
|
|
76261
77577
|
}
|
|
77578
|
+
/**
|
|
77579
|
+
* Reads one single-value string option from raw CLI arguments, keeping the last occurrence.
|
|
77580
|
+
*/
|
|
77581
|
+
function readStringOption(args, flag) {
|
|
77582
|
+
const values = readRepeatableStringOption(args, flag);
|
|
77583
|
+
return values[values.length - 1];
|
|
77584
|
+
}
|
|
76262
77585
|
/**
|
|
76263
77586
|
* Reads one repeatable string option from raw CLI arguments.
|
|
76264
77587
|
*/
|
|
@@ -76320,8 +77643,9 @@ function displayTopLevelFileList(promptFiles) {
|
|
|
76320
77643
|
for (const file of promptFiles) {
|
|
76321
77644
|
const doneCount = file.sections.filter((section) => section.status === 'done').length;
|
|
76322
77645
|
const todoCount = file.sections.filter((section) => section.status === 'todo').length;
|
|
77646
|
+
const inProgressCount = file.sections.filter((section) => section.status === 'in-progress').length;
|
|
76323
77647
|
const notReadyCount = file.sections.filter((section) => section.status === 'not-ready').length;
|
|
76324
|
-
const needsVerification = file.sections.length > 0 &&
|
|
77648
|
+
const needsVerification = file.sections.length > 0 && !file.sections.some(isPromptSectionUnfinished) && doneCount > 0;
|
|
76325
77649
|
const statusParts = [];
|
|
76326
77650
|
if (needsVerification) {
|
|
76327
77651
|
statusParts.push(colors.cyan.bold(`🔍 ${doneCount} done [x] - NEEDS VERIFICATION`));
|
|
@@ -76330,6 +77654,9 @@ function displayTopLevelFileList(promptFiles) {
|
|
|
76330
77654
|
if (todoCount > 0) {
|
|
76331
77655
|
statusParts.push(colors.yellow(`${todoCount} todo [ ]`));
|
|
76332
77656
|
}
|
|
77657
|
+
if (inProgressCount > 0) {
|
|
77658
|
+
statusParts.push(colors.magenta(`${inProgressCount} in-progress [^]`));
|
|
77659
|
+
}
|
|
76333
77660
|
if (doneCount > 0) {
|
|
76334
77661
|
statusParts.push(colors.green(`${doneCount} done [x]`));
|
|
76335
77662
|
}
|
|
@@ -76345,7 +77672,7 @@ function displayTopLevelFileList(promptFiles) {
|
|
|
76345
77672
|
console.info(colors.gray('Goal: verify all done prompts, then process remaining todo prompts.'));
|
|
76346
77673
|
}
|
|
76347
77674
|
/**
|
|
76348
|
-
* Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ].
|
|
77675
|
+
* Finds the first file where at least one prompt is marked as done [x] and no prompts are todo [ ] or in-progress [^].
|
|
76349
77676
|
* Completely ignores not-ready prompts like [-], [.], [?], etc.
|
|
76350
77677
|
* Also excludes files that have been skipped in this session.
|
|
76351
77678
|
*/
|
|
@@ -76358,10 +77685,10 @@ function findFileWithAllDonePrompts(promptFiles, skippedFiles) {
|
|
|
76358
77685
|
if (skippedFiles.has(file.path)) {
|
|
76359
77686
|
return false;
|
|
76360
77687
|
}
|
|
76361
|
-
// File is ready for verification if it has at least one done prompt and no
|
|
76362
|
-
const
|
|
77688
|
+
// File is ready for verification if it has at least one done prompt and no unfinished prompts
|
|
77689
|
+
const hasUnfinishedPrompts = file.sections.some(isPromptSectionUnfinished);
|
|
76363
77690
|
const hasDonePrompts = file.sections.some((section) => section.status === 'done');
|
|
76364
|
-
return !
|
|
77691
|
+
return !hasUnfinishedPrompts && hasDonePrompts;
|
|
76365
77692
|
});
|
|
76366
77693
|
}
|
|
76367
77694
|
/**
|
|
@@ -76467,11 +77794,11 @@ function displayPromptOverview(promptFiles) {
|
|
|
76467
77794
|
const filesNeedingVerification = promptFiles.filter((file) => {
|
|
76468
77795
|
if (file.sections.length === 0)
|
|
76469
77796
|
return false;
|
|
76470
|
-
const
|
|
77797
|
+
const hasUnfinished = file.sections.some(isPromptSectionUnfinished);
|
|
76471
77798
|
const hasDone = file.sections.some((section) => section.status === 'done');
|
|
76472
|
-
return !
|
|
77799
|
+
return !hasUnfinished && hasDone;
|
|
76473
77800
|
});
|
|
76474
|
-
const pendingFiles = promptFiles.filter((file) => file.sections.some(
|
|
77801
|
+
const pendingFiles = promptFiles.filter((file) => file.sections.some(isPromptSectionUnfinished));
|
|
76475
77802
|
if (filesNeedingVerification.length) {
|
|
76476
77803
|
const formattedNames = formatPendingFileNames(filesNeedingVerification.map((file) => file.name));
|
|
76477
77804
|
console.info(colors.cyan(` 🔍 Files needing verification (${filesNeedingVerification.length}): ${formattedNames}`));
|