@promptbook/cli 0.114.0-32 → 0.114.0-34
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/esm/index.es.js +1417 -400
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.d.ts +25 -0
- package/esm/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +21 -3
- package/esm/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +20 -0
- package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +77 -0
- package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTracePath.d.ts +10 -0
- package/esm/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +11 -6
- package/esm/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/prompts/writePromptRunTrace.d.ts +19 -0
- package/esm/scripts/run-codex-prompts/runners/common/buildHarnessAuthenticationError.d.ts +20 -0
- package/esm/scripts/run-codex-prompts/runners/common/createAuthenticationAwarePromptRunner.d.ts +11 -0
- package/esm/scripts/run-codex-prompts/runners/common/extractHarnessAuthenticationFailureReason.d.ts +9 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -0
- package/esm/src/book-2.0/book-language-documentation/getSafeCodeBlock.d.ts +1 -1
- package/esm/src/book-components/Chat/MockedChat/MockedChat.test.d.ts +2 -0
- package/esm/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.d.ts +2 -1
- package/esm/src/cli/cli-commands/coder/agentCliOptions.d.ts +21 -0
- package/esm/src/cli/cli-commands/coder/boilerplateTemplates.d.ts +8 -21
- package/esm/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +33 -0
- package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +4 -2
- package/esm/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.d.ts +2 -0
- package/esm/src/cli/cli-commands/coder/ensureDirectory.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
- package/esm/src/cli/cli-commands/coder/list.d.ts +10 -0
- package/esm/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +19 -1
- package/esm/src/cli/cli-commands/coder/printCoderRunFailure.d.ts +6 -0
- package/esm/src/cli/cli-commands/coder.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/$askForConfirmation.d.ts +4 -2
- package/esm/src/cli/cli-commands/common/$askForConfirmation.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.d.ts +12 -0
- package/esm/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.d.ts +11 -0
- package/esm/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.d.ts +33 -0
- package/esm/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.d.ts +6 -0
- package/esm/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.d.ts +10 -0
- package/esm/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.d.ts +28 -0
- package/esm/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.d.ts +7 -0
- package/esm/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +2 -1
- package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -2
- package/esm/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
- package/esm/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts +4 -2
- package/esm/src/cli/cli-commands/common/promptPriorityCliOptions.d.ts +7 -0
- package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +2 -1
- package/esm/src/cli/cli-commands/common/questionsCliOptions.d.ts +45 -0
- package/esm/src/cli/cli-commands/common/questionsCliOptions.test.d.ts +1 -0
- package/esm/src/cli/common/$deprecateCliCommand.d.ts +3 -0
- package/esm/src/cli/common/$hideCliCommandFromHelp.d.ts +11 -0
- package/esm/src/cli/common/$hideCliCommandFromHelp.test.d.ts +1 -0
- package/esm/src/cli/common/$requireCliSubcommand.d.ts +18 -0
- package/esm/src/cli/common/$requireCliSubcommand.test.d.ts +1 -0
- package/esm/src/llm-providers/vercel/VercelExecutionToolsOptions.d.ts +5 -2
- package/esm/src/version.d.ts +1 -1
- package/package.json +8 -11
- package/src/book-2.0/book-language-documentation/getSafeCodeBlock.ts +1 -1
- package/src/book-components/Chat/MockedChat/MockedChat.tsx +25 -1
- package/src/cli/cli-commands/agent-folder.ts +2 -6
- package/src/cli/cli-commands/agent.ts +2 -6
- package/src/cli/cli-commands/agents-server.ts +2 -6
- package/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.ts +3 -0
- package/src/cli/cli-commands/coder/add.ts +27 -2
- package/src/cli/cli-commands/coder/agentCliOptions.ts +32 -0
- package/src/cli/cli-commands/coder/boilerplateTemplates.ts +17 -44
- package/src/cli/cli-commands/coder/coderReferencedArtifacts.ts +106 -0
- package/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts +4 -2
- package/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.ts +5 -1
- package/src/cli/cli-commands/coder/ensureDirectory.ts +27 -1
- package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +99 -10
- package/src/cli/cli-commands/coder/init.ts +23 -14
- package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +18 -27
- package/src/cli/cli-commands/coder/list.ts +89 -0
- package/src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts +31 -7
- package/src/cli/cli-commands/coder/ping.ts +25 -22
- package/src/cli/cli-commands/coder/printCoderRunFailure.ts +14 -0
- package/src/cli/cli-commands/coder/printInitializationSummary.ts +23 -16
- package/src/cli/cli-commands/coder/run.ts +36 -39
- package/src/cli/cli-commands/coder/server.ts +45 -48
- package/src/cli/cli-commands/coder.ts +6 -6
- package/src/cli/cli-commands/common/$askForConfirmation.ts +12 -2
- package/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.ts +35 -0
- package/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.ts +37 -0
- package/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.ts +39 -0
- package/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.ts +26 -0
- package/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.ts +33 -0
- package/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.ts +33 -0
- package/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.ts +21 -0
- package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +17 -5
- package/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts +8 -3
- package/src/cli/cli-commands/common/harness/HarnessDefinition.ts +36 -0
- package/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.ts +8 -3
- package/src/cli/cli-commands/common/promptPriorityCliOptions.ts +30 -0
- package/src/cli/cli-commands/common/promptRunnerCliOptions.ts +5 -5
- package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +13 -2
- package/src/cli/cli-commands/common/questionsCliOptions.ts +89 -0
- package/src/cli/cli-commands/run.ts +3 -1
- package/src/cli/common/$deprecateCliCommand.ts +5 -0
- package/src/cli/common/$hideCliCommandFromHelp.ts +27 -0
- package/src/cli/common/$requireCliSubcommand.ts +35 -0
- package/src/cli/promptbookCli.ts +13 -9
- package/src/llm-providers/vercel/VercelExecutionToolsOptions.ts +7 -2
- package/src/llm-providers/vercel/createExecutionToolsFromVercelProvider.ts +18 -17
- package/src/other/templates/getTemplatesPipelineCollection.ts +695 -707
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/index.umd.js +1416 -399
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.d.ts +25 -0
- package/umd/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +21 -3
- package/umd/scripts/run-codex-prompts/main/listCoderPrompts.d.ts +20 -0
- package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +77 -0
- package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTracePath.d.ts +10 -0
- package/umd/scripts/run-codex-prompts/prompts/isPromptCompatibleWithRunner.d.ts +11 -6
- package/umd/scripts/run-codex-prompts/prompts/loadPromptFiles.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/prompts/writePromptRunTrace.d.ts +19 -0
- package/umd/scripts/run-codex-prompts/runners/common/buildHarnessAuthenticationError.d.ts +20 -0
- package/umd/scripts/run-codex-prompts/runners/common/createAuthenticationAwarePromptRunner.d.ts +11 -0
- package/umd/scripts/run-codex-prompts/runners/common/extractHarnessAuthenticationFailureReason.d.ts +9 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -0
- package/umd/src/book-2.0/book-language-documentation/getSafeCodeBlock.d.ts +1 -1
- package/umd/src/book-components/Chat/MockedChat/MockedChat.test.d.ts +2 -0
- package/umd/src/cli/cli-commands/coder/$ensureCoderHarnessGitignoreRules.d.ts +2 -1
- package/umd/src/cli/cli-commands/coder/agentCliOptions.d.ts +21 -0
- package/umd/src/cli/cli-commands/coder/boilerplateTemplates.d.ts +8 -21
- package/umd/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +33 -0
- package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +4 -2
- package/umd/src/cli/cli-commands/coder/ensureCoderVscodeSettingsFile.d.ts +2 -0
- package/umd/src/cli/cli-commands/coder/ensureDirectory.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder/init.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +7 -5
- package/umd/src/cli/cli-commands/coder/list.d.ts +10 -0
- package/umd/src/cli/cli-commands/coder/list.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +19 -1
- package/umd/src/cli/cli-commands/coder/printCoderRunFailure.d.ts +6 -0
- package/umd/src/cli/cli-commands/coder.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/$askForConfirmation.d.ts +4 -2
- package/umd/src/cli/cli-commands/common/$askForConfirmation.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.d.ts +12 -0
- package/umd/src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.d.ts +11 -0
- package/umd/src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/disk-space/FreeDiskSpaceStatus.d.ts +33 -0
- package/umd/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.d.ts +6 -0
- package/umd/src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.d.ts +10 -0
- package/umd/src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.d.ts +28 -0
- package/umd/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.d.ts +7 -0
- package/umd/src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +2 -1
- package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -2
- package/umd/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
- package/umd/src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.d.ts +4 -2
- package/umd/src/cli/cli-commands/common/promptPriorityCliOptions.d.ts +7 -0
- package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +2 -1
- package/umd/src/cli/cli-commands/common/questionsCliOptions.d.ts +45 -0
- package/umd/src/cli/cli-commands/common/questionsCliOptions.test.d.ts +1 -0
- package/umd/src/cli/common/$deprecateCliCommand.d.ts +3 -0
- package/umd/src/cli/common/$hideCliCommandFromHelp.d.ts +11 -0
- package/umd/src/cli/common/$hideCliCommandFromHelp.test.d.ts +1 -0
- package/umd/src/cli/common/$requireCliSubcommand.d.ts +18 -0
- package/umd/src/cli/common/$requireCliSubcommand.test.d.ts +1 -0
- package/umd/src/llm-providers/vercel/VercelExecutionToolsOptions.d.ts +5 -2
- package/umd/src/version.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/agentCodingFile.d.ts +0 -13
- package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +0 -29
- package/src/cli/cli-commands/coder/agentCodingFile.ts +0 -25
- package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +0 -46
- package/umd/src/cli/cli-commands/coder/agentCodingFile.d.ts +0 -13
- package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +0 -29
- /package/esm/src/cli/cli-commands/{common/harnessUpdateCliOptions.test.d.ts → coder/init.test.d.ts} +0 -0
- /package/{umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts → esm/src/cli/cli-commands/coder/list.test.d.ts} +0 -0
package/umd/index.umd.js
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* @generated
|
|
59
59
|
* @see https://github.com/webgptorg/promptbook
|
|
60
60
|
*/
|
|
61
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-
|
|
61
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-34';
|
|
62
62
|
/**
|
|
63
63
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
64
64
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1718,6 +1718,35 @@
|
|
|
1718
1718
|
// TODO: [🗽] Unite branding and make single place for it
|
|
1719
1719
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1720
1720
|
|
|
1721
|
+
/**
|
|
1722
|
+
* Message printed when one CLI command which only groups subcommands is run without picking any of them.
|
|
1723
|
+
*
|
|
1724
|
+
* @private internal constant of `$requireCliSubcommand`
|
|
1725
|
+
*/
|
|
1726
|
+
const MISSING_CLI_SUBCOMMAND_MESSAGE = 'Please specify a subcommand.';
|
|
1727
|
+
/**
|
|
1728
|
+
* Makes one CLI command which only groups subcommands ask for a subcommand instead of doing anything on its own.
|
|
1729
|
+
*
|
|
1730
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers an action in the CLI
|
|
1731
|
+
*
|
|
1732
|
+
* @private utility of CLI
|
|
1733
|
+
*/
|
|
1734
|
+
function $requireCliSubcommand(command) {
|
|
1735
|
+
command.action(() => $reportMissingCliSubcommand(command));
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Asks for a subcommand of one CLI command, prints its help and exits the process.
|
|
1739
|
+
*
|
|
1740
|
+
* Note: `$` is used to indicate that this function is not a pure function - it writes to the console and exits the process
|
|
1741
|
+
*
|
|
1742
|
+
* @private utility of CLI
|
|
1743
|
+
*/
|
|
1744
|
+
function $reportMissingCliSubcommand(command) {
|
|
1745
|
+
console.info(colors__default["default"].yellow(MISSING_CLI_SUBCOMMAND_MESSAGE));
|
|
1746
|
+
console.info('');
|
|
1747
|
+
return command.help();
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1721
1750
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1722
1751
|
/**
|
|
1723
1752
|
* All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
|
|
@@ -1822,20 +1851,20 @@
|
|
|
1822
1851
|
*/
|
|
1823
1852
|
const PROMPT_RUNNER_DESCRIPTION = _spaceTrim.spaceTrim(`
|
|
1824
1853
|
Runners:
|
|
1825
|
-
- openai-codex: OpenAI Codex integration (requires --model)
|
|
1854
|
+
- openai-codex: OpenAI Codex integration (requires --model when executing)
|
|
1826
1855
|
- github-copilot: GitHub Copilot CLI integration
|
|
1827
1856
|
- cline: Cline CLI integration
|
|
1828
1857
|
- claude-code: Claude Code integration
|
|
1829
1858
|
- opencode: Opencode integration
|
|
1830
|
-
- gemini: Google Gemini CLI integration (requires --model)
|
|
1831
|
-
- qwen-code: Qwen Code CLI integration (requires --model)
|
|
1859
|
+
- gemini: Google Gemini CLI integration (requires --model when executing)
|
|
1860
|
+
- qwen-code: Qwen Code CLI integration (requires --model when executing)
|
|
1832
1861
|
`);
|
|
1833
1862
|
/**
|
|
1834
1863
|
* Commander description for the `--harness` option.
|
|
1835
1864
|
*
|
|
1836
1865
|
* @private internal utility of `promptbookCli`
|
|
1837
1866
|
*/
|
|
1838
|
-
const PROMPT_RUNNER_HARNESS_OPTION_DESCRIPTION = `Select runner: ${PROMPT_RUNNER_HARNESS_NAMES.join(', ')} (required
|
|
1867
|
+
const PROMPT_RUNNER_HARNESS_OPTION_DESCRIPTION = `Select runner: ${PROMPT_RUNNER_HARNESS_NAMES.join(', ')} (required when executing prompts)`;
|
|
1839
1868
|
/**
|
|
1840
1869
|
* Runner harness names listed as alternatives of the `--harness` option in error messages.
|
|
1841
1870
|
*
|
|
@@ -1848,7 +1877,7 @@
|
|
|
1848
1877
|
* @private internal utility of `promptbookCli`
|
|
1849
1878
|
*/
|
|
1850
1879
|
const PROMPT_RUNNER_MODEL_OPTION_DESCRIPTION = _spaceTrim.spaceTrim(`
|
|
1851
|
-
Model to use (required
|
|
1880
|
+
Model to use or filter by (required when executing with openai-codex, gemini and qwen-code)
|
|
1852
1881
|
|
|
1853
1882
|
OpenAI examples: gpt-5.2-codex, default
|
|
1854
1883
|
Gemini examples: gemini-3-flash-preview, default
|
|
@@ -2113,11 +2142,7 @@
|
|
|
2113
2142
|
`));
|
|
2114
2143
|
$initializeAgentChatCommand(agentCommand);
|
|
2115
2144
|
$initializeAgentExecCommand(agentCommand);
|
|
2116
|
-
agentCommand
|
|
2117
|
-
console.info(colors__default["default"].yellow('Please specify a subcommand.'));
|
|
2118
|
-
console.info('');
|
|
2119
|
-
agentCommand.help();
|
|
2120
|
-
});
|
|
2145
|
+
$requireCliSubcommand(agentCommand);
|
|
2121
2146
|
}
|
|
2122
2147
|
// Note: [🟡] Code for CLI command [agent](src/cli/cli-commands/agent.ts) should never be published outside of `@promptbook/cli`
|
|
2123
2148
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2275,6 +2300,23 @@
|
|
|
2275
2300
|
}
|
|
2276
2301
|
return 'unchanged';
|
|
2277
2302
|
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Checks whether a project-relative directory is missing or contains no entries.
|
|
2305
|
+
*
|
|
2306
|
+
* @private internal utility of `coder init` command
|
|
2307
|
+
*/
|
|
2308
|
+
async function isDirectoryEmpty(projectPath, relativeDirectoryPath) {
|
|
2309
|
+
const directoryPath = path.join(projectPath, relativeDirectoryPath);
|
|
2310
|
+
try {
|
|
2311
|
+
return (await promises.readdir(directoryPath)).length === 0;
|
|
2312
|
+
}
|
|
2313
|
+
catch (error) {
|
|
2314
|
+
if (isNodeJsErrorWithCode$1(error, 'ENOENT')) {
|
|
2315
|
+
return true;
|
|
2316
|
+
}
|
|
2317
|
+
throw error;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2278
2320
|
/**
|
|
2279
2321
|
* Checks whether a path exists and is a directory.
|
|
2280
2322
|
*/
|
|
@@ -2286,6 +2328,12 @@
|
|
|
2286
2328
|
return false;
|
|
2287
2329
|
}
|
|
2288
2330
|
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Checks whether a caught value is a Node.js error with the given error code.
|
|
2333
|
+
*/
|
|
2334
|
+
function isNodeJsErrorWithCode$1(error, code) {
|
|
2335
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
|
|
2336
|
+
}
|
|
2289
2337
|
// Note: [🟡] Code for coder init directory creation [ensureDirectory](src/cli/cli-commands/coder/ensureDirectory.ts) should never be published outside of `@promptbook/cli`
|
|
2290
2338
|
|
|
2291
2339
|
/**
|
|
@@ -2573,11 +2621,7 @@
|
|
|
2573
2621
|
$initializeAgentTickCommand(agentFolderCommand);
|
|
2574
2622
|
$initializeAgentRunCommand(agentFolderCommand);
|
|
2575
2623
|
$initializeAgentRunMultipleCommand(agentFolderCommand);
|
|
2576
|
-
agentFolderCommand
|
|
2577
|
-
console.info(colors__default["default"].yellow('Please specify a subcommand.'));
|
|
2578
|
-
console.info('');
|
|
2579
|
-
agentFolderCommand.help();
|
|
2580
|
-
});
|
|
2624
|
+
$requireCliSubcommand(agentFolderCommand);
|
|
2581
2625
|
}
|
|
2582
2626
|
// Note: [🟡] Code for CLI command [agent-folder](src/cli/cli-commands/agent-folder.ts) should never be published outside of `@promptbook/cli`
|
|
2583
2627
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -21428,10 +21472,30 @@
|
|
|
21428
21472
|
return relativePath.replace(/\\/gu, '/');
|
|
21429
21473
|
}
|
|
21430
21474
|
|
|
21475
|
+
/**
|
|
21476
|
+
* AuthenticationError is thrown from login function which is dependency of remote server
|
|
21477
|
+
*
|
|
21478
|
+
* @public exported from `@promptbook/core`
|
|
21479
|
+
*/
|
|
21480
|
+
class AuthenticationError extends Error {
|
|
21481
|
+
constructor(message) {
|
|
21482
|
+
super(message);
|
|
21483
|
+
this.name = 'AuthenticationError';
|
|
21484
|
+
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
21485
|
+
}
|
|
21486
|
+
}
|
|
21487
|
+
|
|
21431
21488
|
/**
|
|
21432
21489
|
* Formats one unknown error-like value into readable text for logs and feedback.
|
|
21490
|
+
*
|
|
21491
|
+
* An `AuthenticationError` is reported without its stack, because its message is written for the user - it
|
|
21492
|
+
* says which harness has to be signed in again and how - and the frames of a failure nobody can debug would
|
|
21493
|
+
* only push those instructions out of sight.
|
|
21433
21494
|
*/
|
|
21434
21495
|
function formatUnknownErrorDetails(error) {
|
|
21496
|
+
if (error instanceof AuthenticationError) {
|
|
21497
|
+
return error.message;
|
|
21498
|
+
}
|
|
21435
21499
|
if (error instanceof Error) {
|
|
21436
21500
|
return error.stack || error.message;
|
|
21437
21501
|
}
|
|
@@ -24148,6 +24212,7 @@
|
|
|
24148
24212
|
commandName: 'codex',
|
|
24149
24213
|
npmPackageName: '@openai/codex',
|
|
24150
24214
|
projectGitignoreRules: ['.codex'],
|
|
24215
|
+
authenticationMethod: { command: 'codex login' },
|
|
24151
24216
|
// Note: The official standalone installer keeps its package in `~/.codex` and links it onto the `PATH`
|
|
24152
24217
|
standaloneInstallation: {
|
|
24153
24218
|
directoryNames: ['.codex'],
|
|
@@ -24160,6 +24225,7 @@
|
|
|
24160
24225
|
commandName: 'copilot',
|
|
24161
24226
|
npmPackageName: '@github/copilot',
|
|
24162
24227
|
projectGitignoreRules: ['.github/copilot/settings.local.json'],
|
|
24228
|
+
authenticationMethod: { command: 'copilot', interactiveStep: 'running the `/login` command inside it' },
|
|
24163
24229
|
// Note: The GitHub Copilot CLI downloads its native binary in a postinstall script
|
|
24164
24230
|
npmInstallEnvironment: { npm_config_ignore_scripts: 'false' },
|
|
24165
24231
|
},
|
|
@@ -24169,6 +24235,7 @@
|
|
|
24169
24235
|
commandName: 'cline',
|
|
24170
24236
|
npmPackageName: 'cline',
|
|
24171
24237
|
projectGitignoreRules: ['.cline'],
|
|
24238
|
+
authenticationMethod: { command: 'cline', interactiveStep: 'signing in to the provider inside it' },
|
|
24172
24239
|
},
|
|
24173
24240
|
'claude-code': {
|
|
24174
24241
|
harnessName: 'claude-code',
|
|
@@ -24176,6 +24243,7 @@
|
|
|
24176
24243
|
commandName: 'claude',
|
|
24177
24244
|
npmPackageName: '@anthropic-ai/claude-code',
|
|
24178
24245
|
projectGitignoreRules: ['.claude'],
|
|
24246
|
+
authenticationMethod: { command: 'claude', interactiveStep: 'running the `/login` command inside it' },
|
|
24179
24247
|
},
|
|
24180
24248
|
opencode: {
|
|
24181
24249
|
harnessName: 'opencode',
|
|
@@ -24183,6 +24251,7 @@
|
|
|
24183
24251
|
commandName: 'opencode',
|
|
24184
24252
|
npmPackageName: 'opencode-ai',
|
|
24185
24253
|
projectGitignoreRules: ['.opencode'],
|
|
24254
|
+
authenticationMethod: { command: 'opencode auth login' },
|
|
24186
24255
|
},
|
|
24187
24256
|
gemini: {
|
|
24188
24257
|
harnessName: 'gemini',
|
|
@@ -24190,6 +24259,7 @@
|
|
|
24190
24259
|
commandName: 'gemini',
|
|
24191
24260
|
npmPackageName: '@google/gemini-cli',
|
|
24192
24261
|
projectGitignoreRules: ['.gemini'],
|
|
24262
|
+
authenticationMethod: { command: 'gemini', interactiveStep: 'running the `/auth` command inside it' },
|
|
24193
24263
|
},
|
|
24194
24264
|
'qwen-code': {
|
|
24195
24265
|
harnessName: 'qwen-code',
|
|
@@ -24197,6 +24267,7 @@
|
|
|
24197
24267
|
commandName: 'qwen',
|
|
24198
24268
|
npmPackageName: '@qwen-code/qwen-code',
|
|
24199
24269
|
projectGitignoreRules: ['.qwen'],
|
|
24270
|
+
authenticationMethod: { command: 'qwen', interactiveStep: 'running the `/auth` command inside it' },
|
|
24200
24271
|
},
|
|
24201
24272
|
};
|
|
24202
24273
|
/**
|
|
@@ -26230,6 +26301,183 @@
|
|
|
26230
26301
|
return `${sessionId.slice(0, 8)}...`;
|
|
26231
26302
|
}
|
|
26232
26303
|
|
|
26304
|
+
/**
|
|
26305
|
+
* Formats one unknown error-like value into its readable message without the stack trace.
|
|
26306
|
+
*
|
|
26307
|
+
* Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
|
|
26308
|
+
* inside a branded error, where the stack of the wrapped error would only bury the actual cause.
|
|
26309
|
+
*/
|
|
26310
|
+
function formatUnknownErrorMessage(error) {
|
|
26311
|
+
if (error instanceof Error) {
|
|
26312
|
+
return error.message;
|
|
26313
|
+
}
|
|
26314
|
+
if (typeof error === 'string') {
|
|
26315
|
+
return error;
|
|
26316
|
+
}
|
|
26317
|
+
const serializedError = JSON.stringify(error, null, 2);
|
|
26318
|
+
return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
|
|
26319
|
+
}
|
|
26320
|
+
|
|
26321
|
+
/**
|
|
26322
|
+
* Builds the branded error which tells the user that the selected harness has to be signed in again.
|
|
26323
|
+
*/
|
|
26324
|
+
function buildHarnessAuthenticationError(options) {
|
|
26325
|
+
const { harnessName, reason } = options;
|
|
26326
|
+
const { label, authenticationMethod } = getHarnessDefinition(harnessName);
|
|
26327
|
+
return new AuthenticationError(spaceTrim((block) => `
|
|
26328
|
+
The **${label}** harness is not authenticated, so the prompt was not run.
|
|
26329
|
+
|
|
26330
|
+
${label} reported:
|
|
26331
|
+
> ${reason}
|
|
26332
|
+
|
|
26333
|
+
${block(buildSignInInstruction(authenticationMethod))}
|
|
26334
|
+
|
|
26335
|
+
Once you are signed in, start the very same \`ptbk coder\` command again.
|
|
26336
|
+
`));
|
|
26337
|
+
}
|
|
26338
|
+
/**
|
|
26339
|
+
* Builds the sentence which tells the user how to sign in to one harness again.
|
|
26340
|
+
*/
|
|
26341
|
+
function buildSignInInstruction(authenticationMethod) {
|
|
26342
|
+
const { command, interactiveStep } = authenticationMethod;
|
|
26343
|
+
if (interactiveStep === undefined) {
|
|
26344
|
+
return `Sign in again by running \`${command}\`.`;
|
|
26345
|
+
}
|
|
26346
|
+
return `Sign in again by running \`${command}\` and then ${interactiveStep}.`;
|
|
26347
|
+
}
|
|
26348
|
+
|
|
26349
|
+
/**
|
|
26350
|
+
* Maximum length of the harness reason quoted back to the user.
|
|
26351
|
+
*/
|
|
26352
|
+
const MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH = 300;
|
|
26353
|
+
/**
|
|
26354
|
+
* Keys of machine-readable harness output whose string value carries the human-readable failure reason.
|
|
26355
|
+
*
|
|
26356
|
+
* Harnesses which stream JSON events - like Claude Code with its `stream-json` output - report the reason only
|
|
26357
|
+
* inside such a value, surrounded by hundreds of characters of session metadata which must never be shown to
|
|
26358
|
+
* the user. The keys are ordered from the most to the least conclusive one.
|
|
26359
|
+
*/
|
|
26360
|
+
const HARNESS_FAILURE_MESSAGE_KEYS = ['result', 'error_message', 'message', 'text'];
|
|
26361
|
+
/**
|
|
26362
|
+
* Patterns which extract one JSON string value reported under a harness failure message key.
|
|
26363
|
+
*/
|
|
26364
|
+
const HARNESS_FAILURE_MESSAGE_PATTERNS = HARNESS_FAILURE_MESSAGE_KEYS.map((messageKey) => new RegExp(`"${messageKey}"\\s*:\\s*("(?:[^"\\\\]|\\\\.)*")`, 'g'));
|
|
26365
|
+
/**
|
|
26366
|
+
* Signatures which mean that the harness refused to work because it is not logged in.
|
|
26367
|
+
*
|
|
26368
|
+
* They are deliberately narrow phrases instead of single words like `login`, because the output of a harness
|
|
26369
|
+
* routinely mentions its own `/login` command among the commands it offers, and such a mention must never be
|
|
26370
|
+
* read as a failed login.
|
|
26371
|
+
*/
|
|
26372
|
+
const AUTHENTICATION_FAILURE_PATTERNS = [
|
|
26373
|
+
/\bauthentication[ _-]?failed\b/i,
|
|
26374
|
+
/\bfailed to authenticate\b/i,
|
|
26375
|
+
/\bnot authenticated\b/i,
|
|
26376
|
+
/\bnot logged[ -]?in\b/i,
|
|
26377
|
+
/\bnot signed[ -]?in\b/i,
|
|
26378
|
+
/\bno authentication information found\b/i,
|
|
26379
|
+
/\bauthentication (?:is )?required\b/i,
|
|
26380
|
+
/\blogin (?:is )?required\b/i,
|
|
26381
|
+
/\bplease (?:log|sign) in\b/i,
|
|
26382
|
+
/\bunauthorized\b/i,
|
|
26383
|
+
/\b(?:oauth|session|token|credentials?|login)\b[^.]{0,60}?\bexpired\b/i,
|
|
26384
|
+
/\bexpired\b[^.]{0,60}?\b(?:oauth|session|token|credentials?|login)\b/i,
|
|
26385
|
+
/\binvalid api[ _-]?key\b/i,
|
|
26386
|
+
/\bmissing api[ _-]?key\b/i,
|
|
26387
|
+
/\binvalid credentials?\b/i,
|
|
26388
|
+
];
|
|
26389
|
+
/**
|
|
26390
|
+
* Reads the reason why a harness refused to work because it is not logged in, from its raw CLI output.
|
|
26391
|
+
*
|
|
26392
|
+
* Messages the harness reports in machine-readable output are preferred over its raw output lines, so the
|
|
26393
|
+
* reason is the sentence the harness wrote for a human and not the JSON event which transported it.
|
|
26394
|
+
*
|
|
26395
|
+
* @returns One short single-line reason, or `undefined` when the failure was not caused by authentication.
|
|
26396
|
+
*/
|
|
26397
|
+
function extractHarnessAuthenticationFailureReason(harnessOutput) {
|
|
26398
|
+
const reportedMessages = [...extractHarnessReportedMessages(harnessOutput), ...harnessOutput.split(/\r?\n/)];
|
|
26399
|
+
for (const reportedMessage of reportedMessages) {
|
|
26400
|
+
const reason = reportedMessage.replace(/\s+/g, ' ').trim();
|
|
26401
|
+
if (isAuthenticationFailureReason(reason)) {
|
|
26402
|
+
return limitAuthenticationFailureReason(reason);
|
|
26403
|
+
}
|
|
26404
|
+
}
|
|
26405
|
+
return undefined;
|
|
26406
|
+
}
|
|
26407
|
+
/**
|
|
26408
|
+
* Lists the human-readable messages a harness reported in its machine-readable output.
|
|
26409
|
+
*/
|
|
26410
|
+
function extractHarnessReportedMessages(harnessOutput) {
|
|
26411
|
+
const reportedMessages = [];
|
|
26412
|
+
for (const messagePattern of HARNESS_FAILURE_MESSAGE_PATTERNS) {
|
|
26413
|
+
for (const match of harnessOutput.matchAll(messagePattern)) {
|
|
26414
|
+
const reportedMessage = parseSerializedJsonString(match[1]);
|
|
26415
|
+
if (reportedMessage !== undefined) {
|
|
26416
|
+
reportedMessages.push(reportedMessage);
|
|
26417
|
+
}
|
|
26418
|
+
}
|
|
26419
|
+
}
|
|
26420
|
+
return reportedMessages;
|
|
26421
|
+
}
|
|
26422
|
+
/**
|
|
26423
|
+
* Reads one JSON string literal back into plain text, tolerating output which only looks like JSON.
|
|
26424
|
+
*/
|
|
26425
|
+
function parseSerializedJsonString(serializedString) {
|
|
26426
|
+
if (serializedString === undefined) {
|
|
26427
|
+
return undefined;
|
|
26428
|
+
}
|
|
26429
|
+
try {
|
|
26430
|
+
const parsedString = JSON.parse(serializedString);
|
|
26431
|
+
return typeof parsedString === 'string' ? parsedString : undefined;
|
|
26432
|
+
}
|
|
26433
|
+
catch (_a) {
|
|
26434
|
+
return undefined;
|
|
26435
|
+
}
|
|
26436
|
+
}
|
|
26437
|
+
/**
|
|
26438
|
+
* Returns true when one message of the harness says that it is not logged in.
|
|
26439
|
+
*/
|
|
26440
|
+
function isAuthenticationFailureReason(reportedMessage) {
|
|
26441
|
+
return AUTHENTICATION_FAILURE_PATTERNS.some((pattern) => pattern.test(reportedMessage));
|
|
26442
|
+
}
|
|
26443
|
+
/**
|
|
26444
|
+
* Shortens a very long reason, so the guidance which follows it stays visible in the terminal.
|
|
26445
|
+
*/
|
|
26446
|
+
function limitAuthenticationFailureReason(reason) {
|
|
26447
|
+
if (reason.length <= MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH) {
|
|
26448
|
+
return reason;
|
|
26449
|
+
}
|
|
26450
|
+
return `${reason.slice(0, MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH)}...`;
|
|
26451
|
+
}
|
|
26452
|
+
|
|
26453
|
+
/**
|
|
26454
|
+
* Wraps one prompt runner so a failure caused by a missing or expired harness login is reported as a branded
|
|
26455
|
+
* `AuthenticationError` which tells the user how to sign in again.
|
|
26456
|
+
*
|
|
26457
|
+
* Each harness reports its own failures differently, but all of them report them by failing `runPrompt` with
|
|
26458
|
+
* the raw CLI output as the message. Translating the failure here therefore keeps exactly one place which has
|
|
26459
|
+
* to recognize an unauthenticated harness, for every harness and for every command which runs prompts.
|
|
26460
|
+
*/
|
|
26461
|
+
function createAuthenticationAwarePromptRunner(runner, harnessName) {
|
|
26462
|
+
var _a;
|
|
26463
|
+
return {
|
|
26464
|
+
name: runner.name,
|
|
26465
|
+
runPrompt: async (options) => {
|
|
26466
|
+
try {
|
|
26467
|
+
return await runner.runPrompt(options);
|
|
26468
|
+
}
|
|
26469
|
+
catch (error) {
|
|
26470
|
+
const reason = extractHarnessAuthenticationFailureReason(formatUnknownErrorMessage(error));
|
|
26471
|
+
if (reason === undefined) {
|
|
26472
|
+
throw error;
|
|
26473
|
+
}
|
|
26474
|
+
throw buildHarnessAuthenticationError({ harnessName, reason });
|
|
26475
|
+
}
|
|
26476
|
+
},
|
|
26477
|
+
getSubscriptionUsage: (_a = runner.getSubscriptionUsage) === null || _a === void 0 ? void 0 : _a.bind(runner),
|
|
26478
|
+
};
|
|
26479
|
+
}
|
|
26480
|
+
|
|
26233
26481
|
/**
|
|
26234
26482
|
* Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
|
|
26235
26483
|
*/
|
|
@@ -27969,6 +28217,18 @@
|
|
|
27969
28217
|
if (!agentName) {
|
|
27970
28218
|
throw new Error('Missing --harness in non-dry run mode');
|
|
27971
28219
|
}
|
|
28220
|
+
const resolution = resolveHarnessPromptRunner(agentName, options);
|
|
28221
|
+
return {
|
|
28222
|
+
...resolution,
|
|
28223
|
+
// Note: Every harness is wrapped here, so a harness which is not logged in is reported the same clear
|
|
28224
|
+
// way no matter which harness it is and which command has resolved the runner
|
|
28225
|
+
runner: createAuthenticationAwarePromptRunner(resolution.runner, agentName),
|
|
28226
|
+
};
|
|
28227
|
+
}
|
|
28228
|
+
/**
|
|
28229
|
+
* Creates the runner of one selected harness together with its status-line metadata.
|
|
28230
|
+
*/
|
|
28231
|
+
function resolveHarnessPromptRunner(agentName, options) {
|
|
27972
28232
|
if (agentName === 'openai-codex') {
|
|
27973
28233
|
return createOpenAiCodexRunnerResolution(options);
|
|
27974
28234
|
}
|
|
@@ -28436,11 +28696,19 @@
|
|
|
28436
28696
|
* Maximum width used for the rich coder-run frame.
|
|
28437
28697
|
*/
|
|
28438
28698
|
const MAX_FRAME_WIDTH = 96;
|
|
28699
|
+
/**
|
|
28700
|
+
* Number of terminal columns deliberately left unused by the rich coder-run frame.
|
|
28701
|
+
*
|
|
28702
|
+
* A row which fills the final physical terminal column can auto-wrap. The incremental terminal
|
|
28703
|
+
* updater moves by logical rows, so an auto-wrapped row would put its cursor out of sync with the
|
|
28704
|
+
* frame and let a later animated avatar redraw overwrite dashboard content.
|
|
28705
|
+
*/
|
|
28706
|
+
const TERMINAL_AUTO_WRAP_RESERVED_COLUMNS = 1;
|
|
28439
28707
|
/**
|
|
28440
28708
|
* Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
|
|
28441
28709
|
*/
|
|
28442
28710
|
function buildCoderRunUiFrame(options) {
|
|
28443
|
-
const totalWidth =
|
|
28711
|
+
const totalWidth = resolveCoderRunUiFrameWidth(options.terminalWidth);
|
|
28444
28712
|
const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
|
|
28445
28713
|
const promptStatusPrefix = isPromptActive ? `${colors__default["default"].yellow(`${options.spinner} `)}` : '';
|
|
28446
28714
|
const pausePresentation = buildPausePresentation(options.phase, options.pauseState, options.pauseTargetLabel, options.statusMessage);
|
|
@@ -28477,6 +28745,16 @@
|
|
|
28477
28745
|
frame.push(...renderBox('Controls', controlsBoxLines, totalWidth, colors__default["default"].white.bold));
|
|
28478
28746
|
return frame;
|
|
28479
28747
|
}
|
|
28748
|
+
/**
|
|
28749
|
+
* Resolves the safe width of the rich terminal frame from the available physical terminal columns.
|
|
28750
|
+
*
|
|
28751
|
+
* The reserved trailing column keeps every boxed row from auto-wrapping, which makes the frame's
|
|
28752
|
+
* logical row count match its physical terminal row count during animated incremental redraws.
|
|
28753
|
+
*/
|
|
28754
|
+
function resolveCoderRunUiFrameWidth(terminalWidth) {
|
|
28755
|
+
const availableFrameWidth = terminalWidth - TERMINAL_AUTO_WRAP_RESERVED_COLUMNS;
|
|
28756
|
+
return Math.max(MIN_FRAME_WIDTH, Math.min(availableFrameWidth, MAX_FRAME_WIDTH));
|
|
28757
|
+
}
|
|
28480
28758
|
/**
|
|
28481
28759
|
* Builds the header visual above the dashboard boxes.
|
|
28482
28760
|
*
|
|
@@ -29953,16 +30231,14 @@
|
|
|
29953
30231
|
*/
|
|
29954
30232
|
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
29955
30233
|
/**
|
|
29956
|
-
*
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
/**
|
|
29960
|
-
* Returns the usable terminal width, capped at 96.
|
|
30234
|
+
* Returns the number of columns reported by the current terminal.
|
|
30235
|
+
*
|
|
30236
|
+
* The frame builder owns clamping and reserves its trailing auto-wrap safety column.
|
|
29961
30237
|
*
|
|
29962
30238
|
* @private internal utility of coder run UI
|
|
29963
30239
|
*/
|
|
29964
|
-
function
|
|
29965
|
-
return
|
|
30240
|
+
function getTerminalColumnCount() {
|
|
30241
|
+
return process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
|
|
29966
30242
|
}
|
|
29967
30243
|
/**
|
|
29968
30244
|
* Boots the ANSI terminal UI for `ptbk coder run`.
|
|
@@ -30069,7 +30345,7 @@
|
|
|
30069
30345
|
*/
|
|
30070
30346
|
function buildFrameLines() {
|
|
30071
30347
|
return buildFrameLinesFromState({
|
|
30072
|
-
terminalWidth:
|
|
30348
|
+
terminalWidth: getTerminalColumnCount(),
|
|
30073
30349
|
animationFrame: spinnerFrame,
|
|
30074
30350
|
animationTimeMs: Date.now() - animationStartTimeMs,
|
|
30075
30351
|
spinner: SPINNER_FRAMES[spinnerFrame],
|
|
@@ -33800,19 +34076,6 @@
|
|
|
33800
34076
|
}
|
|
33801
34077
|
}
|
|
33802
34078
|
|
|
33803
|
-
/**
|
|
33804
|
-
* AuthenticationError is thrown from login function which is dependency of remote server
|
|
33805
|
-
*
|
|
33806
|
-
* @public exported from `@promptbook/core`
|
|
33807
|
-
*/
|
|
33808
|
-
class AuthenticationError extends Error {
|
|
33809
|
-
constructor(message) {
|
|
33810
|
-
super(message);
|
|
33811
|
-
this.name = 'AuthenticationError';
|
|
33812
|
-
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
33813
|
-
}
|
|
33814
|
-
}
|
|
33815
|
-
|
|
33816
34079
|
/**
|
|
33817
34080
|
* This error indicates that the pipeline collection cannot be properly loaded
|
|
33818
34081
|
*
|
|
@@ -42041,11 +42304,7 @@
|
|
|
42041
42304
|
$initializeAgentsServerDevCommand(agentsServerCommand);
|
|
42042
42305
|
$initializeAgentsServerInitCommand(agentsServerCommand);
|
|
42043
42306
|
$initializeAgentsServerStartCommand(agentsServerCommand);
|
|
42044
|
-
agentsServerCommand
|
|
42045
|
-
console.info(colors__default["default"].yellow('Please specify a subcommand.'));
|
|
42046
|
-
console.info('');
|
|
42047
|
-
agentsServerCommand.help();
|
|
42048
|
-
});
|
|
42307
|
+
$requireCliSubcommand(agentsServerCommand);
|
|
42049
42308
|
}
|
|
42050
42309
|
// Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
|
|
42051
42310
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -42135,24 +42394,82 @@
|
|
|
42135
42394
|
// Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
42136
42395
|
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
42137
42396
|
|
|
42397
|
+
/**
|
|
42398
|
+
* Description block shared by the `ptbk coder` commands which can ask interactive questions.
|
|
42399
|
+
*
|
|
42400
|
+
* @private internal utility of `promptbookCli`
|
|
42401
|
+
*/
|
|
42402
|
+
const QUESTIONS_DESCRIPTION = _spaceTrim.spaceTrim(`
|
|
42403
|
+
Interactive questions:
|
|
42404
|
+
- Steps which need an answer to continue are asked in the terminal
|
|
42405
|
+
- --no-questions never asks: an optional step is skipped with a note how to do it manually, a required one fails right away
|
|
42406
|
+
`);
|
|
42407
|
+
/**
|
|
42408
|
+
* Registers the shared `--no-questions` option on a `ptbk coder` command which can ask interactive questions.
|
|
42409
|
+
*
|
|
42410
|
+
* @private internal utility of `promptbookCli`
|
|
42411
|
+
*/
|
|
42412
|
+
function addQuestionsOption(command) {
|
|
42413
|
+
command.option('--no-questions', 'Never ask an interactive question and skip every optional step which needs an answer to continue');
|
|
42414
|
+
}
|
|
42415
|
+
/**
|
|
42416
|
+
* Converts the Commander questions flag into the normalized interactive-questions option.
|
|
42417
|
+
*
|
|
42418
|
+
* @private internal utility of `promptbookCli`
|
|
42419
|
+
*/
|
|
42420
|
+
function normalizeQuestionsCliOptions(cliOptions) {
|
|
42421
|
+
return {
|
|
42422
|
+
isAskingQuestionsEnabled: cliOptions.questions,
|
|
42423
|
+
};
|
|
42424
|
+
}
|
|
42425
|
+
/**
|
|
42426
|
+
* Asserts that a command which waits for a user confirmation before each prompt is allowed to ask for it.
|
|
42427
|
+
*
|
|
42428
|
+
* @throws {NotAllowed} when `--no-auto` is combined with `--no-questions`
|
|
42429
|
+
* @private internal utility of `promptbookCli`
|
|
42430
|
+
*/
|
|
42431
|
+
function assertUserConfirmationIsAllowed(options) {
|
|
42432
|
+
const { isAskingQuestionsEnabled, isWaitingForUser } = options;
|
|
42433
|
+
if (!isWaitingForUser || isAskingQuestionsEnabled) {
|
|
42434
|
+
return;
|
|
42435
|
+
}
|
|
42436
|
+
throw new NotAllowed(_spaceTrim.spaceTrim(`
|
|
42437
|
+
Flag \`--no-auto\` can not be used together with \`--no-questions\`.
|
|
42438
|
+
|
|
42439
|
+
**There is nobody who could confirm each prompt when no question may be asked.**
|
|
42440
|
+
|
|
42441
|
+
Actionable hints:
|
|
42442
|
+
- Keep \`--no-auto\` for a supervised run, for example \`ptbk coder run --harness claude-code --no-auto\`.
|
|
42443
|
+
- Keep \`--no-questions\` for an unattended run, for example \`ptbk coder run --harness claude-code --no-questions\`.
|
|
42444
|
+
`));
|
|
42445
|
+
}
|
|
42446
|
+
// Note: [🟡] Code for CLI interactive questions options [questionsCliOptions](src/cli/cli-commands/common/questionsCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
42447
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
42448
|
+
|
|
42138
42449
|
/**
|
|
42139
42450
|
* Relative path to the root prompts directory used by Promptbook coder utilities.
|
|
42140
42451
|
*
|
|
42141
42452
|
* @private internal utility of `ptbk coder`
|
|
42142
42453
|
*/
|
|
42143
|
-
const PROMPTS_DIRECTORY_PATH = 'prompts';
|
|
42454
|
+
const PROMPTS_DIRECTORY_PATH$1 = 'prompts';
|
|
42144
42455
|
/**
|
|
42145
42456
|
* Relative path to the archive directory used by `coder verify`.
|
|
42146
42457
|
*
|
|
42147
42458
|
* @private internal utility of `ptbk coder`
|
|
42148
42459
|
*/
|
|
42149
|
-
const PROMPTS_DONE_DIRECTORY_PATH = path.join(PROMPTS_DIRECTORY_PATH, 'done');
|
|
42460
|
+
const PROMPTS_DONE_DIRECTORY_PATH = path.join(PROMPTS_DIRECTORY_PATH$1, 'done');
|
|
42150
42461
|
/**
|
|
42151
42462
|
* Relative path to the project-owned boilerplate templates directory.
|
|
42152
42463
|
*
|
|
42153
42464
|
* @private internal utility of `ptbk coder`
|
|
42154
42465
|
*/
|
|
42155
|
-
const PROMPTS_TEMPLATES_DIRECTORY_PATH = path.join(PROMPTS_DIRECTORY_PATH, 'templates');
|
|
42466
|
+
const PROMPTS_TEMPLATES_DIRECTORY_PATH = path.join(PROMPTS_DIRECTORY_PATH$1, 'templates');
|
|
42467
|
+
/**
|
|
42468
|
+
* Relative path to the project-agnostic `common` boilerplate template referenced by the default coder scripts.
|
|
42469
|
+
*
|
|
42470
|
+
* @private internal utility of `ptbk coder`
|
|
42471
|
+
*/
|
|
42472
|
+
const COMMON_PROMPT_TEMPLATE_FILE_PATH = path.join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'common.md');
|
|
42156
42473
|
/**
|
|
42157
42474
|
* Built-in boilerplate templates available to `coder generate-boilerplates`.
|
|
42158
42475
|
*
|
|
@@ -42161,7 +42478,7 @@
|
|
|
42161
42478
|
const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
|
|
42162
42479
|
{
|
|
42163
42480
|
id: 'common',
|
|
42164
|
-
relativeFilePath:
|
|
42481
|
+
relativeFilePath: COMMON_PROMPT_TEMPLATE_FILE_PATH,
|
|
42165
42482
|
slugPrefix: null,
|
|
42166
42483
|
content: _spaceTrim.spaceTrim(`
|
|
42167
42484
|
- @@@
|
|
@@ -42207,30 +42524,17 @@
|
|
|
42207
42524
|
return definition;
|
|
42208
42525
|
}
|
|
42209
42526
|
/**
|
|
42210
|
-
* Ensures
|
|
42527
|
+
* Ensures one project-owned coder template file exists without overwriting user customizations.
|
|
42211
42528
|
*
|
|
42212
42529
|
* @private internal utility of `ptbk coder`
|
|
42213
42530
|
*/
|
|
42214
|
-
async function
|
|
42215
|
-
const
|
|
42216
|
-
|
|
42217
|
-
|
|
42218
|
-
if (await isExistingFile$3(absoluteTemplatePath)) {
|
|
42219
|
-
ensuredTemplateFiles.push({
|
|
42220
|
-
id: definition.id,
|
|
42221
|
-
relativeFilePath: definition.relativeFilePath,
|
|
42222
|
-
status: 'unchanged',
|
|
42223
|
-
});
|
|
42224
|
-
continue;
|
|
42225
|
-
}
|
|
42226
|
-
await promises.writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
|
|
42227
|
-
ensuredTemplateFiles.push({
|
|
42228
|
-
id: definition.id,
|
|
42229
|
-
relativeFilePath: definition.relativeFilePath,
|
|
42230
|
-
status: 'created',
|
|
42231
|
-
});
|
|
42531
|
+
async function ensureDefaultCoderPromptTemplateFile(projectPath, definition) {
|
|
42532
|
+
const absoluteTemplatePath = path.join(projectPath, definition.relativeFilePath);
|
|
42533
|
+
if (await isExistingFile$3(absoluteTemplatePath)) {
|
|
42534
|
+
return 'unchanged';
|
|
42232
42535
|
}
|
|
42233
|
-
|
|
42536
|
+
await promises.writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
|
|
42537
|
+
return 'created';
|
|
42234
42538
|
}
|
|
42235
42539
|
/**
|
|
42236
42540
|
* Resolves the template requested by `coder generate-boilerplates`.
|
|
@@ -42386,6 +42690,8 @@
|
|
|
42386
42690
|
- \`ptbk coder add\`
|
|
42387
42691
|
|
|
42388
42692
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
42693
|
+
|
|
42694
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
42389
42695
|
`));
|
|
42390
42696
|
command.argument('[description]', 'Plain-language description of the feature or task to implement');
|
|
42391
42697
|
command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
|
|
@@ -42397,11 +42703,13 @@
|
|
|
42397
42703
|
.join(', ')}) or a markdown file path relative to the current project root.
|
|
42398
42704
|
`));
|
|
42399
42705
|
addCoderGitSyncOptions(command);
|
|
42706
|
+
addQuestionsOption(command);
|
|
42400
42707
|
command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
|
|
42401
42708
|
const { priority, template: templateOption } = cliOptions;
|
|
42402
42709
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
42710
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
42403
42711
|
const projectPath = process.cwd();
|
|
42404
|
-
const description = await resolveCoderPromptDescription(descriptionArgument);
|
|
42712
|
+
const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
|
|
42405
42713
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
42406
42714
|
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
42407
42715
|
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
@@ -42436,7 +42744,7 @@
|
|
|
42436
42744
|
// Note: Import these dynamically to avoid circular dependencies and keep the CLI fast
|
|
42437
42745
|
const { buildPromptFilename, getPromptNumbering } = await Promise.resolve().then(function () { return getPromptNumbering$1; });
|
|
42438
42746
|
const { formatPromptEmojiTag, getFreshPromptEmojiTags } = await Promise.resolve().then(function () { return promptEmojiTags; });
|
|
42439
|
-
const promptsDirectory = path.join(projectPath, PROMPTS_DIRECTORY_PATH);
|
|
42747
|
+
const promptsDirectory = path.join(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
42440
42748
|
fs.mkdirSync(promptsDirectory, { recursive: true });
|
|
42441
42749
|
const promptTemplate = await resolveCoderPromptTemplate({ projectPath, templateOption });
|
|
42442
42750
|
const promptNumbering = await getPromptNumbering({
|
|
@@ -42460,7 +42768,7 @@
|
|
|
42460
42768
|
});
|
|
42461
42769
|
const slug = buildAddPromptSlug(promptTemplate.slugPrefix, title);
|
|
42462
42770
|
const filename = buildPromptFilename(promptNumbering.datePrefix, promptNumbering.startNumber, slug);
|
|
42463
|
-
const filePath = path.join(PROMPTS_DIRECTORY_PATH, filename);
|
|
42771
|
+
const filePath = path.join(PROMPTS_DIRECTORY_PATH$1, filename);
|
|
42464
42772
|
const absoluteFilePath = path.join(projectPath, filePath);
|
|
42465
42773
|
fs.writeFileSync(absoluteFilePath, `${section}\n`, 'utf-8');
|
|
42466
42774
|
console.info(colors__default["default"].green(`✓ Added prompt ${emojiTag} in ${filePath}`));
|
|
@@ -42471,7 +42779,7 @@
|
|
|
42471
42779
|
*
|
|
42472
42780
|
* @private internal utility of `coder add` command
|
|
42473
42781
|
*/
|
|
42474
|
-
async function resolveCoderPromptDescription(descriptionArgument) {
|
|
42782
|
+
async function resolveCoderPromptDescription(descriptionArgument, { isAskingQuestionsEnabled }) {
|
|
42475
42783
|
if (descriptionArgument !== undefined && descriptionArgument.trim() !== '') {
|
|
42476
42784
|
return descriptionArgument.trim();
|
|
42477
42785
|
}
|
|
@@ -42489,6 +42797,14 @@
|
|
|
42489
42797
|
}
|
|
42490
42798
|
return standardInputDescription;
|
|
42491
42799
|
}
|
|
42800
|
+
if (!isAskingQuestionsEnabled) {
|
|
42801
|
+
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
42802
|
+
Cannot add a prompt without a description.
|
|
42803
|
+
|
|
42804
|
+
The description can not be typed interactively because \`--no-questions\` is used — provide it as an argument or pipe it through stdin instead:
|
|
42805
|
+
- \`ptbk coder add --no-questions "some new feature"\`
|
|
42806
|
+
`));
|
|
42807
|
+
}
|
|
42492
42808
|
const { default: prompts } = await loadPromptsModule();
|
|
42493
42809
|
const response = await prompts({
|
|
42494
42810
|
type: 'text',
|
|
@@ -43052,10 +43368,10 @@
|
|
|
43052
43368
|
const { buildPromptFilename, getPromptNumbering } = await Promise.resolve().then(function () { return getPromptNumbering$1; });
|
|
43053
43369
|
const { formatPromptEmojiTag, getFreshPromptEmojiTags } = await Promise.resolve().then(function () { return promptEmojiTags; });
|
|
43054
43370
|
console.info(`🚀 Generate prompt boilerplate files`);
|
|
43055
|
-
fs.mkdirSync(path.join(projectPath, PROMPTS_DIRECTORY_PATH), { recursive: true });
|
|
43371
|
+
fs.mkdirSync(path.join(projectPath, PROMPTS_DIRECTORY_PATH$1), { recursive: true });
|
|
43056
43372
|
const promptTemplate = await resolveCoderPromptTemplate({ projectPath, templateOption });
|
|
43057
43373
|
const promptNumbering = await getPromptNumbering({
|
|
43058
|
-
promptsDir: path.join(projectPath, PROMPTS_DIRECTORY_PATH),
|
|
43374
|
+
promptsDir: path.join(projectPath, PROMPTS_DIRECTORY_PATH$1),
|
|
43059
43375
|
step: 10,
|
|
43060
43376
|
ignoreGlobs: ['**/node_modules/**'],
|
|
43061
43377
|
});
|
|
@@ -43079,7 +43395,7 @@
|
|
|
43079
43395
|
const title = titles[i % titles.length];
|
|
43080
43396
|
const emojiTag = formatPromptEmojiTag(selectedEmojis[i]);
|
|
43081
43397
|
const filename = buildPromptFilename(promptNumbering.datePrefix, number, buildPromptSlug$1(promptTemplate.slugPrefix, title));
|
|
43082
|
-
const filepath = path.join(PROMPTS_DIRECTORY_PATH, filename);
|
|
43398
|
+
const filepath = path.join(PROMPTS_DIRECTORY_PATH$1, filename);
|
|
43083
43399
|
const absoluteFilepath = path.join(projectPath, filepath);
|
|
43084
43400
|
const content = buildBoilerplatePromptFileContent({
|
|
43085
43401
|
emojiTag,
|
|
@@ -43139,10 +43455,15 @@
|
|
|
43139
43455
|
*
|
|
43140
43456
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin.
|
|
43141
43457
|
*
|
|
43142
|
-
* @returns `true` when the user confirms, `false` when the user declines
|
|
43458
|
+
* @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
|
|
43459
|
+
* or the questions are disabled by `--no-questions`.
|
|
43143
43460
|
* @private internal utility of `promptbookCli`
|
|
43144
43461
|
*/
|
|
43145
|
-
async function $askForConfirmation(question) {
|
|
43462
|
+
async function $askForConfirmation(question, { isAskingQuestionsEnabled }) {
|
|
43463
|
+
if (!isAskingQuestionsEnabled) {
|
|
43464
|
+
// Note: `--no-questions` forbids asking, so the optional change is declined without blocking the run.
|
|
43465
|
+
return false;
|
|
43466
|
+
}
|
|
43146
43467
|
if (!process.stdin.isTTY) {
|
|
43147
43468
|
// Note: In non-interactive environments like CI there is nobody who could confirm the change.
|
|
43148
43469
|
return false;
|
|
@@ -43165,11 +43486,12 @@
|
|
|
43165
43486
|
*
|
|
43166
43487
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
|
|
43167
43488
|
*
|
|
43168
|
-
* @returns `true` when the user confirms, `false` when the user declines
|
|
43489
|
+
* @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
|
|
43490
|
+
* or the questions are disabled by `--no-questions`
|
|
43169
43491
|
* @private internal utility of `promptbookCli`
|
|
43170
43492
|
*/
|
|
43171
|
-
async function $askForNpmPackageInstallationApproval(question) {
|
|
43172
|
-
return $askForConfirmation(question);
|
|
43493
|
+
async function $askForNpmPackageInstallationApproval(question, questionsOptions) {
|
|
43494
|
+
return $askForConfirmation(question, questionsOptions);
|
|
43173
43495
|
}
|
|
43174
43496
|
// Note: [🟡] Code for CLI npm package installation approval [$askForNpmPackageInstallationApproval](src/cli/cli-commands/common/npm/$askForNpmPackageInstallationApproval.ts) should never be published outside of `@promptbook/cli`
|
|
43175
43497
|
|
|
@@ -43359,7 +43681,7 @@
|
|
|
43359
43681
|
*
|
|
43360
43682
|
* @private internal utility of `promptbookCli`
|
|
43361
43683
|
*/
|
|
43362
|
-
async function $applyHarnessInstallationStatus(status) {
|
|
43684
|
+
async function $applyHarnessInstallationStatus(status, questionsOptions) {
|
|
43363
43685
|
const { definition, installationState, installedVersion } = status;
|
|
43364
43686
|
if (installationState === 'up-to-date') {
|
|
43365
43687
|
console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
|
|
@@ -43375,17 +43697,17 @@
|
|
|
43375
43697
|
}
|
|
43376
43698
|
console.warn(colors__default["default"].yellow(formatHarnessInstallationWarning(status)));
|
|
43377
43699
|
if (installationState === 'not-installed') {
|
|
43378
|
-
await $applyMissingHarnessInstallation(definition);
|
|
43700
|
+
await $applyMissingHarnessInstallation(definition, questionsOptions);
|
|
43379
43701
|
return;
|
|
43380
43702
|
}
|
|
43381
|
-
await $applyOutdatedHarnessInstallation(status);
|
|
43703
|
+
await $applyOutdatedHarnessInstallation(status, questionsOptions);
|
|
43382
43704
|
}
|
|
43383
43705
|
/**
|
|
43384
43706
|
* Offers to install a missing harness globally through npm.
|
|
43385
43707
|
*/
|
|
43386
|
-
async function $applyMissingHarnessInstallation(definition) {
|
|
43708
|
+
async function $applyMissingHarnessInstallation(definition, questionsOptions) {
|
|
43387
43709
|
const installCommand = buildHarnessInstallCommand(definition);
|
|
43388
|
-
const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now
|
|
43710
|
+
const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`, questionsOptions);
|
|
43389
43711
|
if (!isInstallationApproved) {
|
|
43390
43712
|
console.info(colors__default["default"].gray(`Skipped, run \`${installCommand}\` to do it manually.`));
|
|
43391
43713
|
return;
|
|
@@ -43400,14 +43722,14 @@
|
|
|
43400
43722
|
/**
|
|
43401
43723
|
* Offers to update an outdated harness exactly where it is installed.
|
|
43402
43724
|
*/
|
|
43403
|
-
async function $applyOutdatedHarnessInstallation(status) {
|
|
43725
|
+
async function $applyOutdatedHarnessInstallation(status, questionsOptions) {
|
|
43404
43726
|
const { definition, latestVersion, installationOrigin } = status;
|
|
43405
43727
|
const updatePlan = resolveHarnessUpdatePlan(definition, installationOrigin.installationMethod);
|
|
43406
43728
|
if (updatePlan.command === null || !updatePlan.isRunnableByPromptbook) {
|
|
43407
43729
|
console.info(colors__default["default"].gray(formatHarnessManualUpdateInstruction(status, updatePlan)));
|
|
43408
43730
|
return;
|
|
43409
43731
|
}
|
|
43410
|
-
const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now
|
|
43732
|
+
const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`, questionsOptions);
|
|
43411
43733
|
if (!isUpdateApproved) {
|
|
43412
43734
|
console.info(colors__default["default"].gray(`Skipped, run \`${updatePlan.command}\` to do it manually.`));
|
|
43413
43735
|
return;
|
|
@@ -43843,7 +44165,7 @@
|
|
|
43843
44165
|
// Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
|
|
43844
44166
|
|
|
43845
44167
|
/**
|
|
43846
|
-
* Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
|
|
44168
|
+
* Checks that every given CLI coding harness is installed globally and, when the questions are enabled, up to date.
|
|
43847
44169
|
* Offers to install missing harnesses and update outdated ones.
|
|
43848
44170
|
*
|
|
43849
44171
|
* Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
|
|
@@ -43853,15 +44175,18 @@
|
|
|
43853
44175
|
*
|
|
43854
44176
|
* @private internal utility of `promptbookCli`
|
|
43855
44177
|
*/
|
|
43856
|
-
async function $ensureHarnessInstallations(harnessNames,
|
|
44178
|
+
async function $ensureHarnessInstallations(harnessNames, questionsOptions) {
|
|
43857
44179
|
const definitions = resolveCheckedHarnessDefinitions(harnessNames);
|
|
43858
44180
|
if (definitions.length === 0) {
|
|
43859
44181
|
return;
|
|
43860
44182
|
}
|
|
44183
|
+
// Note: An outdated harness can be updated only after the user confirms it, so `--no-questions`
|
|
44184
|
+
// skips the slower npm registry lookup entirely and only reports what is installed.
|
|
44185
|
+
const isHarnessUpdateCheckEnabled = questionsOptions.isAskingQuestionsEnabled;
|
|
43861
44186
|
// Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
|
|
43862
44187
|
const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)));
|
|
43863
44188
|
for (const status of statuses) {
|
|
43864
|
-
await $applyHarnessInstallationStatus(status);
|
|
44189
|
+
await $applyHarnessInstallationStatus(status, questionsOptions);
|
|
43865
44190
|
}
|
|
43866
44191
|
}
|
|
43867
44192
|
/**
|
|
@@ -43873,48 +44198,6 @@
|
|
|
43873
44198
|
}
|
|
43874
44199
|
// Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
|
|
43875
44200
|
|
|
43876
|
-
/**
|
|
43877
|
-
* Registers the shared `--no-harness-update` option on a `ptbk coder` command that uses a coding harness.
|
|
43878
|
-
*
|
|
43879
|
-
* @private internal utility of `promptbookCli`
|
|
43880
|
-
*/
|
|
43881
|
-
function addHarnessUpdateOption(command) {
|
|
43882
|
-
command.option('--no-harness-update', 'Skip checking whether the installed coding harnesses are up to date');
|
|
43883
|
-
}
|
|
43884
|
-
/**
|
|
43885
|
-
* Converts the Commander harness-update flag into the normalized harness-check option.
|
|
43886
|
-
*
|
|
43887
|
-
* @private internal utility of `promptbookCli`
|
|
43888
|
-
*/
|
|
43889
|
-
function normalizeHarnessUpdateCliOptions(cliOptions) {
|
|
43890
|
-
return {
|
|
43891
|
-
isHarnessUpdateCheckEnabled: cliOptions.harnessUpdate,
|
|
43892
|
-
};
|
|
43893
|
-
}
|
|
43894
|
-
// Note: [🟡] Code for CLI harness update options [harnessUpdateCliOptions](src/cli/cli-commands/common/harnessUpdateCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
43895
|
-
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
43896
|
-
|
|
43897
|
-
/**
|
|
43898
|
-
* Relative path to the Promptbook Coder quick-reference file initialized in project roots.
|
|
43899
|
-
*
|
|
43900
|
-
* @private internal utility of `ptbk coder`
|
|
43901
|
-
*/
|
|
43902
|
-
const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
|
|
43903
|
-
/**
|
|
43904
|
-
* Returns the default coder `AGENT_CODING.md` quick-reference content.
|
|
43905
|
-
*
|
|
43906
|
-
* @private internal utility of `ptbk coder`
|
|
43907
|
-
*/
|
|
43908
|
-
function getDefaultCoderAgentCodingFileContent() {
|
|
43909
|
-
return _spaceTrim.spaceTrim(`
|
|
43910
|
-
# ✨ Promptbook Coder agent coding
|
|
43911
|
-
|
|
43912
|
-
This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
|
|
43913
|
-
`);
|
|
43914
|
-
}
|
|
43915
|
-
// Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
|
|
43916
|
-
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
43917
|
-
|
|
43918
44201
|
/**
|
|
43919
44202
|
* Relative path to the shared coder context file initialized in project roots.
|
|
43920
44203
|
*
|
|
@@ -44016,6 +44299,51 @@
|
|
|
44016
44299
|
}
|
|
44017
44300
|
// Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
|
|
44018
44301
|
|
|
44302
|
+
/**
|
|
44303
|
+
* Creates the artifact definition of one project-owned coder prompt template.
|
|
44304
|
+
*/
|
|
44305
|
+
function createCoderPromptTemplateArtifactDefinition(definition) {
|
|
44306
|
+
return {
|
|
44307
|
+
relativeFilePath: definition.relativeFilePath,
|
|
44308
|
+
ensureArtifactFile: (projectPath) => ensureDefaultCoderPromptTemplateFile(projectPath, definition),
|
|
44309
|
+
};
|
|
44310
|
+
}
|
|
44311
|
+
/**
|
|
44312
|
+
* Every artifact which the default coder scripts can reference.
|
|
44313
|
+
*/
|
|
44314
|
+
const CODER_REFERENCED_ARTIFACT_DEFINITIONS = [
|
|
44315
|
+
{
|
|
44316
|
+
relativeFilePath: CODER_DEVELOPER_AGENT_FILE_PATH,
|
|
44317
|
+
ensureArtifactFile: ensureCoderDeveloperAgentFile,
|
|
44318
|
+
},
|
|
44319
|
+
{
|
|
44320
|
+
relativeFilePath: AGENTS_FILE_PATH,
|
|
44321
|
+
ensureArtifactFile: (projectPath) => ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent()),
|
|
44322
|
+
},
|
|
44323
|
+
...getDefaultCoderProjectPromptTemplateDefinitions().map(createCoderPromptTemplateArtifactDefinition),
|
|
44324
|
+
];
|
|
44325
|
+
/**
|
|
44326
|
+
* Creates only those referenced artifacts which the freshly added coder scripts point at.
|
|
44327
|
+
*
|
|
44328
|
+
* An artifact of a script which the project already defines is intentionally **not** created - the project-owned
|
|
44329
|
+
* script is kept as is, so it does not reference the default artifact in the first place.
|
|
44330
|
+
*
|
|
44331
|
+
* @private function of `initializeCoderProjectConfiguration`
|
|
44332
|
+
*/
|
|
44333
|
+
async function ensureCoderReferencedArtifacts(projectPath, referencedArtifactPaths) {
|
|
44334
|
+
const ensuredArtifacts = [];
|
|
44335
|
+
for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
|
|
44336
|
+
if (!referencedArtifactPaths.has(relativeFilePath)) {
|
|
44337
|
+
ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
|
|
44338
|
+
continue;
|
|
44339
|
+
}
|
|
44340
|
+
ensuredArtifacts.push({ relativeFilePath, status: await ensureArtifactFile(projectPath) });
|
|
44341
|
+
}
|
|
44342
|
+
return ensuredArtifacts;
|
|
44343
|
+
}
|
|
44344
|
+
// Note: [🟡] Code for coder init referenced artifacts [coderReferencedArtifacts](src/cli/cli-commands/coder/coderReferencedArtifacts.ts) should never be published outside of `@promptbook/cli`
|
|
44345
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44346
|
+
|
|
44019
44347
|
/**
|
|
44020
44348
|
* Fallback `.env` content used when no required variables need to be appended.
|
|
44021
44349
|
*/
|
|
@@ -44110,26 +44438,79 @@
|
|
|
44110
44438
|
}
|
|
44111
44439
|
// Note: [🟡] Code for coder init gitignore bootstrapping [ensureCoderGitignoreFile](src/cli/cli-commands/coder/ensureCoderGitignoreFile.ts) should never be published outside of `@promptbook/cli`
|
|
44112
44440
|
|
|
44441
|
+
/**
|
|
44442
|
+
* Name of the npm script which `ptbk coder run` uses to verify the project.
|
|
44443
|
+
*/
|
|
44444
|
+
const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
|
|
44113
44445
|
/**
|
|
44114
44446
|
* Default npm scripts initialized by `ptbk coder init`.
|
|
44447
|
+
*
|
|
44448
|
+
* Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
|
|
44115
44449
|
*/
|
|
44116
|
-
const
|
|
44117
|
-
|
|
44118
|
-
|
|
44119
|
-
|
|
44120
|
-
|
|
44121
|
-
|
|
44122
|
-
|
|
44123
|
-
|
|
44450
|
+
const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS = [
|
|
44451
|
+
{
|
|
44452
|
+
scriptName: 'coder:generate-boilerplates',
|
|
44453
|
+
scriptCommand: `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
|
|
44454
|
+
referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
|
|
44455
|
+
},
|
|
44456
|
+
{
|
|
44457
|
+
scriptName: 'coder:add',
|
|
44458
|
+
scriptCommand: `npx ptbk coder add --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
|
|
44459
|
+
referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
|
|
44460
|
+
},
|
|
44461
|
+
{
|
|
44462
|
+
scriptName: 'coder:run',
|
|
44463
|
+
scriptCommand: [
|
|
44464
|
+
'npx ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max',
|
|
44465
|
+
`--agent ${formatDisplayPath(CODER_DEVELOPER_AGENT_FILE_PATH)}`,
|
|
44466
|
+
`--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
|
|
44467
|
+
`--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
|
|
44468
|
+
].join(' '),
|
|
44469
|
+
referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
|
|
44470
|
+
},
|
|
44471
|
+
// { scriptName: 'coder:find-refactor-candidates', scriptCommand: 'npx ptbk coder find-refactor-candidates', referencedArtifactPaths: [] },
|
|
44472
|
+
{
|
|
44473
|
+
scriptName: 'coder:verify',
|
|
44474
|
+
scriptCommand: 'npx ptbk coder verify',
|
|
44475
|
+
referencedArtifactPaths: [],
|
|
44476
|
+
},
|
|
44477
|
+
{
|
|
44478
|
+
// Note: The verification command of `coder:run` is a project-owned script, so every project can decide
|
|
44479
|
+
// what "verified" means without touching the `coder:run` script itself
|
|
44480
|
+
scriptName: CODER_TEST_SCRIPT_NAME,
|
|
44481
|
+
scriptCommand: 'npm test',
|
|
44482
|
+
referencedArtifactPaths: [],
|
|
44483
|
+
},
|
|
44484
|
+
];
|
|
44124
44485
|
/**
|
|
44125
44486
|
* Lists the default npm scripts initialized by `ptbk coder init`.
|
|
44126
44487
|
*
|
|
44127
44488
|
* @private internal utility of `coder init` command
|
|
44128
44489
|
*/
|
|
44129
44490
|
function getDefaultCoderPackageJsonScripts() {
|
|
44130
|
-
return
|
|
44491
|
+
return Object.fromEntries(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.map(({ scriptName, scriptCommand }) => [
|
|
44492
|
+
scriptName,
|
|
44493
|
+
scriptCommand,
|
|
44494
|
+
]));
|
|
44495
|
+
}
|
|
44496
|
+
/**
|
|
44497
|
+
* Collects the project-relative artifact paths referenced by the given default coder scripts.
|
|
44498
|
+
*
|
|
44499
|
+
* @private internal utility of `coder init` command
|
|
44500
|
+
*/
|
|
44501
|
+
function resolveCoderPackageJsonScriptReferencedArtifactPaths(scriptNames) {
|
|
44502
|
+
return new Set(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.filter(({ scriptName }) => scriptNames.includes(scriptName))
|
|
44503
|
+
.map(({ referencedArtifactPaths }) => referencedArtifactPaths)
|
|
44504
|
+
.flat());
|
|
44505
|
+
}
|
|
44506
|
+
/**
|
|
44507
|
+
* Formats one project-relative path as an explicitly project-rooted path usable inside a shell command.
|
|
44508
|
+
*/
|
|
44509
|
+
function formatCoderScriptFilePath(relativeFilePath) {
|
|
44510
|
+
return `./${formatDisplayPath(relativeFilePath)}`;
|
|
44131
44511
|
}
|
|
44132
44512
|
// Note: [🟡] Code for coder init package scripts [getDefaultCoderPackageJsonScripts](src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts) should never be published outside of `@promptbook/cli`
|
|
44513
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44133
44514
|
|
|
44134
44515
|
/**
|
|
44135
44516
|
* Loads the TypeScript runtime used for parsing JSONC-style project files.
|
|
@@ -44162,6 +44543,9 @@
|
|
|
44162
44543
|
/**
|
|
44163
44544
|
* Ensures one JSON object field contains the provided string-record entries.
|
|
44164
44545
|
*
|
|
44546
|
+
* Entries which the project already defines are **never** overridden - only missing keys are added,
|
|
44547
|
+
* so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
|
|
44548
|
+
*
|
|
44165
44549
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44166
44550
|
*/
|
|
44167
44551
|
async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, }) {
|
|
@@ -44173,21 +44557,24 @@
|
|
|
44173
44557
|
const formatting = detectJsonFileFormatting(fileContent);
|
|
44174
44558
|
const jsonObject = fileContent === undefined ? {} : await parseJsonObjectFile(relativeFilePath, fileContent);
|
|
44175
44559
|
const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
|
|
44176
|
-
|
|
44560
|
+
const addedEntryKeys = [];
|
|
44177
44561
|
const mergedEntries = { ...existingEntries };
|
|
44178
44562
|
for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
|
|
44179
|
-
if (mergedEntries
|
|
44180
|
-
|
|
44181
|
-
|
|
44563
|
+
if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
|
|
44564
|
+
// Note: The project already defines this entry, keep its own value untouched
|
|
44565
|
+
continue;
|
|
44182
44566
|
}
|
|
44567
|
+
mergedEntries[entryKey] = entryValue;
|
|
44568
|
+
addedEntryKeys.push(entryKey);
|
|
44183
44569
|
}
|
|
44570
|
+
const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
|
|
44184
44571
|
if (!hasChanges) {
|
|
44185
|
-
return 'unchanged';
|
|
44572
|
+
return { status: 'unchanged', addedEntryKeys };
|
|
44186
44573
|
}
|
|
44187
44574
|
const nextJsonObject = { ...jsonObject };
|
|
44188
44575
|
nextJsonObject[fieldPath] = mergedEntries;
|
|
44189
44576
|
await promises.writeFile(absoluteFilePath, serializeJsonObject(nextJsonObject, formatting), 'utf-8');
|
|
44190
|
-
return fileContent === undefined ? 'created' : 'updated';
|
|
44577
|
+
return { status: fileContent === undefined ? 'created' : 'updated', addedEntryKeys };
|
|
44191
44578
|
}
|
|
44192
44579
|
/**
|
|
44193
44580
|
* Parses one JSON object file while accepting VS Code style comments and trailing commas.
|
|
@@ -44276,6 +44663,8 @@
|
|
|
44276
44663
|
/**
|
|
44277
44664
|
* Ensures `package.json` contains the standalone Promptbook coder helper scripts.
|
|
44278
44665
|
*
|
|
44666
|
+
* Scripts which the project already defines are kept as they are, only missing ones are added.
|
|
44667
|
+
*
|
|
44279
44668
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44280
44669
|
*/
|
|
44281
44670
|
async function ensureCoderPackageJsonFile(projectPath) {
|
|
@@ -44329,17 +44718,20 @@
|
|
|
44329
44718
|
/**
|
|
44330
44719
|
* Ensures VS Code routes pasted prompt images into `prompts/screenshots`.
|
|
44331
44720
|
*
|
|
44721
|
+
* Settings which the project already defines are kept as they are, only missing ones are added.
|
|
44722
|
+
*
|
|
44332
44723
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44333
44724
|
*/
|
|
44334
44725
|
async function ensureCoderVscodeSettingsFile(projectPath) {
|
|
44335
44726
|
const [fieldPath, nextEntries] = resolveDefaultCoderVscodeSettingsEntry();
|
|
44336
|
-
|
|
44727
|
+
const { status } = await mergeStringRecordJsonFile({
|
|
44337
44728
|
projectPath,
|
|
44338
44729
|
relativeFilePath: VSCODE_SETTINGS_FILE_PATH,
|
|
44339
44730
|
fieldPath,
|
|
44340
44731
|
nextEntries,
|
|
44341
44732
|
ensureParentDirectoryPath: VSCODE_DIRECTORY_PATH,
|
|
44342
44733
|
});
|
|
44734
|
+
return status;
|
|
44343
44735
|
}
|
|
44344
44736
|
/**
|
|
44345
44737
|
* Resolves the default string-record entry that `coder init` merges into VS Code settings.
|
|
@@ -44356,34 +44748,32 @@
|
|
|
44356
44748
|
/**
|
|
44357
44749
|
* Creates or updates all coder configuration artifacts required in the current project.
|
|
44358
44750
|
*
|
|
44751
|
+
* Nothing the project already owns is ever overwritten - existing scripts, settings and files are kept as they are,
|
|
44752
|
+
* and the artifacts they would reference are created only together with the scripts which actually reference them.
|
|
44753
|
+
*
|
|
44359
44754
|
* @private internal utility of `coder init` command
|
|
44360
44755
|
*/
|
|
44361
44756
|
async function initializeCoderProjectConfiguration(projectPath) {
|
|
44362
|
-
const promptsDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DIRECTORY_PATH);
|
|
44757
|
+
const promptsDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
44363
44758
|
const promptsDoneDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DONE_DIRECTORY_PATH);
|
|
44364
44759
|
const promptsTemplatesDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_TEMPLATES_DIRECTORY_PATH);
|
|
44365
|
-
const promptTemplateFileStatuses = await ensureDefaultCoderPromptTemplateFiles(projectPath);
|
|
44366
44760
|
const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
|
|
44367
|
-
const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
|
|
44368
|
-
const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
|
|
44369
|
-
const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
|
|
44370
44761
|
const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
|
|
44371
44762
|
const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
|
|
44372
|
-
const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
|
|
44763
|
+
const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await ensureCoderPackageJsonFile(projectPath);
|
|
44373
44764
|
const vscodeSettingsFileStatus = await ensureCoderVscodeSettingsFile(projectPath);
|
|
44765
|
+
const referencedArtifactStatuses = await ensureCoderReferencedArtifacts(projectPath, resolveCoderPackageJsonScriptReferencedArtifactPaths(addedPackageJsonScriptNames));
|
|
44374
44766
|
return {
|
|
44375
44767
|
promptsDirectoryStatus,
|
|
44376
44768
|
promptsDoneDirectoryStatus,
|
|
44377
44769
|
promptsTemplatesDirectoryStatus,
|
|
44378
|
-
promptTemplateFileStatuses,
|
|
44379
44770
|
agentsDirectoryStatus,
|
|
44380
|
-
developerAgentFileStatus,
|
|
44381
|
-
agentsFileStatus,
|
|
44382
|
-
agentCodingFileStatus,
|
|
44383
44771
|
envFileStatus,
|
|
44384
44772
|
gitignoreFileStatus,
|
|
44385
44773
|
packageJsonFileStatus,
|
|
44386
44774
|
vscodeSettingsFileStatus,
|
|
44775
|
+
addedPackageJsonScriptNames,
|
|
44776
|
+
referencedArtifactStatuses,
|
|
44387
44777
|
initializedEnvVariableNames,
|
|
44388
44778
|
};
|
|
44389
44779
|
}
|
|
@@ -44399,17 +44789,20 @@
|
|
|
44399
44789
|
printInitializationStatusLine('prompts/', summary.promptsDirectoryStatus);
|
|
44400
44790
|
printInitializationStatusLine('prompts/done/', summary.promptsDoneDirectoryStatus);
|
|
44401
44791
|
printInitializationStatusLine('prompts/templates/', summary.promptsTemplatesDirectoryStatus);
|
|
44402
|
-
for (const templateFileStatus of summary.promptTemplateFileStatuses) {
|
|
44403
|
-
printInitializationStatusLine(formatDisplayPath(templateFileStatus.relativeFilePath), templateFileStatus.status);
|
|
44404
|
-
}
|
|
44405
44792
|
printInitializationStatusLine('agents/', summary.agentsDirectoryStatus);
|
|
44406
|
-
printInitializationStatusLine(CODER_DEVELOPER_AGENT_FILE_PATH, summary.developerAgentFileStatus);
|
|
44407
|
-
printInitializationStatusLine(AGENTS_FILE_PATH, summary.agentsFileStatus);
|
|
44408
|
-
printInitializationStatusLine(AGENT_CODING_FILE_PATH, summary.agentCodingFileStatus);
|
|
44409
44793
|
printInitializationStatusLine('.env', summary.envFileStatus);
|
|
44410
44794
|
printInitializationStatusLine('.gitignore', summary.gitignoreFileStatus);
|
|
44411
44795
|
printInitializationStatusLine('package.json', summary.packageJsonFileStatus);
|
|
44412
44796
|
printInitializationStatusLine('.vscode/settings.json', summary.vscodeSettingsFileStatus);
|
|
44797
|
+
for (const referencedArtifactStatus of summary.referencedArtifactStatuses) {
|
|
44798
|
+
printInitializationStatusLine(formatDisplayPath(referencedArtifactStatus.relativeFilePath), referencedArtifactStatus.status);
|
|
44799
|
+
}
|
|
44800
|
+
if (summary.addedPackageJsonScriptNames.length > 0) {
|
|
44801
|
+
printInitializationNote(`Added npm scripts: ${summary.addedPackageJsonScriptNames.join(', ')}`, colors__default["default"].cyan);
|
|
44802
|
+
}
|
|
44803
|
+
else {
|
|
44804
|
+
printInitializationNote('All Promptbook coder npm scripts are already present.', colors__default["default"].gray);
|
|
44805
|
+
}
|
|
44413
44806
|
if (summary.initializedEnvVariableNames.length > 0) {
|
|
44414
44807
|
printInitializationNote(`Added env variables: ${summary.initializedEnvVariableNames.join(', ')}`, colors__default["default"].cyan);
|
|
44415
44808
|
}
|
|
@@ -44427,6 +44820,9 @@
|
|
|
44427
44820
|
if (status === 'updated') {
|
|
44428
44821
|
return 'updated';
|
|
44429
44822
|
}
|
|
44823
|
+
if (status === 'not-referenced') {
|
|
44824
|
+
return 'not referenced by the added scripts, kept as is';
|
|
44825
|
+
}
|
|
44430
44826
|
return 'unchanged';
|
|
44431
44827
|
}
|
|
44432
44828
|
/**
|
|
@@ -44469,39 +44865,48 @@
|
|
|
44469
44865
|
${block(listDefaultCoderProjectPromptTemplateDisplayPaths())}
|
|
44470
44866
|
- ${CODER_DEVELOPER_AGENT_FILE_PATH}
|
|
44471
44867
|
- ${AGENTS_FILE_PATH}
|
|
44472
|
-
- ${AGENT_CODING_FILE_PATH}
|
|
44473
44868
|
- .gitignore with local artifacts from every supported harness
|
|
44474
44869
|
- package.json
|
|
44475
44870
|
- .vscode/settings.json
|
|
44476
44871
|
|
|
44872
|
+
Never overwrites what the project already owns:
|
|
44873
|
+
- Existing package.json scripts and .vscode/settings.json settings are kept, only missing ones are added
|
|
44874
|
+
- Files referenced by a script, like the agent and context of coder:run, are created only together
|
|
44875
|
+
with the script which references them
|
|
44876
|
+
|
|
44477
44877
|
Ensures required coding-agent environment variables in .env:
|
|
44478
44878
|
- CODING_AGENT_GIT_NAME
|
|
44479
44879
|
- CODING_AGENT_GIT_EMAIL
|
|
44480
44880
|
- CODING_AGENT_GIT_SIGNING_KEY
|
|
44481
44881
|
|
|
44482
|
-
Checks that the coding harnesses are installed and up to date unless \`--no-
|
|
44882
|
+
Checks that the coding harnesses are installed and up to date unless \`--no-questions\` is used:
|
|
44483
44883
|
${block(listCheckedHarnessLabels())}
|
|
44484
44884
|
|
|
44485
44885
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
44886
|
+
|
|
44887
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
44486
44888
|
`));
|
|
44487
44889
|
addCoderGitSyncOptions(command);
|
|
44488
|
-
|
|
44890
|
+
addQuestionsOption(command);
|
|
44489
44891
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
44490
44892
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
44491
|
-
const
|
|
44893
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
44492
44894
|
const projectPath = process.cwd();
|
|
44493
44895
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
44494
44896
|
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
44495
44897
|
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
44898
|
+
const isPromptsDirectoryEmpty = await isDirectoryEmpty(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
44496
44899
|
const summary = await initializeCoderProjectConfiguration(projectPath);
|
|
44497
44900
|
printInitializationSummary(summary);
|
|
44498
|
-
|
|
44901
|
+
if (isPromptsDirectoryEmpty) {
|
|
44902
|
+
await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
|
|
44903
|
+
}
|
|
44499
44904
|
await $commitCoderChanges({
|
|
44500
44905
|
gitSync,
|
|
44501
44906
|
commitScope,
|
|
44502
44907
|
commitMessage: 'Initialize Promptbook Coder',
|
|
44503
44908
|
});
|
|
44504
|
-
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES,
|
|
44909
|
+
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, questionsOptions);
|
|
44505
44910
|
}));
|
|
44506
44911
|
}
|
|
44507
44912
|
/**
|
|
@@ -44521,6 +44926,112 @@
|
|
|
44521
44926
|
// Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
|
|
44522
44927
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44523
44928
|
|
|
44929
|
+
/**
|
|
44930
|
+
* Creates a Commander argument parser that accepts only non-negative integers.
|
|
44931
|
+
*
|
|
44932
|
+
* The returned parser is meant to be passed as the coercion callback of `command.option(...)`.
|
|
44933
|
+
* It throws a branded `NotAllowed` error with a clear message referencing the given option name
|
|
44934
|
+
* when the provided value is not a non-negative integer.
|
|
44935
|
+
*
|
|
44936
|
+
* @private internal utility of `promptbookCli`
|
|
44937
|
+
*/
|
|
44938
|
+
function createNonNegativeIntegerOptionParser(optionName) {
|
|
44939
|
+
return (value) => {
|
|
44940
|
+
const parsedValue = Number(value);
|
|
44941
|
+
if (!Number.isInteger(parsedValue) || parsedValue < 0) {
|
|
44942
|
+
throw new NotAllowed(_spaceTrim.spaceTrim(`
|
|
44943
|
+
Invalid value for \`${optionName}\`: \`${value}\`.
|
|
44944
|
+
|
|
44945
|
+
Use a non-negative integer.
|
|
44946
|
+
`));
|
|
44947
|
+
}
|
|
44948
|
+
return parsedValue;
|
|
44949
|
+
};
|
|
44950
|
+
}
|
|
44951
|
+
// Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
|
|
44952
|
+
|
|
44953
|
+
/**
|
|
44954
|
+
* Registers the shared prompt-priority filter options on a queue-based `ptbk coder` command.
|
|
44955
|
+
*
|
|
44956
|
+
* @private internal utility of `promptbookCli`
|
|
44957
|
+
*/
|
|
44958
|
+
function addPromptPriorityOptions(command) {
|
|
44959
|
+
command.option('--priority <minimum-priority>', 'Alias for --min-priority; filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--priority'));
|
|
44960
|
+
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
44961
|
+
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
44962
|
+
}
|
|
44963
|
+
// Note: [🟡] Code for CLI prompt priority options [promptPriorityCliOptions](src/cli/cli-commands/common/promptPriorityCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
44964
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44965
|
+
|
|
44966
|
+
/**
|
|
44967
|
+
* Description shared by coder commands which accept an agent Book.
|
|
44968
|
+
*
|
|
44969
|
+
* @private internal utility of `ptbk coder`
|
|
44970
|
+
*/
|
|
44971
|
+
const CODER_AGENT_OPTION_DESCRIPTION = 'Path to a .book file used to personalize coding prompts and select agent-specific tasks';
|
|
44972
|
+
/**
|
|
44973
|
+
* Registers the shared `--agent` option for coder commands.
|
|
44974
|
+
*
|
|
44975
|
+
* @private internal utility of `ptbk coder`
|
|
44976
|
+
*/
|
|
44977
|
+
function addCoderAgentOption(command) {
|
|
44978
|
+
command.option('--agent <agent-book-path>', CODER_AGENT_OPTION_DESCRIPTION);
|
|
44979
|
+
}
|
|
44980
|
+
// Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
|
|
44981
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44982
|
+
|
|
44983
|
+
/**
|
|
44984
|
+
* Initializes `coder list` command for Promptbook CLI utilities.
|
|
44985
|
+
*
|
|
44986
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI.
|
|
44987
|
+
*
|
|
44988
|
+
* @private internal function of `promptbookCli`
|
|
44989
|
+
*/
|
|
44990
|
+
function $initializeCoderListCommand(program) {
|
|
44991
|
+
const command = program.command('list');
|
|
44992
|
+
command.description(_spaceTrim.spaceTrim(`
|
|
44993
|
+
List ready coding prompts by priority without executing them
|
|
44994
|
+
|
|
44995
|
+
${PROMPT_RUNNER_DESCRIPTION}
|
|
44996
|
+
|
|
44997
|
+
Features:
|
|
44998
|
+
- Lists only ready, fully authored prompts
|
|
44999
|
+
- Groups prompts from highest to lowest priority
|
|
45000
|
+
- Optional --harness, --model and --agent filters show only prompts compatible with that selection
|
|
45001
|
+
- Does not start a coding harness or modify prompt files
|
|
45002
|
+
`));
|
|
45003
|
+
addPromptRunnerSelectionOptions(command);
|
|
45004
|
+
addCoderAgentOption(command);
|
|
45005
|
+
addPromptPriorityOptions(command);
|
|
45006
|
+
command.action(handleActionErrors(async (cliOptions) => {
|
|
45007
|
+
const { agent, priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
|
|
45008
|
+
const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
|
|
45009
|
+
const resolvedCoderAgentBook = agent === undefined
|
|
45010
|
+
? undefined
|
|
45011
|
+
: await (await Promise.resolve().then(function () { return resolveCoderAgent$1; }))
|
|
45012
|
+
.resolveCoderAgentBook(agent, process.cwd());
|
|
45013
|
+
const promptRunnerIdentity = runnerOptions.agentName === undefined &&
|
|
45014
|
+
runnerOptions.model === undefined &&
|
|
45015
|
+
resolvedCoderAgentBook === undefined
|
|
45016
|
+
? undefined
|
|
45017
|
+
: {
|
|
45018
|
+
harnessName: runnerOptions.agentName,
|
|
45019
|
+
modelName: runnerOptions.model,
|
|
45020
|
+
agentReferences: resolvedCoderAgentBook === null || resolvedCoderAgentBook === void 0 ? void 0 : resolvedCoderAgentBook.agentReferences,
|
|
45021
|
+
};
|
|
45022
|
+
// Note: Import dynamically to avoid loading prompt parsing dependencies until this command is used.
|
|
45023
|
+
const { listCoderPrompts } = await Promise.resolve().then(function () { return listCoderPrompts$1; });
|
|
45024
|
+
await listCoderPrompts({
|
|
45025
|
+
priority,
|
|
45026
|
+
minimumPriority,
|
|
45027
|
+
maximumPriority,
|
|
45028
|
+
promptRunnerIdentity,
|
|
45029
|
+
});
|
|
45030
|
+
}));
|
|
45031
|
+
}
|
|
45032
|
+
// Note: [🟡] Code for CLI command [list](src/cli/cli-commands/coder/list.ts) should never be published outside of `@promptbook/cli`
|
|
45033
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
45034
|
+
|
|
44524
45035
|
/**
|
|
44525
45036
|
* Default wait duration applied before retrying a prompt round after an error (10 minutes).
|
|
44526
45037
|
*
|
|
@@ -44574,7 +45085,7 @@
|
|
|
44574
45085
|
*
|
|
44575
45086
|
* @private internal utility of `ptbk coder`
|
|
44576
45087
|
*/
|
|
44577
|
-
async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName) {
|
|
45088
|
+
async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName, questionsOptions) {
|
|
44578
45089
|
if (harnessName === undefined) {
|
|
44579
45090
|
return;
|
|
44580
45091
|
}
|
|
@@ -44585,7 +45096,7 @@
|
|
|
44585
45096
|
const { label } = getHarnessDefinition(harnessName);
|
|
44586
45097
|
const formattedRules = formatGitignoreRules(missingRules);
|
|
44587
45098
|
const entryLabel = missingRules.length === 1 ? 'entry' : 'entries';
|
|
44588
|
-
const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now
|
|
45099
|
+
const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now?`, questionsOptions);
|
|
44589
45100
|
if (!isAdditionApproved) {
|
|
44590
45101
|
console.info(colors__default["default"].gray(`Skipped, add ${formattedRules} to \`.gitignore\` manually.`));
|
|
44591
45102
|
return;
|
|
@@ -44610,22 +45121,24 @@
|
|
|
44610
45121
|
*/
|
|
44611
45122
|
function $initializeCoderPingCommand(program) {
|
|
44612
45123
|
const command = program.command('ping');
|
|
44613
|
-
command.description(_spaceTrim.spaceTrim(`
|
|
44614
|
-
|
|
45124
|
+
command.description(_spaceTrim.spaceTrim((block) => `
|
|
45125
|
+
Send one tiny dummy prompt to a harness and model to measure and warm them up
|
|
44615
45126
|
|
|
44616
|
-
|
|
45127
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
44617
45128
|
|
|
44618
|
-
|
|
44619
|
-
|
|
44620
|
-
|
|
44621
|
-
|
|
44622
|
-
|
|
44623
|
-
|
|
44624
|
-
|
|
44625
|
-
|
|
44626
|
-
|
|
45129
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45130
|
+
|
|
45131
|
+
Features:
|
|
45132
|
+
- Verifies that the selected harness, model, thinking level and authentication really work
|
|
45133
|
+
- Reports the answer of the harness, the response time and the reported usage
|
|
45134
|
+
- Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
|
|
45135
|
+
- Optional --period keeps the quota window refreshing by pinging once per period until stopped
|
|
45136
|
+
- Makes no coding changes or commits; if the selected harness has missing local ignore rules, offers to add them to .gitignore
|
|
45137
|
+
- Checks that the selected harness is installed and up to date unless --no-questions is used
|
|
45138
|
+
- Use --no-ui to stream the raw harness output instead of only the compact result
|
|
45139
|
+
`));
|
|
44627
45140
|
addPromptRunnerSelectionOptions(command);
|
|
44628
|
-
|
|
45141
|
+
addQuestionsOption(command);
|
|
44629
45142
|
addPromptRunnerRuntimeOptions(command);
|
|
44630
45143
|
command.option('--period <duration>', _spaceTrim.spaceTrim(`
|
|
44631
45144
|
Keep pinging once per period instead of pinging only once.
|
|
@@ -44634,11 +45147,11 @@
|
|
|
44634
45147
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
44635
45148
|
const { period: periodValue } = cliOptions;
|
|
44636
45149
|
const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
|
|
44637
|
-
const
|
|
45150
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
44638
45151
|
// Note: The period is validated before the harness installation check, so a mistyped duration fails fast
|
|
44639
45152
|
const periodMs = parseOptionalPeriodDuration('--period', periodValue);
|
|
44640
|
-
await $ensureHarnessInstallations([runnerOptions.agentName],
|
|
44641
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45153
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45154
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
44642
45155
|
const pingOptions = {
|
|
44643
45156
|
agentName: runnerOptions.agentName,
|
|
44644
45157
|
model: runnerOptions.model,
|
|
@@ -44662,28 +45175,157 @@
|
|
|
44662
45175
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44663
45176
|
|
|
44664
45177
|
/**
|
|
44665
|
-
*
|
|
45178
|
+
* Free disk space below which `ptbk coder` refuses to start a new run.
|
|
44666
45179
|
*
|
|
44667
|
-
*
|
|
44668
|
-
*
|
|
44669
|
-
*
|
|
45180
|
+
* A coding round writes prompt and log artifacts, lets the harness rewrite the project, may install npm packages,
|
|
45181
|
+
* may create a git worktree for `--isolate` and usually runs a build, so a run started with less than this
|
|
45182
|
+
* would very likely die somewhere in the middle with a filesystem error.
|
|
44670
45183
|
*
|
|
44671
45184
|
* @private internal utility of `promptbookCli`
|
|
44672
45185
|
*/
|
|
44673
|
-
|
|
44674
|
-
|
|
44675
|
-
|
|
44676
|
-
|
|
44677
|
-
|
|
44678
|
-
|
|
45186
|
+
const LOW_FREE_DISK_SPACE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
45187
|
+
/**
|
|
45188
|
+
* Free disk space below which a started `ptbk coder` run stops at its next checkpoint and waits until space is freed.
|
|
45189
|
+
*
|
|
45190
|
+
* This limit is lower than `LOW_FREE_DISK_SPACE_BYTES`, because interrupting a run which is already in progress
|
|
45191
|
+
* is more expensive than not starting one at all.
|
|
45192
|
+
*
|
|
45193
|
+
* @private internal utility of `promptbookCli`
|
|
45194
|
+
*/
|
|
45195
|
+
const CRITICALLY_LOW_FREE_DISK_SPACE_BYTES = 1 * 1024 * 1024 * 1024;
|
|
45196
|
+
/**
|
|
45197
|
+
* How long one free disk space measurement is reused before the filesystem is measured again during a run.
|
|
45198
|
+
*
|
|
45199
|
+
* Pause checkpoints are passed very often - for example on every tick of a timed wait - so without this
|
|
45200
|
+
* interval the filesystem would be measured many times per second.
|
|
45201
|
+
*
|
|
45202
|
+
* @private internal utility of `promptbookCli`
|
|
45203
|
+
*/
|
|
45204
|
+
const FREE_DISK_SPACE_RECHECK_INTERVAL_MS = 60 * 1000;
|
|
45205
|
+
// Note: [🟡] Code for CLI free disk space handling [freeDiskSpaceConstants](src/cli/cli-commands/common/disk-space/freeDiskSpaceConstants.ts) should never be published outside of `@promptbook/cli`
|
|
45206
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44679
45207
|
|
|
44680
|
-
|
|
44681
|
-
|
|
45208
|
+
/**
|
|
45209
|
+
* Classifies one measured amount of free disk space against the `ptbk coder` free disk space limits.
|
|
45210
|
+
*
|
|
45211
|
+
* @private internal utility of `promptbookCli`
|
|
45212
|
+
*/
|
|
45213
|
+
function resolveFreeDiskSpaceLevel(availableBytes) {
|
|
45214
|
+
if (availableBytes < CRITICALLY_LOW_FREE_DISK_SPACE_BYTES) {
|
|
45215
|
+
return 'critical';
|
|
45216
|
+
}
|
|
45217
|
+
if (availableBytes < LOW_FREE_DISK_SPACE_BYTES) {
|
|
45218
|
+
return 'low';
|
|
45219
|
+
}
|
|
45220
|
+
return 'sufficient';
|
|
45221
|
+
}
|
|
45222
|
+
// Note: [🟡] Code for CLI free disk space handling [resolveFreeDiskSpaceLevel](src/cli/cli-commands/common/disk-space/resolveFreeDiskSpaceLevel.ts) should never be published outside of `@promptbook/cli`
|
|
45223
|
+
|
|
45224
|
+
/**
|
|
45225
|
+
* Measures the free disk space on the filesystem which hosts the given path.
|
|
45226
|
+
*
|
|
45227
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
|
|
45228
|
+
*
|
|
45229
|
+
* @returns the measurement, or `null` when the filesystem can not be measured, for example on a platform which
|
|
45230
|
+
* does not report filesystem statistics - a run is never stopped by a measurement which could not be taken
|
|
45231
|
+
* @private internal utility of `promptbookCli`
|
|
45232
|
+
*/
|
|
45233
|
+
async function $readFreeDiskSpaceStatus(inspectedPath) {
|
|
45234
|
+
try {
|
|
45235
|
+
const fileSystemStats = await promises.statfs(inspectedPath);
|
|
45236
|
+
const blockSizeBytes = fileSystemStats.bsize;
|
|
45237
|
+
const availableBytes = fileSystemStats.bavail * blockSizeBytes;
|
|
45238
|
+
const totalBytes = fileSystemStats.blocks * blockSizeBytes;
|
|
45239
|
+
if (!Number.isFinite(availableBytes) || availableBytes < 0) {
|
|
45240
|
+
return null;
|
|
44682
45241
|
}
|
|
44683
|
-
return
|
|
44684
|
-
|
|
45242
|
+
return {
|
|
45243
|
+
inspectedPath,
|
|
45244
|
+
availableBytes,
|
|
45245
|
+
totalBytes,
|
|
45246
|
+
level: resolveFreeDiskSpaceLevel(availableBytes),
|
|
45247
|
+
};
|
|
45248
|
+
}
|
|
45249
|
+
catch (_a) {
|
|
45250
|
+
// Note: A path or a platform which can not report its free disk space must not stop `ptbk coder` from running
|
|
45251
|
+
return null;
|
|
45252
|
+
}
|
|
44685
45253
|
}
|
|
44686
|
-
// Note: [🟡] Code for CLI
|
|
45254
|
+
// Note: [🟡] Code for CLI free disk space handling [$readFreeDiskSpaceStatus](src/cli/cli-commands/common/disk-space/$readFreeDiskSpaceStatus.ts) should never be published outside of `@promptbook/cli`
|
|
45255
|
+
|
|
45256
|
+
/**
|
|
45257
|
+
* Byte units used when one amount of disk space is written for a human.
|
|
45258
|
+
*/
|
|
45259
|
+
const FREE_DISK_SPACE_BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
45260
|
+
/**
|
|
45261
|
+
* Writes one byte count as a compact human-readable amount of disk space, for example `812 MB` or `1.5 GB`.
|
|
45262
|
+
*
|
|
45263
|
+
* @private internal utility of `promptbookCli`
|
|
45264
|
+
*/
|
|
45265
|
+
function formatFreeDiskSpaceBytes(bytes) {
|
|
45266
|
+
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
45267
|
+
return '0 B';
|
|
45268
|
+
}
|
|
45269
|
+
const unitIndex = Math.min(FREE_DISK_SPACE_BYTE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
|
|
45270
|
+
const value = bytes / Math.pow(1024, unitIndex);
|
|
45271
|
+
// Note: One decimal place only matters for small values, `476 GB` is more readable than `476.3 GB`
|
|
45272
|
+
const decimalPlaceCount = value >= 10 || unitIndex === 0 ? 0 : 1;
|
|
45273
|
+
// Note: An empty decimal place only adds noise, `2 GB` is more readable than `2.0 GB`
|
|
45274
|
+
const displayedValue = value.toFixed(decimalPlaceCount).replace(/\.0$/, '');
|
|
45275
|
+
return `${displayedValue} ${FREE_DISK_SPACE_BYTE_UNITS[unitIndex]}`;
|
|
45276
|
+
}
|
|
45277
|
+
// Note: [🟡] Code for CLI free disk space handling [formatFreeDiskSpaceBytes](src/cli/cli-commands/common/disk-space/formatFreeDiskSpaceBytes.ts) should never be published outside of `@promptbook/cli`
|
|
45278
|
+
|
|
45279
|
+
/**
|
|
45280
|
+
* Writes the shared warning which explains one low free disk space measurement and how to get out of it.
|
|
45281
|
+
*
|
|
45282
|
+
* The very same wording is used by the check which refuses to start a run and by the check which pauses
|
|
45283
|
+
* a run that ran out of disk space while it was working.
|
|
45284
|
+
*
|
|
45285
|
+
* @private internal utility of `promptbookCli`
|
|
45286
|
+
*/
|
|
45287
|
+
function formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus) {
|
|
45288
|
+
const { inspectedPath } = freeDiskSpaceStatus;
|
|
45289
|
+
const availableSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes);
|
|
45290
|
+
const totalSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.totalBytes);
|
|
45291
|
+
const requiredSpaceToStart = formatFreeDiskSpaceBytes(LOW_FREE_DISK_SPACE_BYTES);
|
|
45292
|
+
const requiredSpaceToContinue = formatFreeDiskSpaceBytes(CRITICALLY_LOW_FREE_DISK_SPACE_BYTES);
|
|
45293
|
+
return _spaceTrim.spaceTrim(`
|
|
45294
|
+
Only **${availableSpace}** of ${totalSpace} is left on the disk which hosts \`${inspectedPath}\`.
|
|
45295
|
+
|
|
45296
|
+
**\`ptbk coder\` needs at least ${requiredSpaceToStart} of free disk space to start a run and at least ${requiredSpaceToContinue} to keep one running.**
|
|
45297
|
+
|
|
45298
|
+
Actionable hints:
|
|
45299
|
+
- Free up disk space on the drive which hosts \`${inspectedPath}\`.
|
|
45300
|
+
- Remove build artifacts, package caches and \`node_modules\` folders of projects you are not working on.
|
|
45301
|
+
- Remove the temporary worktrees which earlier \`--isolate\` runs left behind.
|
|
45302
|
+
`);
|
|
45303
|
+
}
|
|
45304
|
+
// Note: [🟡] Code for CLI free disk space handling [formatLowFreeDiskSpaceWarning](src/cli/cli-commands/common/disk-space/formatLowFreeDiskSpaceWarning.ts) should never be published outside of `@promptbook/cli`
|
|
45305
|
+
|
|
45306
|
+
/**
|
|
45307
|
+
* Refuses to start one `ptbk coder` command when the disk which hosts the project is nearly full.
|
|
45308
|
+
*
|
|
45309
|
+
* A run which starts without enough free disk space fails somewhere in the middle, after the coding agent has
|
|
45310
|
+
* already rewritten a part of the project, so it is stopped before it writes anything at all.
|
|
45311
|
+
*
|
|
45312
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
|
|
45313
|
+
*
|
|
45314
|
+
* @throws {LimitReachedError} when there is not enough free disk space left to start
|
|
45315
|
+
* @private internal utility of `promptbookCli`
|
|
45316
|
+
*/
|
|
45317
|
+
async function $assertSufficientFreeDiskSpace(inspectedPath) {
|
|
45318
|
+
const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
|
|
45319
|
+
if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level === 'sufficient') {
|
|
45320
|
+
return;
|
|
45321
|
+
}
|
|
45322
|
+
throw new LimitReachedError(_spaceTrim.spaceTrim((block) => `
|
|
45323
|
+
There is not enough free disk space to run \`ptbk coder\`.
|
|
45324
|
+
|
|
45325
|
+
${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
|
|
45326
|
+
`));
|
|
45327
|
+
}
|
|
45328
|
+
// Note: [🟡] Code for CLI free disk space handling [$assertSufficientFreeDiskSpace](src/cli/cli-commands/common/disk-space/$assertSufficientFreeDiskSpace.ts) should never be published outside of `@promptbook/cli`
|
|
44687
45329
|
|
|
44688
45330
|
/**
|
|
44689
45331
|
* Npm packages which directly provide the `ptbk` executable.
|
|
@@ -45013,7 +45655,11 @@
|
|
|
45013
45655
|
* @returns `true` when at least one installation was updated and the current coder run should stop
|
|
45014
45656
|
* @private internal utility of `promptbookCli`
|
|
45015
45657
|
*/
|
|
45016
|
-
async function $ensurePromptbookCliInstallations() {
|
|
45658
|
+
async function $ensurePromptbookCliInstallations(questionsOptions) {
|
|
45659
|
+
if (!questionsOptions.isAskingQuestionsEnabled) {
|
|
45660
|
+
// Note: `--no-questions` forbids asking for the approval, so the registry checks would be useless
|
|
45661
|
+
return false;
|
|
45662
|
+
}
|
|
45017
45663
|
if (!process.stdin.isTTY) {
|
|
45018
45664
|
// Note: Non-interactive runs cannot approve an update and should not wait for registry checks
|
|
45019
45665
|
return false;
|
|
@@ -45025,7 +45671,7 @@
|
|
|
45025
45671
|
return false;
|
|
45026
45672
|
}
|
|
45027
45673
|
console.warn(colors__default["default"].yellow(formatPromptbookCliInstallationWarning(outdatedStatuses)));
|
|
45028
|
-
const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?');
|
|
45674
|
+
const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?', questionsOptions);
|
|
45029
45675
|
if (!isUpdateApproved) {
|
|
45030
45676
|
reportManualPromptbookCliUpdateCommands(outdatedStatuses);
|
|
45031
45677
|
return false;
|
|
@@ -45092,6 +45738,17 @@
|
|
|
45092
45738
|
return TEST_BEFORE_MODE_VALUES.includes(value);
|
|
45093
45739
|
}
|
|
45094
45740
|
|
|
45741
|
+
/**
|
|
45742
|
+
* Prints why a coder run has ended to the terminal.
|
|
45743
|
+
*
|
|
45744
|
+
* @private internal utility of `coder run` and `coder server` commands
|
|
45745
|
+
*/
|
|
45746
|
+
function printCoderRunFailure(error) {
|
|
45747
|
+
console.error(colors__default["default"].bgRed(`${error.name}`));
|
|
45748
|
+
console.error(colors__default["default"].red(formatUnknownErrorDetails(error)));
|
|
45749
|
+
}
|
|
45750
|
+
// Note: [🟡] Code for coder failure reporting [printCoderRunFailure](src/cli/cli-commands/coder/printCoderRunFailure.ts) should never be published outside of `@promptbook/cli`
|
|
45751
|
+
|
|
45095
45752
|
/**
|
|
45096
45753
|
* Initializes `coder run` command for Promptbook CLI utilities
|
|
45097
45754
|
*
|
|
@@ -45101,10 +45758,12 @@
|
|
|
45101
45758
|
*/
|
|
45102
45759
|
function $initializeCoderRunCommand(program) {
|
|
45103
45760
|
const command = program.command('run');
|
|
45104
|
-
command.description(_spaceTrim.spaceTrim(`
|
|
45761
|
+
command.description(_spaceTrim.spaceTrim((block) => `
|
|
45105
45762
|
Execute coding prompts through selected AI agent
|
|
45106
45763
|
|
|
45107
|
-
${PROMPT_RUNNER_DESCRIPTION}
|
|
45764
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
45765
|
+
|
|
45766
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45108
45767
|
|
|
45109
45768
|
Features:
|
|
45110
45769
|
- Automatically stages and commits changes with agent identity unless --no-commit is used
|
|
@@ -45114,9 +45773,11 @@
|
|
|
45114
45773
|
- Optional post-commit git push with explicit --auto-push opt-in
|
|
45115
45774
|
- Optional pre-prompt git pull with explicit --auto-pull opt-in
|
|
45116
45775
|
- Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
|
|
45776
|
+
- Saves the run trace of every round into prompts/traces/, named after its prompt file
|
|
45117
45777
|
- Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
|
|
45118
45778
|
- Optional --no-ui keeps plain streaming console output for logging and debugging
|
|
45119
|
-
-
|
|
45779
|
+
- Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
|
|
45780
|
+
- Checks that the selected harness is installed and up to date before the first prompt unless --no-questions is used
|
|
45120
45781
|
- Offers to add missing project-local ignore rules for the selected harness
|
|
45121
45782
|
- In interactive mode, checks local and global Promptbook CLI installations and offers to update them
|
|
45122
45783
|
- Supports GPG signing of commits
|
|
@@ -45127,8 +45788,8 @@
|
|
|
45127
45788
|
`));
|
|
45128
45789
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
45129
45790
|
addPromptRunnerSelectionOptions(command);
|
|
45130
|
-
|
|
45131
|
-
command
|
|
45791
|
+
addQuestionsOption(command);
|
|
45792
|
+
addCoderAgentOption(command);
|
|
45132
45793
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
45133
45794
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
45134
45795
|
command.addOption(new commander.Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
|
|
@@ -45141,9 +45802,7 @@
|
|
|
45141
45802
|
A verified task is merged back into the branch the coder runs on and the worktree is deleted.
|
|
45142
45803
|
A task that cannot be merged is marked as failed and its worktree is kept for a manual merge.
|
|
45143
45804
|
`), false);
|
|
45144
|
-
command
|
|
45145
|
-
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
45146
|
-
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
45805
|
+
addPromptPriorityOptions(command);
|
|
45147
45806
|
command.option('--limit <run-count>', 'Stop after processing this many prompt runs', createPositiveIntegerOptionParser('--limit'));
|
|
45148
45807
|
command.option('--wait-after-prompt <duration>', _spaceTrim.spaceTrim(`
|
|
45149
45808
|
Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
|
|
@@ -45169,7 +45828,7 @@
|
|
|
45169
45828
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
45170
45829
|
isAgentRequired: !dryRun,
|
|
45171
45830
|
});
|
|
45172
|
-
const
|
|
45831
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
45173
45832
|
// [1] Parse the wait options and --no-auto:
|
|
45174
45833
|
// default: run automatically through the queue (no waiting between prompts)
|
|
45175
45834
|
// --no-auto: wait for user confirmation before each prompt (interactive mode)
|
|
@@ -45177,11 +45836,15 @@
|
|
|
45177
45836
|
// --wait-between-prompts: pace from start of one prompt to start of next
|
|
45178
45837
|
// --wait-after-error: wait before retrying after an error (default 10m)
|
|
45179
45838
|
const waitForUser = !auto;
|
|
45180
|
-
|
|
45839
|
+
assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
|
|
45840
|
+
// Note: Checked before anything is installed or written, because installing a harness, updating the
|
|
45841
|
+
// Promptbook CLI and coding itself all need the disk space which is missing
|
|
45842
|
+
await $assertSufficientFreeDiskSpace(process.cwd());
|
|
45843
|
+
if (await $ensurePromptbookCliInstallations(questionsOptions)) {
|
|
45181
45844
|
return process.exit(0);
|
|
45182
45845
|
}
|
|
45183
|
-
await $ensureHarnessInstallations([runnerOptions.agentName],
|
|
45184
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45846
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45847
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
45185
45848
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
45186
45849
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
45187
45850
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -45214,6 +45877,7 @@
|
|
|
45214
45877
|
allowDestructiveAutoMigrate,
|
|
45215
45878
|
autoPush: runnerOptions.autoPush,
|
|
45216
45879
|
autoPull: runnerOptions.autoPull,
|
|
45880
|
+
isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
|
|
45217
45881
|
};
|
|
45218
45882
|
// Note: Import the function dynamically to avoid loading heavy dependencies until needed
|
|
45219
45883
|
const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
|
|
@@ -45223,8 +45887,7 @@
|
|
|
45223
45887
|
}
|
|
45224
45888
|
catch (error) {
|
|
45225
45889
|
assertsError(error);
|
|
45226
|
-
|
|
45227
|
-
console.error(colors__default["default"].red(error.stack || error.message));
|
|
45890
|
+
printCoderRunFailure(error);
|
|
45228
45891
|
return process.exit(1);
|
|
45229
45892
|
}
|
|
45230
45893
|
return process.exit(0);
|
|
@@ -45268,35 +45931,36 @@
|
|
|
45268
45931
|
*/
|
|
45269
45932
|
function $initializeCoderServerCommand(program) {
|
|
45270
45933
|
const command = program.command('server');
|
|
45271
|
-
command.description(_spaceTrim.spaceTrim(`
|
|
45272
|
-
|
|
45934
|
+
command.description(_spaceTrim.spaceTrim((block) => `
|
|
45935
|
+
Start a coder server that watches for prompts and serves a kanban web UI
|
|
45273
45936
|
|
|
45274
|
-
|
|
45937
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
45275
45938
|
|
|
45276
|
-
|
|
45277
|
-
|
|
45278
|
-
|
|
45279
|
-
|
|
45280
|
-
|
|
45281
|
-
|
|
45282
|
-
|
|
45283
|
-
|
|
45284
|
-
|
|
45285
|
-
|
|
45939
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45940
|
+
|
|
45941
|
+
Features:
|
|
45942
|
+
- Runs the same prompt processing as \`ptbk coder run\`
|
|
45943
|
+
- Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
|
|
45944
|
+
- Checks that the selected harness is installed and up to date on startup unless --no-questions is used
|
|
45945
|
+
- Offers to add missing project-local ignore rules for the selected harness
|
|
45946
|
+
- Does not exit when all prompts are done; polls for new prompt files instead
|
|
45947
|
+
- Serves a kanban board at http://localhost:<port> for visual progress tracking
|
|
45948
|
+
- Allows editing prompt files directly from the browser (Trello-style)
|
|
45949
|
+
- Play / pause button in the browser stays in sync with the CLI pause state
|
|
45950
|
+
- Terminal controls match \`ptbk coder run\`: P pause/resume, S skip waiting, X end after current prompt
|
|
45951
|
+
`));
|
|
45286
45952
|
command.addOption(new commander.Option('--port <port>', 'Port to start the coder server on')
|
|
45287
45953
|
.env('PTBK_CODER_SERVER_PORT')
|
|
45288
45954
|
.default(DEFAULT_CODER_SERVER_PORT));
|
|
45289
45955
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
45290
45956
|
addPromptRunnerSelectionOptions(command);
|
|
45291
|
-
|
|
45292
|
-
command
|
|
45957
|
+
addQuestionsOption(command);
|
|
45958
|
+
addCoderAgentOption(command);
|
|
45293
45959
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
45294
45960
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
45295
45961
|
command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
|
|
45296
45962
|
addPromptRunnerExecutionOptions(command);
|
|
45297
|
-
command
|
|
45298
|
-
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
45299
|
-
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
45963
|
+
addPromptPriorityOptions(command);
|
|
45300
45964
|
command.option('--wait-after-prompt <duration>', _spaceTrim.spaceTrim(`
|
|
45301
45965
|
Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
|
|
45302
45966
|
Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
|
|
@@ -45320,11 +45984,15 @@
|
|
|
45320
45984
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
45321
45985
|
isAgentRequired: !dryRun,
|
|
45322
45986
|
});
|
|
45323
|
-
const
|
|
45324
|
-
await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
|
|
45325
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45987
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
45326
45988
|
// [1] Parse the wait options and --no-auto (same logic as `coder run`)
|
|
45327
45989
|
const waitForUser = !auto;
|
|
45990
|
+
assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
|
|
45991
|
+
// Note: Checked before anything is installed or written, because installing a harness and coding
|
|
45992
|
+
// itself both need the disk space which is missing
|
|
45993
|
+
await $assertSufficientFreeDiskSpace(process.cwd());
|
|
45994
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45995
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
45328
45996
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
45329
45997
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
45330
45998
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -45354,6 +46022,7 @@
|
|
|
45354
46022
|
allowDestructiveAutoMigrate,
|
|
45355
46023
|
autoPush: runnerOptions.autoPush,
|
|
45356
46024
|
autoPull: runnerOptions.autoPull,
|
|
46025
|
+
isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
|
|
45357
46026
|
};
|
|
45358
46027
|
// Note: Import dynamically to avoid loading heavy dependencies until needed
|
|
45359
46028
|
const { runCodexPromptsServer } = await Promise.resolve().then(function () { return runCodexPromptsServer$1; });
|
|
@@ -45362,8 +46031,7 @@
|
|
|
45362
46031
|
}
|
|
45363
46032
|
catch (error) {
|
|
45364
46033
|
assertsError(error);
|
|
45365
|
-
|
|
45366
|
-
console.error(colors__default["default"].red(error.stack || error.message));
|
|
46034
|
+
printCoderRunFailure(error);
|
|
45367
46035
|
return process.exit(1);
|
|
45368
46036
|
}
|
|
45369
46037
|
return process.exit(0);
|
|
@@ -45511,6 +46179,7 @@
|
|
|
45511
46179
|
* - add: Add one ready-to-run prompt file to the queue
|
|
45512
46180
|
* - generate-boilerplates: Generate prompt boilerplate files
|
|
45513
46181
|
* - find-refactor-candidates: Find files that need refactoring
|
|
46182
|
+
* - list: List ready prompts in priority order without running them
|
|
45514
46183
|
* - run: Run coding prompts with AI agents
|
|
45515
46184
|
* - ping: Test one harness and model with a tiny dummy prompt
|
|
45516
46185
|
* - verify: Verify completed prompts
|
|
@@ -45531,6 +46200,7 @@
|
|
|
45531
46200
|
- generate-boilerplates: Generate prompt boilerplate files
|
|
45532
46201
|
- find-refactor-candidates: Find files that need refactoring
|
|
45533
46202
|
- find-unwritten: List prompt sections that still need to be authored
|
|
46203
|
+
- list: List ready prompts in priority order without running them
|
|
45534
46204
|
- run: Run coding prompts with AI agents
|
|
45535
46205
|
- ping: Test the connection, response time and quota of one harness and model
|
|
45536
46206
|
- server: Start a long-running coder server with a kanban web UI
|
|
@@ -45543,17 +46213,14 @@
|
|
|
45543
46213
|
$initializeCoderGenerateBoilerplatesCommand(coderCommand);
|
|
45544
46214
|
$initializeCoderFindRefactorCandidatesCommand(coderCommand);
|
|
45545
46215
|
$initializeCoderFindUnwrittenCommand(coderCommand);
|
|
46216
|
+
$initializeCoderListCommand(coderCommand);
|
|
45546
46217
|
$initializeCoderRunCommand(coderCommand);
|
|
45547
46218
|
$initializeCoderPingCommand(coderCommand);
|
|
45548
46219
|
$initializeCoderServerCommand(coderCommand);
|
|
45549
46220
|
$initializeCoderVerifyCommand(coderCommand);
|
|
45550
46221
|
$initializeCoderFindFreshEmojiTagCommand(coderCommand);
|
|
45551
46222
|
// If no subcommand is provided, show help
|
|
45552
|
-
coderCommand
|
|
45553
|
-
console.info(colors__default["default"].yellow('Please specify a subcommand.'));
|
|
45554
|
-
console.info('');
|
|
45555
|
-
coderCommand.help();
|
|
45556
|
-
});
|
|
46223
|
+
$requireCliSubcommand(coderCommand);
|
|
45557
46224
|
}
|
|
45558
46225
|
// Note: [🟡] Code for CLI command [coder](src/cli/cli-commands/coder.ts) should never be published outside of `@promptbook/cli`
|
|
45559
46226
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -56143,7 +56810,9 @@
|
|
|
56143
56810
|
* @private internal function of `promptbookCli`
|
|
56144
56811
|
*/
|
|
56145
56812
|
function $initializeRunCommand(program) {
|
|
56146
|
-
|
|
56813
|
+
// Note: `run` is deprecated, so it is intentionally NOT the default command of `ptbk`
|
|
56814
|
+
// and it must be asked for explicitly
|
|
56815
|
+
const runCommand = program.command('run');
|
|
56147
56816
|
configureRunCommand(runCommand);
|
|
56148
56817
|
runCommand.action(handleActionErrors((pipelineSource, cliOptions) => runCommandAction(pipelineSource, cliOptions)));
|
|
56149
56818
|
}
|
|
@@ -57982,9 +58651,24 @@
|
|
|
57982
58651
|
command.option('--remote-server-url <url>', `URL of remote server to use when `, DEFAULT_REMOTE_SERVER_URL);
|
|
57983
58652
|
}
|
|
57984
58653
|
|
|
58654
|
+
/**
|
|
58655
|
+
* Keeps one already registered CLI command out of the help of its parent command while the command itself stays
|
|
58656
|
+
* fully usable - it can still be run, it still has its own help and it is still reachable through `ptbk help <command>`.
|
|
58657
|
+
*
|
|
58658
|
+
* Note: `$` is used to indicate that this function is not a pure function - it changes how the CLI renders its help
|
|
58659
|
+
*
|
|
58660
|
+
* @private utility of CLI
|
|
58661
|
+
*/
|
|
58662
|
+
function $hideCliCommandFromHelp(command) {
|
|
58663
|
+
command._hidden = true;
|
|
58664
|
+
}
|
|
58665
|
+
|
|
57985
58666
|
/**
|
|
57986
58667
|
* Marks one CLI command as deprecated while keeping it available for existing callers.
|
|
57987
58668
|
*
|
|
58669
|
+
* Deprecated commands are not advertised anymore - they are hidden from the list of commands in the help of their
|
|
58670
|
+
* parent command, they warn when they are used and they explain the deprecation in their own help.
|
|
58671
|
+
*
|
|
57988
58672
|
* @private utility of CLI
|
|
57989
58673
|
*/
|
|
57990
58674
|
function $deprecateCliCommand(command, deprecationMessage) {
|
|
@@ -57996,6 +58680,7 @@
|
|
|
57996
58680
|
command.hook('preAction', () => {
|
|
57997
58681
|
console.warn(colors__default["default"].yellow(createDeprecatedCliCommandWarning(command, deprecationMessage)));
|
|
57998
58682
|
});
|
|
58683
|
+
$hideCliCommandFromHelp(command);
|
|
57999
58684
|
}
|
|
58000
58685
|
/**
|
|
58001
58686
|
* Creates the runtime warning printed before a deprecated CLI command action starts.
|
|
@@ -58063,10 +58748,17 @@
|
|
|
58063
58748
|
program.version(PROMPTBOOK_ENGINE_VERSION);
|
|
58064
58749
|
program.description(CLAIM);
|
|
58065
58750
|
// Note: These options are valid for all commands
|
|
58751
|
+
// Note: Commands are listed in the help in the order they are registered,
|
|
58752
|
+
// `coder` is the most used one so it goes first and the deprecated ones,
|
|
58753
|
+
// which `$deprecateTopLevelCommands` hides from the help entirely, go last
|
|
58754
|
+
$initializeCoderCommand(program);
|
|
58755
|
+
$initializeAgentCommand(program);
|
|
58756
|
+
$initializeAgentFolderCommand(program);
|
|
58757
|
+
$initializeAgentsServerCommand(program);
|
|
58066
58758
|
$initializeAboutCommand(program);
|
|
58759
|
+
$initializeHelloCommand(program);
|
|
58067
58760
|
$initializeRunCommand(program);
|
|
58068
58761
|
$initializeLoginCommand(program);
|
|
58069
|
-
$initializeHelloCommand(program);
|
|
58070
58762
|
$initializeMakeCommand(program);
|
|
58071
58763
|
$initializePrettifyCommand(program);
|
|
58072
58764
|
$initializeTestCommand(program);
|
|
@@ -58074,16 +58766,13 @@
|
|
|
58074
58766
|
$initializeListScrapersCommand(program);
|
|
58075
58767
|
$initializeStartAgentsServerCommand(program);
|
|
58076
58768
|
$initializeStartPipelinesServerCommand(program);
|
|
58077
|
-
$initializeAgentCommand(program);
|
|
58078
|
-
$initializeAgentFolderCommand(program);
|
|
58079
|
-
$initializeAgentsServerCommand(program);
|
|
58080
|
-
$initializeCoderCommand(program);
|
|
58081
58769
|
$deprecateTopLevelCommands(program);
|
|
58082
58770
|
// TODO: [🧠] Should it be here or not> $addGlobalOptionsToCommand(program);
|
|
58083
58771
|
program.commands.forEach($addGlobalOptionsToCommand);
|
|
58772
|
+
// Note: There is no default command, so `ptbk` without a subcommand asks for one
|
|
58773
|
+
// instead of running the deprecated `ptbk run`
|
|
58084
58774
|
if (commandLineArguments.length === 0) {
|
|
58085
|
-
program
|
|
58086
|
-
return process.exit(0);
|
|
58775
|
+
return $reportMissingCliSubcommand(program);
|
|
58087
58776
|
}
|
|
58088
58777
|
program.parse(process.argv);
|
|
58089
58778
|
}
|
|
@@ -58097,7 +58786,7 @@
|
|
|
58097
58786
|
return firstCommandLineArgument !== undefined && VERSION_OPTION_ARGUMENTS.has(firstCommandLineArgument);
|
|
58098
58787
|
}
|
|
58099
58788
|
/**
|
|
58100
|
-
*
|
|
58789
|
+
* Marks each configured top-level legacy command as deprecated, which keeps it usable but takes it out of the help.
|
|
58101
58790
|
*/
|
|
58102
58791
|
function $deprecateTopLevelCommands(program) {
|
|
58103
58792
|
for (const command of program.commands) {
|
|
@@ -58108,7 +58797,6 @@
|
|
|
58108
58797
|
}
|
|
58109
58798
|
}
|
|
58110
58799
|
// Note: [🟡] Code for CLI program [promptbookCli](src/cli/promptbookCli.ts) should never be published outside of `@promptbook/cli`
|
|
58111
|
-
// TODO: [🧠] Maybe `run` command the default, instead of `ptbk run ./foo.book` -> `ptbk ./foo.book`
|
|
58112
58800
|
// TODO: [🥠] Do not export, its just for CLI script
|
|
58113
58801
|
// TODO: [🕌] When more functionalities, rename
|
|
58114
58802
|
// Note: 11:11
|
|
@@ -59220,7 +59908,9 @@
|
|
|
59220
59908
|
*/
|
|
59221
59909
|
function createExecutionToolsFromVercelProvider(options) {
|
|
59222
59910
|
let { title, description } = options;
|
|
59223
|
-
const { vercelProvider, availableModels,
|
|
59911
|
+
const { vercelProvider, availableModels, additionalChatSettings = {} } = options;
|
|
59912
|
+
// <- Note: `userId` is not forwarded, because Vercel AI SDK moved provider-specific settings like `user` from
|
|
59913
|
+
// `vercelProvider.chat(modelName, settings)` into `providerOptions` which are keyed by the provider
|
|
59224
59914
|
if (!/Vercel/i.test(title)) {
|
|
59225
59915
|
title = `${title} (through Vercel)`;
|
|
59226
59916
|
// <- TODO: [🧈] Maybe standartize the suffix
|
|
@@ -59261,10 +59951,7 @@
|
|
|
59261
59951
|
|
|
59262
59952
|
`));
|
|
59263
59953
|
}
|
|
59264
|
-
const model = await vercelProvider.chat(modelName
|
|
59265
|
-
user: (userId === null || userId === void 0 ? void 0 : userId.toString()) || undefined,
|
|
59266
|
-
...additionalChatSettings,
|
|
59267
|
-
});
|
|
59954
|
+
const model = await vercelProvider.chat(modelName);
|
|
59268
59955
|
const rawPromptContent = templateParameters(content, { ...parameters, modelName });
|
|
59269
59956
|
// Support for passing a chat thread (multi-message conversation)
|
|
59270
59957
|
let promptMessages;
|
|
@@ -59302,14 +59989,9 @@
|
|
|
59302
59989
|
}
|
|
59303
59990
|
const rawRequest = {
|
|
59304
59991
|
// <- TODO: [☂]
|
|
59305
|
-
inputFormat: 'messages',
|
|
59306
|
-
mode: {
|
|
59307
|
-
type: 'regular',
|
|
59308
|
-
tools: [
|
|
59309
|
-
/* <- TODO: Pass the tools */
|
|
59310
|
-
],
|
|
59311
|
-
},
|
|
59312
59992
|
prompt: promptMessages,
|
|
59993
|
+
// <- TODO: Pass the tools via `tools`
|
|
59994
|
+
...additionalChatSettings,
|
|
59313
59995
|
};
|
|
59314
59996
|
const start = $getCurrentDate();
|
|
59315
59997
|
if (options.isVerbose) {
|
|
@@ -59326,28 +60008,31 @@
|
|
|
59326
60008
|
if (options.isVerbose) {
|
|
59327
60009
|
console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
59328
60010
|
}
|
|
59329
|
-
|
|
60011
|
+
// Note: Vercel AI SDK returns ordered content parts, the chat message is made only of the text parts
|
|
60012
|
+
const responseTextParts = rawResponse.content.flatMap((contentPart) => contentPart.type === 'text' ? [contentPart.text] : []);
|
|
60013
|
+
if (responseTextParts.length === 0) {
|
|
59330
60014
|
throw new PipelineExecutionError('No response message');
|
|
59331
60015
|
}
|
|
60016
|
+
const responseText = responseTextParts.join('');
|
|
59332
60017
|
const complete = $getCurrentDate();
|
|
59333
60018
|
const duration = uncertainNumber((new Date(complete).getTime() - new Date(start).getTime()) / 1000);
|
|
59334
60019
|
const usage = {
|
|
59335
60020
|
price: UNCERTAIN_ZERO_VALUE,
|
|
59336
60021
|
duration,
|
|
59337
60022
|
input: {
|
|
59338
|
-
tokensCount: uncertainNumber(rawResponse.usage.
|
|
60023
|
+
tokensCount: uncertainNumber(rawResponse.usage.inputTokens),
|
|
59339
60024
|
...computeUsageCounts(rawPromptContent),
|
|
59340
60025
|
},
|
|
59341
60026
|
output: {
|
|
59342
|
-
tokensCount: uncertainNumber(rawResponse.usage.
|
|
59343
|
-
...computeUsageCounts(
|
|
60027
|
+
tokensCount: uncertainNumber(rawResponse.usage.outputTokens),
|
|
60028
|
+
...computeUsageCounts(responseText),
|
|
59344
60029
|
},
|
|
59345
60030
|
};
|
|
59346
60031
|
return exportJson({
|
|
59347
60032
|
name: 'promptResult',
|
|
59348
60033
|
message: `Result of \`createExecutionToolsFromVercelProvider.callChatModel\``,
|
|
59349
60034
|
value: {
|
|
59350
|
-
content:
|
|
60035
|
+
content: responseText,
|
|
59351
60036
|
modelName,
|
|
59352
60037
|
timing: {
|
|
59353
60038
|
start,
|
|
@@ -74652,13 +75337,15 @@
|
|
|
74652
75337
|
}
|
|
74653
75338
|
|
|
74654
75339
|
/**
|
|
74655
|
-
* Checks whether a prompt is unrestricted or matches the selected harness
|
|
75340
|
+
* Checks whether a prompt is unrestricted or matches the selected harness, model or Book agent.
|
|
74656
75341
|
*
|
|
74657
|
-
* A prompt status line can contain one or more backtick-delimited model or
|
|
74658
|
-
*
|
|
74659
|
-
* `gpt` selects any `gpt-*` model
|
|
75342
|
+
* A prompt status line can contain one or more backtick-delimited model, harness or agent
|
|
75343
|
+
* references. Matching is intentionally based on normalized substrings so a token such as
|
|
75344
|
+
* `gpt` selects any `gpt-*` model, `developer` selects an agent Book named `developer.book`,
|
|
75345
|
+
* and `opus` selects a `claude-opus-*` model.
|
|
74660
75346
|
*/
|
|
74661
75347
|
function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
|
|
75348
|
+
var _a;
|
|
74662
75349
|
if (promptRunnerIdentity === undefined) {
|
|
74663
75350
|
return true;
|
|
74664
75351
|
}
|
|
@@ -74667,7 +75354,11 @@
|
|
|
74667
75354
|
if (requiredRunnerTokens.length === 0) {
|
|
74668
75355
|
return true;
|
|
74669
75356
|
}
|
|
74670
|
-
const normalizedRunnerNames = [
|
|
75357
|
+
const normalizedRunnerNames = [
|
|
75358
|
+
promptRunnerIdentity.harnessName,
|
|
75359
|
+
promptRunnerIdentity.modelName,
|
|
75360
|
+
...((_a = promptRunnerIdentity.agentReferences) !== null && _a !== void 0 ? _a : []),
|
|
75361
|
+
]
|
|
74671
75362
|
.filter((name) => name !== undefined && name.trim() !== '')
|
|
74672
75363
|
.map((name) => normalizeToKebabCase(name))
|
|
74673
75364
|
.filter((name) => name !== '');
|
|
@@ -74678,7 +75369,7 @@
|
|
|
74678
75369
|
});
|
|
74679
75370
|
}
|
|
74680
75371
|
/**
|
|
74681
|
-
* Extracts model and
|
|
75372
|
+
* Extracts model, harness and Book-agent tokens from a prompt status line.
|
|
74682
75373
|
*/
|
|
74683
75374
|
function extractPromptRunnerTokens(statusLine) {
|
|
74684
75375
|
return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
|
|
@@ -74693,7 +75384,7 @@
|
|
|
74693
75384
|
*
|
|
74694
75385
|
* @private internal constant of `parsePromptFile`
|
|
74695
75386
|
*/
|
|
74696
|
-
const
|
|
75387
|
+
const PROMPT_STATUS_LINE_PATTERN = /^\[(?<marker> |-|\.|[xX]|\^|!)\](?<details>.*)$/u;
|
|
74697
75388
|
/**
|
|
74698
75389
|
* Parses a prompt markdown file into sections and metadata.
|
|
74699
75390
|
*/
|
|
@@ -74717,7 +75408,7 @@
|
|
|
74717
75408
|
if (firstNonEmptyLine !== undefined) {
|
|
74718
75409
|
const statusLine = (lines[firstNonEmptyLine] || '').trim();
|
|
74719
75410
|
const parsedStatus = parseStatusLine(statusLine);
|
|
74720
|
-
const status = (_a = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.status) !== null && _a !== void 0 ? _a : (
|
|
75411
|
+
const status = (_a = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.status) !== null && _a !== void 0 ? _a : (PROMPT_STATUS_LINE_PATTERN.test(statusLine) ? 'not-ready' : 'todo');
|
|
74721
75412
|
const priority = (_b = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.priority) !== null && _b !== void 0 ? _b : 0;
|
|
74722
75413
|
sections.push({
|
|
74723
75414
|
index,
|
|
@@ -74745,51 +75436,47 @@
|
|
|
74745
75436
|
* For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
|
|
74746
75437
|
*/
|
|
74747
75438
|
function parseStatusLine(line) {
|
|
74748
|
-
var _a, _b, _c, _d;
|
|
74749
|
-
|
|
74750
|
-
const
|
|
74751
|
-
|
|
75439
|
+
var _a, _b, _c, _d, _e;
|
|
75440
|
+
const statusLineMatch = line.match(PROMPT_STATUS_LINE_PATTERN);
|
|
75441
|
+
const marker = (_a = statusLineMatch === null || statusLineMatch === void 0 ? void 0 : statusLineMatch.groups) === null || _a === void 0 ? void 0 : _a.marker;
|
|
75442
|
+
const details = (_c = (_b = statusLineMatch === null || statusLineMatch === void 0 ? void 0 : statusLineMatch.groups) === null || _b === void 0 ? void 0 : _b.details) !== null && _c !== void 0 ? _c : '';
|
|
75443
|
+
if (marker === 'x' || marker === 'X') {
|
|
75444
|
+
// For done prompts [x], allow any content after (for cost/time metadata).
|
|
74752
75445
|
return { status: 'done', priority: 0 };
|
|
74753
75446
|
}
|
|
74754
|
-
|
|
74755
|
-
|
|
74756
|
-
if (failedMatch) {
|
|
75447
|
+
if (marker === '!') {
|
|
75448
|
+
// For failed prompts [!], allow any content after (for failure metadata).
|
|
74757
75449
|
return { status: 'failed', priority: 0 };
|
|
74758
75450
|
}
|
|
74759
|
-
|
|
74760
|
-
|
|
74761
|
-
if (inProgressMatch) {
|
|
75451
|
+
if (marker === '^') {
|
|
75452
|
+
// For in-progress prompts [^], allow any content after (for the steps recorded so far).
|
|
74762
75453
|
return { status: 'in-progress', priority: 0 };
|
|
74763
75454
|
}
|
|
74764
|
-
//
|
|
74765
|
-
if (
|
|
75455
|
+
// Treat [.] as the alternative spelling of not-ready [-], preserving the historical clean-line syntax.
|
|
75456
|
+
if ((marker === '-' || marker === '.') && /^[!\s]*$/u.test(details)) {
|
|
74766
75457
|
return { status: 'not-ready', priority: 0 };
|
|
74767
75458
|
}
|
|
74768
|
-
|
|
74769
|
-
// before or after those tokens. Other trailing text remains an invalid status line.
|
|
74770
|
-
const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
|
|
74771
|
-
if (!todoMatch) {
|
|
75459
|
+
if (marker !== ' ') {
|
|
74772
75460
|
return undefined;
|
|
74773
75461
|
}
|
|
74774
|
-
|
|
75462
|
+
// Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
|
|
75463
|
+
// before or after those tokens. Other trailing text remains an invalid status line.
|
|
74775
75464
|
const isPriorityOnly = /^[!\s]*$/u.test(details);
|
|
74776
75465
|
const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
|
|
74777
75466
|
if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
|
|
74778
75467
|
return undefined;
|
|
74779
75468
|
}
|
|
74780
|
-
return { status: 'todo', priority: (
|
|
75469
|
+
return { status: 'todo', priority: (_e = (_d = details.match(/!/gu)) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0 };
|
|
74781
75470
|
}
|
|
75471
|
+
|
|
74782
75472
|
/**
|
|
74783
|
-
*
|
|
75473
|
+
* HTML comment which excludes a Markdown file from all `ptbk coder` queues.
|
|
74784
75474
|
*
|
|
74785
|
-
* @private internal
|
|
75475
|
+
* @private internal constant of `loadPromptFiles`
|
|
74786
75476
|
*/
|
|
74787
|
-
|
|
74788
|
-
return PROMPT_STATUS_MARKER_PATTERN.test(line);
|
|
74789
|
-
}
|
|
74790
|
-
|
|
75477
|
+
const PTBK_CODER_IGNORE_MARKER = '<!--ptbk-coder-ignore-->';
|
|
74791
75478
|
/**
|
|
74792
|
-
* Loads and parses prompt files from the prompts directory.
|
|
75479
|
+
* Loads and parses prompt files from the prompts directory, excluding files marked for `ptbk coder` to ignore.
|
|
74793
75480
|
*/
|
|
74794
75481
|
async function loadPromptFiles(promptsDir) {
|
|
74795
75482
|
const entries = await promises.readdir(promptsDir, { withFileTypes: true });
|
|
@@ -74800,6 +75487,9 @@
|
|
|
74800
75487
|
const promptFiles = [];
|
|
74801
75488
|
for (const filePath of files) {
|
|
74802
75489
|
const content = await promises.readFile(filePath, 'utf-8');
|
|
75490
|
+
if (content.includes(PTBK_CODER_IGNORE_MARKER)) {
|
|
75491
|
+
continue;
|
|
75492
|
+
}
|
|
74803
75493
|
promptFiles.push(parsePromptFile(filePath, content));
|
|
74804
75494
|
}
|
|
74805
75495
|
return promptFiles;
|
|
@@ -74910,22 +75600,158 @@
|
|
|
74910
75600
|
});
|
|
74911
75601
|
|
|
74912
75602
|
/**
|
|
74913
|
-
*
|
|
75603
|
+
* Reads an optional agent `.book` file and prepares the references that route prompts to it.
|
|
74914
75604
|
*
|
|
74915
|
-
*
|
|
74916
|
-
* inside a branded error, where the stack of the wrapped error would only bury the actual cause.
|
|
75605
|
+
* Returns `undefined` when no agent path is provided.
|
|
74917
75606
|
*/
|
|
74918
|
-
function
|
|
74919
|
-
|
|
74920
|
-
|
|
75607
|
+
async function resolveCoderAgentBook(agentBookReference, currentWorkingDirectory) {
|
|
75608
|
+
const normalizedAgentBookReference = agentBookReference === null || agentBookReference === void 0 ? void 0 : agentBookReference.trim();
|
|
75609
|
+
if (!normalizedAgentBookReference) {
|
|
75610
|
+
return undefined;
|
|
74921
75611
|
}
|
|
74922
|
-
|
|
74923
|
-
|
|
75612
|
+
const resolvedAgentBookPath = path.resolve(currentWorkingDirectory, normalizedAgentBookReference);
|
|
75613
|
+
const agentSource = (await promises.readFile(resolvedAgentBookPath, 'utf-8').catch((error) => {
|
|
75614
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
|
75615
|
+
throw new NotFoundError(spaceTrim(`
|
|
75616
|
+
Agent book \`${normalizedAgentBookReference}\` was not found or is not a file.
|
|
75617
|
+
|
|
75618
|
+
Pass a path to a \`.book\` file in \`--agent\`.
|
|
75619
|
+
`));
|
|
75620
|
+
}
|
|
75621
|
+
throw error;
|
|
75622
|
+
}));
|
|
75623
|
+
return {
|
|
75624
|
+
agentSource,
|
|
75625
|
+
agentReferences: createCoderAgentReferences({
|
|
75626
|
+
agentBookReference: normalizedAgentBookReference,
|
|
75627
|
+
resolvedAgentBookPath,
|
|
75628
|
+
currentWorkingDirectory,
|
|
75629
|
+
agentSource,
|
|
75630
|
+
}),
|
|
75631
|
+
};
|
|
75632
|
+
}
|
|
75633
|
+
/**
|
|
75634
|
+
* Reads an optional agent `.book` file and compiles its system message for injection into coder prompts.
|
|
75635
|
+
*
|
|
75636
|
+
* Returns `undefined` when no agent path is provided.
|
|
75637
|
+
*/
|
|
75638
|
+
async function resolveCoderAgent(agentBookReference, currentWorkingDirectory) {
|
|
75639
|
+
const resolvedAgentBook = await resolveCoderAgentBook(agentBookReference, currentWorkingDirectory);
|
|
75640
|
+
if (resolvedAgentBook === undefined) {
|
|
75641
|
+
return undefined;
|
|
74924
75642
|
}
|
|
74925
|
-
|
|
74926
|
-
|
|
75643
|
+
return {
|
|
75644
|
+
...resolvedAgentBook,
|
|
75645
|
+
systemMessage: await createAgentRunnerSystemMessage(resolvedAgentBook.agentSource),
|
|
75646
|
+
};
|
|
75647
|
+
}
|
|
75648
|
+
/**
|
|
75649
|
+
* Creates the path and Book-title aliases which a prompt can use to target one selected agent.
|
|
75650
|
+
*
|
|
75651
|
+
* @private internal utility of `resolveCoderAgentBook`
|
|
75652
|
+
*/
|
|
75653
|
+
function createCoderAgentReferences(options) {
|
|
75654
|
+
const relativeAgentBookPath = path.relative(options.currentWorkingDirectory, options.resolvedAgentBookPath).replaceAll('\\', '/');
|
|
75655
|
+
const agentBookFileName = path.basename(options.resolvedAgentBookPath);
|
|
75656
|
+
const agentBookName = path.basename(options.resolvedAgentBookPath, path.extname(options.resolvedAgentBookPath));
|
|
75657
|
+
const agentNameFromBook = parseAgentSource(options.agentSource).agentName;
|
|
75658
|
+
return Array.from(new Set([
|
|
75659
|
+
options.agentBookReference,
|
|
75660
|
+
relativeAgentBookPath,
|
|
75661
|
+
agentBookFileName,
|
|
75662
|
+
agentBookName,
|
|
75663
|
+
agentNameFromBook,
|
|
75664
|
+
].filter((agentReference) => agentReference.trim() !== '')));
|
|
75665
|
+
}
|
|
75666
|
+
|
|
75667
|
+
var resolveCoderAgent$1 = /*#__PURE__*/Object.freeze({
|
|
75668
|
+
__proto__: null,
|
|
75669
|
+
resolveCoderAgentBook: resolveCoderAgentBook,
|
|
75670
|
+
resolveCoderAgent: resolveCoderAgent
|
|
75671
|
+
});
|
|
75672
|
+
|
|
75673
|
+
/**
|
|
75674
|
+
* Lists todo prompts that are ready to run (no authoring placeholders).
|
|
75675
|
+
*/
|
|
75676
|
+
function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
75677
|
+
return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
|
|
75678
|
+
isPromptInPriorityFilter(prompt.section, priorityFilter) &&
|
|
75679
|
+
isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
|
|
75680
|
+
}
|
|
75681
|
+
|
|
75682
|
+
/**
|
|
75683
|
+
* Lists upcoming tasks that are ready to run (no authoring placeholders).
|
|
75684
|
+
*/
|
|
75685
|
+
function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
75686
|
+
return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
|
|
75687
|
+
label: buildPromptLabelForDisplay(file, section),
|
|
75688
|
+
summary: buildPromptSummary(file, section),
|
|
75689
|
+
priority: section.priority,
|
|
75690
|
+
}));
|
|
75691
|
+
}
|
|
75692
|
+
|
|
75693
|
+
/**
|
|
75694
|
+
* Groups upcoming tasks by priority, high to low.
|
|
75695
|
+
*/
|
|
75696
|
+
function groupUpcomingTasksByPriority(tasks) {
|
|
75697
|
+
const grouped = new Map();
|
|
75698
|
+
for (const task of tasks) {
|
|
75699
|
+
const group = grouped.get(task.priority);
|
|
75700
|
+
if (group) {
|
|
75701
|
+
group.push(task);
|
|
75702
|
+
}
|
|
75703
|
+
else {
|
|
75704
|
+
grouped.set(task.priority, [task]);
|
|
75705
|
+
}
|
|
75706
|
+
}
|
|
75707
|
+
return Array.from(grouped.entries())
|
|
75708
|
+
.sort((a, b) => b[0] - a[0])
|
|
75709
|
+
.map(([priority, groupedTasks]) => ({ priority, tasks: groupedTasks }));
|
|
75710
|
+
}
|
|
75711
|
+
|
|
75712
|
+
/**
|
|
75713
|
+
* Prints upcoming tasks grouped by priority.
|
|
75714
|
+
*/
|
|
75715
|
+
function printUpcomingTasks(tasks) {
|
|
75716
|
+
if (tasks.length === 0) {
|
|
75717
|
+
console.info(colors__default["default"].green('No upcoming tasks.'));
|
|
75718
|
+
return;
|
|
75719
|
+
}
|
|
75720
|
+
console.info(colors__default["default"].cyan('Upcoming tasks (grouped by priority):'));
|
|
75721
|
+
for (const group of groupUpcomingTasksByPriority(tasks)) {
|
|
75722
|
+
console.info(colors__default["default"].cyan(`Priority ${group.priority}:`));
|
|
75723
|
+
for (const [index, task] of group.tasks.entries()) {
|
|
75724
|
+
const summary = task.summary ? ` - ${task.summary}` : '';
|
|
75725
|
+
console.info(` ${index + 1}. ${task.label}${summary}`);
|
|
75726
|
+
}
|
|
75727
|
+
}
|
|
75728
|
+
}
|
|
75729
|
+
|
|
75730
|
+
/**
|
|
75731
|
+
* Directory containing the prompt queue of the current project.
|
|
75732
|
+
*
|
|
75733
|
+
* @private internal constant of `listCoderPrompts`
|
|
75734
|
+
*/
|
|
75735
|
+
const PROMPTS_DIRECTORY_PATH = path.join(process.cwd(), 'prompts');
|
|
75736
|
+
/**
|
|
75737
|
+
* Lists ready, fully authored coding prompts in descending priority groups without starting a coding harness.
|
|
75738
|
+
*
|
|
75739
|
+
* @returns The number of prompts printed.
|
|
75740
|
+
* @public exported from `@promptbook/cli`
|
|
75741
|
+
*/
|
|
75742
|
+
async function listCoderPrompts(options = {}) {
|
|
75743
|
+
const priorityFilter = normalizePriorityFilter(options);
|
|
75744
|
+
const promptFiles = await loadPromptFiles(PROMPTS_DIRECTORY_PATH);
|
|
75745
|
+
const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity);
|
|
75746
|
+
printUpcomingTasks(upcomingTasks);
|
|
75747
|
+
return upcomingTasks.length;
|
|
74927
75748
|
}
|
|
74928
75749
|
|
|
75750
|
+
var listCoderPrompts$1 = /*#__PURE__*/Object.freeze({
|
|
75751
|
+
__proto__: null,
|
|
75752
|
+
listCoderPrompts: listCoderPrompts
|
|
75753
|
+
});
|
|
75754
|
+
|
|
74929
75755
|
/**
|
|
74930
75756
|
* Builds a normalized temporary shell script path for prompt runners.
|
|
74931
75757
|
*/
|
|
@@ -75224,7 +76050,7 @@
|
|
|
75224
76050
|
/**
|
|
75225
76051
|
* CLI usage text for this script.
|
|
75226
76052
|
*/
|
|
75227
|
-
const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
|
|
76053
|
+
const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--agent <agent-book-path>] [--context <context-or-file>] [--test <test-command...>] [--test-before <no|yes-and-fail|yes-and-fix>] [--preserve-logs] [--isolate] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--min-priority <minimum-priority>] [--max-priority <maximum-priority>] [--limit <run-count>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--git-changes <fail|ignore|continue>] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
|
|
75228
76054
|
/**
|
|
75229
76055
|
* Top-level flags supported by this command.
|
|
75230
76056
|
*/
|
|
@@ -75232,6 +76058,7 @@
|
|
|
75232
76058
|
'--dry-run',
|
|
75233
76059
|
'--harness',
|
|
75234
76060
|
'--model',
|
|
76061
|
+
'--agent',
|
|
75235
76062
|
'--context',
|
|
75236
76063
|
'--test',
|
|
75237
76064
|
'--test-before',
|
|
@@ -75270,6 +76097,7 @@
|
|
|
75270
76097
|
}
|
|
75271
76098
|
}
|
|
75272
76099
|
const model = readOptionValue(args, '--model');
|
|
76100
|
+
const agent = readOptionValue(args, '--agent');
|
|
75273
76101
|
const context = readOptionValue(args, '--context');
|
|
75274
76102
|
const hasTestCommandFlag = args.includes('--test');
|
|
75275
76103
|
const testCommand = readVariadicOptionValue(args, '--test');
|
|
@@ -75346,6 +76174,7 @@
|
|
|
75346
76174
|
noUi,
|
|
75347
76175
|
agentName,
|
|
75348
76176
|
model,
|
|
76177
|
+
agent,
|
|
75349
76178
|
context,
|
|
75350
76179
|
testCommand,
|
|
75351
76180
|
testBefore,
|
|
@@ -75780,30 +76609,43 @@
|
|
|
75780
76609
|
// Note: [🟡] Code in this file is only used from `@promptbook/cli` orchestration scripts and is not shipped to other consumers.
|
|
75781
76610
|
|
|
75782
76611
|
/**
|
|
75783
|
-
*
|
|
76612
|
+
* Creates the guard which watches the free disk space while one `ptbk coder` run is in progress.
|
|
75784
76613
|
*
|
|
75785
|
-
*
|
|
76614
|
+
* The guard runs at every pause checkpoint of the run. When the disk became critically full it reports the
|
|
76615
|
+
* warning and requests a pause, so the run stops at that checkpoint instead of failing in the middle of a
|
|
76616
|
+
* coding round, and continues once the user has freed some space and pressed `P`.
|
|
76617
|
+
*
|
|
76618
|
+
* @private internal utility of `ptbk coder` free disk space handling
|
|
75786
76619
|
*/
|
|
75787
|
-
|
|
75788
|
-
|
|
75789
|
-
|
|
75790
|
-
|
|
75791
|
-
|
|
75792
|
-
|
|
75793
|
-
|
|
75794
|
-
|
|
75795
|
-
|
|
76620
|
+
function createFreeDiskSpaceGuard(options) {
|
|
76621
|
+
const { inspectedPath, isAskingQuestionsEnabled } = options;
|
|
76622
|
+
let lastMeasurementTimeMs;
|
|
76623
|
+
return async () => {
|
|
76624
|
+
if (lastMeasurementTimeMs !== undefined &&
|
|
76625
|
+
Date.now() - lastMeasurementTimeMs < FREE_DISK_SPACE_RECHECK_INTERVAL_MS) {
|
|
76626
|
+
return;
|
|
76627
|
+
}
|
|
76628
|
+
lastMeasurementTimeMs = Date.now();
|
|
76629
|
+
const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
|
|
76630
|
+
if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level !== 'critical') {
|
|
76631
|
+
return;
|
|
76632
|
+
}
|
|
76633
|
+
console.warn(colors__default["default"].yellow(_spaceTrim.spaceTrim((block) => `
|
|
76634
|
+
The disk is running out of space while \`ptbk coder\` is working.
|
|
75796
76635
|
|
|
75797
|
-
|
|
75798
|
-
|
|
76636
|
+
${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
|
|
76637
|
+
- Press \`P\` to continue once the disk space is freed, or start the run with \`--no-questions\` to never pause on a full disk.
|
|
76638
|
+
`)));
|
|
76639
|
+
if (!isAskingQuestionsEnabled) {
|
|
76640
|
+
// Note: `--no-questions` forbids stopping and waiting for somebody who would free the disk space,
|
|
76641
|
+
// so the run keeps going and the warning above is the only thing which is reported
|
|
76642
|
+
return;
|
|
75799
76643
|
}
|
|
75800
|
-
|
|
75801
|
-
|
|
75802
|
-
return {
|
|
75803
|
-
agentSource: agentSource,
|
|
75804
|
-
systemMessage: await createAgentRunnerSystemMessage(agentSource),
|
|
76644
|
+
announcePauseTargetLabel(`continuing with only ${formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes)} of free disk space left`);
|
|
76645
|
+
requestPause();
|
|
75805
76646
|
};
|
|
75806
76647
|
}
|
|
76648
|
+
// Note: [🟡] Code for `ptbk coder` free disk space handling [createFreeDiskSpaceGuard](scripts/run-codex-prompts/common/createFreeDiskSpaceGuard.ts) should never be published outside of `@promptbook/cli`
|
|
75807
76649
|
|
|
75808
76650
|
/**
|
|
75809
76651
|
* Refresh interval used by the countdown display while waiting.
|
|
@@ -77774,6 +78616,169 @@
|
|
|
77774
78616
|
await promises.writeFile(file.path, content, 'utf-8');
|
|
77775
78617
|
}
|
|
77776
78618
|
|
|
78619
|
+
/**
|
|
78620
|
+
* Creates a safe markdown fenced code block even when content contains backticks.
|
|
78621
|
+
*
|
|
78622
|
+
* @param content - Raw code content.
|
|
78623
|
+
* @param language - Optional info-string language label.
|
|
78624
|
+
* @returns Fenced code block.
|
|
78625
|
+
*
|
|
78626
|
+
* @private internal utility of `createStandaloneBookLanguageMarkdown` and of the `ptbk coder` run traces
|
|
78627
|
+
*/
|
|
78628
|
+
function getSafeCodeBlock(content, language = 'markdown') {
|
|
78629
|
+
const maxBacktickCount = Math.max(0, ...(content.match(/`+/g) || []).map((match) => match.length));
|
|
78630
|
+
const fence = '`'.repeat(Math.max(3, maxBacktickCount + 1));
|
|
78631
|
+
return `${fence}${language}\n${content}\n${fence}`;
|
|
78632
|
+
}
|
|
78633
|
+
|
|
78634
|
+
/**
|
|
78635
|
+
* Renders the markdown run trace of one finished or failed prompt round.
|
|
78636
|
+
*
|
|
78637
|
+
* The trace pairs the metadata of the round - which harness, model and thinking level ran it, how long each of
|
|
78638
|
+
* its steps took and what it cost - with the untouched runtime log, so a finished round can still be analyzed
|
|
78639
|
+
* after its temporary artifacts are cleaned up.
|
|
78640
|
+
*/
|
|
78641
|
+
function buildPromptRunTraceContent(options) {
|
|
78642
|
+
const sections = [
|
|
78643
|
+
buildPromptRunTraceSummarySection(options),
|
|
78644
|
+
buildPromptRunTraceFailureSection(options.outcome),
|
|
78645
|
+
buildPromptRunTraceRuntimeLogSection(options.runtimeLog),
|
|
78646
|
+
].filter((section) => section !== undefined);
|
|
78647
|
+
return `${sections.join('\n\n')}\n`;
|
|
78648
|
+
}
|
|
78649
|
+
/**
|
|
78650
|
+
* Renders the heading and the metadata bullet list of one run trace.
|
|
78651
|
+
*/
|
|
78652
|
+
function buildPromptRunTraceSummarySection(options) {
|
|
78653
|
+
const { file, section, outcome } = options;
|
|
78654
|
+
const runnerSignature = formatRunnerSignature(options.runnerName, options.modelName, options.thinkingLevel);
|
|
78655
|
+
const loginMethodLabel = outcome.kind === 'succeeded' ? formatCodexLoginMethod(outcome.loginMethod) : undefined;
|
|
78656
|
+
const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
|
|
78657
|
+
const detailLines = [
|
|
78658
|
+
`- **Prompt:** ${buildPromptSummary(file, section)}`,
|
|
78659
|
+
...buildPromptRunTraceSectionLines(file, section),
|
|
78660
|
+
`- **Outcome:** ${formatPromptRunTraceOutcomeLabel(outcome)}`,
|
|
78661
|
+
`- **Runner:** ${runnerSignature}${loginMethodSuffix}`,
|
|
78662
|
+
`- **Attempts:** ${options.attemptCount}`,
|
|
78663
|
+
...buildPromptRunTraceStepsLines(outcome),
|
|
78664
|
+
...buildPromptRunTraceTestCommandLines(options.testCommand),
|
|
78665
|
+
`- **Started:** ${options.startedDate.toISOString()}`,
|
|
78666
|
+
`- **Finished:** ${options.finishedDate.toISOString()}`,
|
|
78667
|
+
`- **Duration:** ${moment__default["default"].duration(options.finishedDate.diff(options.startedDate)).humanize()}`,
|
|
78668
|
+
];
|
|
78669
|
+
return _spaceTrim.spaceTrim((block) => `
|
|
78670
|
+
# Run trace of \`${buildPromptLabelForDisplay(file, section)}\`
|
|
78671
|
+
|
|
78672
|
+
${block(detailLines.join('\n'))}
|
|
78673
|
+
`);
|
|
78674
|
+
}
|
|
78675
|
+
/**
|
|
78676
|
+
* Renders which section of its prompt file the round implemented.
|
|
78677
|
+
*
|
|
78678
|
+
* A prompt file holding exactly one section says nothing by naming it, exactly like the section suffix of its
|
|
78679
|
+
* generated artifact names, so only a file with more than one section reports the section it traced.
|
|
78680
|
+
*/
|
|
78681
|
+
function buildPromptRunTraceSectionLines(file, section) {
|
|
78682
|
+
if (file.sections.length <= 1) {
|
|
78683
|
+
return [];
|
|
78684
|
+
}
|
|
78685
|
+
return [`- **Prompt section:** ${section.index + 1} of ${file.sections.length}`];
|
|
78686
|
+
}
|
|
78687
|
+
/**
|
|
78688
|
+
* Renders the per-step usage breakdown of a successful round, omitted for a round which never finished one.
|
|
78689
|
+
*/
|
|
78690
|
+
function buildPromptRunTraceStepsLines(outcome) {
|
|
78691
|
+
if (outcome.kind !== 'succeeded') {
|
|
78692
|
+
return [];
|
|
78693
|
+
}
|
|
78694
|
+
const stepsSummary = formatCoderRunSteps(outcome.steps);
|
|
78695
|
+
if (stepsSummary === '') {
|
|
78696
|
+
return [];
|
|
78697
|
+
}
|
|
78698
|
+
return [`- **Steps:** ${stepsSummary}`];
|
|
78699
|
+
}
|
|
78700
|
+
/**
|
|
78701
|
+
* Renders the verification command of the round, omitted when the round ran without one.
|
|
78702
|
+
*/
|
|
78703
|
+
function buildPromptRunTraceTestCommandLines(testCommand) {
|
|
78704
|
+
if (!testCommand) {
|
|
78705
|
+
return [];
|
|
78706
|
+
}
|
|
78707
|
+
return [`- **Verification command:** \`${testCommand}\``];
|
|
78708
|
+
}
|
|
78709
|
+
/**
|
|
78710
|
+
* Renders the failure details of a failed round, omitted for a successful one.
|
|
78711
|
+
*/
|
|
78712
|
+
function buildPromptRunTraceFailureSection(outcome) {
|
|
78713
|
+
if (outcome.kind !== 'failed') {
|
|
78714
|
+
return undefined;
|
|
78715
|
+
}
|
|
78716
|
+
return `## Failure\n\n${getSafeCodeBlock(formatUnknownErrorDetails(outcome.error), 'text')}`;
|
|
78717
|
+
}
|
|
78718
|
+
/**
|
|
78719
|
+
* Renders the untouched runtime log of the round, or says that the round produced none.
|
|
78720
|
+
*/
|
|
78721
|
+
function buildPromptRunTraceRuntimeLogSection(runtimeLog) {
|
|
78722
|
+
if (runtimeLog.trim() === '') {
|
|
78723
|
+
return '## Runtime log\n\n_This round has produced no readable runtime log._';
|
|
78724
|
+
}
|
|
78725
|
+
return `## Runtime log\n\n${getSafeCodeBlock(runtimeLog.replace(/\r\n/gu, '\n').trimEnd(), 'text')}`;
|
|
78726
|
+
}
|
|
78727
|
+
/**
|
|
78728
|
+
* Formats the outcome of the round as the human-readable label of its trace.
|
|
78729
|
+
*/
|
|
78730
|
+
function formatPromptRunTraceOutcomeLabel(outcome) {
|
|
78731
|
+
return outcome.kind === 'succeeded' ? 'Succeeded' : 'Failed';
|
|
78732
|
+
}
|
|
78733
|
+
|
|
78734
|
+
/**
|
|
78735
|
+
* Name of the directory which keeps the run traces inside the prompts directory.
|
|
78736
|
+
*/
|
|
78737
|
+
const PROMPT_RUN_TRACES_DIRECTORY_NAME = 'traces';
|
|
78738
|
+
/**
|
|
78739
|
+
* Builds the path of the run trace which belongs to one prompt section.
|
|
78740
|
+
*
|
|
78741
|
+
* The trace is stored in the `traces` directory of the very same prompts directory the prompt itself lives in,
|
|
78742
|
+
* and it carries the name of its prompt file. A prompt file holding more than one section appends the section
|
|
78743
|
+
* suffix, exactly like the temporary scripts and the isolation worktrees of the same section do.
|
|
78744
|
+
*/
|
|
78745
|
+
function buildPromptRunTracePath(file, section) {
|
|
78746
|
+
const promptFileBaseName = file.name.replace(/\.[^.]+$/u, '');
|
|
78747
|
+
const traceFileName = `${promptFileBaseName}${buildPromptSectionSuffix(file, section)}.md`;
|
|
78748
|
+
return path.join(path.dirname(file.path), PROMPT_RUN_TRACES_DIRECTORY_NAME, traceFileName);
|
|
78749
|
+
}
|
|
78750
|
+
|
|
78751
|
+
/**
|
|
78752
|
+
* Writes the run trace of one prompt round into the `traces` directory of the prompts directory.
|
|
78753
|
+
*
|
|
78754
|
+
* Returns the path of the written trace so callers can include it in a commit.
|
|
78755
|
+
*/
|
|
78756
|
+
async function writePromptRunTrace(options) {
|
|
78757
|
+
const { logPath, ...contentOptions } = options;
|
|
78758
|
+
const tracePath = buildPromptRunTracePath(options.file, options.section);
|
|
78759
|
+
const runtimeLog = await readPromptRuntimeLog(logPath);
|
|
78760
|
+
await promises.mkdir(path.dirname(tracePath), { recursive: true });
|
|
78761
|
+
await promises.writeFile(tracePath, buildPromptRunTraceContent({ ...contentOptions, runtimeLog }), 'utf-8');
|
|
78762
|
+
return tracePath;
|
|
78763
|
+
}
|
|
78764
|
+
/**
|
|
78765
|
+
* Reads the live runtime log of one round.
|
|
78766
|
+
*
|
|
78767
|
+
* A round which never produced a readable log still gets its trace, because the metadata of the round is worth
|
|
78768
|
+
* keeping on its own - so a missing log is reported as empty instead of failing the round which has just passed.
|
|
78769
|
+
*/
|
|
78770
|
+
async function readPromptRuntimeLog(logPath) {
|
|
78771
|
+
if (!logPath) {
|
|
78772
|
+
return '';
|
|
78773
|
+
}
|
|
78774
|
+
try {
|
|
78775
|
+
return await promises.readFile(logPath, 'utf-8');
|
|
78776
|
+
}
|
|
78777
|
+
catch (_a) {
|
|
78778
|
+
return '';
|
|
78779
|
+
}
|
|
78780
|
+
}
|
|
78781
|
+
|
|
77777
78782
|
/**
|
|
77778
78783
|
* Refreshes optional harness subscription usage in the shared coder-run UI state.
|
|
77779
78784
|
*
|
|
@@ -77896,7 +78901,9 @@
|
|
|
77896
78901
|
catch (error) {
|
|
77897
78902
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
77898
78903
|
lastError = error;
|
|
77899
|
-
|
|
78904
|
+
// Note: A harness which is not logged in answers every retry the same way, so the user gets
|
|
78905
|
+
// the sign-in instructions right away instead of after every retry has waited its delay
|
|
78906
|
+
if (error instanceof AuthenticationError || errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
|
|
77900
78907
|
break;
|
|
77901
78908
|
}
|
|
77902
78909
|
await waitAfterErrorBeforeRetry({
|
|
@@ -77918,6 +78925,7 @@
|
|
|
77918
78925
|
attemptCount,
|
|
77919
78926
|
error: lastError,
|
|
77920
78927
|
options,
|
|
78928
|
+
logPath,
|
|
77921
78929
|
roundCommitScope,
|
|
77922
78930
|
uiHandle,
|
|
77923
78931
|
waitForRequestedPause,
|
|
@@ -78034,6 +79042,17 @@
|
|
|
78034
79042
|
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
78035
79043
|
// its own changes back through the merge instead
|
|
78036
79044
|
await writePromptFile(nextPrompt.file);
|
|
79045
|
+
// Note: Written before the round is committed, so the trace of the round lands in the very same commit
|
|
79046
|
+
// as the prompt it describes, and before the live runtime log it is built from is deleted
|
|
79047
|
+
await recordPromptRoundTrace({
|
|
79048
|
+
options: runOptions,
|
|
79049
|
+
nextPrompt,
|
|
79050
|
+
runnerMetadata,
|
|
79051
|
+
promptExecutionStartedDate,
|
|
79052
|
+
attemptCount: result.attemptCount,
|
|
79053
|
+
logPath,
|
|
79054
|
+
outcome: { kind: 'succeeded', steps: result.steps, loginMethod: result.loginMethod },
|
|
79055
|
+
});
|
|
78037
79056
|
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
78038
79057
|
await recordPromptDurationInEstimateCache({
|
|
78039
79058
|
options: runOptions,
|
|
@@ -78081,7 +79100,7 @@
|
|
|
78081
79100
|
* Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
|
|
78082
79101
|
*/
|
|
78083
79102
|
async function finalizeFailedPromptRound(options) {
|
|
78084
|
-
const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
79103
|
+
const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, logPath, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
78085
79104
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
78086
79105
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
|
|
78087
79106
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
|
|
@@ -78107,8 +79126,42 @@
|
|
|
78107
79126
|
modelName: runnerMetadata.modelName,
|
|
78108
79127
|
error,
|
|
78109
79128
|
});
|
|
79129
|
+
// Note: A failed round is exactly the round whose trace is worth reading, so it is written before the live
|
|
79130
|
+
// runtime log it is built from is deleted
|
|
79131
|
+
await recordPromptRoundTrace({
|
|
79132
|
+
options: runOptions,
|
|
79133
|
+
nextPrompt,
|
|
79134
|
+
runnerMetadata,
|
|
79135
|
+
promptExecutionStartedDate,
|
|
79136
|
+
attemptCount,
|
|
79137
|
+
logPath,
|
|
79138
|
+
outcome: { kind: 'failed', error },
|
|
79139
|
+
});
|
|
78110
79140
|
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
78111
79141
|
}
|
|
79142
|
+
/**
|
|
79143
|
+
* Persists the run trace of one prompt round, so the round can still be analyzed after its temporary
|
|
79144
|
+
* artifacts are gone.
|
|
79145
|
+
*
|
|
79146
|
+
* This is the single place where both the successful and the failed round record what they did, which harness,
|
|
79147
|
+
* model and thinking level did it and everything that harness has written while doing it.
|
|
79148
|
+
*/
|
|
79149
|
+
async function recordPromptRoundTrace(options) {
|
|
79150
|
+
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
|
|
79151
|
+
await writePromptRunTrace({
|
|
79152
|
+
file: nextPrompt.file,
|
|
79153
|
+
section: nextPrompt.section,
|
|
79154
|
+
runnerName: runnerMetadata.runnerName,
|
|
79155
|
+
modelName: runnerMetadata.modelName,
|
|
79156
|
+
thinkingLevel: runOptions.thinkingLevel,
|
|
79157
|
+
testCommand: runOptions.testCommand,
|
|
79158
|
+
attemptCount,
|
|
79159
|
+
startedDate: promptExecutionStartedDate,
|
|
79160
|
+
finishedDate: moment__default["default"](),
|
|
79161
|
+
outcome,
|
|
79162
|
+
logPath,
|
|
79163
|
+
});
|
|
79164
|
+
}
|
|
78112
79165
|
/**
|
|
78113
79166
|
* Waits for the optional user confirmation immediately before creating the commit.
|
|
78114
79167
|
*/
|
|
@@ -78757,7 +79810,13 @@
|
|
|
78757
79810
|
await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
|
|
78758
79811
|
autoPush: options.options.autoPush,
|
|
78759
79812
|
projectPath: worktree.projectPath,
|
|
78760
|
-
|
|
79813
|
+
// Note: The round itself has already succeeded, so it has left its run trace in the original project.
|
|
79814
|
+
// It belongs to this commit, which is the only one this task still gets.
|
|
79815
|
+
relevantPaths: [
|
|
79816
|
+
nextPrompt.file.path,
|
|
79817
|
+
errorLogPath,
|
|
79818
|
+
buildPromptRunTracePath(nextPrompt.file, nextPrompt.section),
|
|
79819
|
+
].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
|
|
78761
79820
|
});
|
|
78762
79821
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
|
|
78763
79822
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
|
|
@@ -78781,15 +79840,6 @@
|
|
|
78781
79840
|
return path.relative(projectPath, path$1).replace(/\\/gu, '/');
|
|
78782
79841
|
}
|
|
78783
79842
|
|
|
78784
|
-
/**
|
|
78785
|
-
* Lists todo prompts that are ready to run (no authoring placeholders).
|
|
78786
|
-
*/
|
|
78787
|
-
function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
78788
|
-
return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
|
|
78789
|
-
isPromptInPriorityFilter(prompt.section, priorityFilter) &&
|
|
78790
|
-
isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
|
|
78791
|
-
}
|
|
78792
|
-
|
|
78793
79843
|
/**
|
|
78794
79844
|
* Selects the next runnable prompt based on priority.
|
|
78795
79845
|
*/
|
|
@@ -78803,54 +79853,6 @@
|
|
|
78803
79853
|
return nextPrompt;
|
|
78804
79854
|
}
|
|
78805
79855
|
|
|
78806
|
-
/**
|
|
78807
|
-
* Lists upcoming tasks that are ready to run (no authoring placeholders).
|
|
78808
|
-
*/
|
|
78809
|
-
function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
78810
|
-
return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
|
|
78811
|
-
label: buildPromptLabelForDisplay(file, section),
|
|
78812
|
-
summary: buildPromptSummary(file, section),
|
|
78813
|
-
priority: section.priority,
|
|
78814
|
-
}));
|
|
78815
|
-
}
|
|
78816
|
-
|
|
78817
|
-
/**
|
|
78818
|
-
* Groups upcoming tasks by priority, high to low.
|
|
78819
|
-
*/
|
|
78820
|
-
function groupUpcomingTasksByPriority(tasks) {
|
|
78821
|
-
const grouped = new Map();
|
|
78822
|
-
for (const task of tasks) {
|
|
78823
|
-
const group = grouped.get(task.priority);
|
|
78824
|
-
if (group) {
|
|
78825
|
-
group.push(task);
|
|
78826
|
-
}
|
|
78827
|
-
else {
|
|
78828
|
-
grouped.set(task.priority, [task]);
|
|
78829
|
-
}
|
|
78830
|
-
}
|
|
78831
|
-
return Array.from(grouped.entries())
|
|
78832
|
-
.sort((a, b) => b[0] - a[0])
|
|
78833
|
-
.map(([priority, groupedTasks]) => ({ priority, tasks: groupedTasks }));
|
|
78834
|
-
}
|
|
78835
|
-
|
|
78836
|
-
/**
|
|
78837
|
-
* Prints upcoming tasks grouped by priority.
|
|
78838
|
-
*/
|
|
78839
|
-
function printUpcomingTasks(tasks) {
|
|
78840
|
-
if (tasks.length === 0) {
|
|
78841
|
-
console.info(colors__default["default"].green('No upcoming tasks.'));
|
|
78842
|
-
return;
|
|
78843
|
-
}
|
|
78844
|
-
console.info(colors__default["default"].cyan('Upcoming tasks (grouped by priority):'));
|
|
78845
|
-
for (const group of groupUpcomingTasksByPriority(tasks)) {
|
|
78846
|
-
console.info(colors__default["default"].cyan(`Priority ${group.priority}:`));
|
|
78847
|
-
for (const [index, task] of group.tasks.entries()) {
|
|
78848
|
-
const summary = task.summary ? ` - ${task.summary}` : '';
|
|
78849
|
-
console.info(` ${index + 1}. ${task.label}${summary}`);
|
|
78850
|
-
}
|
|
78851
|
-
}
|
|
78852
|
-
}
|
|
78853
|
-
|
|
78854
79856
|
/**
|
|
78855
79857
|
* Builds a label that identifies the prompt section by index.
|
|
78856
79858
|
*/
|
|
@@ -79089,12 +80091,23 @@
|
|
|
79089
80091
|
* @public exported from `@promptbook/cli`
|
|
79090
80092
|
*/
|
|
79091
80093
|
async function runCodexPrompts(providedOptions) {
|
|
80094
|
+
var _a;
|
|
79092
80095
|
const options = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
|
|
79093
80096
|
validateRunCodexPromptOptions(options);
|
|
79094
80097
|
resetCoderRunControls();
|
|
79095
80098
|
const runStartDate = moment__default["default"]();
|
|
79096
80099
|
const { isRichUiEnabled, progressDisplay, uiHandle } = createRunDisplays(options, runStartDate);
|
|
79097
|
-
const waitForRequestedPause = createPauseWaiter({
|
|
80100
|
+
const waitForRequestedPause = createPauseWaiter({
|
|
80101
|
+
isRichUiEnabled,
|
|
80102
|
+
progressDisplay,
|
|
80103
|
+
uiHandle,
|
|
80104
|
+
// Note: Every pause checkpoint of the whole run goes through this one waiter, so watching the free disk
|
|
80105
|
+
// space here covers each round, each verification and each runner without repeating the check
|
|
80106
|
+
guardFreeDiskSpace: createFreeDiskSpaceGuard({
|
|
80107
|
+
inspectedPath: process.cwd(),
|
|
80108
|
+
isAskingQuestionsEnabled: (_a = options.isAskingQuestionsEnabled) !== null && _a !== void 0 ? _a : true,
|
|
80109
|
+
}),
|
|
80110
|
+
});
|
|
79098
80111
|
startPauseListenerIfNeeded(isRichUiEnabled);
|
|
79099
80112
|
try {
|
|
79100
80113
|
const resolvedCoderContext = await resolveCoderContext(options.context, process.cwd());
|
|
@@ -79107,6 +80120,7 @@
|
|
|
79107
80120
|
const promptRunnerIdentity = {
|
|
79108
80121
|
harnessName: options.agentName,
|
|
79109
80122
|
modelName: actualRunnerModel,
|
|
80123
|
+
agentReferences: resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentReferences,
|
|
79110
80124
|
};
|
|
79111
80125
|
console.info(colors__default["default"].green(`Running prompts with ${runner.name}`));
|
|
79112
80126
|
initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
|
|
@@ -79543,11 +80557,14 @@
|
|
|
79543
80557
|
* Creates a pause waiter that keeps the progress display and rich UI in sync.
|
|
79544
80558
|
*/
|
|
79545
80559
|
function createPauseWaiter(options) {
|
|
79546
|
-
const { isRichUiEnabled, progressDisplay, uiHandle } = options;
|
|
80560
|
+
const { isRichUiEnabled, progressDisplay, uiHandle, guardFreeDiskSpace } = options;
|
|
79547
80561
|
return async (checkpoint) => {
|
|
79548
80562
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase(checkpoint.phase);
|
|
79549
80563
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(checkpoint.statusMessage);
|
|
79550
80564
|
announcePauseTargetLabel(checkpoint.checkpointLabel);
|
|
80565
|
+
// Note: Runs after the checkpoint label is announced, so a pause requested because of a full disk
|
|
80566
|
+
// replaces that label and says why the run is standing still
|
|
80567
|
+
await guardFreeDiskSpace();
|
|
79551
80568
|
await checkPause({
|
|
79552
80569
|
silent: isRichUiEnabled,
|
|
79553
80570
|
onPaused: () => {
|
|
@@ -79666,7 +80683,7 @@
|
|
|
79666
80683
|
return false;
|
|
79667
80684
|
}
|
|
79668
80685
|
if (promptQueueSnapshot.stats.forAgent > 0) {
|
|
79669
|
-
announceRunCompletion('No prompts match the selected harness or
|
|
80686
|
+
announceRunCompletion('No prompts match the selected harness, model or agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
|
|
79670
80687
|
}
|
|
79671
80688
|
else if (promptQueueSnapshot.stats.toBeWritten > 0) {
|
|
79672
80689
|
announceRunCompletion('No prompts ready for agent.', colors__default["default"].yellow, isRichUiEnabled, uiHandle);
|
|
@@ -79711,7 +80728,7 @@
|
|
|
79711
80728
|
function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
|
|
79712
80729
|
let message;
|
|
79713
80730
|
if (promptQueueSnapshot.stats.forAgent > 0) {
|
|
79714
|
-
message = 'No prompts match the selected harness or
|
|
80731
|
+
message = 'No prompts match the selected harness, model or agent. Watching for changes...';
|
|
79715
80732
|
}
|
|
79716
80733
|
else if (promptQueueSnapshot.stats.toBeWritten > 0) {
|
|
79717
80734
|
message = 'No prompts ready for agent. Watching for changes...';
|