@promptbook/cli 0.114.0-41 → 0.114.0-47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/default/copywriter.book +15 -0
- package/agents/default/developer.book +3 -0
- package/agents/default/lawyer.book +14 -0
- package/agents/default/planner.book +16 -0
- package/esm/index.es.js +4769 -809
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/find-fresh-emoji-tags/find-fresh-emoji-tags.d.ts +1 -1
- package/esm/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +2 -0
- package/esm/scripts/find-refactor-candidates/resolveRefactorCandidateProject.d.ts +3 -1
- package/esm/scripts/run-codex-prompts/common/CoderRunControlFeedback.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/buildCoderRunControlKeyBadge.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/listenForCoderRunControls.d.ts +3 -2
- package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +15 -5
- package/esm/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/captureLiveScriptOutput.d.ts +9 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/createScriptOutputLineReader.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/resolveShellHereDocumentDelimiter.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/git/coderCommitScope.d.ts +4 -1
- package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +5 -0
- package/esm/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +2 -1
- package/esm/scripts/run-codex-prompts/git/ensureWorkingTreeClean.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/isolation/copyCoderIsolationEnvironment.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/isolation/createCoderIsolationWorktree.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/main/findUnwrittenPrompts.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +11 -0
- package/esm/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +10 -2
- package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/cline/ClineRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/gemini/GeminiRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/github-copilot/GitHubCopilotRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/openai-codex/OpenAiCodexRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/opencode/OpencodeRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/qwen-code/QwenCodeRunner.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +3 -0
- package/esm/scripts/run-codex-prompts/team/CoderTeamRuntime.d.ts +87 -0
- package/esm/scripts/run-codex-prompts/team/coderTeamProtocol.d.ts +6 -0
- package/esm/scripts/run-codex-prompts/team/compileCoderTeamAgent.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +9 -0
- package/esm/scripts/run-codex-prompts/team/startCoderTeamBridge.d.ts +14 -0
- package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +16 -1
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +6 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiViewport.d.ts +5 -0
- package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -1
- package/esm/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/ui/output/CoderOutputEvent.d.ts +20 -0
- package/esm/scripts/run-codex-prompts/ui/output/CoderRunOutput.d.ts +48 -0
- package/esm/scripts/run-codex-prompts/ui/output/buildCoderOutputLines.d.ts +18 -0
- package/esm/scripts/run-codex-prompts/ui/output/normalizeCoderOutputRecord.d.ts +10 -0
- package/esm/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +2 -0
- package/esm/scripts/utils/prompts/isPromptDocumentationFile.d.ts +5 -0
- package/esm/scripts/utils/prompts/promptEmojiTags.d.ts +2 -0
- package/esm/scripts/verify-prompts/verify-prompts.d.ts +3 -0
- package/esm/src/book-2.0/agent-source/CreateAgentModelRequirementsOptions.d.ts +2 -0
- package/esm/src/cli/cli-commands/coder/add.d.ts +16 -0
- package/esm/src/cli/cli-commands/coder/agentCliOptions.d.ts +3 -2
- package/esm/src/cli/cli-commands/coder/augmentCoderAgentTeam.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/coderAgentRole.d.ts +16 -0
- package/esm/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +8 -7
- package/esm/src/cli/cli-commands/coder/ensureCoderDefaultAgentFiles.d.ts +8 -0
- package/esm/src/cli/cli-commands/coder/ensureCoderDefaultAgentFiles.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/ensureCoderRoleAgentFile.d.ts +12 -0
- package/esm/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/getCoderInitializationCommitPaths.d.ts +9 -0
- package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
- package/esm/src/cli/cli-commands/coder/plan.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/planning/assertPlanningCommitSafe.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder/planning/assertPlanningPrdCommitScope.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/planning/assertPlanningRuntimePath.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder/planning/createPlanningTerminal.d.ts +8 -0
- package/esm/src/cli/cli-commands/coder/planning/createPlanningTerminal.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/createPlanningWorkspace.d.ts +11 -0
- package/esm/src/cli/cli-commands/coder/planning/discussPlanningTurn.d.ts +15 -0
- package/esm/src/cli/cli-commands/coder/planning/fixtures/snapshotPlanningProject.d.ts +5 -0
- package/esm/src/cli/cli-commands/coder/planning/planningGitSync.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/planningPackagedCli.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/planningProtocol.d.ts +152 -0
- package/esm/src/cli/cli-commands/coder/planning/planningTeam.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/preparePlanningChanges.d.ts +16 -0
- package/esm/src/cli/cli-commands/coder/planning/readPlanningContext.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder/planning/resolvePlanningPath.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/planning/runPlanningHarness.d.ts +30 -0
- package/esm/src/cli/cli-commands/coder/planning/runPlanningHarness.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +21 -0
- package/esm/src/cli/cli-commands/coder/planning/runPlanningSession.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/planning/savePlanningChanges.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/printInitializationSummary.d.ts +2 -1
- package/esm/src/cli/cli-commands/coder/promptsReadmeTemplate.d.ts +13 -0
- package/esm/src/cli/cli-commands/coder/promptsReadmeTemplate.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/readCoderAgentBook.d.ts +8 -0
- package/esm/src/cli/cli-commands/coder/workspacePreflight.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +4 -2
- package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +1 -1
- package/esm/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +8 -1
- package/esm/src/cli/cli-commands/common/validateCoderRunOptions.d.ts +6 -0
- package/esm/src/cli/cli-commands/common/workspaceRepository.d.ts +36 -0
- package/esm/src/cli/cli-commands/common/workspaceRepository.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/workspaceRepositoryCliOptions.d.ts +11 -0
- package/esm/src/commitments/TEAM/TEAM.test.d.ts +1 -0
- package/esm/src/conversion/archive/loadJsZipModule.d.ts +10 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +4 -3
- package/src/book-2.0/agent-source/CreateAgentModelRequirementsOptions.ts +2 -0
- package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/applyCommitmentsToAgentModelRequirements.ts +8 -3
- package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.ts +6 -0
- package/src/cli/$initializePromptbookCliProgram.ts +2 -0
- package/src/cli/cli-commands/coder/add.ts +44 -14
- package/src/cli/cli-commands/coder/agentCliOptions.ts +9 -3
- package/src/cli/cli-commands/coder/augmentCoderAgentTeam.ts +152 -0
- package/src/cli/cli-commands/coder/coderAgentRole.ts +22 -0
- package/src/cli/cli-commands/coder/coderReferencedArtifacts.ts +9 -12
- package/src/cli/cli-commands/coder/ensureCoderDefaultAgentFiles.ts +90 -0
- package/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts +2 -21
- package/src/cli/cli-commands/coder/ensureCoderRoleAgentFile.ts +36 -0
- package/src/cli/cli-commands/coder/find-fresh-emoji-tags.ts +11 -2
- package/src/cli/cli-commands/coder/find-refactor-candidates.ts +10 -1
- package/src/cli/cli-commands/coder/find-unwritten.ts +10 -1
- package/src/cli/cli-commands/coder/generate-boilerplates.ts +12 -3
- package/src/cli/cli-commands/coder/getCoderInitializationCommitPaths.ts +42 -0
- package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +7 -2
- package/src/cli/cli-commands/coder/init.ts +88 -26
- package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +67 -21
- package/src/cli/cli-commands/coder/list.ts +7 -1
- package/src/cli/cli-commands/coder/ping.ts +8 -1
- package/src/cli/cli-commands/coder/plan.ts +127 -0
- package/src/cli/cli-commands/coder/planning/README.md +37 -0
- package/src/cli/cli-commands/coder/planning/assertPlanningCommitSafe.ts +23 -0
- package/src/cli/cli-commands/coder/planning/assertPlanningPrdCommitScope.ts +57 -0
- package/src/cli/cli-commands/coder/planning/assertPlanningRuntimePath.ts +34 -0
- package/src/cli/cli-commands/coder/planning/createPlanningTerminal.ts +57 -0
- package/src/cli/cli-commands/coder/planning/createPlanningWorkspace.ts +36 -0
- package/src/cli/cli-commands/coder/planning/discussPlanningTurn.ts +61 -0
- package/src/cli/cli-commands/coder/planning/fixtures/codex.cjs +54 -0
- package/src/cli/cli-commands/coder/planning/fixtures/conversation.json +70 -0
- package/src/cli/cli-commands/coder/planning/fixtures/snapshotPlanningProject.ts +17 -0
- package/src/cli/cli-commands/coder/planning/fixtures/terminal.cjs +26 -0
- package/src/cli/cli-commands/coder/planning/planningProtocol.ts +132 -0
- package/src/cli/cli-commands/coder/planning/preparePlanningChanges.ts +178 -0
- package/src/cli/cli-commands/coder/planning/readPlanningContext.ts +108 -0
- package/src/cli/cli-commands/coder/planning/resolvePlanningPath.ts +72 -0
- package/src/cli/cli-commands/coder/planning/runPlanningHarness.ts +249 -0
- package/src/cli/cli-commands/coder/planning/runPlanningSession.ts +243 -0
- package/src/cli/cli-commands/coder/planning/savePlanningChanges.ts +107 -0
- package/src/cli/cli-commands/coder/printInitializationSummary.ts +26 -3
- package/src/cli/cli-commands/coder/promptsReadmeTemplate.ts +358 -0
- package/src/cli/cli-commands/coder/readCoderAgentBook.ts +62 -0
- package/src/cli/cli-commands/coder/run.ts +37 -15
- package/src/cli/cli-commands/coder/server.ts +31 -10
- package/src/cli/cli-commands/coder/verify.ts +18 -1
- package/src/cli/cli-commands/coder.ts +7 -0
- package/src/cli/cli-commands/common/LocalAgentBookCollection.ts +56 -18
- package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +1 -1
- package/src/cli/cli-commands/common/createLocalAgentReferenceResolver.ts +7 -0
- package/src/cli/cli-commands/common/resolveLocalAgentSource.ts +39 -19
- package/src/cli/cli-commands/common/validateCoderRunOptions.ts +91 -0
- package/src/cli/cli-commands/common/workspaceRepository.md +46 -0
- package/src/cli/cli-commands/common/workspaceRepository.ts +265 -0
- package/src/cli/cli-commands/common/workspaceRepositoryCliOptions.ts +29 -0
- package/src/commitments/TEAM/TEAM.ts +26 -4
- package/src/conversion/archive/loadJsZipModule.ts +3 -1
- package/src/other/templates/getTemplatesPipelineCollection.ts +921 -830
- package/src/version.ts +2 -2
- package/src/versions.txt +7 -1
- package/umd/index.umd.js +4767 -809
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/find-fresh-emoji-tags/find-fresh-emoji-tags.d.ts +1 -1
- package/umd/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +2 -0
- package/umd/scripts/find-refactor-candidates/resolveRefactorCandidateProject.d.ts +3 -1
- package/umd/scripts/run-codex-prompts/common/CoderRunControlFeedback.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/buildCoderRunControlKeyBadge.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/listenForCoderRunControls.d.ts +3 -2
- package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +15 -5
- package/umd/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/captureLiveScriptOutput.d.ts +9 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/createScriptOutputLineReader.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/resolveShellHereDocumentDelimiter.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/git/coderCommitScope.d.ts +4 -1
- package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +5 -0
- package/umd/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +2 -1
- package/umd/scripts/run-codex-prompts/git/ensureWorkingTreeClean.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/isolation/copyCoderIsolationEnvironment.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/isolation/createCoderIsolationWorktree.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/main/findUnwrittenPrompts.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +11 -0
- package/umd/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +10 -2
- package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/cline/ClineRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/gemini/GeminiRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/github-copilot/GitHubCopilotRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/openai-codex/OpenAiCodexRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/opencode/OpencodeRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/qwen-code/QwenCodeRunner.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +3 -0
- package/umd/scripts/run-codex-prompts/team/CoderTeamRuntime.d.ts +87 -0
- package/umd/scripts/run-codex-prompts/team/coderTeamProtocol.d.ts +6 -0
- package/umd/scripts/run-codex-prompts/team/compileCoderTeamAgent.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +9 -0
- package/umd/scripts/run-codex-prompts/team/startCoderTeamBridge.d.ts +14 -0
- package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +16 -1
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +6 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiViewport.d.ts +5 -0
- package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -1
- package/umd/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/ui/output/CoderOutputEvent.d.ts +20 -0
- package/umd/scripts/run-codex-prompts/ui/output/CoderRunOutput.d.ts +48 -0
- package/umd/scripts/run-codex-prompts/ui/output/buildCoderOutputLines.d.ts +18 -0
- package/umd/scripts/run-codex-prompts/ui/output/normalizeCoderOutputRecord.d.ts +10 -0
- package/umd/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +2 -0
- package/umd/scripts/utils/prompts/isPromptDocumentationFile.d.ts +5 -0
- package/umd/scripts/utils/prompts/promptEmojiTags.d.ts +2 -0
- package/umd/scripts/verify-prompts/verify-prompts.d.ts +3 -0
- package/umd/src/book-2.0/agent-source/CreateAgentModelRequirementsOptions.d.ts +2 -0
- package/umd/src/cli/cli-commands/coder/add.d.ts +16 -0
- package/umd/src/cli/cli-commands/coder/agentCliOptions.d.ts +3 -2
- package/umd/src/cli/cli-commands/coder/augmentCoderAgentTeam.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/coderAgentRole.d.ts +16 -0
- package/umd/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +8 -7
- package/umd/src/cli/cli-commands/coder/ensureCoderDefaultAgentFiles.d.ts +8 -0
- package/umd/src/cli/cli-commands/coder/ensureCoderDefaultAgentFiles.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/ensureCoderRoleAgentFile.d.ts +12 -0
- package/umd/src/cli/cli-commands/coder/generate-boilerplates.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/getCoderInitializationCommitPaths.d.ts +9 -0
- package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
- package/umd/src/cli/cli-commands/coder/plan.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/planning/assertPlanningCommitSafe.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder/planning/assertPlanningPrdCommitScope.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/planning/assertPlanningRuntimePath.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder/planning/createPlanningTerminal.d.ts +8 -0
- package/umd/src/cli/cli-commands/coder/planning/createPlanningTerminal.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/createPlanningWorkspace.d.ts +11 -0
- package/umd/src/cli/cli-commands/coder/planning/discussPlanningTurn.d.ts +15 -0
- package/umd/src/cli/cli-commands/coder/planning/fixtures/snapshotPlanningProject.d.ts +5 -0
- package/umd/src/cli/cli-commands/coder/planning/planningGitSync.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/planningPackagedCli.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/planningProtocol.d.ts +152 -0
- package/umd/src/cli/cli-commands/coder/planning/planningTeam.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/preparePlanningChanges.d.ts +16 -0
- package/umd/src/cli/cli-commands/coder/planning/readPlanningContext.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder/planning/resolvePlanningPath.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/planning/runPlanningHarness.d.ts +30 -0
- package/umd/src/cli/cli-commands/coder/planning/runPlanningHarness.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +21 -0
- package/umd/src/cli/cli-commands/coder/planning/runPlanningSession.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/planning/savePlanningChanges.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/printInitializationSummary.d.ts +2 -1
- package/umd/src/cli/cli-commands/coder/promptsReadmeTemplate.d.ts +13 -0
- package/umd/src/cli/cli-commands/coder/promptsReadmeTemplate.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/readCoderAgentBook.d.ts +8 -0
- package/umd/src/cli/cli-commands/coder/workspacePreflight.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +4 -2
- package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +1 -1
- package/umd/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +8 -1
- package/umd/src/cli/cli-commands/common/validateCoderRunOptions.d.ts +6 -0
- package/umd/src/cli/cli-commands/common/workspaceRepository.d.ts +36 -0
- package/umd/src/cli/cli-commands/common/workspaceRepository.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/workspaceRepositoryCliOptions.d.ts +11 -0
- package/umd/src/commitments/TEAM/TEAM.test.d.ts +1 -0
- package/umd/src/conversion/archive/loadJsZipModule.d.ts +10 -0
- package/umd/src/version.d.ts +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { readFile, realpath } from 'fs/promises';
|
|
2
|
+
import { relative } from 'path';
|
|
3
|
+
import { isPromptToBeWritten } from '../../../../../scripts/run-codex-prompts/prompts/isPromptToBeWritten';
|
|
4
|
+
import { PTBK_CODER_IGNORE_MARKER } from '../../../../../scripts/run-codex-prompts/prompts/loadPromptFiles';
|
|
5
|
+
import { parsePromptFile } from '../../../../../scripts/run-codex-prompts/prompts/parsePromptFile';
|
|
6
|
+
import { formatPromptEmojiTag, getFreshPromptEmojiTags } from '../../../../../scripts/utils/prompts/promptEmojiTags';
|
|
7
|
+
import { NotAllowed } from '../../../../errors/NotAllowed';
|
|
8
|
+
import { spaceTrim } from '../../../../utils/organization/spaceTrim';
|
|
9
|
+
import { prepareCoderPrompt } from '../add';
|
|
10
|
+
import { resolvePlanningPath } from './resolvePlanningPath';
|
|
11
|
+
import type { PlanningProposal } from './planningProtocol';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A fully rendered change retained in memory until the user explicitly saves it.
|
|
15
|
+
* @private internal type of `coder plan`
|
|
16
|
+
*/
|
|
17
|
+
export type PlanningChange = {
|
|
18
|
+
readonly path: string;
|
|
19
|
+
readonly before: string | null;
|
|
20
|
+
readonly after: string;
|
|
21
|
+
readonly draft: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Prepares an entire proposal set without writing. Exact replacements preserve unrelated content and line endings.
|
|
26
|
+
* @private internal utility of `coder plan`
|
|
27
|
+
*/
|
|
28
|
+
export async function preparePlanningChanges(
|
|
29
|
+
projectPath: string,
|
|
30
|
+
proposals: ReadonlyArray<PlanningProposal>,
|
|
31
|
+
templateOption: string | undefined,
|
|
32
|
+
): Promise<PlanningChange[]> {
|
|
33
|
+
const canonicalProjectPath = await realpath(projectPath);
|
|
34
|
+
const createdCount = proposals.filter((proposal) => proposal.kind === 'create').length;
|
|
35
|
+
const { selectedEmojis } = createdCount
|
|
36
|
+
? await getFreshPromptEmojiTags({ count: createdCount, rootDir: projectPath, isCacheWriteEnabled: false })
|
|
37
|
+
: { selectedEmojis: [] };
|
|
38
|
+
const changes: PlanningChange[] = [];
|
|
39
|
+
let numberOffset = 0;
|
|
40
|
+
for (const proposal of proposals) {
|
|
41
|
+
if (proposal.kind === 'create') {
|
|
42
|
+
const prepared = await prepareCoderPrompt({
|
|
43
|
+
projectPath,
|
|
44
|
+
description: `${proposal.title}\n\n${proposal.body}`,
|
|
45
|
+
priority: proposal.priority,
|
|
46
|
+
templateOption,
|
|
47
|
+
numberOffset,
|
|
48
|
+
reservedEmojiTag: formatPromptEmojiTag(selectedEmojis[numberOffset]!),
|
|
49
|
+
});
|
|
50
|
+
numberOffset++;
|
|
51
|
+
const path = prepared.filePath.replace(/\\/gu, '/');
|
|
52
|
+
resolvePlanningPath(projectPath, path, true);
|
|
53
|
+
const draft = prepared.content.replace(/^\[ \]/u, '[-]');
|
|
54
|
+
const after = proposal.isReady ? prepared.content : draft;
|
|
55
|
+
const parsed = parsePromptFile(path, after);
|
|
56
|
+
if (
|
|
57
|
+
after.includes(PTBK_CODER_IGNORE_MARKER) ||
|
|
58
|
+
parsed.sections.length !== 1 ||
|
|
59
|
+
(proposal.isReady && isPromptToBeWritten(parsed, parsed.sections[0]!))
|
|
60
|
+
) {
|
|
61
|
+
throw new NotAllowed(
|
|
62
|
+
spaceTrim(
|
|
63
|
+
'A new proposal must contain one task without task separators or a queue ignore marker; unresolved placeholders require a draft. Split related tasks into separate proposals.',
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
changes.push({ path, before: null, after, draft });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const absolutePath = await realpath(resolvePlanningPath(projectPath, proposal.path, true));
|
|
71
|
+
const path = relative(canonicalProjectPath, absolutePath).replace(/\\/gu, '/');
|
|
72
|
+
const previous = changes.find((change) =>
|
|
73
|
+
process.platform === 'win32' ? change.path.toLowerCase() === path.toLowerCase() : change.path === path,
|
|
74
|
+
);
|
|
75
|
+
if (previous) {
|
|
76
|
+
throw new NotAllowed(
|
|
77
|
+
spaceTrim(
|
|
78
|
+
`Propose one exact replacement per file per preview: \`${path}\`. Further revisions can be saved in the next turn.`,
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const before = await readFile(absolutePath, 'utf-8');
|
|
83
|
+
if (before.includes(PTBK_CODER_IGNORE_MARKER)) {
|
|
84
|
+
throw new NotAllowed(
|
|
85
|
+
spaceTrim(
|
|
86
|
+
`\`${path}\` is excluded from the PRD queue. Its context and ignore marker must be preserved.`,
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const file = parsePromptFile(path, before);
|
|
91
|
+
const find = proposal.find.replace(/\r?\n/gu, file.eol);
|
|
92
|
+
const replacement = proposal.replace.replace(/\r?\n/gu, file.eol);
|
|
93
|
+
const start = before.indexOf(find);
|
|
94
|
+
if (start < 0 || before.indexOf(find, start + 1) !== -1) {
|
|
95
|
+
throw new NotAllowed(
|
|
96
|
+
spaceTrim(
|
|
97
|
+
`Edit in \`${path}\` must match one unique, unchanged text passage. Read it again before revising.`,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const startLine = before.slice(0, start).split(/\r?\n/u).length - 1;
|
|
102
|
+
const endLine = startLine + find.split(/\r?\n/u).length - 1;
|
|
103
|
+
const section = file.sections.find(
|
|
104
|
+
(candidate) => candidate.startLine <= startLine && candidate.endLine >= endLine,
|
|
105
|
+
);
|
|
106
|
+
if (!section || !['todo', 'not-ready'].includes(section.status)) {
|
|
107
|
+
throw new NotAllowed(
|
|
108
|
+
spaceTrim(
|
|
109
|
+
`Only pending or draft task bodies may be revised in \`${path}\`. Create new follow-up work as a new task.`,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const identityLineIndex = file.lines.findIndex(
|
|
114
|
+
(line, index) =>
|
|
115
|
+
index >= (section.statusLineIndex === undefined ? section.startLine : section.statusLineIndex + 1) &&
|
|
116
|
+
index <= section.endLine &&
|
|
117
|
+
line.trim() !== '',
|
|
118
|
+
);
|
|
119
|
+
if (startLine <= identityLineIndex || /^\s*---\s*$/mu.test(replacement)) {
|
|
120
|
+
throw new NotAllowed(
|
|
121
|
+
spaceTrim(
|
|
122
|
+
`Preserve task identity and lifecycle metadata in \`${path}\`; replace only the body of one task.`,
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
let after = before.slice(0, start) + replacement + before.slice(start + find.length);
|
|
127
|
+
if (after.includes(PTBK_CODER_IGNORE_MARKER)) {
|
|
128
|
+
throw new NotAllowed(spaceTrim(`An edit cannot change the queue ignore metadata in \`${path}\`.`));
|
|
129
|
+
}
|
|
130
|
+
const afterFile = parsePromptFile(path, after);
|
|
131
|
+
if (afterFile.sections.length !== file.sections.length) {
|
|
132
|
+
throw new NotAllowed(spaceTrim(`An edit cannot add or remove task sections in \`${path}\`.`));
|
|
133
|
+
}
|
|
134
|
+
if (proposal.isReady && isPromptToBeWritten(afterFile, afterFile.sections[section.index]!)) {
|
|
135
|
+
throw new NotAllowed(
|
|
136
|
+
spaceTrim(
|
|
137
|
+
`Unresolved placeholders in \`${path}\` require a draft; finish the entire task before marking it ready.`,
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const statusLine = section.statusLineIndex === undefined ? undefined : file.lines[section.statusLineIndex]!;
|
|
142
|
+
const lines = after.split(/\r?\n/u);
|
|
143
|
+
if (statusLine === undefined || section.statusLineIndex === undefined) {
|
|
144
|
+
if (section.status !== 'todo') {
|
|
145
|
+
throw new NotAllowed(
|
|
146
|
+
spaceTrim(
|
|
147
|
+
`Unsupported lifecycle metadata in \`${path}\` must be preserved; create a new task instead.`,
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const draftLines = [...lines];
|
|
152
|
+
draftLines.splice(identityLineIndex, 0, '[-]', '');
|
|
153
|
+
const draft = draftLines.join(file.eol);
|
|
154
|
+
changes.push({ path, before, after: proposal.isReady ? after : draft, draft });
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
// Ready tasks retain routing tokens and priority verbatim. Draft promotion preserves the remaining suffix.
|
|
158
|
+
if (proposal.isReady && section.status === 'not-ready') {
|
|
159
|
+
lines[section.statusLineIndex] = statusLine.replace(/^(\s*)\[(?:-|\.)\]/u, '$1[ ]');
|
|
160
|
+
after = lines.join(file.eol);
|
|
161
|
+
}
|
|
162
|
+
// The parser supports clean [-] with priority marks. Refuse to discard model/agent metadata to make a draft.
|
|
163
|
+
const isDraftConversionSupported = /^\s*\[(?: |-|\.)\][!\s]*$/u.test(statusLine);
|
|
164
|
+
const draftLines = [...lines];
|
|
165
|
+
if (isDraftConversionSupported)
|
|
166
|
+
draftLines[section.statusLineIndex] = statusLine.replace(/^(\s*)\[.\]/u, '$1[-]');
|
|
167
|
+
if (!proposal.isReady && !isDraftConversionSupported) {
|
|
168
|
+
throw new NotAllowed(
|
|
169
|
+
spaceTrim(
|
|
170
|
+
`Cannot convert routed task \`${path}\` into a draft without losing metadata. Keep the revision unsaved or create a separate draft task.`,
|
|
171
|
+
),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const draft = isDraftConversionSupported ? draftLines.join(file.eol) : '';
|
|
175
|
+
changes.push({ path, before, after: proposal.isReady ? after : draft, draft });
|
|
176
|
+
}
|
|
177
|
+
return changes;
|
|
178
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
// cspell:ignore fsmonitor textconv
|
|
3
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
4
|
+
import { relative } from 'path';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { NotAllowed } from '../../../../errors/NotAllowed';
|
|
7
|
+
import { spaceTrim } from '../../../../utils/organization/spaceTrim';
|
|
8
|
+
import type { PlanningRead } from './planningProtocol';
|
|
9
|
+
import { resolvePlanningPath } from './resolvePlanningPath';
|
|
10
|
+
|
|
11
|
+
/** Directories omitted from repository discovery; no links or hidden runtime state are followed. */
|
|
12
|
+
const IGNORED_DIRECTORIES = new Set(['node_modules', 'vendor', 'dist', 'build', 'coverage']);
|
|
13
|
+
/** Maximum bytes read from an individual repository file. */
|
|
14
|
+
const MAX_FILE_BYTES = 524288;
|
|
15
|
+
/** Fixed Git argument vectors prevent shell, diff-driver, pager, and filesystem-monitor execution. */
|
|
16
|
+
const READ_ONLY_GIT_ARGUMENTS = {
|
|
17
|
+
status: ['status', '--short'],
|
|
18
|
+
diff: ['diff', '--no-ext-diff', '--no-textconv', '--stat'],
|
|
19
|
+
log: ['log', '-10', '--format=%h %s'],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Executes only the read operations in the planning protocol. Never evaluates a command from the model.
|
|
24
|
+
* @private internal utility of `coder plan`
|
|
25
|
+
*/
|
|
26
|
+
export async function readPlanningContext(projectPath: string, request: PlanningRead): Promise<string> {
|
|
27
|
+
if (request.kind === 'git') {
|
|
28
|
+
const result = await promisify(execFile)(
|
|
29
|
+
'git',
|
|
30
|
+
[
|
|
31
|
+
'--no-pager',
|
|
32
|
+
'--no-optional-locks',
|
|
33
|
+
'-c',
|
|
34
|
+
'core.fsmonitor=false',
|
|
35
|
+
...READ_ONLY_GIT_ARGUMENTS[request.operation],
|
|
36
|
+
],
|
|
37
|
+
{
|
|
38
|
+
cwd: projectPath,
|
|
39
|
+
timeout: 10000,
|
|
40
|
+
maxBuffer: MAX_FILE_BYTES,
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
return result.stdout;
|
|
46
|
+
}
|
|
47
|
+
const path = resolvePlanningPath(projectPath, request.path);
|
|
48
|
+
if (request.kind === 'read') {
|
|
49
|
+
const content = await readPlanningText(path);
|
|
50
|
+
return content
|
|
51
|
+
.split(/\r?\n/u)
|
|
52
|
+
.slice(request.startLine - 1, request.startLine - 1 + request.lineCount)
|
|
53
|
+
.map((line, index) => `${request.startLine + index}: ${line}`)
|
|
54
|
+
.join('\n');
|
|
55
|
+
}
|
|
56
|
+
const files: string[] = [];
|
|
57
|
+
await listPlanningFiles(projectPath, request.path, files);
|
|
58
|
+
if (request.kind === 'list') {
|
|
59
|
+
return (
|
|
60
|
+
files.join('\n') +
|
|
61
|
+
(files.length >= 2000 ? '\n[Listing limited to 2000 paths; choose a narrower directory.]' : '')
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const matches: string[] = [];
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
const content = await readPlanningText(resolvePlanningPath(projectPath, file)).catch(() => '');
|
|
67
|
+
for (const [index, line] of content.split(/\r?\n/u).entries()) {
|
|
68
|
+
if (line.includes(request.query)) {
|
|
69
|
+
matches.push(`${file}:${index + 1}: ${line.slice(0, 500)}`);
|
|
70
|
+
}
|
|
71
|
+
if (matches.length >= 100) {
|
|
72
|
+
return matches.join('\n') + '\n[Search limited to 100 matches.]';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return matches.join('\n') || 'No matches.';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Reads bounded regular text, refusing binary or oversized files. */
|
|
80
|
+
async function readPlanningText(path: string): Promise<string> {
|
|
81
|
+
const stats = await stat(path);
|
|
82
|
+
if (!stats.isFile() || stats.size > MAX_FILE_BYTES) {
|
|
83
|
+
throw new NotAllowed(spaceTrim('Choose a regular text file smaller than 512 KiB.'));
|
|
84
|
+
}
|
|
85
|
+
const content = await readFile(path, 'utf-8');
|
|
86
|
+
if (content.includes('\0')) {
|
|
87
|
+
throw new NotAllowed(spaceTrim('Binary files cannot be read as planning context.'));
|
|
88
|
+
}
|
|
89
|
+
return content;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Walks a bounded, deterministic directory listing, skipping links before traversing them. */
|
|
93
|
+
async function listPlanningFiles(projectPath: string, directory: string, files: string[]): Promise<void> {
|
|
94
|
+
const path = resolvePlanningPath(projectPath, directory);
|
|
95
|
+
if ((await stat(path)).isFile()) {
|
|
96
|
+
files.push(relative(projectPath, path).replace(/\\/gu, '/'));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
for (const entry of (await readdir(path, { withFileTypes: true })).sort((left, right) =>
|
|
100
|
+
left.name.localeCompare(right.name),
|
|
101
|
+
)) {
|
|
102
|
+
if (files.length >= 2000) return;
|
|
103
|
+
if (entry.isSymbolicLink() || entry.name.startsWith('.') || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
104
|
+
const entryPath = `${directory}/${entry.name}`;
|
|
105
|
+
if (entry.isDirectory()) await listPlanningFiles(projectPath, entryPath, files);
|
|
106
|
+
else if (entry.isFile()) files.push(entryPath.replace(/^\.\//u, ''));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { lstatSync, realpathSync } from 'fs';
|
|
2
|
+
import { isAbsolute, join, relative, resolve } from 'path';
|
|
3
|
+
import { NotAllowed } from '../../../../errors/NotAllowed';
|
|
4
|
+
import { spaceTrim } from '../../../../utils/organization/spaceTrim';
|
|
5
|
+
|
|
6
|
+
/** Control characters cannot be model-supplied filesystem path components. */
|
|
7
|
+
const FIRST_PRINTABLE_CHARACTER_CODE = 32;
|
|
8
|
+
|
|
9
|
+
/** Windows devices are not ordinary files, including when they carry a Markdown extension. */
|
|
10
|
+
const WINDOWS_DEVICE_NAME_PATTERN = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu;
|
|
11
|
+
|
|
12
|
+
/** Repository guidance is never an editable PRD, even when stored alongside the queue. */
|
|
13
|
+
const PLANNING_CONTEXT_FILE_NAMES = new Set(['agents.md', 'claude.md', 'gemini.md', 'readme.md', 'skill.md']);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves a repository path without traversing symlinks, junctions, device paths, or parent escapes.
|
|
17
|
+
* Writes additionally require an ordinary, singly linked PRD Markdown file in the active prompts directory.
|
|
18
|
+
* The queue loader reads only this directory; archives, templates and reusable context remain read-only.
|
|
19
|
+
* @private internal execution boundary of `coder plan`
|
|
20
|
+
*/
|
|
21
|
+
export function resolvePlanningPath(projectPath: string, path: string, isWriting = false): string {
|
|
22
|
+
const root = realpathSync(projectPath);
|
|
23
|
+
const normalizedPath = path.replace(/\\/gu, '/');
|
|
24
|
+
if (isAbsolute(path) || normalizedPath.includes(':') || normalizedPath.split('/').includes('..')) {
|
|
25
|
+
throw new NotAllowed(spaceTrim(`Planning path \`${path}\` must stay inside the project.`));
|
|
26
|
+
}
|
|
27
|
+
const segments = normalizedPath.split('/').filter((part) => part && part !== '.');
|
|
28
|
+
if (
|
|
29
|
+
segments.some(
|
|
30
|
+
(part) =>
|
|
31
|
+
part.startsWith('.') ||
|
|
32
|
+
WINDOWS_DEVICE_NAME_PATTERN.test(part) ||
|
|
33
|
+
(isWriting && /[$`%[\]]/u.test(part)) ||
|
|
34
|
+
/[<>"|?*]/u.test(part) ||
|
|
35
|
+
[...part].some((character) => character.charCodeAt(0) < FIRST_PRINTABLE_CHARACTER_CODE) ||
|
|
36
|
+
/[. ]$/u.test(part),
|
|
37
|
+
)
|
|
38
|
+
) {
|
|
39
|
+
throw new NotAllowed(spaceTrim(`Planning cannot access hidden or special path \`${path}\`.`));
|
|
40
|
+
}
|
|
41
|
+
if (
|
|
42
|
+
isWriting &&
|
|
43
|
+
(segments[0] !== 'prompts' ||
|
|
44
|
+
segments.length !== 2 ||
|
|
45
|
+
!path.toLowerCase().endsWith('.md') ||
|
|
46
|
+
PLANNING_CONTEXT_FILE_NAMES.has(segments[1]!.toLowerCase()))
|
|
47
|
+
) {
|
|
48
|
+
throw new NotAllowed(spaceTrim(`Planning can only author PRD Markdown files in \`prompts/\`: \`${path}\`.`));
|
|
49
|
+
}
|
|
50
|
+
let current = root;
|
|
51
|
+
for (const [index, segment] of segments.entries()) {
|
|
52
|
+
current = join(current, segment);
|
|
53
|
+
try {
|
|
54
|
+
const stats = lstatSync(current);
|
|
55
|
+
if (
|
|
56
|
+
stats.isSymbolicLink() ||
|
|
57
|
+
(!stats.isDirectory() && !stats.isFile()) ||
|
|
58
|
+
(isWriting && stats.isFile() && stats.nlink !== 1)
|
|
59
|
+
) {
|
|
60
|
+
throw new NotAllowed(spaceTrim(`Planning refuses linked or special path \`${path}\`.`));
|
|
61
|
+
}
|
|
62
|
+
if (relative(root, realpathSync(current)).startsWith('..')) {
|
|
63
|
+
throw new NotAllowed(spaceTrim(`Planning path \`${path}\` escapes the project.`));
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT' || index !== segments.length - 1 || !isWriting) {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return resolve(current);
|
|
72
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { stat } from 'fs/promises';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
5
|
+
import { $terminateLoggedBashProcessTree } from '../../../../../scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree';
|
|
6
|
+
import { buildCodexUsageFromOutput } from '../../../../../scripts/run-codex-prompts/runners/openai-codex/buildCodexUsageFromOutput';
|
|
7
|
+
import type { Usage } from '../../../../execution/Usage';
|
|
8
|
+
import { UNCERTAIN_USAGE } from '../../../../execution/utils/usage-constants';
|
|
9
|
+
import { PipelineExecutionError } from '../../../../errors/PipelineExecutionError';
|
|
10
|
+
import { NotAllowed } from '../../../../errors/NotAllowed';
|
|
11
|
+
import { spaceTrim } from '../../../../utils/organization/spaceTrim';
|
|
12
|
+
import { $resolveHarnessCommandPath } from '../../common/harness/$resolveHarnessCommandPath';
|
|
13
|
+
import { getHarnessDefinition } from '../../common/harness/HarnessDefinition';
|
|
14
|
+
import { HARNESS_DEFAULT_MODELS } from '../../common/harness/HARNESS_DEFAULT_MODELS';
|
|
15
|
+
import type { NormalizedPromptRunnerSelectionCliOptions } from '../../common/promptRunnerCliOptions';
|
|
16
|
+
import { PLANNING_RESPONSE_SCHEMA_FILENAME } from './createPlanningWorkspace';
|
|
17
|
+
|
|
18
|
+
/** Capabilities excluded from the planning process independently of any Book or prompt. */
|
|
19
|
+
const DISABLED_PLANNING_FEATURES = [
|
|
20
|
+
'shell_tool',
|
|
21
|
+
'unified_exec',
|
|
22
|
+
'multi_agent',
|
|
23
|
+
'multi_agent_v2',
|
|
24
|
+
'apps',
|
|
25
|
+
'plugins',
|
|
26
|
+
'hooks',
|
|
27
|
+
'browser_use',
|
|
28
|
+
'computer_use',
|
|
29
|
+
'image_generation',
|
|
30
|
+
'code_mode',
|
|
31
|
+
'code_mode_host',
|
|
32
|
+
'request_permissions_tool',
|
|
33
|
+
'memories',
|
|
34
|
+
'skill_mcp_dependency_install',
|
|
35
|
+
'remote_plugin',
|
|
36
|
+
'workspace_dependencies',
|
|
37
|
+
'shell_snapshot',
|
|
38
|
+
'skill_search',
|
|
39
|
+
'tool_suggest',
|
|
40
|
+
];
|
|
41
|
+
/** Maximum duration of one inference; errors never enter the implementation runner's retry loop. */
|
|
42
|
+
const PLANNING_TURN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
43
|
+
|
|
44
|
+
/** Bounded diagnostic tail retained when a harness fails. */
|
|
45
|
+
const MAX_DIAGNOSTIC_LENGTH = 8000;
|
|
46
|
+
|
|
47
|
+
/** Maximum raw output, including malformed streams that never terminate a JSON line. */
|
|
48
|
+
const MAX_PLANNING_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parameters for one tool-restricted inference. Repository reads and PRD writes are host operations.
|
|
52
|
+
* @private internal type of `coder plan`
|
|
53
|
+
*/
|
|
54
|
+
export type PlanningHarnessOptions = NormalizedPromptRunnerSelectionCliOptions & {
|
|
55
|
+
readonly workspacePath: string;
|
|
56
|
+
readonly prompt: string;
|
|
57
|
+
readonly signal: AbortSignal;
|
|
58
|
+
/** Reports available usage from this inference once, including turns whose final answer is malformed. */
|
|
59
|
+
readonly onUsage?: (usage: Usage) => void;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Rejects harnesses without a planning execution policy before any Git sync or process launch.
|
|
64
|
+
* @private internal utility of `coder plan`
|
|
65
|
+
*/
|
|
66
|
+
export function assertPlanningHarnessSupported(harness: string | undefined): void {
|
|
67
|
+
if (harness !== 'openai-codex') {
|
|
68
|
+
throw new NotAllowed(
|
|
69
|
+
spaceTrim(
|
|
70
|
+
'Planning currently supports `--harness openai-codex` only. Other harnesses have no enforced planning tool boundary.',
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Constructs a fail-closed Codex invocation. User config, rules, project config discovery, native shell,
|
|
78
|
+
* connectors, hooks and delegation cannot expand the planning tool set. Read-only sandboxing also denies patches.
|
|
79
|
+
* Older Codex versions that do not support these switches fail instead of using the implementation runner.
|
|
80
|
+
* @private internal utility of `coder plan`
|
|
81
|
+
*/
|
|
82
|
+
export function buildPlanningHarnessArguments(options: PlanningHarnessOptions): string[] {
|
|
83
|
+
const model = options.model === 'default' ? undefined : options.model || HARNESS_DEFAULT_MODELS['openai-codex'];
|
|
84
|
+
return [
|
|
85
|
+
'--ask-for-approval',
|
|
86
|
+
'never',
|
|
87
|
+
'exec',
|
|
88
|
+
'--sandbox',
|
|
89
|
+
'read-only',
|
|
90
|
+
'--ephemeral',
|
|
91
|
+
'--ignore-user-config',
|
|
92
|
+
'--ignore-rules',
|
|
93
|
+
'--skip-git-repo-check',
|
|
94
|
+
'--json',
|
|
95
|
+
'--output-schema',
|
|
96
|
+
join(options.workspacePath, PLANNING_RESPONSE_SCHEMA_FILENAME),
|
|
97
|
+
'--color',
|
|
98
|
+
'never',
|
|
99
|
+
'-C',
|
|
100
|
+
options.workspacePath,
|
|
101
|
+
'-c',
|
|
102
|
+
'project_root_markers=[".git"]',
|
|
103
|
+
'-c',
|
|
104
|
+
'web_search="disabled"',
|
|
105
|
+
'-c',
|
|
106
|
+
'mcp_servers={}',
|
|
107
|
+
'-c',
|
|
108
|
+
'project_doc_max_bytes=0',
|
|
109
|
+
...DISABLED_PLANNING_FEATURES.flatMap((feature) => ['--disable', feature]),
|
|
110
|
+
...(!options.allowCredits ? ['-c', 'forced_login_method="chatgpt"'] : []),
|
|
111
|
+
...(model ? ['--model', model] : []),
|
|
112
|
+
...(options.thinkingLevel ? ['-c', `model_reasoning_effort=${JSON.stringify(options.thinkingLevel)}`] : []),
|
|
113
|
+
'-',
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Runs a bounded inference with no shell interpolation and consumes only its final JSON answer.
|
|
119
|
+
* @private internal utility of `coder plan`
|
|
120
|
+
*/
|
|
121
|
+
export async function runPlanningHarness(options: PlanningHarnessOptions): Promise<string> {
|
|
122
|
+
assertPlanningHarnessSupported(options.agentName);
|
|
123
|
+
const command = await resolvePlanningHarnessCommand();
|
|
124
|
+
if (options.signal.aborted) throw new NotAllowed(spaceTrim('Planning cancelled.'));
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const environment = { ...process.env };
|
|
127
|
+
// Config is isolated, but Codex still uses its normal authentication store. Do not source project .env files.
|
|
128
|
+
if (!options.allowCredits) {
|
|
129
|
+
delete environment.OPENAI_API_KEY;
|
|
130
|
+
delete environment.OPENAI_BASE_URL;
|
|
131
|
+
delete environment.CODEX_API_KEY;
|
|
132
|
+
}
|
|
133
|
+
const child = spawn(command.executable, [...command.arguments, ...buildPlanningHarnessArguments(options)], {
|
|
134
|
+
cwd: options.workspacePath,
|
|
135
|
+
env: environment,
|
|
136
|
+
stdio: 'pipe',
|
|
137
|
+
shell: false,
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
detached: process.platform !== 'win32',
|
|
140
|
+
});
|
|
141
|
+
const reader = createInterface({ input: child.stdout });
|
|
142
|
+
let answer = '';
|
|
143
|
+
let diagnostics = '';
|
|
144
|
+
let outputSize = 0;
|
|
145
|
+
let isSettled = false;
|
|
146
|
+
let isHarnessFailed = false;
|
|
147
|
+
let usageEvent: string | undefined;
|
|
148
|
+
/** Finishes exactly once and terminates only this harness process tree. */
|
|
149
|
+
const finish = (error?: Error): void => {
|
|
150
|
+
if (isSettled) return;
|
|
151
|
+
isSettled = true;
|
|
152
|
+
clearTimeout(timeout);
|
|
153
|
+
options.signal.removeEventListener('abort', cancel);
|
|
154
|
+
reader.close();
|
|
155
|
+
options.onUsage?.(usageEvent ? buildCodexUsageFromOutput(usageEvent, options.model) : UNCERTAIN_USAGE);
|
|
156
|
+
if (error) {
|
|
157
|
+
if (process.platform !== 'win32' && child.pid) {
|
|
158
|
+
try {
|
|
159
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
160
|
+
} catch {
|
|
161
|
+
child.kill('SIGKILL');
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
$terminateLoggedBashProcessTree(child);
|
|
165
|
+
}
|
|
166
|
+
reject(error);
|
|
167
|
+
} else resolve(answer);
|
|
168
|
+
};
|
|
169
|
+
/** Cancels inference before any host write can be proposed. */
|
|
170
|
+
const cancel = (): void => finish(new NotAllowed(spaceTrim('Planning cancelled. Saved PRDs are unchanged.')));
|
|
171
|
+
const timeout = setTimeout(
|
|
172
|
+
() =>
|
|
173
|
+
finish(
|
|
174
|
+
new PipelineExecutionError(spaceTrim('Planner harness timed out. No proposed changes were saved.')),
|
|
175
|
+
),
|
|
176
|
+
PLANNING_TURN_TIMEOUT_MS,
|
|
177
|
+
);
|
|
178
|
+
options.signal.addEventListener('abort', cancel, { once: true });
|
|
179
|
+
child.stdout.on('data', (data: Buffer) => {
|
|
180
|
+
outputSize += data.length;
|
|
181
|
+
if (outputSize > MAX_PLANNING_OUTPUT_BYTES) {
|
|
182
|
+
finish(new PipelineExecutionError(spaceTrim('Planner output exceeded the session limit.')));
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
child.stderr.on('data', (data: Buffer) => {
|
|
186
|
+
diagnostics = (diagnostics + data.toString()).slice(-MAX_DIAGNOSTIC_LENGTH);
|
|
187
|
+
});
|
|
188
|
+
child.stdin.on('error', (error) =>
|
|
189
|
+
finish(new PipelineExecutionError(spaceTrim(`Planner harness input failed: ${error.message}`))),
|
|
190
|
+
);
|
|
191
|
+
child.on('error', (error) =>
|
|
192
|
+
finish(
|
|
193
|
+
new PipelineExecutionError(
|
|
194
|
+
spaceTrim(`Cannot start Codex: ${error.message}. Install and sign in to Codex before planning.`),
|
|
195
|
+
),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
child.on('close', (code) =>
|
|
199
|
+
finish(
|
|
200
|
+
code === 0 && answer && !isHarnessFailed
|
|
201
|
+
? undefined
|
|
202
|
+
: new PipelineExecutionError(
|
|
203
|
+
spaceTrim(
|
|
204
|
+
`Planner harness failed (${code}). No proposed changes were saved. Use a Codex version supporting --ignore-user-config and --ignore-rules.\n${diagnostics}`,
|
|
205
|
+
),
|
|
206
|
+
),
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
reader.on('line', (line) => {
|
|
210
|
+
if (isSettled) return;
|
|
211
|
+
try {
|
|
212
|
+
const event = JSON.parse(line);
|
|
213
|
+
if (event.type === 'turn.completed' && event.usage) usageEvent = line;
|
|
214
|
+
if (event.type === 'item.completed' && event.item?.type === 'agent_message') answer = event.item.text;
|
|
215
|
+
if (event.type === 'turn.failed' || event.type === 'error') {
|
|
216
|
+
isHarnessFailed = true;
|
|
217
|
+
diagnostics = JSON.stringify(event);
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
/* Non-JSON diagnostics never become executable instructions. */
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
child.stdin.end(options.prompt);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Resolves npm Windows shims to their JavaScript entrypoint so all model text stays off the command line. */
|
|
228
|
+
async function resolvePlanningHarnessCommand(): Promise<{ executable: string; arguments: string[] }> {
|
|
229
|
+
const path = await $resolveHarnessCommandPath(getHarnessDefinition('openai-codex'));
|
|
230
|
+
if (!path)
|
|
231
|
+
throw new NotAllowed(
|
|
232
|
+
spaceTrim('Codex is not installed. Install and sign in to Codex, then retry `ptbk coder plan`.'),
|
|
233
|
+
);
|
|
234
|
+
if (process.platform !== 'win32' || path.toLowerCase().endsWith('.exe')) return { executable: path, arguments: [] };
|
|
235
|
+
const entrypoint = join(dirname(path), 'node_modules', '@openai', 'codex', 'bin', 'codex.js');
|
|
236
|
+
if (
|
|
237
|
+
await stat(entrypoint).then(
|
|
238
|
+
(entry) => entry.isFile(),
|
|
239
|
+
() => false,
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
return { executable: process.execPath, arguments: [entrypoint] };
|
|
243
|
+
}
|
|
244
|
+
throw new NotAllowed(
|
|
245
|
+
spaceTrim(
|
|
246
|
+
'Planning needs the native Codex executable or its standard npm installation on Windows; shell shims cannot enforce argument isolation.',
|
|
247
|
+
),
|
|
248
|
+
);
|
|
249
|
+
}
|