@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/esm/index.es.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import colors from 'colors';
|
|
2
2
|
import commander, { Option } from 'commander';
|
|
3
3
|
import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
4
|
-
import { writeFile, stat, mkdir, readFile,
|
|
4
|
+
import { writeFile, stat, readdir, mkdir, readFile, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, statfs, access, constants, watch, rmdir } from 'fs/promises';
|
|
5
5
|
import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
|
|
6
6
|
import { createHash, randomBytes } from 'crypto';
|
|
7
7
|
import { spawn, spawnSync, execFile } from 'child_process';
|
|
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
48
48
|
* @generated
|
|
49
49
|
* @see https://github.com/webgptorg/promptbook
|
|
50
50
|
*/
|
|
51
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-34';
|
|
52
52
|
/**
|
|
53
53
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
54
54
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1708,6 +1708,35 @@ function $initializeAboutCommand(program) {
|
|
|
1708
1708
|
// TODO: [🗽] Unite branding and make single place for it
|
|
1709
1709
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1710
1710
|
|
|
1711
|
+
/**
|
|
1712
|
+
* Message printed when one CLI command which only groups subcommands is run without picking any of them.
|
|
1713
|
+
*
|
|
1714
|
+
* @private internal constant of `$requireCliSubcommand`
|
|
1715
|
+
*/
|
|
1716
|
+
const MISSING_CLI_SUBCOMMAND_MESSAGE = 'Please specify a subcommand.';
|
|
1717
|
+
/**
|
|
1718
|
+
* Makes one CLI command which only groups subcommands ask for a subcommand instead of doing anything on its own.
|
|
1719
|
+
*
|
|
1720
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers an action in the CLI
|
|
1721
|
+
*
|
|
1722
|
+
* @private utility of CLI
|
|
1723
|
+
*/
|
|
1724
|
+
function $requireCliSubcommand(command) {
|
|
1725
|
+
command.action(() => $reportMissingCliSubcommand(command));
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Asks for a subcommand of one CLI command, prints its help and exits the process.
|
|
1729
|
+
*
|
|
1730
|
+
* Note: `$` is used to indicate that this function is not a pure function - it writes to the console and exits the process
|
|
1731
|
+
*
|
|
1732
|
+
* @private utility of CLI
|
|
1733
|
+
*/
|
|
1734
|
+
function $reportMissingCliSubcommand(command) {
|
|
1735
|
+
console.info(colors.yellow(MISSING_CLI_SUBCOMMAND_MESSAGE));
|
|
1736
|
+
console.info('');
|
|
1737
|
+
return command.help();
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1711
1740
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1712
1741
|
/**
|
|
1713
1742
|
* All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
|
|
@@ -1812,20 +1841,20 @@ const PROMPT_RUNNER_HARNESS_NAMES = CLI_AGENT_HARNESS_NAMES;
|
|
|
1812
1841
|
*/
|
|
1813
1842
|
const PROMPT_RUNNER_DESCRIPTION = spaceTrim$1(`
|
|
1814
1843
|
Runners:
|
|
1815
|
-
- openai-codex: OpenAI Codex integration (requires --model)
|
|
1844
|
+
- openai-codex: OpenAI Codex integration (requires --model when executing)
|
|
1816
1845
|
- github-copilot: GitHub Copilot CLI integration
|
|
1817
1846
|
- cline: Cline CLI integration
|
|
1818
1847
|
- claude-code: Claude Code integration
|
|
1819
1848
|
- opencode: Opencode integration
|
|
1820
|
-
- gemini: Google Gemini CLI integration (requires --model)
|
|
1821
|
-
- qwen-code: Qwen Code CLI integration (requires --model)
|
|
1849
|
+
- gemini: Google Gemini CLI integration (requires --model when executing)
|
|
1850
|
+
- qwen-code: Qwen Code CLI integration (requires --model when executing)
|
|
1822
1851
|
`);
|
|
1823
1852
|
/**
|
|
1824
1853
|
* Commander description for the `--harness` option.
|
|
1825
1854
|
*
|
|
1826
1855
|
* @private internal utility of `promptbookCli`
|
|
1827
1856
|
*/
|
|
1828
|
-
const PROMPT_RUNNER_HARNESS_OPTION_DESCRIPTION = `Select runner: ${PROMPT_RUNNER_HARNESS_NAMES.join(', ')} (required
|
|
1857
|
+
const PROMPT_RUNNER_HARNESS_OPTION_DESCRIPTION = `Select runner: ${PROMPT_RUNNER_HARNESS_NAMES.join(', ')} (required when executing prompts)`;
|
|
1829
1858
|
/**
|
|
1830
1859
|
* Runner harness names listed as alternatives of the `--harness` option in error messages.
|
|
1831
1860
|
*
|
|
@@ -1838,7 +1867,7 @@ const PROMPT_RUNNER_HARNESS_OPTION_HINT = `--harness <${PROMPT_RUNNER_HARNESS_NA
|
|
|
1838
1867
|
* @private internal utility of `promptbookCli`
|
|
1839
1868
|
*/
|
|
1840
1869
|
const PROMPT_RUNNER_MODEL_OPTION_DESCRIPTION = spaceTrim$1(`
|
|
1841
|
-
Model to use (required
|
|
1870
|
+
Model to use or filter by (required when executing with openai-codex, gemini and qwen-code)
|
|
1842
1871
|
|
|
1843
1872
|
OpenAI examples: gpt-5.2-codex, default
|
|
1844
1873
|
Gemini examples: gemini-3-flash-preview, default
|
|
@@ -2103,11 +2132,7 @@ function $initializeAgentCommand(program) {
|
|
|
2103
2132
|
`));
|
|
2104
2133
|
$initializeAgentChatCommand(agentCommand);
|
|
2105
2134
|
$initializeAgentExecCommand(agentCommand);
|
|
2106
|
-
agentCommand
|
|
2107
|
-
console.info(colors.yellow('Please specify a subcommand.'));
|
|
2108
|
-
console.info('');
|
|
2109
|
-
agentCommand.help();
|
|
2110
|
-
});
|
|
2135
|
+
$requireCliSubcommand(agentCommand);
|
|
2111
2136
|
}
|
|
2112
2137
|
// Note: [🟡] Code for CLI command [agent](src/cli/cli-commands/agent.ts) should never be published outside of `@promptbook/cli`
|
|
2113
2138
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2265,6 +2290,23 @@ async function ensureDirectory(projectPath, relativeDirectoryPath) {
|
|
|
2265
2290
|
}
|
|
2266
2291
|
return 'unchanged';
|
|
2267
2292
|
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Checks whether a project-relative directory is missing or contains no entries.
|
|
2295
|
+
*
|
|
2296
|
+
* @private internal utility of `coder init` command
|
|
2297
|
+
*/
|
|
2298
|
+
async function isDirectoryEmpty(projectPath, relativeDirectoryPath) {
|
|
2299
|
+
const directoryPath = join(projectPath, relativeDirectoryPath);
|
|
2300
|
+
try {
|
|
2301
|
+
return (await readdir(directoryPath)).length === 0;
|
|
2302
|
+
}
|
|
2303
|
+
catch (error) {
|
|
2304
|
+
if (isNodeJsErrorWithCode$1(error, 'ENOENT')) {
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
throw error;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2268
2310
|
/**
|
|
2269
2311
|
* Checks whether a path exists and is a directory.
|
|
2270
2312
|
*/
|
|
@@ -2276,6 +2318,12 @@ async function isExistingDirectory$2(path) {
|
|
|
2276
2318
|
return false;
|
|
2277
2319
|
}
|
|
2278
2320
|
}
|
|
2321
|
+
/**
|
|
2322
|
+
* Checks whether a caught value is a Node.js error with the given error code.
|
|
2323
|
+
*/
|
|
2324
|
+
function isNodeJsErrorWithCode$1(error, code) {
|
|
2325
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
|
|
2326
|
+
}
|
|
2279
2327
|
// Note: [🟡] Code for coder init directory creation [ensureDirectory](src/cli/cli-commands/coder/ensureDirectory.ts) should never be published outside of `@promptbook/cli`
|
|
2280
2328
|
|
|
2281
2329
|
/**
|
|
@@ -2563,11 +2611,7 @@ function $initializeAgentFolderCommand(program) {
|
|
|
2563
2611
|
$initializeAgentTickCommand(agentFolderCommand);
|
|
2564
2612
|
$initializeAgentRunCommand(agentFolderCommand);
|
|
2565
2613
|
$initializeAgentRunMultipleCommand(agentFolderCommand);
|
|
2566
|
-
agentFolderCommand
|
|
2567
|
-
console.info(colors.yellow('Please specify a subcommand.'));
|
|
2568
|
-
console.info('');
|
|
2569
|
-
agentFolderCommand.help();
|
|
2570
|
-
});
|
|
2614
|
+
$requireCliSubcommand(agentFolderCommand);
|
|
2571
2615
|
}
|
|
2572
2616
|
// Note: [🟡] Code for CLI command [agent-folder](src/cli/cli-commands/agent-folder.ts) should never be published outside of `@promptbook/cli`
|
|
2573
2617
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -21418,10 +21462,30 @@ function normalizeRelativePath$3(relativePath) {
|
|
|
21418
21462
|
return relativePath.replace(/\\/gu, '/');
|
|
21419
21463
|
}
|
|
21420
21464
|
|
|
21465
|
+
/**
|
|
21466
|
+
* AuthenticationError is thrown from login function which is dependency of remote server
|
|
21467
|
+
*
|
|
21468
|
+
* @public exported from `@promptbook/core`
|
|
21469
|
+
*/
|
|
21470
|
+
class AuthenticationError extends Error {
|
|
21471
|
+
constructor(message) {
|
|
21472
|
+
super(message);
|
|
21473
|
+
this.name = 'AuthenticationError';
|
|
21474
|
+
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
21475
|
+
}
|
|
21476
|
+
}
|
|
21477
|
+
|
|
21421
21478
|
/**
|
|
21422
21479
|
* Formats one unknown error-like value into readable text for logs and feedback.
|
|
21480
|
+
*
|
|
21481
|
+
* An `AuthenticationError` is reported without its stack, because its message is written for the user - it
|
|
21482
|
+
* says which harness has to be signed in again and how - and the frames of a failure nobody can debug would
|
|
21483
|
+
* only push those instructions out of sight.
|
|
21423
21484
|
*/
|
|
21424
21485
|
function formatUnknownErrorDetails(error) {
|
|
21486
|
+
if (error instanceof AuthenticationError) {
|
|
21487
|
+
return error.message;
|
|
21488
|
+
}
|
|
21425
21489
|
if (error instanceof Error) {
|
|
21426
21490
|
return error.stack || error.message;
|
|
21427
21491
|
}
|
|
@@ -24138,6 +24202,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24138
24202
|
commandName: 'codex',
|
|
24139
24203
|
npmPackageName: '@openai/codex',
|
|
24140
24204
|
projectGitignoreRules: ['.codex'],
|
|
24205
|
+
authenticationMethod: { command: 'codex login' },
|
|
24141
24206
|
// Note: The official standalone installer keeps its package in `~/.codex` and links it onto the `PATH`
|
|
24142
24207
|
standaloneInstallation: {
|
|
24143
24208
|
directoryNames: ['.codex'],
|
|
@@ -24150,6 +24215,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24150
24215
|
commandName: 'copilot',
|
|
24151
24216
|
npmPackageName: '@github/copilot',
|
|
24152
24217
|
projectGitignoreRules: ['.github/copilot/settings.local.json'],
|
|
24218
|
+
authenticationMethod: { command: 'copilot', interactiveStep: 'running the `/login` command inside it' },
|
|
24153
24219
|
// Note: The GitHub Copilot CLI downloads its native binary in a postinstall script
|
|
24154
24220
|
npmInstallEnvironment: { npm_config_ignore_scripts: 'false' },
|
|
24155
24221
|
},
|
|
@@ -24159,6 +24225,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24159
24225
|
commandName: 'cline',
|
|
24160
24226
|
npmPackageName: 'cline',
|
|
24161
24227
|
projectGitignoreRules: ['.cline'],
|
|
24228
|
+
authenticationMethod: { command: 'cline', interactiveStep: 'signing in to the provider inside it' },
|
|
24162
24229
|
},
|
|
24163
24230
|
'claude-code': {
|
|
24164
24231
|
harnessName: 'claude-code',
|
|
@@ -24166,6 +24233,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24166
24233
|
commandName: 'claude',
|
|
24167
24234
|
npmPackageName: '@anthropic-ai/claude-code',
|
|
24168
24235
|
projectGitignoreRules: ['.claude'],
|
|
24236
|
+
authenticationMethod: { command: 'claude', interactiveStep: 'running the `/login` command inside it' },
|
|
24169
24237
|
},
|
|
24170
24238
|
opencode: {
|
|
24171
24239
|
harnessName: 'opencode',
|
|
@@ -24173,6 +24241,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24173
24241
|
commandName: 'opencode',
|
|
24174
24242
|
npmPackageName: 'opencode-ai',
|
|
24175
24243
|
projectGitignoreRules: ['.opencode'],
|
|
24244
|
+
authenticationMethod: { command: 'opencode auth login' },
|
|
24176
24245
|
},
|
|
24177
24246
|
gemini: {
|
|
24178
24247
|
harnessName: 'gemini',
|
|
@@ -24180,6 +24249,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24180
24249
|
commandName: 'gemini',
|
|
24181
24250
|
npmPackageName: '@google/gemini-cli',
|
|
24182
24251
|
projectGitignoreRules: ['.gemini'],
|
|
24252
|
+
authenticationMethod: { command: 'gemini', interactiveStep: 'running the `/auth` command inside it' },
|
|
24183
24253
|
},
|
|
24184
24254
|
'qwen-code': {
|
|
24185
24255
|
harnessName: 'qwen-code',
|
|
@@ -24187,6 +24257,7 @@ const HARNESS_DEFINITIONS = {
|
|
|
24187
24257
|
commandName: 'qwen',
|
|
24188
24258
|
npmPackageName: '@qwen-code/qwen-code',
|
|
24189
24259
|
projectGitignoreRules: ['.qwen'],
|
|
24260
|
+
authenticationMethod: { command: 'qwen', interactiveStep: 'running the `/auth` command inside it' },
|
|
24190
24261
|
},
|
|
24191
24262
|
};
|
|
24192
24263
|
/**
|
|
@@ -26220,6 +26291,183 @@ function formatClaudeCodeSessionIdForDisplay(sessionId) {
|
|
|
26220
26291
|
return `${sessionId.slice(0, 8)}...`;
|
|
26221
26292
|
}
|
|
26222
26293
|
|
|
26294
|
+
/**
|
|
26295
|
+
* Formats one unknown error-like value into its readable message without the stack trace.
|
|
26296
|
+
*
|
|
26297
|
+
* Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
|
|
26298
|
+
* inside a branded error, where the stack of the wrapped error would only bury the actual cause.
|
|
26299
|
+
*/
|
|
26300
|
+
function formatUnknownErrorMessage(error) {
|
|
26301
|
+
if (error instanceof Error) {
|
|
26302
|
+
return error.message;
|
|
26303
|
+
}
|
|
26304
|
+
if (typeof error === 'string') {
|
|
26305
|
+
return error;
|
|
26306
|
+
}
|
|
26307
|
+
const serializedError = JSON.stringify(error, null, 2);
|
|
26308
|
+
return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
|
|
26309
|
+
}
|
|
26310
|
+
|
|
26311
|
+
/**
|
|
26312
|
+
* Builds the branded error which tells the user that the selected harness has to be signed in again.
|
|
26313
|
+
*/
|
|
26314
|
+
function buildHarnessAuthenticationError(options) {
|
|
26315
|
+
const { harnessName, reason } = options;
|
|
26316
|
+
const { label, authenticationMethod } = getHarnessDefinition(harnessName);
|
|
26317
|
+
return new AuthenticationError(spaceTrim((block) => `
|
|
26318
|
+
The **${label}** harness is not authenticated, so the prompt was not run.
|
|
26319
|
+
|
|
26320
|
+
${label} reported:
|
|
26321
|
+
> ${reason}
|
|
26322
|
+
|
|
26323
|
+
${block(buildSignInInstruction(authenticationMethod))}
|
|
26324
|
+
|
|
26325
|
+
Once you are signed in, start the very same \`ptbk coder\` command again.
|
|
26326
|
+
`));
|
|
26327
|
+
}
|
|
26328
|
+
/**
|
|
26329
|
+
* Builds the sentence which tells the user how to sign in to one harness again.
|
|
26330
|
+
*/
|
|
26331
|
+
function buildSignInInstruction(authenticationMethod) {
|
|
26332
|
+
const { command, interactiveStep } = authenticationMethod;
|
|
26333
|
+
if (interactiveStep === undefined) {
|
|
26334
|
+
return `Sign in again by running \`${command}\`.`;
|
|
26335
|
+
}
|
|
26336
|
+
return `Sign in again by running \`${command}\` and then ${interactiveStep}.`;
|
|
26337
|
+
}
|
|
26338
|
+
|
|
26339
|
+
/**
|
|
26340
|
+
* Maximum length of the harness reason quoted back to the user.
|
|
26341
|
+
*/
|
|
26342
|
+
const MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH = 300;
|
|
26343
|
+
/**
|
|
26344
|
+
* Keys of machine-readable harness output whose string value carries the human-readable failure reason.
|
|
26345
|
+
*
|
|
26346
|
+
* Harnesses which stream JSON events - like Claude Code with its `stream-json` output - report the reason only
|
|
26347
|
+
* inside such a value, surrounded by hundreds of characters of session metadata which must never be shown to
|
|
26348
|
+
* the user. The keys are ordered from the most to the least conclusive one.
|
|
26349
|
+
*/
|
|
26350
|
+
const HARNESS_FAILURE_MESSAGE_KEYS = ['result', 'error_message', 'message', 'text'];
|
|
26351
|
+
/**
|
|
26352
|
+
* Patterns which extract one JSON string value reported under a harness failure message key.
|
|
26353
|
+
*/
|
|
26354
|
+
const HARNESS_FAILURE_MESSAGE_PATTERNS = HARNESS_FAILURE_MESSAGE_KEYS.map((messageKey) => new RegExp(`"${messageKey}"\\s*:\\s*("(?:[^"\\\\]|\\\\.)*")`, 'g'));
|
|
26355
|
+
/**
|
|
26356
|
+
* Signatures which mean that the harness refused to work because it is not logged in.
|
|
26357
|
+
*
|
|
26358
|
+
* They are deliberately narrow phrases instead of single words like `login`, because the output of a harness
|
|
26359
|
+
* routinely mentions its own `/login` command among the commands it offers, and such a mention must never be
|
|
26360
|
+
* read as a failed login.
|
|
26361
|
+
*/
|
|
26362
|
+
const AUTHENTICATION_FAILURE_PATTERNS = [
|
|
26363
|
+
/\bauthentication[ _-]?failed\b/i,
|
|
26364
|
+
/\bfailed to authenticate\b/i,
|
|
26365
|
+
/\bnot authenticated\b/i,
|
|
26366
|
+
/\bnot logged[ -]?in\b/i,
|
|
26367
|
+
/\bnot signed[ -]?in\b/i,
|
|
26368
|
+
/\bno authentication information found\b/i,
|
|
26369
|
+
/\bauthentication (?:is )?required\b/i,
|
|
26370
|
+
/\blogin (?:is )?required\b/i,
|
|
26371
|
+
/\bplease (?:log|sign) in\b/i,
|
|
26372
|
+
/\bunauthorized\b/i,
|
|
26373
|
+
/\b(?:oauth|session|token|credentials?|login)\b[^.]{0,60}?\bexpired\b/i,
|
|
26374
|
+
/\bexpired\b[^.]{0,60}?\b(?:oauth|session|token|credentials?|login)\b/i,
|
|
26375
|
+
/\binvalid api[ _-]?key\b/i,
|
|
26376
|
+
/\bmissing api[ _-]?key\b/i,
|
|
26377
|
+
/\binvalid credentials?\b/i,
|
|
26378
|
+
];
|
|
26379
|
+
/**
|
|
26380
|
+
* Reads the reason why a harness refused to work because it is not logged in, from its raw CLI output.
|
|
26381
|
+
*
|
|
26382
|
+
* Messages the harness reports in machine-readable output are preferred over its raw output lines, so the
|
|
26383
|
+
* reason is the sentence the harness wrote for a human and not the JSON event which transported it.
|
|
26384
|
+
*
|
|
26385
|
+
* @returns One short single-line reason, or `undefined` when the failure was not caused by authentication.
|
|
26386
|
+
*/
|
|
26387
|
+
function extractHarnessAuthenticationFailureReason(harnessOutput) {
|
|
26388
|
+
const reportedMessages = [...extractHarnessReportedMessages(harnessOutput), ...harnessOutput.split(/\r?\n/)];
|
|
26389
|
+
for (const reportedMessage of reportedMessages) {
|
|
26390
|
+
const reason = reportedMessage.replace(/\s+/g, ' ').trim();
|
|
26391
|
+
if (isAuthenticationFailureReason(reason)) {
|
|
26392
|
+
return limitAuthenticationFailureReason(reason);
|
|
26393
|
+
}
|
|
26394
|
+
}
|
|
26395
|
+
return undefined;
|
|
26396
|
+
}
|
|
26397
|
+
/**
|
|
26398
|
+
* Lists the human-readable messages a harness reported in its machine-readable output.
|
|
26399
|
+
*/
|
|
26400
|
+
function extractHarnessReportedMessages(harnessOutput) {
|
|
26401
|
+
const reportedMessages = [];
|
|
26402
|
+
for (const messagePattern of HARNESS_FAILURE_MESSAGE_PATTERNS) {
|
|
26403
|
+
for (const match of harnessOutput.matchAll(messagePattern)) {
|
|
26404
|
+
const reportedMessage = parseSerializedJsonString(match[1]);
|
|
26405
|
+
if (reportedMessage !== undefined) {
|
|
26406
|
+
reportedMessages.push(reportedMessage);
|
|
26407
|
+
}
|
|
26408
|
+
}
|
|
26409
|
+
}
|
|
26410
|
+
return reportedMessages;
|
|
26411
|
+
}
|
|
26412
|
+
/**
|
|
26413
|
+
* Reads one JSON string literal back into plain text, tolerating output which only looks like JSON.
|
|
26414
|
+
*/
|
|
26415
|
+
function parseSerializedJsonString(serializedString) {
|
|
26416
|
+
if (serializedString === undefined) {
|
|
26417
|
+
return undefined;
|
|
26418
|
+
}
|
|
26419
|
+
try {
|
|
26420
|
+
const parsedString = JSON.parse(serializedString);
|
|
26421
|
+
return typeof parsedString === 'string' ? parsedString : undefined;
|
|
26422
|
+
}
|
|
26423
|
+
catch (_a) {
|
|
26424
|
+
return undefined;
|
|
26425
|
+
}
|
|
26426
|
+
}
|
|
26427
|
+
/**
|
|
26428
|
+
* Returns true when one message of the harness says that it is not logged in.
|
|
26429
|
+
*/
|
|
26430
|
+
function isAuthenticationFailureReason(reportedMessage) {
|
|
26431
|
+
return AUTHENTICATION_FAILURE_PATTERNS.some((pattern) => pattern.test(reportedMessage));
|
|
26432
|
+
}
|
|
26433
|
+
/**
|
|
26434
|
+
* Shortens a very long reason, so the guidance which follows it stays visible in the terminal.
|
|
26435
|
+
*/
|
|
26436
|
+
function limitAuthenticationFailureReason(reason) {
|
|
26437
|
+
if (reason.length <= MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH) {
|
|
26438
|
+
return reason;
|
|
26439
|
+
}
|
|
26440
|
+
return `${reason.slice(0, MAXIMUM_AUTHENTICATION_FAILURE_REASON_LENGTH)}...`;
|
|
26441
|
+
}
|
|
26442
|
+
|
|
26443
|
+
/**
|
|
26444
|
+
* Wraps one prompt runner so a failure caused by a missing or expired harness login is reported as a branded
|
|
26445
|
+
* `AuthenticationError` which tells the user how to sign in again.
|
|
26446
|
+
*
|
|
26447
|
+
* Each harness reports its own failures differently, but all of them report them by failing `runPrompt` with
|
|
26448
|
+
* the raw CLI output as the message. Translating the failure here therefore keeps exactly one place which has
|
|
26449
|
+
* to recognize an unauthenticated harness, for every harness and for every command which runs prompts.
|
|
26450
|
+
*/
|
|
26451
|
+
function createAuthenticationAwarePromptRunner(runner, harnessName) {
|
|
26452
|
+
var _a;
|
|
26453
|
+
return {
|
|
26454
|
+
name: runner.name,
|
|
26455
|
+
runPrompt: async (options) => {
|
|
26456
|
+
try {
|
|
26457
|
+
return await runner.runPrompt(options);
|
|
26458
|
+
}
|
|
26459
|
+
catch (error) {
|
|
26460
|
+
const reason = extractHarnessAuthenticationFailureReason(formatUnknownErrorMessage(error));
|
|
26461
|
+
if (reason === undefined) {
|
|
26462
|
+
throw error;
|
|
26463
|
+
}
|
|
26464
|
+
throw buildHarnessAuthenticationError({ harnessName, reason });
|
|
26465
|
+
}
|
|
26466
|
+
},
|
|
26467
|
+
getSubscriptionUsage: (_a = runner.getSubscriptionUsage) === null || _a === void 0 ? void 0 : _a.bind(runner),
|
|
26468
|
+
};
|
|
26469
|
+
}
|
|
26470
|
+
|
|
26223
26471
|
/**
|
|
26224
26472
|
* Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
|
|
26225
26473
|
*/
|
|
@@ -27959,6 +28207,18 @@ function resolvePromptRunner(options) {
|
|
|
27959
28207
|
if (!agentName) {
|
|
27960
28208
|
throw new Error('Missing --harness in non-dry run mode');
|
|
27961
28209
|
}
|
|
28210
|
+
const resolution = resolveHarnessPromptRunner(agentName, options);
|
|
28211
|
+
return {
|
|
28212
|
+
...resolution,
|
|
28213
|
+
// Note: Every harness is wrapped here, so a harness which is not logged in is reported the same clear
|
|
28214
|
+
// way no matter which harness it is and which command has resolved the runner
|
|
28215
|
+
runner: createAuthenticationAwarePromptRunner(resolution.runner, agentName),
|
|
28216
|
+
};
|
|
28217
|
+
}
|
|
28218
|
+
/**
|
|
28219
|
+
* Creates the runner of one selected harness together with its status-line metadata.
|
|
28220
|
+
*/
|
|
28221
|
+
function resolveHarnessPromptRunner(agentName, options) {
|
|
27962
28222
|
if (agentName === 'openai-codex') {
|
|
27963
28223
|
return createOpenAiCodexRunnerResolution(options);
|
|
27964
28224
|
}
|
|
@@ -28426,11 +28686,19 @@ const MIN_FRAME_WIDTH = 56;
|
|
|
28426
28686
|
* Maximum width used for the rich coder-run frame.
|
|
28427
28687
|
*/
|
|
28428
28688
|
const MAX_FRAME_WIDTH = 96;
|
|
28689
|
+
/**
|
|
28690
|
+
* Number of terminal columns deliberately left unused by the rich coder-run frame.
|
|
28691
|
+
*
|
|
28692
|
+
* A row which fills the final physical terminal column can auto-wrap. The incremental terminal
|
|
28693
|
+
* updater moves by logical rows, so an auto-wrapped row would put its cursor out of sync with the
|
|
28694
|
+
* frame and let a later animated avatar redraw overwrite dashboard content.
|
|
28695
|
+
*/
|
|
28696
|
+
const TERMINAL_AUTO_WRAP_RESERVED_COLUMNS = 1;
|
|
28429
28697
|
/**
|
|
28430
28698
|
* Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
|
|
28431
28699
|
*/
|
|
28432
28700
|
function buildCoderRunUiFrame(options) {
|
|
28433
|
-
const totalWidth =
|
|
28701
|
+
const totalWidth = resolveCoderRunUiFrameWidth(options.terminalWidth);
|
|
28434
28702
|
const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
|
|
28435
28703
|
const promptStatusPrefix = isPromptActive ? `${colors.yellow(`${options.spinner} `)}` : '';
|
|
28436
28704
|
const pausePresentation = buildPausePresentation(options.phase, options.pauseState, options.pauseTargetLabel, options.statusMessage);
|
|
@@ -28467,6 +28735,16 @@ function buildCoderRunUiFrame(options) {
|
|
|
28467
28735
|
frame.push(...renderBox('Controls', controlsBoxLines, totalWidth, colors.white.bold));
|
|
28468
28736
|
return frame;
|
|
28469
28737
|
}
|
|
28738
|
+
/**
|
|
28739
|
+
* Resolves the safe width of the rich terminal frame from the available physical terminal columns.
|
|
28740
|
+
*
|
|
28741
|
+
* The reserved trailing column keeps every boxed row from auto-wrapping, which makes the frame's
|
|
28742
|
+
* logical row count match its physical terminal row count during animated incremental redraws.
|
|
28743
|
+
*/
|
|
28744
|
+
function resolveCoderRunUiFrameWidth(terminalWidth) {
|
|
28745
|
+
const availableFrameWidth = terminalWidth - TERMINAL_AUTO_WRAP_RESERVED_COLUMNS;
|
|
28746
|
+
return Math.max(MIN_FRAME_WIDTH, Math.min(availableFrameWidth, MAX_FRAME_WIDTH));
|
|
28747
|
+
}
|
|
28470
28748
|
/**
|
|
28471
28749
|
* Builds the header visual above the dashboard boxes.
|
|
28472
28750
|
*
|
|
@@ -29943,16 +30221,14 @@ const SPINNER_FRAMES = [
|
|
|
29943
30221
|
*/
|
|
29944
30222
|
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
29945
30223
|
/**
|
|
29946
|
-
*
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
/**
|
|
29950
|
-
* Returns the usable terminal width, capped at 96.
|
|
30224
|
+
* Returns the number of columns reported by the current terminal.
|
|
30225
|
+
*
|
|
30226
|
+
* The frame builder owns clamping and reserves its trailing auto-wrap safety column.
|
|
29951
30227
|
*
|
|
29952
30228
|
* @private internal utility of coder run UI
|
|
29953
30229
|
*/
|
|
29954
|
-
function
|
|
29955
|
-
return
|
|
30230
|
+
function getTerminalColumnCount() {
|
|
30231
|
+
return process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
|
|
29956
30232
|
}
|
|
29957
30233
|
/**
|
|
29958
30234
|
* Boots the ANSI terminal UI for `ptbk coder run`.
|
|
@@ -30059,7 +30335,7 @@ function renderCoderRunUi(startTime, options = {}) {
|
|
|
30059
30335
|
*/
|
|
30060
30336
|
function buildFrameLines() {
|
|
30061
30337
|
return buildFrameLinesFromState({
|
|
30062
|
-
terminalWidth:
|
|
30338
|
+
terminalWidth: getTerminalColumnCount(),
|
|
30063
30339
|
animationFrame: spinnerFrame,
|
|
30064
30340
|
animationTimeMs: Date.now() - animationStartTimeMs,
|
|
30065
30341
|
spinner: SPINNER_FRAMES[spinnerFrame],
|
|
@@ -33790,19 +34066,6 @@ class CsvFormatError extends AbstractFormatError {
|
|
|
33790
34066
|
}
|
|
33791
34067
|
}
|
|
33792
34068
|
|
|
33793
|
-
/**
|
|
33794
|
-
* AuthenticationError is thrown from login function which is dependency of remote server
|
|
33795
|
-
*
|
|
33796
|
-
* @public exported from `@promptbook/core`
|
|
33797
|
-
*/
|
|
33798
|
-
class AuthenticationError extends Error {
|
|
33799
|
-
constructor(message) {
|
|
33800
|
-
super(message);
|
|
33801
|
-
this.name = 'AuthenticationError';
|
|
33802
|
-
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
33803
|
-
}
|
|
33804
|
-
}
|
|
33805
|
-
|
|
33806
34069
|
/**
|
|
33807
34070
|
* This error indicates that the pipeline collection cannot be properly loaded
|
|
33808
34071
|
*
|
|
@@ -42031,11 +42294,7 @@ function $initializeAgentsServerCommand(program) {
|
|
|
42031
42294
|
$initializeAgentsServerDevCommand(agentsServerCommand);
|
|
42032
42295
|
$initializeAgentsServerInitCommand(agentsServerCommand);
|
|
42033
42296
|
$initializeAgentsServerStartCommand(agentsServerCommand);
|
|
42034
|
-
agentsServerCommand
|
|
42035
|
-
console.info(colors.yellow('Please specify a subcommand.'));
|
|
42036
|
-
console.info('');
|
|
42037
|
-
agentsServerCommand.help();
|
|
42038
|
-
});
|
|
42297
|
+
$requireCliSubcommand(agentsServerCommand);
|
|
42039
42298
|
}
|
|
42040
42299
|
// Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
|
|
42041
42300
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -42125,24 +42384,82 @@ function normalizeCoderGitSyncCliOptions(cliOptions) {
|
|
|
42125
42384
|
// Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
42126
42385
|
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
42127
42386
|
|
|
42387
|
+
/**
|
|
42388
|
+
* Description block shared by the `ptbk coder` commands which can ask interactive questions.
|
|
42389
|
+
*
|
|
42390
|
+
* @private internal utility of `promptbookCli`
|
|
42391
|
+
*/
|
|
42392
|
+
const QUESTIONS_DESCRIPTION = spaceTrim$1(`
|
|
42393
|
+
Interactive questions:
|
|
42394
|
+
- Steps which need an answer to continue are asked in the terminal
|
|
42395
|
+
- --no-questions never asks: an optional step is skipped with a note how to do it manually, a required one fails right away
|
|
42396
|
+
`);
|
|
42397
|
+
/**
|
|
42398
|
+
* Registers the shared `--no-questions` option on a `ptbk coder` command which can ask interactive questions.
|
|
42399
|
+
*
|
|
42400
|
+
* @private internal utility of `promptbookCli`
|
|
42401
|
+
*/
|
|
42402
|
+
function addQuestionsOption(command) {
|
|
42403
|
+
command.option('--no-questions', 'Never ask an interactive question and skip every optional step which needs an answer to continue');
|
|
42404
|
+
}
|
|
42405
|
+
/**
|
|
42406
|
+
* Converts the Commander questions flag into the normalized interactive-questions option.
|
|
42407
|
+
*
|
|
42408
|
+
* @private internal utility of `promptbookCli`
|
|
42409
|
+
*/
|
|
42410
|
+
function normalizeQuestionsCliOptions(cliOptions) {
|
|
42411
|
+
return {
|
|
42412
|
+
isAskingQuestionsEnabled: cliOptions.questions,
|
|
42413
|
+
};
|
|
42414
|
+
}
|
|
42415
|
+
/**
|
|
42416
|
+
* Asserts that a command which waits for a user confirmation before each prompt is allowed to ask for it.
|
|
42417
|
+
*
|
|
42418
|
+
* @throws {NotAllowed} when `--no-auto` is combined with `--no-questions`
|
|
42419
|
+
* @private internal utility of `promptbookCli`
|
|
42420
|
+
*/
|
|
42421
|
+
function assertUserConfirmationIsAllowed(options) {
|
|
42422
|
+
const { isAskingQuestionsEnabled, isWaitingForUser } = options;
|
|
42423
|
+
if (!isWaitingForUser || isAskingQuestionsEnabled) {
|
|
42424
|
+
return;
|
|
42425
|
+
}
|
|
42426
|
+
throw new NotAllowed(spaceTrim$1(`
|
|
42427
|
+
Flag \`--no-auto\` can not be used together with \`--no-questions\`.
|
|
42428
|
+
|
|
42429
|
+
**There is nobody who could confirm each prompt when no question may be asked.**
|
|
42430
|
+
|
|
42431
|
+
Actionable hints:
|
|
42432
|
+
- Keep \`--no-auto\` for a supervised run, for example \`ptbk coder run --harness claude-code --no-auto\`.
|
|
42433
|
+
- Keep \`--no-questions\` for an unattended run, for example \`ptbk coder run --harness claude-code --no-questions\`.
|
|
42434
|
+
`));
|
|
42435
|
+
}
|
|
42436
|
+
// Note: [🟡] Code for CLI interactive questions options [questionsCliOptions](src/cli/cli-commands/common/questionsCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
42437
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
42438
|
+
|
|
42128
42439
|
/**
|
|
42129
42440
|
* Relative path to the root prompts directory used by Promptbook coder utilities.
|
|
42130
42441
|
*
|
|
42131
42442
|
* @private internal utility of `ptbk coder`
|
|
42132
42443
|
*/
|
|
42133
|
-
const PROMPTS_DIRECTORY_PATH = 'prompts';
|
|
42444
|
+
const PROMPTS_DIRECTORY_PATH$1 = 'prompts';
|
|
42134
42445
|
/**
|
|
42135
42446
|
* Relative path to the archive directory used by `coder verify`.
|
|
42136
42447
|
*
|
|
42137
42448
|
* @private internal utility of `ptbk coder`
|
|
42138
42449
|
*/
|
|
42139
|
-
const PROMPTS_DONE_DIRECTORY_PATH = join(PROMPTS_DIRECTORY_PATH, 'done');
|
|
42450
|
+
const PROMPTS_DONE_DIRECTORY_PATH = join(PROMPTS_DIRECTORY_PATH$1, 'done');
|
|
42140
42451
|
/**
|
|
42141
42452
|
* Relative path to the project-owned boilerplate templates directory.
|
|
42142
42453
|
*
|
|
42143
42454
|
* @private internal utility of `ptbk coder`
|
|
42144
42455
|
*/
|
|
42145
|
-
const PROMPTS_TEMPLATES_DIRECTORY_PATH = join(PROMPTS_DIRECTORY_PATH, 'templates');
|
|
42456
|
+
const PROMPTS_TEMPLATES_DIRECTORY_PATH = join(PROMPTS_DIRECTORY_PATH$1, 'templates');
|
|
42457
|
+
/**
|
|
42458
|
+
* Relative path to the project-agnostic `common` boilerplate template referenced by the default coder scripts.
|
|
42459
|
+
*
|
|
42460
|
+
* @private internal utility of `ptbk coder`
|
|
42461
|
+
*/
|
|
42462
|
+
const COMMON_PROMPT_TEMPLATE_FILE_PATH = join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'common.md');
|
|
42146
42463
|
/**
|
|
42147
42464
|
* Built-in boilerplate templates available to `coder generate-boilerplates`.
|
|
42148
42465
|
*
|
|
@@ -42151,7 +42468,7 @@ const PROMPTS_TEMPLATES_DIRECTORY_PATH = join(PROMPTS_DIRECTORY_PATH, 'templates
|
|
|
42151
42468
|
const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
|
|
42152
42469
|
{
|
|
42153
42470
|
id: 'common',
|
|
42154
|
-
relativeFilePath:
|
|
42471
|
+
relativeFilePath: COMMON_PROMPT_TEMPLATE_FILE_PATH,
|
|
42155
42472
|
slugPrefix: null,
|
|
42156
42473
|
content: spaceTrim$1(`
|
|
42157
42474
|
- @@@
|
|
@@ -42197,30 +42514,17 @@ function getDefaultCoderPromptTemplateDefinition(template) {
|
|
|
42197
42514
|
return definition;
|
|
42198
42515
|
}
|
|
42199
42516
|
/**
|
|
42200
|
-
* Ensures
|
|
42517
|
+
* Ensures one project-owned coder template file exists without overwriting user customizations.
|
|
42201
42518
|
*
|
|
42202
42519
|
* @private internal utility of `ptbk coder`
|
|
42203
42520
|
*/
|
|
42204
|
-
async function
|
|
42205
|
-
const
|
|
42206
|
-
|
|
42207
|
-
|
|
42208
|
-
if (await isExistingFile$3(absoluteTemplatePath)) {
|
|
42209
|
-
ensuredTemplateFiles.push({
|
|
42210
|
-
id: definition.id,
|
|
42211
|
-
relativeFilePath: definition.relativeFilePath,
|
|
42212
|
-
status: 'unchanged',
|
|
42213
|
-
});
|
|
42214
|
-
continue;
|
|
42215
|
-
}
|
|
42216
|
-
await writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
|
|
42217
|
-
ensuredTemplateFiles.push({
|
|
42218
|
-
id: definition.id,
|
|
42219
|
-
relativeFilePath: definition.relativeFilePath,
|
|
42220
|
-
status: 'created',
|
|
42221
|
-
});
|
|
42521
|
+
async function ensureDefaultCoderPromptTemplateFile(projectPath, definition) {
|
|
42522
|
+
const absoluteTemplatePath = join(projectPath, definition.relativeFilePath);
|
|
42523
|
+
if (await isExistingFile$3(absoluteTemplatePath)) {
|
|
42524
|
+
return 'unchanged';
|
|
42222
42525
|
}
|
|
42223
|
-
|
|
42526
|
+
await writeFile(absoluteTemplatePath, `${definition.content}\n`, 'utf-8');
|
|
42527
|
+
return 'created';
|
|
42224
42528
|
}
|
|
42225
42529
|
/**
|
|
42226
42530
|
* Resolves the template requested by `coder generate-boilerplates`.
|
|
@@ -42376,6 +42680,8 @@ function $initializeCoderAddCommand(program) {
|
|
|
42376
42680
|
- \`ptbk coder add\`
|
|
42377
42681
|
|
|
42378
42682
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
42683
|
+
|
|
42684
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
42379
42685
|
`));
|
|
42380
42686
|
command.argument('[description]', 'Plain-language description of the feature or task to implement');
|
|
42381
42687
|
command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
|
|
@@ -42387,11 +42693,13 @@ function $initializeCoderAddCommand(program) {
|
|
|
42387
42693
|
.join(', ')}) or a markdown file path relative to the current project root.
|
|
42388
42694
|
`));
|
|
42389
42695
|
addCoderGitSyncOptions(command);
|
|
42696
|
+
addQuestionsOption(command);
|
|
42390
42697
|
command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
|
|
42391
42698
|
const { priority, template: templateOption } = cliOptions;
|
|
42392
42699
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
42700
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
42393
42701
|
const projectPath = process.cwd();
|
|
42394
|
-
const description = await resolveCoderPromptDescription(descriptionArgument);
|
|
42702
|
+
const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
|
|
42395
42703
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
42396
42704
|
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
42397
42705
|
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
@@ -42426,7 +42734,7 @@ async function addCoderPrompt({ projectPath, description, priority, templateOpti
|
|
|
42426
42734
|
// Note: Import these dynamically to avoid circular dependencies and keep the CLI fast
|
|
42427
42735
|
const { buildPromptFilename, getPromptNumbering } = await Promise.resolve().then(function () { return getPromptNumbering$1; });
|
|
42428
42736
|
const { formatPromptEmojiTag, getFreshPromptEmojiTags } = await Promise.resolve().then(function () { return promptEmojiTags; });
|
|
42429
|
-
const promptsDirectory = join(projectPath, PROMPTS_DIRECTORY_PATH);
|
|
42737
|
+
const promptsDirectory = join(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
42430
42738
|
mkdirSync(promptsDirectory, { recursive: true });
|
|
42431
42739
|
const promptTemplate = await resolveCoderPromptTemplate({ projectPath, templateOption });
|
|
42432
42740
|
const promptNumbering = await getPromptNumbering({
|
|
@@ -42450,7 +42758,7 @@ async function addCoderPrompt({ projectPath, description, priority, templateOpti
|
|
|
42450
42758
|
});
|
|
42451
42759
|
const slug = buildAddPromptSlug(promptTemplate.slugPrefix, title);
|
|
42452
42760
|
const filename = buildPromptFilename(promptNumbering.datePrefix, promptNumbering.startNumber, slug);
|
|
42453
|
-
const filePath = join(PROMPTS_DIRECTORY_PATH, filename);
|
|
42761
|
+
const filePath = join(PROMPTS_DIRECTORY_PATH$1, filename);
|
|
42454
42762
|
const absoluteFilePath = join(projectPath, filePath);
|
|
42455
42763
|
writeFileSync(absoluteFilePath, `${section}\n`, 'utf-8');
|
|
42456
42764
|
console.info(colors.green(`✓ Added prompt ${emojiTag} in ${filePath}`));
|
|
@@ -42461,7 +42769,7 @@ async function addCoderPrompt({ projectPath, description, priority, templateOpti
|
|
|
42461
42769
|
*
|
|
42462
42770
|
* @private internal utility of `coder add` command
|
|
42463
42771
|
*/
|
|
42464
|
-
async function resolveCoderPromptDescription(descriptionArgument) {
|
|
42772
|
+
async function resolveCoderPromptDescription(descriptionArgument, { isAskingQuestionsEnabled }) {
|
|
42465
42773
|
if (descriptionArgument !== undefined && descriptionArgument.trim() !== '') {
|
|
42466
42774
|
return descriptionArgument.trim();
|
|
42467
42775
|
}
|
|
@@ -42479,6 +42787,14 @@ async function resolveCoderPromptDescription(descriptionArgument) {
|
|
|
42479
42787
|
}
|
|
42480
42788
|
return standardInputDescription;
|
|
42481
42789
|
}
|
|
42790
|
+
if (!isAskingQuestionsEnabled) {
|
|
42791
|
+
throw new ParseError(spaceTrim$1(`
|
|
42792
|
+
Cannot add a prompt without a description.
|
|
42793
|
+
|
|
42794
|
+
The description can not be typed interactively because \`--no-questions\` is used — provide it as an argument or pipe it through stdin instead:
|
|
42795
|
+
- \`ptbk coder add --no-questions "some new feature"\`
|
|
42796
|
+
`));
|
|
42797
|
+
}
|
|
42482
42798
|
const { default: prompts } = await loadPromptsModule();
|
|
42483
42799
|
const response = await prompts({
|
|
42484
42800
|
type: 'text',
|
|
@@ -43042,10 +43358,10 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
43042
43358
|
const { buildPromptFilename, getPromptNumbering } = await Promise.resolve().then(function () { return getPromptNumbering$1; });
|
|
43043
43359
|
const { formatPromptEmojiTag, getFreshPromptEmojiTags } = await Promise.resolve().then(function () { return promptEmojiTags; });
|
|
43044
43360
|
console.info(`🚀 Generate prompt boilerplate files`);
|
|
43045
|
-
mkdirSync(join(projectPath, PROMPTS_DIRECTORY_PATH), { recursive: true });
|
|
43361
|
+
mkdirSync(join(projectPath, PROMPTS_DIRECTORY_PATH$1), { recursive: true });
|
|
43046
43362
|
const promptTemplate = await resolveCoderPromptTemplate({ projectPath, templateOption });
|
|
43047
43363
|
const promptNumbering = await getPromptNumbering({
|
|
43048
|
-
promptsDir: join(projectPath, PROMPTS_DIRECTORY_PATH),
|
|
43364
|
+
promptsDir: join(projectPath, PROMPTS_DIRECTORY_PATH$1),
|
|
43049
43365
|
step: 10,
|
|
43050
43366
|
ignoreGlobs: ['**/node_modules/**'],
|
|
43051
43367
|
});
|
|
@@ -43069,7 +43385,7 @@ async function generatePromptBoilerplate({ projectPath, boilerplateCount, templa
|
|
|
43069
43385
|
const title = titles[i % titles.length];
|
|
43070
43386
|
const emojiTag = formatPromptEmojiTag(selectedEmojis[i]);
|
|
43071
43387
|
const filename = buildPromptFilename(promptNumbering.datePrefix, number, buildPromptSlug$1(promptTemplate.slugPrefix, title));
|
|
43072
|
-
const filepath = join(PROMPTS_DIRECTORY_PATH, filename);
|
|
43388
|
+
const filepath = join(PROMPTS_DIRECTORY_PATH$1, filename);
|
|
43073
43389
|
const absoluteFilepath = join(projectPath, filepath);
|
|
43074
43390
|
const content = buildBoilerplatePromptFileContent({
|
|
43075
43391
|
emojiTag,
|
|
@@ -43129,10 +43445,15 @@ function buildPromptSlug$1(templateSlugPrefix, title) {
|
|
|
43129
43445
|
*
|
|
43130
43446
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin.
|
|
43131
43447
|
*
|
|
43132
|
-
* @returns `true` when the user confirms, `false` when the user declines
|
|
43448
|
+
* @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
|
|
43449
|
+
* or the questions are disabled by `--no-questions`.
|
|
43133
43450
|
* @private internal utility of `promptbookCli`
|
|
43134
43451
|
*/
|
|
43135
|
-
async function $askForConfirmation(question) {
|
|
43452
|
+
async function $askForConfirmation(question, { isAskingQuestionsEnabled }) {
|
|
43453
|
+
if (!isAskingQuestionsEnabled) {
|
|
43454
|
+
// Note: `--no-questions` forbids asking, so the optional change is declined without blocking the run.
|
|
43455
|
+
return false;
|
|
43456
|
+
}
|
|
43136
43457
|
if (!process.stdin.isTTY) {
|
|
43137
43458
|
// Note: In non-interactive environments like CI there is nobody who could confirm the change.
|
|
43138
43459
|
return false;
|
|
@@ -43155,11 +43476,12 @@ async function $askForConfirmation(question) {
|
|
|
43155
43476
|
*
|
|
43156
43477
|
* Note: `$` is used to indicate that this function is not a pure function - it reads the answer from stdin
|
|
43157
43478
|
*
|
|
43158
|
-
* @returns `true` when the user confirms, `false` when the user declines
|
|
43479
|
+
* @returns `true` when the user confirms, `false` when the user declines, the terminal is not interactive
|
|
43480
|
+
* or the questions are disabled by `--no-questions`
|
|
43159
43481
|
* @private internal utility of `promptbookCli`
|
|
43160
43482
|
*/
|
|
43161
|
-
async function $askForNpmPackageInstallationApproval(question) {
|
|
43162
|
-
return $askForConfirmation(question);
|
|
43483
|
+
async function $askForNpmPackageInstallationApproval(question, questionsOptions) {
|
|
43484
|
+
return $askForConfirmation(question, questionsOptions);
|
|
43163
43485
|
}
|
|
43164
43486
|
// 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`
|
|
43165
43487
|
|
|
@@ -43349,7 +43671,7 @@ function resolveHarnessUpdatePlan(definition, installationMethod) {
|
|
|
43349
43671
|
*
|
|
43350
43672
|
* @private internal utility of `promptbookCli`
|
|
43351
43673
|
*/
|
|
43352
|
-
async function $applyHarnessInstallationStatus(status) {
|
|
43674
|
+
async function $applyHarnessInstallationStatus(status, questionsOptions) {
|
|
43353
43675
|
const { definition, installationState, installedVersion } = status;
|
|
43354
43676
|
if (installationState === 'up-to-date') {
|
|
43355
43677
|
console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
|
|
@@ -43365,17 +43687,17 @@ async function $applyHarnessInstallationStatus(status) {
|
|
|
43365
43687
|
}
|
|
43366
43688
|
console.warn(colors.yellow(formatHarnessInstallationWarning(status)));
|
|
43367
43689
|
if (installationState === 'not-installed') {
|
|
43368
|
-
await $applyMissingHarnessInstallation(definition);
|
|
43690
|
+
await $applyMissingHarnessInstallation(definition, questionsOptions);
|
|
43369
43691
|
return;
|
|
43370
43692
|
}
|
|
43371
|
-
await $applyOutdatedHarnessInstallation(status);
|
|
43693
|
+
await $applyOutdatedHarnessInstallation(status, questionsOptions);
|
|
43372
43694
|
}
|
|
43373
43695
|
/**
|
|
43374
43696
|
* Offers to install a missing harness globally through npm.
|
|
43375
43697
|
*/
|
|
43376
|
-
async function $applyMissingHarnessInstallation(definition) {
|
|
43698
|
+
async function $applyMissingHarnessInstallation(definition, questionsOptions) {
|
|
43377
43699
|
const installCommand = buildHarnessInstallCommand(definition);
|
|
43378
|
-
const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now
|
|
43700
|
+
const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`, questionsOptions);
|
|
43379
43701
|
if (!isInstallationApproved) {
|
|
43380
43702
|
console.info(colors.gray(`Skipped, run \`${installCommand}\` to do it manually.`));
|
|
43381
43703
|
return;
|
|
@@ -43390,14 +43712,14 @@ async function $applyMissingHarnessInstallation(definition) {
|
|
|
43390
43712
|
/**
|
|
43391
43713
|
* Offers to update an outdated harness exactly where it is installed.
|
|
43392
43714
|
*/
|
|
43393
|
-
async function $applyOutdatedHarnessInstallation(status) {
|
|
43715
|
+
async function $applyOutdatedHarnessInstallation(status, questionsOptions) {
|
|
43394
43716
|
const { definition, latestVersion, installationOrigin } = status;
|
|
43395
43717
|
const updatePlan = resolveHarnessUpdatePlan(definition, installationOrigin.installationMethod);
|
|
43396
43718
|
if (updatePlan.command === null || !updatePlan.isRunnableByPromptbook) {
|
|
43397
43719
|
console.info(colors.gray(formatHarnessManualUpdateInstruction(status, updatePlan)));
|
|
43398
43720
|
return;
|
|
43399
43721
|
}
|
|
43400
|
-
const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now
|
|
43722
|
+
const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`, questionsOptions);
|
|
43401
43723
|
if (!isUpdateApproved) {
|
|
43402
43724
|
console.info(colors.gray(`Skipped, run \`${updatePlan.command}\` to do it manually.`));
|
|
43403
43725
|
return;
|
|
@@ -43833,7 +44155,7 @@ function resolveHarnessInstallationState(installedVersion, latestVersion, isHarn
|
|
|
43833
44155
|
// Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
|
|
43834
44156
|
|
|
43835
44157
|
/**
|
|
43836
|
-
* Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
|
|
44158
|
+
* Checks that every given CLI coding harness is installed globally and, when the questions are enabled, up to date.
|
|
43837
44159
|
* Offers to install missing harnesses and update outdated ones.
|
|
43838
44160
|
*
|
|
43839
44161
|
* Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
|
|
@@ -43843,15 +44165,18 @@ function resolveHarnessInstallationState(installedVersion, latestVersion, isHarn
|
|
|
43843
44165
|
*
|
|
43844
44166
|
* @private internal utility of `promptbookCli`
|
|
43845
44167
|
*/
|
|
43846
|
-
async function $ensureHarnessInstallations(harnessNames,
|
|
44168
|
+
async function $ensureHarnessInstallations(harnessNames, questionsOptions) {
|
|
43847
44169
|
const definitions = resolveCheckedHarnessDefinitions(harnessNames);
|
|
43848
44170
|
if (definitions.length === 0) {
|
|
43849
44171
|
return;
|
|
43850
44172
|
}
|
|
44173
|
+
// Note: An outdated harness can be updated only after the user confirms it, so `--no-questions`
|
|
44174
|
+
// skips the slower npm registry lookup entirely and only reports what is installed.
|
|
44175
|
+
const isHarnessUpdateCheckEnabled = questionsOptions.isAskingQuestionsEnabled;
|
|
43851
44176
|
// Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
|
|
43852
44177
|
const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)));
|
|
43853
44178
|
for (const status of statuses) {
|
|
43854
|
-
await $applyHarnessInstallationStatus(status);
|
|
44179
|
+
await $applyHarnessInstallationStatus(status, questionsOptions);
|
|
43855
44180
|
}
|
|
43856
44181
|
}
|
|
43857
44182
|
/**
|
|
@@ -43863,48 +44188,6 @@ function resolveCheckedHarnessDefinitions(harnessNames) {
|
|
|
43863
44188
|
}
|
|
43864
44189
|
// Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
|
|
43865
44190
|
|
|
43866
|
-
/**
|
|
43867
|
-
* Registers the shared `--no-harness-update` option on a `ptbk coder` command that uses a coding harness.
|
|
43868
|
-
*
|
|
43869
|
-
* @private internal utility of `promptbookCli`
|
|
43870
|
-
*/
|
|
43871
|
-
function addHarnessUpdateOption(command) {
|
|
43872
|
-
command.option('--no-harness-update', 'Skip checking whether the installed coding harnesses are up to date');
|
|
43873
|
-
}
|
|
43874
|
-
/**
|
|
43875
|
-
* Converts the Commander harness-update flag into the normalized harness-check option.
|
|
43876
|
-
*
|
|
43877
|
-
* @private internal utility of `promptbookCli`
|
|
43878
|
-
*/
|
|
43879
|
-
function normalizeHarnessUpdateCliOptions(cliOptions) {
|
|
43880
|
-
return {
|
|
43881
|
-
isHarnessUpdateCheckEnabled: cliOptions.harnessUpdate,
|
|
43882
|
-
};
|
|
43883
|
-
}
|
|
43884
|
-
// Note: [🟡] Code for CLI harness update options [harnessUpdateCliOptions](src/cli/cli-commands/common/harnessUpdateCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
43885
|
-
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
43886
|
-
|
|
43887
|
-
/**
|
|
43888
|
-
* Relative path to the Promptbook Coder quick-reference file initialized in project roots.
|
|
43889
|
-
*
|
|
43890
|
-
* @private internal utility of `ptbk coder`
|
|
43891
|
-
*/
|
|
43892
|
-
const AGENT_CODING_FILE_PATH = 'AGENT_CODING.md';
|
|
43893
|
-
/**
|
|
43894
|
-
* Returns the default coder `AGENT_CODING.md` quick-reference content.
|
|
43895
|
-
*
|
|
43896
|
-
* @private internal utility of `ptbk coder`
|
|
43897
|
-
*/
|
|
43898
|
-
function getDefaultCoderAgentCodingFileContent() {
|
|
43899
|
-
return spaceTrim$1(`
|
|
43900
|
-
# ✨ Promptbook Coder agent coding
|
|
43901
|
-
|
|
43902
|
-
This project is using [Promptbook Coder](https://coder.ptbk.io) or run \`ptbk coder\`!
|
|
43903
|
-
`);
|
|
43904
|
-
}
|
|
43905
|
-
// Note: [🟡] Code for coder AGENT_CODING file boilerplate [agentCodingFile](src/cli/cli-commands/coder/agentCodingFile.ts) should never be published outside of `@promptbook/cli`
|
|
43906
|
-
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
43907
|
-
|
|
43908
44191
|
/**
|
|
43909
44192
|
* Relative path to the shared coder context file initialized in project roots.
|
|
43910
44193
|
*
|
|
@@ -44006,6 +44289,51 @@ function formatDisplayPath(relativePath) {
|
|
|
44006
44289
|
}
|
|
44007
44290
|
// Note: [🟡] Code for coder path formatting [formatDisplayPath](src/cli/cli-commands/coder/formatDisplayPath.ts) should never be published outside of `@promptbook/cli`
|
|
44008
44291
|
|
|
44292
|
+
/**
|
|
44293
|
+
* Creates the artifact definition of one project-owned coder prompt template.
|
|
44294
|
+
*/
|
|
44295
|
+
function createCoderPromptTemplateArtifactDefinition(definition) {
|
|
44296
|
+
return {
|
|
44297
|
+
relativeFilePath: definition.relativeFilePath,
|
|
44298
|
+
ensureArtifactFile: (projectPath) => ensureDefaultCoderPromptTemplateFile(projectPath, definition),
|
|
44299
|
+
};
|
|
44300
|
+
}
|
|
44301
|
+
/**
|
|
44302
|
+
* Every artifact which the default coder scripts can reference.
|
|
44303
|
+
*/
|
|
44304
|
+
const CODER_REFERENCED_ARTIFACT_DEFINITIONS = [
|
|
44305
|
+
{
|
|
44306
|
+
relativeFilePath: CODER_DEVELOPER_AGENT_FILE_PATH,
|
|
44307
|
+
ensureArtifactFile: ensureCoderDeveloperAgentFile,
|
|
44308
|
+
},
|
|
44309
|
+
{
|
|
44310
|
+
relativeFilePath: AGENTS_FILE_PATH,
|
|
44311
|
+
ensureArtifactFile: (projectPath) => ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent()),
|
|
44312
|
+
},
|
|
44313
|
+
...getDefaultCoderProjectPromptTemplateDefinitions().map(createCoderPromptTemplateArtifactDefinition),
|
|
44314
|
+
];
|
|
44315
|
+
/**
|
|
44316
|
+
* Creates only those referenced artifacts which the freshly added coder scripts point at.
|
|
44317
|
+
*
|
|
44318
|
+
* An artifact of a script which the project already defines is intentionally **not** created - the project-owned
|
|
44319
|
+
* script is kept as is, so it does not reference the default artifact in the first place.
|
|
44320
|
+
*
|
|
44321
|
+
* @private function of `initializeCoderProjectConfiguration`
|
|
44322
|
+
*/
|
|
44323
|
+
async function ensureCoderReferencedArtifacts(projectPath, referencedArtifactPaths) {
|
|
44324
|
+
const ensuredArtifacts = [];
|
|
44325
|
+
for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
|
|
44326
|
+
if (!referencedArtifactPaths.has(relativeFilePath)) {
|
|
44327
|
+
ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
|
|
44328
|
+
continue;
|
|
44329
|
+
}
|
|
44330
|
+
ensuredArtifacts.push({ relativeFilePath, status: await ensureArtifactFile(projectPath) });
|
|
44331
|
+
}
|
|
44332
|
+
return ensuredArtifacts;
|
|
44333
|
+
}
|
|
44334
|
+
// Note: [🟡] Code for coder init referenced artifacts [coderReferencedArtifacts](src/cli/cli-commands/coder/coderReferencedArtifacts.ts) should never be published outside of `@promptbook/cli`
|
|
44335
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44336
|
+
|
|
44009
44337
|
/**
|
|
44010
44338
|
* Fallback `.env` content used when no required variables need to be appended.
|
|
44011
44339
|
*/
|
|
@@ -44100,26 +44428,79 @@ async function ensureCoderGitignoreRules(projectPath, rules) {
|
|
|
44100
44428
|
}
|
|
44101
44429
|
// Note: [🟡] Code for coder init gitignore bootstrapping [ensureCoderGitignoreFile](src/cli/cli-commands/coder/ensureCoderGitignoreFile.ts) should never be published outside of `@promptbook/cli`
|
|
44102
44430
|
|
|
44431
|
+
/**
|
|
44432
|
+
* Name of the npm script which `ptbk coder run` uses to verify the project.
|
|
44433
|
+
*/
|
|
44434
|
+
const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
|
|
44103
44435
|
/**
|
|
44104
44436
|
* Default npm scripts initialized by `ptbk coder init`.
|
|
44437
|
+
*
|
|
44438
|
+
* Note: Using NPX because `ptbk` can be installed globally or locally, and NPX will resolve it correctly in either case.
|
|
44105
44439
|
*/
|
|
44106
|
-
const
|
|
44107
|
-
|
|
44108
|
-
|
|
44109
|
-
|
|
44110
|
-
|
|
44111
|
-
|
|
44112
|
-
|
|
44113
|
-
|
|
44440
|
+
const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS = [
|
|
44441
|
+
{
|
|
44442
|
+
scriptName: 'coder:generate-boilerplates',
|
|
44443
|
+
scriptCommand: `npx ptbk coder generate-boilerplates --count ${DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE} --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
|
|
44444
|
+
referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
|
|
44445
|
+
},
|
|
44446
|
+
{
|
|
44447
|
+
scriptName: 'coder:add',
|
|
44448
|
+
scriptCommand: `npx ptbk coder add --template ${formatCoderScriptFilePath(COMMON_PROMPT_TEMPLATE_FILE_PATH)}`,
|
|
44449
|
+
referencedArtifactPaths: [COMMON_PROMPT_TEMPLATE_FILE_PATH],
|
|
44450
|
+
},
|
|
44451
|
+
{
|
|
44452
|
+
scriptName: 'coder:run',
|
|
44453
|
+
scriptCommand: [
|
|
44454
|
+
'npx ptbk coder run --harness openai-codex --model gpt-5.6-terra --thinking-level max',
|
|
44455
|
+
`--agent ${formatDisplayPath(CODER_DEVELOPER_AGENT_FILE_PATH)}`,
|
|
44456
|
+
`--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
|
|
44457
|
+
`--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
|
|
44458
|
+
].join(' '),
|
|
44459
|
+
referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
|
|
44460
|
+
},
|
|
44461
|
+
// { scriptName: 'coder:find-refactor-candidates', scriptCommand: 'npx ptbk coder find-refactor-candidates', referencedArtifactPaths: [] },
|
|
44462
|
+
{
|
|
44463
|
+
scriptName: 'coder:verify',
|
|
44464
|
+
scriptCommand: 'npx ptbk coder verify',
|
|
44465
|
+
referencedArtifactPaths: [],
|
|
44466
|
+
},
|
|
44467
|
+
{
|
|
44468
|
+
// Note: The verification command of `coder:run` is a project-owned script, so every project can decide
|
|
44469
|
+
// what "verified" means without touching the `coder:run` script itself
|
|
44470
|
+
scriptName: CODER_TEST_SCRIPT_NAME,
|
|
44471
|
+
scriptCommand: 'npm test',
|
|
44472
|
+
referencedArtifactPaths: [],
|
|
44473
|
+
},
|
|
44474
|
+
];
|
|
44114
44475
|
/**
|
|
44115
44476
|
* Lists the default npm scripts initialized by `ptbk coder init`.
|
|
44116
44477
|
*
|
|
44117
44478
|
* @private internal utility of `coder init` command
|
|
44118
44479
|
*/
|
|
44119
44480
|
function getDefaultCoderPackageJsonScripts() {
|
|
44120
|
-
return
|
|
44481
|
+
return Object.fromEntries(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.map(({ scriptName, scriptCommand }) => [
|
|
44482
|
+
scriptName,
|
|
44483
|
+
scriptCommand,
|
|
44484
|
+
]));
|
|
44485
|
+
}
|
|
44486
|
+
/**
|
|
44487
|
+
* Collects the project-relative artifact paths referenced by the given default coder scripts.
|
|
44488
|
+
*
|
|
44489
|
+
* @private internal utility of `coder init` command
|
|
44490
|
+
*/
|
|
44491
|
+
function resolveCoderPackageJsonScriptReferencedArtifactPaths(scriptNames) {
|
|
44492
|
+
return new Set(DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS.filter(({ scriptName }) => scriptNames.includes(scriptName))
|
|
44493
|
+
.map(({ referencedArtifactPaths }) => referencedArtifactPaths)
|
|
44494
|
+
.flat());
|
|
44495
|
+
}
|
|
44496
|
+
/**
|
|
44497
|
+
* Formats one project-relative path as an explicitly project-rooted path usable inside a shell command.
|
|
44498
|
+
*/
|
|
44499
|
+
function formatCoderScriptFilePath(relativeFilePath) {
|
|
44500
|
+
return `./${formatDisplayPath(relativeFilePath)}`;
|
|
44121
44501
|
}
|
|
44122
44502
|
// Note: [🟡] Code for coder init package scripts [getDefaultCoderPackageJsonScripts](src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts) should never be published outside of `@promptbook/cli`
|
|
44503
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44123
44504
|
|
|
44124
44505
|
/**
|
|
44125
44506
|
* Loads the TypeScript runtime used for parsing JSONC-style project files.
|
|
@@ -44152,6 +44533,9 @@ const DEFAULT_JSON_FILE_NEWLINE = '\n';
|
|
|
44152
44533
|
/**
|
|
44153
44534
|
* Ensures one JSON object field contains the provided string-record entries.
|
|
44154
44535
|
*
|
|
44536
|
+
* Entries which the project already defines are **never** overridden - only missing keys are added,
|
|
44537
|
+
* so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
|
|
44538
|
+
*
|
|
44155
44539
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44156
44540
|
*/
|
|
44157
44541
|
async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldPath, nextEntries, ensureParentDirectoryPath, }) {
|
|
@@ -44163,21 +44547,24 @@ async function mergeStringRecordJsonFile({ projectPath, relativeFilePath, fieldP
|
|
|
44163
44547
|
const formatting = detectJsonFileFormatting(fileContent);
|
|
44164
44548
|
const jsonObject = fileContent === undefined ? {} : await parseJsonObjectFile(relativeFilePath, fileContent);
|
|
44165
44549
|
const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
|
|
44166
|
-
|
|
44550
|
+
const addedEntryKeys = [];
|
|
44167
44551
|
const mergedEntries = { ...existingEntries };
|
|
44168
44552
|
for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
|
|
44169
|
-
if (mergedEntries
|
|
44170
|
-
|
|
44171
|
-
|
|
44553
|
+
if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
|
|
44554
|
+
// Note: The project already defines this entry, keep its own value untouched
|
|
44555
|
+
continue;
|
|
44172
44556
|
}
|
|
44557
|
+
mergedEntries[entryKey] = entryValue;
|
|
44558
|
+
addedEntryKeys.push(entryKey);
|
|
44173
44559
|
}
|
|
44560
|
+
const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
|
|
44174
44561
|
if (!hasChanges) {
|
|
44175
|
-
return 'unchanged';
|
|
44562
|
+
return { status: 'unchanged', addedEntryKeys };
|
|
44176
44563
|
}
|
|
44177
44564
|
const nextJsonObject = { ...jsonObject };
|
|
44178
44565
|
nextJsonObject[fieldPath] = mergedEntries;
|
|
44179
44566
|
await writeFile(absoluteFilePath, serializeJsonObject(nextJsonObject, formatting), 'utf-8');
|
|
44180
|
-
return fileContent === undefined ? 'created' : 'updated';
|
|
44567
|
+
return { status: fileContent === undefined ? 'created' : 'updated', addedEntryKeys };
|
|
44181
44568
|
}
|
|
44182
44569
|
/**
|
|
44183
44570
|
* Parses one JSON object file while accepting VS Code style comments and trailing commas.
|
|
@@ -44266,6 +44653,8 @@ const PACKAGE_JSON_FILE_PATH = 'package.json';
|
|
|
44266
44653
|
/**
|
|
44267
44654
|
* Ensures `package.json` contains the standalone Promptbook coder helper scripts.
|
|
44268
44655
|
*
|
|
44656
|
+
* Scripts which the project already defines are kept as they are, only missing ones are added.
|
|
44657
|
+
*
|
|
44269
44658
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44270
44659
|
*/
|
|
44271
44660
|
async function ensureCoderPackageJsonFile(projectPath) {
|
|
@@ -44319,17 +44708,20 @@ const VSCODE_DIRECTORY_PATH = '.vscode';
|
|
|
44319
44708
|
/**
|
|
44320
44709
|
* Ensures VS Code routes pasted prompt images into `prompts/screenshots`.
|
|
44321
44710
|
*
|
|
44711
|
+
* Settings which the project already defines are kept as they are, only missing ones are added.
|
|
44712
|
+
*
|
|
44322
44713
|
* @private function of `initializeCoderProjectConfiguration`
|
|
44323
44714
|
*/
|
|
44324
44715
|
async function ensureCoderVscodeSettingsFile(projectPath) {
|
|
44325
44716
|
const [fieldPath, nextEntries] = resolveDefaultCoderVscodeSettingsEntry();
|
|
44326
|
-
|
|
44717
|
+
const { status } = await mergeStringRecordJsonFile({
|
|
44327
44718
|
projectPath,
|
|
44328
44719
|
relativeFilePath: VSCODE_SETTINGS_FILE_PATH,
|
|
44329
44720
|
fieldPath,
|
|
44330
44721
|
nextEntries,
|
|
44331
44722
|
ensureParentDirectoryPath: VSCODE_DIRECTORY_PATH,
|
|
44332
44723
|
});
|
|
44724
|
+
return status;
|
|
44333
44725
|
}
|
|
44334
44726
|
/**
|
|
44335
44727
|
* Resolves the default string-record entry that `coder init` merges into VS Code settings.
|
|
@@ -44346,34 +44738,32 @@ function resolveDefaultCoderVscodeSettingsEntry() {
|
|
|
44346
44738
|
/**
|
|
44347
44739
|
* Creates or updates all coder configuration artifacts required in the current project.
|
|
44348
44740
|
*
|
|
44741
|
+
* Nothing the project already owns is ever overwritten - existing scripts, settings and files are kept as they are,
|
|
44742
|
+
* and the artifacts they would reference are created only together with the scripts which actually reference them.
|
|
44743
|
+
*
|
|
44349
44744
|
* @private internal utility of `coder init` command
|
|
44350
44745
|
*/
|
|
44351
44746
|
async function initializeCoderProjectConfiguration(projectPath) {
|
|
44352
|
-
const promptsDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DIRECTORY_PATH);
|
|
44747
|
+
const promptsDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
44353
44748
|
const promptsDoneDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DONE_DIRECTORY_PATH);
|
|
44354
44749
|
const promptsTemplatesDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_TEMPLATES_DIRECTORY_PATH);
|
|
44355
|
-
const promptTemplateFileStatuses = await ensureDefaultCoderPromptTemplateFiles(projectPath);
|
|
44356
44750
|
const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
|
|
44357
|
-
const developerAgentFileStatus = await ensureCoderDeveloperAgentFile(projectPath);
|
|
44358
|
-
const agentsFileStatus = await ensureCoderMarkdownFile(projectPath, AGENTS_FILE_PATH, getDefaultCoderAgentsFileContent());
|
|
44359
|
-
const agentCodingFileStatus = await ensureCoderMarkdownFile(projectPath, AGENT_CODING_FILE_PATH, getDefaultCoderAgentCodingFileContent());
|
|
44360
44751
|
const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
|
|
44361
44752
|
const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
|
|
44362
|
-
const packageJsonFileStatus = await ensureCoderPackageJsonFile(projectPath);
|
|
44753
|
+
const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await ensureCoderPackageJsonFile(projectPath);
|
|
44363
44754
|
const vscodeSettingsFileStatus = await ensureCoderVscodeSettingsFile(projectPath);
|
|
44755
|
+
const referencedArtifactStatuses = await ensureCoderReferencedArtifacts(projectPath, resolveCoderPackageJsonScriptReferencedArtifactPaths(addedPackageJsonScriptNames));
|
|
44364
44756
|
return {
|
|
44365
44757
|
promptsDirectoryStatus,
|
|
44366
44758
|
promptsDoneDirectoryStatus,
|
|
44367
44759
|
promptsTemplatesDirectoryStatus,
|
|
44368
|
-
promptTemplateFileStatuses,
|
|
44369
44760
|
agentsDirectoryStatus,
|
|
44370
|
-
developerAgentFileStatus,
|
|
44371
|
-
agentsFileStatus,
|
|
44372
|
-
agentCodingFileStatus,
|
|
44373
44761
|
envFileStatus,
|
|
44374
44762
|
gitignoreFileStatus,
|
|
44375
44763
|
packageJsonFileStatus,
|
|
44376
44764
|
vscodeSettingsFileStatus,
|
|
44765
|
+
addedPackageJsonScriptNames,
|
|
44766
|
+
referencedArtifactStatuses,
|
|
44377
44767
|
initializedEnvVariableNames,
|
|
44378
44768
|
};
|
|
44379
44769
|
}
|
|
@@ -44389,17 +44779,20 @@ function printInitializationSummary(summary) {
|
|
|
44389
44779
|
printInitializationStatusLine('prompts/', summary.promptsDirectoryStatus);
|
|
44390
44780
|
printInitializationStatusLine('prompts/done/', summary.promptsDoneDirectoryStatus);
|
|
44391
44781
|
printInitializationStatusLine('prompts/templates/', summary.promptsTemplatesDirectoryStatus);
|
|
44392
|
-
for (const templateFileStatus of summary.promptTemplateFileStatuses) {
|
|
44393
|
-
printInitializationStatusLine(formatDisplayPath(templateFileStatus.relativeFilePath), templateFileStatus.status);
|
|
44394
|
-
}
|
|
44395
44782
|
printInitializationStatusLine('agents/', summary.agentsDirectoryStatus);
|
|
44396
|
-
printInitializationStatusLine(CODER_DEVELOPER_AGENT_FILE_PATH, summary.developerAgentFileStatus);
|
|
44397
|
-
printInitializationStatusLine(AGENTS_FILE_PATH, summary.agentsFileStatus);
|
|
44398
|
-
printInitializationStatusLine(AGENT_CODING_FILE_PATH, summary.agentCodingFileStatus);
|
|
44399
44783
|
printInitializationStatusLine('.env', summary.envFileStatus);
|
|
44400
44784
|
printInitializationStatusLine('.gitignore', summary.gitignoreFileStatus);
|
|
44401
44785
|
printInitializationStatusLine('package.json', summary.packageJsonFileStatus);
|
|
44402
44786
|
printInitializationStatusLine('.vscode/settings.json', summary.vscodeSettingsFileStatus);
|
|
44787
|
+
for (const referencedArtifactStatus of summary.referencedArtifactStatuses) {
|
|
44788
|
+
printInitializationStatusLine(formatDisplayPath(referencedArtifactStatus.relativeFilePath), referencedArtifactStatus.status);
|
|
44789
|
+
}
|
|
44790
|
+
if (summary.addedPackageJsonScriptNames.length > 0) {
|
|
44791
|
+
printInitializationNote(`Added npm scripts: ${summary.addedPackageJsonScriptNames.join(', ')}`, colors.cyan);
|
|
44792
|
+
}
|
|
44793
|
+
else {
|
|
44794
|
+
printInitializationNote('All Promptbook coder npm scripts are already present.', colors.gray);
|
|
44795
|
+
}
|
|
44403
44796
|
if (summary.initializedEnvVariableNames.length > 0) {
|
|
44404
44797
|
printInitializationNote(`Added env variables: ${summary.initializedEnvVariableNames.join(', ')}`, colors.cyan);
|
|
44405
44798
|
}
|
|
@@ -44417,6 +44810,9 @@ function formatInitializationStatus(status) {
|
|
|
44417
44810
|
if (status === 'updated') {
|
|
44418
44811
|
return 'updated';
|
|
44419
44812
|
}
|
|
44813
|
+
if (status === 'not-referenced') {
|
|
44814
|
+
return 'not referenced by the added scripts, kept as is';
|
|
44815
|
+
}
|
|
44420
44816
|
return 'unchanged';
|
|
44421
44817
|
}
|
|
44422
44818
|
/**
|
|
@@ -44459,39 +44855,48 @@ function $initializeCoderInitCommand(program) {
|
|
|
44459
44855
|
${block(listDefaultCoderProjectPromptTemplateDisplayPaths())}
|
|
44460
44856
|
- ${CODER_DEVELOPER_AGENT_FILE_PATH}
|
|
44461
44857
|
- ${AGENTS_FILE_PATH}
|
|
44462
|
-
- ${AGENT_CODING_FILE_PATH}
|
|
44463
44858
|
- .gitignore with local artifacts from every supported harness
|
|
44464
44859
|
- package.json
|
|
44465
44860
|
- .vscode/settings.json
|
|
44466
44861
|
|
|
44862
|
+
Never overwrites what the project already owns:
|
|
44863
|
+
- Existing package.json scripts and .vscode/settings.json settings are kept, only missing ones are added
|
|
44864
|
+
- Files referenced by a script, like the agent and context of coder:run, are created only together
|
|
44865
|
+
with the script which references them
|
|
44866
|
+
|
|
44467
44867
|
Ensures required coding-agent environment variables in .env:
|
|
44468
44868
|
- CODING_AGENT_GIT_NAME
|
|
44469
44869
|
- CODING_AGENT_GIT_EMAIL
|
|
44470
44870
|
- CODING_AGENT_GIT_SIGNING_KEY
|
|
44471
44871
|
|
|
44472
|
-
Checks that the coding harnesses are installed and up to date unless \`--no-
|
|
44872
|
+
Checks that the coding harnesses are installed and up to date unless \`--no-questions\` is used:
|
|
44473
44873
|
${block(listCheckedHarnessLabels())}
|
|
44474
44874
|
|
|
44475
44875
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
44876
|
+
|
|
44877
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
44476
44878
|
`));
|
|
44477
44879
|
addCoderGitSyncOptions(command);
|
|
44478
|
-
|
|
44880
|
+
addQuestionsOption(command);
|
|
44479
44881
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
44480
44882
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
44481
|
-
const
|
|
44883
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
44482
44884
|
const projectPath = process.cwd();
|
|
44483
44885
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
44484
44886
|
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
44485
44887
|
const commitScope = await $startCoderGitSync({ gitSync, projectPath });
|
|
44888
|
+
const isPromptsDirectoryEmpty = await isDirectoryEmpty(projectPath, PROMPTS_DIRECTORY_PATH$1);
|
|
44486
44889
|
const summary = await initializeCoderProjectConfiguration(projectPath);
|
|
44487
44890
|
printInitializationSummary(summary);
|
|
44488
|
-
|
|
44891
|
+
if (isPromptsDirectoryEmpty) {
|
|
44892
|
+
await generatePromptBoilerplate({ projectPath, boilerplateCount: DEFAULT_BOILERPLATE_COUNT });
|
|
44893
|
+
}
|
|
44489
44894
|
await $commitCoderChanges({
|
|
44490
44895
|
gitSync,
|
|
44491
44896
|
commitScope,
|
|
44492
44897
|
commitMessage: 'Initialize Promptbook Coder',
|
|
44493
44898
|
});
|
|
44494
|
-
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES,
|
|
44899
|
+
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, questionsOptions);
|
|
44495
44900
|
}));
|
|
44496
44901
|
}
|
|
44497
44902
|
/**
|
|
@@ -44511,6 +44916,112 @@ function listDefaultCoderProjectPromptTemplateDisplayPaths() {
|
|
|
44511
44916
|
// Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
|
|
44512
44917
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44513
44918
|
|
|
44919
|
+
/**
|
|
44920
|
+
* Creates a Commander argument parser that accepts only non-negative integers.
|
|
44921
|
+
*
|
|
44922
|
+
* The returned parser is meant to be passed as the coercion callback of `command.option(...)`.
|
|
44923
|
+
* It throws a branded `NotAllowed` error with a clear message referencing the given option name
|
|
44924
|
+
* when the provided value is not a non-negative integer.
|
|
44925
|
+
*
|
|
44926
|
+
* @private internal utility of `promptbookCli`
|
|
44927
|
+
*/
|
|
44928
|
+
function createNonNegativeIntegerOptionParser(optionName) {
|
|
44929
|
+
return (value) => {
|
|
44930
|
+
const parsedValue = Number(value);
|
|
44931
|
+
if (!Number.isInteger(parsedValue) || parsedValue < 0) {
|
|
44932
|
+
throw new NotAllowed(spaceTrim$1(`
|
|
44933
|
+
Invalid value for \`${optionName}\`: \`${value}\`.
|
|
44934
|
+
|
|
44935
|
+
Use a non-negative integer.
|
|
44936
|
+
`));
|
|
44937
|
+
}
|
|
44938
|
+
return parsedValue;
|
|
44939
|
+
};
|
|
44940
|
+
}
|
|
44941
|
+
// Note: [🟡] Code for CLI option parser [createNonNegativeIntegerOptionParser](src/cli/cli-commands/common/createNonNegativeIntegerOptionParser.ts) should never be published outside of `@promptbook/cli`
|
|
44942
|
+
|
|
44943
|
+
/**
|
|
44944
|
+
* Registers the shared prompt-priority filter options on a queue-based `ptbk coder` command.
|
|
44945
|
+
*
|
|
44946
|
+
* @private internal utility of `promptbookCli`
|
|
44947
|
+
*/
|
|
44948
|
+
function addPromptPriorityOptions(command) {
|
|
44949
|
+
command.option('--priority <minimum-priority>', 'Alias for --min-priority; filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--priority'));
|
|
44950
|
+
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
44951
|
+
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
44952
|
+
}
|
|
44953
|
+
// Note: [🟡] Code for CLI prompt priority options [promptPriorityCliOptions](src/cli/cli-commands/common/promptPriorityCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
44954
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44955
|
+
|
|
44956
|
+
/**
|
|
44957
|
+
* Description shared by coder commands which accept an agent Book.
|
|
44958
|
+
*
|
|
44959
|
+
* @private internal utility of `ptbk coder`
|
|
44960
|
+
*/
|
|
44961
|
+
const CODER_AGENT_OPTION_DESCRIPTION = 'Path to a .book file used to personalize coding prompts and select agent-specific tasks';
|
|
44962
|
+
/**
|
|
44963
|
+
* Registers the shared `--agent` option for coder commands.
|
|
44964
|
+
*
|
|
44965
|
+
* @private internal utility of `ptbk coder`
|
|
44966
|
+
*/
|
|
44967
|
+
function addCoderAgentOption(command) {
|
|
44968
|
+
command.option('--agent <agent-book-path>', CODER_AGENT_OPTION_DESCRIPTION);
|
|
44969
|
+
}
|
|
44970
|
+
// Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
|
|
44971
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
44972
|
+
|
|
44973
|
+
/**
|
|
44974
|
+
* Initializes `coder list` command for Promptbook CLI utilities.
|
|
44975
|
+
*
|
|
44976
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI.
|
|
44977
|
+
*
|
|
44978
|
+
* @private internal function of `promptbookCli`
|
|
44979
|
+
*/
|
|
44980
|
+
function $initializeCoderListCommand(program) {
|
|
44981
|
+
const command = program.command('list');
|
|
44982
|
+
command.description(spaceTrim$1(`
|
|
44983
|
+
List ready coding prompts by priority without executing them
|
|
44984
|
+
|
|
44985
|
+
${PROMPT_RUNNER_DESCRIPTION}
|
|
44986
|
+
|
|
44987
|
+
Features:
|
|
44988
|
+
- Lists only ready, fully authored prompts
|
|
44989
|
+
- Groups prompts from highest to lowest priority
|
|
44990
|
+
- Optional --harness, --model and --agent filters show only prompts compatible with that selection
|
|
44991
|
+
- Does not start a coding harness or modify prompt files
|
|
44992
|
+
`));
|
|
44993
|
+
addPromptRunnerSelectionOptions(command);
|
|
44994
|
+
addCoderAgentOption(command);
|
|
44995
|
+
addPromptPriorityOptions(command);
|
|
44996
|
+
command.action(handleActionErrors(async (cliOptions) => {
|
|
44997
|
+
const { agent, priority, minPriority: minimumPriority, maxPriority: maximumPriority, } = cliOptions;
|
|
44998
|
+
const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
|
|
44999
|
+
const resolvedCoderAgentBook = agent === undefined
|
|
45000
|
+
? undefined
|
|
45001
|
+
: await (await Promise.resolve().then(function () { return resolveCoderAgent$1; }))
|
|
45002
|
+
.resolveCoderAgentBook(agent, process.cwd());
|
|
45003
|
+
const promptRunnerIdentity = runnerOptions.agentName === undefined &&
|
|
45004
|
+
runnerOptions.model === undefined &&
|
|
45005
|
+
resolvedCoderAgentBook === undefined
|
|
45006
|
+
? undefined
|
|
45007
|
+
: {
|
|
45008
|
+
harnessName: runnerOptions.agentName,
|
|
45009
|
+
modelName: runnerOptions.model,
|
|
45010
|
+
agentReferences: resolvedCoderAgentBook === null || resolvedCoderAgentBook === void 0 ? void 0 : resolvedCoderAgentBook.agentReferences,
|
|
45011
|
+
};
|
|
45012
|
+
// Note: Import dynamically to avoid loading prompt parsing dependencies until this command is used.
|
|
45013
|
+
const { listCoderPrompts } = await Promise.resolve().then(function () { return listCoderPrompts$1; });
|
|
45014
|
+
await listCoderPrompts({
|
|
45015
|
+
priority,
|
|
45016
|
+
minimumPriority,
|
|
45017
|
+
maximumPriority,
|
|
45018
|
+
promptRunnerIdentity,
|
|
45019
|
+
});
|
|
45020
|
+
}));
|
|
45021
|
+
}
|
|
45022
|
+
// Note: [🟡] Code for CLI command [list](src/cli/cli-commands/coder/list.ts) should never be published outside of `@promptbook/cli`
|
|
45023
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
45024
|
+
|
|
44514
45025
|
/**
|
|
44515
45026
|
* Default wait duration applied before retrying a prompt round after an error (10 minutes).
|
|
44516
45027
|
*
|
|
@@ -44564,7 +45075,7 @@ function parseOptionalPeriodDuration(optionName, value) {
|
|
|
44564
45075
|
*
|
|
44565
45076
|
* @private internal utility of `ptbk coder`
|
|
44566
45077
|
*/
|
|
44567
|
-
async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName) {
|
|
45078
|
+
async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName, questionsOptions) {
|
|
44568
45079
|
if (harnessName === undefined) {
|
|
44569
45080
|
return;
|
|
44570
45081
|
}
|
|
@@ -44575,7 +45086,7 @@ async function $ensureCoderHarnessGitignoreRules(projectPath, harnessName) {
|
|
|
44575
45086
|
const { label } = getHarnessDefinition(harnessName);
|
|
44576
45087
|
const formattedRules = formatGitignoreRules(missingRules);
|
|
44577
45088
|
const entryLabel = missingRules.length === 1 ? 'entry' : 'entries';
|
|
44578
|
-
const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now
|
|
45089
|
+
const isAdditionApproved = await $askForConfirmation(`Add the missing ${label} ignore ${entryLabel} ${formattedRules} to \`.gitignore\` now?`, questionsOptions);
|
|
44579
45090
|
if (!isAdditionApproved) {
|
|
44580
45091
|
console.info(colors.gray(`Skipped, add ${formattedRules} to \`.gitignore\` manually.`));
|
|
44581
45092
|
return;
|
|
@@ -44600,22 +45111,24 @@ function formatGitignoreRules(rules) {
|
|
|
44600
45111
|
*/
|
|
44601
45112
|
function $initializeCoderPingCommand(program) {
|
|
44602
45113
|
const command = program.command('ping');
|
|
44603
|
-
command.description(spaceTrim$1(`
|
|
44604
|
-
|
|
45114
|
+
command.description(spaceTrim$1((block) => `
|
|
45115
|
+
Send one tiny dummy prompt to a harness and model to measure and warm them up
|
|
44605
45116
|
|
|
44606
|
-
|
|
45117
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
44607
45118
|
|
|
44608
|
-
|
|
44609
|
-
|
|
44610
|
-
|
|
44611
|
-
|
|
44612
|
-
|
|
44613
|
-
|
|
44614
|
-
|
|
44615
|
-
|
|
44616
|
-
|
|
45119
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45120
|
+
|
|
45121
|
+
Features:
|
|
45122
|
+
- Verifies that the selected harness, model, thinking level and authentication really work
|
|
45123
|
+
- Reports the answer of the harness, the response time and the reported usage
|
|
45124
|
+
- Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
|
|
45125
|
+
- Optional --period keeps the quota window refreshing by pinging once per period until stopped
|
|
45126
|
+
- Makes no coding changes or commits; if the selected harness has missing local ignore rules, offers to add them to .gitignore
|
|
45127
|
+
- Checks that the selected harness is installed and up to date unless --no-questions is used
|
|
45128
|
+
- Use --no-ui to stream the raw harness output instead of only the compact result
|
|
45129
|
+
`));
|
|
44617
45130
|
addPromptRunnerSelectionOptions(command);
|
|
44618
|
-
|
|
45131
|
+
addQuestionsOption(command);
|
|
44619
45132
|
addPromptRunnerRuntimeOptions(command);
|
|
44620
45133
|
command.option('--period <duration>', spaceTrim$1(`
|
|
44621
45134
|
Keep pinging once per period instead of pinging only once.
|
|
@@ -44624,11 +45137,11 @@ function $initializeCoderPingCommand(program) {
|
|
|
44624
45137
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
44625
45138
|
const { period: periodValue } = cliOptions;
|
|
44626
45139
|
const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
|
|
44627
|
-
const
|
|
45140
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
44628
45141
|
// Note: The period is validated before the harness installation check, so a mistyped duration fails fast
|
|
44629
45142
|
const periodMs = parseOptionalPeriodDuration('--period', periodValue);
|
|
44630
|
-
await $ensureHarnessInstallations([runnerOptions.agentName],
|
|
44631
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45143
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45144
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
44632
45145
|
const pingOptions = {
|
|
44633
45146
|
agentName: runnerOptions.agentName,
|
|
44634
45147
|
model: runnerOptions.model,
|
|
@@ -44652,28 +45165,157 @@ function $initializeCoderPingCommand(program) {
|
|
|
44652
45165
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44653
45166
|
|
|
44654
45167
|
/**
|
|
44655
|
-
*
|
|
45168
|
+
* Free disk space below which `ptbk coder` refuses to start a new run.
|
|
44656
45169
|
*
|
|
44657
|
-
*
|
|
44658
|
-
*
|
|
44659
|
-
*
|
|
45170
|
+
* A coding round writes prompt and log artifacts, lets the harness rewrite the project, may install npm packages,
|
|
45171
|
+
* may create a git worktree for `--isolate` and usually runs a build, so a run started with less than this
|
|
45172
|
+
* would very likely die somewhere in the middle with a filesystem error.
|
|
44660
45173
|
*
|
|
44661
45174
|
* @private internal utility of `promptbookCli`
|
|
44662
45175
|
*/
|
|
44663
|
-
|
|
44664
|
-
|
|
44665
|
-
|
|
44666
|
-
|
|
44667
|
-
|
|
44668
|
-
|
|
45176
|
+
const LOW_FREE_DISK_SPACE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
45177
|
+
/**
|
|
45178
|
+
* Free disk space below which a started `ptbk coder` run stops at its next checkpoint and waits until space is freed.
|
|
45179
|
+
*
|
|
45180
|
+
* This limit is lower than `LOW_FREE_DISK_SPACE_BYTES`, because interrupting a run which is already in progress
|
|
45181
|
+
* is more expensive than not starting one at all.
|
|
45182
|
+
*
|
|
45183
|
+
* @private internal utility of `promptbookCli`
|
|
45184
|
+
*/
|
|
45185
|
+
const CRITICALLY_LOW_FREE_DISK_SPACE_BYTES = 1 * 1024 * 1024 * 1024;
|
|
45186
|
+
/**
|
|
45187
|
+
* How long one free disk space measurement is reused before the filesystem is measured again during a run.
|
|
45188
|
+
*
|
|
45189
|
+
* Pause checkpoints are passed very often - for example on every tick of a timed wait - so without this
|
|
45190
|
+
* interval the filesystem would be measured many times per second.
|
|
45191
|
+
*
|
|
45192
|
+
* @private internal utility of `promptbookCli`
|
|
45193
|
+
*/
|
|
45194
|
+
const FREE_DISK_SPACE_RECHECK_INTERVAL_MS = 60 * 1000;
|
|
45195
|
+
// 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`
|
|
45196
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
44669
45197
|
|
|
44670
|
-
|
|
44671
|
-
|
|
45198
|
+
/**
|
|
45199
|
+
* Classifies one measured amount of free disk space against the `ptbk coder` free disk space limits.
|
|
45200
|
+
*
|
|
45201
|
+
* @private internal utility of `promptbookCli`
|
|
45202
|
+
*/
|
|
45203
|
+
function resolveFreeDiskSpaceLevel(availableBytes) {
|
|
45204
|
+
if (availableBytes < CRITICALLY_LOW_FREE_DISK_SPACE_BYTES) {
|
|
45205
|
+
return 'critical';
|
|
45206
|
+
}
|
|
45207
|
+
if (availableBytes < LOW_FREE_DISK_SPACE_BYTES) {
|
|
45208
|
+
return 'low';
|
|
45209
|
+
}
|
|
45210
|
+
return 'sufficient';
|
|
45211
|
+
}
|
|
45212
|
+
// 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`
|
|
45213
|
+
|
|
45214
|
+
/**
|
|
45215
|
+
* Measures the free disk space on the filesystem which hosts the given path.
|
|
45216
|
+
*
|
|
45217
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
|
|
45218
|
+
*
|
|
45219
|
+
* @returns the measurement, or `null` when the filesystem can not be measured, for example on a platform which
|
|
45220
|
+
* does not report filesystem statistics - a run is never stopped by a measurement which could not be taken
|
|
45221
|
+
* @private internal utility of `promptbookCli`
|
|
45222
|
+
*/
|
|
45223
|
+
async function $readFreeDiskSpaceStatus(inspectedPath) {
|
|
45224
|
+
try {
|
|
45225
|
+
const fileSystemStats = await statfs(inspectedPath);
|
|
45226
|
+
const blockSizeBytes = fileSystemStats.bsize;
|
|
45227
|
+
const availableBytes = fileSystemStats.bavail * blockSizeBytes;
|
|
45228
|
+
const totalBytes = fileSystemStats.blocks * blockSizeBytes;
|
|
45229
|
+
if (!Number.isFinite(availableBytes) || availableBytes < 0) {
|
|
45230
|
+
return null;
|
|
44672
45231
|
}
|
|
44673
|
-
return
|
|
44674
|
-
|
|
45232
|
+
return {
|
|
45233
|
+
inspectedPath,
|
|
45234
|
+
availableBytes,
|
|
45235
|
+
totalBytes,
|
|
45236
|
+
level: resolveFreeDiskSpaceLevel(availableBytes),
|
|
45237
|
+
};
|
|
45238
|
+
}
|
|
45239
|
+
catch (_a) {
|
|
45240
|
+
// Note: A path or a platform which can not report its free disk space must not stop `ptbk coder` from running
|
|
45241
|
+
return null;
|
|
45242
|
+
}
|
|
44675
45243
|
}
|
|
44676
|
-
// Note: [🟡] Code for CLI
|
|
45244
|
+
// 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`
|
|
45245
|
+
|
|
45246
|
+
/**
|
|
45247
|
+
* Byte units used when one amount of disk space is written for a human.
|
|
45248
|
+
*/
|
|
45249
|
+
const FREE_DISK_SPACE_BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
45250
|
+
/**
|
|
45251
|
+
* Writes one byte count as a compact human-readable amount of disk space, for example `812 MB` or `1.5 GB`.
|
|
45252
|
+
*
|
|
45253
|
+
* @private internal utility of `promptbookCli`
|
|
45254
|
+
*/
|
|
45255
|
+
function formatFreeDiskSpaceBytes(bytes) {
|
|
45256
|
+
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
45257
|
+
return '0 B';
|
|
45258
|
+
}
|
|
45259
|
+
const unitIndex = Math.min(FREE_DISK_SPACE_BYTE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
|
|
45260
|
+
const value = bytes / Math.pow(1024, unitIndex);
|
|
45261
|
+
// Note: One decimal place only matters for small values, `476 GB` is more readable than `476.3 GB`
|
|
45262
|
+
const decimalPlaceCount = value >= 10 || unitIndex === 0 ? 0 : 1;
|
|
45263
|
+
// Note: An empty decimal place only adds noise, `2 GB` is more readable than `2.0 GB`
|
|
45264
|
+
const displayedValue = value.toFixed(decimalPlaceCount).replace(/\.0$/, '');
|
|
45265
|
+
return `${displayedValue} ${FREE_DISK_SPACE_BYTE_UNITS[unitIndex]}`;
|
|
45266
|
+
}
|
|
45267
|
+
// 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`
|
|
45268
|
+
|
|
45269
|
+
/**
|
|
45270
|
+
* Writes the shared warning which explains one low free disk space measurement and how to get out of it.
|
|
45271
|
+
*
|
|
45272
|
+
* The very same wording is used by the check which refuses to start a run and by the check which pauses
|
|
45273
|
+
* a run that ran out of disk space while it was working.
|
|
45274
|
+
*
|
|
45275
|
+
* @private internal utility of `promptbookCli`
|
|
45276
|
+
*/
|
|
45277
|
+
function formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus) {
|
|
45278
|
+
const { inspectedPath } = freeDiskSpaceStatus;
|
|
45279
|
+
const availableSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes);
|
|
45280
|
+
const totalSpace = formatFreeDiskSpaceBytes(freeDiskSpaceStatus.totalBytes);
|
|
45281
|
+
const requiredSpaceToStart = formatFreeDiskSpaceBytes(LOW_FREE_DISK_SPACE_BYTES);
|
|
45282
|
+
const requiredSpaceToContinue = formatFreeDiskSpaceBytes(CRITICALLY_LOW_FREE_DISK_SPACE_BYTES);
|
|
45283
|
+
return spaceTrim$1(`
|
|
45284
|
+
Only **${availableSpace}** of ${totalSpace} is left on the disk which hosts \`${inspectedPath}\`.
|
|
45285
|
+
|
|
45286
|
+
**\`ptbk coder\` needs at least ${requiredSpaceToStart} of free disk space to start a run and at least ${requiredSpaceToContinue} to keep one running.**
|
|
45287
|
+
|
|
45288
|
+
Actionable hints:
|
|
45289
|
+
- Free up disk space on the drive which hosts \`${inspectedPath}\`.
|
|
45290
|
+
- Remove build artifacts, package caches and \`node_modules\` folders of projects you are not working on.
|
|
45291
|
+
- Remove the temporary worktrees which earlier \`--isolate\` runs left behind.
|
|
45292
|
+
`);
|
|
45293
|
+
}
|
|
45294
|
+
// 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`
|
|
45295
|
+
|
|
45296
|
+
/**
|
|
45297
|
+
* Refuses to start one `ptbk coder` command when the disk which hosts the project is nearly full.
|
|
45298
|
+
*
|
|
45299
|
+
* A run which starts without enough free disk space fails somewhere in the middle, after the coding agent has
|
|
45300
|
+
* already rewritten a part of the project, so it is stopped before it writes anything at all.
|
|
45301
|
+
*
|
|
45302
|
+
* Note: `$` is used to indicate that this function is not a pure function - it reads the state of the filesystem
|
|
45303
|
+
*
|
|
45304
|
+
* @throws {LimitReachedError} when there is not enough free disk space left to start
|
|
45305
|
+
* @private internal utility of `promptbookCli`
|
|
45306
|
+
*/
|
|
45307
|
+
async function $assertSufficientFreeDiskSpace(inspectedPath) {
|
|
45308
|
+
const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
|
|
45309
|
+
if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level === 'sufficient') {
|
|
45310
|
+
return;
|
|
45311
|
+
}
|
|
45312
|
+
throw new LimitReachedError(spaceTrim$1((block) => `
|
|
45313
|
+
There is not enough free disk space to run \`ptbk coder\`.
|
|
45314
|
+
|
|
45315
|
+
${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
|
|
45316
|
+
`));
|
|
45317
|
+
}
|
|
45318
|
+
// 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`
|
|
44677
45319
|
|
|
44678
45320
|
/**
|
|
44679
45321
|
* Npm packages which directly provide the `ptbk` executable.
|
|
@@ -45003,7 +45645,11 @@ function formatPromptbookCliInstallationLocation(installationLocation) {
|
|
|
45003
45645
|
* @returns `true` when at least one installation was updated and the current coder run should stop
|
|
45004
45646
|
* @private internal utility of `promptbookCli`
|
|
45005
45647
|
*/
|
|
45006
|
-
async function $ensurePromptbookCliInstallations() {
|
|
45648
|
+
async function $ensurePromptbookCliInstallations(questionsOptions) {
|
|
45649
|
+
if (!questionsOptions.isAskingQuestionsEnabled) {
|
|
45650
|
+
// Note: `--no-questions` forbids asking for the approval, so the registry checks would be useless
|
|
45651
|
+
return false;
|
|
45652
|
+
}
|
|
45007
45653
|
if (!process.stdin.isTTY) {
|
|
45008
45654
|
// Note: Non-interactive runs cannot approve an update and should not wait for registry checks
|
|
45009
45655
|
return false;
|
|
@@ -45015,7 +45661,7 @@ async function $ensurePromptbookCliInstallations() {
|
|
|
45015
45661
|
return false;
|
|
45016
45662
|
}
|
|
45017
45663
|
console.warn(colors.yellow(formatPromptbookCliInstallationWarning(outdatedStatuses)));
|
|
45018
|
-
const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?');
|
|
45664
|
+
const isUpdateApproved = await $askForNpmPackageInstallationApproval('Update Promptbook CLI now?', questionsOptions);
|
|
45019
45665
|
if (!isUpdateApproved) {
|
|
45020
45666
|
reportManualPromptbookCliUpdateCommands(outdatedStatuses);
|
|
45021
45667
|
return false;
|
|
@@ -45082,6 +45728,17 @@ function isTestBeforeMode(value) {
|
|
|
45082
45728
|
return TEST_BEFORE_MODE_VALUES.includes(value);
|
|
45083
45729
|
}
|
|
45084
45730
|
|
|
45731
|
+
/**
|
|
45732
|
+
* Prints why a coder run has ended to the terminal.
|
|
45733
|
+
*
|
|
45734
|
+
* @private internal utility of `coder run` and `coder server` commands
|
|
45735
|
+
*/
|
|
45736
|
+
function printCoderRunFailure(error) {
|
|
45737
|
+
console.error(colors.bgRed(`${error.name}`));
|
|
45738
|
+
console.error(colors.red(formatUnknownErrorDetails(error)));
|
|
45739
|
+
}
|
|
45740
|
+
// Note: [🟡] Code for coder failure reporting [printCoderRunFailure](src/cli/cli-commands/coder/printCoderRunFailure.ts) should never be published outside of `@promptbook/cli`
|
|
45741
|
+
|
|
45085
45742
|
/**
|
|
45086
45743
|
* Initializes `coder run` command for Promptbook CLI utilities
|
|
45087
45744
|
*
|
|
@@ -45091,10 +45748,12 @@ function isTestBeforeMode(value) {
|
|
|
45091
45748
|
*/
|
|
45092
45749
|
function $initializeCoderRunCommand(program) {
|
|
45093
45750
|
const command = program.command('run');
|
|
45094
|
-
command.description(spaceTrim$1(`
|
|
45751
|
+
command.description(spaceTrim$1((block) => `
|
|
45095
45752
|
Execute coding prompts through selected AI agent
|
|
45096
45753
|
|
|
45097
|
-
${PROMPT_RUNNER_DESCRIPTION}
|
|
45754
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
45755
|
+
|
|
45756
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45098
45757
|
|
|
45099
45758
|
Features:
|
|
45100
45759
|
- Automatically stages and commits changes with agent identity unless --no-commit is used
|
|
@@ -45104,9 +45763,11 @@ function $initializeCoderRunCommand(program) {
|
|
|
45104
45763
|
- Optional post-commit git push with explicit --auto-push opt-in
|
|
45105
45764
|
- Optional pre-prompt git pull with explicit --auto-pull opt-in
|
|
45106
45765
|
- Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
|
|
45766
|
+
- Saves the run trace of every round into prompts/traces/, named after its prompt file
|
|
45107
45767
|
- Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
|
|
45108
45768
|
- Optional --no-ui keeps plain streaming console output for logging and debugging
|
|
45109
|
-
-
|
|
45769
|
+
- Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
|
|
45770
|
+
- Checks that the selected harness is installed and up to date before the first prompt unless --no-questions is used
|
|
45110
45771
|
- Offers to add missing project-local ignore rules for the selected harness
|
|
45111
45772
|
- In interactive mode, checks local and global Promptbook CLI installations and offers to update them
|
|
45112
45773
|
- Supports GPG signing of commits
|
|
@@ -45117,8 +45778,8 @@ function $initializeCoderRunCommand(program) {
|
|
|
45117
45778
|
`));
|
|
45118
45779
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
45119
45780
|
addPromptRunnerSelectionOptions(command);
|
|
45120
|
-
|
|
45121
|
-
command
|
|
45781
|
+
addQuestionsOption(command);
|
|
45782
|
+
addCoderAgentOption(command);
|
|
45122
45783
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
45123
45784
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
45124
45785
|
command.addOption(new Option('--test-before <mode>', `Run tests before coding: ${TEST_BEFORE_MODE_VALUES.join(', ')} (defaults to no; uses npm test when --test is omitted)`)
|
|
@@ -45131,9 +45792,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
45131
45792
|
A verified task is merged back into the branch the coder runs on and the worktree is deleted.
|
|
45132
45793
|
A task that cannot be merged is marked as failed and its worktree is kept for a manual merge.
|
|
45133
45794
|
`), false);
|
|
45134
|
-
command
|
|
45135
|
-
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
45136
|
-
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
45795
|
+
addPromptPriorityOptions(command);
|
|
45137
45796
|
command.option('--limit <run-count>', 'Stop after processing this many prompt runs', createPositiveIntegerOptionParser('--limit'));
|
|
45138
45797
|
command.option('--wait-after-prompt <duration>', spaceTrim$1(`
|
|
45139
45798
|
Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
|
|
@@ -45159,7 +45818,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
45159
45818
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
45160
45819
|
isAgentRequired: !dryRun,
|
|
45161
45820
|
});
|
|
45162
|
-
const
|
|
45821
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
45163
45822
|
// [1] Parse the wait options and --no-auto:
|
|
45164
45823
|
// default: run automatically through the queue (no waiting between prompts)
|
|
45165
45824
|
// --no-auto: wait for user confirmation before each prompt (interactive mode)
|
|
@@ -45167,11 +45826,15 @@ function $initializeCoderRunCommand(program) {
|
|
|
45167
45826
|
// --wait-between-prompts: pace from start of one prompt to start of next
|
|
45168
45827
|
// --wait-after-error: wait before retrying after an error (default 10m)
|
|
45169
45828
|
const waitForUser = !auto;
|
|
45170
|
-
|
|
45829
|
+
assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
|
|
45830
|
+
// Note: Checked before anything is installed or written, because installing a harness, updating the
|
|
45831
|
+
// Promptbook CLI and coding itself all need the disk space which is missing
|
|
45832
|
+
await $assertSufficientFreeDiskSpace(process.cwd());
|
|
45833
|
+
if (await $ensurePromptbookCliInstallations(questionsOptions)) {
|
|
45171
45834
|
return process.exit(0);
|
|
45172
45835
|
}
|
|
45173
|
-
await $ensureHarnessInstallations([runnerOptions.agentName],
|
|
45174
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45836
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45837
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
45175
45838
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
45176
45839
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
45177
45840
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -45204,6 +45867,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
45204
45867
|
allowDestructiveAutoMigrate,
|
|
45205
45868
|
autoPush: runnerOptions.autoPush,
|
|
45206
45869
|
autoPull: runnerOptions.autoPull,
|
|
45870
|
+
isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
|
|
45207
45871
|
};
|
|
45208
45872
|
// Note: Import the function dynamically to avoid loading heavy dependencies until needed
|
|
45209
45873
|
const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
|
|
@@ -45213,8 +45877,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
45213
45877
|
}
|
|
45214
45878
|
catch (error) {
|
|
45215
45879
|
assertsError(error);
|
|
45216
|
-
|
|
45217
|
-
console.error(colors.red(error.stack || error.message));
|
|
45880
|
+
printCoderRunFailure(error);
|
|
45218
45881
|
return process.exit(1);
|
|
45219
45882
|
}
|
|
45220
45883
|
return process.exit(0);
|
|
@@ -45258,35 +45921,36 @@ const DEFAULT_CODER_SERVER_PORT = '4441';
|
|
|
45258
45921
|
*/
|
|
45259
45922
|
function $initializeCoderServerCommand(program) {
|
|
45260
45923
|
const command = program.command('server');
|
|
45261
|
-
command.description(spaceTrim$1(`
|
|
45262
|
-
|
|
45924
|
+
command.description(spaceTrim$1((block) => `
|
|
45925
|
+
Start a coder server that watches for prompts and serves a kanban web UI
|
|
45263
45926
|
|
|
45264
|
-
|
|
45927
|
+
${block(PROMPT_RUNNER_DESCRIPTION)}
|
|
45265
45928
|
|
|
45266
|
-
|
|
45267
|
-
|
|
45268
|
-
|
|
45269
|
-
|
|
45270
|
-
|
|
45271
|
-
|
|
45272
|
-
|
|
45273
|
-
|
|
45274
|
-
|
|
45275
|
-
|
|
45929
|
+
${block(QUESTIONS_DESCRIPTION)}
|
|
45930
|
+
|
|
45931
|
+
Features:
|
|
45932
|
+
- Runs the same prompt processing as \`ptbk coder run\`
|
|
45933
|
+
- Refuses to start on a nearly full disk and pauses the run when the free disk space becomes critical, unless --no-questions is used
|
|
45934
|
+
- Checks that the selected harness is installed and up to date on startup unless --no-questions is used
|
|
45935
|
+
- Offers to add missing project-local ignore rules for the selected harness
|
|
45936
|
+
- Does not exit when all prompts are done; polls for new prompt files instead
|
|
45937
|
+
- Serves a kanban board at http://localhost:<port> for visual progress tracking
|
|
45938
|
+
- Allows editing prompt files directly from the browser (Trello-style)
|
|
45939
|
+
- Play / pause button in the browser stays in sync with the CLI pause state
|
|
45940
|
+
- Terminal controls match \`ptbk coder run\`: P pause/resume, S skip waiting, X end after current prompt
|
|
45941
|
+
`));
|
|
45276
45942
|
command.addOption(new Option('--port <port>', 'Port to start the coder server on')
|
|
45277
45943
|
.env('PTBK_CODER_SERVER_PORT')
|
|
45278
45944
|
.default(DEFAULT_CODER_SERVER_PORT));
|
|
45279
45945
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
45280
45946
|
addPromptRunnerSelectionOptions(command);
|
|
45281
|
-
|
|
45282
|
-
command
|
|
45947
|
+
addQuestionsOption(command);
|
|
45948
|
+
addCoderAgentOption(command);
|
|
45283
45949
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
45284
45950
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
45285
45951
|
command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
|
|
45286
45952
|
addPromptRunnerExecutionOptions(command);
|
|
45287
|
-
command
|
|
45288
|
-
command.option('--min-priority <minimum-priority>', 'Filter prompts by minimum priority level', createNonNegativeIntegerOptionParser('--min-priority'));
|
|
45289
|
-
command.option('--max-priority <maximum-priority>', 'Filter prompts by maximum priority level', createNonNegativeIntegerOptionParser('--max-priority'));
|
|
45953
|
+
addPromptPriorityOptions(command);
|
|
45290
45954
|
command.option('--wait-after-prompt <duration>', spaceTrim$1(`
|
|
45291
45955
|
Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
|
|
45292
45956
|
Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
|
|
@@ -45310,11 +45974,15 @@ function $initializeCoderServerCommand(program) {
|
|
|
45310
45974
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
45311
45975
|
isAgentRequired: !dryRun,
|
|
45312
45976
|
});
|
|
45313
|
-
const
|
|
45314
|
-
await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
|
|
45315
|
-
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName);
|
|
45977
|
+
const questionsOptions = normalizeQuestionsCliOptions(cliOptions);
|
|
45316
45978
|
// [1] Parse the wait options and --no-auto (same logic as `coder run`)
|
|
45317
45979
|
const waitForUser = !auto;
|
|
45980
|
+
assertUserConfirmationIsAllowed({ ...questionsOptions, isWaitingForUser: waitForUser });
|
|
45981
|
+
// Note: Checked before anything is installed or written, because installing a harness and coding
|
|
45982
|
+
// itself both need the disk space which is missing
|
|
45983
|
+
await $assertSufficientFreeDiskSpace(process.cwd());
|
|
45984
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], questionsOptions);
|
|
45985
|
+
await $ensureCoderHarnessGitignoreRules(process.cwd(), runnerOptions.agentName, questionsOptions);
|
|
45318
45986
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
45319
45987
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
45320
45988
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -45344,6 +46012,7 @@ function $initializeCoderServerCommand(program) {
|
|
|
45344
46012
|
allowDestructiveAutoMigrate,
|
|
45345
46013
|
autoPush: runnerOptions.autoPush,
|
|
45346
46014
|
autoPull: runnerOptions.autoPull,
|
|
46015
|
+
isAskingQuestionsEnabled: questionsOptions.isAskingQuestionsEnabled,
|
|
45347
46016
|
};
|
|
45348
46017
|
// Note: Import dynamically to avoid loading heavy dependencies until needed
|
|
45349
46018
|
const { runCodexPromptsServer } = await Promise.resolve().then(function () { return runCodexPromptsServer$1; });
|
|
@@ -45352,8 +46021,7 @@ function $initializeCoderServerCommand(program) {
|
|
|
45352
46021
|
}
|
|
45353
46022
|
catch (error) {
|
|
45354
46023
|
assertsError(error);
|
|
45355
|
-
|
|
45356
|
-
console.error(colors.red(error.stack || error.message));
|
|
46024
|
+
printCoderRunFailure(error);
|
|
45357
46025
|
return process.exit(1);
|
|
45358
46026
|
}
|
|
45359
46027
|
return process.exit(0);
|
|
@@ -45501,6 +46169,7 @@ function collectStringOption(value, previousValues) {
|
|
|
45501
46169
|
* - add: Add one ready-to-run prompt file to the queue
|
|
45502
46170
|
* - generate-boilerplates: Generate prompt boilerplate files
|
|
45503
46171
|
* - find-refactor-candidates: Find files that need refactoring
|
|
46172
|
+
* - list: List ready prompts in priority order without running them
|
|
45504
46173
|
* - run: Run coding prompts with AI agents
|
|
45505
46174
|
* - ping: Test one harness and model with a tiny dummy prompt
|
|
45506
46175
|
* - verify: Verify completed prompts
|
|
@@ -45521,6 +46190,7 @@ function $initializeCoderCommand(program) {
|
|
|
45521
46190
|
- generate-boilerplates: Generate prompt boilerplate files
|
|
45522
46191
|
- find-refactor-candidates: Find files that need refactoring
|
|
45523
46192
|
- find-unwritten: List prompt sections that still need to be authored
|
|
46193
|
+
- list: List ready prompts in priority order without running them
|
|
45524
46194
|
- run: Run coding prompts with AI agents
|
|
45525
46195
|
- ping: Test the connection, response time and quota of one harness and model
|
|
45526
46196
|
- server: Start a long-running coder server with a kanban web UI
|
|
@@ -45533,17 +46203,14 @@ function $initializeCoderCommand(program) {
|
|
|
45533
46203
|
$initializeCoderGenerateBoilerplatesCommand(coderCommand);
|
|
45534
46204
|
$initializeCoderFindRefactorCandidatesCommand(coderCommand);
|
|
45535
46205
|
$initializeCoderFindUnwrittenCommand(coderCommand);
|
|
46206
|
+
$initializeCoderListCommand(coderCommand);
|
|
45536
46207
|
$initializeCoderRunCommand(coderCommand);
|
|
45537
46208
|
$initializeCoderPingCommand(coderCommand);
|
|
45538
46209
|
$initializeCoderServerCommand(coderCommand);
|
|
45539
46210
|
$initializeCoderVerifyCommand(coderCommand);
|
|
45540
46211
|
$initializeCoderFindFreshEmojiTagCommand(coderCommand);
|
|
45541
46212
|
// If no subcommand is provided, show help
|
|
45542
|
-
coderCommand
|
|
45543
|
-
console.info(colors.yellow('Please specify a subcommand.'));
|
|
45544
|
-
console.info('');
|
|
45545
|
-
coderCommand.help();
|
|
45546
|
-
});
|
|
46213
|
+
$requireCliSubcommand(coderCommand);
|
|
45547
46214
|
}
|
|
45548
46215
|
// Note: [🟡] Code for CLI command [coder](src/cli/cli-commands/coder.ts) should never be published outside of `@promptbook/cli`
|
|
45549
46216
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -56133,7 +56800,9 @@ function shouldRunInteractiveChatbot(options) {
|
|
|
56133
56800
|
* @private internal function of `promptbookCli`
|
|
56134
56801
|
*/
|
|
56135
56802
|
function $initializeRunCommand(program) {
|
|
56136
|
-
|
|
56803
|
+
// Note: `run` is deprecated, so it is intentionally NOT the default command of `ptbk`
|
|
56804
|
+
// and it must be asked for explicitly
|
|
56805
|
+
const runCommand = program.command('run');
|
|
56137
56806
|
configureRunCommand(runCommand);
|
|
56138
56807
|
runCommand.action(handleActionErrors((pipelineSource, cliOptions) => runCommandAction(pipelineSource, cliOptions)));
|
|
56139
56808
|
}
|
|
@@ -57972,9 +58641,24 @@ function $addGlobalOptionsToCommand(command) {
|
|
|
57972
58641
|
command.option('--remote-server-url <url>', `URL of remote server to use when `, DEFAULT_REMOTE_SERVER_URL);
|
|
57973
58642
|
}
|
|
57974
58643
|
|
|
58644
|
+
/**
|
|
58645
|
+
* Keeps one already registered CLI command out of the help of its parent command while the command itself stays
|
|
58646
|
+
* fully usable - it can still be run, it still has its own help and it is still reachable through `ptbk help <command>`.
|
|
58647
|
+
*
|
|
58648
|
+
* Note: `$` is used to indicate that this function is not a pure function - it changes how the CLI renders its help
|
|
58649
|
+
*
|
|
58650
|
+
* @private utility of CLI
|
|
58651
|
+
*/
|
|
58652
|
+
function $hideCliCommandFromHelp(command) {
|
|
58653
|
+
command._hidden = true;
|
|
58654
|
+
}
|
|
58655
|
+
|
|
57975
58656
|
/**
|
|
57976
58657
|
* Marks one CLI command as deprecated while keeping it available for existing callers.
|
|
57977
58658
|
*
|
|
58659
|
+
* Deprecated commands are not advertised anymore - they are hidden from the list of commands in the help of their
|
|
58660
|
+
* parent command, they warn when they are used and they explain the deprecation in their own help.
|
|
58661
|
+
*
|
|
57978
58662
|
* @private utility of CLI
|
|
57979
58663
|
*/
|
|
57980
58664
|
function $deprecateCliCommand(command, deprecationMessage) {
|
|
@@ -57986,6 +58670,7 @@ function $deprecateCliCommand(command, deprecationMessage) {
|
|
|
57986
58670
|
command.hook('preAction', () => {
|
|
57987
58671
|
console.warn(colors.yellow(createDeprecatedCliCommandWarning(command, deprecationMessage)));
|
|
57988
58672
|
});
|
|
58673
|
+
$hideCliCommandFromHelp(command);
|
|
57989
58674
|
}
|
|
57990
58675
|
/**
|
|
57991
58676
|
* Creates the runtime warning printed before a deprecated CLI command action starts.
|
|
@@ -58053,10 +58738,17 @@ async function promptbookCli() {
|
|
|
58053
58738
|
program.version(PROMPTBOOK_ENGINE_VERSION);
|
|
58054
58739
|
program.description(CLAIM);
|
|
58055
58740
|
// Note: These options are valid for all commands
|
|
58741
|
+
// Note: Commands are listed in the help in the order they are registered,
|
|
58742
|
+
// `coder` is the most used one so it goes first and the deprecated ones,
|
|
58743
|
+
// which `$deprecateTopLevelCommands` hides from the help entirely, go last
|
|
58744
|
+
$initializeCoderCommand(program);
|
|
58745
|
+
$initializeAgentCommand(program);
|
|
58746
|
+
$initializeAgentFolderCommand(program);
|
|
58747
|
+
$initializeAgentsServerCommand(program);
|
|
58056
58748
|
$initializeAboutCommand(program);
|
|
58749
|
+
$initializeHelloCommand(program);
|
|
58057
58750
|
$initializeRunCommand(program);
|
|
58058
58751
|
$initializeLoginCommand(program);
|
|
58059
|
-
$initializeHelloCommand(program);
|
|
58060
58752
|
$initializeMakeCommand(program);
|
|
58061
58753
|
$initializePrettifyCommand(program);
|
|
58062
58754
|
$initializeTestCommand(program);
|
|
@@ -58064,16 +58756,13 @@ async function promptbookCli() {
|
|
|
58064
58756
|
$initializeListScrapersCommand(program);
|
|
58065
58757
|
$initializeStartAgentsServerCommand(program);
|
|
58066
58758
|
$initializeStartPipelinesServerCommand(program);
|
|
58067
|
-
$initializeAgentCommand(program);
|
|
58068
|
-
$initializeAgentFolderCommand(program);
|
|
58069
|
-
$initializeAgentsServerCommand(program);
|
|
58070
|
-
$initializeCoderCommand(program);
|
|
58071
58759
|
$deprecateTopLevelCommands(program);
|
|
58072
58760
|
// TODO: [🧠] Should it be here or not> $addGlobalOptionsToCommand(program);
|
|
58073
58761
|
program.commands.forEach($addGlobalOptionsToCommand);
|
|
58762
|
+
// Note: There is no default command, so `ptbk` without a subcommand asks for one
|
|
58763
|
+
// instead of running the deprecated `ptbk run`
|
|
58074
58764
|
if (commandLineArguments.length === 0) {
|
|
58075
|
-
program
|
|
58076
|
-
return process.exit(0);
|
|
58765
|
+
return $reportMissingCliSubcommand(program);
|
|
58077
58766
|
}
|
|
58078
58767
|
program.parse(process.argv);
|
|
58079
58768
|
}
|
|
@@ -58087,7 +58776,7 @@ function isTopLevelVersionRequested(commandLineArguments) {
|
|
|
58087
58776
|
return firstCommandLineArgument !== undefined && VERSION_OPTION_ARGUMENTS.has(firstCommandLineArgument);
|
|
58088
58777
|
}
|
|
58089
58778
|
/**
|
|
58090
|
-
*
|
|
58779
|
+
* Marks each configured top-level legacy command as deprecated, which keeps it usable but takes it out of the help.
|
|
58091
58780
|
*/
|
|
58092
58781
|
function $deprecateTopLevelCommands(program) {
|
|
58093
58782
|
for (const command of program.commands) {
|
|
@@ -58098,7 +58787,6 @@ function $deprecateTopLevelCommands(program) {
|
|
|
58098
58787
|
}
|
|
58099
58788
|
}
|
|
58100
58789
|
// Note: [🟡] Code for CLI program [promptbookCli](src/cli/promptbookCli.ts) should never be published outside of `@promptbook/cli`
|
|
58101
|
-
// TODO: [🧠] Maybe `run` command the default, instead of `ptbk run ./foo.book` -> `ptbk ./foo.book`
|
|
58102
58790
|
// TODO: [🥠] Do not export, its just for CLI script
|
|
58103
58791
|
// TODO: [🕌] When more functionalities, rename
|
|
58104
58792
|
// Note: 11:11
|
|
@@ -59210,7 +59898,9 @@ const VERCEL_PROVIDER_PROFILE = {
|
|
|
59210
59898
|
*/
|
|
59211
59899
|
function createExecutionToolsFromVercelProvider(options) {
|
|
59212
59900
|
let { title, description } = options;
|
|
59213
|
-
const { vercelProvider, availableModels,
|
|
59901
|
+
const { vercelProvider, availableModels, additionalChatSettings = {} } = options;
|
|
59902
|
+
// <- Note: `userId` is not forwarded, because Vercel AI SDK moved provider-specific settings like `user` from
|
|
59903
|
+
// `vercelProvider.chat(modelName, settings)` into `providerOptions` which are keyed by the provider
|
|
59214
59904
|
if (!/Vercel/i.test(title)) {
|
|
59215
59905
|
title = `${title} (through Vercel)`;
|
|
59216
59906
|
// <- TODO: [🧈] Maybe standartize the suffix
|
|
@@ -59251,10 +59941,7 @@ function createExecutionToolsFromVercelProvider(options) {
|
|
|
59251
59941
|
|
|
59252
59942
|
`));
|
|
59253
59943
|
}
|
|
59254
|
-
const model = await vercelProvider.chat(modelName
|
|
59255
|
-
user: (userId === null || userId === void 0 ? void 0 : userId.toString()) || undefined,
|
|
59256
|
-
...additionalChatSettings,
|
|
59257
|
-
});
|
|
59944
|
+
const model = await vercelProvider.chat(modelName);
|
|
59258
59945
|
const rawPromptContent = templateParameters(content, { ...parameters, modelName });
|
|
59259
59946
|
// Support for passing a chat thread (multi-message conversation)
|
|
59260
59947
|
let promptMessages;
|
|
@@ -59292,14 +59979,9 @@ function createExecutionToolsFromVercelProvider(options) {
|
|
|
59292
59979
|
}
|
|
59293
59980
|
const rawRequest = {
|
|
59294
59981
|
// <- TODO: [☂]
|
|
59295
|
-
inputFormat: 'messages',
|
|
59296
|
-
mode: {
|
|
59297
|
-
type: 'regular',
|
|
59298
|
-
tools: [
|
|
59299
|
-
/* <- TODO: Pass the tools */
|
|
59300
|
-
],
|
|
59301
|
-
},
|
|
59302
59982
|
prompt: promptMessages,
|
|
59983
|
+
// <- TODO: Pass the tools via `tools`
|
|
59984
|
+
...additionalChatSettings,
|
|
59303
59985
|
};
|
|
59304
59986
|
const start = $getCurrentDate();
|
|
59305
59987
|
if (options.isVerbose) {
|
|
@@ -59316,28 +59998,31 @@ function createExecutionToolsFromVercelProvider(options) {
|
|
|
59316
59998
|
if (options.isVerbose) {
|
|
59317
59999
|
console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
59318
60000
|
}
|
|
59319
|
-
|
|
60001
|
+
// Note: Vercel AI SDK returns ordered content parts, the chat message is made only of the text parts
|
|
60002
|
+
const responseTextParts = rawResponse.content.flatMap((contentPart) => contentPart.type === 'text' ? [contentPart.text] : []);
|
|
60003
|
+
if (responseTextParts.length === 0) {
|
|
59320
60004
|
throw new PipelineExecutionError('No response message');
|
|
59321
60005
|
}
|
|
60006
|
+
const responseText = responseTextParts.join('');
|
|
59322
60007
|
const complete = $getCurrentDate();
|
|
59323
60008
|
const duration = uncertainNumber((new Date(complete).getTime() - new Date(start).getTime()) / 1000);
|
|
59324
60009
|
const usage = {
|
|
59325
60010
|
price: UNCERTAIN_ZERO_VALUE,
|
|
59326
60011
|
duration,
|
|
59327
60012
|
input: {
|
|
59328
|
-
tokensCount: uncertainNumber(rawResponse.usage.
|
|
60013
|
+
tokensCount: uncertainNumber(rawResponse.usage.inputTokens),
|
|
59329
60014
|
...computeUsageCounts(rawPromptContent),
|
|
59330
60015
|
},
|
|
59331
60016
|
output: {
|
|
59332
|
-
tokensCount: uncertainNumber(rawResponse.usage.
|
|
59333
|
-
...computeUsageCounts(
|
|
60017
|
+
tokensCount: uncertainNumber(rawResponse.usage.outputTokens),
|
|
60018
|
+
...computeUsageCounts(responseText),
|
|
59334
60019
|
},
|
|
59335
60020
|
};
|
|
59336
60021
|
return exportJson({
|
|
59337
60022
|
name: 'promptResult',
|
|
59338
60023
|
message: `Result of \`createExecutionToolsFromVercelProvider.callChatModel\``,
|
|
59339
60024
|
value: {
|
|
59340
|
-
content:
|
|
60025
|
+
content: responseText,
|
|
59341
60026
|
modelName,
|
|
59342
60027
|
timing: {
|
|
59343
60028
|
start,
|
|
@@ -74642,13 +75327,15 @@ function findFirstNonEmptyLineIndex(lines, startLine, endLine) {
|
|
|
74642
75327
|
}
|
|
74643
75328
|
|
|
74644
75329
|
/**
|
|
74645
|
-
* Checks whether a prompt is unrestricted or matches the selected harness
|
|
75330
|
+
* Checks whether a prompt is unrestricted or matches the selected harness, model or Book agent.
|
|
74646
75331
|
*
|
|
74647
|
-
* A prompt status line can contain one or more backtick-delimited model or
|
|
74648
|
-
*
|
|
74649
|
-
* `gpt` selects any `gpt-*` model
|
|
75332
|
+
* A prompt status line can contain one or more backtick-delimited model, harness or agent
|
|
75333
|
+
* references. Matching is intentionally based on normalized substrings so a token such as
|
|
75334
|
+
* `gpt` selects any `gpt-*` model, `developer` selects an agent Book named `developer.book`,
|
|
75335
|
+
* and `opus` selects a `claude-opus-*` model.
|
|
74650
75336
|
*/
|
|
74651
75337
|
function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
|
|
75338
|
+
var _a;
|
|
74652
75339
|
if (promptRunnerIdentity === undefined) {
|
|
74653
75340
|
return true;
|
|
74654
75341
|
}
|
|
@@ -74657,7 +75344,11 @@ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
|
|
|
74657
75344
|
if (requiredRunnerTokens.length === 0) {
|
|
74658
75345
|
return true;
|
|
74659
75346
|
}
|
|
74660
|
-
const normalizedRunnerNames = [
|
|
75347
|
+
const normalizedRunnerNames = [
|
|
75348
|
+
promptRunnerIdentity.harnessName,
|
|
75349
|
+
promptRunnerIdentity.modelName,
|
|
75350
|
+
...((_a = promptRunnerIdentity.agentReferences) !== null && _a !== void 0 ? _a : []),
|
|
75351
|
+
]
|
|
74661
75352
|
.filter((name) => name !== undefined && name.trim() !== '')
|
|
74662
75353
|
.map((name) => normalizeToKebabCase(name))
|
|
74663
75354
|
.filter((name) => name !== '');
|
|
@@ -74668,7 +75359,7 @@ function isPromptCompatibleWithRunner(file, section, promptRunnerIdentity) {
|
|
|
74668
75359
|
});
|
|
74669
75360
|
}
|
|
74670
75361
|
/**
|
|
74671
|
-
* Extracts model and
|
|
75362
|
+
* Extracts model, harness and Book-agent tokens from a prompt status line.
|
|
74672
75363
|
*/
|
|
74673
75364
|
function extractPromptRunnerTokens(statusLine) {
|
|
74674
75365
|
return Array.from(statusLine.matchAll(/`([^`]+)`/gu))
|
|
@@ -74683,7 +75374,7 @@ function extractPromptRunnerTokens(statusLine) {
|
|
|
74683
75374
|
*
|
|
74684
75375
|
* @private internal constant of `parsePromptFile`
|
|
74685
75376
|
*/
|
|
74686
|
-
const
|
|
75377
|
+
const PROMPT_STATUS_LINE_PATTERN = /^\[(?<marker> |-|\.|[xX]|\^|!)\](?<details>.*)$/u;
|
|
74687
75378
|
/**
|
|
74688
75379
|
* Parses a prompt markdown file into sections and metadata.
|
|
74689
75380
|
*/
|
|
@@ -74707,7 +75398,7 @@ function parsePromptFile(filePath, content) {
|
|
|
74707
75398
|
if (firstNonEmptyLine !== undefined) {
|
|
74708
75399
|
const statusLine = (lines[firstNonEmptyLine] || '').trim();
|
|
74709
75400
|
const parsedStatus = parseStatusLine(statusLine);
|
|
74710
|
-
const status = (_a = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.status) !== null && _a !== void 0 ? _a : (
|
|
75401
|
+
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');
|
|
74711
75402
|
const priority = (_b = parsedStatus === null || parsedStatus === void 0 ? void 0 : parsedStatus.priority) !== null && _b !== void 0 ? _b : 0;
|
|
74712
75403
|
sections.push({
|
|
74713
75404
|
index,
|
|
@@ -74735,51 +75426,47 @@ function parsePromptFile(filePath, content) {
|
|
|
74735
75426
|
* For [x] done, [!] failed and [^] in-progress prompts, allow metadata after the status marker.
|
|
74736
75427
|
*/
|
|
74737
75428
|
function parseStatusLine(line) {
|
|
74738
|
-
var _a, _b, _c, _d;
|
|
74739
|
-
|
|
74740
|
-
const
|
|
74741
|
-
|
|
75429
|
+
var _a, _b, _c, _d, _e;
|
|
75430
|
+
const statusLineMatch = line.match(PROMPT_STATUS_LINE_PATTERN);
|
|
75431
|
+
const marker = (_a = statusLineMatch === null || statusLineMatch === void 0 ? void 0 : statusLineMatch.groups) === null || _a === void 0 ? void 0 : _a.marker;
|
|
75432
|
+
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 : '';
|
|
75433
|
+
if (marker === 'x' || marker === 'X') {
|
|
75434
|
+
// For done prompts [x], allow any content after (for cost/time metadata).
|
|
74742
75435
|
return { status: 'done', priority: 0 };
|
|
74743
75436
|
}
|
|
74744
|
-
|
|
74745
|
-
|
|
74746
|
-
if (failedMatch) {
|
|
75437
|
+
if (marker === '!') {
|
|
75438
|
+
// For failed prompts [!], allow any content after (for failure metadata).
|
|
74747
75439
|
return { status: 'failed', priority: 0 };
|
|
74748
75440
|
}
|
|
74749
|
-
|
|
74750
|
-
|
|
74751
|
-
if (inProgressMatch) {
|
|
75441
|
+
if (marker === '^') {
|
|
75442
|
+
// For in-progress prompts [^], allow any content after (for the steps recorded so far).
|
|
74752
75443
|
return { status: 'in-progress', priority: 0 };
|
|
74753
75444
|
}
|
|
74754
|
-
//
|
|
74755
|
-
if (
|
|
75445
|
+
// Treat [.] as the alternative spelling of not-ready [-], preserving the historical clean-line syntax.
|
|
75446
|
+
if ((marker === '-' || marker === '.') && /^[!\s]*$/u.test(details)) {
|
|
74756
75447
|
return { status: 'not-ready', priority: 0 };
|
|
74757
75448
|
}
|
|
74758
|
-
|
|
74759
|
-
// before or after those tokens. Other trailing text remains an invalid status line.
|
|
74760
|
-
const todoMatch = line.match(/^\[ \](?<details>.*)$/u);
|
|
74761
|
-
if (!todoMatch) {
|
|
75449
|
+
if (marker !== ' ') {
|
|
74762
75450
|
return undefined;
|
|
74763
75451
|
}
|
|
74764
|
-
|
|
75452
|
+
// Todo [ ] may contain backtick-delimited model/harness tokens and priority markers
|
|
75453
|
+
// before or after those tokens. Other trailing text remains an invalid status line.
|
|
74765
75454
|
const isPriorityOnly = /^[!\s]*$/u.test(details);
|
|
74766
75455
|
const hasPromptRunnerTokens = extractPromptRunnerTokens(line).length > 0;
|
|
74767
75456
|
if (details.trim() !== '' && !isPriorityOnly && !hasPromptRunnerTokens) {
|
|
74768
75457
|
return undefined;
|
|
74769
75458
|
}
|
|
74770
|
-
return { status: 'todo', priority: (
|
|
75459
|
+
return { status: 'todo', priority: (_e = (_d = details.match(/!/gu)) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0 };
|
|
74771
75460
|
}
|
|
75461
|
+
|
|
74772
75462
|
/**
|
|
74773
|
-
*
|
|
75463
|
+
* HTML comment which excludes a Markdown file from all `ptbk coder` queues.
|
|
74774
75464
|
*
|
|
74775
|
-
* @private internal
|
|
75465
|
+
* @private internal constant of `loadPromptFiles`
|
|
74776
75466
|
*/
|
|
74777
|
-
|
|
74778
|
-
return PROMPT_STATUS_MARKER_PATTERN.test(line);
|
|
74779
|
-
}
|
|
74780
|
-
|
|
75467
|
+
const PTBK_CODER_IGNORE_MARKER = '<!--ptbk-coder-ignore-->';
|
|
74781
75468
|
/**
|
|
74782
|
-
* Loads and parses prompt files from the prompts directory.
|
|
75469
|
+
* Loads and parses prompt files from the prompts directory, excluding files marked for `ptbk coder` to ignore.
|
|
74783
75470
|
*/
|
|
74784
75471
|
async function loadPromptFiles(promptsDir) {
|
|
74785
75472
|
const entries = await readdir(promptsDir, { withFileTypes: true });
|
|
@@ -74790,6 +75477,9 @@ async function loadPromptFiles(promptsDir) {
|
|
|
74790
75477
|
const promptFiles = [];
|
|
74791
75478
|
for (const filePath of files) {
|
|
74792
75479
|
const content = await readFile(filePath, 'utf-8');
|
|
75480
|
+
if (content.includes(PTBK_CODER_IGNORE_MARKER)) {
|
|
75481
|
+
continue;
|
|
75482
|
+
}
|
|
74793
75483
|
promptFiles.push(parsePromptFile(filePath, content));
|
|
74794
75484
|
}
|
|
74795
75485
|
return promptFiles;
|
|
@@ -74900,22 +75590,158 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
|
|
|
74900
75590
|
});
|
|
74901
75591
|
|
|
74902
75592
|
/**
|
|
74903
|
-
*
|
|
75593
|
+
* Reads an optional agent `.book` file and prepares the references that route prompts to it.
|
|
74904
75594
|
*
|
|
74905
|
-
*
|
|
74906
|
-
* inside a branded error, where the stack of the wrapped error would only bury the actual cause.
|
|
75595
|
+
* Returns `undefined` when no agent path is provided.
|
|
74907
75596
|
*/
|
|
74908
|
-
function
|
|
74909
|
-
|
|
74910
|
-
|
|
75597
|
+
async function resolveCoderAgentBook(agentBookReference, currentWorkingDirectory) {
|
|
75598
|
+
const normalizedAgentBookReference = agentBookReference === null || agentBookReference === void 0 ? void 0 : agentBookReference.trim();
|
|
75599
|
+
if (!normalizedAgentBookReference) {
|
|
75600
|
+
return undefined;
|
|
74911
75601
|
}
|
|
74912
|
-
|
|
74913
|
-
|
|
75602
|
+
const resolvedAgentBookPath = resolve(currentWorkingDirectory, normalizedAgentBookReference);
|
|
75603
|
+
const agentSource = (await readFile(resolvedAgentBookPath, 'utf-8').catch((error) => {
|
|
75604
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
|
75605
|
+
throw new NotFoundError(spaceTrim(`
|
|
75606
|
+
Agent book \`${normalizedAgentBookReference}\` was not found or is not a file.
|
|
75607
|
+
|
|
75608
|
+
Pass a path to a \`.book\` file in \`--agent\`.
|
|
75609
|
+
`));
|
|
75610
|
+
}
|
|
75611
|
+
throw error;
|
|
75612
|
+
}));
|
|
75613
|
+
return {
|
|
75614
|
+
agentSource,
|
|
75615
|
+
agentReferences: createCoderAgentReferences({
|
|
75616
|
+
agentBookReference: normalizedAgentBookReference,
|
|
75617
|
+
resolvedAgentBookPath,
|
|
75618
|
+
currentWorkingDirectory,
|
|
75619
|
+
agentSource,
|
|
75620
|
+
}),
|
|
75621
|
+
};
|
|
75622
|
+
}
|
|
75623
|
+
/**
|
|
75624
|
+
* Reads an optional agent `.book` file and compiles its system message for injection into coder prompts.
|
|
75625
|
+
*
|
|
75626
|
+
* Returns `undefined` when no agent path is provided.
|
|
75627
|
+
*/
|
|
75628
|
+
async function resolveCoderAgent(agentBookReference, currentWorkingDirectory) {
|
|
75629
|
+
const resolvedAgentBook = await resolveCoderAgentBook(agentBookReference, currentWorkingDirectory);
|
|
75630
|
+
if (resolvedAgentBook === undefined) {
|
|
75631
|
+
return undefined;
|
|
74914
75632
|
}
|
|
74915
|
-
|
|
74916
|
-
|
|
75633
|
+
return {
|
|
75634
|
+
...resolvedAgentBook,
|
|
75635
|
+
systemMessage: await createAgentRunnerSystemMessage(resolvedAgentBook.agentSource),
|
|
75636
|
+
};
|
|
75637
|
+
}
|
|
75638
|
+
/**
|
|
75639
|
+
* Creates the path and Book-title aliases which a prompt can use to target one selected agent.
|
|
75640
|
+
*
|
|
75641
|
+
* @private internal utility of `resolveCoderAgentBook`
|
|
75642
|
+
*/
|
|
75643
|
+
function createCoderAgentReferences(options) {
|
|
75644
|
+
const relativeAgentBookPath = relative(options.currentWorkingDirectory, options.resolvedAgentBookPath).replaceAll('\\', '/');
|
|
75645
|
+
const agentBookFileName = basename(options.resolvedAgentBookPath);
|
|
75646
|
+
const agentBookName = basename(options.resolvedAgentBookPath, extname(options.resolvedAgentBookPath));
|
|
75647
|
+
const agentNameFromBook = parseAgentSource(options.agentSource).agentName;
|
|
75648
|
+
return Array.from(new Set([
|
|
75649
|
+
options.agentBookReference,
|
|
75650
|
+
relativeAgentBookPath,
|
|
75651
|
+
agentBookFileName,
|
|
75652
|
+
agentBookName,
|
|
75653
|
+
agentNameFromBook,
|
|
75654
|
+
].filter((agentReference) => agentReference.trim() !== '')));
|
|
75655
|
+
}
|
|
75656
|
+
|
|
75657
|
+
var resolveCoderAgent$1 = /*#__PURE__*/Object.freeze({
|
|
75658
|
+
__proto__: null,
|
|
75659
|
+
resolveCoderAgentBook: resolveCoderAgentBook,
|
|
75660
|
+
resolveCoderAgent: resolveCoderAgent
|
|
75661
|
+
});
|
|
75662
|
+
|
|
75663
|
+
/**
|
|
75664
|
+
* Lists todo prompts that are ready to run (no authoring placeholders).
|
|
75665
|
+
*/
|
|
75666
|
+
function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
75667
|
+
return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
|
|
75668
|
+
isPromptInPriorityFilter(prompt.section, priorityFilter) &&
|
|
75669
|
+
isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
|
|
75670
|
+
}
|
|
75671
|
+
|
|
75672
|
+
/**
|
|
75673
|
+
* Lists upcoming tasks that are ready to run (no authoring placeholders).
|
|
75674
|
+
*/
|
|
75675
|
+
function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
75676
|
+
return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
|
|
75677
|
+
label: buildPromptLabelForDisplay(file, section),
|
|
75678
|
+
summary: buildPromptSummary(file, section),
|
|
75679
|
+
priority: section.priority,
|
|
75680
|
+
}));
|
|
75681
|
+
}
|
|
75682
|
+
|
|
75683
|
+
/**
|
|
75684
|
+
* Groups upcoming tasks by priority, high to low.
|
|
75685
|
+
*/
|
|
75686
|
+
function groupUpcomingTasksByPriority(tasks) {
|
|
75687
|
+
const grouped = new Map();
|
|
75688
|
+
for (const task of tasks) {
|
|
75689
|
+
const group = grouped.get(task.priority);
|
|
75690
|
+
if (group) {
|
|
75691
|
+
group.push(task);
|
|
75692
|
+
}
|
|
75693
|
+
else {
|
|
75694
|
+
grouped.set(task.priority, [task]);
|
|
75695
|
+
}
|
|
75696
|
+
}
|
|
75697
|
+
return Array.from(grouped.entries())
|
|
75698
|
+
.sort((a, b) => b[0] - a[0])
|
|
75699
|
+
.map(([priority, groupedTasks]) => ({ priority, tasks: groupedTasks }));
|
|
75700
|
+
}
|
|
75701
|
+
|
|
75702
|
+
/**
|
|
75703
|
+
* Prints upcoming tasks grouped by priority.
|
|
75704
|
+
*/
|
|
75705
|
+
function printUpcomingTasks(tasks) {
|
|
75706
|
+
if (tasks.length === 0) {
|
|
75707
|
+
console.info(colors.green('No upcoming tasks.'));
|
|
75708
|
+
return;
|
|
75709
|
+
}
|
|
75710
|
+
console.info(colors.cyan('Upcoming tasks (grouped by priority):'));
|
|
75711
|
+
for (const group of groupUpcomingTasksByPriority(tasks)) {
|
|
75712
|
+
console.info(colors.cyan(`Priority ${group.priority}:`));
|
|
75713
|
+
for (const [index, task] of group.tasks.entries()) {
|
|
75714
|
+
const summary = task.summary ? ` - ${task.summary}` : '';
|
|
75715
|
+
console.info(` ${index + 1}. ${task.label}${summary}`);
|
|
75716
|
+
}
|
|
75717
|
+
}
|
|
75718
|
+
}
|
|
75719
|
+
|
|
75720
|
+
/**
|
|
75721
|
+
* Directory containing the prompt queue of the current project.
|
|
75722
|
+
*
|
|
75723
|
+
* @private internal constant of `listCoderPrompts`
|
|
75724
|
+
*/
|
|
75725
|
+
const PROMPTS_DIRECTORY_PATH = join(process.cwd(), 'prompts');
|
|
75726
|
+
/**
|
|
75727
|
+
* Lists ready, fully authored coding prompts in descending priority groups without starting a coding harness.
|
|
75728
|
+
*
|
|
75729
|
+
* @returns The number of prompts printed.
|
|
75730
|
+
* @public exported from `@promptbook/cli`
|
|
75731
|
+
*/
|
|
75732
|
+
async function listCoderPrompts(options = {}) {
|
|
75733
|
+
const priorityFilter = normalizePriorityFilter(options);
|
|
75734
|
+
const promptFiles = await loadPromptFiles(PROMPTS_DIRECTORY_PATH);
|
|
75735
|
+
const upcomingTasks = listUpcomingTasks(promptFiles, priorityFilter, options.promptRunnerIdentity);
|
|
75736
|
+
printUpcomingTasks(upcomingTasks);
|
|
75737
|
+
return upcomingTasks.length;
|
|
74917
75738
|
}
|
|
74918
75739
|
|
|
75740
|
+
var listCoderPrompts$1 = /*#__PURE__*/Object.freeze({
|
|
75741
|
+
__proto__: null,
|
|
75742
|
+
listCoderPrompts: listCoderPrompts
|
|
75743
|
+
});
|
|
75744
|
+
|
|
74919
75745
|
/**
|
|
74920
75746
|
* Builds a normalized temporary shell script path for prompt runners.
|
|
74921
75747
|
*/
|
|
@@ -75214,7 +76040,7 @@ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
|
|
|
75214
76040
|
/**
|
|
75215
76041
|
* CLI usage text for this script.
|
|
75216
76042
|
*/
|
|
75217
|
-
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]';
|
|
76043
|
+
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]';
|
|
75218
76044
|
/**
|
|
75219
76045
|
* Top-level flags supported by this command.
|
|
75220
76046
|
*/
|
|
@@ -75222,6 +76048,7 @@ const KNOWN_OPTION_FLAGS = new Set([
|
|
|
75222
76048
|
'--dry-run',
|
|
75223
76049
|
'--harness',
|
|
75224
76050
|
'--model',
|
|
76051
|
+
'--agent',
|
|
75225
76052
|
'--context',
|
|
75226
76053
|
'--test',
|
|
75227
76054
|
'--test-before',
|
|
@@ -75260,6 +76087,7 @@ function parseRunOptions(args) {
|
|
|
75260
76087
|
}
|
|
75261
76088
|
}
|
|
75262
76089
|
const model = readOptionValue(args, '--model');
|
|
76090
|
+
const agent = readOptionValue(args, '--agent');
|
|
75263
76091
|
const context = readOptionValue(args, '--context');
|
|
75264
76092
|
const hasTestCommandFlag = args.includes('--test');
|
|
75265
76093
|
const testCommand = readVariadicOptionValue(args, '--test');
|
|
@@ -75336,6 +76164,7 @@ function parseRunOptions(args) {
|
|
|
75336
76164
|
noUi,
|
|
75337
76165
|
agentName,
|
|
75338
76166
|
model,
|
|
76167
|
+
agent,
|
|
75339
76168
|
context,
|
|
75340
76169
|
testCommand,
|
|
75341
76170
|
testBefore,
|
|
@@ -75770,30 +76599,43 @@ async function recordPromptDurationSample(key, promptDurationMs) {
|
|
|
75770
76599
|
// Note: [🟡] Code in this file is only used from `@promptbook/cli` orchestration scripts and is not shipped to other consumers.
|
|
75771
76600
|
|
|
75772
76601
|
/**
|
|
75773
|
-
*
|
|
76602
|
+
* Creates the guard which watches the free disk space while one `ptbk coder` run is in progress.
|
|
75774
76603
|
*
|
|
75775
|
-
*
|
|
76604
|
+
* The guard runs at every pause checkpoint of the run. When the disk became critically full it reports the
|
|
76605
|
+
* warning and requests a pause, so the run stops at that checkpoint instead of failing in the middle of a
|
|
76606
|
+
* coding round, and continues once the user has freed some space and pressed `P`.
|
|
76607
|
+
*
|
|
76608
|
+
* @private internal utility of `ptbk coder` free disk space handling
|
|
75776
76609
|
*/
|
|
75777
|
-
|
|
75778
|
-
|
|
75779
|
-
|
|
75780
|
-
|
|
75781
|
-
|
|
75782
|
-
|
|
75783
|
-
|
|
75784
|
-
|
|
75785
|
-
|
|
76610
|
+
function createFreeDiskSpaceGuard(options) {
|
|
76611
|
+
const { inspectedPath, isAskingQuestionsEnabled } = options;
|
|
76612
|
+
let lastMeasurementTimeMs;
|
|
76613
|
+
return async () => {
|
|
76614
|
+
if (lastMeasurementTimeMs !== undefined &&
|
|
76615
|
+
Date.now() - lastMeasurementTimeMs < FREE_DISK_SPACE_RECHECK_INTERVAL_MS) {
|
|
76616
|
+
return;
|
|
76617
|
+
}
|
|
76618
|
+
lastMeasurementTimeMs = Date.now();
|
|
76619
|
+
const freeDiskSpaceStatus = await $readFreeDiskSpaceStatus(inspectedPath);
|
|
76620
|
+
if (freeDiskSpaceStatus === null || freeDiskSpaceStatus.level !== 'critical') {
|
|
76621
|
+
return;
|
|
76622
|
+
}
|
|
76623
|
+
console.warn(colors.yellow(spaceTrim$1((block) => `
|
|
76624
|
+
The disk is running out of space while \`ptbk coder\` is working.
|
|
75786
76625
|
|
|
75787
|
-
|
|
75788
|
-
|
|
76626
|
+
${block(formatLowFreeDiskSpaceWarning(freeDiskSpaceStatus))}
|
|
76627
|
+
- Press \`P\` to continue once the disk space is freed, or start the run with \`--no-questions\` to never pause on a full disk.
|
|
76628
|
+
`)));
|
|
76629
|
+
if (!isAskingQuestionsEnabled) {
|
|
76630
|
+
// Note: `--no-questions` forbids stopping and waiting for somebody who would free the disk space,
|
|
76631
|
+
// so the run keeps going and the warning above is the only thing which is reported
|
|
76632
|
+
return;
|
|
75789
76633
|
}
|
|
75790
|
-
|
|
75791
|
-
|
|
75792
|
-
return {
|
|
75793
|
-
agentSource: agentSource,
|
|
75794
|
-
systemMessage: await createAgentRunnerSystemMessage(agentSource),
|
|
76634
|
+
announcePauseTargetLabel(`continuing with only ${formatFreeDiskSpaceBytes(freeDiskSpaceStatus.availableBytes)} of free disk space left`);
|
|
76635
|
+
requestPause();
|
|
75795
76636
|
};
|
|
75796
76637
|
}
|
|
76638
|
+
// 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`
|
|
75797
76639
|
|
|
75798
76640
|
/**
|
|
75799
76641
|
* Refresh interval used by the countdown display while waiting.
|
|
@@ -77764,6 +78606,169 @@ async function writePromptFile(file) {
|
|
|
77764
78606
|
await writeFile(file.path, content, 'utf-8');
|
|
77765
78607
|
}
|
|
77766
78608
|
|
|
78609
|
+
/**
|
|
78610
|
+
* Creates a safe markdown fenced code block even when content contains backticks.
|
|
78611
|
+
*
|
|
78612
|
+
* @param content - Raw code content.
|
|
78613
|
+
* @param language - Optional info-string language label.
|
|
78614
|
+
* @returns Fenced code block.
|
|
78615
|
+
*
|
|
78616
|
+
* @private internal utility of `createStandaloneBookLanguageMarkdown` and of the `ptbk coder` run traces
|
|
78617
|
+
*/
|
|
78618
|
+
function getSafeCodeBlock(content, language = 'markdown') {
|
|
78619
|
+
const maxBacktickCount = Math.max(0, ...(content.match(/`+/g) || []).map((match) => match.length));
|
|
78620
|
+
const fence = '`'.repeat(Math.max(3, maxBacktickCount + 1));
|
|
78621
|
+
return `${fence}${language}\n${content}\n${fence}`;
|
|
78622
|
+
}
|
|
78623
|
+
|
|
78624
|
+
/**
|
|
78625
|
+
* Renders the markdown run trace of one finished or failed prompt round.
|
|
78626
|
+
*
|
|
78627
|
+
* The trace pairs the metadata of the round - which harness, model and thinking level ran it, how long each of
|
|
78628
|
+
* its steps took and what it cost - with the untouched runtime log, so a finished round can still be analyzed
|
|
78629
|
+
* after its temporary artifacts are cleaned up.
|
|
78630
|
+
*/
|
|
78631
|
+
function buildPromptRunTraceContent(options) {
|
|
78632
|
+
const sections = [
|
|
78633
|
+
buildPromptRunTraceSummarySection(options),
|
|
78634
|
+
buildPromptRunTraceFailureSection(options.outcome),
|
|
78635
|
+
buildPromptRunTraceRuntimeLogSection(options.runtimeLog),
|
|
78636
|
+
].filter((section) => section !== undefined);
|
|
78637
|
+
return `${sections.join('\n\n')}\n`;
|
|
78638
|
+
}
|
|
78639
|
+
/**
|
|
78640
|
+
* Renders the heading and the metadata bullet list of one run trace.
|
|
78641
|
+
*/
|
|
78642
|
+
function buildPromptRunTraceSummarySection(options) {
|
|
78643
|
+
const { file, section, outcome } = options;
|
|
78644
|
+
const runnerSignature = formatRunnerSignature(options.runnerName, options.modelName, options.thinkingLevel);
|
|
78645
|
+
const loginMethodLabel = outcome.kind === 'succeeded' ? formatCodexLoginMethod(outcome.loginMethod) : undefined;
|
|
78646
|
+
const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
|
|
78647
|
+
const detailLines = [
|
|
78648
|
+
`- **Prompt:** ${buildPromptSummary(file, section)}`,
|
|
78649
|
+
...buildPromptRunTraceSectionLines(file, section),
|
|
78650
|
+
`- **Outcome:** ${formatPromptRunTraceOutcomeLabel(outcome)}`,
|
|
78651
|
+
`- **Runner:** ${runnerSignature}${loginMethodSuffix}`,
|
|
78652
|
+
`- **Attempts:** ${options.attemptCount}`,
|
|
78653
|
+
...buildPromptRunTraceStepsLines(outcome),
|
|
78654
|
+
...buildPromptRunTraceTestCommandLines(options.testCommand),
|
|
78655
|
+
`- **Started:** ${options.startedDate.toISOString()}`,
|
|
78656
|
+
`- **Finished:** ${options.finishedDate.toISOString()}`,
|
|
78657
|
+
`- **Duration:** ${moment.duration(options.finishedDate.diff(options.startedDate)).humanize()}`,
|
|
78658
|
+
];
|
|
78659
|
+
return spaceTrim$1((block) => `
|
|
78660
|
+
# Run trace of \`${buildPromptLabelForDisplay(file, section)}\`
|
|
78661
|
+
|
|
78662
|
+
${block(detailLines.join('\n'))}
|
|
78663
|
+
`);
|
|
78664
|
+
}
|
|
78665
|
+
/**
|
|
78666
|
+
* Renders which section of its prompt file the round implemented.
|
|
78667
|
+
*
|
|
78668
|
+
* A prompt file holding exactly one section says nothing by naming it, exactly like the section suffix of its
|
|
78669
|
+
* generated artifact names, so only a file with more than one section reports the section it traced.
|
|
78670
|
+
*/
|
|
78671
|
+
function buildPromptRunTraceSectionLines(file, section) {
|
|
78672
|
+
if (file.sections.length <= 1) {
|
|
78673
|
+
return [];
|
|
78674
|
+
}
|
|
78675
|
+
return [`- **Prompt section:** ${section.index + 1} of ${file.sections.length}`];
|
|
78676
|
+
}
|
|
78677
|
+
/**
|
|
78678
|
+
* Renders the per-step usage breakdown of a successful round, omitted for a round which never finished one.
|
|
78679
|
+
*/
|
|
78680
|
+
function buildPromptRunTraceStepsLines(outcome) {
|
|
78681
|
+
if (outcome.kind !== 'succeeded') {
|
|
78682
|
+
return [];
|
|
78683
|
+
}
|
|
78684
|
+
const stepsSummary = formatCoderRunSteps(outcome.steps);
|
|
78685
|
+
if (stepsSummary === '') {
|
|
78686
|
+
return [];
|
|
78687
|
+
}
|
|
78688
|
+
return [`- **Steps:** ${stepsSummary}`];
|
|
78689
|
+
}
|
|
78690
|
+
/**
|
|
78691
|
+
* Renders the verification command of the round, omitted when the round ran without one.
|
|
78692
|
+
*/
|
|
78693
|
+
function buildPromptRunTraceTestCommandLines(testCommand) {
|
|
78694
|
+
if (!testCommand) {
|
|
78695
|
+
return [];
|
|
78696
|
+
}
|
|
78697
|
+
return [`- **Verification command:** \`${testCommand}\``];
|
|
78698
|
+
}
|
|
78699
|
+
/**
|
|
78700
|
+
* Renders the failure details of a failed round, omitted for a successful one.
|
|
78701
|
+
*/
|
|
78702
|
+
function buildPromptRunTraceFailureSection(outcome) {
|
|
78703
|
+
if (outcome.kind !== 'failed') {
|
|
78704
|
+
return undefined;
|
|
78705
|
+
}
|
|
78706
|
+
return `## Failure\n\n${getSafeCodeBlock(formatUnknownErrorDetails(outcome.error), 'text')}`;
|
|
78707
|
+
}
|
|
78708
|
+
/**
|
|
78709
|
+
* Renders the untouched runtime log of the round, or says that the round produced none.
|
|
78710
|
+
*/
|
|
78711
|
+
function buildPromptRunTraceRuntimeLogSection(runtimeLog) {
|
|
78712
|
+
if (runtimeLog.trim() === '') {
|
|
78713
|
+
return '## Runtime log\n\n_This round has produced no readable runtime log._';
|
|
78714
|
+
}
|
|
78715
|
+
return `## Runtime log\n\n${getSafeCodeBlock(runtimeLog.replace(/\r\n/gu, '\n').trimEnd(), 'text')}`;
|
|
78716
|
+
}
|
|
78717
|
+
/**
|
|
78718
|
+
* Formats the outcome of the round as the human-readable label of its trace.
|
|
78719
|
+
*/
|
|
78720
|
+
function formatPromptRunTraceOutcomeLabel(outcome) {
|
|
78721
|
+
return outcome.kind === 'succeeded' ? 'Succeeded' : 'Failed';
|
|
78722
|
+
}
|
|
78723
|
+
|
|
78724
|
+
/**
|
|
78725
|
+
* Name of the directory which keeps the run traces inside the prompts directory.
|
|
78726
|
+
*/
|
|
78727
|
+
const PROMPT_RUN_TRACES_DIRECTORY_NAME = 'traces';
|
|
78728
|
+
/**
|
|
78729
|
+
* Builds the path of the run trace which belongs to one prompt section.
|
|
78730
|
+
*
|
|
78731
|
+
* The trace is stored in the `traces` directory of the very same prompts directory the prompt itself lives in,
|
|
78732
|
+
* and it carries the name of its prompt file. A prompt file holding more than one section appends the section
|
|
78733
|
+
* suffix, exactly like the temporary scripts and the isolation worktrees of the same section do.
|
|
78734
|
+
*/
|
|
78735
|
+
function buildPromptRunTracePath(file, section) {
|
|
78736
|
+
const promptFileBaseName = file.name.replace(/\.[^.]+$/u, '');
|
|
78737
|
+
const traceFileName = `${promptFileBaseName}${buildPromptSectionSuffix(file, section)}.md`;
|
|
78738
|
+
return join(dirname(file.path), PROMPT_RUN_TRACES_DIRECTORY_NAME, traceFileName);
|
|
78739
|
+
}
|
|
78740
|
+
|
|
78741
|
+
/**
|
|
78742
|
+
* Writes the run trace of one prompt round into the `traces` directory of the prompts directory.
|
|
78743
|
+
*
|
|
78744
|
+
* Returns the path of the written trace so callers can include it in a commit.
|
|
78745
|
+
*/
|
|
78746
|
+
async function writePromptRunTrace(options) {
|
|
78747
|
+
const { logPath, ...contentOptions } = options;
|
|
78748
|
+
const tracePath = buildPromptRunTracePath(options.file, options.section);
|
|
78749
|
+
const runtimeLog = await readPromptRuntimeLog(logPath);
|
|
78750
|
+
await mkdir(dirname(tracePath), { recursive: true });
|
|
78751
|
+
await writeFile(tracePath, buildPromptRunTraceContent({ ...contentOptions, runtimeLog }), 'utf-8');
|
|
78752
|
+
return tracePath;
|
|
78753
|
+
}
|
|
78754
|
+
/**
|
|
78755
|
+
* Reads the live runtime log of one round.
|
|
78756
|
+
*
|
|
78757
|
+
* A round which never produced a readable log still gets its trace, because the metadata of the round is worth
|
|
78758
|
+
* keeping on its own - so a missing log is reported as empty instead of failing the round which has just passed.
|
|
78759
|
+
*/
|
|
78760
|
+
async function readPromptRuntimeLog(logPath) {
|
|
78761
|
+
if (!logPath) {
|
|
78762
|
+
return '';
|
|
78763
|
+
}
|
|
78764
|
+
try {
|
|
78765
|
+
return await readFile(logPath, 'utf-8');
|
|
78766
|
+
}
|
|
78767
|
+
catch (_a) {
|
|
78768
|
+
return '';
|
|
78769
|
+
}
|
|
78770
|
+
}
|
|
78771
|
+
|
|
77767
78772
|
/**
|
|
77768
78773
|
* Refreshes optional harness subscription usage in the shared coder-run UI state.
|
|
77769
78774
|
*
|
|
@@ -77886,7 +78891,9 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
77886
78891
|
catch (error) {
|
|
77887
78892
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
77888
78893
|
lastError = error;
|
|
77889
|
-
|
|
78894
|
+
// Note: A harness which is not logged in answers every retry the same way, so the user gets
|
|
78895
|
+
// the sign-in instructions right away instead of after every retry has waited its delay
|
|
78896
|
+
if (error instanceof AuthenticationError || errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
|
|
77890
78897
|
break;
|
|
77891
78898
|
}
|
|
77892
78899
|
await waitAfterErrorBeforeRetry({
|
|
@@ -77908,6 +78915,7 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
|
|
|
77908
78915
|
attemptCount,
|
|
77909
78916
|
error: lastError,
|
|
77910
78917
|
options,
|
|
78918
|
+
logPath,
|
|
77911
78919
|
roundCommitScope,
|
|
77912
78920
|
uiHandle,
|
|
77913
78921
|
waitForRequestedPause,
|
|
@@ -78024,6 +79032,17 @@ async function finalizeSuccessfulPromptRound(options) {
|
|
|
78024
79032
|
// Note: The prompt status is always written into the original project, an isolated round transports
|
|
78025
79033
|
// its own changes back through the merge instead
|
|
78026
79034
|
await writePromptFile(nextPrompt.file);
|
|
79035
|
+
// Note: Written before the round is committed, so the trace of the round lands in the very same commit
|
|
79036
|
+
// as the prompt it describes, and before the live runtime log it is built from is deleted
|
|
79037
|
+
await recordPromptRoundTrace({
|
|
79038
|
+
options: runOptions,
|
|
79039
|
+
nextPrompt,
|
|
79040
|
+
runnerMetadata,
|
|
79041
|
+
promptExecutionStartedDate,
|
|
79042
|
+
attemptCount: result.attemptCount,
|
|
79043
|
+
logPath,
|
|
79044
|
+
outcome: { kind: 'succeeded', steps: result.steps, loginMethod: result.loginMethod },
|
|
79045
|
+
});
|
|
78027
79046
|
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
78028
79047
|
await recordPromptDurationInEstimateCache({
|
|
78029
79048
|
options: runOptions,
|
|
@@ -78071,7 +79090,7 @@ async function finalizeSuccessfulPromptRound(options) {
|
|
|
78071
79090
|
* Finalizes a failed prompt round, persisting prompt failure metadata before rethrowing.
|
|
78072
79091
|
*/
|
|
78073
79092
|
async function finalizeFailedPromptRound(options) {
|
|
78074
|
-
const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
79093
|
+
const { nextPrompt, runnerMetadata, previousRunnerSignatures, promptExecutionStartedDate, attemptCount, error, options: runOptions, logPath, roundCommitScope, uiHandle, waitForRequestedPause, roundProjectPath, } = options;
|
|
78075
79094
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
78076
79095
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
|
|
78077
79096
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
|
|
@@ -78097,8 +79116,42 @@ async function finalizeFailedPromptRound(options) {
|
|
|
78097
79116
|
modelName: runnerMetadata.modelName,
|
|
78098
79117
|
error,
|
|
78099
79118
|
});
|
|
79119
|
+
// Note: A failed round is exactly the round whose trace is worth reading, so it is written before the live
|
|
79120
|
+
// runtime log it is built from is deleted
|
|
79121
|
+
await recordPromptRoundTrace({
|
|
79122
|
+
options: runOptions,
|
|
79123
|
+
nextPrompt,
|
|
79124
|
+
runnerMetadata,
|
|
79125
|
+
promptExecutionStartedDate,
|
|
79126
|
+
attemptCount,
|
|
79127
|
+
logPath,
|
|
79128
|
+
outcome: { kind: 'failed', error },
|
|
79129
|
+
});
|
|
78100
79130
|
await normalizeLineEndingsForCurrentRound(runOptions, roundProjectPath, roundCommitScope);
|
|
78101
79131
|
}
|
|
79132
|
+
/**
|
|
79133
|
+
* Persists the run trace of one prompt round, so the round can still be analyzed after its temporary
|
|
79134
|
+
* artifacts are gone.
|
|
79135
|
+
*
|
|
79136
|
+
* This is the single place where both the successful and the failed round record what they did, which harness,
|
|
79137
|
+
* model and thinking level did it and everything that harness has written while doing it.
|
|
79138
|
+
*/
|
|
79139
|
+
async function recordPromptRoundTrace(options) {
|
|
79140
|
+
const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
|
|
79141
|
+
await writePromptRunTrace({
|
|
79142
|
+
file: nextPrompt.file,
|
|
79143
|
+
section: nextPrompt.section,
|
|
79144
|
+
runnerName: runnerMetadata.runnerName,
|
|
79145
|
+
modelName: runnerMetadata.modelName,
|
|
79146
|
+
thinkingLevel: runOptions.thinkingLevel,
|
|
79147
|
+
testCommand: runOptions.testCommand,
|
|
79148
|
+
attemptCount,
|
|
79149
|
+
startedDate: promptExecutionStartedDate,
|
|
79150
|
+
finishedDate: moment(),
|
|
79151
|
+
outcome,
|
|
79152
|
+
logPath,
|
|
79153
|
+
});
|
|
79154
|
+
}
|
|
78102
79155
|
/**
|
|
78103
79156
|
* Waits for the optional user confirmation immediately before creating the commit.
|
|
78104
79157
|
*/
|
|
@@ -78747,7 +79800,13 @@ async function recordIsolationMergeFailure(options, worktree, failureDetails) {
|
|
|
78747
79800
|
await commitChanges(buildCoderIsolationMergeFailureCommitMessage(worktree), {
|
|
78748
79801
|
autoPush: options.options.autoPush,
|
|
78749
79802
|
projectPath: worktree.projectPath,
|
|
78750
|
-
|
|
79803
|
+
// Note: The round itself has already succeeded, so it has left its run trace in the original project.
|
|
79804
|
+
// It belongs to this commit, which is the only one this task still gets.
|
|
79805
|
+
relevantPaths: [
|
|
79806
|
+
nextPrompt.file.path,
|
|
79807
|
+
errorLogPath,
|
|
79808
|
+
buildPromptRunTracePath(nextPrompt.file, nextPrompt.section),
|
|
79809
|
+
].map((path) => toProjectRelativeGitPath(worktree.projectPath, path)),
|
|
78751
79810
|
});
|
|
78752
79811
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(mergeFailureError.message);
|
|
78753
79812
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Merging \`${worktree.taskName}\` failed, worktree kept for a manual merge`);
|
|
@@ -78771,15 +79830,6 @@ function toProjectRelativeGitPath(projectPath, path) {
|
|
|
78771
79830
|
return relative(projectPath, path).replace(/\\/gu, '/');
|
|
78772
79831
|
}
|
|
78773
79832
|
|
|
78774
|
-
/**
|
|
78775
|
-
* Lists todo prompts that are ready to run (no authoring placeholders).
|
|
78776
|
-
*/
|
|
78777
|
-
function listRunnablePrompts(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
78778
|
-
return listTodoPrompts(files).filter((prompt) => !isPromptToBeWritten(prompt.file, prompt.section) &&
|
|
78779
|
-
isPromptInPriorityFilter(prompt.section, priorityFilter) &&
|
|
78780
|
-
isPromptCompatibleWithRunner(prompt.file, prompt.section, promptRunnerIdentity));
|
|
78781
|
-
}
|
|
78782
|
-
|
|
78783
79833
|
/**
|
|
78784
79834
|
* Selects the next runnable prompt based on priority.
|
|
78785
79835
|
*/
|
|
@@ -78793,54 +79843,6 @@ function findNextTodoPrompt(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
|
78793
79843
|
return nextPrompt;
|
|
78794
79844
|
}
|
|
78795
79845
|
|
|
78796
|
-
/**
|
|
78797
|
-
* Lists upcoming tasks that are ready to run (no authoring placeholders).
|
|
78798
|
-
*/
|
|
78799
|
-
function listUpcomingTasks(files, priorityFilter = {}, promptRunnerIdentity) {
|
|
78800
|
-
return listRunnablePrompts(files, priorityFilter, promptRunnerIdentity).map(({ file, section }) => ({
|
|
78801
|
-
label: buildPromptLabelForDisplay(file, section),
|
|
78802
|
-
summary: buildPromptSummary(file, section),
|
|
78803
|
-
priority: section.priority,
|
|
78804
|
-
}));
|
|
78805
|
-
}
|
|
78806
|
-
|
|
78807
|
-
/**
|
|
78808
|
-
* Groups upcoming tasks by priority, high to low.
|
|
78809
|
-
*/
|
|
78810
|
-
function groupUpcomingTasksByPriority(tasks) {
|
|
78811
|
-
const grouped = new Map();
|
|
78812
|
-
for (const task of tasks) {
|
|
78813
|
-
const group = grouped.get(task.priority);
|
|
78814
|
-
if (group) {
|
|
78815
|
-
group.push(task);
|
|
78816
|
-
}
|
|
78817
|
-
else {
|
|
78818
|
-
grouped.set(task.priority, [task]);
|
|
78819
|
-
}
|
|
78820
|
-
}
|
|
78821
|
-
return Array.from(grouped.entries())
|
|
78822
|
-
.sort((a, b) => b[0] - a[0])
|
|
78823
|
-
.map(([priority, groupedTasks]) => ({ priority, tasks: groupedTasks }));
|
|
78824
|
-
}
|
|
78825
|
-
|
|
78826
|
-
/**
|
|
78827
|
-
* Prints upcoming tasks grouped by priority.
|
|
78828
|
-
*/
|
|
78829
|
-
function printUpcomingTasks(tasks) {
|
|
78830
|
-
if (tasks.length === 0) {
|
|
78831
|
-
console.info(colors.green('No upcoming tasks.'));
|
|
78832
|
-
return;
|
|
78833
|
-
}
|
|
78834
|
-
console.info(colors.cyan('Upcoming tasks (grouped by priority):'));
|
|
78835
|
-
for (const group of groupUpcomingTasksByPriority(tasks)) {
|
|
78836
|
-
console.info(colors.cyan(`Priority ${group.priority}:`));
|
|
78837
|
-
for (const [index, task] of group.tasks.entries()) {
|
|
78838
|
-
const summary = task.summary ? ` - ${task.summary}` : '';
|
|
78839
|
-
console.info(` ${index + 1}. ${task.label}${summary}`);
|
|
78840
|
-
}
|
|
78841
|
-
}
|
|
78842
|
-
}
|
|
78843
|
-
|
|
78844
79846
|
/**
|
|
78845
79847
|
* Builds a label that identifies the prompt section by index.
|
|
78846
79848
|
*/
|
|
@@ -79079,12 +80081,23 @@ const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-
|
|
|
79079
80081
|
* @public exported from `@promptbook/cli`
|
|
79080
80082
|
*/
|
|
79081
80083
|
async function runCodexPrompts(providedOptions) {
|
|
80084
|
+
var _a;
|
|
79082
80085
|
const options = normalizeRunOptions(providedOptions !== null && providedOptions !== void 0 ? providedOptions : parseRunOptions(process.argv.slice(2)));
|
|
79083
80086
|
validateRunCodexPromptOptions(options);
|
|
79084
80087
|
resetCoderRunControls();
|
|
79085
80088
|
const runStartDate = moment();
|
|
79086
80089
|
const { isRichUiEnabled, progressDisplay, uiHandle } = createRunDisplays(options, runStartDate);
|
|
79087
|
-
const waitForRequestedPause = createPauseWaiter({
|
|
80090
|
+
const waitForRequestedPause = createPauseWaiter({
|
|
80091
|
+
isRichUiEnabled,
|
|
80092
|
+
progressDisplay,
|
|
80093
|
+
uiHandle,
|
|
80094
|
+
// Note: Every pause checkpoint of the whole run goes through this one waiter, so watching the free disk
|
|
80095
|
+
// space here covers each round, each verification and each runner without repeating the check
|
|
80096
|
+
guardFreeDiskSpace: createFreeDiskSpaceGuard({
|
|
80097
|
+
inspectedPath: process.cwd(),
|
|
80098
|
+
isAskingQuestionsEnabled: (_a = options.isAskingQuestionsEnabled) !== null && _a !== void 0 ? _a : true,
|
|
80099
|
+
}),
|
|
80100
|
+
});
|
|
79088
80101
|
startPauseListenerIfNeeded(isRichUiEnabled);
|
|
79089
80102
|
try {
|
|
79090
80103
|
const resolvedCoderContext = await resolveCoderContext(options.context, process.cwd());
|
|
@@ -79097,6 +80110,7 @@ async function runCodexPrompts(providedOptions) {
|
|
|
79097
80110
|
const promptRunnerIdentity = {
|
|
79098
80111
|
harnessName: options.agentName,
|
|
79099
80112
|
modelName: actualRunnerModel,
|
|
80113
|
+
agentReferences: resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentReferences,
|
|
79100
80114
|
};
|
|
79101
80115
|
console.info(colors.green(`Running prompts with ${runner.name}`));
|
|
79102
80116
|
initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
|
|
@@ -79533,11 +80547,14 @@ async function commitTestBeforeChangesIfNeeded(options) {
|
|
|
79533
80547
|
* Creates a pause waiter that keeps the progress display and rich UI in sync.
|
|
79534
80548
|
*/
|
|
79535
80549
|
function createPauseWaiter(options) {
|
|
79536
|
-
const { isRichUiEnabled, progressDisplay, uiHandle } = options;
|
|
80550
|
+
const { isRichUiEnabled, progressDisplay, uiHandle, guardFreeDiskSpace } = options;
|
|
79537
80551
|
return async (checkpoint) => {
|
|
79538
80552
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase(checkpoint.phase);
|
|
79539
80553
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(checkpoint.statusMessage);
|
|
79540
80554
|
announcePauseTargetLabel(checkpoint.checkpointLabel);
|
|
80555
|
+
// Note: Runs after the checkpoint label is announced, so a pause requested because of a full disk
|
|
80556
|
+
// replaces that label and says why the run is standing still
|
|
80557
|
+
await guardFreeDiskSpace();
|
|
79541
80558
|
await checkPause({
|
|
79542
80559
|
silent: isRichUiEnabled,
|
|
79543
80560
|
onPaused: () => {
|
|
@@ -79656,7 +80673,7 @@ function finishWhenNoPromptIsAvailable(promptQueueSnapshot, isRichUiEnabled, uiH
|
|
|
79656
80673
|
return false;
|
|
79657
80674
|
}
|
|
79658
80675
|
if (promptQueueSnapshot.stats.forAgent > 0) {
|
|
79659
|
-
announceRunCompletion('No prompts match the selected harness or
|
|
80676
|
+
announceRunCompletion('No prompts match the selected harness, model or agent.', colors.yellow, isRichUiEnabled, uiHandle);
|
|
79660
80677
|
}
|
|
79661
80678
|
else if (promptQueueSnapshot.stats.toBeWritten > 0) {
|
|
79662
80679
|
announceRunCompletion('No prompts ready for agent.', colors.yellow, isRichUiEnabled, uiHandle);
|
|
@@ -79701,7 +80718,7 @@ function finishWhenEndAfterCurrentPromptIsRequested(options) {
|
|
|
79701
80718
|
function announceKeepAliveStatus(promptQueueSnapshot, isRichUiEnabled, uiHandle) {
|
|
79702
80719
|
let message;
|
|
79703
80720
|
if (promptQueueSnapshot.stats.forAgent > 0) {
|
|
79704
|
-
message = 'No prompts match the selected harness or
|
|
80721
|
+
message = 'No prompts match the selected harness, model or agent. Watching for changes...';
|
|
79705
80722
|
}
|
|
79706
80723
|
else if (promptQueueSnapshot.stats.toBeWritten > 0) {
|
|
79707
80724
|
message = 'No prompts ready for agent. Watching for changes...';
|