@promptbook/cli 0.112.0-126 → 0.112.0-128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/README.md +5 -5
  2. package/apps/agents-server/README.md +3 -0
  3. package/apps/agents-server/playwright.config.ts +1 -0
  4. package/apps/agents-server/public/promptbook-logo-blue.png +0 -0
  5. package/apps/agents-server/public/promptbook-logo-white.png +0 -0
  6. package/apps/agents-server/src/app/[agentName]/layout.tsx +1 -0
  7. package/apps/agents-server/src/app/actions.ts +2 -2
  8. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +68 -36
  9. package/apps/agents-server/src/app/agents/[agentName]/ActiveAgentBreadcrumbBinder.tsx +30 -0
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +2 -0
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistoryPayloadState.ts +38 -4
  12. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistorySyncOperations.ts +4 -1
  13. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +19 -15
  14. package/apps/agents-server/src/app/agents/[agentName]/chat/useCanonicalAgentChatPanelState.ts +58 -7
  15. package/apps/agents-server/src/app/agents/[agentName]/layout.tsx +47 -1
  16. package/apps/agents-server/src/app/api/chat/citation-label/route.ts +133 -0
  17. package/apps/agents-server/src/app/globals.css +78 -0
  18. package/apps/agents-server/src/components/Header/ActiveAgentBreadcrumbContext.tsx +82 -0
  19. package/apps/agents-server/src/components/Header/useHeaderActiveAgent.ts +26 -7
  20. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +35 -32
  21. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoNewAgentWizard.tsx +151 -0
  22. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoOnboardingNavigation.tsx +50 -0
  23. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/BookLanguagePanel.tsx +125 -0
  24. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/DropZone.tsx +83 -0
  25. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/EmailTestRun.tsx +216 -0
  26. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/KnowledgeList.tsx +79 -0
  27. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/Logo.tsx +20 -0
  28. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownBookEditor.tsx +154 -0
  29. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownPreview.tsx +188 -0
  30. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/RailStepper.tsx +115 -0
  31. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/StepFrame.tsx +47 -0
  32. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/TestChat.tsx +159 -0
  33. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/WizardShell.tsx +114 -0
  34. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/brand/RailArtwork.tsx +60 -0
  35. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/BookStep.tsx +135 -0
  36. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/DoneStep.tsx +215 -0
  37. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/KnowledgeStep.tsx +152 -0
  38. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/TestStep.tsx +262 -0
  39. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/ZadaniStep.tsx +64 -0
  40. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Badge.tsx +41 -0
  41. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Banner.tsx +69 -0
  42. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Button.tsx +77 -0
  43. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Card.tsx +33 -0
  44. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Field.tsx +103 -0
  45. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/IconButton.tsx +40 -0
  46. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Spinner.tsx +14 -0
  47. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/tokens.ts +37 -0
  48. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/bookSections.ts +62 -0
  49. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/emailScenarios.ts +22 -0
  50. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/steps.ts +46 -0
  51. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/index.ts +11 -0
  52. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/cn.ts +4 -0
  53. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/format.ts +9 -0
  54. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/id.ts +11 -0
  55. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentEvalService.ts +30 -0
  56. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentTestService.ts +69 -0
  57. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/bookService.ts +21 -0
  58. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoAgentSource.ts +107 -0
  59. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/draftService.ts +45 -0
  60. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/uploadService.ts +27 -0
  61. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/state/OnboardingProvider.tsx +121 -0
  62. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/types.ts +52 -0
  63. package/apps/agents-server/src/components/NewAgentDialog/createNewAgentWizardSource.ts +1 -1
  64. package/apps/agents-server/src/components/NewAgentDialog/trackNewAgentCreationEvent.ts +1 -1
  65. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentDialog.tsx +76 -0
  66. package/apps/agents-server/src/constants/newAgentWizard.ts +17 -8
  67. package/apps/agents-server/src/database/getMetadata.ts +37 -1
  68. package/apps/agents-server/src/database/metadataDefaults.ts +18 -7
  69. package/apps/agents-server/src/database/migrations/2026-06-2200-new-agent-wizard-metadata-key.sql +27 -0
  70. package/apps/agents-server/src/database/migrations/2026-06-2201-new-agent-wizard-mango-default.sql +11 -0
  71. package/apps/agents-server/src/generated/reservedPaths.ts +2 -0
  72. package/apps/agents-server/src/languages/translations/english.yaml +1 -1
  73. package/apps/agents-server/src/tools/createChatAttachmentToolFunctions.ts +8 -4
  74. package/apps/agents-server/src/utils/chat/resolveAgentsServerCitationLabel.ts +130 -0
  75. package/apps/agents-server/src/utils/chat/resolveCitationSourceLabel.ts +436 -0
  76. package/apps/agents-server/src/utils/knowledge/resolveWebsiteKnowledgeSourcesForServer.ts +10 -1
  77. package/apps/agents-server/src/utils/session.ts +62 -5
  78. package/apps/agents-server/src/utils/transpilers/resolveTranspiledTeamExport.ts +10 -10
  79. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +198 -63
  80. package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -0
  81. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +81 -8
  82. package/esm/index.es.js +1052 -455
  83. package/esm/index.es.js.map +1 -1
  84. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  85. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  86. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  87. package/esm/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  88. package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  89. package/esm/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  90. package/esm/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  91. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  92. package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  93. package/esm/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  94. package/esm/src/_packages/components.index.d.ts +2 -0
  95. package/esm/src/_packages/types.index.d.ts +2 -0
  96. package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  97. package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  98. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  99. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  100. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  101. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  102. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  103. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  104. package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  105. package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  106. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  107. package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  108. package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  109. package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  110. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  111. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  112. package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  113. package/esm/src/version.d.ts +1 -1
  114. package/package.json +1 -1
  115. package/src/_packages/components.index.ts +2 -0
  116. package/src/_packages/types.index.ts +2 -0
  117. package/src/avatars/avatarAnimationScheduler.ts +61 -20
  118. package/src/avatars/visuals/octopus3d2AvatarVisual.ts +106 -21
  119. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +131 -28
  120. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +18 -2
  121. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +7 -1
  122. package/src/book-components/Chat/Chat/Chat.tsx +4 -0
  123. package/src/book-components/Chat/Chat/ChatCitationModal.tsx +17 -3
  124. package/src/book-components/Chat/Chat/ChatMessageItem.tsx +22 -2
  125. package/src/book-components/Chat/Chat/ChatMessageList.tsx +6 -0
  126. package/src/book-components/Chat/Chat/ChatProps.tsx +6 -0
  127. package/src/book-components/Chat/Chat/ChatToolCallModal.tsx +4 -0
  128. package/src/book-components/Chat/Chat/ChatToolCallModalContent.tsx +4 -0
  129. package/src/book-components/Chat/Chat/TeamToolCallModalContent.tsx +16 -3
  130. package/src/book-components/Chat/Chat/useChatPostprocessedMessages.ts +13 -2
  131. package/src/book-components/Chat/SourceChip/SourceChip.module.css +8 -0
  132. package/src/book-components/Chat/SourceChip/SourceChip.tsx +19 -5
  133. package/src/book-components/Chat/hooks/useResolvedCitationLabel.ts +65 -0
  134. package/src/book-components/Chat/save/html/htmlSaveFormatDefinition.ts +315 -88
  135. package/src/book-components/Chat/save/react/reactSaveFormatDefinition.ts +7 -1
  136. package/src/book-components/Chat/types/ChatMessage.ts +5 -0
  137. package/src/book-components/Chat/types/ChatParticipant.ts +1 -1
  138. package/src/book-components/Chat/types/CitationLabelResolver.ts +9 -0
  139. package/src/book-components/Chat/utils/citationHelpers.ts +90 -2
  140. package/src/book-components/Chat/utils/createCitationFootnoteRenderModel.ts +3 -0
  141. package/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.ts +178 -0
  142. package/src/book-components/Chat/utils/parseCitationsFromContent.ts +6 -0
  143. package/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.ts +13 -2
  144. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  145. package/src/cli/cli-commands/coder/run.ts +17 -20
  146. package/src/cli/cli-commands/coder/server.ts +13 -16
  147. package/src/cli/common/$deprecateCliCommand.ts +10 -1
  148. package/src/commitments/GOAL/GOAL.ts +7 -2
  149. package/src/commitments/LANGUAGE/LANGUAGE.ts +7 -2
  150. package/src/commitments/TEAM/TEAM.ts +12 -1
  151. package/src/commitments/USE_PROJECT/createUseProjectToolFunctions.ts +8 -1
  152. package/src/import-plugins/JsonFileImportPlugin.ts +15 -2
  153. package/src/import-plugins/TextFileImportPlugin.ts +8 -1
  154. package/src/llm-providers/_common/register/$registeredLlmToolsMessage.ts +6 -1
  155. package/src/other/templates/getTemplatesPipelineCollection.ts +713 -785
  156. package/src/utils/chat/chatAttachments/resolveChatAttachmentContent.ts +8 -1
  157. package/src/version.ts +2 -2
  158. package/src/versions.txt +1 -0
  159. package/umd/index.umd.js +1055 -458
  160. package/umd/index.umd.js.map +1 -1
  161. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  162. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  163. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  164. package/umd/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  165. package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  166. package/umd/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  167. package/umd/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  168. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  169. package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  170. package/umd/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  171. package/umd/src/_packages/components.index.d.ts +2 -0
  172. package/umd/src/_packages/types.index.d.ts +2 -0
  173. package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  174. package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  175. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  176. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  177. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  178. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  179. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  180. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  181. package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  182. package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  183. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  184. package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  185. package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  186. package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  187. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  188. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  189. package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  190. package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('child_process'), require('crypto'), require('fs'), require('dotenv'), require('moment'), require('waitasecond'), require('prompts'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('socket.io-client'), require('jszip'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('rxjs'), require('@openai/agents'), require('openai'), require('typescript'), require('ignore'), require('readline'), require('mime-types'), require('papaparse'), require('url'), require('events'), require('pg'), require('@supabase/supabase-js')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'child_process', 'crypto', 'fs', 'dotenv', 'moment', 'waitasecond', 'prompts', 'crypto-js/enc-hex', 'crypto-js/sha256', 'socket.io-client', 'jszip', '@mozilla/readability', 'jsdom', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'rxjs', '@openai/agents', 'openai', 'typescript', 'ignore', 'readline', 'mime-types', 'papaparse', 'url', 'events', 'pg', '@supabase/supabase-js'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.child_process, global.crypto, global.fs, global.dotenv, global.moment, global.waitasecond, global.prompts, global.hexEncoder, global.sha256, global.socket_ioClient, global.JSZip, global.readability, global.jsdom, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.rxjs, global.agents, global.OpenAI, global.ts, global.ignore, global.readline, global.mimeTypes, global.papaparse, global.url, global.events, global.pg));
5
- })(this, (function (exports, colors, commander, _spaceTrim, promises, path, child_process, crypto, fs, dotenv, moment, waitasecond, prompts, hexEncoder, sha256, socket_ioClient, JSZip, readability, jsdom, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Anthropic, Bottleneck, openai, rxjs, agents, OpenAI, ts, ignore, readline, mimeTypes, papaparse, url, events, pg) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('commander'), require('spacetrim'), require('fs/promises'), require('path'), require('child_process'), require('crypto'), require('fs'), require('dotenv'), require('moment'), require('waitasecond'), require('prompts'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('socket.io-client'), require('jszip'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('showdown'), require('glob-promise'), require('http'), require('express'), require('socket.io'), require('express-openapi-validator'), require('swagger-ui-express'), require('react'), require('react-dom/server'), require('@anthropic-ai/sdk'), require('bottleneck'), require('@azure/openai'), require('rxjs'), require('@openai/agents'), require('openai'), require('typescript'), require('ignore'), require('readline'), require('events'), require('mime-types'), require('papaparse'), require('url'), require('pg'), require('@supabase/supabase-js')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'colors', 'commander', 'spacetrim', 'fs/promises', 'path', 'child_process', 'crypto', 'fs', 'dotenv', 'moment', 'waitasecond', 'prompts', 'crypto-js/enc-hex', 'crypto-js/sha256', 'socket.io-client', 'jszip', '@mozilla/readability', 'jsdom', 'crypto-js', 'showdown', 'glob-promise', 'http', 'express', 'socket.io', 'express-openapi-validator', 'swagger-ui-express', 'react', 'react-dom/server', '@anthropic-ai/sdk', 'bottleneck', '@azure/openai', 'rxjs', '@openai/agents', 'openai', 'typescript', 'ignore', 'readline', 'events', 'mime-types', 'papaparse', 'url', 'pg', '@supabase/supabase-js'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-cli"] = {}, global.colors, global.commander, global._spaceTrim, global.promises, global.path, global.child_process, global.crypto, global.fs, global.dotenv, global.moment, global.waitasecond, global.prompts, global.hexEncoder, global.sha256, global.socket_ioClient, global.JSZip, global.readability, global.jsdom, global.CryptoJS, global.showdown, global.glob, global.http, global.express, global.socket_io, global.OpenApiValidator, global.swaggerUi, global.react, global.server, global.Anthropic, global.Bottleneck, global.openai, global.rxjs, global.agents, global.OpenAI, global.ts, global.ignore, global.readline, global.events, global.mimeTypes, global.papaparse, global.url, global.pg));
5
+ })(this, (function (exports, colors, commander, _spaceTrim, promises, path, child_process, crypto, fs, dotenv, moment, waitasecond, prompts, hexEncoder, sha256, socket_ioClient, JSZip, readability, jsdom, CryptoJS, showdown, glob, http, express, socket_io, OpenApiValidator, swaggerUi, react, server, Anthropic, Bottleneck, openai, rxjs, agents, OpenAI, ts, ignore, readline, events, mimeTypes, papaparse, url, pg) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -63,7 +63,7 @@
63
63
  * @generated
64
64
  * @see https://github.com/webgptorg/promptbook
65
65
  */
66
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-126';
66
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-128';
67
67
  /**
68
68
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
69
69
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2697,8 +2697,16 @@
2697
2697
  * Builds a `.env` block containing missing Agents Server variables and documentation links.
2698
2698
  */
2699
2699
  function buildMissingEnvVariablesBlock$1(variables) {
2700
- const variableBlocks = variables.map(({ documentationUrl, name, value }) => [AGENTS_SERVER_ENV_CREATED_COMMENT, `# Documentation: ${documentationUrl}`, `${name}=${value}`].join('\n'));
2701
- return ['# Promptbook Agents Server', ...variableBlocks].join('\n\n');
2700
+ const variableBlocks = variables.map(({ documentationUrl, name, value }) => _spaceTrim.spaceTrim(`
2701
+ ${AGENTS_SERVER_ENV_CREATED_COMMENT}
2702
+ # Documentation: ${documentationUrl}
2703
+ ${name}=${value}
2704
+ `));
2705
+ return _spaceTrim.spaceTrim((block) => `
2706
+ # Promptbook Agents Server
2707
+
2708
+ ${block(variableBlocks.join('\n\n'))}
2709
+ `);
2702
2710
  }
2703
2711
  // Note: [🟡] Code for Agents Server environment bootstrapping [ensureAgentsServerEnvFile](src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.ts) should never be published outside of `@promptbook/cli`
2704
2712
 
@@ -4270,6 +4278,13 @@
4270
4278
  },
4271
4279
  ];
4272
4280
  }
4281
+ /**
4282
+ * Builds one OSC 8 terminal hyperlink to an HTTP URL.
4283
+ */
4284
+ function buildTerminalUrlLink(url, availableWidth) {
4285
+ const displayUrl = fitPlainText(url, availableWidth);
4286
+ return `\u001B]8;;${url}\u0007${displayUrl}\u001B]8;;\u0007`;
4287
+ }
4273
4288
  /**
4274
4289
  * Builds the colored phase badge shown in the session box.
4275
4290
  */
@@ -4460,6 +4475,9 @@
4460
4475
  }
4461
4476
  const configurationRows = [
4462
4477
  ...buildOptionalSessionRow('Context', options.config.context),
4478
+ ...buildOptionalSessionRow('Server', options.config.serverUrl
4479
+ ? buildTerminalUrlLink(options.config.serverUrl, bodyWidth - SESSION_LABEL_WIDTH - 1)
4480
+ : undefined),
4463
4481
  ...buildOptionalSessionRow('Test', options.config.testCommand),
4464
4482
  ];
4465
4483
  return [
@@ -4902,7 +4920,8 @@
4902
4920
  * @private internal entry point of coder run UI
4903
4921
  */
4904
4922
  function renderCoderRunUi(startTime, options = {}) {
4905
- const state = new CoderRunUiState(startTime);
4923
+ var _a;
4924
+ const state = (_a = options.state) !== null && _a !== void 0 ? _a : new CoderRunUiState(startTime);
4906
4925
  const buildFrameLinesFromState = options.buildFrameLines || buildCoderRunUiFrame;
4907
4926
  if (!process.stdout.isTTY) {
4908
4927
  return {
@@ -7881,8 +7900,11 @@
7881
7900
  if (!trimmedContent) {
7882
7901
  return requirements;
7883
7902
  }
7884
- // Add goal as a proper h2 section to the system message
7885
- const goalSection = `## Goal\n\n${trimmedContent}`;
7903
+ const goalSection = _spaceTrim.spaceTrim((block) => `
7904
+ ## Goal
7905
+
7906
+ ${block(trimmedContent)}
7907
+ `);
7886
7908
  const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
7887
7909
  return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
7888
7910
  }
@@ -8404,8 +8426,11 @@
8404
8426
  if (!trimmedContent) {
8405
8427
  return requirements;
8406
8428
  }
8407
- // Add language as a bullet under a ## Language section
8408
- const languageSection = `## Language\n\n- Your language is ${trimmedContent}`;
8429
+ const languageSection = _spaceTrim.spaceTrim((block) => `
8430
+ ## Language
8431
+
8432
+ - Your language is ${block(trimmedContent)}
8433
+ `);
8409
8434
  return this.appendToSystemMessage(requirements, languageSection, '\n\n');
8410
8435
  }
8411
8436
  }
@@ -13370,41 +13395,91 @@
13370
13395
  context.fill();
13371
13396
  context.restore();
13372
13397
  }
13398
+ /**
13399
+ * Number of latitude segments used by the single blobby octopus mesh.
13400
+ *
13401
+ * @private helper of `octopus3d2AvatarVisual`
13402
+ */
13403
+ const LATITUDE_PATCH_COUNT$1 = 12;
13404
+ /**
13405
+ * Number of longitude segments used by the single blobby octopus mesh.
13406
+ *
13407
+ * @private helper of `octopus3d2AvatarVisual`
13408
+ */
13409
+ const LONGITUDE_PATCH_COUNT$1 = 24;
13373
13410
  /**
13374
13411
  * Resolves all visible projected patches for the single blobby octopus mesh.
13375
13412
  *
13413
+ * Within a single frame, mesh corner samples and longitude-only lobe-wave values are
13414
+ * quantized to the patch grid and computed once each rather than re-evaluated for every
13415
+ * patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
13416
+ * `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
13417
+ * corners are shared between neighboring patches.
13418
+ *
13376
13419
  * @private helper of `octopus3d2AvatarVisual`
13377
13420
  */
13378
13421
  function resolveVisibleBlobbyOctopusPatches(options) {
13379
13422
  const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
13380
- const latitudePatchCount = 12;
13381
- const longitudePatchCount = 24;
13423
+ const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
13424
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
13382
13425
  const surfacePatches = [];
13426
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
13427
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
13428
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
13429
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
13430
+ }
13431
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
13432
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
13433
+ }
13434
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
13435
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
13436
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
13437
+ }
13438
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
13439
+ const transformedCornerSamples = new Array(cornerCount);
13440
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
13441
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
13442
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
13443
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
13444
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
13445
+ const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
13446
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
13447
+ }
13448
+ }
13383
13449
  for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
13384
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
13385
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
13450
+ const startLatitude = latitudeBoundaries[latitudeIndex];
13451
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
13386
13452
  const centerLatitude = (startLatitude + endLatitude) / 2;
13387
13453
  const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
13454
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
13455
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
13388
13456
  for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
13389
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
13390
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
13457
+ const startLongitude = longitudeBoundaries[longitudeIndex];
13458
+ const endLongitude = longitudeBoundaries[longitudeIndex + 1];
13391
13459
  const centerLongitude = (startLongitude + endLongitude) / 2;
13392
- const localCorners = [
13393
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, startLongitude),
13394
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, endLongitude),
13395
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, endLongitude),
13396
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, startLongitude),
13460
+ const transformedCorners = [
13461
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
13462
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
13463
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
13464
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
13397
13465
  ];
13398
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
13399
13466
  const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
13400
13467
  if (surfaceNormal.z <= 0.01) {
13401
13468
  continue;
13402
13469
  }
13403
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
13470
+ const projectedCorners = [
13471
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
13472
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
13473
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
13474
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
13475
+ ];
13404
13476
  surfacePatches.push({
13405
13477
  corners: projectedCorners,
13406
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
13407
- transformedCorners.length,
13478
+ averageDepth: (transformedCorners[0].z +
13479
+ transformedCorners[1].z +
13480
+ transformedCorners[2].z +
13481
+ transformedCorners[3].z) /
13482
+ 4,
13408
13483
  lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
13409
13484
  fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
13410
13485
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
@@ -13422,12 +13497,21 @@
13422
13497
  * @private helper of `octopus3d2AvatarVisual`
13423
13498
  */
13424
13499
  function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
13500
+ const { morphologyProfile, animationPhase, timeMs } = options;
13501
+ return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
13502
+ }
13503
+ /**
13504
+ * Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
13505
+ * to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
13506
+ *
13507
+ * @private helper of `octopus3d2AvatarVisual`
13508
+ */
13509
+ function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
13425
13510
  const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
13426
13511
  const cosineLatitude = Math.max(0, Math.cos(latitude));
13427
13512
  const verticalProgress = (Math.sin(latitude) + 1) / 2;
13428
13513
  const upperBlend = Math.pow(1 - verticalProgress, 1.2);
13429
13514
  const lowerBlend = Math.pow(verticalProgress, 1.42);
13430
- const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
13431
13515
  const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
13432
13516
  const horizontalScale = 1.02 +
13433
13517
  skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
@@ -13724,45 +13808,102 @@
13724
13808
  context.fill();
13725
13809
  context.restore();
13726
13810
  }
13811
+ /**
13812
+ * Number of latitude segments used by the continuous Octopus 3D 3 mesh.
13813
+ *
13814
+ * @private helper of `octopus3d3AvatarVisual`
13815
+ */
13816
+ const LATITUDE_PATCH_COUNT = 16;
13817
+ /**
13818
+ * Number of longitude segments used by the continuous Octopus 3D 3 mesh.
13819
+ *
13820
+ * @private helper of `octopus3d3AvatarVisual`
13821
+ */
13822
+ const LONGITUDE_PATCH_COUNT = 40;
13727
13823
  /**
13728
13824
  * Resolves visible projected patches for the continuous octopus mesh.
13729
13825
  *
13826
+ * Within a single frame, mesh corner samples and longitude-only computations (tentacle
13827
+ * influence and lobe wave) are quantized to the patch grid and computed once each rather
13828
+ * than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
13829
+ * for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
13830
+ * triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
13831
+ *
13730
13832
  * @private helper of `octopus3d3AvatarVisual`
13731
13833
  */
13732
13834
  function resolveVisibleContinuousOctopusPatches(options) {
13733
13835
  const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
13734
- const latitudePatchCount = 16;
13735
- const longitudePatchCount = 40;
13836
+ const latitudePatchCount = LATITUDE_PATCH_COUNT;
13837
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT;
13736
13838
  const surfacePatches = [];
13839
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
13840
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
13841
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
13842
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
13843
+ }
13844
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
13845
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
13846
+ }
13847
+ const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
13848
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
13849
+ const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
13850
+ const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
13851
+ const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
13852
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
13853
+ const cornerLongitude = longitudeBoundaries[boundaryIndex];
13854
+ cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
13855
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
13856
+ }
13857
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
13858
+ const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
13859
+ cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
13860
+ cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
13861
+ cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
13862
+ }
13863
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
13864
+ const transformedCornerSamples = new Array(cornerCount);
13865
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
13866
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
13867
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
13868
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
13869
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
13870
+ const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
13871
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
13872
+ }
13873
+ }
13737
13874
  for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
13738
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
13739
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
13875
+ const startLatitude = latitudeBoundaries[latitudeIndex];
13876
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
13740
13877
  const centerLatitude = (startLatitude + endLatitude) / 2;
13741
13878
  const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
13879
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
13880
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
13742
13881
  for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
13743
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
13744
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
13745
- const centerLongitude = (startLongitude + endLongitude) / 2;
13746
- const localCorners = [
13747
- sampleContinuousOctopusSurfacePoint(options, startLatitude, startLongitude),
13748
- sampleContinuousOctopusSurfacePoint(options, startLatitude, endLongitude),
13749
- sampleContinuousOctopusSurfacePoint(options, endLatitude, endLongitude),
13750
- sampleContinuousOctopusSurfacePoint(options, endLatitude, startLongitude),
13882
+ const transformedCorners = [
13883
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
13884
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
13885
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
13886
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
13751
13887
  ];
13752
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
13753
13888
  const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
13754
13889
  if (surfaceNormal.z <= 0.008) {
13755
13890
  continue;
13756
13891
  }
13757
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
13758
- const tentacleInfluence = resolveContinuousTentacleInfluence(options, centerLongitude);
13759
- const lowerLobeWave = resolveContinuousLobeWave(options, centerLongitude);
13892
+ const projectedCorners = [
13893
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
13894
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
13895
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
13896
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
13897
+ ];
13760
13898
  surfacePatches.push({
13761
13899
  corners: projectedCorners,
13762
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
13763
- transformedCorners.length,
13900
+ averageDepth: (transformedCorners[0].z +
13901
+ transformedCorners[1].z +
13902
+ transformedCorners[2].z +
13903
+ transformedCorners[3].z) /
13904
+ 4,
13764
13905
  lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
13765
- fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), tentacleInfluence.core, lowerLobeWave),
13906
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
13766
13907
  outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
13767
13908
  });
13768
13909
  }
@@ -13778,16 +13919,27 @@
13778
13919
  * @private helper of `octopus3d3AvatarVisual`
13779
13920
  */
13780
13921
  function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
13922
+ return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
13923
+ }
13924
+ /**
13925
+ * Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
13926
+ * values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
13927
+ *
13928
+ * The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
13929
+ * corner grid, so the same longitude is reused across every latitude row and each
13930
+ * tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
13931
+ *
13932
+ * @private helper of `octopus3d3AvatarVisual`
13933
+ */
13934
+ function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
13781
13935
  const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
13782
13936
  const cosineLatitude = Math.max(0, Math.cos(latitude));
13783
13937
  const verticalProgress = (Math.sin(latitude) + 1) / 2;
13784
13938
  const upperBlend = Math.pow(1 - verticalProgress, 1.28);
13785
13939
  const lowerBlend = smoothStep(0.38, 1, verticalProgress);
13786
13940
  const tipBlend = smoothStep(0.68, 1, verticalProgress);
13787
- const tentacleInfluence = resolveContinuousTentacleInfluence(options, longitude);
13788
13941
  const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
13789
13942
  const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
13790
- const lowerLobeWave = resolveContinuousLobeWave(options, longitude);
13791
13943
  const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
13792
13944
  animationPhase * 0.6 +
13793
13945
  timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
@@ -17192,7 +17344,15 @@
17192
17344
  * Builds the teammate request text, optionally including context.
17193
17345
  */
17194
17346
  function buildTeammateRequest(message, context) {
17195
- return context ? `${message}\n\nContext:\n${context}` : message;
17347
+ if (!context) {
17348
+ return message;
17349
+ }
17350
+ return _spaceTrim.spaceTrim((block) => `
17351
+ ${block(message)}
17352
+
17353
+ Context:
17354
+ ${block(context)}
17355
+ `);
17196
17356
  }
17197
17357
  /**
17198
17358
  * Builds a minimal chat prompt for teammate calls.
@@ -21215,7 +21375,11 @@
21215
21375
  const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
21216
21376
  const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
21217
21377
  const contentToReturn = wasCharacterTruncated
21218
- ? `${lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS)}\n\n[...truncated...]`
21378
+ ? _spaceTrim.spaceTrim((block) => `
21379
+ ${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
21380
+
21381
+ [...truncated...]
21382
+ `)
21219
21383
  : lineRangedContent;
21220
21384
  const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
21221
21385
  if (wasCharacterTruncated) {
@@ -26090,7 +26254,8 @@
26090
26254
  /**
26091
26255
  * Commits staged changes with the provided message using the dedicated coding-agent identity when configured,
26092
26256
  * otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`,
26093
- * `options.includePaths` can restrict staging, and `options.excludePaths` can keep temporary artifacts out of the created commit.
26257
+ * `options.includePaths` can restrict staging, `options.onlyPaths` can restrict the commit pathspec,
26258
+ * and `options.excludePaths` can keep temporary artifacts out of the created commit.
26094
26259
  */
26095
26260
  async function commitChanges(message, options) {
26096
26261
  const projectPath = (options === null || options === void 0 ? void 0 : options.projectPath) || process.cwd();
@@ -26102,7 +26267,7 @@
26102
26267
  const signingFlag = buildAgentGitSigningFlag();
26103
26268
  await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, options === null || options === void 0 ? void 0 : options.excludePaths);
26104
26269
  await runGitCommand({
26105
- command: buildGitCommitCommand(commitMessagePath, signingFlag),
26270
+ command: buildGitCommitCommand(commitMessagePath, signingFlag, options === null || options === void 0 ? void 0 : options.onlyPaths),
26106
26271
  cwd: projectPath,
26107
26272
  env: agentEnv,
26108
26273
  });
@@ -26282,12 +26447,15 @@
26282
26447
  /**
26283
26448
  * Builds the `git commit` command with an optional signing flag.
26284
26449
  */
26285
- function buildGitCommitCommand(commitMessagePath, signingFlag) {
26450
+ function buildGitCommitCommand(commitMessagePath, signingFlag, onlyPaths) {
26286
26451
  const commandParts = ['git commit'];
26287
26452
  if (signingFlag) {
26288
26453
  commandParts.push(signingFlag);
26289
26454
  }
26290
26455
  commandParts.push(`--file "${commitMessagePath}"`);
26456
+ if (onlyPaths && onlyPaths.length > 0) {
26457
+ commandParts.push('--', ...onlyPaths.map(quoteShellPath$1));
26458
+ }
26291
26459
  return commandParts.join(' ');
26292
26460
  }
26293
26461
  /**
@@ -27785,7 +27953,11 @@
27785
27953
  if (normalized.length <= OUTPUT_SNIPPET_MAX_CHARS) {
27786
27954
  return normalized;
27787
27955
  }
27788
- return `${normalized.slice(0, OUTPUT_SNIPPET_MAX_CHARS)}\n\n...[truncated]`;
27956
+ return _spaceTrim.spaceTrim((block) => `
27957
+ ${block(normalized.slice(0, OUTPUT_SNIPPET_MAX_CHARS))}
27958
+
27959
+ ...[truncated]
27960
+ `);
27789
27961
  }
27790
27962
  /**
27791
27963
  * Builds one command failure message that keeps a useful snippet of CLI output.
@@ -27795,7 +27967,11 @@
27795
27967
  if (!outputSnippet) {
27796
27968
  return `Command "bash ${scriptPathPosix}" exited with code ${code !== null && code !== void 0 ? code : 'unknown'}`;
27797
27969
  }
27798
- return `Command "bash ${scriptPathPosix}" exited with code ${code !== null && code !== void 0 ? code : 'unknown'}\n\n${outputSnippet}`;
27970
+ return _spaceTrim.spaceTrim((block) => `
27971
+ Command "bash ${scriptPathPosix}" exited with code ${code !== null && code !== void 0 ? code : 'unknown'}
27972
+
27973
+ ${block(outputSnippet)}
27974
+ `);
27799
27975
  }
27800
27976
  /**
27801
27977
  * Runs a script until a completion marker is observed and output is idle for a set timeout.
@@ -27912,7 +28088,9 @@
27912
28088
  * Handles process exit and resolves or rejects accordingly.
27913
28089
  */
27914
28090
  const handleExit = (code) => {
27915
- const failure = code === 0 || markerSeen ? undefined : new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
28091
+ const failure = code === 0 || markerSeen
28092
+ ? undefined
28093
+ : new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
27916
28094
  const status = failure ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}` : 'succeeded';
27917
28095
  settleWithLog(status, () => {
27918
28096
  if (!failure) {
@@ -27930,8 +28108,14 @@
27930
28108
  });
27931
28109
  commandProcess.on('error', (error) => {
27932
28110
  const outputSnippet = createOutputSnippet(fullOutput);
27933
- const details = outputSnippet ? `\n\n${outputSnippet}` : '';
27934
- const failure = new Error(`Command "bash ${scriptPathPosix}" failed: ${error.message}${details}`);
28111
+ const failureMessage = outputSnippet
28112
+ ? _spaceTrim.spaceTrim((block) => `
28113
+ Command "bash ${scriptPathPosix}" failed: ${error.message}
28114
+
28115
+ ${block(outputSnippet)}
28116
+ `)
28117
+ : `Command "bash ${scriptPathPosix}" failed: ${error.message}`;
28118
+ const failure = new Error(failureMessage);
27935
28119
  settleWithLog('failed before completion', () => reject(failure), failure);
27936
28120
  });
27937
28121
  });
@@ -28270,7 +28454,11 @@
28270
28454
  if (normalized.length <= maxChars) {
28271
28455
  return normalized;
28272
28456
  }
28273
- return `${normalized.slice(0, maxChars)}\n\n...[truncated]`;
28457
+ return spaceTrim((block) => `
28458
+ ${block(normalized.slice(0, maxChars))}
28459
+
28460
+ ...[truncated]
28461
+ `);
28274
28462
  }
28275
28463
  /**
28276
28464
  * Returns true if at least one regex matches the provided text.
@@ -34919,11 +35107,19 @@
34919
35107
  try {
34920
35108
  const json = JSON.parse(content);
34921
35109
  const formattedJson = JSON.stringify(json, null, 4);
34922
- return `\`\`\`json\n${formattedJson}\n\`\`\``;
35110
+ return _spaceTrim.spaceTrim((block) => `
35111
+ \`\`\`json
35112
+ ${block(formattedJson)}
35113
+ \`\`\`
35114
+ `);
34923
35115
  }
34924
35116
  catch (error) {
34925
35117
  // If JSON is invalid, still import it but maybe not as pretty JSON
34926
- return `\`\`\`json\n${content}\n\`\`\``;
35118
+ return _spaceTrim.spaceTrim((block) => `
35119
+ \`\`\`json
35120
+ ${block(content)}
35121
+ \`\`\`
35122
+ `);
34927
35123
  }
34928
35124
  },
34929
35125
  };
@@ -34947,7 +35143,11 @@
34947
35143
  import(content, mimeType) {
34948
35144
  const extension = mimeTypeToExtension(mimeType);
34949
35145
  const codeBlockType = extension || 'txt';
34950
- return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
35146
+ return _spaceTrim.spaceTrim((block) => `
35147
+ \`\`\`${codeBlockType}
35148
+ ${block(content)}
35149
+ \`\`\`
35150
+ `);
34951
35151
  },
34952
35152
  };
34953
35153
 
@@ -35194,11 +35394,19 @@
35194
35394
  const examples = [];
35195
35395
  const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
35196
35396
  if (initialMessage) {
35197
- examples.push(`**Agent:**\n${initialMessage}`);
35397
+ examples.push(_spaceTrim.spaceTrim((block) => `
35398
+ **Agent:**
35399
+ ${block(initialMessage)}
35400
+ `));
35198
35401
  }
35199
35402
  if (samples && samples.length > 0) {
35200
35403
  for (const sample of samples) {
35201
- examples.push(`**User:** ${sample.question}\n\n**Agent:**\n${sample.answer}`);
35404
+ examples.push(_spaceTrim.spaceTrim((block) => `
35405
+ **User:** ${block(String(sample.question))}
35406
+
35407
+ **Agent:**
35408
+ ${block(sample.answer)}
35409
+ `));
35202
35410
  }
35203
35411
  }
35204
35412
  return examples;
@@ -35633,10 +35841,14 @@
35633
35841
  * Formats prepared model requirements for a text-only CLI harness prompt.
35634
35842
  */
35635
35843
  function formatAgentModelRequirementsForRunner(modelRequirements) {
35844
+ const promptSuffix = modelRequirements.promptSuffix.trim();
35636
35845
  return [
35637
35846
  modelRequirements.systemMessage.trim(),
35638
- modelRequirements.promptSuffix.trim()
35639
- ? `## Prompt suffix\n${modelRequirements.promptSuffix.trim()}`
35847
+ promptSuffix
35848
+ ? _spaceTrim.spaceTrim((block) => `
35849
+ ## Prompt suffix
35850
+ ${block(promptSuffix)}
35851
+ `)
35640
35852
  : '',
35641
35853
  ]
35642
35854
  .filter(Boolean)
@@ -39289,7 +39501,7 @@
39289
39501
  */
39290
39502
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
39291
39503
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
39292
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.4 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md --no-wait',
39504
+ 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.4 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
39293
39505
  // 'coder:find-refactor-candidates': 'ptbk coder find-refactor-candidates',
39294
39506
  'coder:verify': 'ptbk coder verify',
39295
39507
  };
@@ -39748,33 +39960,26 @@
39748
39960
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
39749
39961
  addPromptRunnerExecutionOptions(command);
39750
39962
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption$1, 0);
39751
- command.option('--wait [duration]', _spaceTrim.spaceTrim(`
39752
- Wait between prompt rounds.
39753
- Without a value (default): waits for user confirmation before each prompt (interactive mode).
39754
- With a duration like 1h, 30m, 5s: waits that long between prompts to avoid hitting rate limits of the harness.
39755
- `), true);
39756
- // Note: --no-wait disables the default interactive wait-for-user behaviour
39757
- command.option('--no-wait', 'Skip all waiting between prompts and run non-interactively');
39963
+ command.option('--wait <duration>', _spaceTrim.spaceTrim(`
39964
+ Wait this long between prompt rounds to avoid hitting rate limits of the harness.
39965
+ Accepts durations like 1h, 30m, 5s.
39966
+ `));
39967
+ // Note: --no-auto disables the default auto behaviour and waits for user confirmation before each prompt
39968
+ command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
39758
39969
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
39759
39970
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
39760
39971
  command.action(handleActionErrors(async (cliOptions) => {
39761
- const { dryRun, agent, context, test, preserveLogs, priority, wait, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39972
+ const { dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39762
39973
  const testCommand = normalizeCommandOptionValue$1(test);
39763
39974
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
39764
39975
  isAgentRequired: !dryRun,
39765
39976
  });
39766
- // [1] Parse the --wait option:
39767
- // true (default or --wait without value): wait for user confirmation
39768
- // false (--no-wait): no waiting at all
39769
- // string (--wait 1h): wait that long between prompt rounds
39770
- let waitForUser = false;
39771
- let waitBetweenPrompts = 0;
39772
- if (wait === true) {
39773
- waitForUser = true;
39774
- }
39775
- else if (typeof wait === 'string' && wait !== '') {
39776
- waitBetweenPrompts = parseDuration(wait);
39777
- }
39977
+ // [1] Parse the --wait and --no-auto options:
39978
+ // default: run automatically through the queue (no waiting)
39979
+ // --no-auto: wait for user confirmation before each prompt (interactive mode)
39980
+ // --wait <duration>: wait that long between prompt rounds to avoid rate limits
39981
+ const waitForUser = !auto;
39982
+ const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
39778
39983
  // Convert commander options to RunOptions format
39779
39984
  const runOptions = {
39780
39985
  dryRun,
@@ -39887,30 +40092,23 @@
39887
40092
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
39888
40093
  addPromptRunnerExecutionOptions(command);
39889
40094
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
39890
- command.option('--wait [duration]', _spaceTrim.spaceTrim(`
39891
- Wait between prompt rounds.
39892
- Without a value (default): waits for user confirmation before each prompt (interactive mode).
39893
- With a duration like 1h, 30m, 5s: waits that long between prompts to avoid hitting rate limits of the harness.
39894
- `), true);
39895
- command.option('--no-wait', 'Skip all waiting between prompts and run non-interactively');
40095
+ command.option('--wait <duration>', _spaceTrim.spaceTrim(`
40096
+ Wait this long between prompt rounds to avoid hitting rate limits of the harness.
40097
+ Accepts durations like 1h, 30m, 5s.
40098
+ `));
40099
+ command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
39896
40100
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
39897
40101
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
39898
40102
  command.action(handleActionErrors(async (cliOptions) => {
39899
- const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, wait, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40103
+ const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39900
40104
  const port = parseCoderServerPort(rawPort);
39901
40105
  const testCommand = normalizeCommandOptionValue(test);
39902
40106
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
39903
40107
  isAgentRequired: !dryRun,
39904
40108
  });
39905
- // [1] Parse the --wait option (same logic as `coder run`)
39906
- let waitForUser = false;
39907
- let waitBetweenPrompts = 0;
39908
- if (wait === true) {
39909
- waitForUser = true;
39910
- }
39911
- else if (typeof wait === 'string' && wait !== '') {
39912
- waitBetweenPrompts = parseDuration(wait);
39913
- }
40109
+ // [1] Parse the --wait and --no-auto options (same logic as `coder run`)
40110
+ const waitForUser = !auto;
40111
+ const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
39914
40112
  const runOptions = {
39915
40113
  port,
39916
40114
  dryRun,
@@ -40516,7 +40714,12 @@
40516
40714
  * @private internal function of `$registeredLlmToolsMessage`
40517
40715
  */
40518
40716
  function createUsedEnvMessage() {
40519
- return $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
40717
+ return $usedEnvFilename === null
40718
+ ? `Unknown \`.env\` file`
40719
+ : _spaceTrim.spaceTrim(`
40720
+ Used \`.env\` file:
40721
+ ${$usedEnvFilename}
40722
+ `);
40520
40723
  }
40521
40724
  // TODO: [®] DRY Register logic
40522
40725
  // TODO: [🧠][⚛] Maybe pass env as argument
@@ -53106,7 +53309,11 @@
53106
53309
  * @private utility of CLI
53107
53310
  */
53108
53311
  function $deprecateCliCommand(command, deprecationMessage) {
53109
- command.description(`${command.description()}\n\nDeprecated: ${deprecationMessage}`);
53312
+ command.description(_spaceTrim.spaceTrim((block) => `
53313
+ ${block(command.description())}
53314
+
53315
+ Deprecated: ${block(deprecationMessage)}
53316
+ `));
53110
53317
  command.hook('preAction', () => {
53111
53318
  console.warn(colors__default["default"].yellow(createDeprecatedCliCommandWarning(command, deprecationMessage)));
53112
53319
  });
@@ -65915,7 +66122,11 @@
65915
66122
  };
65916
66123
  }
65917
66124
  return {
65918
- content: `${content.slice(0, Math.max(0, maxCharacters))}\n\n[...truncated...]`,
66125
+ content: _spaceTrim.spaceTrim((block) => `
66126
+ ${block(content.slice(0, Math.max(0, maxCharacters)))}
66127
+
66128
+ [...truncated...]
66129
+ `),
65919
66130
  isTruncated: true,
65920
66131
  };
65921
66132
  }
@@ -69215,7 +69426,7 @@
69215
69426
  /**
69216
69427
  * CLI usage text for this script.
69217
69428
  */
69218
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait [duration]] [--no-wait] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
69429
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait <duration>] [--no-auto] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
69219
69430
  /**
69220
69431
  * Top-level flags supported by this command.
69221
69432
  */
@@ -69233,7 +69444,7 @@
69233
69444
  '--auto-migrate',
69234
69445
  '--allow-destructive-auto-migrate',
69235
69446
  '--wait',
69236
- '--no-wait',
69447
+ '--no-auto',
69237
69448
  '--no-commit',
69238
69449
  '--ignore-git-changes',
69239
69450
  '--no-normalize-line-endings',
@@ -69271,22 +69482,20 @@
69271
69482
  const allowDestructiveAutoMigrate = args.includes('--allow-destructive-auto-migrate');
69272
69483
  const autoPush = args.includes('--auto-push');
69273
69484
  const autoPull = args.includes('--auto-pull');
69274
- // [1] Parse --wait [duration]:
69275
- // absent or --no-wait: no waiting
69276
- // --wait (no value): wait for user confirmation
69485
+ // [1] Parse --wait <duration> and --no-auto:
69486
+ // default: run automatically through the queue (no waiting)
69487
+ // --no-auto: wait for user confirmation before each prompt (interactive mode)
69277
69488
  // --wait 1h: wait 1h between prompt rounds to avoid rate limits
69278
- const waitOptionalValue = readOptionalOptionValue(args, '--wait');
69279
- let waitForUser = false;
69489
+ const waitForUser = args.includes('--no-auto');
69280
69490
  let waitBetweenPrompts = 0;
69281
- if (waitOptionalValue === null) {
69282
- // --wait present without a duration value: interactive mode
69283
- waitForUser = true;
69284
- }
69285
- else if (waitOptionalValue !== undefined) {
69286
- // --wait <duration>: time-based wait between rounds
69287
- waitBetweenPrompts = parseDuration(waitOptionalValue);
69491
+ const hasWaitFlag = args.includes('--wait');
69492
+ const waitValue = readOptionValue(args, '--wait');
69493
+ if (hasWaitFlag) {
69494
+ if (waitValue === undefined || waitValue.startsWith('-')) {
69495
+ exitWithUsageError('Missing value for --wait. Use a duration like 1h, 30m, 5s.');
69496
+ }
69497
+ waitBetweenPrompts = parseDuration(waitValue);
69288
69498
  }
69289
- // --no-wait (or no flag): both waitForUser and waitBetweenPrompts remain false/0
69290
69499
  let thinkingLevel;
69291
69500
  if (hasTestCommandFlag && testCommand === undefined) {
69292
69501
  exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
@@ -69325,23 +69534,6 @@
69325
69534
  priority,
69326
69535
  };
69327
69536
  }
69328
- /**
69329
- * Reads an optional value for a flag that may appear with or without a following value.
69330
- *
69331
- * Returns `undefined` when the flag is absent, `null` when the flag is present but has no value
69332
- * (the next token is another flag or the end of args), and the value string otherwise.
69333
- */
69334
- function readOptionalOptionValue(args, flag) {
69335
- if (!args.includes(flag)) {
69336
- return undefined;
69337
- }
69338
- const index = args.indexOf(flag);
69339
- const nextArg = args[index + 1];
69340
- if (nextArg === undefined || nextArg.startsWith('-')) {
69341
- return null;
69342
- }
69343
- return nextArg;
69344
- }
69345
69537
  /**
69346
69538
  * Reads a value of a CLI option that follows a given flag.
69347
69539
  */
@@ -71318,7 +71510,13 @@
71318
71510
  const taskPrompt = buildCodexPrompt(nextPrompt.file, nextPrompt.section);
71319
71511
  // Prepend agent system message before the task so the harness sees agent instructions first
71320
71512
  const promptWithAgent = resolvedAgentSystemMessage
71321
- ? `${resolvedAgentSystemMessage.trim()}\n\n${taskPrompt}`
71513
+ ? _spaceTrim.spaceTrim((block) => `
71514
+ ${block(resolvedAgentSystemMessage.trim())}
71515
+
71516
+ ## Your Task
71517
+
71518
+ ${block(taskPrompt)}
71519
+ `)
71322
71520
  : taskPrompt;
71323
71521
  const codexPrompt = appendCoderContext(promptWithAgent, resolvedCoderContext);
71324
71522
  const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
@@ -71387,11 +71585,11 @@
71387
71585
  */
71388
71586
  function setPromptRoundRunningState(options) {
71389
71587
  const { isRichUiEnabled, promptLabel, scriptPath, uiHandle } = options;
71588
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
71589
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(scriptPath);
71590
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
71591
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Running');
71390
71592
  if (isRichUiEnabled) {
71391
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
71392
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(scriptPath);
71393
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
71394
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Running');
71395
71593
  return;
71396
71594
  }
71397
71595
  console.info(colors__default["default"].blue(`Processing ${promptLabel}`));
@@ -71661,7 +71859,7 @@
71661
71859
  }
71662
71860
  if (options.noCommit && !options.waitForUser && !options.ignoreGitChanges) {
71663
71861
  throw new NotAllowed(_spaceTrim.spaceTrim(`
71664
- Flag \`--no-commit\` requires \`--ignore-git-changes\` when combined with \`--no-wait\`.
71862
+ Flag \`--no-commit\` requires \`--ignore-git-changes\` when running in auto mode (the default; pass \`--no-auto\` for interactive confirmation).
71665
71863
 
71666
71864
  Without commits, the next prompt round would fail the clean working tree check.
71667
71865
  `));
@@ -71693,7 +71891,11 @@
71693
71891
  function createRunDisplays(options, runStartDate) {
71694
71892
  const isRichUiEnabled = !options.dryRun && !options.noUi && Boolean(process.stdout.isTTY);
71695
71893
  const progressDisplay = options.dryRun || options.noUi || isRichUiEnabled ? undefined : new CliProgressDisplay(runStartDate, options.priority);
71696
- const uiHandle = isRichUiEnabled ? renderCoderRunUi(runStartDate) : undefined;
71894
+ const uiHandle = isRichUiEnabled || options.uiState
71895
+ ? renderCoderRunUi(runStartDate, {
71896
+ state: options.uiState,
71897
+ })
71898
+ : undefined;
71697
71899
  return {
71698
71900
  isRichUiEnabled,
71699
71901
  progressDisplay,
@@ -71758,6 +71960,7 @@
71758
71960
  modelName: actualRunnerModel,
71759
71961
  thinkingLevel: options.thinkingLevel,
71760
71962
  context: options.context,
71963
+ serverUrl: options.serverUrl,
71761
71964
  priority: options.priority,
71762
71965
  testCommand: options.testCommand,
71763
71966
  });
@@ -71916,6 +72119,166 @@
71916
72119
  runCodexPrompts: runCodexPrompts
71917
72120
  });
71918
72121
 
72122
+ /**
72123
+ * Converts parsed prompt files into a web-board response with derived columns and tags.
72124
+ */
72125
+ function buildCoderServerPromptFileResponses(options) {
72126
+ const activePrompt = getActivePrompt(options.uiState);
72127
+ return [
72128
+ ...options.promptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
72129
+ promptFile,
72130
+ isFinished: false,
72131
+ minimumPriority: options.minimumPriority,
72132
+ activePrompt,
72133
+ })),
72134
+ ...options.finishedPromptFiles.map((promptFile) => buildCoderServerPromptFileResponse({
72135
+ promptFile,
72136
+ isFinished: true,
72137
+ minimumPriority: options.minimumPriority,
72138
+ activePrompt,
72139
+ })),
72140
+ ];
72141
+ }
72142
+ /**
72143
+ * Builds one prompt-file response.
72144
+ */
72145
+ function buildCoderServerPromptFileResponse(options) {
72146
+ const { promptFile, isFinished, minimumPriority, activePrompt } = options;
72147
+ return {
72148
+ filePath: promptFile.path,
72149
+ fileName: promptFile.name,
72150
+ relativeFilePath: path.relative(process.cwd(), promptFile.path).replace(/\\/gu, '/'),
72151
+ isFinished,
72152
+ sections: promptFile.sections.map((section) => buildCoderServerPromptSectionResponse({
72153
+ promptFile,
72154
+ section,
72155
+ isFinished,
72156
+ minimumPriority,
72157
+ activePrompt,
72158
+ })),
72159
+ };
72160
+ }
72161
+ /**
72162
+ * Builds one prompt-section response with derived board metadata.
72163
+ */
72164
+ function buildCoderServerPromptSectionResponse(options) {
72165
+ const { promptFile, section, isFinished, minimumPriority, activePrompt } = options;
72166
+ const isUnwritten = isPromptToBeWritten(promptFile, section);
72167
+ const isActive = isPromptActive(promptFile, section, activePrompt);
72168
+ const column = getPromptColumn({
72169
+ section,
72170
+ isFinished,
72171
+ isUnwritten,
72172
+ isActive,
72173
+ minimumPriority,
72174
+ });
72175
+ return {
72176
+ index: section.index,
72177
+ status: section.status,
72178
+ column,
72179
+ priority: section.priority,
72180
+ summary: buildPromptSummary(promptFile, section),
72181
+ content: buildCodexPrompt(promptFile, section),
72182
+ label: buildPromptLabelForDisplay(promptFile, section),
72183
+ tags: buildPromptTags({
72184
+ section,
72185
+ isUnwritten,
72186
+ isActive,
72187
+ activePrompt,
72188
+ }),
72189
+ };
72190
+ }
72191
+ /**
72192
+ * Chooses the web-board column for one prompt section without changing persisted prompt status.
72193
+ */
72194
+ function getPromptColumn(options) {
72195
+ const { section, isFinished, isUnwritten, isActive, minimumPriority } = options;
72196
+ if (isFinished) {
72197
+ return 'finished';
72198
+ }
72199
+ if (section.status === 'failed') {
72200
+ return 'errors';
72201
+ }
72202
+ if (section.status === 'done') {
72203
+ return 'done';
72204
+ }
72205
+ if (section.status === 'not-ready' || isUnwritten) {
72206
+ return 'backlog';
72207
+ }
72208
+ if (isActive) {
72209
+ return 'in-progress';
72210
+ }
72211
+ if (!hasSufficientPriority(section, minimumPriority)) {
72212
+ return 'low-priority';
72213
+ }
72214
+ return 'todo';
72215
+ }
72216
+ /**
72217
+ * Builds all tags shown on a prompt card.
72218
+ */
72219
+ function buildPromptTags(options) {
72220
+ var _a;
72221
+ const tags = [];
72222
+ if (options.section.status === 'not-ready') {
72223
+ tags.push({ id: 'not-ready', label: '[-]' });
72224
+ }
72225
+ if (options.isUnwritten) {
72226
+ tags.push({ id: 'unwritten', label: '@@@' });
72227
+ }
72228
+ if (options.isActive) {
72229
+ tags.push(((_a = options.activePrompt) === null || _a === void 0 ? void 0 : _a.phase) === 'verifying'
72230
+ ? { id: 'verifying', label: 'Verifying' }
72231
+ : { id: 'implementing', label: 'Implementing' });
72232
+ }
72233
+ return tags;
72234
+ }
72235
+ /**
72236
+ * Extracts an active prompt only while an agent is implementing or verifying it.
72237
+ */
72238
+ function getActivePrompt(uiState) {
72239
+ if (!(uiState === null || uiState === void 0 ? void 0 : uiState.currentPromptLabel)) {
72240
+ return undefined;
72241
+ }
72242
+ if (uiState.phase !== 'running' && uiState.phase !== 'verifying') {
72243
+ return undefined;
72244
+ }
72245
+ return {
72246
+ label: uiState.currentPromptLabel,
72247
+ phase: uiState.phase,
72248
+ };
72249
+ }
72250
+ /**
72251
+ * Checks whether a parsed section is the prompt currently handled by the active agent.
72252
+ */
72253
+ function isPromptActive(promptFile, section, activePrompt) {
72254
+ if (!activePrompt || section.status !== 'todo') {
72255
+ return false;
72256
+ }
72257
+ return buildPromptLabelForDisplay(promptFile, section) === activePrompt.label;
72258
+ }
72259
+ // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/buildCoderServerPromptResponse.ts) should never be published outside of `@promptbook/cli`
72260
+
72261
+ /**
72262
+ * Builds a plain JSON-safe snapshot from the shared terminal UI state.
72263
+ */
72264
+ function buildCoderServerRunState(uiState) {
72265
+ return {
72266
+ config: uiState.config,
72267
+ phase: uiState.phase,
72268
+ currentPromptLabel: uiState.currentPromptLabel,
72269
+ currentAttempt: uiState.currentAttempt,
72270
+ maxAttempts: uiState.maxAttempts,
72271
+ statusMessage: uiState.statusMessage,
72272
+ detailLines: [...uiState.detailLines],
72273
+ agentStatusLines: [...uiState.agentStatusLines],
72274
+ agentStatusTableRows: [...uiState.agentStatusTableRows],
72275
+ agentOutputLines: [...uiState.agentOutputLines],
72276
+ errors: [...uiState.errors],
72277
+ progress: uiState.getProgress(),
72278
+ };
72279
+ }
72280
+ // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/buildCoderServerRunState.ts) should never be published outside of `@promptbook/cli`
72281
+
71919
72282
  /**
71920
72283
  * Overwrites the body of one prompt section with new content, preserving the status line.
71921
72284
  *
@@ -71928,9 +72291,14 @@
71928
72291
  var _a;
71929
72292
  const rawContent = await promises.readFile(filePath, 'utf-8');
71930
72293
  const promptFile = parsePromptFile(filePath, rawContent);
71931
- const section = promptFile.sections.find((s) => s.index === sectionIndex);
72294
+ const section = promptFile.sections.find((sectionCandidate) => sectionCandidate.index === sectionIndex);
71932
72295
  if (!section) {
71933
- throw new Error(`Section ${sectionIndex} not found in file: ${filePath}`);
72296
+ throw new NotFoundError(_spaceTrim.spaceTrim(`
72297
+ Prompt section \`${sectionIndex}\` was not found.
72298
+
72299
+ File:
72300
+ \`${filePath}\`
72301
+ `));
71934
72302
  }
71935
72303
  // Lines before the status line (usually empty lines at section start)
71936
72304
  const linesBeforeStatus = section.statusLineIndex !== undefined
@@ -71957,7 +72325,11 @@
71957
72325
  ...promptFile.lines.slice(section.endLine + 1),
71958
72326
  ];
71959
72327
  const updatedContent = updatedLines.join(promptFile.eol) + (promptFile.hasFinalEol ? promptFile.eol : '');
72328
+ if (updatedContent === rawContent) {
72329
+ return false;
72330
+ }
71960
72331
  await promises.writeFile(filePath, updatedContent, 'utf-8');
72332
+ return true;
71961
72333
  }
71962
72334
  // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/updatePromptSection.ts) should never be published outside of `@promptbook/cli`
71963
72335
 
@@ -71978,38 +72350,37 @@
71978
72350
  <style>
71979
72351
  * { box-sizing: border-box; margin: 0; padding: 0; }
71980
72352
  body {
71981
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
71982
- background: #f4f5f7;
71983
- color: #172b4d;
72353
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
72354
+ background: #f6f7f9;
72355
+ color: #1f2933;
71984
72356
  min-height: 100vh;
71985
72357
  }
71986
72358
 
71987
72359
  header {
71988
- background: #0052cc;
72360
+ background: #22313f;
71989
72361
  color: white;
71990
- padding: 12px 20px;
72362
+ padding: 12px 18px;
71991
72363
  display: flex;
71992
72364
  align-items: center;
71993
72365
  gap: 12px;
71994
72366
  position: sticky;
71995
72367
  top: 0;
71996
72368
  z-index: 10;
71997
- box-shadow: 0 2px 8px rgba(0,0,0,0.2);
72369
+ box-shadow: 0 2px 10px rgba(15,23,42,0.22);
71998
72370
  }
71999
- header h1 { font-size: 17px; font-weight: 700; flex-shrink: 0; }
72371
+ header h1 { font-size: 17px; font-weight: 700; flex-shrink: 0; letter-spacing: 0; }
72000
72372
 
72001
72373
  .status-badge {
72002
72374
  padding: 3px 10px;
72003
- border-radius: 12px;
72375
+ border-radius: 999px;
72004
72376
  font-size: 11px;
72005
72377
  font-weight: 700;
72006
- letter-spacing: 0.5px;
72007
72378
  text-transform: uppercase;
72008
72379
  flex-shrink: 0;
72009
72380
  }
72010
- .status-RUNNING { background: #00875a; color: white; }
72011
- .status-PAUSING { background: #ff8b00; color: white; }
72012
- .status-PAUSED { background: #bf2600; color: white; }
72381
+ .status-RUNNING { background: #0b875b; color: white; }
72382
+ .status-PAUSING { background: #d97904; color: white; }
72383
+ .status-PAUSED { background: #b42318; color: white; }
72013
72384
 
72014
72385
  #pause-label {
72015
72386
  font-size: 12px;
@@ -72022,57 +72393,122 @@
72022
72393
  }
72023
72394
 
72024
72395
  .btn {
72025
- padding: 6px 14px;
72026
- border: none;
72396
+ padding: 6px 12px;
72397
+ border: 0;
72027
72398
  border-radius: 4px;
72028
72399
  cursor: pointer;
72029
72400
  font-size: 13px;
72030
- font-weight: 600;
72031
- transition: opacity 0.15s;
72401
+ font-weight: 700;
72402
+ transition: background 0.15s, opacity 0.15s;
72032
72403
  flex-shrink: 0;
72033
72404
  }
72034
- .btn:hover { opacity: 0.85; }
72035
- .btn-pause { background: #ffc400; color: #172b4d; }
72036
- .btn-resume { background: #00875a; color: white; }
72405
+ .btn:disabled { opacity: 0.55; cursor: default; }
72406
+ .btn-pause { background: #ffd166; color: #1f2933; }
72407
+ .btn-resume { background: #0b875b; color: white; }
72408
+ .btn-save { background: #2563eb; color: white; }
72409
+ .btn-cancel { background: #edf0f4; color: #1f2933; }
72410
+
72411
+ .run-strip {
72412
+ background: white;
72413
+ border-bottom: 1px solid #d8dee8;
72414
+ padding: 12px 18px;
72415
+ display: grid;
72416
+ grid-template-columns: minmax(220px, 1.2fr) minmax(240px, 1fr) minmax(260px, 1.4fr);
72417
+ gap: 14px;
72418
+ align-items: center;
72419
+ }
72420
+ .run-title {
72421
+ font-size: 14px;
72422
+ font-weight: 700;
72423
+ overflow: hidden;
72424
+ text-overflow: ellipsis;
72425
+ white-space: nowrap;
72426
+ }
72427
+ .run-subtitle,
72428
+ .run-current,
72429
+ .run-output {
72430
+ color: #64748b;
72431
+ font-size: 12px;
72432
+ line-height: 1.45;
72433
+ overflow: hidden;
72434
+ }
72435
+ .run-current,
72436
+ .run-output {
72437
+ display: -webkit-box;
72438
+ -webkit-box-orient: vertical;
72439
+ -webkit-line-clamp: 2;
72440
+ }
72441
+ .progress-shell {
72442
+ display: grid;
72443
+ grid-template-columns: 1fr auto;
72444
+ gap: 8px;
72445
+ align-items: center;
72446
+ }
72447
+ .progress-track {
72448
+ height: 10px;
72449
+ background: #e4e9f0;
72450
+ border-radius: 999px;
72451
+ overflow: hidden;
72452
+ }
72453
+ .progress-fill {
72454
+ display: block;
72455
+ height: 100%;
72456
+ width: 0;
72457
+ background: linear-gradient(90deg, #0b875b, #2563eb);
72458
+ transition: width 0.2s;
72459
+ }
72460
+ .progress-label {
72461
+ color: #475569;
72462
+ font-size: 12px;
72463
+ font-weight: 700;
72464
+ min-width: 72px;
72465
+ text-align: right;
72466
+ }
72037
72467
 
72038
72468
  .board {
72039
72469
  display: flex;
72040
72470
  gap: 14px;
72041
72471
  padding: 16px;
72042
72472
  overflow-x: auto;
72043
- min-height: calc(100vh - 52px);
72473
+ min-height: calc(100vh - 119px);
72044
72474
  align-items: flex-start;
72045
72475
  }
72046
72476
 
72047
72477
  .column {
72048
- background: #ebecf0;
72049
- border-radius: 8px;
72478
+ background: #e9edf3;
72479
+ border-top: 4px solid #94a3b8;
72480
+ border-radius: 6px;
72050
72481
  padding: 10px;
72051
72482
  min-width: 250px;
72052
72483
  width: 270px;
72053
72484
  flex-shrink: 0;
72054
72485
  }
72486
+ .column-backlog { border-top-color: #64748b; }
72487
+ .column-low-priority { border-top-color: #d97904; }
72488
+ .column-todo { border-top-color: #2563eb; }
72489
+ .column-in-progress { border-top-color: #7c3aed; }
72490
+ .column-done { border-top-color: #0b875b; }
72491
+ .column-errors { border-top-color: #b42318; }
72492
+ .column-finished { border-top-color: #0891b2; }
72055
72493
 
72056
72494
  .column-header {
72057
72495
  font-size: 12px;
72058
- font-weight: 700;
72496
+ font-weight: 800;
72059
72497
  text-transform: uppercase;
72060
- letter-spacing: 0.6px;
72061
- color: #5e6c84;
72498
+ color: #475569;
72062
72499
  margin-bottom: 10px;
72063
72500
  display: flex;
72064
72501
  align-items: center;
72065
72502
  justify-content: space-between;
72503
+ gap: 8px;
72066
72504
  }
72067
-
72068
72505
  .column-count {
72069
- background: #dfe1e6;
72070
- border-radius: 10px;
72071
- padding: 1px 7px;
72506
+ background: rgba(255,255,255,0.72);
72507
+ border-radius: 999px;
72508
+ padding: 2px 8px;
72072
72509
  font-size: 11px;
72073
- color: #5e6c84;
72510
+ color: #475569;
72074
72511
  }
72075
-
72076
72512
  .column-cards { min-height: 40px; }
72077
72513
 
72078
72514
  .card {
@@ -72080,57 +72516,61 @@
72080
72516
  border-radius: 6px;
72081
72517
  padding: 10px 12px;
72082
72518
  margin-bottom: 8px;
72083
- box-shadow: 0 1px 3px rgba(0,0,0,0.08);
72519
+ box-shadow: 0 1px 3px rgba(15,23,42,0.12);
72084
72520
  cursor: pointer;
72085
- transition: box-shadow 0.15s, transform 0.1s;
72086
- border-left: 3px solid transparent;
72087
- }
72088
- .card:hover {
72089
- box-shadow: 0 4px 10px rgba(0,0,0,0.14);
72090
- transform: translateY(-1px);
72521
+ border-left: 3px solid #94a3b8;
72091
72522
  }
72092
- .card-todo { border-left-color: #0052cc; }
72093
- .card-not-ready { border-left-color: #8993a4; }
72094
- .card-done { border-left-color: #00875a; }
72095
- .card-failed { border-left-color: #bf2600; }
72523
+ .card:hover { box-shadow: 0 4px 12px rgba(15,23,42,0.16); }
72524
+ .card-backlog { border-left-color: #64748b; }
72525
+ .card-low-priority { border-left-color: #d97904; }
72526
+ .card-todo { border-left-color: #2563eb; }
72527
+ .card-in-progress { border-left-color: #7c3aed; }
72528
+ .card-done { border-left-color: #0b875b; }
72529
+ .card-errors { border-left-color: #b42318; }
72530
+ .card-finished { border-left-color: #0891b2; }
72096
72531
 
72097
72532
  .card-file {
72098
72533
  font-size: 10px;
72099
- color: #8993a4;
72100
- margin-bottom: 5px;
72101
- font-weight: 500;
72534
+ color: #7b8794;
72535
+ margin-bottom: 6px;
72536
+ font-weight: 600;
72537
+ overflow: hidden;
72538
+ text-overflow: ellipsis;
72539
+ white-space: nowrap;
72102
72540
  }
72103
-
72104
72541
  .card-summary {
72105
72542
  font-size: 13px;
72106
- line-height: 1.5;
72107
- color: #172b4d;
72543
+ line-height: 1.45;
72544
+ color: #1f2933;
72108
72545
  word-break: break-word;
72109
72546
  white-space: pre-wrap;
72110
- max-height: 78px;
72547
+ max-height: 76px;
72111
72548
  overflow: hidden;
72112
72549
  display: -webkit-box;
72113
72550
  -webkit-line-clamp: 4;
72114
72551
  -webkit-box-orient: vertical;
72115
72552
  }
72116
-
72117
- .card-priority {
72118
- margin-top: 6px;
72119
- color: #ff8b00;
72120
- font-size: 11px;
72121
- font-weight: 700;
72553
+ .card-tags {
72554
+ display: flex;
72555
+ flex-wrap: wrap;
72556
+ gap: 5px;
72557
+ margin-top: 8px;
72122
72558
  }
72123
-
72124
- .card-edit-hint {
72559
+ .card-tag {
72560
+ border-radius: 999px;
72561
+ padding: 2px 7px;
72125
72562
  font-size: 10px;
72126
- color: #c1c7d0;
72127
- margin-top: 6px;
72128
- display: none;
72563
+ font-weight: 800;
72564
+ line-height: 1.35;
72129
72565
  }
72130
- .card:hover .card-edit-hint { display: block; }
72566
+ .tag-not-ready { background: #e2e8f0; color: #475569; }
72567
+ .tag-unwritten { background: #fff4cc; color: #8a5a00; }
72568
+ .tag-implementing { background: #ede9fe; color: #5b21b6; }
72569
+ .tag-verifying { background: #fae8ff; color: #86198f; }
72570
+ .tag-priority { background: #ffedd5; color: #9a3412; }
72131
72571
 
72132
72572
  .empty-column {
72133
- color: #b3bac5;
72573
+ color: #94a3b8;
72134
72574
  font-size: 12px;
72135
72575
  padding: 10px 4px;
72136
72576
  text-align: center;
@@ -72139,29 +72579,23 @@
72139
72579
  .modal-overlay {
72140
72580
  position: fixed;
72141
72581
  inset: 0;
72142
- background: rgba(9,30,66,0.54);
72582
+ background: rgba(15,23,42,0.58);
72143
72583
  display: flex;
72144
72584
  align-items: flex-start;
72145
72585
  justify-content: center;
72146
- padding: 60px 16px 16px;
72586
+ padding: 56px 16px 16px;
72147
72587
  z-index: 100;
72148
- animation: fadeIn 0.1s ease;
72149
72588
  }
72150
72589
  .modal-overlay.hidden { display: none; }
72151
72590
 
72152
- @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
72153
-
72154
72591
  .modal {
72155
72592
  background: white;
72156
72593
  border-radius: 8px;
72157
72594
  width: 100%;
72158
- max-width: 620px;
72159
- padding: 20px;
72160
- box-shadow: 0 8px 32px rgba(0,0,0,0.25);
72161
- animation: slideIn 0.15s ease;
72595
+ max-width: 720px;
72596
+ padding: 18px;
72597
+ box-shadow: 0 14px 40px rgba(15,23,42,0.28);
72162
72598
  }
72163
- @keyframes slideIn { from { transform: translateY(-8px); opacity: 0; } to { transform: none; opacity: 1; } }
72164
-
72165
72599
  .modal-header {
72166
72600
  display: flex;
72167
72601
  align-items: flex-start;
@@ -72169,62 +72603,59 @@
72169
72603
  margin-bottom: 14px;
72170
72604
  gap: 12px;
72171
72605
  }
72172
-
72173
72606
  .modal-meta { flex: 1; min-width: 0; }
72174
- .modal-file { font-size: 11px; color: #8993a4; margin-bottom: 4px; }
72175
- .modal-section-label { font-size: 13px; font-weight: 600; color: #172b4d; }
72176
-
72607
+ .modal-file {
72608
+ font-size: 11px;
72609
+ color: #7b8794;
72610
+ margin-bottom: 4px;
72611
+ overflow: hidden;
72612
+ text-overflow: ellipsis;
72613
+ white-space: nowrap;
72614
+ }
72615
+ .modal-section-label { font-size: 14px; font-weight: 700; color: #1f2933; }
72177
72616
  .modal-status {
72178
72617
  font-size: 11px;
72179
- font-weight: 700;
72180
- padding: 2px 8px;
72181
- border-radius: 10px;
72618
+ font-weight: 800;
72619
+ padding: 3px 8px;
72620
+ border-radius: 999px;
72182
72621
  text-transform: uppercase;
72183
- letter-spacing: 0.4px;
72184
72622
  flex-shrink: 0;
72185
72623
  }
72186
- .status-todo { background: #deebff; color: #0052cc; }
72187
- .status-not-ready { background: #f4f5f7; color: #5e6c84; }
72188
- .status-done { background: #e3fcef; color: #006644; }
72189
- .status-failed { background: #ffebe6; color: #bf2600; }
72190
-
72624
+ .status-backlog { background: #e2e8f0; color: #475569; }
72625
+ .status-low-priority { background: #ffedd5; color: #9a3412; }
72626
+ .status-todo { background: #dbeafe; color: #1d4ed8; }
72627
+ .status-in-progress { background: #ede9fe; color: #5b21b6; }
72628
+ .status-done { background: #dcfce7; color: #166534; }
72629
+ .status-errors { background: #fee2e2; color: #991b1b; }
72630
+ .status-finished { background: #cffafe; color: #155e75; }
72191
72631
  .modal-close {
72192
- background: none;
72193
- border: none;
72194
- color: #8993a4;
72632
+ background: transparent;
72633
+ border: 0;
72634
+ color: #64748b;
72195
72635
  cursor: pointer;
72196
- font-size: 18px;
72636
+ font-size: 20px;
72197
72637
  padding: 0 4px;
72198
72638
  line-height: 1;
72199
- flex-shrink: 0;
72200
72639
  }
72201
- .modal-close:hover { color: #172b4d; }
72640
+ .modal-close:hover { color: #1f2933; }
72202
72641
 
72203
72642
  textarea {
72204
72643
  width: 100%;
72205
- min-height: 220px;
72206
- font-family: 'SFMono-Regular', 'Consolas', 'Courier New', monospace;
72644
+ min-height: 300px;
72645
+ font-family: "SFMono-Regular", Consolas, "Courier New", monospace;
72207
72646
  font-size: 13px;
72208
- border: 2px solid #dfe1e6;
72647
+ border: 2px solid #d8dee8;
72209
72648
  border-radius: 4px;
72210
72649
  padding: 10px 12px;
72211
72650
  resize: vertical;
72212
- line-height: 1.65;
72213
- color: #172b4d;
72214
- transition: border-color 0.15s;
72651
+ line-height: 1.6;
72652
+ color: #1f2933;
72215
72653
  }
72216
72654
  textarea:focus {
72217
72655
  outline: none;
72218
- border-color: #0052cc;
72219
- box-shadow: 0 0 0 3px rgba(0,82,204,0.12);
72656
+ border-color: #2563eb;
72657
+ box-shadow: 0 0 0 3px rgba(37,99,235,0.12);
72220
72658
  }
72221
-
72222
- .modal-hint {
72223
- font-size: 11px;
72224
- color: #8993a4;
72225
- margin-top: 6px;
72226
- }
72227
-
72228
72659
  .modal-actions {
72229
72660
  display: flex;
72230
72661
  gap: 8px;
@@ -72232,67 +72663,50 @@
72232
72663
  justify-content: flex-end;
72233
72664
  }
72234
72665
 
72235
- .btn-save { background: #0052cc; color: white; }
72236
- .btn-cancel { background: #f4f5f7; color: #172b4d; }
72237
- .btn-cancel:hover { background: #ebecf0; opacity: 1; }
72238
-
72239
72666
  .error-banner {
72240
- background: #ffebe6;
72241
- border-bottom: 2px solid #bf2600;
72242
- color: #bf2600;
72243
- padding: 8px 20px;
72667
+ background: #fee2e2;
72668
+ border-bottom: 2px solid #b42318;
72669
+ color: #991b1b;
72670
+ padding: 8px 18px;
72244
72671
  font-size: 13px;
72245
- font-weight: 500;
72672
+ font-weight: 700;
72246
72673
  }
72247
72674
  .error-banner.hidden { display: none; }
72675
+
72676
+ @media (max-width: 860px) {
72677
+ header { flex-wrap: wrap; }
72678
+ #pause-label { order: 4; flex-basis: 100%; }
72679
+ .run-strip { grid-template-columns: 1fr; }
72680
+ .board { min-height: calc(100vh - 212px); }
72681
+ }
72248
72682
  </style>
72249
72683
  </head>
72250
72684
  <body>
72251
-
72252
72685
  <div id="error-banner" class="error-banner hidden"></div>
72253
72686
 
72254
72687
  <header>
72255
- <h1>&#128295; Ptbk Coder Server</h1>
72688
+ <h1>Ptbk Coder Server</h1>
72256
72689
  <span id="status-badge" class="status-badge status-RUNNING">RUNNING</span>
72257
72690
  <span id="pause-label"></span>
72258
- <button id="toggle-btn" class="btn btn-pause">&#9646;&#9646; Pause</button>
72691
+ <button id="toggle-btn" class="btn btn-pause">Pause</button>
72259
72692
  </header>
72260
72693
 
72261
- <div class="board">
72262
- <div class="column">
72263
- <div class="column-header">
72264
- To Do
72265
- <span class="column-count" id="count-todo">0</span>
72266
- </div>
72267
- <div class="column-cards" id="cards-todo">
72268
- <div class="empty-column">Loading&hellip;</div>
72269
- </div>
72694
+ <section class="run-strip">
72695
+ <div>
72696
+ <div class="run-title" id="run-title">Initializing...</div>
72697
+ <div class="run-subtitle" id="run-subtitle">Loading runner state</div>
72270
72698
  </div>
72271
-
72272
- <div class="column">
72273
- <div class="column-header">
72274
- Not Ready
72275
- <span class="column-count" id="count-not-ready">0</span>
72276
- </div>
72277
- <div class="column-cards" id="cards-not-ready"></div>
72699
+ <div class="progress-shell">
72700
+ <div class="progress-track"><span class="progress-fill" id="progress-fill"></span></div>
72701
+ <div class="progress-label" id="progress-label">0%</div>
72278
72702
  </div>
72279
-
72280
- <div class="column">
72281
- <div class="column-header">
72282
- Done
72283
- <span class="column-count" id="count-done">0</span>
72284
- </div>
72285
- <div class="column-cards" id="cards-done"></div>
72703
+ <div>
72704
+ <div class="run-current" id="run-current">No active prompt</div>
72705
+ <div class="run-output" id="run-output"></div>
72286
72706
  </div>
72707
+ </section>
72287
72708
 
72288
- <div class="column">
72289
- <div class="column-header">
72290
- Failed
72291
- <span class="column-count" id="count-failed">0</span>
72292
- </div>
72293
- <div class="column-cards" id="cards-failed"></div>
72294
- </div>
72295
- </div>
72709
+ <div class="board" id="board"></div>
72296
72710
 
72297
72711
  <div class="modal-overlay hidden" id="modal-overlay">
72298
72712
  <div class="modal">
@@ -72302,196 +72716,317 @@
72302
72716
  <div class="modal-section-label" id="modal-section-label"></div>
72303
72717
  </div>
72304
72718
  <span class="modal-status" id="modal-status-badge"></span>
72305
- <button class="modal-close" onclick="closeModal()" title="Close (Esc)">&#x2715;</button>
72719
+ <button class="modal-close" id="modal-close" title="Close">&times;</button>
72306
72720
  </div>
72307
72721
 
72308
- <textarea id="modal-content" placeholder="Prompt content&hellip;"></textarea>
72309
- <div class="modal-hint">Tip: press Ctrl+Enter to save, Esc to cancel.</div>
72722
+ <textarea id="modal-content" placeholder="Prompt content"></textarea>
72310
72723
 
72311
72724
  <div class="modal-actions">
72312
- <button class="btn btn-cancel" onclick="closeModal()">Cancel</button>
72313
- <button class="btn btn-save" onclick="saveModal()">Save</button>
72725
+ <button class="btn btn-cancel" id="cancel-button">Cancel</button>
72726
+ <button class="btn btn-save" id="save-button">Save</button>
72314
72727
  </div>
72315
72728
  </div>
72316
72729
  </div>
72317
72730
 
72318
72731
  <script>
72319
- 'use strict';
72732
+ "use strict";
72733
+
72734
+ const BOARD_COLUMNS = [
72735
+ { id: "backlog", title: "Backlog" },
72736
+ { id: "low-priority", title: "Low priority" },
72737
+ { id: "todo", title: "To do" },
72738
+ { id: "in-progress", title: "In progress" },
72739
+ { id: "done", title: "Done" },
72740
+ { id: "errors", title: "Errors" },
72741
+ { id: "finished", title: "Finished" },
72742
+ ];
72320
72743
 
72321
72744
  let modalState = null;
72322
- let lastPauseState = 'RUNNING';
72745
+ let lastPauseState = "RUNNING";
72746
+
72747
+ function getColumnTitle(columnId) {
72748
+ const column = BOARD_COLUMNS.find((columnCandidate) => columnCandidate.id === columnId);
72749
+ return column ? column.title : columnId;
72750
+ }
72751
+
72752
+ function showError(message) {
72753
+ const banner = document.getElementById("error-banner");
72754
+ banner.textContent = message;
72755
+ banner.classList.remove("hidden");
72756
+ clearTimeout(banner.timer);
72757
+ banner.timer = setTimeout(() => banner.classList.add("hidden"), 6000);
72758
+ }
72759
+
72760
+ function escapeHtml(value) {
72761
+ const element = document.createElement("div");
72762
+ element.textContent = String(value);
72763
+ return element.innerHTML;
72764
+ }
72765
+
72766
+ function renderBoardSkeleton() {
72767
+ const board = document.getElementById("board");
72768
+ board.innerHTML = "";
72769
+
72770
+ for (const column of BOARD_COLUMNS) {
72771
+ const columnElement = document.createElement("section");
72772
+ columnElement.className = "column column-" + column.id;
72773
+ columnElement.innerHTML =
72774
+ '<div class="column-header">' +
72775
+ '<span>' + escapeHtml(column.title) + '</span>' +
72776
+ '<span class="column-count" id="count-' + column.id + '">0</span>' +
72777
+ '</div>' +
72778
+ '<div class="column-cards" id="cards-' + column.id + '">' +
72779
+ '<div class="empty-column">Loading</div>' +
72780
+ '</div>';
72781
+ board.appendChild(columnElement);
72782
+ }
72783
+ }
72323
72784
 
72324
- function showError(msg) {
72325
- const banner = document.getElementById('error-banner');
72326
- banner.textContent = '\\u26a0 ' + msg;
72327
- banner.classList.remove('hidden');
72328
- clearTimeout(banner._timer);
72329
- banner._timer = setTimeout(() => banner.classList.add('hidden'), 6000);
72785
+ async function fetchStatus() {
72786
+ try {
72787
+ const response = await fetch("/api/status");
72788
+ if (!response.ok) {
72789
+ showError("Status API error: " + response.status);
72790
+ return;
72791
+ }
72792
+
72793
+ const status = await response.json();
72794
+ renderPauseState(status);
72795
+ renderRunState(status.runState);
72796
+ } catch (error) {
72797
+ showError("Could not reach coder server: " + error.message);
72798
+ }
72330
72799
  }
72331
72800
 
72332
- function escapeHtml(str) {
72333
- const d = document.createElement('div');
72334
- d.textContent = String(str);
72335
- return d.innerHTML;
72801
+ function renderPauseState(status) {
72802
+ lastPauseState = status.pauseState;
72803
+
72804
+ const badge = document.getElementById("status-badge");
72805
+ badge.textContent = status.pauseState;
72806
+ badge.className = "status-badge status-" + status.pauseState;
72807
+
72808
+ const toggleButton = document.getElementById("toggle-btn");
72809
+ const pauseLabel = document.getElementById("pause-label");
72810
+
72811
+ if (status.pauseState === "RUNNING") {
72812
+ toggleButton.textContent = "Pause";
72813
+ toggleButton.className = "btn btn-pause";
72814
+ pauseLabel.textContent = "";
72815
+ return;
72816
+ }
72817
+
72818
+ toggleButton.textContent = "Resume";
72819
+ toggleButton.className = "btn btn-resume";
72820
+ pauseLabel.textContent =
72821
+ (status.pauseState === "PAUSING" ? "Pausing before: " : "Paused before: ") +
72822
+ (status.pauseTargetLabel || "next checkpoint");
72336
72823
  }
72337
72824
 
72338
- async function fetchStatus() {
72339
- try {
72340
- const res = await fetch('/api/status');
72341
- if (!res.ok) { showError('Status API error: ' + res.status); return; }
72342
- const data = await res.json();
72343
-
72344
- lastPauseState = data.pauseState;
72345
-
72346
- const badge = document.getElementById('status-badge');
72347
- badge.textContent = data.pauseState;
72348
- badge.className = 'status-badge status-' + data.pauseState;
72349
-
72350
- const btn = document.getElementById('toggle-btn');
72351
- const label = document.getElementById('pause-label');
72352
-
72353
- if (data.pauseState === 'RUNNING') {
72354
- btn.textContent = '\\u23f8 Pause';
72355
- btn.className = 'btn btn-pause';
72356
- label.textContent = '';
72357
- } else if (data.pauseState === 'PAUSING') {
72358
- btn.textContent = '\\u23f5 Resume';
72359
- btn.className = 'btn btn-resume';
72360
- label.textContent = 'Pausing before: ' + (data.pauseTargetLabel || '\\u2026');
72361
- } else {
72362
- btn.textContent = '\\u23f5 Resume';
72363
- btn.className = 'btn btn-resume';
72364
- label.textContent = 'Paused before: ' + (data.pauseTargetLabel || '\\u2026');
72365
- }
72366
- } catch (e) {
72367
- showError('Could not reach coder server: ' + e.message);
72825
+ function renderRunState(runState) {
72826
+ if (!runState) {
72827
+ document.getElementById("run-title").textContent = "Waiting for runner state";
72828
+ document.getElementById("run-subtitle").textContent = "";
72829
+ return;
72368
72830
  }
72831
+
72832
+ const progress = runState.progress || {};
72833
+ const percentage = Number(progress.percentage || 0);
72834
+ const runnerParts = [runState.config.agentName, runState.config.modelName, runState.config.thinkingLevel]
72835
+ .filter(Boolean);
72836
+
72837
+ document.getElementById("run-title").textContent = runState.statusMessage || runState.phase;
72838
+ document.getElementById("run-subtitle").textContent = runnerParts.join(" / ");
72839
+ document.getElementById("progress-fill").style.width = Math.max(0, Math.min(100, percentage)) + "%";
72840
+ document.getElementById("progress-label").textContent =
72841
+ percentage + "% " + (progress.sessionDone || 0) + "/" + (progress.sessionTotal || 0);
72842
+ document.getElementById("run-current").textContent = runState.currentPromptLabel
72843
+ ? runState.currentPromptLabel + " - attempt " + runState.currentAttempt + "/" + runState.maxAttempts
72844
+ : "No active prompt";
72845
+
72846
+ renderRunOutput(runState);
72847
+ }
72848
+
72849
+ function renderRunOutput(runState) {
72850
+ const statusLines = (runState.agentStatusTableRows || []).map((row) =>
72851
+ row.status + " - " + row.agentName + (row.url ? " - " + row.url : "")
72852
+ );
72853
+ const outputLines = [
72854
+ ...(runState.agentStatusLines || []),
72855
+ ...statusLines,
72856
+ ...(runState.agentOutputLines || []),
72857
+ ...(runState.errors || []).map((errorLine) => "Error: " + errorLine),
72858
+ ].slice(-3);
72859
+
72860
+ document.getElementById("run-output").textContent = outputLines.join("\\n");
72369
72861
  }
72370
72862
 
72371
72863
  async function fetchPrompts() {
72372
72864
  try {
72373
- const res = await fetch('/api/prompts');
72374
- if (!res.ok) { showError('Prompts API error: ' + res.status); return; }
72375
- renderBoard(await res.json());
72376
- } catch (e) {
72377
- showError('Could not load prompts: ' + e.message);
72865
+ const response = await fetch("/api/prompts");
72866
+ if (!response.ok) {
72867
+ showError("Prompts API error: " + response.status);
72868
+ return;
72869
+ }
72870
+
72871
+ renderBoard(await response.json());
72872
+ } catch (error) {
72873
+ showError("Could not load prompts: " + error.message);
72378
72874
  }
72379
72875
  }
72380
72876
 
72381
72877
  function renderBoard(promptFiles) {
72382
- const columns = { 'todo': [], 'not-ready': [], 'done': [], 'failed': [] };
72878
+ const columns = Object.fromEntries(BOARD_COLUMNS.map((column) => [column.id, []]));
72383
72879
 
72384
72880
  for (const file of promptFiles) {
72385
72881
  for (const section of file.sections) {
72386
- const col = columns[section.status];
72387
- if (col) col.push({ file, section });
72882
+ if (columns[section.column]) {
72883
+ columns[section.column].push({ file, section });
72884
+ }
72388
72885
  }
72389
72886
  }
72390
72887
 
72391
- for (const [status, cards] of Object.entries(columns)) {
72392
- const container = document.getElementById('cards-' + status);
72393
- const countEl = document.getElementById('count-' + status);
72394
- if (!container) continue;
72888
+ for (const column of BOARD_COLUMNS) {
72889
+ renderColumn(column.id, columns[column.id]);
72890
+ }
72891
+ }
72395
72892
 
72396
- countEl.textContent = cards.length;
72397
- container.innerHTML = '';
72893
+ function renderColumn(columnId, cards) {
72894
+ const container = document.getElementById("cards-" + columnId);
72895
+ const countElement = document.getElementById("count-" + columnId);
72398
72896
 
72399
- if (cards.length === 0) {
72400
- container.innerHTML = '<div class="empty-column">Empty</div>';
72401
- continue;
72402
- }
72897
+ countElement.textContent = cards.length;
72898
+ container.innerHTML = "";
72403
72899
 
72404
- for (const { file, section } of cards) {
72405
- const card = document.createElement('div');
72406
- card.className = 'card card-' + section.status;
72407
- card.innerHTML =
72408
- '<div class="card-file">' + escapeHtml(file.fileName) + ' &bull; #' + (section.index + 1) + '</div>' +
72409
- '<div class="card-summary">' + escapeHtml(section.summary) + '</div>' +
72410
- (section.priority > 0
72411
- ? '<div class="card-priority">' + '!'.repeat(section.priority) + ' priority ' + section.priority + '</div>'
72412
- : '') +
72413
- '<div class="card-edit-hint">Click to edit</div>';
72414
- card.onclick = () => openModal(file, section);
72415
- container.appendChild(card);
72416
- }
72900
+ if (cards.length === 0) {
72901
+ container.innerHTML = '<div class="empty-column">Empty</div>';
72902
+ return;
72903
+ }
72904
+
72905
+ for (const cardData of cards) {
72906
+ container.appendChild(createPromptCard(cardData.file, cardData.section));
72907
+ }
72908
+ }
72909
+
72910
+ function createPromptCard(file, section) {
72911
+ const card = document.createElement("article");
72912
+ card.className = "card card-" + section.column;
72913
+ card.innerHTML =
72914
+ '<div class="card-file">' + escapeHtml(file.relativeFilePath || file.fileName) + " #" + (section.index + 1) + '</div>' +
72915
+ '<div class="card-summary">' + escapeHtml(section.summary) + '</div>' +
72916
+ renderTags(section);
72917
+ card.onclick = () => openModal(file, section);
72918
+ return card;
72919
+ }
72920
+
72921
+ function renderTags(section) {
72922
+ const tags = [...(section.tags || [])];
72923
+ if (section.priority > 0) {
72924
+ tags.push({ id: "priority", label: "P" + section.priority });
72417
72925
  }
72926
+
72927
+ if (tags.length === 0) {
72928
+ return "";
72929
+ }
72930
+
72931
+ return '<div class="card-tags">' + tags.map((tag) =>
72932
+ '<span class="card-tag tag-' + escapeHtml(tag.id) + '">' + escapeHtml(tag.label) + '</span>'
72933
+ ).join("") + '</div>';
72418
72934
  }
72419
72935
 
72420
72936
  function openModal(file, section) {
72421
72937
  modalState = { filePath: file.filePath, sectionIndex: section.index };
72422
72938
 
72423
- document.getElementById('modal-file').textContent = file.fileName;
72424
- document.getElementById('modal-section-label').textContent = 'Section ' + (section.index + 1);
72939
+ document.getElementById("modal-file").textContent = file.relativeFilePath || file.fileName;
72940
+ document.getElementById("modal-section-label").textContent = "Section " + (section.index + 1);
72425
72941
 
72426
- const badge = document.getElementById('modal-status-badge');
72427
- badge.textContent = section.status.replace('-', '\\u2011');
72428
- badge.className = 'modal-status status-' + section.status;
72942
+ const statusBadge = document.getElementById("modal-status-badge");
72943
+ statusBadge.textContent = getColumnTitle(section.column);
72944
+ statusBadge.className = "modal-status status-" + section.column;
72429
72945
 
72430
- document.getElementById('modal-content').value = section.content;
72431
- document.getElementById('modal-overlay').classList.remove('hidden');
72432
- setTimeout(() => document.getElementById('modal-content').focus(), 50);
72946
+ document.getElementById("modal-content").value = section.content;
72947
+ document.getElementById("modal-overlay").classList.remove("hidden");
72948
+ setTimeout(() => document.getElementById("modal-content").focus(), 50);
72433
72949
  }
72434
72950
 
72435
72951
  function closeModal() {
72436
- document.getElementById('modal-overlay').classList.add('hidden');
72952
+ document.getElementById("modal-overlay").classList.add("hidden");
72437
72953
  modalState = null;
72438
72954
  }
72439
72955
 
72440
72956
  async function saveModal() {
72441
- if (!modalState) return;
72957
+ if (!modalState) {
72958
+ return;
72959
+ }
72442
72960
 
72443
- const content = document.getElementById('modal-content').value;
72444
- const saveBtn = document.querySelector('.btn-save');
72445
- saveBtn.disabled = true;
72446
- saveBtn.textContent = 'Saving\\u2026';
72961
+ const content = document.getElementById("modal-content").value;
72962
+ const saveButton = document.getElementById("save-button");
72963
+ saveButton.disabled = true;
72964
+ saveButton.textContent = "Saving...";
72447
72965
 
72448
72966
  try {
72449
- const res = await fetch('/api/prompts/update', {
72450
- method: 'PUT',
72451
- headers: { 'Content-Type': 'application/json' },
72967
+ const response = await fetch("/api/prompts/update", {
72968
+ method: "PUT",
72969
+ headers: { "Content-Type": "application/json" },
72452
72970
  body: JSON.stringify({
72453
- filePath: modalState.filePath,
72971
+ filePath: modalState.filePath,
72454
72972
  sectionIndex: modalState.sectionIndex,
72455
72973
  content,
72456
72974
  }),
72457
72975
  });
72458
- if (!res.ok) throw new Error('HTTP ' + res.status);
72976
+
72977
+ if (!response.ok) {
72978
+ throw new Error("HTTP " + response.status);
72979
+ }
72980
+
72459
72981
  closeModal();
72460
- fetchPrompts();
72461
- } catch (e) {
72462
- showError('Save failed: ' + e.message);
72982
+ await fetchPrompts();
72983
+ } catch (error) {
72984
+ showError("Save failed: " + error.message);
72463
72985
  } finally {
72464
- saveBtn.disabled = false;
72465
- saveBtn.textContent = 'Save';
72986
+ saveButton.disabled = false;
72987
+ saveButton.textContent = "Save";
72466
72988
  }
72467
72989
  }
72468
72990
 
72469
- document.getElementById('toggle-btn').onclick = async () => {
72991
+ document.getElementById("toggle-btn").onclick = async () => {
72470
72992
  try {
72471
- const endpoint = lastPauseState === 'RUNNING' ? '/api/pause' : '/api/resume';
72472
- await fetch(endpoint, { method: 'POST' });
72993
+ const endpoint = lastPauseState === "RUNNING" ? "/api/pause" : "/api/resume";
72994
+ await fetch(endpoint, { method: "POST" });
72473
72995
  await fetchStatus();
72474
- } catch (e) {
72475
- showError('Toggle failed: ' + e.message);
72996
+ } catch (error) {
72997
+ showError("Toggle failed: " + error.message);
72476
72998
  }
72477
72999
  };
72478
73000
 
72479
- document.addEventListener('keydown', (e) => {
72480
- if (e.key === 'Escape') { closeModal(); return; }
72481
- if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { saveModal(); return; }
73001
+ document.getElementById("modal-close").onclick = closeModal;
73002
+ document.getElementById("cancel-button").onclick = closeModal;
73003
+ document.getElementById("save-button").onclick = saveModal;
73004
+
73005
+ document.addEventListener("keydown", (event) => {
73006
+ if (event.key === "Escape") {
73007
+ closeModal();
73008
+ return;
73009
+ }
73010
+ if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
73011
+ saveModal();
73012
+ }
72482
73013
  });
72483
73014
 
72484
- document.getElementById('modal-overlay').addEventListener('click', (e) => {
72485
- if (e.target === document.getElementById('modal-overlay')) closeModal();
73015
+ document.getElementById("modal-overlay").addEventListener("click", (event) => {
73016
+ if (event.target === document.getElementById("modal-overlay")) {
73017
+ closeModal();
73018
+ }
72486
73019
  });
72487
73020
 
73021
+ renderBoardSkeleton();
72488
73022
  fetchStatus();
72489
73023
  fetchPrompts();
72490
- setInterval(fetchStatus, 2000);
73024
+ setInterval(fetchStatus, 2000);
72491
73025
  setInterval(fetchPrompts, 5000);
72492
73026
  </script>
72493
73027
  </body>
72494
- </html>`;
73028
+ </html>
73029
+ `;
72495
73030
  // Note: [🟡] Code for CLI command [coder server](scripts/run-codex-prompts/server/coderServerHtml.ts) should never be published outside of `@promptbook/cli`
72496
73031
  // Note: Keep in sync with apps/coder-server/index.html
72497
73032
 
@@ -72501,6 +73036,12 @@
72501
73036
  * @private internal constant of `ptbk coder server`
72502
73037
  */
72503
73038
  const PROMPTS_DIRECTORY_NAME = 'prompts';
73039
+ /**
73040
+ * Directory containing human-verified prompt files, relative to `prompts/`.
73041
+ *
73042
+ * @private internal constant of `ptbk coder server`
73043
+ */
73044
+ const FINISHED_PROMPTS_DIRECTORY_NAME = 'done';
72504
73045
  /**
72505
73046
  * Starts the lightweight HTTP server that serves the coder kanban UI and REST API.
72506
73047
  *
@@ -72514,24 +73055,29 @@
72514
73055
  *
72515
73056
  * @private internal utility of `ptbk coder server`
72516
73057
  */
72517
- function startCoderHttpServer(port) {
73058
+ function startCoderHttpServer(options) {
73059
+ const { port, minimumPriority, serverUrl, uiState } = options;
72518
73060
  const promptsDir = path.join(process.cwd(), PROMPTS_DIRECTORY_NAME);
72519
- const server = http.createServer(async (req, res) => {
73061
+ const server = http.createServer(async (request, response) => {
72520
73062
  try {
72521
- await handleRequest(req, res, promptsDir);
73063
+ await handleRequest(request, response, {
73064
+ promptsDir,
73065
+ minimumPriority,
73066
+ uiState,
73067
+ });
72522
73068
  }
72523
73069
  catch (error) {
72524
73070
  const message = error instanceof Error ? error.message : String(error);
72525
73071
  console.error(colors__default["default"].red(`Coder server request error: ${message}`));
72526
- if (!res.headersSent) {
72527
- res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
73072
+ if (!response.headersSent) {
73073
+ response.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
72528
73074
  }
72529
- res.end('Internal server error');
73075
+ response.end('Internal server error');
72530
73076
  }
72531
73077
  });
72532
73078
  server.listen(port, () => {
72533
73079
  console.info(_spaceTrim.spaceTrim(`
72534
- Coder server running at ${colors__default["default"].cyan(`http://localhost:${port}`)}
73080
+ Coder server running at ${colors__default["default"].cyan(serverUrl)}
72535
73081
  Open the URL above in your browser to see the kanban board.
72536
73082
  Press ${colors__default["default"].bold('p')} to pause / resume the agent runner.
72537
73083
  `));
@@ -72545,51 +73091,57 @@
72545
73091
  /**
72546
73092
  * Routes one HTTP request to the appropriate handler.
72547
73093
  */
72548
- async function handleRequest(req, res, promptsDir) {
72549
- const urlPath = new URL(req.url || '/', `http://localhost`).pathname;
72550
- const method = (req.method || 'GET').toUpperCase();
73094
+ async function handleRequest(request, response, options) {
73095
+ const { promptsDir, minimumPriority, uiState } = options;
73096
+ const urlPath = new URL(request.url || '/', `http://localhost`).pathname;
73097
+ const method = (request.method || 'GET').toUpperCase();
72551
73098
  // Serve the kanban UI
72552
73099
  if (urlPath === '/' && method === 'GET') {
72553
- res.writeHead(200, {
73100
+ response.writeHead(200, {
72554
73101
  'Content-Type': 'text/html; charset=utf-8',
72555
73102
  'Cache-Control': 'no-cache',
72556
73103
  });
72557
- res.end(CODER_SERVER_HTML);
73104
+ response.end(CODER_SERVER_HTML);
72558
73105
  return;
72559
73106
  }
72560
73107
  // GET /api/status
72561
73108
  if (urlPath === '/api/status' && method === 'GET') {
72562
- res.writeHead(200, jsonHeaders());
72563
- res.end(JSON.stringify({
73109
+ response.writeHead(200, jsonHeaders());
73110
+ response.end(JSON.stringify({
72564
73111
  pauseState: getPauseState(),
72565
73112
  pauseTargetLabel: getPauseTargetLabel(),
73113
+ runState: uiState ? buildCoderServerRunState(uiState) : undefined,
72566
73114
  }));
72567
73115
  return;
72568
73116
  }
72569
73117
  // GET /api/prompts
72570
73118
  if (urlPath === '/api/prompts' && method === 'GET') {
72571
- const promptData = await loadPromptsForApi(promptsDir);
72572
- res.writeHead(200, jsonHeaders());
72573
- res.end(JSON.stringify(promptData));
73119
+ const promptData = await loadPromptsForApi({
73120
+ promptsDir,
73121
+ minimumPriority,
73122
+ uiState,
73123
+ });
73124
+ response.writeHead(200, jsonHeaders());
73125
+ response.end(JSON.stringify(promptData));
72574
73126
  return;
72575
73127
  }
72576
73128
  // POST /api/pause
72577
73129
  if (urlPath === '/api/pause' && method === 'POST') {
72578
73130
  requestPause();
72579
- res.writeHead(200, jsonHeaders());
72580
- res.end(JSON.stringify({ pauseState: getPauseState() }));
73131
+ response.writeHead(200, jsonHeaders());
73132
+ response.end(JSON.stringify({ pauseState: getPauseState() }));
72581
73133
  return;
72582
73134
  }
72583
73135
  // POST /api/resume
72584
73136
  if (urlPath === '/api/resume' && method === 'POST') {
72585
73137
  requestResume();
72586
- res.writeHead(200, jsonHeaders());
72587
- res.end(JSON.stringify({ pauseState: getPauseState() }));
73138
+ response.writeHead(200, jsonHeaders());
73139
+ response.end(JSON.stringify({ pauseState: getPauseState() }));
72588
73140
  return;
72589
73141
  }
72590
73142
  // PUT /api/prompts/update
72591
73143
  if (urlPath === '/api/prompts/update' && method === 'PUT') {
72592
- const body = await readRequestBody(req);
73144
+ const body = await readRequestBody(request);
72593
73145
  const parsed = JSON.parse(body);
72594
73146
  if (typeof parsed.filePath !== 'string' ||
72595
73147
  typeof parsed.sectionIndex !== 'number' ||
@@ -72600,49 +73152,85 @@
72600
73152
  Expected: \`{ filePath: string, sectionIndex: number, content: string }\`
72601
73153
  `));
72602
73154
  }
72603
- await updatePromptSection(parsed.filePath, parsed.sectionIndex, parsed.content);
72604
- res.writeHead(200, jsonHeaders());
72605
- res.end(JSON.stringify({ success: true }));
73155
+ const promptFilePath = resolveEditablePromptFilePath(parsed.filePath, promptsDir);
73156
+ const isUpdated = await updatePromptSection(promptFilePath, parsed.sectionIndex, parsed.content);
73157
+ if (isUpdated) {
73158
+ await commitPromptEdit(promptFilePath, parsed.sectionIndex);
73159
+ }
73160
+ response.writeHead(200, jsonHeaders());
73161
+ response.end(JSON.stringify({ success: true, committed: isUpdated }));
72606
73162
  return;
72607
73163
  }
72608
- res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
72609
- res.end('Not found');
73164
+ response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
73165
+ response.end('Not found');
72610
73166
  }
72611
73167
  /**
72612
73168
  * Loads all prompt files and converts them to the API response shape.
72613
73169
  */
72614
- async function loadPromptsForApi(promptsDir) {
73170
+ async function loadPromptsForApi(options) {
73171
+ const { promptsDir, minimumPriority, uiState } = options;
73172
+ const promptFiles = await loadPromptFilesSafely(promptsDir);
73173
+ const finishedPromptFiles = await loadPromptFilesSafely(path.join(promptsDir, FINISHED_PROMPTS_DIRECTORY_NAME));
73174
+ return buildCoderServerPromptFileResponses({
73175
+ promptFiles,
73176
+ finishedPromptFiles,
73177
+ minimumPriority,
73178
+ uiState,
73179
+ });
73180
+ }
73181
+ /**
73182
+ * Loads prompt files from a directory that may not exist yet.
73183
+ */
73184
+ async function loadPromptFilesSafely(promptsDir) {
72615
73185
  try {
72616
- const promptFiles = await loadPromptFiles(promptsDir);
72617
- return promptFiles.map((file) => ({
72618
- filePath: file.path,
72619
- fileName: file.name,
72620
- sections: file.sections.map((section) => ({
72621
- index: section.index,
72622
- status: section.status,
72623
- priority: section.priority,
72624
- summary: buildPromptSummary(file, section),
72625
- content: buildCodexPrompt(file, section),
72626
- })),
72627
- }));
73186
+ return await loadPromptFiles(promptsDir);
72628
73187
  }
72629
73188
  catch (error) {
72630
- // Prompts directory may not exist yet; return empty list
72631
73189
  if (error.code === 'ENOENT') {
72632
73190
  return [];
72633
73191
  }
72634
73192
  throw error;
72635
73193
  }
72636
73194
  }
73195
+ /**
73196
+ * Resolves and validates one editable prompt file path received from the browser.
73197
+ */
73198
+ function resolveEditablePromptFilePath(filePath, promptsDir) {
73199
+ const promptFilePath = path.resolve(filePath);
73200
+ const promptsDirectoryPath = path.resolve(promptsDir);
73201
+ const relativePromptFilePath = path.relative(promptsDirectoryPath, promptFilePath);
73202
+ const isOutsidePromptsDirectory = relativePromptFilePath === '' ||
73203
+ relativePromptFilePath.startsWith('..') ||
73204
+ path.isAbsolute(relativePromptFilePath);
73205
+ if (isOutsidePromptsDirectory || !promptFilePath.toLowerCase().endsWith('.md')) {
73206
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
73207
+ Prompt file edits are limited to Markdown files inside \`${promptsDirectoryPath}\`.
73208
+
73209
+ Requested file:
73210
+ \`${filePath}\`
73211
+ `));
73212
+ }
73213
+ return promptFilePath;
73214
+ }
73215
+ /**
73216
+ * Commits a browser prompt edit to Git without sweeping unrelated staged files into the commit.
73217
+ */
73218
+ async function commitPromptEdit(promptFilePath, sectionIndex) {
73219
+ const relativePromptFilePath = path.relative(process.cwd(), promptFilePath).replace(/\\/gu, '/');
73220
+ await commitChanges(`Edit coder prompt ${relativePromptFilePath}#${sectionIndex + 1}`, {
73221
+ includePaths: [relativePromptFilePath],
73222
+ onlyPaths: [relativePromptFilePath],
73223
+ });
73224
+ }
72637
73225
  /**
72638
73226
  * Reads the full request body as a UTF-8 string.
72639
73227
  */
72640
- function readRequestBody(req) {
73228
+ function readRequestBody(request) {
72641
73229
  return new Promise((resolve, reject) => {
72642
73230
  const chunks = [];
72643
- req.on('data', (chunk) => chunks.push(chunk));
72644
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
72645
- req.on('error', reject);
73231
+ request.on('data', (chunk) => chunks.push(chunk));
73232
+ request.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
73233
+ request.on('error', reject);
72646
73234
  });
72647
73235
  }
72648
73236
  /**
@@ -72671,12 +73259,21 @@
72671
73259
  */
72672
73260
  async function runCodexPromptsServer(options) {
72673
73261
  const { port, ...runOptions } = options;
72674
- const serverHandle = startCoderHttpServer(port);
73262
+ const serverUrl = `http://localhost:${port}`;
73263
+ const uiState = new CoderRunUiState(moment__default["default"]());
73264
+ const serverHandle = startCoderHttpServer({
73265
+ port,
73266
+ minimumPriority: runOptions.priority,
73267
+ serverUrl,
73268
+ uiState,
73269
+ });
72675
73270
  console.info(colors__default["default"].gray('Starting prompt runner in server (keep-alive) mode…'));
72676
73271
  try {
72677
73272
  await runCodexPrompts({
72678
73273
  ...runOptions,
72679
73274
  keepAlive: true,
73275
+ serverUrl,
73276
+ uiState,
72680
73277
  });
72681
73278
  }
72682
73279
  finally {