@promptbook/cli 0.114.0-2 → 0.114.0-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/apps/agents-server/next.config.ts +11 -0
  2. package/apps/agents-server/src/app/layout.tsx +6 -0
  3. package/esm/index.es.js +677 -199
  4. package/esm/index.es.js.map +1 -1
  5. package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
  6. package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
  7. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
  8. package/esm/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
  9. package/esm/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
  10. package/esm/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
  11. package/esm/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
  12. package/esm/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
  13. package/esm/scripts/verify-prompts/verify-prompts.d.ts +5 -0
  14. package/esm/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
  15. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
  16. package/esm/src/cli/cli-commands/coder/ping.d.ts +2 -5
  17. package/esm/src/cli/cli-commands/coder.d.ts +1 -1
  18. package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
  19. package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
  20. package/esm/src/cli/common/loadPromptsModule.d.ts +16 -0
  21. package/esm/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
  22. package/esm/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
  23. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
  24. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
  25. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
  26. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
  27. package/esm/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
  28. package/esm/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
  29. package/esm/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
  30. package/esm/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
  31. package/esm/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
  32. package/esm/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
  33. package/esm/src/version.d.ts +1 -1
  34. package/package.json +1 -1
  35. package/src/book-3.0/LiteAgent.ts +11 -9
  36. package/src/book-components/Chat/utils/$provideServerDomWindow.ts +77 -0
  37. package/src/book-components/Chat/utils/renderMarkdown.ts +2 -23
  38. package/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.ts +14 -0
  39. package/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.ts +1 -0
  40. package/src/cli/cli-commands/coder/add.ts +40 -12
  41. package/src/cli/cli-commands/coder/generate-boilerplates.ts +31 -5
  42. package/src/cli/cli-commands/coder/init.ts +27 -1
  43. package/src/cli/cli-commands/coder/ping.ts +23 -45
  44. package/src/cli/cli-commands/coder/verify.ts +27 -12
  45. package/src/cli/cli-commands/coder.ts +3 -3
  46. package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +78 -0
  47. package/src/cli/cli-commands/run/prepareRunCommandResources.ts +2 -1
  48. package/src/cli/cli-commands/run/resolveRunInputParameters.ts +2 -1
  49. package/src/cli/cli-commands/runInteractiveChatbot.ts +2 -1
  50. package/src/cli/common/$provideLlmToolsForCli.ts +2 -1
  51. package/src/cli/common/loadPromptsModule.ts +13 -0
  52. package/src/conversion/archive/loadArchive.ts +2 -1
  53. package/src/conversion/archive/loadJsZipModule.ts +10 -0
  54. package/src/conversion/archive/saveArchive.ts +2 -1
  55. package/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.ts +13 -1
  56. package/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.ts +13 -1
  57. package/src/llm-providers/openai/OpenAiAgentKitExecutionTools.ts +6 -2
  58. package/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.ts +7 -5
  59. package/src/llm-providers/openai/OpenAiAssistantExecutionTools.ts +1 -1
  60. package/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.ts +1 -1
  61. package/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.ts +1 -1
  62. package/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.ts +1 -1
  63. package/src/llm-providers/openai/OpenAiCompatibleRequestManager.ts +12 -1
  64. package/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.ts +1 -1
  65. package/src/llm-providers/openai/utils/loadOpenAiAgentsModule.ts +11 -0
  66. package/src/llm-providers/openai/utils/uploadFilesToOpenAi.ts +1 -1
  67. package/src/other/templates/getTemplatesPipelineCollection.ts +678 -967
  68. package/src/remote-server/createRemoteClient.ts +12 -1
  69. package/src/scrapers/website/WebsiteScraper.ts +22 -2
  70. package/src/utils/misc/createLazyModuleLoader.ts +27 -0
  71. package/src/version.ts +2 -2
  72. package/src/versions.txt +1 -0
  73. package/umd/index.umd.js +750 -266
  74. package/umd/index.umd.js.map +1 -1
  75. package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
  76. package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
  77. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
  78. package/umd/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
  79. package/umd/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
  80. package/umd/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
  81. package/umd/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
  82. package/umd/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
  83. package/umd/scripts/verify-prompts/verify-prompts.d.ts +5 -0
  84. package/umd/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
  85. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
  86. package/umd/src/cli/cli-commands/coder/ping.d.ts +2 -5
  87. package/umd/src/cli/cli-commands/coder.d.ts +1 -1
  88. package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
  89. package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
  90. package/umd/src/cli/common/loadPromptsModule.d.ts +16 -0
  91. package/umd/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
  92. package/umd/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
  93. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
  94. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
  95. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
  96. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
  97. package/umd/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
  98. package/umd/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
  99. package/umd/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
  100. package/umd/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
  101. package/umd/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
  102. package/umd/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
  103. package/umd/src/version.d.ts +1 -1
  104. package/esm/scripts/run-agent-chat/runCoderPing.d.ts +0 -48
  105. package/umd/scripts/run-agent-chat/runCoderPing.d.ts +0 -48
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, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, access, constants, watch, rmdir, mkdtemp } from 'fs/promises';
4
+ import { writeFile, stat, mkdir, readFile, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, 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 } from 'child_process';
@@ -12,13 +12,8 @@ import * as dotenv from 'dotenv';
12
12
  import * as readline from 'readline';
13
13
  import { emitKeypressEvents, clearLine, cursorTo, createInterface } from 'readline';
14
14
  import { forTime, forEver } from 'waitasecond';
15
- import prompts from 'prompts';
16
15
  import hexEncoder from 'crypto-js/enc-hex';
17
16
  import sha256 from 'crypto-js/sha256';
18
- import { io } from 'socket.io-client';
19
- import JSZip from 'jszip';
20
- import { Readability } from '@mozilla/readability';
21
- import { JSDOM } from 'jsdom';
22
17
  import CryptoJS from 'crypto-js';
23
18
  import showdown from 'showdown';
24
19
  import glob from 'glob-promise';
@@ -29,19 +24,13 @@ import * as OpenApiValidator from 'express-openapi-validator';
29
24
  import swaggerUi from 'swagger-ui-express';
30
25
  import { createElement } from 'react';
31
26
  import { renderToStaticMarkup } from 'react-dom/server';
32
- import Anthropic from '@anthropic-ai/sdk';
33
27
  import Bottleneck from 'bottleneck';
34
- import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
35
28
  import { Subject, BehaviorSubject } from 'rxjs';
36
- import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
37
- import OpenAI from 'openai';
38
- import * as ts from 'typescript';
39
29
  import ignore from 'ignore';
40
- import { tmpdir } from 'os';
41
30
  import { EventEmitter } from 'events';
31
+ import { tmpdir } from 'os';
42
32
  import { lookup, extension } from 'mime-types';
43
33
  import papaparse from 'papaparse';
44
- import { Client } from 'pg';
45
34
  import '@supabase/supabase-js';
46
35
  import { pathToFileURL } from 'url';
47
36
 
@@ -59,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
59
48
  * @generated
60
49
  * @see https://github.com/webgptorg/promptbook
61
50
  */
62
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-2';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-3';
63
52
  /**
64
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
65
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3293,6 +3282,12 @@ const PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV = 'PTBK_AGENTS_SERVER_BUILD_WORK
3293
3282
  * @private internal constant of `buildAgentsServer`
3294
3283
  */
3295
3284
  const PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV = 'PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION';
3285
+ /**
3286
+ * Environment variable that disables throwaway webpack filesystem caches for CLI-owned production builds.
3287
+ *
3288
+ * @private internal constant of `buildAgentsServer`
3289
+ */
3290
+ const PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV = 'PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE';
3296
3291
  /**
3297
3292
  * Conservative Next.js build worker count used by CLI-owned Agents Server production builds.
3298
3293
  *
@@ -3317,6 +3312,11 @@ function createAgentsServerRuntimeEnvironment(environment, nodeModulesPath, opti
3317
3312
  [PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
3318
3313
  }
3319
3314
  : {}),
3315
+ ...(options.isWebpackFilesystemCacheDisabled
3316
+ ? {
3317
+ [PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV]: 'true',
3318
+ }
3319
+ : {}),
3320
3320
  };
3321
3321
  }
3322
3322
  /**
@@ -4050,6 +4050,7 @@ async function ensureAgentsServerBuild(options = {}) {
4050
4050
  });
4051
4051
  const buildEnvironment = createAgentsServerRuntimeEnvironment(environment, preparedRuntime.nodeModulesPath, {
4052
4052
  isNextValidationIgnored: preparedRuntime.isAppPathMaterialized,
4053
+ isWebpackFilesystemCacheDisabled: true,
4053
4054
  });
4054
4055
  if (!options.isBuildForced &&
4055
4056
  (await isAgentsServerBuildCacheCurrent({
@@ -28226,6 +28227,13 @@ function toPosixPath(filePath) {
28226
28227
  * Environment variable read by the shell wrapper to tee live output into the temporary runtime log file.
28227
28228
  */
28228
28229
  const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
28230
+ /**
28231
+ * Log line which separates the raw script input from the raw script output of one execution section.
28232
+ *
28233
+ * Readers of a runtime log split on this marker to look only at what the harness really produced,
28234
+ * without the generated script and the prompt it embeds.
28235
+ */
28236
+ const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
28229
28237
  /**
28230
28238
  * Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
28231
28239
  */
@@ -28261,7 +28269,7 @@ async function appendScriptExecutionLogStart({ scriptPath, scriptContent, logPat
28261
28269
  --- raw input ---
28262
28270
  ${block(normalizedInput)}
28263
28271
 
28264
- --- raw output ---
28272
+ ${SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER}
28265
28273
  `);
28266
28274
  await appendFile(logPath, `${logSection}\n`, 'utf-8');
28267
28275
  }
@@ -32938,8 +32946,12 @@ function readOptionalSigningKeyValue() {
32938
32946
  * `options.includePaths` can restrict staging, `options.onlyPaths` can restrict the commit pathspec,
32939
32947
  * `options.excludePaths` can keep temporary artifacts out of the created commit and
32940
32948
  * `options.isEmptyCommitAllowed` keeps a round without any file change from failing.
32949
+ *
32950
+ * Note: The temporary commit message file is written inside the project, so it is always excluded from the commit
32951
+ * itself for projects which do not keep the Promptbook temporary directory out of version control.
32941
32952
  */
32942
32953
  async function commitChanges(message, options) {
32954
+ var _a;
32943
32955
  const projectPath = (options === null || options === void 0 ? void 0 : options.projectPath) || process.cwd();
32944
32956
  const commitMessagePath = resolvePromptbookTemporaryPath(projectPath, 'ptbk-coder', 'commit-messages', `COMMIT_MESSAGE_${Date.now()}.txt`);
32945
32957
  await mkdir(dirname(commitMessagePath), { recursive: true });
@@ -32947,7 +32959,10 @@ async function commitChanges(message, options) {
32947
32959
  try {
32948
32960
  const agentEnv = buildAgentGitEnv();
32949
32961
  const signingFlag = buildAgentGitSigningFlag();
32950
- await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, options === null || options === void 0 ? void 0 : options.excludePaths);
32962
+ await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.includePaths, [
32963
+ commitMessagePath,
32964
+ ...((_a = options === null || options === void 0 ? void 0 : options.excludePaths) !== null && _a !== void 0 ? _a : []),
32965
+ ]);
32951
32966
  await runGitCommand({
32952
32967
  command: buildGitCommitCommand({
32953
32968
  commitMessagePath,
@@ -42287,6 +42302,91 @@ function $initializeAgentsServerCommand(program) {
42287
42302
  // Note: [🟡] Code for CLI command [agents-server](src/cli/cli-commands/agents-server.ts) should never be published outside of `@promptbook/cli`
42288
42303
  // Note: [💞] Ignore a discrepancy between file name and entity name
42289
42304
 
42305
+ /**
42306
+ * Creates a loader which imports one module on the first call and reuses the very same module afterwards
42307
+ *
42308
+ * Note: [🐌] Heavy third-party dependencies are imported lazily to keep the startup of the Promptbook CLI fast.
42309
+ * A statically imported dependency is loaded every single time the bundle is loaded, even when the running
42310
+ * command never touches it. A lazily imported dependency is loaded only when the feature is really used.
42311
+ *
42312
+ * @example
42313
+ * const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
42314
+ * const { JSDOM } = await loadJsdomModule();
42315
+ *
42316
+ * @private internal utility of Promptbook
42317
+ */
42318
+ function createLazyModuleLoader(importModule) {
42319
+ let importedModulePromise = null;
42320
+ return function loadModule() {
42321
+ if (importedModulePromise === null) {
42322
+ importedModulePromise = importModule();
42323
+ }
42324
+ return importedModulePromise;
42325
+ };
42326
+ }
42327
+ // Note: [🐌] Do not convert the lazy `import(...)` calls back to static `import` statements, it would bring back the
42328
+ // slow startup of the `ptbk` CLI utility
42329
+
42330
+ /**
42331
+ * Loads the interactive terminal prompt library (`prompts`) on demand
42332
+ *
42333
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast - most commands never ask the user
42334
+ * anything interactively
42335
+ *
42336
+ * @private internal utility of Promptbook CLI
42337
+ */
42338
+ const loadPromptsModule = createLazyModuleLoader(() => import('prompts'));
42339
+ // Note: [🟡] Code for CLI prompt loading [loadPromptsModule](src/cli/common/loadPromptsModule.ts) should never be published outside of `@promptbook/cli`
42340
+
42341
+ /**
42342
+ * Description block shared by the `ptbk coder` commands which can synchronize their changes with git.
42343
+ *
42344
+ * @private internal utility of `promptbookCli`
42345
+ */
42346
+ const CODER_GIT_SYNC_DESCRIPTION = spaceTrim$1(`
42347
+ Git synchronization:
42348
+ - --auto-pull pulls the latest changes before this command changes anything
42349
+ - --commit commits the changes made by this command
42350
+ - --auto-push pushes the created commit to the remote repository
42351
+ `);
42352
+ /**
42353
+ * Registers the shared `--commit`, `--auto-push` and `--auto-pull` flags on a `ptbk coder` command.
42354
+ *
42355
+ * Note: Unlike `ptbk coder run`, which commits by default and opts out through `--no-commit`,
42356
+ * these commands never touch git unless the flags are used explicitly.
42357
+ *
42358
+ * @private internal utility of `promptbookCli`
42359
+ */
42360
+ function addCoderGitSyncOptions(command) {
42361
+ command.option('--commit', 'Commit the changes made by this command with the coding-agent git identity', false);
42362
+ command.option('--auto-push', 'Automatically git push the created commit, requires --commit', false);
42363
+ command.option('--auto-pull', 'Automatically git pull the latest changes before this command changes anything', false);
42364
+ }
42365
+ /**
42366
+ * Converts the Commander git synchronization flags into normalized git synchronization options.
42367
+ *
42368
+ * @private internal utility of `promptbookCli`
42369
+ */
42370
+ function normalizeCoderGitSyncCliOptions(cliOptions) {
42371
+ if (cliOptions.autoPush && !cliOptions.commit) {
42372
+ throw new NotAllowed(spaceTrim$1(`
42373
+ Flag \`--auto-push\` can be used only together with \`--commit\`.
42374
+
42375
+ **There is nothing to push when the changes are not committed.**
42376
+
42377
+ Actionable hint:
42378
+ - Add \`--commit\`, for example \`ptbk coder init --commit --auto-push\`.
42379
+ `));
42380
+ }
42381
+ return {
42382
+ isCommitEnabled: cliOptions.commit,
42383
+ isAutoPushEnabled: cliOptions.autoPush,
42384
+ isAutoPullEnabled: cliOptions.autoPull,
42385
+ };
42386
+ }
42387
+ // Note: [🟡] Code for CLI git synchronization options [coderGitSyncCliOptions](src/cli/cli-commands/common/coderGitSyncCliOptions.ts) should never be published outside of `@promptbook/cli`
42388
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names
42389
+
42290
42390
  /**
42291
42391
  * Relative path to the root prompts directory used by Promptbook coder utilities.
42292
42392
  *
@@ -42540,15 +42640,17 @@ const FALLBACK_PROMPT_SLUG = 'prompt';
42540
42640
  */
42541
42641
  function $initializeCoderAddCommand(program) {
42542
42642
  const command = program.command('add');
42543
- command.description(spaceTrim$1(`
42544
- Add one ready-to-run prompt file to the queue
42643
+ command.description(spaceTrim$1((block) => `
42644
+ Add one ready-to-run prompt file to the queue
42545
42645
 
42546
- Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42547
- - \`ptbk coder add "some new feature"\`
42548
- - \`ptbk coder add --priority 1 "some new feature"\`
42549
- - \`ptbk coder add <<EOF ... EOF\`
42550
- - \`ptbk coder add\`
42551
- `));
42646
+ Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
42647
+ - \`ptbk coder add "some new feature"\`
42648
+ - \`ptbk coder add --priority 1 "some new feature"\`
42649
+ - \`ptbk coder add <<EOF ... EOF\`
42650
+ - \`ptbk coder add\`
42651
+
42652
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
42653
+ `));
42552
42654
  command.argument('[description]', 'Plain-language description of the feature or task to implement');
42553
42655
  command.option('--priority <priority>', 'Priority of the new prompt — higher priorities run first (rendered as trailing `!` markers)', parsePriorityOption, 0);
42554
42656
  command.option('--template <template>', spaceTrim$1(`
@@ -42558,15 +42660,26 @@ function $initializeCoderAddCommand(program) {
42558
42660
  .map(({ id }) => id)
42559
42661
  .join(', ')}) or a markdown file path relative to the current project root.
42560
42662
  `));
42663
+ addCoderGitSyncOptions(command);
42561
42664
  command.action(handleActionErrors(async (descriptionArgument, cliOptions) => {
42562
42665
  const { priority, template: templateOption } = cliOptions;
42666
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
42667
+ const projectPath = process.cwd();
42563
42668
  const description = await resolveCoderPromptDescription(descriptionArgument);
42564
- await addCoderPrompt({
42565
- projectPath: process.cwd(),
42669
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
42670
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
42671
+ await $pullCoderChanges({ gitSync, projectPath });
42672
+ const { /* filePath,*/ emojiTag } = await addCoderPrompt({
42673
+ projectPath,
42566
42674
  description,
42567
42675
  priority,
42568
42676
  templateOption,
42569
42677
  });
42678
+ await $commitCoderChanges({
42679
+ gitSync,
42680
+ projectPath,
42681
+ commitMessage: `${emojiTag} Add prompt`,
42682
+ });
42570
42683
  }));
42571
42684
  }
42572
42685
  /**
@@ -42640,6 +42753,7 @@ async function resolveCoderPromptDescription(descriptionArgument) {
42640
42753
  }
42641
42754
  return standardInputDescription;
42642
42755
  }
42756
+ const { default: prompts } = await loadPromptsModule();
42643
42757
  const response = await prompts({
42644
42758
  type: 'text',
42645
42759
  name: 'description',
@@ -43060,9 +43174,11 @@ function parseIntOption(value) {
43060
43174
  */
43061
43175
  function $initializeCoderGenerateBoilerplatesCommand(program) {
43062
43176
  const command = program.command('generate-boilerplates');
43063
- command.description(spaceTrim$1(`
43064
- Generate prompt boilerplate files with unique emoji tags
43065
- `));
43177
+ command.description(spaceTrim$1((block) => `
43178
+ Generate prompt boilerplate files with unique emoji tags
43179
+
43180
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
43181
+ `));
43066
43182
  command.option('--count <count>', `Number of prompt boilerplate files to generate`, '5');
43067
43183
  command.option('--template <template>', spaceTrim$1(`
43068
43184
  Prompt template to use.
@@ -43071,14 +43187,25 @@ function $initializeCoderGenerateBoilerplatesCommand(program) {
43071
43187
  .map(({ id }) => id)
43072
43188
  .join(', ')}) or a markdown file path relative to the current project root.
43073
43189
  `));
43190
+ addCoderGitSyncOptions(command);
43074
43191
  command.action(handleActionErrors(async (cliOptions) => {
43075
43192
  const { count: countOption, template: templateOption } = cliOptions;
43076
43193
  const filesCount = parseFilesCount(countOption);
43194
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
43195
+ const projectPath = process.cwd();
43196
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
43197
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
43198
+ await $pullCoderChanges({ gitSync, projectPath });
43077
43199
  await generatePromptBoilerplate({
43078
- projectPath: process.cwd(),
43200
+ projectPath,
43079
43201
  filesCount,
43080
43202
  templateOption,
43081
43203
  });
43204
+ await $commitCoderChanges({
43205
+ gitSync,
43206
+ projectPath,
43207
+ commitMessage: `Prompts ${filesCount}x`,
43208
+ });
43082
43209
  return process.exit(0);
43083
43210
  }));
43084
43211
  }
@@ -44054,12 +44181,24 @@ function $initializeCoderInitCommand(program) {
44054
44181
 
44055
44182
  Checks that the coding harnesses are installed globally and up to date:
44056
44183
  ${block(listCheckedHarnessLabels())}
44184
+
44185
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44057
44186
  `));
44058
- command.action(handleActionErrors(async () => {
44187
+ addCoderGitSyncOptions(command);
44188
+ command.action(handleActionErrors(async (cliOptions) => {
44189
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44059
44190
  const projectPath = process.cwd();
44191
+ // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
44192
+ const { $commitCoderChanges, $pullCoderChanges } = await Promise.resolve().then(function () { return coderGitSync; });
44193
+ await $pullCoderChanges({ gitSync, projectPath });
44060
44194
  const summary = await initializeCoderProjectConfiguration(projectPath);
44061
44195
  printInitializationSummary(summary);
44062
44196
  await generatePromptBoilerplate({ projectPath, filesCount: 5 });
44197
+ await $commitCoderChanges({
44198
+ gitSync,
44199
+ projectPath,
44200
+ commitMessage: 'Initialize Promptbook Coder',
44201
+ });
44063
44202
  await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
44064
44203
  }));
44065
44204
  }
@@ -44081,62 +44220,44 @@ function listDefaultCoderProjectPromptTemplateDisplayPaths() {
44081
44220
  // Note: [💞] Ignore a discrepancy between file name and entity name
44082
44221
 
44083
44222
  /**
44084
- * Initializes `coder ping` command for Promptbook CLI utilities.
44223
+ * Initializes `coder ping` command for Promptbook CLI utilities
44085
44224
  *
44086
- * The command makes one small, isolated harness/model call, prints its result
44087
- * and reports how long the harness/model turn took.
44088
- *
44089
- * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI.
44225
+ * Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
44090
44226
  *
44091
44227
  * @private internal function of `promptbookCli`
44092
44228
  */
44093
44229
  function $initializeCoderPingCommand(program) {
44094
44230
  const command = program.command('ping');
44095
44231
  command.description(spaceTrim$1(`
44096
- Test one harness and model connection with a small disposable task
44232
+ Send one tiny dummy prompt to a harness and model to measure and warm them up
44097
44233
 
44098
44234
  ${PROMPT_RUNNER_DESCRIPTION}
44099
44235
 
44100
44236
  Features:
44101
- - Runs the dummy task in a temporary directory outside the current project
44102
- - Prints the result returned by the selected harness and model
44103
- - Reports the elapsed harness/model response time in milliseconds
44104
- - Can be used to start consuming an applicable harness/model quota before a longer run
44237
+ - Verifies that the selected harness, model, thinking level and authentication really work
44238
+ - Reports the answer of the harness, the response time and the reported usage
44239
+ - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44240
+ - Leaves the project exactly as it was nothing is read, written, changed or committed
44241
+ - Use --no-ui to stream the raw harness output instead of only the compact result
44105
44242
  `));
44106
44243
  addPromptRunnerSelectionOptions(command);
44107
44244
  addPromptRunnerRuntimeOptions(command);
44108
44245
  command.action(handleActionErrors(async (cliOptions) => {
44109
- const normalizedOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44110
- const agentName = normalizedOptions.agentName;
44111
- if (!agentName) {
44112
- throw new NotAllowed(spaceTrim$1(`
44113
- A harness is required for \`ptbk coder ping\`.
44114
-
44115
- Pass one with \`--harness <harness-name>\`.
44116
- `));
44117
- }
44118
- await $ensureHarnessInstallations([agentName]);
44119
- // Note: Import the runner-backed implementation dynamically to avoid loading heavy dependencies until needed.
44120
- const { runCoderPing } = await Promise.resolve().then(function () { return runCoderPing$1; });
44121
- const pingResult = await runCoderPing({
44122
- agentName,
44123
- model: normalizedOptions.model,
44124
- thinkingLevel: normalizedOptions.thinkingLevel,
44125
- isUiDisabled: normalizedOptions.noUi,
44126
- isCreditsAllowed: normalizedOptions.allowCredits,
44246
+ const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
44247
+ await $ensureHarnessInstallations([runnerOptions.agentName]);
44248
+ // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
44249
+ const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
44250
+ const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
44251
+ const result = await pingCoderHarness({
44252
+ agentName: runnerOptions.agentName,
44253
+ model: runnerOptions.model,
44254
+ thinkingLevel: runnerOptions.thinkingLevel,
44255
+ allowCredits: runnerOptions.allowCredits,
44256
+ shouldPrintLiveOutput: runnerOptions.noUi,
44127
44257
  });
44128
- printCoderPingResult(pingResult);
44258
+ printCoderPingResult(result);
44129
44259
  }));
44130
44260
  }
44131
- /**
44132
- * Prints the result and duration of one completed coder ping.
44133
- */
44134
- function printCoderPingResult(pingResult) {
44135
- console.info(spaceTrim$1(`
44136
- Result: ${pingResult.result}
44137
- Time: ${pingResult.elapsedTimeMs.toFixed(0)} ms
44138
- `));
44139
- }
44140
44261
  // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
44141
44262
  // Note: [💞] Ignore a discrepancy between file name and entity name
44142
44263
 
@@ -44507,25 +44628,31 @@ function normalizeCommandOptionValue(value) {
44507
44628
  */
44508
44629
  function $initializeCoderVerifyCommand(program) {
44509
44630
  const command = program.command('verify');
44510
- command.description(spaceTrim$1(`
44511
- Interactive verification helper for completed prompts
44631
+ command.description(spaceTrim$1((block) => `
44632
+ Interactive verification helper for completed prompts
44512
44633
 
44513
- Features:
44514
- - Displays list of prompt files with status counts
44515
- - Guides through verification of completed prompts marked [x]
44516
- - Archives verified prompt files to prompts/done/ directory
44517
- - Auto-appends repair prompts for incomplete work
44518
- - Processes files with all-done prompts first
44519
- - Supports ignoring matching prompt candidates for one verification run
44520
- `));
44634
+ Features:
44635
+ - Displays list of prompt files with status counts
44636
+ - Guides through verification of completed prompts marked [x]
44637
+ - Archives verified prompt files to prompts/done/ directory
44638
+ - Auto-appends repair prompts for incomplete work
44639
+ - Processes files with all-done prompts first
44640
+ - Supports ignoring matching prompt candidates for one verification run
44641
+
44642
+ ${block(CODER_GIT_SYNC_DESCRIPTION)}
44643
+
44644
+ Note: The git synchronization is applied around each single verification, not once per run.
44645
+ `));
44521
44646
  command.option('--reverse', 'Process prompt files in reverse order', false);
44522
44647
  command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
44648
+ addCoderGitSyncOptions(command);
44523
44649
  command.action(handleActionErrors(async (cliOptions) => {
44524
44650
  const { reverse, ignore } = cliOptions;
44651
+ const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
44525
44652
  // Note: Import the main function dynamically to avoid loading heavy dependencies until needed
44526
44653
  const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
44527
44654
  try {
44528
- await verifyPrompts({ reverse, ignore });
44655
+ await verifyPrompts({ reverse, ignore, gitSync });
44529
44656
  }
44530
44657
  catch (error) {
44531
44658
  console.error(colors.bgRed('Prompt verification failed:'), error);
@@ -44553,8 +44680,8 @@ function collectStringOption(value, previousValues) {
44553
44680
  * - add: Add one ready-to-run prompt file to the queue
44554
44681
  * - generate-boilerplates: Generate prompt boilerplate files
44555
44682
  * - find-refactor-candidates: Find files that need refactoring
44556
- * - ping: Test one harness and model connection with a disposable task
44557
44683
  * - run: Run coding prompts with AI agents
44684
+ * - ping: Test one harness and model with a tiny dummy prompt
44558
44685
  * - verify: Verify completed prompts
44559
44686
  * - find-fresh-emoji-tags: Find unused emoji tags
44560
44687
  *
@@ -44573,8 +44700,8 @@ function $initializeCoderCommand(program) {
44573
44700
  - generate-boilerplates: Generate prompt boilerplate files
44574
44701
  - find-refactor-candidates: Find files that need refactoring
44575
44702
  - find-unwritten: List prompt sections that still need to be authored
44576
- - ping: Test one harness and model connection with a disposable task
44577
44703
  - run: Run coding prompts with AI agents
44704
+ - ping: Test the connection, response time and quota of one harness and model
44578
44705
  - server: Start a long-running coder server with a kanban web UI
44579
44706
  - verify: Verify completed prompts
44580
44707
  - find-fresh-emoji-tags: Find unused emoji tags
@@ -44585,8 +44712,8 @@ function $initializeCoderCommand(program) {
44585
44712
  $initializeCoderGenerateBoilerplatesCommand(coderCommand);
44586
44713
  $initializeCoderFindRefactorCandidatesCommand(coderCommand);
44587
44714
  $initializeCoderFindUnwrittenCommand(coderCommand);
44588
- $initializeCoderPingCommand(coderCommand);
44589
44715
  $initializeCoderRunCommand(coderCommand);
44716
+ $initializeCoderPingCommand(coderCommand);
44590
44717
  $initializeCoderServerCommand(coderCommand);
44591
44718
  $initializeCoderVerifyCommand(coderCommand);
44592
44719
  $initializeCoderFindFreshEmojiTagCommand(coderCommand);
@@ -45420,6 +45547,14 @@ class FileCacheStorage {
45420
45547
  // Note: [🟢] Code for Node file-cache storage [FileCacheStorage](src/storage/file-cache-storage/FileCacheStorage.ts) should never be published into packages that could be imported into browser environment
45421
45548
  // TODO: [🌗] Maybe some checkers, not all valid JSONs are desired and valid values
45422
45549
 
45550
+ /**
45551
+ * Loads the Socket.io client (`socket.io-client`) on demand
45552
+ *
45553
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
45554
+ *
45555
+ * @private internal utility of `createRemoteClient`
45556
+ */
45557
+ const loadSocketIoClientModule = createLazyModuleLoader(() => import('socket.io-client'));
45423
45558
  /**
45424
45559
  * Creates a connection to the remote proxy server.
45425
45560
  *
@@ -45448,6 +45583,7 @@ async function createRemoteClient(options) {
45448
45583
 
45449
45584
  `));
45450
45585
  }
45586
+ const { io } = await loadSocketIoClientModule();
45451
45587
  return new Promise((resolve, reject) => {
45452
45588
  const socket = io(remoteServerUrl, {
45453
45589
  retries: CONNECTION_RETRIES_LIMIT,
@@ -46115,6 +46251,7 @@ async function $provideLlmToolsForCli(options) {
46115
46251
  You will be logged in to ${remoteServerUrl}
46116
46252
  If you don't have an account, it will be created automatically.
46117
46253
  `)));
46254
+ const { default: prompts } = await loadPromptsModule();
46118
46255
  const { username, password } = await prompts([
46119
46256
  {
46120
46257
  type: 'text',
@@ -46490,6 +46627,15 @@ function $initializeLoginCommand(program) {
46490
46627
  // TODO: Implement non-interactive login
46491
46628
  // Note: [💞] Ignore a discrepancy between file name and entity name
46492
46629
 
46630
+ /**
46631
+ * Loads the ZIP archive library (`jszip`) on demand
46632
+ *
46633
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
46634
+ *
46635
+ * @private internal utility of `loadArchive` and `saveArchive`
46636
+ */
46637
+ const loadJsZipModule = createLazyModuleLoader(() => import('jszip'));
46638
+
46493
46639
  /**
46494
46640
  * Loads the books from the archive file with `.bookc` extension
46495
46641
  *
@@ -46504,6 +46650,7 @@ async function loadArchive(filePath, fs) {
46504
46650
  throw new UnexpectedError(`Archive file must have '.bookc' extension`);
46505
46651
  }
46506
46652
  const data = await fs.readFile(filePath);
46653
+ const { default: JSZip } = await loadJsZipModule();
46507
46654
  const archive = await JSZip.loadAsync(data);
46508
46655
  const indexFile = archive.file('index.book.json');
46509
46656
  if (!indexFile) {
@@ -50567,7 +50714,23 @@ function createShowdownConverter() {
50567
50714
  });
50568
50715
  }
50569
50716
 
50570
- // TODO: [🏳‍🌈] Finally take pick of .json vs .ts
50717
+ /**
50718
+ * Loads `jsdom` on demand
50719
+ *
50720
+ * Note: [🐌] `jsdom` is by far the heaviest dependency of Promptbook, loading it eagerly would slow down every single
50721
+ * run of the `ptbk` CLI utility even when no website is scraped
50722
+ *
50723
+ * @private internal utility of `WebsiteScraper`
50724
+ */
50725
+ const loadJsdomModule = createLazyModuleLoader(() => import('jsdom'));
50726
+ /**
50727
+ * Loads `@mozilla/readability` on demand
50728
+ *
50729
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
50730
+ *
50731
+ * @private internal utility of `WebsiteScraper`
50732
+ */
50733
+ const loadReadabilityModule = createLazyModuleLoader(() => import('@mozilla/readability'));
50571
50734
  /**
50572
50735
  * Scraper for websites
50573
50736
  *
@@ -50603,6 +50766,7 @@ class WebsiteScraper {
50603
50766
  if (this.tools.fs === undefined) {
50604
50767
  throw new EnvironmentMismatchError('Can not scrape websites without filesystem tools');
50605
50768
  }
50769
+ const [{ JSDOM }, { Readability }] = await Promise.all([loadJsdomModule(), loadReadabilityModule()]);
50606
50770
  const jsdom = new JSDOM(await source.asText(), {
50607
50771
  url: source.url,
50608
50772
  });
@@ -54206,6 +54370,7 @@ async function saveArchive(filePath, collectionJson, fs) {
54206
54370
  for (const pipelineJson of collectionJson) {
54207
54371
  validatePipeline(pipelineJson);
54208
54372
  }
54373
+ const { default: JSZip } = await loadJsZipModule();
54209
54374
  const archive = new JSZip();
54210
54375
  const collectionJsonString = stringifyPipelineJson(collectionJson);
54211
54376
  archive.file('index.book.json', collectionJsonString);
@@ -54834,6 +54999,7 @@ async function runInteractiveChatbot(options) {
54834
54999
  else {
54835
55000
  console.info(colors.gray(`---`));
54836
55001
  }
55002
+ const { default: prompts } = await loadPromptsModule();
54837
55003
  const response = await prompts({
54838
55004
  type: 'text',
54839
55005
  name: 'userMessage',
@@ -55141,6 +55307,7 @@ async function resolveRunPipelineSource(pipelineSource) {
55141
55307
  if (pipelineSource) {
55142
55308
  return pipelineSource;
55143
55309
  }
55310
+ const { default: prompts } = await loadPromptsModule();
55144
55311
  const response = await prompts({
55145
55312
  type: 'text',
55146
55313
  name: 'pipelineSource',
@@ -55228,6 +55395,7 @@ async function resolveRunInputParameters(options) {
55228
55395
  console.error(colors.red(createRunMissingInputParametersMessage(pipeline, inputParameters, questions)));
55229
55396
  return process.exit(1);
55230
55397
  }
55398
+ const { default: prompts } = await loadPromptsModule();
55231
55399
  const response = await prompts(questions);
55232
55400
  // <- TODO: [🧠][🍼] Change behavior according to the formfactor
55233
55401
  return { ...inputParameters, ...response };
@@ -58133,6 +58301,14 @@ const ANTHROPIC_PROVIDER_PROFILE = {
58133
58301
  fullname: 'Anthropic Claude',
58134
58302
  color: '#d97706',
58135
58303
  };
58304
+ /**
58305
+ * Loads the Anthropic Claude SDK (`@anthropic-ai/sdk`) on demand
58306
+ *
58307
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58308
+ *
58309
+ * @private internal utility of `AnthropicClaudeExecutionTools`
58310
+ */
58311
+ const loadAnthropicClaudeModule = createLazyModuleLoader(() => import('@anthropic-ai/sdk'));
58136
58312
  /**
58137
58313
  * Execution Tools for calling Anthropic Claude API.
58138
58314
  *
@@ -58170,6 +58346,7 @@ class AnthropicClaudeExecutionTools {
58170
58346
  const anthropicOptions = { ...this.options };
58171
58347
  delete anthropicOptions.isVerbose;
58172
58348
  delete anthropicOptions.isProxied;
58349
+ const { Anthropic } = await loadAnthropicClaudeModule();
58173
58350
  this.client = new Anthropic(anthropicOptions);
58174
58351
  }
58175
58352
  return this.client;
@@ -58433,6 +58610,14 @@ const AZURE_OPENAI_PROVIDER_PROFILE = {
58433
58610
  fullname: 'Azure OpenAI',
58434
58611
  color: '#0078d4',
58435
58612
  };
58613
+ /**
58614
+ * Loads the Azure OpenAI SDK (`@azure/openai`) on demand
58615
+ *
58616
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
58617
+ *
58618
+ * @private internal utility of `AzureOpenAiExecutionTools`
58619
+ */
58620
+ const loadAzureOpenAiModule = createLazyModuleLoader(() => import('@azure/openai'));
58436
58621
  /**
58437
58622
  * Execution Tools for calling Azure OpenAI API.
58438
58623
  *
@@ -58466,6 +58651,7 @@ class AzureOpenAiExecutionTools {
58466
58651
  }
58467
58652
  async getClient() {
58468
58653
  if (this.client === null) {
58654
+ const { AzureKeyCredential, OpenAIClient } = await loadAzureOpenAiModule();
58469
58655
  this.client = new OpenAIClient(`https://${this.options.resourceName}.openai.azure.com/`, new AzureKeyCredential(this.options.apiKey));
58470
58656
  }
58471
58657
  return this.client;
@@ -60275,6 +60461,14 @@ class OpenAiCompatibleNonChatPromptCaller {
60275
60461
  }
60276
60462
  }
60277
60463
 
60464
+ /**
60465
+ * Loads the OpenAI SDK (`openai`) on demand
60466
+ *
60467
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
60468
+ *
60469
+ * @private internal utility of `OpenAiCompatibleRequestManager`
60470
+ */
60471
+ const loadOpenAiModule = createLazyModuleLoader(() => import('openai'));
60278
60472
  /**
60279
60473
  * Manages OpenAI-compatible client creation plus shared retry and rate-limit behavior.
60280
60474
  *
@@ -60301,6 +60495,7 @@ class OpenAiCompatibleRequestManager {
60301
60495
  timeout: API_REQUEST_TIMEOUT,
60302
60496
  maxRetries: CONNECTION_RETRIES_LIMIT,
60303
60497
  };
60498
+ const { default: OpenAI } = await loadOpenAiModule();
60304
60499
  this.client = new OpenAI(enhancedOptions);
60305
60500
  }
60306
60501
  return this.client;
@@ -68014,6 +68209,7 @@ async function runAgentChat(options) {
68014
68209
  if (options.isVerbose) {
68015
68210
  console.info(colors.gray('Type "exit" or "quit" to end the chat.'));
68016
68211
  }
68212
+ const { default: prompts } = await loadPromptsModule();
68017
68213
  while (true) {
68018
68214
  const response = await prompts({
68019
68215
  type: 'text',
@@ -69227,6 +69423,16 @@ class OpenAiAgentKitExecutionToolsOutputTypeMapper {
69227
69423
  }
69228
69424
  }
69229
69425
 
69426
+ /**
69427
+ * Loads the OpenAI AgentKit SDK (`@openai/agents`) on demand
69428
+ *
69429
+ * Note: [🐌] The AgentKit SDK is one of the heaviest dependencies of Promptbook, loading it eagerly would slow down
69430
+ * every single run of the `ptbk` CLI utility even when no AgentKit agent is used
69431
+ *
69432
+ * @private internal utility of `@promptbook/openai`
69433
+ */
69434
+ const loadOpenAiAgentsModule = createLazyModuleLoader(() => import('@openai/agents'));
69435
+
69230
69436
  /**
69231
69437
  * Constant for default model used for nested DeepSearch tool invocations.
69232
69438
  */
@@ -69280,8 +69486,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69280
69486
  /**
69281
69487
  * Builds the tool list for AgentKit, including hosted file search when applicable.
69282
69488
  */
69283
- buildAgentKitTools(options) {
69489
+ async buildAgentKitTools(options) {
69284
69490
  const { tools, vectorStoreId } = options;
69491
+ const { fileSearchTool, tool: agentKitTool } = await loadOpenAiAgentsModule();
69285
69492
  const agentKitTools = [];
69286
69493
  if (vectorStoreId) {
69287
69494
  agentKitTools.push(fileSearchTool(vectorStoreId));
@@ -69292,11 +69499,11 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69292
69499
  let scriptTools = null;
69293
69500
  for (const toolDefinition of tools) {
69294
69501
  if (this.isDeepSearchToolDefinition(toolDefinition)) {
69295
- agentKitTools.push(this.createDeepSearchAgentKitTool(toolDefinition));
69502
+ agentKitTools.push(await this.createDeepSearchAgentKitTool(toolDefinition));
69296
69503
  continue;
69297
69504
  }
69298
69505
  scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
69299
- agentKitTools.push(tool({
69506
+ agentKitTools.push(agentKitTool({
69300
69507
  name: toolDefinition.name,
69301
69508
  description: toolDefinition.description,
69302
69509
  parameters: this.normalizeAgentKitToolParameters(toolDefinition.parameters),
@@ -69503,8 +69710,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
69503
69710
  /**
69504
69711
  * Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
69505
69712
  */
69506
- createDeepSearchAgentKitTool(toolDefinition) {
69507
- const deepSearchAgent = new Agent$1({
69713
+ async createDeepSearchAgentKitTool(toolDefinition) {
69714
+ const { Agent: AgentFromKit, webSearchTool } = await loadOpenAiAgentsModule();
69715
+ const deepSearchAgent = new AgentFromKit({
69508
69716
  name: 'DeepSearch',
69509
69717
  model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
69510
69718
  instructions: this.createDeepSearchAgentInstructions(toolDefinition.description),
@@ -69717,8 +69925,9 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69717
69925
  vectorStoreId,
69718
69926
  });
69719
69927
  }
69720
- const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
69721
- const openAiAgentKitAgent = new Agent$1({
69928
+ const { Agent: AgentFromKit } = await loadOpenAiAgentsModule();
69929
+ const agentKitTools = await this.buildAgentKitTools({ tools, vectorStoreId });
69930
+ const openAiAgentKitAgent = new AgentFromKit({
69722
69931
  name,
69723
69932
  model: this.agentKitModelName,
69724
69933
  instructions: instructions || 'You are a helpful assistant.',
@@ -69763,6 +69972,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69763
69972
  agentName: agentForRun.name,
69764
69973
  input: inputItems,
69765
69974
  };
69975
+ const { run } = await loadOpenAiAgentsModule();
69766
69976
  const streamResult = await run(agentForRun, inputItems, {
69767
69977
  stream: true,
69768
69978
  maxTurns: 200,
@@ -69905,6 +70115,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
69905
70115
  * Ensures the AgentKit SDK is wired to the OpenAI client and API key.
69906
70116
  */
69907
70117
  async ensureAgentKitDefaults() {
70118
+ const { setDefaultOpenAIClient, setDefaultOpenAIKey } = await loadOpenAiAgentsModule();
69908
70119
  const client = await this.getClient();
69909
70120
  setDefaultOpenAIClient(client);
69910
70121
  const apiKey = this.agentKitOptions.apiKey;
@@ -72237,6 +72448,66 @@ var RemoteAgent$1 = /*#__PURE__*/Object.freeze({
72237
72448
  RemoteAgent: RemoteAgent
72238
72449
  });
72239
72450
 
72451
+ /**
72452
+ * Git synchronization which leaves the repository completely untouched.
72453
+ *
72454
+ * Note: This is the default for every command and helper which supports the git synchronization.
72455
+ */
72456
+ const DISABLED_CODER_GIT_SYNC_OPTIONS = Object.freeze({
72457
+ isCommitEnabled: false,
72458
+ isAutoPushEnabled: false,
72459
+ isAutoPullEnabled: false,
72460
+ });
72461
+ /**
72462
+ * Pulls the latest repository changes before a `ptbk coder` command changes the project.
72463
+ */
72464
+ async function $pullCoderChanges(options) {
72465
+ const { gitSync, projectPath = process.cwd() } = options;
72466
+ if (!gitSync.isAutoPullEnabled) {
72467
+ return;
72468
+ }
72469
+ console.info(colors.gray('Pulling the latest changes from the remote repository...'));
72470
+ await pullLatestChanges(projectPath);
72471
+ }
72472
+ /**
72473
+ * Commits - and when requested also pushes - the changes one `ptbk coder` command has just made.
72474
+ *
72475
+ * Note: A repository without any change is left alone instead of creating an empty commit.
72476
+ */
72477
+ async function $commitCoderChanges(options) {
72478
+ const { gitSync, commitMessage, projectPath = process.cwd() } = options;
72479
+ if (!gitSync.isCommitEnabled) {
72480
+ return;
72481
+ }
72482
+ if (!(await hasChangesToCommit(projectPath))) {
72483
+ console.info(colors.gray('Nothing to commit, the working tree is clean'));
72484
+ return;
72485
+ }
72486
+ await commitChanges(commitMessage, {
72487
+ projectPath,
72488
+ autoPush: gitSync.isAutoPushEnabled,
72489
+ });
72490
+ console.info(colors.green(`✓ ${gitSync.isAutoPushEnabled ? 'Committed and pushed' : 'Committed'}: ${commitMessage}`));
72491
+ }
72492
+ /**
72493
+ * Checks whether the repository holds any change which can be committed.
72494
+ */
72495
+ async function hasChangesToCommit(projectPath) {
72496
+ const gitStatus = await runGitCommand({
72497
+ command: 'git status --porcelain',
72498
+ cwd: projectPath,
72499
+ isVerbose: false,
72500
+ });
72501
+ return gitStatus.trim() !== '';
72502
+ }
72503
+
72504
+ var coderGitSync = /*#__PURE__*/Object.freeze({
72505
+ __proto__: null,
72506
+ DISABLED_CODER_GIT_SYNC_OPTIONS: DISABLED_CODER_GIT_SYNC_OPTIONS,
72507
+ $pullCoderChanges: $pullCoderChanges,
72508
+ $commitCoderChanges: $commitCoderChanges
72509
+ });
72510
+
72240
72511
  /**
72241
72512
  * Calculates the next available prompt numbering sequence for a month.
72242
72513
  */
@@ -72711,6 +72982,31 @@ function normalizeRefactorCandidatePath(pathValue) {
72711
72982
  }
72712
72983
  // Note: [🟡] Code for repository script [normalizeRefactorCandidatePath](scripts/find-refactor-candidates/normalizeRefactorCandidatePath.ts) should never be published outside of `@promptbook/cli`
72713
72984
 
72985
+ /**
72986
+ * The TypeScript compiler API once it was loaded by `analyzeSourceFileForRefactorCandidate`
72987
+ *
72988
+ * Note: [🐌] `typescript` is a heavy package, it is loaded on demand so that it does not slow down every single run
72989
+ * of the `ptbk` CLI utility
72990
+ *
72991
+ * @private variable of analyzeSourceFileForRefactorCandidate
72992
+ */
72993
+ let loadedTypescriptModule = null;
72994
+ /**
72995
+ * Returns the TypeScript compiler API which was already loaded for the structural analysis.
72996
+ *
72997
+ * @private function of analyzeSourceFileForRefactorCandidate
72998
+ */
72999
+ function getLoadedTypescriptModule() {
73000
+ if (loadedTypescriptModule === null) {
73001
+ throw new UnexpectedError(spaceTrim(`
73002
+ The \`typescript\` module was not loaded yet.
73003
+
73004
+ Structural analysis helpers must be called only from \`analyzeSourceFileForRefactorCandidate\` which
73005
+ loads \`typescript\` lazily.
73006
+ `));
73007
+ }
73008
+ return loadedTypescriptModule;
73009
+ }
72714
73010
  /**
72715
73011
  * Resolves whether a source file should produce a refactor candidate entry.
72716
73012
  *
@@ -72736,6 +73032,7 @@ async function analyzeSourceFileForRefactorCandidate(options) {
72736
73032
  }
72737
73033
  }
72738
73034
  if (STRUCTURAL_ANALYSIS_EXTENSIONS.includes(extension)) {
73035
+ loadedTypescriptModule !== null && loadedTypescriptModule !== void 0 ? loadedTypescriptModule : (loadedTypescriptModule = await getTypescriptModule());
72739
73036
  const structureSummary = summarizeSourceFileStructure(content, extension, filePath);
72740
73037
  if (structureSummary.entityCount > heuristics.maxEntityCountPerFile) {
72741
73038
  reasons.push(`entities ${structureSummary.entityCount}/${heuristics.maxEntityCountPerFile}`);
@@ -72795,6 +73092,7 @@ function countLines(content) {
72795
73092
  * @private function of analyzeSourceFileForRefactorCandidate
72796
73093
  */
72797
73094
  function summarizeSourceFileStructure(content, extension, filePath) {
73095
+ const ts = getLoadedTypescriptModule();
72798
73096
  const scriptKind = getScriptKindForExtension(extension);
72799
73097
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKind);
72800
73098
  return {
@@ -72808,6 +73106,7 @@ function summarizeSourceFileStructure(content, extension, filePath) {
72808
73106
  * @private function of analyzeSourceFileForRefactorCandidate
72809
73107
  */
72810
73108
  function countEntitiesInSourceFile(sourceFile) {
73109
+ const ts = getLoadedTypescriptModule();
72811
73110
  let count = 0;
72812
73111
  // Only count top-level declarations to avoid inflating with members or nested scopes.
72813
73112
  for (const statement of sourceFile.statements) {
@@ -72840,6 +73139,7 @@ function countEntitiesInSourceFile(sourceFile) {
72840
73139
  * @private function of analyzeSourceFileForRefactorCandidate
72841
73140
  */
72842
73141
  function summarizeFunctionsInSourceFile(sourceFile) {
73142
+ const ts = getLoadedTypescriptModule();
72843
73143
  let functionCount = 0;
72844
73144
  let maxFunctionComplexity = 0;
72845
73145
  let mostComplexFunctionName = null;
@@ -72867,6 +73167,7 @@ function summarizeFunctionsInSourceFile(sourceFile) {
72867
73167
  * @private function of analyzeSourceFileForRefactorCandidate
72868
73168
  */
72869
73169
  function isCountedFunctionLikeDeclaration(node) {
73170
+ const ts = getLoadedTypescriptModule();
72870
73171
  if (ts.isFunctionDeclaration(node) ||
72871
73172
  ts.isMethodDeclaration(node) ||
72872
73173
  ts.isConstructorDeclaration(node) ||
@@ -72885,6 +73186,7 @@ function isCountedFunctionLikeDeclaration(node) {
72885
73186
  * @private function of analyzeSourceFileForRefactorCandidate
72886
73187
  */
72887
73188
  function isNamedFunctionExpression(node) {
73189
+ const ts = getLoadedTypescriptModule();
72888
73190
  const parent = node.parent;
72889
73191
  return (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent) || ts.isPropertyAssignment(parent));
72890
73192
  }
@@ -72897,6 +73199,7 @@ function calculateFunctionComplexity(functionNode) {
72897
73199
  if (!functionNode.body) {
72898
73200
  return 1;
72899
73201
  }
73202
+ const ts = getLoadedTypescriptModule();
72900
73203
  let complexity = 1;
72901
73204
  const visitNode = (node) => {
72902
73205
  if (node !== functionNode.body && isCountedFunctionLikeDeclaration(node)) {
@@ -72916,6 +73219,7 @@ function calculateFunctionComplexity(functionNode) {
72916
73219
  * @private function of analyzeSourceFileForRefactorCandidate
72917
73220
  */
72918
73221
  function isComplexityDecisionNode(node) {
73222
+ const ts = getLoadedTypescriptModule();
72919
73223
  if (ts.isIfStatement(node) ||
72920
73224
  ts.isConditionalExpression(node) ||
72921
73225
  ts.isCatchClause(node) ||
@@ -72941,6 +73245,7 @@ function isComplexityDecisionNode(node) {
72941
73245
  * @private function of analyzeSourceFileForRefactorCandidate
72942
73246
  */
72943
73247
  function getFunctionDisplayName(functionNode) {
73248
+ const ts = getLoadedTypescriptModule();
72944
73249
  if (ts.isConstructorDeclaration(functionNode)) {
72945
73250
  return 'constructor';
72946
73251
  }
@@ -72973,6 +73278,7 @@ function getFunctionDisplayName(functionNode) {
72973
73278
  * @private function of analyzeSourceFileForRefactorCandidate
72974
73279
  */
72975
73280
  function getBindingNameText(name) {
73281
+ const ts = getLoadedTypescriptModule();
72976
73282
  return ts.isIdentifier(name) ? name.text : null;
72977
73283
  }
72978
73284
  /**
@@ -72981,6 +73287,7 @@ function getBindingNameText(name) {
72981
73287
  * @private function of analyzeSourceFileForRefactorCandidate
72982
73288
  */
72983
73289
  function getPropertyNameText(name) {
73290
+ const ts = getLoadedTypescriptModule();
72984
73291
  if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
72985
73292
  return name.text;
72986
73293
  }
@@ -73003,6 +73310,7 @@ function buildComplexityReason(structureSummary, maxAllowedFunctionComplexity) {
73003
73310
  * @private function of analyzeSourceFileForRefactorCandidate
73004
73311
  */
73005
73312
  function getScriptKindForExtension(extension) {
73313
+ const ts = getLoadedTypescriptModule();
73006
73314
  if (extension === '.tsx') {
73007
73315
  return ts.ScriptKind.TSX;
73008
73316
  }
@@ -73862,63 +74170,240 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
73862
74170
  });
73863
74171
 
73864
74172
  /**
73865
- * Agent source used by `ptbk coder ping` for its disposable connectivity turn.
74173
+ * Builds a normalized temporary shell script path for prompt runners.
73866
74174
  */
73867
- const PING_AGENT_SOURCE = spaceTrim$1(`
73868
- Promptbook Coder Ping Agent
74175
+ function buildTemporaryPromptScriptPath(options) {
74176
+ const sourceFileName = basename(options.sourceFileName);
74177
+ const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
74178
+ return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
74179
+ }
73869
74180
 
73870
- PERSONA You are a connectivity test agent. Perform only the tiny task requested by the user and answer concisely.
73871
- `);
73872
74181
  /**
73873
- * User message used by `ptbk coder ping` to produce a deterministic response.
74182
+ * Marker the pinged harness is asked to prefix its answer with, so the reply can be recognized
74183
+ * in the raw runner output of every supported harness.
74184
+ *
74185
+ * Note: The marker must stay free of regular-expression metacharacters, because
74186
+ * `extractCoderPingAnswer` builds its pattern from it.
74187
+ */
74188
+ const CODER_PING_ANSWER_MARKER = 'PTBK-CODER-PING-ANSWER';
74189
+ /**
74190
+ * First factor of the dummy multiplication the pinged harness is asked to compute.
74191
+ */
74192
+ const CODER_PING_FIRST_FACTOR = 6;
74193
+ /**
74194
+ * Second factor of the dummy multiplication the pinged harness is asked to compute.
73874
74195
  */
73875
- const PING_MESSAGE = 'Reply with exactly the single word PONG and nothing else.';
74196
+ const CODER_PING_SECOND_FACTOR = 7;
73876
74197
  /**
73877
- * Runs one small harness/model turn in a disposable temporary project.
74198
+ * Answer a working harness and model returns for the dummy work of `ptbk coder ping`.
74199
+ */
74200
+ const CODER_PING_EXPECTED_ANSWER = String(CODER_PING_FIRST_FACTOR * CODER_PING_SECOND_FACTOR);
74201
+ /**
74202
+ * Builds the dummy prompt sent by `ptbk coder ping`.
73878
74203
  *
73879
- * The temporary project is outside the caller's repository, so even a harness
73880
- * that writes files cannot change the project from which `ptbk coder ping` was
73881
- * started.
74204
+ * The work is intentionally the smallest possible one that still reaches the model: it spends a
74205
+ * negligible amount of the harness quota, it needs no tool and it explicitly forbids touching the
74206
+ * project, so a ping leaves the project exactly as it was.
73882
74207
  */
73883
- async function runCoderPing(options) {
73884
- const temporaryProjectPath = await mkdtemp(join(tmpdir(), 'promptbook-coder-ping-'));
73885
- const originalWorkingDirectory = process.cwd();
73886
- try {
73887
- const agentPath = join(temporaryProjectPath, 'ping.book');
73888
- await writeFile(agentPath, `${PING_AGENT_SOURCE}\n`, 'utf-8');
73889
- // Note: Some supported CLI wrappers inherit the Node process working directory instead of using projectPath.
73890
- process.chdir(temporaryProjectPath);
73891
- const startedAt = performance.now();
73892
- const result = await executeAgentChatTurn({
73893
- agentPath,
73894
- currentWorkingDirectory: temporaryProjectPath,
73895
- agentName: options.agentName,
73896
- model: options.model,
73897
- isVerbose: false,
73898
- noUi: options.isUiDisabled,
73899
- thinkingLevel: options.thinkingLevel,
73900
- allowCredits: options.isCreditsAllowed,
73901
- messages: [
73902
- {
73903
- sender: 'USER',
73904
- content: PING_MESSAGE,
73905
- },
73906
- ],
74208
+ function buildCoderPingPrompt() {
74209
+ return spaceTrim(`
74210
+ # Promptbook connection check
74211
+
74212
+ This is an automated \`ptbk coder ping\` connection check, not a coding task.
74213
+
74214
+ Do exactly this and nothing else:
74215
+
74216
+ 1. Multiply \`${CODER_PING_FIRST_FACTOR}\` by \`${CODER_PING_SECOND_FACTOR}\`.
74217
+ 2. Answer with one single line \`${CODER_PING_ANSWER_MARKER}: <result>\` where \`<result>\` is the number you computed.
74218
+
74219
+ Rules:
74220
+
74221
+ - Do not read, create, change, move or delete any file.
74222
+ - Do not run any command and do not use any tool.
74223
+ - Do not write anything except the single answer line.
74224
+ `);
74225
+ }
74226
+
74227
+ /**
74228
+ * Pattern matching one answer line produced by the pinged harness.
74229
+ *
74230
+ * The captured answer deliberately stops at a quote, a backslash or a line break, so an answer
74231
+ * embedded in a JSON event stream — as produced by Claude Code, Opencode or Codex `--json` — is
74232
+ * captured without the surrounding JSON.
74233
+ */
74234
+ const CODER_PING_ANSWER_PATTERN = new RegExp(`${CODER_PING_ANSWER_MARKER}\\s*:[ \\t]*([^\\r\\n"\\\\]*)`, 'gu');
74235
+ /**
74236
+ * Extracts the answer of a pinged harness from the runtime log of its runner shell.
74237
+ *
74238
+ * Only the raw output of the last execution is searched, so the answer marker contained in the
74239
+ * prompt of the raw input is never mistaken for the answer of the harness.
74240
+ *
74241
+ * @returns The answer of the harness, or `null` when the harness produced no recognizable answer
74242
+ */
74243
+ function extractCoderPingAnswer(runtimeLog) {
74244
+ var _a;
74245
+ const rawOutput = runtimeLog.split(SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER).pop();
74246
+ if (rawOutput === undefined) {
74247
+ return null;
74248
+ }
74249
+ // Note: The last answer wins because harnesses which stream partial messages repeat the growing answer line
74250
+ const answers = Array.from(rawOutput.matchAll(CODER_PING_ANSWER_PATTERN))
74251
+ .map((match) => (match[1] || '').trim())
74252
+ .filter((answer) => answer !== '');
74253
+ return (_a = answers[answers.length - 1]) !== null && _a !== void 0 ? _a : null;
74254
+ }
74255
+
74256
+ /**
74257
+ * Temporary subdirectory used for the `ptbk coder ping` runner shell script and its runtime log.
74258
+ */
74259
+ const CODER_PING_SCRIPT_DIRECTORY_NAME = 'coder-ping';
74260
+ /**
74261
+ * Base name of the temporary `ptbk coder ping` runner shell script.
74262
+ */
74263
+ const CODER_PING_SCRIPT_SOURCE_NAME = 'ping';
74264
+ /**
74265
+ * Sends one tiny dummy prompt through the selected harness and model and measures the round trip.
74266
+ *
74267
+ * The ping reuses the very same runner the coding queue uses, so it really exercises the configured
74268
+ * harness, model, thinking level and authentication — including the retry behavior on rate limits.
74269
+ * Both temporary artifacts it creates are removed again, so the project is left as it was.
74270
+ */
74271
+ async function pingCoderHarness(options) {
74272
+ const projectPath = options.projectPath || process.cwd();
74273
+ const { runner, runnerMetadata } = resolvePromptRunner(options);
74274
+ const scriptPath = buildTemporaryPromptScriptPath({
74275
+ projectPath,
74276
+ scriptDirectoryName: CODER_PING_SCRIPT_DIRECTORY_NAME,
74277
+ sourceFileName: CODER_PING_SCRIPT_SOURCE_NAME,
74278
+ });
74279
+ const startedTimeMs = Date.now();
74280
+ const { answer, usage, loginMethod } = await withPromptRuntimeLog(scriptPath, async (logPath) => {
74281
+ var _a;
74282
+ const result = await runner.runPrompt({
74283
+ prompt: buildCoderPingPrompt(),
74284
+ scriptPath,
74285
+ projectPath,
74286
+ logPath,
74287
+ shouldPrintLiveOutput: (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : false,
74288
+ preserveArtifactsOnSuccess: false,
73907
74289
  });
73908
- return {
73909
- result: result.answer,
73910
- elapsedTimeMs: performance.now() - startedAt,
73911
- };
74290
+ return { ...result, answer: extractCoderPingAnswer(await readRuntimeLog(logPath)) };
74291
+ }, { preserveArtifactsOnSuccess: false });
74292
+ return {
74293
+ runnerName: runnerMetadata.runnerName,
74294
+ modelName: runnerMetadata.modelName,
74295
+ thinkingLevel: options.thinkingLevel,
74296
+ answer,
74297
+ isAnswerCorrect: answer === CODER_PING_EXPECTED_ANSWER,
74298
+ durationMs: Date.now() - startedTimeMs,
74299
+ usage,
74300
+ loginMethod,
74301
+ };
74302
+ }
74303
+ /**
74304
+ * Reads the runtime log of the finished ping, treating an unreadable log as no output at all.
74305
+ */
74306
+ async function readRuntimeLog(logPath) {
74307
+ return await readFile(logPath, 'utf-8').catch(() => '');
74308
+ }
74309
+
74310
+ var pingCoderHarness$1 = /*#__PURE__*/Object.freeze({
74311
+ __proto__: null,
74312
+ pingCoderHarness: pingCoderHarness
74313
+ });
74314
+
74315
+ /**
74316
+ * Formats usage price for display in prompt status lines and task details.
74317
+ * Examples:
74318
+ * - "$0.12" (certain)
74319
+ * - "~$3.05" (uncertain)
74320
+ * - "$0.00" (zero cost)
74321
+ * - "<$0.01" (tiny non-zero cost)
74322
+ *
74323
+ * @private internal utility of the prompt runners and the Agents Server task details
74324
+ */
74325
+ function formatUsagePrice(usage) {
74326
+ const price = usage.price.value;
74327
+ const isUncertain = usage.price.isUncertain === true;
74328
+ const prefix = isUncertain ? '~' : '';
74329
+ if (price === 0) {
74330
+ return `${prefix}$0.00`;
73912
74331
  }
73913
- finally {
73914
- process.chdir(originalWorkingDirectory);
73915
- await rm(temporaryProjectPath, { recursive: true, force: true });
74332
+ if (price < 0.01) {
74333
+ return `${prefix}<$0.01`;
73916
74334
  }
74335
+ if (price < 1) {
74336
+ return `${prefix}$${price.toFixed(4)}`;
74337
+ }
74338
+ return `${prefix}$${price.toFixed(2)}`;
73917
74339
  }
73918
74340
 
73919
- var runCoderPing$1 = /*#__PURE__*/Object.freeze({
74341
+ /**
74342
+ * Formats runner details for prompt status lines.
74343
+ */
74344
+ function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
74345
+ const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
74346
+ const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
74347
+ const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
74348
+ if (!normalizedRunner && !normalizedModel) {
74349
+ return 'unknown';
74350
+ }
74351
+ const runnerLabel = normalizedRunner || 'unknown';
74352
+ if (!normalizedModel) {
74353
+ return `${runnerLabel}${thinkingLevelSuffix}`;
74354
+ }
74355
+ return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
74356
+ }
74357
+
74358
+ /**
74359
+ * Prints the compact summary of one finished `ptbk coder ping`.
74360
+ */
74361
+ function printCoderPingResult(result) {
74362
+ const runnerSignature = formatRunnerSignature(result.runnerName, result.modelName, result.thinkingLevel);
74363
+ const loginMethodLabel = formatCodexLoginMethod(result.loginMethod);
74364
+ const loginMethodSuffix = loginMethodLabel === undefined ? '' : ` (${loginMethodLabel})`;
74365
+ console.info(colors.green(`🏓 ${runnerSignature}${loginMethodSuffix} answered in ${formatCoderPingResponseTime(result.durationMs)}`));
74366
+ console.info(colors.gray(` Answer: ${formatCoderPingAnswer(result)}`));
74367
+ console.info(colors.gray(` Usage: ${formatCoderPingUsage(result.usage)}`));
74368
+ }
74369
+ /**
74370
+ * Formats the measured round-trip time, keeping the sub-second precision a response time needs.
74371
+ */
74372
+ function formatCoderPingResponseTime(durationMs) {
74373
+ return `${(durationMs / 1000).toFixed(2)}s`;
74374
+ }
74375
+ /**
74376
+ * Formats the answer of the pinged harness together with what was expected from it.
74377
+ */
74378
+ function formatCoderPingAnswer(result) {
74379
+ if (result.answer === null) {
74380
+ return `Reached, but the answer line was missing from the output (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74381
+ }
74382
+ if (result.isAnswerCorrect) {
74383
+ return result.answer;
74384
+ }
74385
+ return `${result.answer} (expected \`${CODER_PING_EXPECTED_ANSWER}\`)`;
74386
+ }
74387
+ /**
74388
+ * Formats the resources the pinged harness reported for the dummy work.
74389
+ */
74390
+ function formatCoderPingUsage(usage) {
74391
+ return [
74392
+ formatUsagePrice(usage),
74393
+ `${formatUncertainCount(usage.input.tokensCount)} input tokens`,
74394
+ `${formatUncertainCount(usage.output.tokensCount)} output tokens`,
74395
+ ].join(', ');
74396
+ }
74397
+ /**
74398
+ * Formats one counted usage value, marking an estimated count with a leading `~`.
74399
+ */
74400
+ function formatUncertainCount(count) {
74401
+ return `${count.isUncertain === true ? '~' : ''}${Math.round(count.value)}`;
74402
+ }
74403
+
74404
+ var printCoderPingResult$1 = /*#__PURE__*/Object.freeze({
73920
74405
  __proto__: null,
73921
- runCoderPing: runCoderPing
74406
+ printCoderPingResult: printCoderPingResult
73922
74407
  });
73923
74408
 
73924
74409
  /**
@@ -75833,6 +76318,14 @@ function splitSqlStatements(sql) {
75833
76318
  .filter((statement) => statement !== '');
75834
76319
  }
75835
76320
 
76321
+ /**
76322
+ * Loads the PostgreSQL client (`pg`) on demand
76323
+ *
76324
+ * Note: [🐌] Loaded lazily to keep the startup of the `ptbk` CLI utility fast
76325
+ *
76326
+ * @private function of runAutoMigrateTestingServers
76327
+ */
76328
+ const loadPostgresModule = createLazyModuleLoader(() => import('pg'));
75836
76329
  /**
75837
76330
  * Migration targets for testing servers that should be migrated by coding-script auto-migration.
75838
76331
  */
@@ -75910,6 +76403,7 @@ async function runAutoMigrateTestingServersImmediately(options) {
75910
76403
  * @returns Pending migration files grouped by prefix.
75911
76404
  */
75912
76405
  async function listPendingMigrationsByPrefix(options) {
76406
+ const { Client } = await loadPostgresModule();
75913
76407
  const client = new Client({
75914
76408
  connectionString: options.connectionString,
75915
76409
  ssl: { rejectUnauthorized: false },
@@ -76067,15 +76561,6 @@ function buildCommitMessage(file, section) {
76067
76561
  return lines.join(file.eol);
76068
76562
  }
76069
76563
 
76070
- /**
76071
- * Builds a normalized temporary shell script path for prompt runners.
76072
- */
76073
- function buildTemporaryPromptScriptPath(options) {
76074
- const sourceFileName = basename(options.sourceFileName);
76075
- const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
76076
- return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
76077
- }
76078
-
76079
76564
  /**
76080
76565
  * Builds the suffix which disambiguates one prompt section inside its prompt file.
76081
76566
  *
@@ -76102,32 +76587,6 @@ function buildScriptPath(file, section, projectPath = process.cwd()) {
76102
76587
  });
76103
76588
  }
76104
76589
 
76105
- /**
76106
- * Formats usage price for display in prompt status lines and task details.
76107
- * Examples:
76108
- * - "$0.12" (certain)
76109
- * - "~$3.05" (uncertain)
76110
- * - "$0.00" (zero cost)
76111
- * - "<$0.01" (tiny non-zero cost)
76112
- *
76113
- * @private internal utility of the prompt runners and the Agents Server task details
76114
- */
76115
- function formatUsagePrice(usage) {
76116
- const price = usage.price.value;
76117
- const isUncertain = usage.price.isUncertain === true;
76118
- const prefix = isUncertain ? '~' : '';
76119
- if (price === 0) {
76120
- return `${prefix}$0.00`;
76121
- }
76122
- if (price < 0.01) {
76123
- return `${prefix}<$0.01`;
76124
- }
76125
- if (price < 1) {
76126
- return `${prefix}$${price.toFixed(4)}`;
76127
- }
76128
- return `${prefix}$${price.toFixed(2)}`;
76129
- }
76130
-
76131
76590
  /**
76132
76591
  * Human-readable labels for each coder run step kind shown in prompt status lines.
76133
76592
  */
@@ -76172,23 +76631,6 @@ function formatPromptAttemptMetadata(status, attemptCount) {
76172
76631
  return `(failed after ${attemptCount} attempts) `;
76173
76632
  }
76174
76633
 
76175
- /**
76176
- * Formats runner details for prompt status lines.
76177
- */
76178
- function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
76179
- const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
76180
- const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
76181
- const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
76182
- if (!normalizedRunner && !normalizedModel) {
76183
- return 'unknown';
76184
- }
76185
- const runnerLabel = normalizedRunner || 'unknown';
76186
- if (!normalizedModel) {
76187
- return `${runnerLabel}${thinkingLevelSuffix}`;
76188
- }
76189
- return `${runnerLabel} \`${normalizedModel}\`${thinkingLevelSuffix}`;
76190
- }
76191
-
76192
76634
  /**
76193
76635
  * Replaces the complete todo status line while preserving its indentation.
76194
76636
  *
@@ -79342,14 +79784,21 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
79342
79784
  let promptFiles = initialFiles;
79343
79785
  const skippedFiles = new Set();
79344
79786
  while (true) {
79787
+ // Note: The git synchronization is applied around each single verification, not once per whole run
79788
+ await $pullCoderChanges({ gitSync: normalizedOptions.gitSync });
79789
+ if (normalizedOptions.gitSync.isAutoPullEnabled) {
79790
+ // Note: The pull can bring in prompt file changes, so the queue is reloaded before it is used
79791
+ promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79792
+ }
79345
79793
  displayPromptOverview(promptFiles);
79346
79794
  // First priority: verify files where all prompts are marked as done
79347
79795
  const fileWithAllDone = findFileWithAllDonePrompts(promptFiles, skippedFiles);
79348
79796
  if (fileWithAllDone) {
79349
- const wasSkipped = await verifyDonePromptsInFile(fileWithAllDone);
79350
- if (wasSkipped) {
79797
+ const outcome = await verifyDonePromptsInFile(fileWithAllDone);
79798
+ if (outcome.wasSkipped) {
79351
79799
  skippedFiles.add(fileWithAllDone.path);
79352
79800
  }
79801
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
79353
79802
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79354
79803
  continue;
79355
79804
  }
@@ -79359,10 +79808,20 @@ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
79359
79808
  console.info(colors.green('\n✅ All prompts have been verified.'));
79360
79809
  break;
79361
79810
  }
79362
- await resolvePrompt(nextPrompt);
79811
+ const outcome = await resolvePrompt(nextPrompt);
79812
+ await $commitVerificationOutcome(normalizedOptions.gitSync, outcome);
79363
79813
  promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
79364
79814
  }
79365
79815
  }
79816
+ /**
79817
+ * Commits and pushes one applied verification when the git synchronization is enabled.
79818
+ */
79819
+ async function $commitVerificationOutcome(gitSync, outcome) {
79820
+ if (outcome.commitMessage === null) {
79821
+ return;
79822
+ }
79823
+ await $commitCoderChanges({ gitSync, commitMessage: outcome.commitMessage });
79824
+ }
79366
79825
  /**
79367
79826
  * Parses supported command-line arguments for the standalone verification script.
79368
79827
  */
@@ -79370,6 +79829,11 @@ function parseVerifyPromptsCliOptions(args) {
79370
79829
  return {
79371
79830
  reverse: args.includes('--reverse'),
79372
79831
  ignore: readRepeatableStringOption(args, '--ignore'),
79832
+ gitSync: {
79833
+ isCommitEnabled: args.includes('--commit'),
79834
+ isAutoPushEnabled: args.includes('--auto-push'),
79835
+ isAutoPullEnabled: args.includes('--auto-pull'),
79836
+ },
79373
79837
  };
79374
79838
  }
79375
79839
  /**
@@ -79421,10 +79885,11 @@ async function prepareArchiveDirectory() {
79421
79885
  * Normalizes verification options so the rest of the flow can assume stable defaults.
79422
79886
  */
79423
79887
  function normalizeVerifyPromptsOptions(options) {
79424
- var _a, _b;
79888
+ var _a, _b, _c;
79425
79889
  return {
79426
79890
  reverse: (_a = options.reverse) !== null && _a !== void 0 ? _a : false,
79427
79891
  ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
79892
+ gitSync: (_c = options.gitSync) !== null && _c !== void 0 ? _c : DISABLED_CODER_GIT_SYNC_OPTIONS,
79428
79893
  };
79429
79894
  }
79430
79895
  /**
@@ -79555,7 +80020,6 @@ function findFileWithAllDonePrompts(promptFiles, skippedFiles) {
79555
80020
  /**
79556
80021
  * Verifies the last done [x] prompt in a file and decides whether to archive it or add a repair prompt.
79557
80022
  * Ignores not-ready prompts like [-], [.], [?], etc.
79558
- * Returns true if the file was skipped, false otherwise.
79559
80023
  */
79560
80024
  async function verifyDonePromptsInFile(file) {
79561
80025
  const doneCount = file.sections.filter((s) => s.status === 'done').length;
@@ -79575,31 +80039,44 @@ async function verifyDonePromptsInFile(file) {
79575
80039
  }
79576
80040
  if (!lastDoneSection) {
79577
80041
  console.info(colors.gray('No done [x] prompts found in this file.'));
79578
- return false;
80042
+ return { wasSkipped: false, commitMessage: null };
79579
80043
  }
79580
80044
  console.info(colors.gray('Verifying the last [x] prompt in the file...\n'));
79581
80045
  displayPromptSnippet({ file, section: lastDoneSection });
79582
80046
  const decision = await promptForDoneVerification(file, lastDoneSection);
79583
80047
  if (decision === 'done') {
79584
80048
  await archivePromptFile(file);
79585
- return false;
80049
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(file) };
79586
80050
  }
79587
80051
  else if (decision === 'needs-work') {
79588
80052
  console.info(colors.yellow('\n⚠️ This prompt needs repair.'));
79589
80053
  await appendRepairPrompt(file, lastDoneSection);
79590
- return false;
80054
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(file) };
79591
80055
  }
79592
80056
  else {
79593
80057
  console.info(colors.gray('\n⏩ Skipped, no changes made.'));
79594
- return true;
80058
+ return { wasSkipped: true, commitMessage: null };
79595
80059
  }
79596
80060
  }
80061
+ /**
80062
+ * Builds the commit message describing one archived prompt file.
80063
+ */
80064
+ function buildArchiveCommitMessage(file) {
80065
+ return `✅ Prompt done and archived \`${file.name}\``; // <- $commitCoderChanges({
80066
+ }
80067
+ /**
80068
+ * Builds the commit message describing one appended repair prompt.
80069
+ */
80070
+ function buildRepairCommitMessage(file) {
80071
+ return `❌ Repair prompt added into \`${file.name}\``; // <- $commitCoderChanges({
80072
+ }
79597
80073
  /**
79598
80074
  * Asks the user to verify if a done prompt is actually completed.
79599
80075
  * Returns 'done' if verified, 'needs-work' if not done, or 'skip' to skip this file.
79600
80076
  */
79601
80077
  async function promptForDoneVerification(file, section) {
79602
80078
  const promptLabel = buildPromptLabelForDisplay(file, section);
80079
+ const { default: prompts } = await loadPromptsModule();
79603
80080
  const response = await prompts({
79604
80081
  type: 'select',
79605
80082
  name: 'verified',
@@ -79668,16 +80145,17 @@ async function resolvePrompt(selection) {
79668
80145
  const decision = await promptForDecision(selection);
79669
80146
  if (decision === 'done') {
79670
80147
  await archivePromptFile(selection.file);
80148
+ return { wasSkipped: false, commitMessage: buildArchiveCommitMessage(selection.file) };
79671
80149
  }
79672
- else {
79673
- await appendRepairPrompt(selection.file, selection.section);
79674
- }
80150
+ await appendRepairPrompt(selection.file, selection.section);
80151
+ return { wasSkipped: false, commitMessage: buildRepairCommitMessage(selection.file) };
79675
80152
  }
79676
80153
  /**
79677
80154
  * Presents the interactive decision menu for the current prompt section.
79678
80155
  */
79679
80156
  async function promptForDecision(selection) {
79680
80157
  const promptLabel = buildPromptLabelForDisplay(selection.file, selection.section);
80158
+ const { default: prompts } = await loadPromptsModule();
79681
80159
  const response = await prompts({
79682
80160
  type: 'select',
79683
80161
  name: 'decision',