@promptbook/cli 0.114.0-25 → 0.114.0-27

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 (74) hide show
  1. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimePm2.ts +56 -10
  2. package/apps/agents-server/src/utils/userChat/createImmediateUserChatAnswerModelRequirements.ts +1 -0
  3. package/esm/index.es.js +391 -68
  4. package/esm/index.es.js.map +1 -1
  5. package/esm/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +1 -1
  6. package/esm/src/cli/cli-commands/common/harness/$resolveHarnessCommandPath.d.ts +14 -0
  7. package/esm/src/cli/cli-commands/common/harness/$resolveHarnessInstallationOrigin.d.ts +10 -0
  8. package/esm/src/cli/cli-commands/common/harness/$runHarnessInstallationCommand.d.ts +43 -0
  9. package/esm/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
  10. package/esm/src/cli/cli-commands/common/harness/HarnessInstallationOrigin.d.ts +36 -0
  11. package/esm/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +5 -0
  12. package/esm/src/cli/cli-commands/common/harness/HarnessUpdatePlan.d.ts +23 -0
  13. package/esm/src/cli/cli-commands/common/harness/buildHarnessInstallCommand.d.ts +4 -1
  14. package/esm/src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.d.ts +8 -0
  15. package/esm/src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.test.d.ts +1 -0
  16. package/esm/src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.d.ts +31 -0
  17. package/esm/src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.test.d.ts +1 -0
  18. package/esm/src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.d.ts +12 -0
  19. package/esm/src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.test.d.ts +1 -0
  20. package/esm/src/cli/cli-commands/common/npm/$resolveNpmGlobalPrefixPath.d.ts +9 -0
  21. package/esm/src/commitments/WRITING_RULES/WRITING_RULES.d.ts +14 -7
  22. package/esm/src/commitments/index.d.ts +1 -1
  23. package/esm/src/version.d.ts +1 -1
  24. package/package.json +1 -1
  25. package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +1 -0
  26. package/src/cli/cli-commands/coder/init.ts +1 -1
  27. package/src/cli/cli-commands/coder/ping.ts +1 -1
  28. package/src/cli/cli-commands/coder/run.ts +1 -1
  29. package/src/cli/cli-commands/coder/server.ts +1 -1
  30. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +57 -8
  31. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +8 -1
  32. package/src/cli/cli-commands/common/harness/$resolveHarnessCommandPath.ts +46 -0
  33. package/src/cli/cli-commands/common/harness/$resolveHarnessInstallationOrigin.ts +36 -0
  34. package/src/cli/cli-commands/common/harness/$runHarnessInstallationCommand.ts +100 -0
  35. package/src/cli/cli-commands/common/harness/HarnessDefinition.ts +34 -0
  36. package/src/cli/cli-commands/common/harness/HarnessInstallationOrigin.ts +44 -0
  37. package/src/cli/cli-commands/common/harness/HarnessInstallationStatus.ts +6 -0
  38. package/src/cli/cli-commands/common/harness/HarnessUpdatePlan.ts +27 -0
  39. package/src/cli/cli-commands/common/harness/buildHarnessInstallCommand.ts +4 -1
  40. package/src/cli/cli-commands/common/harness/formatHarnessInstallationWarning.ts +28 -6
  41. package/src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.ts +41 -0
  42. package/src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.ts +111 -0
  43. package/src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.ts +47 -0
  44. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +1 -1
  45. package/src/cli/cli-commands/common/npm/$resolveNpmGlobalPrefixPath.ts +51 -0
  46. package/src/commitments/WRITING_RULES/WRITING_RULES.ts +16 -8
  47. package/src/commitments/index.ts +2 -1
  48. package/src/other/templates/getTemplatesPipelineCollection.ts +823 -1016
  49. package/src/version.ts +2 -2
  50. package/src/versions.txt +2 -0
  51. package/umd/index.umd.js +391 -68
  52. package/umd/index.umd.js.map +1 -1
  53. package/umd/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.d.ts +1 -1
  54. package/umd/src/cli/cli-commands/common/harness/$resolveHarnessCommandPath.d.ts +14 -0
  55. package/umd/src/cli/cli-commands/common/harness/$resolveHarnessInstallationOrigin.d.ts +10 -0
  56. package/umd/src/cli/cli-commands/common/harness/$runHarnessInstallationCommand.d.ts +43 -0
  57. package/umd/src/cli/cli-commands/common/harness/HarnessDefinition.d.ts +26 -0
  58. package/umd/src/cli/cli-commands/common/harness/HarnessInstallationOrigin.d.ts +36 -0
  59. package/umd/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +5 -0
  60. package/umd/src/cli/cli-commands/common/harness/HarnessUpdatePlan.d.ts +23 -0
  61. package/umd/src/cli/cli-commands/common/harness/buildHarnessInstallCommand.d.ts +4 -1
  62. package/umd/src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.d.ts +8 -0
  63. package/umd/src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.test.d.ts +1 -0
  64. package/umd/src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.d.ts +31 -0
  65. package/umd/src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.test.d.ts +1 -0
  66. package/umd/src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.d.ts +12 -0
  67. package/umd/src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.test.d.ts +1 -0
  68. package/umd/src/cli/cli-commands/common/npm/$resolveNpmGlobalPrefixPath.d.ts +9 -0
  69. package/umd/src/commitments/WRITING_RULES/WRITING_RULES.d.ts +14 -7
  70. package/umd/src/commitments/index.d.ts +1 -1
  71. package/umd/src/version.d.ts +1 -1
  72. package/esm/src/cli/cli-commands/common/harness/$installHarness.d.ts +0 -13
  73. package/src/cli/cli-commands/common/harness/$installHarness.ts +0 -56
  74. package/umd/src/cli/cli-commands/common/harness/$installHarness.d.ts +0 -13
package/esm/index.es.js CHANGED
@@ -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-25';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-27';
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
@@ -3703,7 +3703,7 @@ async function resolveAgentsServerAppPath() {
3703
3703
  *
3704
3704
  * @private internal constant of `buildAgentsServer`
3705
3705
  */
3706
- const NODE_MODULES_DIRECTORY_NAME$1 = 'node_modules';
3706
+ const NODE_MODULES_DIRECTORY_NAME$2 = 'node_modules';
3707
3707
 
3708
3708
  /**
3709
3709
  * Returns true when one path is nested below a `node_modules` segment.
@@ -3711,7 +3711,7 @@ const NODE_MODULES_DIRECTORY_NAME$1 = 'node_modules';
3711
3711
  * @private internal utility of `buildAgentsServer`
3712
3712
  */
3713
3713
  function isPathInsideNodeModules(path) {
3714
- return path.split(/[\\/]+/u).includes(NODE_MODULES_DIRECTORY_NAME$1);
3714
+ return path.split(/[\\/]+/u).includes(NODE_MODULES_DIRECTORY_NAME$2);
3715
3715
  }
3716
3716
 
3717
3717
  /**
@@ -3824,7 +3824,7 @@ async function copyAgentsServerRuntimePath(options) {
3824
3824
  * Links the materialized runtime back to the package dependency tree used by the installed CLI.
3825
3825
  */
3826
3826
  async function ensureMaterializedRuntimeNodeModulesLink(options) {
3827
- const nodeModulesLinkPath = join(options.materializedRuntimeRootPath, NODE_MODULES_DIRECTORY_NAME$1);
3827
+ const nodeModulesLinkPath = join(options.materializedRuntimeRootPath, NODE_MODULES_DIRECTORY_NAME$2);
3828
3828
  try {
3829
3829
  const existingLinkStats = await lstat(nodeModulesLinkPath);
3830
3830
  await rm(nodeModulesLinkPath, {
@@ -3910,7 +3910,7 @@ function resolveNextCliPath() {
3910
3910
  */
3911
3911
  function resolveNodeModulesPath(nextCliPath) {
3912
3912
  const normalizedNextCliPath = nextCliPath.replace(/\\/gu, '/');
3913
- const marker = `/${NODE_MODULES_DIRECTORY_NAME$1}/next/`;
3913
+ const marker = `/${NODE_MODULES_DIRECTORY_NAME$2}/next/`;
3914
3914
  const markerIndex = normalizedNextCliPath.lastIndexOf(marker);
3915
3915
  if (markerIndex === -1) {
3916
3916
  return resolve(nextCliPath, '..', '..', '..');
@@ -20063,29 +20063,36 @@ class UseUserLocationCommitmentDefinition extends BaseCommitmentDefinition {
20063
20063
  /**
20064
20064
  * `WRITING RULES` commitment definition.
20065
20065
  *
20066
- * It adds constraints that apply strictly to writing style and presentation
20067
- * rather than task-solving behavior.
20066
+ * The `WRITING RULES`/`WRITING RULE` commitment adds constraints that apply strictly to writing style
20067
+ * and presentation rather than task-solving behavior.
20068
+ *
20069
+ * Example usage in agent source:
20070
+ *
20071
+ * ```book
20072
+ * WRITING RULES Keep every response under 120 words
20073
+ * WRITING RULE Always end the message with an emoji
20074
+ * ```
20068
20075
  *
20069
20076
  * @private [🪔] Maybe export the commitments through some package
20070
20077
  */
20071
20078
  class WritingRulesCommitmentDefinition extends BaseCommitmentDefinition {
20072
- constructor() {
20073
- super('WRITING RULES');
20079
+ constructor(type = 'WRITING RULES') {
20080
+ super(type);
20074
20081
  }
20075
20082
  /**
20076
- * Short one-line description of `WRITING RULES`.
20083
+ * Short one-line description of `WRITING RULES`/`WRITING RULE`.
20077
20084
  */
20078
20085
  get description() {
20079
20086
  return 'Add writing-only constraints such as tone, length, formatting, or emoji usage.';
20080
20087
  }
20081
20088
  /**
20082
- * Icon for `WRITING RULES`.
20089
+ * Icon for `WRITING RULES`/`WRITING RULE`.
20083
20090
  */
20084
20091
  get icon() {
20085
20092
  return '📝';
20086
20093
  }
20087
20094
  /**
20088
- * Markdown documentation for `WRITING RULES`.
20095
+ * Markdown documentation for `WRITING RULES`/`WRITING RULE` commitment.
20089
20096
  */
20090
20097
  get documentation() {
20091
20098
  return spaceTrim$1(`
@@ -20095,6 +20102,7 @@ class WritingRulesCommitmentDefinition extends BaseCommitmentDefinition {
20095
20102
 
20096
20103
  ## Key aspects
20097
20104
 
20105
+ - All writing rules are treated equally regardless of singular/plural form.
20098
20106
  - Use it for writing-only constraints such as tone, formatting, length, emoji usage, punctuation, or reading level.
20099
20107
  - Do **not** use it for problem-solving behavior, policy, tool usage, or decision-making logic. Use \`RULE\` for that.
20100
20108
  - Newer writing-rules blocks override conflicting earlier writing-rules blocks.
@@ -20293,7 +20301,8 @@ const COMMITMENT_REGISTRY = [
20293
20301
  new LanguageCommitmentDefinition('LANGUAGE'),
20294
20302
  new LanguageCommitmentDefinition('LANGUAGES'),
20295
20303
  new WritingSampleCommitmentDefinition(),
20296
- new WritingRulesCommitmentDefinition(),
20304
+ new WritingRulesCommitmentDefinition('WRITING RULES'),
20305
+ new WritingRulesCommitmentDefinition('WRITING RULE'),
20297
20306
  new SampleCommitmentDefinition('SAMPLE'),
20298
20307
  new SampleCommitmentDefinition('EXAMPLE'),
20299
20308
  new FormatCommitmentDefinition('FORMAT'),
@@ -24109,6 +24118,11 @@ const HARNESS_DEFINITIONS = {
24109
24118
  label: 'OpenAI Codex',
24110
24119
  commandName: 'codex',
24111
24120
  npmPackageName: '@openai/codex',
24121
+ // Note: The official standalone installer keeps its package in `~/.codex` and links it onto the `PATH`
24122
+ standaloneInstallation: {
24123
+ directoryNames: ['.codex'],
24124
+ updateCommand: 'codex update',
24125
+ },
24112
24126
  },
24113
24127
  'github-copilot': {
24114
24128
  harnessName: 'github-copilot',
@@ -43079,6 +43093,53 @@ async function $askForNpmPackageInstallationApproval(question) {
43079
43093
  }
43080
43094
  // 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`
43081
43095
 
43096
+ /**
43097
+ * Words describing each operation in the terminal output.
43098
+ */
43099
+ const HARNESS_INSTALLATION_OPERATION_WORDS = {
43100
+ install: { running: 'Installing', finished: 'installed' },
43101
+ update: { running: 'Updating', finished: 'updated' },
43102
+ };
43103
+ /**
43104
+ * Runs one command which installs or updates a CLI coding harness.
43105
+ *
43106
+ * A failed installation is reported but never aborts the running command, because the harness check
43107
+ * is only an assistant - the command itself may still work with a manually managed harness.
43108
+ *
43109
+ * Note: `$` is used to indicate that this function is not a pure function - it installs software
43110
+ *
43111
+ * @returns `true` when the command succeeded, `false` when it failed
43112
+ * @private internal utility of `promptbookCli`
43113
+ */
43114
+ async function $runHarnessInstallationCommand(options) {
43115
+ const { definition, operation, command, environment } = options;
43116
+ const operationWords = HARNESS_INSTALLATION_OPERATION_WORDS[operation];
43117
+ console.info(colors.cyan(`${operationWords.running} ${definition.label} with \`${command}\`...`));
43118
+ try {
43119
+ await $execCommand({
43120
+ command,
43121
+ crashOnError: true,
43122
+ isVerbose: true,
43123
+ env: environment ? { ...environment } : undefined,
43124
+ });
43125
+ }
43126
+ catch (error) {
43127
+ assertsError(error);
43128
+ const installErrorMessage = error.message;
43129
+ console.error(colors.red(spaceTrim$1((block) => `
43130
+ Could not ${operation} **${definition.label}**.
43131
+
43132
+ Run \`${command}\` manually and run the command again.
43133
+
43134
+ ${block(installErrorMessage)}
43135
+ `)));
43136
+ return false;
43137
+ }
43138
+ console.info(colors.green(`${definition.label} has been ${operationWords.finished}.`));
43139
+ return true;
43140
+ }
43141
+ // Note: [🟡] Code for CLI harness installation commands [$runHarnessInstallationCommand](src/cli/cli-commands/common/harness/$runHarnessInstallationCommand.ts) should never be published outside of `@promptbook/cli`
43142
+
43082
43143
  /**
43083
43144
  * Builds an npm command which installs or updates a package in the requested existing location.
43084
43145
  *
@@ -43098,9 +43159,12 @@ function buildNpmPackageInstallCommand(npmPackageName, installationLocation) {
43098
43159
  /**
43099
43160
  * Builds the shell command which installs or updates one CLI coding harness globally.
43100
43161
  *
43101
- * The very same command is executed by `$installHarness` and printed as the manual fallback,
43162
+ * The very same command is executed by `$runHarnessInstallationCommand` and printed as the manual fallback,
43102
43163
  * so the user always sees exactly what Promptbook would run.
43103
43164
  *
43165
+ * Note: This command installs the harness through npm, so it may only update a harness which npm really owns,
43166
+ * see `resolveHarnessUpdatePlan`.
43167
+ *
43104
43168
  * @private internal utility of `promptbookCli`
43105
43169
  */
43106
43170
  function buildHarnessInstallCommand(definition) {
@@ -43109,76 +43173,114 @@ function buildHarnessInstallCommand(definition) {
43109
43173
  // Note: [🟡] Code for CLI harness install command [buildHarnessInstallCommand](src/cli/cli-commands/common/harness/buildHarnessInstallCommand.ts) should never be published outside of `@promptbook/cli`
43110
43174
 
43111
43175
  /**
43112
- * Installs or updates one CLI coding harness globally through npm.
43113
- *
43114
- * A failed installation is reported but never aborts the running command, because the harness check
43115
- * is only an assistant - the command itself may still work with a manually managed harness.
43116
- *
43117
- * Note: `$` is used to indicate that this function is not a pure function - it installs a global npm package
43176
+ * Human-readable description of each way a harness can be installed.
43177
+ */
43178
+ const HARNESS_INSTALLATION_METHOD_DESCRIPTIONS = {
43179
+ 'npm-global': 'global npm installation',
43180
+ standalone: 'standalone installation',
43181
+ homebrew: 'Homebrew installation',
43182
+ unknown: 'unknown installation method',
43183
+ };
43184
+ /**
43185
+ * Formats the warning shown when a harness is missing or outdated.
43118
43186
  *
43119
- * @returns `true` when the harness has been installed, `false` when the installation failed
43120
43187
  * @private internal utility of `promptbookCli`
43121
43188
  */
43122
- async function $installHarness(definition) {
43123
- const installCommand = buildHarnessInstallCommand(definition);
43124
- console.info(colors.cyan(`Installing ${definition.label} with \`${installCommand}\`...`));
43125
- try {
43126
- await $execCommand({
43127
- command: installCommand,
43128
- crashOnError: true,
43129
- isVerbose: true,
43130
- env: definition.npmInstallEnvironment ? { ...definition.npmInstallEnvironment } : undefined,
43131
- });
43132
- }
43133
- catch (error) {
43134
- assertsError(error);
43135
- const installErrorMessage = error.message;
43136
- console.error(colors.red(spaceTrim$1((block) => `
43137
- Could not install **${definition.label}**.
43138
-
43139
- Install it manually with \`${installCommand}\` and run the command again.
43189
+ function formatHarnessInstallationWarning(status) {
43190
+ const { definition, installedVersion, latestVersion, installationOrigin } = status;
43191
+ if (status.installationState === 'not-installed') {
43192
+ return spaceTrim$1(`
43193
+ **${definition.label}** is not installed globally.
43140
43194
 
43141
- ${block(installErrorMessage)}
43142
- `)));
43143
- return false;
43195
+ The \`${definition.commandName}\` command was not found, so the \`${definition.harnessName}\` harness cannot run.
43196
+ `);
43144
43197
  }
43145
- console.info(colors.green(`${definition.label} has been installed.`));
43146
- return true;
43198
+ const warningLines = [
43199
+ `**${definition.label}** is outdated.`,
43200
+ '',
43201
+ `Installed version: \`${installedVersion}\``,
43202
+ `Newest version: \`${latestVersion}\``,
43203
+ ];
43204
+ if (installationOrigin.commandPath !== null) {
43205
+ const installationMethodDescription = HARNESS_INSTALLATION_METHOD_DESCRIPTIONS[installationOrigin.installationMethod];
43206
+ warningLines.push(`Installed command: \`${installationOrigin.commandPath}\` *(${installationMethodDescription})*`);
43207
+ }
43208
+ return spaceTrim$1(warningLines.join('\n'));
43147
43209
  }
43148
- // Note: [🟡] Code for CLI harness installation [$installHarness](src/cli/cli-commands/common/harness/$installHarness.ts) should never be published outside of `@promptbook/cli`
43210
+ // Note: [🟡] Code for CLI harness installation warning [formatHarnessInstallationWarning](src/cli/cli-commands/common/harness/formatHarnessInstallationWarning.ts) should never be published outside of `@promptbook/cli`
43149
43211
 
43150
43212
  /**
43151
- * Formats the warning shown when a harness is missing or outdated.
43213
+ * Formats the instruction shown when an outdated harness must be updated by the user.
43152
43214
  *
43153
43215
  * @private internal utility of `promptbookCli`
43154
43216
  */
43155
- function formatHarnessInstallationWarning(status) {
43156
- const { definition, installedVersion, latestVersion } = status;
43157
- if (status.installationState === 'not-installed') {
43217
+ function formatHarnessManualUpdateInstruction(status, updatePlan) {
43218
+ const { definition, installationOrigin } = status;
43219
+ const commandLocation = installationOrigin.commandPath === null
43220
+ ? `The \`${definition.commandName}\` command`
43221
+ : `The \`${definition.commandName}\` command at \`${installationOrigin.commandPath}\``;
43222
+ if (updatePlan.command !== null) {
43158
43223
  return spaceTrim$1(`
43159
- **${definition.label}** is not installed globally.
43224
+ ${commandLocation} is not managed by npm, so Promptbook leaves it alone.
43160
43225
 
43161
- The \`${definition.commandName}\` command was not found, so the \`${definition.harnessName}\` harness cannot run.
43226
+ Update it manually with \`${updatePlan.command}\`.
43162
43227
  `);
43163
43228
  }
43229
+ const unknownInstallationReason = installationOrigin.commandPath === null ? 'could not be located' : 'is not inside the global npm prefix';
43230
+ const npmInstallCommand = buildHarnessInstallCommand(definition);
43164
43231
  return spaceTrim$1(`
43165
- **${definition.label}** is outdated.
43232
+ ${commandLocation} ${unknownInstallationReason}, so Promptbook cannot tell how it was installed.
43166
43233
 
43167
- Installed version: \`${installedVersion}\`
43168
- Newest version: \`${latestVersion}\`
43234
+ Update it the same way it was installed - \`${npmInstallCommand}\` would install a **second** copy of ${definition.label} instead of updating the one which is really used.
43169
43235
  `);
43170
43236
  }
43171
- // Note: [🟡] Code for CLI harness installation warning [formatHarnessInstallationWarning](src/cli/cli-commands/common/harness/formatHarnessInstallationWarning.ts) should never be published outside of `@promptbook/cli`
43237
+ // Note: [🟡] Code for CLI harness manual update instruction [formatHarnessManualUpdateInstruction](src/cli/cli-commands/common/harness/formatHarnessManualUpdateInstruction.ts) should never be published outside of `@promptbook/cli`
43238
+
43239
+ /**
43240
+ * Decides how one outdated CLI coding harness should be updated.
43241
+ *
43242
+ * The plan follows the way the harness was installed, because `npm install -g` updates only a harness
43243
+ * which npm owns and installs a **second** copy of every other one.
43244
+ *
43245
+ * @private internal utility of `promptbookCli`
43246
+ */
43247
+ function resolveHarnessUpdatePlan(definition, installationMethod) {
43248
+ if (installationMethod === 'npm-global') {
43249
+ return {
43250
+ command: buildHarnessInstallCommand(definition),
43251
+ isRunnableByPromptbook: true,
43252
+ environment: definition.npmInstallEnvironment,
43253
+ };
43254
+ }
43255
+ if (installationMethod === 'standalone' && definition.standaloneInstallation !== undefined) {
43256
+ return {
43257
+ command: definition.standaloneInstallation.updateCommand,
43258
+ isRunnableByPromptbook: true,
43259
+ };
43260
+ }
43261
+ if (installationMethod === 'homebrew') {
43262
+ // Note: The formula may be named differently than the command, so the update is only suggested
43263
+ return {
43264
+ command: `brew upgrade ${definition.commandName}`,
43265
+ isRunnableByPromptbook: false,
43266
+ };
43267
+ }
43268
+ return {
43269
+ command: null,
43270
+ isRunnableByPromptbook: false,
43271
+ };
43272
+ }
43273
+ // Note: [🟡] Code for CLI harness update planning [resolveHarnessUpdatePlan](src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.ts) should never be published outside of `@promptbook/cli`
43172
43274
 
43173
43275
  /**
43174
43276
  * Reports one detected harness installation status and offers to install or update the harness.
43175
43277
  *
43176
- * Note: `$` is used to indicate that this function is not a pure function - it asks the user and installs npm packages
43278
+ * Note: `$` is used to indicate that this function is not a pure function - it asks the user and installs software
43177
43279
  *
43178
43280
  * @private internal utility of `promptbookCli`
43179
43281
  */
43180
43282
  async function $applyHarnessInstallationStatus(status) {
43181
- const { definition, installationState, installedVersion, latestVersion } = status;
43283
+ const { definition, installationState, installedVersion } = status;
43182
43284
  if (installationState === 'up-to-date') {
43183
43285
  console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
43184
43286
  return;
@@ -43192,14 +43294,50 @@ async function $applyHarnessInstallationStatus(status) {
43192
43294
  return;
43193
43295
  }
43194
43296
  console.warn(colors.yellow(formatHarnessInstallationWarning(status)));
43195
- const isInstallationApproved = await $askForNpmPackageInstallationApproval(installationState === 'not-installed'
43196
- ? `Install ${definition.label} globally now?`
43197
- : `Update ${definition.label} to ${latestVersion} now?`);
43297
+ if (installationState === 'not-installed') {
43298
+ await $applyMissingHarnessInstallation(definition);
43299
+ return;
43300
+ }
43301
+ await $applyOutdatedHarnessInstallation(status);
43302
+ }
43303
+ /**
43304
+ * Offers to install a missing harness globally through npm.
43305
+ */
43306
+ async function $applyMissingHarnessInstallation(definition) {
43307
+ const installCommand = buildHarnessInstallCommand(definition);
43308
+ const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`);
43198
43309
  if (!isInstallationApproved) {
43199
- console.info(colors.gray(`Skipped, run \`${buildHarnessInstallCommand(definition)}\` to do it manually.`));
43310
+ console.info(colors.gray(`Skipped, run \`${installCommand}\` to do it manually.`));
43311
+ return;
43312
+ }
43313
+ await $runHarnessInstallationCommand({
43314
+ definition,
43315
+ operation: 'install',
43316
+ command: installCommand,
43317
+ environment: definition.npmInstallEnvironment,
43318
+ });
43319
+ }
43320
+ /**
43321
+ * Offers to update an outdated harness exactly where it is installed.
43322
+ */
43323
+ async function $applyOutdatedHarnessInstallation(status) {
43324
+ const { definition, latestVersion, installationOrigin } = status;
43325
+ const updatePlan = resolveHarnessUpdatePlan(definition, installationOrigin.installationMethod);
43326
+ if (updatePlan.command === null || !updatePlan.isRunnableByPromptbook) {
43327
+ console.info(colors.gray(formatHarnessManualUpdateInstruction(status, updatePlan)));
43328
+ return;
43329
+ }
43330
+ const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`);
43331
+ if (!isUpdateApproved) {
43332
+ console.info(colors.gray(`Skipped, run \`${updatePlan.command}\` to do it manually.`));
43200
43333
  return;
43201
43334
  }
43202
- await $installHarness(definition);
43335
+ await $runHarnessInstallationCommand({
43336
+ definition,
43337
+ operation: 'update',
43338
+ command: updatePlan.command,
43339
+ environment: updatePlan.environment,
43340
+ });
43203
43341
  }
43204
43342
  // Note: [🟡] Code for CLI harness installation handling [$applyHarnessInstallationStatus](src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts) should never be published outside of `@promptbook/cli`
43205
43343
 
@@ -43399,6 +43537,186 @@ function comparePrereleaseIdentifier(firstIdentifier, secondIdentifier) {
43399
43537
  }
43400
43538
  // Note: [🟡] Code for CLI npm package version comparison [isNpmPackageVersionOutdated](src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.ts) should never be published outside of `@promptbook/cli`
43401
43539
 
43540
+ /**
43541
+ * Time limit for asking npm where it installs global packages.
43542
+ */
43543
+ const NPM_GLOBAL_PREFIX_COMMAND_TIMEOUT_MS = 30 * 1000;
43544
+ /**
43545
+ * Cached lookup, because the global npm prefix cannot change while one command runs and several
43546
+ * harnesses are checked at the same time.
43547
+ */
43548
+ let npmGlobalPrefixPathPromise = null;
43549
+ /**
43550
+ * Reads the root directory under which npm installs global packages.
43551
+ *
43552
+ * Note: `$` is used to indicate that this function is not a pure function - it runs npm
43553
+ *
43554
+ * @returns The global npm prefix or `null` when npm could not be asked for it
43555
+ * @private internal utility of `promptbookCli`
43556
+ */
43557
+ function $resolveNpmGlobalPrefixPath() {
43558
+ if (npmGlobalPrefixPathPromise === null) {
43559
+ npmGlobalPrefixPathPromise = $readNpmGlobalPrefixPath();
43560
+ }
43561
+ return npmGlobalPrefixPathPromise;
43562
+ }
43563
+ /**
43564
+ * Asks npm for the global prefix once.
43565
+ */
43566
+ async function $readNpmGlobalPrefixPath() {
43567
+ const output = await $execCommand({
43568
+ command: 'npm prefix --global --loglevel=error',
43569
+ crashOnError: true,
43570
+ timeout: NPM_GLOBAL_PREFIX_COMMAND_TIMEOUT_MS,
43571
+ isVerbose: false,
43572
+ }).catch(() => '');
43573
+ // Note: npm prints the prefix as the last line, warnings emitted before it are ignored
43574
+ const prefixPath = output
43575
+ .split('\n')
43576
+ .map((line) => line.trim())
43577
+ .filter((line) => line !== '')
43578
+ .pop();
43579
+ return prefixPath !== null && prefixPath !== void 0 ? prefixPath : null;
43580
+ }
43581
+ // Note: [🟡] Code for CLI npm global prefix lookup [$resolveNpmGlobalPrefixPath](src/cli/cli-commands/common/npm/$resolveNpmGlobalPrefixPath.ts) should never be published outside of `@promptbook/cli`
43582
+
43583
+ /**
43584
+ * Time limit for looking up where the harness command lives.
43585
+ */
43586
+ const HARNESS_COMMAND_LOOKUP_TIMEOUT_MS = 30 * 1000;
43587
+ /**
43588
+ * Finds out which file is really executed when the harness command runs.
43589
+ *
43590
+ * The lookup deliberately uses the very same `PATH` which runs the harness, so it reports the copy of the
43591
+ * harness which Promptbook itself would use. Symbolic links are followed, because installers like the
43592
+ * standalone OpenAI Codex installer only link their package into a directory on `PATH`.
43593
+ *
43594
+ * Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
43595
+ *
43596
+ * @returns Resolved path of the harness command or `null` when the command is not available
43597
+ * @private internal utility of `promptbookCli`
43598
+ */
43599
+ async function $resolveHarnessCommandPath(definition) {
43600
+ const lookupCommand = process.platform === 'win32' ? `where ${definition.commandName}` : `command -v ${definition.commandName}`;
43601
+ const output = await $execCommand({
43602
+ command: lookupCommand,
43603
+ crashOnError: true,
43604
+ timeout: HARNESS_COMMAND_LOOKUP_TIMEOUT_MS,
43605
+ isVerbose: false,
43606
+ }).catch(() => '');
43607
+ // Note: `where` lists every match on its own line, only the first one is really executed
43608
+ const commandPath = output
43609
+ .split('\n')
43610
+ .map((line) => line.trim())
43611
+ .find((line) => line !== '');
43612
+ if (commandPath === undefined) {
43613
+ return null;
43614
+ }
43615
+ return await realpath(commandPath).catch(() => commandPath);
43616
+ }
43617
+ // Note: [🟡] Code for CLI harness command lookup [$resolveHarnessCommandPath](src/cli/cli-commands/common/harness/$resolveHarnessCommandPath.ts) should never be published outside of `@promptbook/cli`
43618
+
43619
+ /**
43620
+ * Origin used when the harness command was not looked up at all, for example when the update check is disabled.
43621
+ *
43622
+ * @private internal utility of `promptbookCli`
43623
+ */
43624
+ const UNKNOWN_HARNESS_INSTALLATION_ORIGIN = {
43625
+ commandPath: null,
43626
+ installationMethod: 'unknown',
43627
+ };
43628
+ // Note: [🟡] Code for CLI harness installation origin [HarnessInstallationOrigin](src/cli/cli-commands/common/harness/HarnessInstallationOrigin.ts) should never be published outside of `@promptbook/cli`
43629
+
43630
+ /**
43631
+ * Directory which holds npm packages, so it appears in the resolved path of every globally installed npm binary
43632
+ * on systems where the global binary is only a symbolic link into the package.
43633
+ *
43634
+ * Note: Written in lower case because it is compared against normalized paths.
43635
+ */
43636
+ const NODE_MODULES_DIRECTORY_NAME$1 = 'node_modules';
43637
+ /**
43638
+ * Directory into which Homebrew unpacks every formula, so it appears in the resolved path of
43639
+ * everything Homebrew manages and in nothing else.
43640
+ *
43641
+ * Note: Written in lower case because it is compared against normalized paths.
43642
+ */
43643
+ const HOMEBREW_DIRECTORY_NAME = 'cellar';
43644
+ /**
43645
+ * Decides how a harness command was installed from the place where it really lives.
43646
+ *
43647
+ * Only a command which npm owns may be updated with `npm install -g`, every other command has to be
43648
+ * updated where it is installed, otherwise the update silently adds a second copy of the harness.
43649
+ *
43650
+ * @private internal utility of `promptbookCli`
43651
+ */
43652
+ function resolveHarnessInstallationMethodFromPaths(paths) {
43653
+ var _a, _b;
43654
+ const { definition, commandPath, npmGlobalPrefixPath } = paths;
43655
+ const normalizedCommandPath = normalizeInstallationPath(commandPath);
43656
+ const commandPathDirectoryNames = normalizedCommandPath.split('/');
43657
+ // Note: A Homebrew formula may bundle its own `node_modules` and may live under the npm prefix of a
43658
+ // Homebrew-installed Node, so Homebrew has to be recognized before anything npm-shaped
43659
+ if (commandPathDirectoryNames.includes(HOMEBREW_DIRECTORY_NAME)) {
43660
+ return 'homebrew';
43661
+ }
43662
+ const standaloneDirectoryNames = (_b = (_a = definition.standaloneInstallation) === null || _a === void 0 ? void 0 : _a.directoryNames) !== null && _b !== void 0 ? _b : [];
43663
+ if (standaloneDirectoryNames.some((directoryName) => commandPathDirectoryNames.includes(normalizeInstallationPath(directoryName)))) {
43664
+ return 'standalone';
43665
+ }
43666
+ // Note: npm is not always reachable, but its own package directory is a reliable fallback marker
43667
+ if (normalizedCommandPath.includes(`/${NODE_MODULES_DIRECTORY_NAME$1}/${normalizeInstallationPath(definition.npmPackageName)}/`)) {
43668
+ return 'npm-global';
43669
+ }
43670
+ if (isPathInsideDirectory(normalizedCommandPath, npmGlobalPrefixPath)) {
43671
+ return 'npm-global';
43672
+ }
43673
+ return 'unknown';
43674
+ }
43675
+ /**
43676
+ * Normalizes one path so that paths coming from different tools and platforms can be compared.
43677
+ *
43678
+ * Note: The comparison is case-insensitive because Windows paths are, and two harness paths which differ
43679
+ * only in letter casing do not exist in practice.
43680
+ */
43681
+ function normalizeInstallationPath(path) {
43682
+ return path.trim().replace(/\\/gu, '/').replace(/\/+$/u, '').toLowerCase();
43683
+ }
43684
+ /**
43685
+ * Checks whether one path lies inside a directory.
43686
+ */
43687
+ function isPathInsideDirectory(normalizedPath, directoryPath) {
43688
+ if (directoryPath === null) {
43689
+ return false;
43690
+ }
43691
+ const normalizedDirectoryPath = normalizeInstallationPath(directoryPath);
43692
+ return normalizedDirectoryPath !== '' && normalizedPath.startsWith(`${normalizedDirectoryPath}/`);
43693
+ }
43694
+ // Note: [🟡] Code for CLI harness installation method detection [resolveHarnessInstallationMethodFromPaths](src/cli/cli-commands/common/harness/resolveHarnessInstallationMethodFromPaths.ts) should never be published outside of `@promptbook/cli`
43695
+
43696
+ /**
43697
+ * Finds out where one CLI coding harness is really installed and which tool manages it.
43698
+ *
43699
+ * Note: `$` is used to indicate that this function is not a pure function - it runs shell commands
43700
+ *
43701
+ * @private internal utility of `promptbookCli`
43702
+ */
43703
+ async function $resolveHarnessInstallationOrigin(definition) {
43704
+ const commandPath = await $resolveHarnessCommandPath(definition);
43705
+ if (commandPath === null) {
43706
+ return UNKNOWN_HARNESS_INSTALLATION_ORIGIN;
43707
+ }
43708
+ const npmGlobalPrefixPath = await $resolveNpmGlobalPrefixPath();
43709
+ return {
43710
+ commandPath,
43711
+ installationMethod: resolveHarnessInstallationMethodFromPaths({
43712
+ definition,
43713
+ commandPath,
43714
+ npmGlobalPrefixPath,
43715
+ }),
43716
+ };
43717
+ }
43718
+ // Note: [🟡] Code for CLI harness installation origin detection [$resolveHarnessInstallationOrigin](src/cli/cli-commands/common/harness/$resolveHarnessInstallationOrigin.ts) should never be published outside of `@promptbook/cli`
43719
+
43402
43720
  /**
43403
43721
  * Detects whether one CLI coding harness is installed globally and, when enabled, whether it is up to date.
43404
43722
  *
@@ -43407,17 +43725,22 @@ function comparePrereleaseIdentifier(firstIdentifier, secondIdentifier) {
43407
43725
  * @private internal utility of `promptbookCli`
43408
43726
  */
43409
43727
  async function $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled = true) {
43410
- const [installedVersion, latestVersion] = await Promise.all([
43728
+ // Note: The origin is only needed to update an outdated harness, so it is not looked up without the update check
43729
+ const [installedVersion, latestVersion, installationOrigin] = await Promise.all([
43411
43730
  $resolveInstalledHarnessVersion(definition),
43412
43731
  isHarnessUpdateCheckEnabled
43413
43732
  ? $resolveLatestNpmPackageVersion(definition.npmPackageName)
43414
43733
  : Promise.resolve(null),
43734
+ isHarnessUpdateCheckEnabled
43735
+ ? $resolveHarnessInstallationOrigin(definition)
43736
+ : Promise.resolve(UNKNOWN_HARNESS_INSTALLATION_ORIGIN),
43415
43737
  ]);
43416
43738
  return {
43417
43739
  definition,
43418
43740
  installationState: resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled),
43419
43741
  installedVersion,
43420
43742
  latestVersion,
43743
+ installationOrigin,
43421
43744
  };
43422
43745
  }
43423
43746
  /**
@@ -43476,7 +43799,7 @@ function resolveCheckedHarnessDefinitions(harnessNames) {
43476
43799
  * @private internal utility of `promptbookCli`
43477
43800
  */
43478
43801
  function addHarnessUpdateOption(command) {
43479
- command.option('--no-harness-update', 'Skip checking whether globally installed coding harnesses are up to date');
43802
+ command.option('--no-harness-update', 'Skip checking whether the installed coding harnesses are up to date');
43480
43803
  }
43481
43804
  /**
43482
43805
  * Converts the Commander harness-update flag into the normalized harness-check option.
@@ -44053,7 +44376,7 @@ function $initializeCoderInitCommand(program) {
44053
44376
  - CODING_AGENT_GIT_EMAIL
44054
44377
  - CODING_AGENT_GIT_SIGNING_KEY
44055
44378
 
44056
- Checks that the coding harnesses are installed globally and up to date unless \`--no-harness-update\` is used:
44379
+ Checks that the coding harnesses are installed and up to date unless \`--no-harness-update\` is used:
44057
44380
  ${block(listCheckedHarnessLabels())}
44058
44381
 
44059
44382
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
@@ -44161,7 +44484,7 @@ function $initializeCoderPingCommand(program) {
44161
44484
  - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44162
44485
  - Optional --period keeps the quota window refreshing by pinging once per period until stopped
44163
44486
  - Leaves the project exactly as it was — nothing is read, written, changed or committed
44164
- - Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
44487
+ - Checks that the selected harness is installed and up to date unless --no-harness-update is used
44165
44488
  - Use --no-ui to stream the raw harness output instead of only the compact result
44166
44489
  `));
44167
44490
  addPromptRunnerSelectionOptions(command);
@@ -44655,7 +44978,7 @@ function $initializeCoderRunCommand(program) {
44655
44978
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
44656
44979
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
44657
44980
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44658
- - Checks that the selected harness is installed globally and up to date before the first prompt unless --no-harness-update is used
44981
+ - Checks that the selected harness is installed and up to date before the first prompt unless --no-harness-update is used
44659
44982
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
44660
44983
  - Supports GPG signing of commits
44661
44984
  - Optional pre-coding test run that can stop or repair pre-existing failures
@@ -44812,7 +45135,7 @@ function $initializeCoderServerCommand(program) {
44812
45135
 
44813
45136
  Features:
44814
45137
  - Runs the same prompt processing as \`ptbk coder run\`
44815
- - Checks that the selected harness is installed globally and up to date on startup unless --no-harness-update is used
45138
+ - Checks that the selected harness is installed and up to date on startup unless --no-harness-update is used
44816
45139
  - Does not exit when all prompts are done; polls for new prompt files instead
44817
45140
  - Serves a kanban board at http://localhost:<port> for visual progress tracking
44818
45141
  - Allows editing prompt files directly from the browser (Trello-style)