@promptbook/cli 0.112.0-45 → 0.112.0-46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.es.js CHANGED
@@ -58,7 +58,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-45';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-46';
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
@@ -45444,7 +45444,8 @@ function stringifyUnknownError$1(error) {
45444
45444
 
45445
45445
  /**
45446
45446
  * Commits staged changes with the provided message using the dedicated coding-agent identity when configured,
45447
- * otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`.
45447
+ * otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`,
45448
+ * while `options.excludePaths` can keep temporary artifacts out of the created commit.
45448
45449
  */
45449
45450
  async function commitChanges(message, options) {
45450
45451
  const projectPath = process.cwd();
@@ -45454,11 +45455,7 @@ async function commitChanges(message, options) {
45454
45455
  try {
45455
45456
  const agentEnv = buildAgentGitEnv();
45456
45457
  const signingFlag = buildAgentGitSigningFlag();
45457
- await runGitCommand({
45458
- command: 'git add .',
45459
- cwd: projectPath,
45460
- env: agentEnv,
45461
- });
45458
+ await stageCommitChanges(projectPath, agentEnv, options === null || options === void 0 ? void 0 : options.excludePaths);
45462
45459
  await runGitCommand({
45463
45460
  command: buildGitCommitCommand(commitMessagePath, signingFlag),
45464
45461
  cwd: projectPath,
@@ -45472,6 +45469,56 @@ async function commitChanges(message, options) {
45472
45469
  await unlink(commitMessagePath).catch(() => undefined);
45473
45470
  }
45474
45471
  }
45472
+ /**
45473
+ * Stages repository changes and optionally unstages temporary files that should not end up inside the commit.
45474
+ */
45475
+ async function stageCommitChanges(projectPath, agentEnv, excludePaths) {
45476
+ await runGitCommand({
45477
+ command: 'git add .',
45478
+ cwd: projectPath,
45479
+ env: agentEnv,
45480
+ });
45481
+ const excludedGitPaths = normalizeExcludedGitPaths(projectPath, excludePaths);
45482
+ if (excludedGitPaths.length === 0) {
45483
+ return;
45484
+ }
45485
+ await runGitCommand({
45486
+ command: `git reset --quiet HEAD -- ${excludedGitPaths.map(quoteShellPath).join(' ')}`,
45487
+ cwd: projectPath,
45488
+ env: agentEnv,
45489
+ isVerbose: false,
45490
+ });
45491
+ }
45492
+ /**
45493
+ * Converts excluded filesystem paths into unique repository-relative Git paths.
45494
+ */
45495
+ function normalizeExcludedGitPaths(projectPath, excludePaths) {
45496
+ if (!excludePaths || excludePaths.length === 0) {
45497
+ return [];
45498
+ }
45499
+ return [
45500
+ ...new Set(excludePaths
45501
+ .map((excludePath) => normalizeExcludedGitPath(projectPath, excludePath))
45502
+ .filter((gitPath) => Boolean(gitPath))),
45503
+ ];
45504
+ }
45505
+ /**
45506
+ * Converts one excluded filesystem path into a Git-friendly repository-relative path.
45507
+ */
45508
+ function normalizeExcludedGitPath(projectPath, excludePath) {
45509
+ const absoluteExcludePath = resolve(projectPath, excludePath);
45510
+ const relativeExcludePath = relative(projectPath, absoluteExcludePath).replace(/\\/gu, '/');
45511
+ if (relativeExcludePath === '' || relativeExcludePath === '.' || relativeExcludePath.startsWith('../')) {
45512
+ return undefined;
45513
+ }
45514
+ return relativeExcludePath;
45515
+ }
45516
+ /**
45517
+ * Quotes one Git path for safe shell execution.
45518
+ */
45519
+ function quoteShellPath(path) {
45520
+ return JSON.stringify(path);
45521
+ }
45475
45522
  /**
45476
45523
  * Branded error used when pushing committed changes fails.
45477
45524
  */
@@ -48742,9 +48789,7 @@ function buildCoderRunUiFrame(options) {
48742
48789
  ...options.detailLines.map((detailLine) => `• ${detailLine}`),
48743
48790
  ]
48744
48791
  : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
48745
- const visibleOutputLines = options.agentOutputLines.length > 0
48746
- ? options.agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES).map((line) => `› ${stripAnsi(line)}`)
48747
- : ['No live agent output yet.'];
48792
+ const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
48748
48793
  const controls = buildControlPills(options.pauseState, options.pendingEnterLabel).join(' ');
48749
48794
  const frame = [
48750
48795
  ...renderBox('Brand', runnerDetails, totalWidth, colors.cyan.bold),
@@ -48758,6 +48803,18 @@ function buildCoderRunUiFrame(options) {
48758
48803
  frame.push(...renderBox('Controls', [controls], totalWidth, colors.white.bold));
48759
48804
  return frame;
48760
48805
  }
48806
+ /**
48807
+ * Builds the fixed-height live output section so streaming updates do not keep resizing the frame.
48808
+ */
48809
+ function buildVisibleOutputLines(agentOutputLines) {
48810
+ const visibleOutputLines = agentOutputLines.length > 0
48811
+ ? agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES).map((line) => `› ${stripAnsi(line)}`)
48812
+ : ['No live agent output yet.'];
48813
+ while (visibleOutputLines.length < MAX_VISIBLE_OUTPUT_LINES) {
48814
+ visibleOutputLines.push('');
48815
+ }
48816
+ return visibleOutputLines;
48817
+ }
48761
48818
  /**
48762
48819
  * Renders a framed box with a colored title and padded body lines.
48763
48820
  */
@@ -49024,11 +49081,33 @@ class CoderRunUiState extends EventEmitter {
49024
49081
  }
49025
49082
 
49026
49083
  /**
49027
- * Refresh interval for the terminal UI in milliseconds.
49084
+ * Refresh cadence used only while the rich coder UI needs animated updates.
49085
+ *
49086
+ * @private internal constant of coder run UI
49087
+ */
49088
+ const ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS = 1000;
49089
+ /**
49090
+ * Phases that still benefit from automatic refreshes because the frame can change
49091
+ * over time even without new runner output.
49028
49092
  *
49029
49093
  * @private internal constant of coder run UI
49030
49094
  */
49031
- const UI_REFRESH_INTERVAL_MS = 200;
49095
+ const AUTO_REFRESH_PHASES = ['initializing', 'loading', 'running', 'verifying'];
49096
+ /**
49097
+ * Returns the automatic refresh interval for the current UI state.
49098
+ *
49099
+ * Waiting, paused, and completed states return `undefined` so the rich UI stays
49100
+ * perfectly still until actual state changes arrive.
49101
+ *
49102
+ * @private internal utility of coder run UI
49103
+ */
49104
+ function getCoderRunUiAutoRefreshInterval(phase, pauseState) {
49105
+ if (pauseState !== 'RUNNING') {
49106
+ return undefined;
49107
+ }
49108
+ return AUTO_REFRESH_PHASES.includes(phase) ? ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS : undefined;
49109
+ }
49110
+
49032
49111
  /**
49033
49112
  * Spinner animation frames.
49034
49113
  *
@@ -49057,9 +49136,10 @@ function getTerminalWidth() {
49057
49136
  /**
49058
49137
  * Boots the ANSI terminal UI for `ptbk coder run`.
49059
49138
  *
49060
- * The UI reserves a fixed number of terminal lines and repaints them periodically.
49061
- * Between repaints, any console output from runners is captured and fed into the
49062
- * scrolling agent-output area.
49139
+ * The UI reserves a fixed number of terminal lines and refreshes them incrementally.
49140
+ * While a prompt is actively running, it schedules lightweight timed refreshes for
49141
+ * the spinner/progress area; otherwise it redraws only when real state changes arrive.
49142
+ * Any console output from runners is captured and fed into the scrolling agent-output area.
49063
49143
  *
49064
49144
  * On non-interactive (non-TTY) terminals the UI is skipped entirely and
49065
49145
  * only the state object is provided.
@@ -49109,72 +49189,139 @@ function renderCoderRunUi(startTime) {
49109
49189
  process.stdin.setRawMode(true);
49110
49190
  }
49111
49191
  let spinnerFrame = 0;
49112
- let previousFrameLineCount = 0;
49192
+ let previousFrameLines = [];
49113
49193
  let isRendering = false;
49114
49194
  let renderScheduled = false;
49195
+ let autoRefreshTimeout;
49196
+ let isDisposed = false;
49115
49197
  /**
49116
49198
  * Schedules a render on the next tick if one isn't already pending.
49117
49199
  * Prevents overlapping renders that cause cursor desync.
49118
49200
  */
49119
49201
  function scheduleRender() {
49120
- if (renderScheduled) {
49202
+ if (renderScheduled || isDisposed) {
49121
49203
  return;
49122
49204
  }
49123
49205
  renderScheduled = true;
49124
49206
  setImmediate(() => {
49125
49207
  renderScheduled = false;
49208
+ if (isDisposed) {
49209
+ return;
49210
+ }
49126
49211
  render();
49127
49212
  });
49128
49213
  }
49129
49214
  /**
49130
- * Clears previously rendered lines and writes a new frame.
49215
+ * Re-schedules automatic animation refreshes only while the frame can change by itself.
49131
49216
  */
49132
- function render() {
49133
- if (isRendering) {
49217
+ function scheduleAutoRefresh() {
49218
+ if (autoRefreshTimeout) {
49219
+ clearTimeout(autoRefreshTimeout);
49220
+ autoRefreshTimeout = undefined;
49221
+ }
49222
+ const autoRefreshInterval = getCoderRunUiAutoRefreshInterval(state.phase, getPauseState());
49223
+ if (autoRefreshInterval === undefined) {
49224
+ return;
49225
+ }
49226
+ autoRefreshTimeout = setTimeout(() => {
49227
+ autoRefreshTimeout = undefined;
49228
+ scheduleRender();
49229
+ }, autoRefreshInterval);
49230
+ }
49231
+ /**
49232
+ * Moves the cursor relative to the bottom of the current frame and rewrites one line in place.
49233
+ */
49234
+ function rewriteFrameLine(frameLineCount, lineIndex, line) {
49235
+ const linesUpFromBottom = Math.max(0, frameLineCount - 1 - lineIndex);
49236
+ if (linesUpFromBottom > 0) {
49237
+ process.stdout.write(`\x1b[${linesUpFromBottom}A`);
49238
+ }
49239
+ clearLine(process.stdout, 0);
49240
+ cursorTo(process.stdout, 0);
49241
+ process.stdout.write(line);
49242
+ cursorTo(process.stdout, 0);
49243
+ if (linesUpFromBottom > 0) {
49244
+ process.stdout.write(`\x1b[${linesUpFromBottom}B`);
49245
+ cursorTo(process.stdout, 0);
49246
+ }
49247
+ }
49248
+ /**
49249
+ * Fully rewrites the reserved frame area.
49250
+ */
49251
+ function renderFullFrame(lines) {
49252
+ var _a;
49253
+ const previousFrameLineCount = previousFrameLines.length;
49254
+ const linesToRewriteCount = Math.max(previousFrameLineCount, lines.length);
49255
+ if (previousFrameLineCount > 1) {
49256
+ process.stdout.write(`\x1b[${previousFrameLineCount - 1}A`);
49257
+ }
49258
+ for (let i = 0; i < linesToRewriteCount; i++) {
49259
+ clearLine(process.stdout, 0);
49260
+ cursorTo(process.stdout, 0);
49261
+ process.stdout.write((_a = lines[i]) !== null && _a !== void 0 ? _a : '');
49262
+ if (i < linesToRewriteCount - 1) {
49263
+ process.stdout.write('\n');
49264
+ }
49265
+ }
49266
+ const clearedTrailingLines = linesToRewriteCount - lines.length;
49267
+ if (clearedTrailingLines > 0) {
49268
+ process.stdout.write(`\x1b[${clearedTrailingLines}A`);
49269
+ }
49270
+ cursorTo(process.stdout, 0);
49271
+ }
49272
+ /**
49273
+ * Updates only the frame rows whose visible content changed.
49274
+ */
49275
+ function renderChangedLines(lines) {
49276
+ for (let i = 0; i < lines.length; i++) {
49277
+ if (previousFrameLines[i] === lines[i]) {
49278
+ continue;
49279
+ }
49280
+ rewriteFrameLine(lines.length, i, lines[i]);
49281
+ }
49282
+ }
49283
+ /**
49284
+ * Builds the current frame snapshot from the latest state.
49285
+ */
49286
+ function buildFrameLines() {
49287
+ return buildCoderRunUiFrame({
49288
+ terminalWidth: getTerminalWidth(),
49289
+ spinner: SPINNER_FRAMES[spinnerFrame],
49290
+ pauseState: getPauseState(),
49291
+ config: state.config,
49292
+ phase: state.phase,
49293
+ currentPromptLabel: state.currentPromptLabel,
49294
+ currentAttempt: state.currentAttempt,
49295
+ maxAttempts: state.maxAttempts,
49296
+ statusMessage: state.statusMessage,
49297
+ detailLines: state.detailLines,
49298
+ pendingEnterLabel: state.pendingEnterLabel,
49299
+ agentOutputLines: state.agentOutputLines,
49300
+ errors: state.errors,
49301
+ progress: state.getProgress(),
49302
+ });
49303
+ }
49304
+ /**
49305
+ * Clears previously rendered lines and writes a new frame only where needed.
49306
+ */
49307
+ function render(options) {
49308
+ if (isRendering || isDisposed) {
49134
49309
  return;
49135
49310
  }
49136
49311
  isRendering = true;
49137
49312
  try {
49138
- const lines = buildCoderRunUiFrame({
49139
- terminalWidth: getTerminalWidth(),
49140
- spinner: SPINNER_FRAMES[spinnerFrame],
49141
- pauseState: getPauseState(),
49142
- config: state.config,
49143
- phase: state.phase,
49144
- currentPromptLabel: state.currentPromptLabel,
49145
- currentAttempt: state.currentAttempt,
49146
- maxAttempts: state.maxAttempts,
49147
- statusMessage: state.statusMessage,
49148
- detailLines: state.detailLines,
49149
- pendingEnterLabel: state.pendingEnterLabel,
49150
- agentOutputLines: state.agentOutputLines,
49151
- errors: state.errors,
49152
- progress: state.getProgress(),
49153
- });
49154
- if (previousFrameLineCount > 0) {
49155
- process.stdout.write(`\x1b[${previousFrameLineCount}A`);
49156
- }
49157
- for (let i = 0; i < lines.length; i++) {
49158
- clearLine(process.stdout, 0);
49159
- cursorTo(process.stdout, 0);
49160
- process.stdout.write(lines[i]);
49161
- if (i < lines.length - 1) {
49162
- process.stdout.write('\n');
49163
- }
49313
+ const lines = buildFrameLines();
49314
+ if (previousFrameLines.length === 0 || previousFrameLines.length !== lines.length) {
49315
+ renderFullFrame(lines);
49164
49316
  }
49165
- if (lines.length < previousFrameLineCount) {
49166
- for (let i = lines.length; i < previousFrameLineCount; i++) {
49167
- process.stdout.write('\n');
49168
- clearLine(process.stdout, 0);
49169
- cursorTo(process.stdout, 0);
49170
- }
49171
- const overshoot = previousFrameLineCount - lines.length;
49172
- if (overshoot > 0) {
49173
- process.stdout.write(`\x1b[${overshoot}A`);
49174
- }
49317
+ else {
49318
+ renderChangedLines(lines);
49175
49319
  }
49176
- previousFrameLineCount = lines.length;
49320
+ previousFrameLines = [...lines];
49177
49321
  spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
49322
+ if (!(options === null || options === void 0 ? void 0 : options.skipAutoRefresh)) {
49323
+ scheduleAutoRefresh();
49324
+ }
49178
49325
  }
49179
49326
  finally {
49180
49327
  isRendering = false;
@@ -49192,6 +49339,7 @@ function renderCoderRunUi(startTime) {
49192
49339
  else {
49193
49340
  requestResume();
49194
49341
  }
49342
+ scheduleRender();
49195
49343
  return;
49196
49344
  }
49197
49345
  if ((key.name === 'return' || key.name === 'enter') && pendingEnterResolver) {
@@ -49202,17 +49350,24 @@ function renderCoderRunUi(startTime) {
49202
49350
  }
49203
49351
  };
49204
49352
  process.stdin.on('keypress', keypressHandler);
49353
+ process.stdout.on('resize', scheduleRender);
49205
49354
  process.stdout.write('\n');
49206
49355
  render();
49207
- const interval = setInterval(scheduleRender, UI_REFRESH_INTERVAL_MS);
49208
49356
  state.on('change', scheduleRender);
49209
49357
  /**
49210
49358
  * Tears down the terminal UI and restores console / stdin state.
49211
49359
  */
49212
49360
  function cleanup() {
49213
- clearInterval(interval);
49361
+ if (isDisposed) {
49362
+ return;
49363
+ }
49364
+ if (autoRefreshTimeout) {
49365
+ clearTimeout(autoRefreshTimeout);
49366
+ autoRefreshTimeout = undefined;
49367
+ }
49214
49368
  state.off('change', scheduleRender);
49215
49369
  process.stdin.off('keypress', keypressHandler);
49370
+ process.stdout.off('resize', scheduleRender);
49216
49371
  if (process.stdin.isTTY) {
49217
49372
  process.stdin.setRawMode(false);
49218
49373
  }
@@ -49224,8 +49379,9 @@ function renderCoderRunUi(startTime) {
49224
49379
  console.warn = originalConsoleWarn;
49225
49380
  console.error = originalConsoleError;
49226
49381
  console.log = originalConsoleLog;
49227
- render();
49382
+ render({ skipAutoRefresh: true });
49228
49383
  process.stdout.write('\n');
49384
+ isDisposed = true;
49229
49385
  }
49230
49386
  return {
49231
49387
  state,
@@ -49572,7 +49728,11 @@ async function runCodexPrompts(providedOptions) {
49572
49728
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
49573
49729
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
49574
49730
  }
49575
- await commitChanges(commitMessage, { autoPush: options.autoPush });
49731
+ await commitChanges(commitMessage, {
49732
+ autoPush: options.autoPush,
49733
+ // Keep the live runtime log out of default commits because it is deleted after a successful round.
49734
+ excludePaths: options.preserveLogs ? undefined : [logPath],
49735
+ });
49576
49736
  await runPostPromptAutoMigrationIfEnabled(options);
49577
49737
  }
49578
49738
  catch (error) {