@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/umd/index.umd.js CHANGED
@@ -58,7 +58,7 @@
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-25';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-27';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3713,7 +3713,7 @@
3713
3713
  *
3714
3714
  * @private internal constant of `buildAgentsServer`
3715
3715
  */
3716
- const NODE_MODULES_DIRECTORY_NAME$1 = 'node_modules';
3716
+ const NODE_MODULES_DIRECTORY_NAME$2 = 'node_modules';
3717
3717
 
3718
3718
  /**
3719
3719
  * Returns true when one path is nested below a `node_modules` segment.
@@ -3721,7 +3721,7 @@
3721
3721
  * @private internal utility of `buildAgentsServer`
3722
3722
  */
3723
3723
  function isPathInsideNodeModules(path) {
3724
- return path.split(/[\\/]+/u).includes(NODE_MODULES_DIRECTORY_NAME$1);
3724
+ return path.split(/[\\/]+/u).includes(NODE_MODULES_DIRECTORY_NAME$2);
3725
3725
  }
3726
3726
 
3727
3727
  /**
@@ -3834,7 +3834,7 @@
3834
3834
  * Links the materialized runtime back to the package dependency tree used by the installed CLI.
3835
3835
  */
3836
3836
  async function ensureMaterializedRuntimeNodeModulesLink(options) {
3837
- const nodeModulesLinkPath = path.join(options.materializedRuntimeRootPath, NODE_MODULES_DIRECTORY_NAME$1);
3837
+ const nodeModulesLinkPath = path.join(options.materializedRuntimeRootPath, NODE_MODULES_DIRECTORY_NAME$2);
3838
3838
  try {
3839
3839
  const existingLinkStats = await promises.lstat(nodeModulesLinkPath);
3840
3840
  await promises.rm(nodeModulesLinkPath, {
@@ -3920,7 +3920,7 @@
3920
3920
  */
3921
3921
  function resolveNodeModulesPath(nextCliPath) {
3922
3922
  const normalizedNextCliPath = nextCliPath.replace(/\\/gu, '/');
3923
- const marker = `/${NODE_MODULES_DIRECTORY_NAME$1}/next/`;
3923
+ const marker = `/${NODE_MODULES_DIRECTORY_NAME$2}/next/`;
3924
3924
  const markerIndex = normalizedNextCliPath.lastIndexOf(marker);
3925
3925
  if (markerIndex === -1) {
3926
3926
  return path.resolve(nextCliPath, '..', '..', '..');
@@ -20073,29 +20073,36 @@
20073
20073
  /**
20074
20074
  * `WRITING RULES` commitment definition.
20075
20075
  *
20076
- * It adds constraints that apply strictly to writing style and presentation
20077
- * rather than task-solving behavior.
20076
+ * The `WRITING RULES`/`WRITING RULE` commitment adds constraints that apply strictly to writing style
20077
+ * and presentation rather than task-solving behavior.
20078
+ *
20079
+ * Example usage in agent source:
20080
+ *
20081
+ * ```book
20082
+ * WRITING RULES Keep every response under 120 words
20083
+ * WRITING RULE Always end the message with an emoji
20084
+ * ```
20078
20085
  *
20079
20086
  * @private [🪔] Maybe export the commitments through some package
20080
20087
  */
20081
20088
  class WritingRulesCommitmentDefinition extends BaseCommitmentDefinition {
20082
- constructor() {
20083
- super('WRITING RULES');
20089
+ constructor(type = 'WRITING RULES') {
20090
+ super(type);
20084
20091
  }
20085
20092
  /**
20086
- * Short one-line description of `WRITING RULES`.
20093
+ * Short one-line description of `WRITING RULES`/`WRITING RULE`.
20087
20094
  */
20088
20095
  get description() {
20089
20096
  return 'Add writing-only constraints such as tone, length, formatting, or emoji usage.';
20090
20097
  }
20091
20098
  /**
20092
- * Icon for `WRITING RULES`.
20099
+ * Icon for `WRITING RULES`/`WRITING RULE`.
20093
20100
  */
20094
20101
  get icon() {
20095
20102
  return '📝';
20096
20103
  }
20097
20104
  /**
20098
- * Markdown documentation for `WRITING RULES`.
20105
+ * Markdown documentation for `WRITING RULES`/`WRITING RULE` commitment.
20099
20106
  */
20100
20107
  get documentation() {
20101
20108
  return _spaceTrim.spaceTrim(`
@@ -20105,6 +20112,7 @@
20105
20112
 
20106
20113
  ## Key aspects
20107
20114
 
20115
+ - All writing rules are treated equally regardless of singular/plural form.
20108
20116
  - Use it for writing-only constraints such as tone, formatting, length, emoji usage, punctuation, or reading level.
20109
20117
  - Do **not** use it for problem-solving behavior, policy, tool usage, or decision-making logic. Use \`RULE\` for that.
20110
20118
  - Newer writing-rules blocks override conflicting earlier writing-rules blocks.
@@ -20303,7 +20311,8 @@
20303
20311
  new LanguageCommitmentDefinition('LANGUAGE'),
20304
20312
  new LanguageCommitmentDefinition('LANGUAGES'),
20305
20313
  new WritingSampleCommitmentDefinition(),
20306
- new WritingRulesCommitmentDefinition(),
20314
+ new WritingRulesCommitmentDefinition('WRITING RULES'),
20315
+ new WritingRulesCommitmentDefinition('WRITING RULE'),
20307
20316
  new SampleCommitmentDefinition('SAMPLE'),
20308
20317
  new SampleCommitmentDefinition('EXAMPLE'),
20309
20318
  new FormatCommitmentDefinition('FORMAT'),
@@ -24119,6 +24128,11 @@
24119
24128
  label: 'OpenAI Codex',
24120
24129
  commandName: 'codex',
24121
24130
  npmPackageName: '@openai/codex',
24131
+ // Note: The official standalone installer keeps its package in `~/.codex` and links it onto the `PATH`
24132
+ standaloneInstallation: {
24133
+ directoryNames: ['.codex'],
24134
+ updateCommand: 'codex update',
24135
+ },
24122
24136
  },
24123
24137
  'github-copilot': {
24124
24138
  harnessName: 'github-copilot',
@@ -43089,6 +43103,53 @@
43089
43103
  }
43090
43104
  // 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`
43091
43105
 
43106
+ /**
43107
+ * Words describing each operation in the terminal output.
43108
+ */
43109
+ const HARNESS_INSTALLATION_OPERATION_WORDS = {
43110
+ install: { running: 'Installing', finished: 'installed' },
43111
+ update: { running: 'Updating', finished: 'updated' },
43112
+ };
43113
+ /**
43114
+ * Runs one command which installs or updates a CLI coding harness.
43115
+ *
43116
+ * A failed installation is reported but never aborts the running command, because the harness check
43117
+ * is only an assistant - the command itself may still work with a manually managed harness.
43118
+ *
43119
+ * Note: `$` is used to indicate that this function is not a pure function - it installs software
43120
+ *
43121
+ * @returns `true` when the command succeeded, `false` when it failed
43122
+ * @private internal utility of `promptbookCli`
43123
+ */
43124
+ async function $runHarnessInstallationCommand(options) {
43125
+ const { definition, operation, command, environment } = options;
43126
+ const operationWords = HARNESS_INSTALLATION_OPERATION_WORDS[operation];
43127
+ console.info(colors__default["default"].cyan(`${operationWords.running} ${definition.label} with \`${command}\`...`));
43128
+ try {
43129
+ await $execCommand({
43130
+ command,
43131
+ crashOnError: true,
43132
+ isVerbose: true,
43133
+ env: environment ? { ...environment } : undefined,
43134
+ });
43135
+ }
43136
+ catch (error) {
43137
+ assertsError(error);
43138
+ const installErrorMessage = error.message;
43139
+ console.error(colors__default["default"].red(_spaceTrim.spaceTrim((block) => `
43140
+ Could not ${operation} **${definition.label}**.
43141
+
43142
+ Run \`${command}\` manually and run the command again.
43143
+
43144
+ ${block(installErrorMessage)}
43145
+ `)));
43146
+ return false;
43147
+ }
43148
+ console.info(colors__default["default"].green(`${definition.label} has been ${operationWords.finished}.`));
43149
+ return true;
43150
+ }
43151
+ // Note: [🟡] Code for CLI harness installation commands [$runHarnessInstallationCommand](src/cli/cli-commands/common/harness/$runHarnessInstallationCommand.ts) should never be published outside of `@promptbook/cli`
43152
+
43092
43153
  /**
43093
43154
  * Builds an npm command which installs or updates a package in the requested existing location.
43094
43155
  *
@@ -43108,9 +43169,12 @@
43108
43169
  /**
43109
43170
  * Builds the shell command which installs or updates one CLI coding harness globally.
43110
43171
  *
43111
- * The very same command is executed by `$installHarness` and printed as the manual fallback,
43172
+ * The very same command is executed by `$runHarnessInstallationCommand` and printed as the manual fallback,
43112
43173
  * so the user always sees exactly what Promptbook would run.
43113
43174
  *
43175
+ * Note: This command installs the harness through npm, so it may only update a harness which npm really owns,
43176
+ * see `resolveHarnessUpdatePlan`.
43177
+ *
43114
43178
  * @private internal utility of `promptbookCli`
43115
43179
  */
43116
43180
  function buildHarnessInstallCommand(definition) {
@@ -43119,76 +43183,114 @@
43119
43183
  // Note: [🟡] Code for CLI harness install command [buildHarnessInstallCommand](src/cli/cli-commands/common/harness/buildHarnessInstallCommand.ts) should never be published outside of `@promptbook/cli`
43120
43184
 
43121
43185
  /**
43122
- * Installs or updates one CLI coding harness globally through npm.
43123
- *
43124
- * A failed installation is reported but never aborts the running command, because the harness check
43125
- * is only an assistant - the command itself may still work with a manually managed harness.
43126
- *
43127
- * Note: `$` is used to indicate that this function is not a pure function - it installs a global npm package
43186
+ * Human-readable description of each way a harness can be installed.
43187
+ */
43188
+ const HARNESS_INSTALLATION_METHOD_DESCRIPTIONS = {
43189
+ 'npm-global': 'global npm installation',
43190
+ standalone: 'standalone installation',
43191
+ homebrew: 'Homebrew installation',
43192
+ unknown: 'unknown installation method',
43193
+ };
43194
+ /**
43195
+ * Formats the warning shown when a harness is missing or outdated.
43128
43196
  *
43129
- * @returns `true` when the harness has been installed, `false` when the installation failed
43130
43197
  * @private internal utility of `promptbookCli`
43131
43198
  */
43132
- async function $installHarness(definition) {
43133
- const installCommand = buildHarnessInstallCommand(definition);
43134
- console.info(colors__default["default"].cyan(`Installing ${definition.label} with \`${installCommand}\`...`));
43135
- try {
43136
- await $execCommand({
43137
- command: installCommand,
43138
- crashOnError: true,
43139
- isVerbose: true,
43140
- env: definition.npmInstallEnvironment ? { ...definition.npmInstallEnvironment } : undefined,
43141
- });
43142
- }
43143
- catch (error) {
43144
- assertsError(error);
43145
- const installErrorMessage = error.message;
43146
- console.error(colors__default["default"].red(_spaceTrim.spaceTrim((block) => `
43147
- Could not install **${definition.label}**.
43148
-
43149
- Install it manually with \`${installCommand}\` and run the command again.
43199
+ function formatHarnessInstallationWarning(status) {
43200
+ const { definition, installedVersion, latestVersion, installationOrigin } = status;
43201
+ if (status.installationState === 'not-installed') {
43202
+ return _spaceTrim.spaceTrim(`
43203
+ **${definition.label}** is not installed globally.
43150
43204
 
43151
- ${block(installErrorMessage)}
43152
- `)));
43153
- return false;
43205
+ The \`${definition.commandName}\` command was not found, so the \`${definition.harnessName}\` harness cannot run.
43206
+ `);
43154
43207
  }
43155
- console.info(colors__default["default"].green(`${definition.label} has been installed.`));
43156
- return true;
43208
+ const warningLines = [
43209
+ `**${definition.label}** is outdated.`,
43210
+ '',
43211
+ `Installed version: \`${installedVersion}\``,
43212
+ `Newest version: \`${latestVersion}\``,
43213
+ ];
43214
+ if (installationOrigin.commandPath !== null) {
43215
+ const installationMethodDescription = HARNESS_INSTALLATION_METHOD_DESCRIPTIONS[installationOrigin.installationMethod];
43216
+ warningLines.push(`Installed command: \`${installationOrigin.commandPath}\` *(${installationMethodDescription})*`);
43217
+ }
43218
+ return _spaceTrim.spaceTrim(warningLines.join('\n'));
43157
43219
  }
43158
- // Note: [🟡] Code for CLI harness installation [$installHarness](src/cli/cli-commands/common/harness/$installHarness.ts) should never be published outside of `@promptbook/cli`
43220
+ // Note: [🟡] Code for CLI harness installation warning [formatHarnessInstallationWarning](src/cli/cli-commands/common/harness/formatHarnessInstallationWarning.ts) should never be published outside of `@promptbook/cli`
43159
43221
 
43160
43222
  /**
43161
- * Formats the warning shown when a harness is missing or outdated.
43223
+ * Formats the instruction shown when an outdated harness must be updated by the user.
43162
43224
  *
43163
43225
  * @private internal utility of `promptbookCli`
43164
43226
  */
43165
- function formatHarnessInstallationWarning(status) {
43166
- const { definition, installedVersion, latestVersion } = status;
43167
- if (status.installationState === 'not-installed') {
43227
+ function formatHarnessManualUpdateInstruction(status, updatePlan) {
43228
+ const { definition, installationOrigin } = status;
43229
+ const commandLocation = installationOrigin.commandPath === null
43230
+ ? `The \`${definition.commandName}\` command`
43231
+ : `The \`${definition.commandName}\` command at \`${installationOrigin.commandPath}\``;
43232
+ if (updatePlan.command !== null) {
43168
43233
  return _spaceTrim.spaceTrim(`
43169
- **${definition.label}** is not installed globally.
43234
+ ${commandLocation} is not managed by npm, so Promptbook leaves it alone.
43170
43235
 
43171
- The \`${definition.commandName}\` command was not found, so the \`${definition.harnessName}\` harness cannot run.
43236
+ Update it manually with \`${updatePlan.command}\`.
43172
43237
  `);
43173
43238
  }
43239
+ const unknownInstallationReason = installationOrigin.commandPath === null ? 'could not be located' : 'is not inside the global npm prefix';
43240
+ const npmInstallCommand = buildHarnessInstallCommand(definition);
43174
43241
  return _spaceTrim.spaceTrim(`
43175
- **${definition.label}** is outdated.
43242
+ ${commandLocation} ${unknownInstallationReason}, so Promptbook cannot tell how it was installed.
43176
43243
 
43177
- Installed version: \`${installedVersion}\`
43178
- Newest version: \`${latestVersion}\`
43244
+ 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.
43179
43245
  `);
43180
43246
  }
43181
- // Note: [🟡] Code for CLI harness installation warning [formatHarnessInstallationWarning](src/cli/cli-commands/common/harness/formatHarnessInstallationWarning.ts) should never be published outside of `@promptbook/cli`
43247
+ // 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`
43248
+
43249
+ /**
43250
+ * Decides how one outdated CLI coding harness should be updated.
43251
+ *
43252
+ * The plan follows the way the harness was installed, because `npm install -g` updates only a harness
43253
+ * which npm owns and installs a **second** copy of every other one.
43254
+ *
43255
+ * @private internal utility of `promptbookCli`
43256
+ */
43257
+ function resolveHarnessUpdatePlan(definition, installationMethod) {
43258
+ if (installationMethod === 'npm-global') {
43259
+ return {
43260
+ command: buildHarnessInstallCommand(definition),
43261
+ isRunnableByPromptbook: true,
43262
+ environment: definition.npmInstallEnvironment,
43263
+ };
43264
+ }
43265
+ if (installationMethod === 'standalone' && definition.standaloneInstallation !== undefined) {
43266
+ return {
43267
+ command: definition.standaloneInstallation.updateCommand,
43268
+ isRunnableByPromptbook: true,
43269
+ };
43270
+ }
43271
+ if (installationMethod === 'homebrew') {
43272
+ // Note: The formula may be named differently than the command, so the update is only suggested
43273
+ return {
43274
+ command: `brew upgrade ${definition.commandName}`,
43275
+ isRunnableByPromptbook: false,
43276
+ };
43277
+ }
43278
+ return {
43279
+ command: null,
43280
+ isRunnableByPromptbook: false,
43281
+ };
43282
+ }
43283
+ // Note: [🟡] Code for CLI harness update planning [resolveHarnessUpdatePlan](src/cli/cli-commands/common/harness/resolveHarnessUpdatePlan.ts) should never be published outside of `@promptbook/cli`
43182
43284
 
43183
43285
  /**
43184
43286
  * Reports one detected harness installation status and offers to install or update the harness.
43185
43287
  *
43186
- * Note: `$` is used to indicate that this function is not a pure function - it asks the user and installs npm packages
43288
+ * Note: `$` is used to indicate that this function is not a pure function - it asks the user and installs software
43187
43289
  *
43188
43290
  * @private internal utility of `promptbookCli`
43189
43291
  */
43190
43292
  async function $applyHarnessInstallationStatus(status) {
43191
- const { definition, installationState, installedVersion, latestVersion } = status;
43293
+ const { definition, installationState, installedVersion } = status;
43192
43294
  if (installationState === 'up-to-date') {
43193
43295
  console.info(colors__default["default"].gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
43194
43296
  return;
@@ -43202,14 +43304,50 @@
43202
43304
  return;
43203
43305
  }
43204
43306
  console.warn(colors__default["default"].yellow(formatHarnessInstallationWarning(status)));
43205
- const isInstallationApproved = await $askForNpmPackageInstallationApproval(installationState === 'not-installed'
43206
- ? `Install ${definition.label} globally now?`
43207
- : `Update ${definition.label} to ${latestVersion} now?`);
43307
+ if (installationState === 'not-installed') {
43308
+ await $applyMissingHarnessInstallation(definition);
43309
+ return;
43310
+ }
43311
+ await $applyOutdatedHarnessInstallation(status);
43312
+ }
43313
+ /**
43314
+ * Offers to install a missing harness globally through npm.
43315
+ */
43316
+ async function $applyMissingHarnessInstallation(definition) {
43317
+ const installCommand = buildHarnessInstallCommand(definition);
43318
+ const isInstallationApproved = await $askForNpmPackageInstallationApproval(`Install ${definition.label} globally now?`);
43208
43319
  if (!isInstallationApproved) {
43209
- console.info(colors__default["default"].gray(`Skipped, run \`${buildHarnessInstallCommand(definition)}\` to do it manually.`));
43320
+ console.info(colors__default["default"].gray(`Skipped, run \`${installCommand}\` to do it manually.`));
43321
+ return;
43322
+ }
43323
+ await $runHarnessInstallationCommand({
43324
+ definition,
43325
+ operation: 'install',
43326
+ command: installCommand,
43327
+ environment: definition.npmInstallEnvironment,
43328
+ });
43329
+ }
43330
+ /**
43331
+ * Offers to update an outdated harness exactly where it is installed.
43332
+ */
43333
+ async function $applyOutdatedHarnessInstallation(status) {
43334
+ const { definition, latestVersion, installationOrigin } = status;
43335
+ const updatePlan = resolveHarnessUpdatePlan(definition, installationOrigin.installationMethod);
43336
+ if (updatePlan.command === null || !updatePlan.isRunnableByPromptbook) {
43337
+ console.info(colors__default["default"].gray(formatHarnessManualUpdateInstruction(status, updatePlan)));
43338
+ return;
43339
+ }
43340
+ const isUpdateApproved = await $askForNpmPackageInstallationApproval(`Update ${definition.label} to ${latestVersion} with \`${updatePlan.command}\` now?`);
43341
+ if (!isUpdateApproved) {
43342
+ console.info(colors__default["default"].gray(`Skipped, run \`${updatePlan.command}\` to do it manually.`));
43210
43343
  return;
43211
43344
  }
43212
- await $installHarness(definition);
43345
+ await $runHarnessInstallationCommand({
43346
+ definition,
43347
+ operation: 'update',
43348
+ command: updatePlan.command,
43349
+ environment: updatePlan.environment,
43350
+ });
43213
43351
  }
43214
43352
  // Note: [🟡] Code for CLI harness installation handling [$applyHarnessInstallationStatus](src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts) should never be published outside of `@promptbook/cli`
43215
43353
 
@@ -43409,6 +43547,186 @@
43409
43547
  }
43410
43548
  // 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`
43411
43549
 
43550
+ /**
43551
+ * Time limit for asking npm where it installs global packages.
43552
+ */
43553
+ const NPM_GLOBAL_PREFIX_COMMAND_TIMEOUT_MS = 30 * 1000;
43554
+ /**
43555
+ * Cached lookup, because the global npm prefix cannot change while one command runs and several
43556
+ * harnesses are checked at the same time.
43557
+ */
43558
+ let npmGlobalPrefixPathPromise = null;
43559
+ /**
43560
+ * Reads the root directory under which npm installs global packages.
43561
+ *
43562
+ * Note: `$` is used to indicate that this function is not a pure function - it runs npm
43563
+ *
43564
+ * @returns The global npm prefix or `null` when npm could not be asked for it
43565
+ * @private internal utility of `promptbookCli`
43566
+ */
43567
+ function $resolveNpmGlobalPrefixPath() {
43568
+ if (npmGlobalPrefixPathPromise === null) {
43569
+ npmGlobalPrefixPathPromise = $readNpmGlobalPrefixPath();
43570
+ }
43571
+ return npmGlobalPrefixPathPromise;
43572
+ }
43573
+ /**
43574
+ * Asks npm for the global prefix once.
43575
+ */
43576
+ async function $readNpmGlobalPrefixPath() {
43577
+ const output = await $execCommand({
43578
+ command: 'npm prefix --global --loglevel=error',
43579
+ crashOnError: true,
43580
+ timeout: NPM_GLOBAL_PREFIX_COMMAND_TIMEOUT_MS,
43581
+ isVerbose: false,
43582
+ }).catch(() => '');
43583
+ // Note: npm prints the prefix as the last line, warnings emitted before it are ignored
43584
+ const prefixPath = output
43585
+ .split('\n')
43586
+ .map((line) => line.trim())
43587
+ .filter((line) => line !== '')
43588
+ .pop();
43589
+ return prefixPath !== null && prefixPath !== void 0 ? prefixPath : null;
43590
+ }
43591
+ // 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`
43592
+
43593
+ /**
43594
+ * Time limit for looking up where the harness command lives.
43595
+ */
43596
+ const HARNESS_COMMAND_LOOKUP_TIMEOUT_MS = 30 * 1000;
43597
+ /**
43598
+ * Finds out which file is really executed when the harness command runs.
43599
+ *
43600
+ * The lookup deliberately uses the very same `PATH` which runs the harness, so it reports the copy of the
43601
+ * harness which Promptbook itself would use. Symbolic links are followed, because installers like the
43602
+ * standalone OpenAI Codex installer only link their package into a directory on `PATH`.
43603
+ *
43604
+ * Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
43605
+ *
43606
+ * @returns Resolved path of the harness command or `null` when the command is not available
43607
+ * @private internal utility of `promptbookCli`
43608
+ */
43609
+ async function $resolveHarnessCommandPath(definition) {
43610
+ const lookupCommand = process.platform === 'win32' ? `where ${definition.commandName}` : `command -v ${definition.commandName}`;
43611
+ const output = await $execCommand({
43612
+ command: lookupCommand,
43613
+ crashOnError: true,
43614
+ timeout: HARNESS_COMMAND_LOOKUP_TIMEOUT_MS,
43615
+ isVerbose: false,
43616
+ }).catch(() => '');
43617
+ // Note: `where` lists every match on its own line, only the first one is really executed
43618
+ const commandPath = output
43619
+ .split('\n')
43620
+ .map((line) => line.trim())
43621
+ .find((line) => line !== '');
43622
+ if (commandPath === undefined) {
43623
+ return null;
43624
+ }
43625
+ return await promises.realpath(commandPath).catch(() => commandPath);
43626
+ }
43627
+ // Note: [🟡] Code for CLI harness command lookup [$resolveHarnessCommandPath](src/cli/cli-commands/common/harness/$resolveHarnessCommandPath.ts) should never be published outside of `@promptbook/cli`
43628
+
43629
+ /**
43630
+ * Origin used when the harness command was not looked up at all, for example when the update check is disabled.
43631
+ *
43632
+ * @private internal utility of `promptbookCli`
43633
+ */
43634
+ const UNKNOWN_HARNESS_INSTALLATION_ORIGIN = {
43635
+ commandPath: null,
43636
+ installationMethod: 'unknown',
43637
+ };
43638
+ // Note: [🟡] Code for CLI harness installation origin [HarnessInstallationOrigin](src/cli/cli-commands/common/harness/HarnessInstallationOrigin.ts) should never be published outside of `@promptbook/cli`
43639
+
43640
+ /**
43641
+ * Directory which holds npm packages, so it appears in the resolved path of every globally installed npm binary
43642
+ * on systems where the global binary is only a symbolic link into the package.
43643
+ *
43644
+ * Note: Written in lower case because it is compared against normalized paths.
43645
+ */
43646
+ const NODE_MODULES_DIRECTORY_NAME$1 = 'node_modules';
43647
+ /**
43648
+ * Directory into which Homebrew unpacks every formula, so it appears in the resolved path of
43649
+ * everything Homebrew manages and in nothing else.
43650
+ *
43651
+ * Note: Written in lower case because it is compared against normalized paths.
43652
+ */
43653
+ const HOMEBREW_DIRECTORY_NAME = 'cellar';
43654
+ /**
43655
+ * Decides how a harness command was installed from the place where it really lives.
43656
+ *
43657
+ * Only a command which npm owns may be updated with `npm install -g`, every other command has to be
43658
+ * updated where it is installed, otherwise the update silently adds a second copy of the harness.
43659
+ *
43660
+ * @private internal utility of `promptbookCli`
43661
+ */
43662
+ function resolveHarnessInstallationMethodFromPaths(paths) {
43663
+ var _a, _b;
43664
+ const { definition, commandPath, npmGlobalPrefixPath } = paths;
43665
+ const normalizedCommandPath = normalizeInstallationPath(commandPath);
43666
+ const commandPathDirectoryNames = normalizedCommandPath.split('/');
43667
+ // Note: A Homebrew formula may bundle its own `node_modules` and may live under the npm prefix of a
43668
+ // Homebrew-installed Node, so Homebrew has to be recognized before anything npm-shaped
43669
+ if (commandPathDirectoryNames.includes(HOMEBREW_DIRECTORY_NAME)) {
43670
+ return 'homebrew';
43671
+ }
43672
+ const standaloneDirectoryNames = (_b = (_a = definition.standaloneInstallation) === null || _a === void 0 ? void 0 : _a.directoryNames) !== null && _b !== void 0 ? _b : [];
43673
+ if (standaloneDirectoryNames.some((directoryName) => commandPathDirectoryNames.includes(normalizeInstallationPath(directoryName)))) {
43674
+ return 'standalone';
43675
+ }
43676
+ // Note: npm is not always reachable, but its own package directory is a reliable fallback marker
43677
+ if (normalizedCommandPath.includes(`/${NODE_MODULES_DIRECTORY_NAME$1}/${normalizeInstallationPath(definition.npmPackageName)}/`)) {
43678
+ return 'npm-global';
43679
+ }
43680
+ if (isPathInsideDirectory(normalizedCommandPath, npmGlobalPrefixPath)) {
43681
+ return 'npm-global';
43682
+ }
43683
+ return 'unknown';
43684
+ }
43685
+ /**
43686
+ * Normalizes one path so that paths coming from different tools and platforms can be compared.
43687
+ *
43688
+ * Note: The comparison is case-insensitive because Windows paths are, and two harness paths which differ
43689
+ * only in letter casing do not exist in practice.
43690
+ */
43691
+ function normalizeInstallationPath(path) {
43692
+ return path.trim().replace(/\\/gu, '/').replace(/\/+$/u, '').toLowerCase();
43693
+ }
43694
+ /**
43695
+ * Checks whether one path lies inside a directory.
43696
+ */
43697
+ function isPathInsideDirectory(normalizedPath, directoryPath) {
43698
+ if (directoryPath === null) {
43699
+ return false;
43700
+ }
43701
+ const normalizedDirectoryPath = normalizeInstallationPath(directoryPath);
43702
+ return normalizedDirectoryPath !== '' && normalizedPath.startsWith(`${normalizedDirectoryPath}/`);
43703
+ }
43704
+ // 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`
43705
+
43706
+ /**
43707
+ * Finds out where one CLI coding harness is really installed and which tool manages it.
43708
+ *
43709
+ * Note: `$` is used to indicate that this function is not a pure function - it runs shell commands
43710
+ *
43711
+ * @private internal utility of `promptbookCli`
43712
+ */
43713
+ async function $resolveHarnessInstallationOrigin(definition) {
43714
+ const commandPath = await $resolveHarnessCommandPath(definition);
43715
+ if (commandPath === null) {
43716
+ return UNKNOWN_HARNESS_INSTALLATION_ORIGIN;
43717
+ }
43718
+ const npmGlobalPrefixPath = await $resolveNpmGlobalPrefixPath();
43719
+ return {
43720
+ commandPath,
43721
+ installationMethod: resolveHarnessInstallationMethodFromPaths({
43722
+ definition,
43723
+ commandPath,
43724
+ npmGlobalPrefixPath,
43725
+ }),
43726
+ };
43727
+ }
43728
+ // 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`
43729
+
43412
43730
  /**
43413
43731
  * Detects whether one CLI coding harness is installed globally and, when enabled, whether it is up to date.
43414
43732
  *
@@ -43417,17 +43735,22 @@
43417
43735
  * @private internal utility of `promptbookCli`
43418
43736
  */
43419
43737
  async function $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled = true) {
43420
- const [installedVersion, latestVersion] = await Promise.all([
43738
+ // Note: The origin is only needed to update an outdated harness, so it is not looked up without the update check
43739
+ const [installedVersion, latestVersion, installationOrigin] = await Promise.all([
43421
43740
  $resolveInstalledHarnessVersion(definition),
43422
43741
  isHarnessUpdateCheckEnabled
43423
43742
  ? $resolveLatestNpmPackageVersion(definition.npmPackageName)
43424
43743
  : Promise.resolve(null),
43744
+ isHarnessUpdateCheckEnabled
43745
+ ? $resolveHarnessInstallationOrigin(definition)
43746
+ : Promise.resolve(UNKNOWN_HARNESS_INSTALLATION_ORIGIN),
43425
43747
  ]);
43426
43748
  return {
43427
43749
  definition,
43428
43750
  installationState: resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled),
43429
43751
  installedVersion,
43430
43752
  latestVersion,
43753
+ installationOrigin,
43431
43754
  };
43432
43755
  }
43433
43756
  /**
@@ -43486,7 +43809,7 @@
43486
43809
  * @private internal utility of `promptbookCli`
43487
43810
  */
43488
43811
  function addHarnessUpdateOption(command) {
43489
- command.option('--no-harness-update', 'Skip checking whether globally installed coding harnesses are up to date');
43812
+ command.option('--no-harness-update', 'Skip checking whether the installed coding harnesses are up to date');
43490
43813
  }
43491
43814
  /**
43492
43815
  * Converts the Commander harness-update flag into the normalized harness-check option.
@@ -44063,7 +44386,7 @@
44063
44386
  - CODING_AGENT_GIT_EMAIL
44064
44387
  - CODING_AGENT_GIT_SIGNING_KEY
44065
44388
 
44066
- Checks that the coding harnesses are installed globally and up to date unless \`--no-harness-update\` is used:
44389
+ Checks that the coding harnesses are installed and up to date unless \`--no-harness-update\` is used:
44067
44390
  ${block(listCheckedHarnessLabels())}
44068
44391
 
44069
44392
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
@@ -44171,7 +44494,7 @@
44171
44494
  - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
44172
44495
  - Optional --period keeps the quota window refreshing by pinging once per period until stopped
44173
44496
  - Leaves the project exactly as it was — nothing is read, written, changed or committed
44174
- - Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
44497
+ - Checks that the selected harness is installed and up to date unless --no-harness-update is used
44175
44498
  - Use --no-ui to stream the raw harness output instead of only the compact result
44176
44499
  `));
44177
44500
  addPromptRunnerSelectionOptions(command);
@@ -44665,7 +44988,7 @@
44665
44988
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
44666
44989
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
44667
44990
  - Optional --no-ui keeps plain streaming console output for logging and debugging
44668
- - Checks that the selected harness is installed globally and up to date before the first prompt unless --no-harness-update is used
44991
+ - Checks that the selected harness is installed and up to date before the first prompt unless --no-harness-update is used
44669
44992
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
44670
44993
  - Supports GPG signing of commits
44671
44994
  - Optional pre-coding test run that can stop or repair pre-existing failures
@@ -44822,7 +45145,7 @@
44822
45145
 
44823
45146
  Features:
44824
45147
  - Runs the same prompt processing as \`ptbk coder run\`
44825
- - Checks that the selected harness is installed globally and up to date on startup unless --no-harness-update is used
45148
+ - Checks that the selected harness is installed and up to date on startup unless --no-harness-update is used
44826
45149
  - Does not exit when all prompts are done; polls for new prompt files instead
44827
45150
  - Serves a kanban board at http://localhost:<port> for visual progress tracking
44828
45151
  - Allows editing prompt files directly from the browser (Trello-style)