@promptbook/cli 0.112.0-44 → 0.112.0-45

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 (59) hide show
  1. package/esm/index.es.js +675 -491
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/scripts/run-codex-prompts/common/CoderRunTimer.d.ts +31 -0
  4. package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +23 -0
  5. package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +13 -4
  6. package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +16 -0
  7. package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScript.d.ts +1 -1
  8. package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScriptUntilMarkerIdle.d.ts +1 -1
  9. package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScriptWithOutput.d.ts +1 -1
  10. package/esm/scripts/run-codex-prompts/common/runGoScript/shouldDeleteTemporaryArtifact.d.ts +7 -0
  11. package/esm/scripts/run-codex-prompts/common/runGoScript/withPromptRuntimeLog.d.ts +4 -3
  12. package/esm/scripts/run-codex-prompts/common/runGoScript/withTempScript.d.ts +1 -1
  13. package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -0
  14. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +15 -20
  15. package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +28 -0
  16. package/esm/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +2 -0
  17. package/esm/src/avatars/Avatar.d.ts +7 -0
  18. package/esm/src/avatars/avatarRenderingUtils.d.ts +117 -0
  19. package/esm/src/avatars/index.d.ts +6 -0
  20. package/esm/src/avatars/renderAvatarVisual.d.ts +9 -0
  21. package/esm/src/avatars/types/AvatarDefinition.d.ts +20 -0
  22. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +96 -0
  23. package/esm/src/avatars/visuals/avatarVisualRegistry.d.ts +16 -0
  24. package/esm/src/avatars/visuals/minecraftAvatarVisual.d.ts +7 -0
  25. package/esm/src/avatars/visuals/octopusAvatarVisual.d.ts +7 -0
  26. package/esm/src/avatars/visuals/pixelArtAvatarVisual.d.ts +7 -0
  27. package/esm/src/commitments/STYLE/STYLE.d.ts +9 -2
  28. package/esm/src/version.d.ts +1 -1
  29. package/package.json +1 -1
  30. package/umd/index.umd.js +675 -491
  31. package/umd/index.umd.js.map +1 -1
  32. package/umd/scripts/run-codex-prompts/common/CoderRunTimer.d.ts +31 -0
  33. package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +23 -0
  34. package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +13 -4
  35. package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +16 -0
  36. package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScript.d.ts +1 -1
  37. package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScriptUntilMarkerIdle.d.ts +1 -1
  38. package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScriptWithOutput.d.ts +1 -1
  39. package/umd/scripts/run-codex-prompts/common/runGoScript/shouldDeleteTemporaryArtifact.d.ts +7 -0
  40. package/umd/scripts/run-codex-prompts/common/runGoScript/withPromptRuntimeLog.d.ts +4 -3
  41. package/umd/scripts/run-codex-prompts/common/runGoScript/withTempScript.d.ts +1 -1
  42. package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +1 -0
  43. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +15 -20
  44. package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +28 -0
  45. package/umd/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +2 -0
  46. package/umd/src/avatars/Avatar.d.ts +7 -0
  47. package/umd/src/avatars/avatarRenderingUtils.d.ts +117 -0
  48. package/umd/src/avatars/index.d.ts +6 -0
  49. package/umd/src/avatars/renderAvatarVisual.d.ts +9 -0
  50. package/umd/src/avatars/types/AvatarDefinition.d.ts +20 -0
  51. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +96 -0
  52. package/umd/src/avatars/visuals/avatarVisualRegistry.d.ts +16 -0
  53. package/umd/src/avatars/visuals/minecraftAvatarVisual.d.ts +7 -0
  54. package/umd/src/avatars/visuals/octopusAvatarVisual.d.ts +7 -0
  55. package/umd/src/avatars/visuals/pixelArtAvatarVisual.d.ts +7 -0
  56. package/umd/src/commitments/STYLE/STYLE.d.ts +9 -2
  57. package/umd/src/version.d.ts +1 -1
  58. package/esm/scripts/run-codex-prompts/common/runGoScript/PromptRoundArtifacts.d.ts +0 -35
  59. package/umd/scripts/run-codex-prompts/common/runGoScript/PromptRoundArtifacts.d.ts +0 -35
package/umd/index.umd.js CHANGED
@@ -60,7 +60,7 @@
60
60
  * @generated
61
61
  * @see https://github.com/webgptorg/promptbook
62
62
  */
63
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-44';
63
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-45';
64
64
  /**
65
65
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
66
66
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3007,6 +3007,8 @@
3007
3007
  Features:
3008
3008
  - Automatically stages and commits changes with agent identity
3009
3009
  - Optional post-commit git push with explicit --auto-push opt-in
3010
+ - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
3011
+ - Optional --no-ui keeps plain streaming console output for logging and debugging
3010
3012
  - Supports GPG signing of commits
3011
3013
  - Optional post-prompt verification with test-feedback retries
3012
3014
  - Progress tracking and interactive controls
@@ -3022,19 +3024,21 @@
3022
3024
  `));
3023
3025
  command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
3024
3026
  command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
3027
+ command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
3028
+ command.option('--no-ui', 'Disable the rich terminal UI and keep plain streaming console output for logging and debugging');
3025
3029
  command.addOption(new commander.Option('--thinking-level <thinking-level>', `Set reasoning effort for supported runners (${THINKING_LEVEL_VALUES.join(', ')})`).choices([...THINKING_LEVEL_VALUES]));
3026
3030
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
3027
3031
  command.option('--no-wait', 'Skip user prompts between processing');
3028
3032
  command.option('--ignore-git-changes', 'Skip clean working tree check before running prompts', false);
3029
3033
  command.option('--allow-credits', 'Allow OpenAI Codex runner to spend credits when rate limits are exhausted', false);
3030
- command.option('--preserve-logs', 'Keep generated runner shells and runtime logs after successful prompt rounds; failures keep them automatically', false);
3031
3034
  command.option('--no-normalize-line-endings', 'Disable automatic LF normalization for files changed in each coding round');
3032
3035
  command.option('--auto-push', 'Automatically git push after each commit', false);
3033
3036
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
3034
3037
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
3035
3038
  command.action(handleActionErrors(async (cliOptions) => {
3036
- const { dryRun, agent, model, context, test, thinkingLevel, priority, wait, ignoreGitChanges, allowCredits, preserveLogs, normalizeLineEndings, autoMigrate, allowDestructiveAutoMigrate, autoPush, } = cliOptions;
3039
+ const { dryRun, agent, model, context, test, preserveLogs, ui, thinkingLevel, priority, wait, ignoreGitChanges, allowCredits, normalizeLineEndings, autoMigrate, allowDestructiveAutoMigrate, autoPush, } = cliOptions;
3037
3040
  const testCommand = normalizeCommandOptionValue(test);
3041
+ const noUi = !ui;
3038
3042
  // Validate agent
3039
3043
  let agentName = undefined;
3040
3044
  if (agent) {
@@ -3064,11 +3068,12 @@
3064
3068
  model,
3065
3069
  context,
3066
3070
  testCommand,
3071
+ preserveLogs,
3072
+ noUi,
3067
3073
  thinkingLevel,
3068
3074
  priority,
3069
3075
  normalizeLineEndings,
3070
3076
  allowCredits,
3071
- preserveLogs,
3072
3077
  autoMigrate,
3073
3078
  allowDestructiveAutoMigrate,
3074
3079
  autoPush,
@@ -18388,7 +18393,7 @@
18388
18393
  DELETE Casual conversational style
18389
18394
  REMOVE All emoji usage
18390
18395
  GOAL Provide professional business communications
18391
- STYLE Use formal language and proper business etiquette
18396
+ WRITING RULES Use formal language and proper business etiquette
18392
18397
  \`\`\`
18393
18398
 
18394
18399
  \`\`\`book
@@ -18399,7 +18404,7 @@
18399
18404
  DISCARD Technical jargon explanations
18400
18405
  CANCEL Advanced troubleshooting procedures
18401
18406
  GOAL Help users with simple, easy-to-follow solutions
18402
- STYLE Use plain language that anyone can understand
18407
+ WRITING RULES Use plain language that anyone can understand
18403
18408
  \`\`\`
18404
18409
 
18405
18410
  \`\`\`book
@@ -18416,11 +18421,11 @@
18416
18421
  Concise Information Provider
18417
18422
 
18418
18423
  PERSONA You are a helpful assistant who provides detailed explanations
18419
- STYLE Include examples, analogies, and comprehensive context
18424
+ WRITING RULES Include examples, analogies, and comprehensive context
18420
18425
  CANCEL Detailed explanation style
18421
18426
  DISCARD Examples and analogies
18422
18427
  GOAL Provide brief, direct answers without unnecessary elaboration
18423
- STYLE Be concise and to the point
18428
+ WRITING RULES Be concise and to the point
18424
18429
  \`\`\`
18425
18430
  `);
18426
18431
  }
@@ -18604,7 +18609,7 @@
18604
18609
  PERSONA You are a data analysis expert
18605
18610
  FORMAT Present results in structured tables
18606
18611
  FORMAT Include confidence scores for all predictions
18607
- STYLE Be concise and precise in explanations
18612
+ WRITING RULES Be concise and precise in explanations
18608
18613
  \`\`\`
18609
18614
  `);
18610
18615
  }
@@ -18998,7 +19003,7 @@
18998
19003
 
18999
19004
  GOAL Help students understand mathematical concepts clearly
19000
19005
  GOAL Ensure all explanations are age-appropriate and accessible
19001
- STYLE Use simple language and provide step-by-step explanations
19006
+ WRITING RULES Use simple language and provide step-by-step explanations
19002
19007
  \`\`\`
19003
19008
 
19004
19009
  \`\`\`book
@@ -19412,7 +19417,7 @@
19412
19417
  KNOWLEDGE Academic research requires careful citation and verification
19413
19418
  KNOWLEDGE https://example.com/research-guidelines.pdf
19414
19419
  ACTION Can help with literature reviews and data analysis
19415
- STYLE Present information in clear, academic format
19420
+ WRITING RULES Present information in clear, academic format
19416
19421
  \`\`\`
19417
19422
  `);
19418
19423
  }
@@ -21163,7 +21168,7 @@
21163
21168
 
21164
21169
  META IMAGE https://example.com/professional-avatar.jpg
21165
21170
  PERSONA You are a professional business assistant
21166
- STYLE Maintain a formal and courteous tone
21171
+ WRITING RULES Maintain a formal and courteous tone
21167
21172
  \`\`\`
21168
21173
 
21169
21174
  \`\`\`book
@@ -21171,7 +21176,7 @@
21171
21176
 
21172
21177
  META IMAGE /assets/creative-bot-avatar.png
21173
21178
  PERSONA You are a creative and inspiring assistant
21174
- STYLE Be enthusiastic and encouraging
21179
+ WRITING RULES Be enthusiastic and encouraging
21175
21180
  ACTION Can help with brainstorming and ideation
21176
21181
  \`\`\`
21177
21182
  `);
@@ -21330,7 +21335,7 @@
21330
21335
  META LINK https://twitter.com/devhandle
21331
21336
  PERSONA You are an experienced open source developer
21332
21337
  ACTION Can help with code reviews and architecture decisions
21333
- STYLE Be direct and technical in explanations
21338
+ WRITING RULES Be direct and technical in explanations
21334
21339
  \`\`\`
21335
21340
  `);
21336
21341
  }
@@ -21536,7 +21541,7 @@
21536
21541
  MODEL TEMPERATURE 0.8
21537
21542
  MODEL TOP_P 0.9
21538
21543
  MODEL MAX_TOKENS 2048
21539
- STYLE Be imaginative and expressive
21544
+ WRITING RULES Be imaginative and expressive
21540
21545
  ACTION Can help with storytelling and character development
21541
21546
  \`\`\`
21542
21547
 
@@ -21748,7 +21753,7 @@
21748
21753
  NOTE Uses RAG for accessing latest research papers
21749
21754
  PERSONA You are a knowledgeable research assistant
21750
21755
  ACTION Can help with literature reviews and citations
21751
- STYLE Present information in academic format
21756
+ WRITING RULES Present information in academic format
21752
21757
  \`\`\`
21753
21758
  `);
21754
21759
  }
@@ -22042,7 +22047,7 @@
22042
22047
  RULE Always ask for clarification if the user's request is ambiguous
22043
22048
  RULE Be polite and professional in all interactions
22044
22049
  RULES Never provide medical or legal advice
22045
- STYLE Maintain a friendly and helpful tone
22050
+ WRITING RULES Maintain a friendly and helpful tone
22046
22051
  \`\`\`
22047
22052
 
22048
22053
  \`\`\`book
@@ -22311,8 +22316,8 @@
22311
22316
  /**
22312
22317
  * STYLE commitment definition
22313
22318
  *
22314
- * The STYLE commitment defines how the agent should format and present its responses.
22315
- * This includes tone, writing style, formatting preferences, and communication patterns.
22319
+ * Deprecated legacy writing-style commitment kept for backward compatibility.
22320
+ * New books should prefer `WRITING RULES` for writing-only constraints.
22316
22321
  *
22317
22322
  * Example usage in agent source:
22318
22323
  *
@@ -22331,7 +22336,16 @@
22331
22336
  * Short one-line description of STYLE.
22332
22337
  */
22333
22338
  get description() {
22334
- return 'Control the tone and writing style of responses.';
22339
+ return 'Deprecated legacy writing-style commitment. Prefer `WRITING RULES` for new books.';
22340
+ }
22341
+ /**
22342
+ * Optional UI/docs-only deprecation metadata.
22343
+ */
22344
+ get deprecation() {
22345
+ return {
22346
+ message: 'Use `WRITING RULES` for writing-only constraints such as tone, length, formatting, or emoji usage.',
22347
+ replacedBy: ['WRITING RULES'],
22348
+ };
22335
22349
  }
22336
22350
  /**
22337
22351
  * Icon for this commitment.
@@ -22346,15 +22360,34 @@
22346
22360
  return _spaceTrim.spaceTrim(`
22347
22361
  # ${this.type}
22348
22362
 
22349
- Defines how the agent should format and present its responses (tone, writing style, formatting).
22363
+ Deprecated legacy commitment for writing and presentation instructions.
22364
+
22365
+ ## Migration
22366
+
22367
+ - Existing \`${this.type}\` books still parse and compile.
22368
+ - New books should prefer \`WRITING RULES\`.
22369
+ - Use \`WRITING SAMPLE\` when you want to anchor voice by example instead of stating constraints directly.
22370
+ - The plural alias \`STYLES\` is the same legacy commitment family.
22350
22371
 
22351
22372
  ## Key aspects
22352
22373
 
22353
- - Both terms work identically and can be used interchangeably.
22374
+ - \`${this.type}\` remains functional for backward compatibility only.
22354
22375
  - Later style instructions can override earlier ones.
22355
22376
  - Style affects both tone and presentation format.
22356
22377
 
22357
- ## Examples
22378
+ ## Preferred replacement
22379
+
22380
+ \`\`\`book
22381
+ Technical Writer
22382
+
22383
+ GOAL Help the user understand technical topics with practical, accurate guidance.
22384
+ WRITING RULES Write in a professional but friendly tone.
22385
+ WRITING RULES Use bullet points for lists.
22386
+ WRITING RULES Always provide code examples when explaining programming concepts.
22387
+ FORMAT Use markdown formatting with clear headings
22388
+ \`\`\`
22389
+
22390
+ ## Legacy compatibility examples
22358
22391
 
22359
22392
  \`\`\`book
22360
22393
  Technical Writer
@@ -23095,7 +23128,7 @@
23095
23128
 
23096
23129
  PERSONA You are a helpful customer support representative
23097
23130
  TEMPLATE Always structure your response with: 1) Acknowledgment, 2) Solution, 3) Follow-up question
23098
- STYLE Be professional and empathetic
23131
+ WRITING RULES Be professional and empathetic
23099
23132
  \`\`\`
23100
23133
 
23101
23134
  \`\`\`book
@@ -23539,7 +23572,7 @@
23539
23572
 
23540
23573
  PERSONA You are a news analyst who stays up-to-date with current events
23541
23574
  USE BROWSER
23542
- STYLE Present news in a balanced and objective manner
23575
+ WRITING RULES Present news in a balanced and objective manner
23543
23576
  ACTION Can search for and summarize news articles
23544
23577
  \`\`\`
23545
23578
 
@@ -44245,7 +44278,7 @@
44245
44278
  /**
44246
44279
  * CLI usage text for this script.
44247
44280
  */
44248
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--agent <agent-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--preserve-logs] [--auto-migrate] [--allow-destructive-auto-migrate] [--no-wait] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push]';
44281
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--agent <agent-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--no-wait] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push]';
44249
44282
  /**
44250
44283
  * Top-level flags supported by this command.
44251
44284
  */
@@ -44255,10 +44288,11 @@
44255
44288
  '--model',
44256
44289
  '--context',
44257
44290
  '--test',
44291
+ '--preserve-logs',
44292
+ '--no-ui',
44258
44293
  '--thinking-level',
44259
44294
  '--priority',
44260
44295
  '--allow-credits',
44261
- '--preserve-logs',
44262
44296
  '--auto-migrate',
44263
44297
  '--allow-destructive-auto-migrate',
44264
44298
  '--no-wait',
@@ -44288,6 +44322,8 @@
44288
44322
  const context = readOptionValue(args, '--context');
44289
44323
  const hasTestCommandFlag = args.includes('--test');
44290
44324
  const testCommand = readVariadicOptionValue(args, '--test');
44325
+ const preserveLogs = args.includes('--preserve-logs');
44326
+ const noUi = args.includes('--no-ui');
44291
44327
  const hasThinkingLevelFlag = args.includes('--thinking-level');
44292
44328
  const thinkingLevelValue = readOptionValue(args, '--thinking-level');
44293
44329
  const hasPriorityFlag = args.includes('--priority');
@@ -44295,7 +44331,6 @@
44295
44331
  const ignoreGitChanges = args.includes('--ignore-git-changes');
44296
44332
  const normalizeLineEndings = !args.includes('--no-normalize-line-endings');
44297
44333
  const allowCredits = args.includes('--allow-credits');
44298
- const preserveLogs = args.includes('--preserve-logs');
44299
44334
  const autoMigrate = args.includes('--auto-migrate');
44300
44335
  const allowDestructiveAutoMigrate = args.includes('--allow-destructive-auto-migrate');
44301
44336
  const autoPush = args.includes('--auto-push');
@@ -44321,10 +44356,11 @@
44321
44356
  ignoreGitChanges,
44322
44357
  normalizeLineEndings,
44323
44358
  allowCredits,
44324
- preserveLogs,
44325
44359
  autoMigrate,
44326
44360
  allowDestructiveAutoMigrate,
44327
44361
  autoPush,
44362
+ preserveLogs,
44363
+ noUi,
44328
44364
  agentName,
44329
44365
  model,
44330
44366
  context,
@@ -44405,18 +44441,10 @@
44405
44441
  return `${normalizedPrompt}\n\n${normalizedContext}`;
44406
44442
  }
44407
44443
 
44408
- /**
44409
- * Refresh interval for the progress header in milliseconds.
44410
- */
44411
- const PROGRESS_REFRESH_INTERVAL_MS = 1000;
44412
- /**
44413
- * Number of terminal lines reserved for the sticky progress header.
44414
- */
44415
- const PROGRESS_HEADER_RESERVED_LINES = 1;
44416
44444
  /**
44417
44445
  * Calendar formats used when displaying the estimated completion time.
44418
44446
  */
44419
- const ESTIMATED_DONE_CALENDAR_FORMATS$1 = {
44447
+ const ESTIMATED_DONE_CALENDAR_FORMATS = {
44420
44448
  sameDay: '[Today] h:mm',
44421
44449
  nextDay: '[Tomorrow] h:mm',
44422
44450
  nextWeek: 'dddd h:mm',
@@ -44424,6 +44452,121 @@
44424
44452
  lastWeek: 'dddd h:mm',
44425
44453
  sameElse: 'MMM D h:mm',
44426
44454
  };
44455
+ /**
44456
+ * Formats a duration into a compact string such as "3h 12m" or "45s".
44457
+ */
44458
+ function formatDurationBrief(duration) {
44459
+ const totalSeconds = Math.max(0, Math.round(duration.asSeconds()));
44460
+ const hours = Math.floor(totalSeconds / 3600);
44461
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
44462
+ const seconds = totalSeconds % 60;
44463
+ const parts = [];
44464
+ if (hours > 0) {
44465
+ parts.push(`${hours}h`);
44466
+ }
44467
+ if (minutes > 0) {
44468
+ parts.push(`${minutes}m`);
44469
+ }
44470
+ if (!parts.length && seconds > 0) {
44471
+ parts.push(`${seconds}s`);
44472
+ }
44473
+ if (!parts.length) {
44474
+ parts.push('0s');
44475
+ }
44476
+ return parts.join(' ');
44477
+ }
44478
+
44479
+ /**
44480
+ * Builds a session-scoped progress snapshot from prompt stats and elapsed active time.
44481
+ */
44482
+ function buildCoderRunProgressSnapshot(stats, elapsedDuration, initialDone) {
44483
+ const totalPrompts = stats.done + stats.forAgent + stats.toBeWritten;
44484
+ const sessionDone = Math.max(0, stats.done - initialDone);
44485
+ const sessionRemaining = stats.forAgent;
44486
+ const sessionTotal = sessionDone + sessionRemaining;
44487
+ const currentPromptIndex = sessionTotal > 0 ? Math.min(sessionDone + 1, sessionTotal) : 0;
44488
+ const percentage = sessionTotal > 0 ? Math.round((sessionDone / sessionTotal) * 100) : 0;
44489
+ const elapsedText = formatDurationBrief(elapsedDuration);
44490
+ let estimatedTotalText = 'estimating...';
44491
+ let estimatedLabel = 'after first completion';
44492
+ let isEstimatedTotalKnown = false;
44493
+ if (sessionTotal > 0 && sessionDone > 0) {
44494
+ const estimatedTotalMs = (elapsedDuration.asMilliseconds() * sessionTotal) / sessionDone;
44495
+ const estimatedRemainingMs = Math.max(0, estimatedTotalMs - elapsedDuration.asMilliseconds());
44496
+ const estimatedTotalDuration = moment__default["default"].duration(estimatedTotalMs);
44497
+ const estimatedCompletion = moment__default["default"]().add(estimatedRemainingMs, 'milliseconds');
44498
+ estimatedTotalText = formatDurationBrief(estimatedTotalDuration);
44499
+ estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS);
44500
+ isEstimatedTotalKnown = true;
44501
+ }
44502
+ return {
44503
+ totalPrompts,
44504
+ sessionDone,
44505
+ sessionRemaining,
44506
+ sessionTotal,
44507
+ currentPromptIndex,
44508
+ skippedPrompts: stats.belowMinimumPriority,
44509
+ toBeWrittenPrompts: stats.toBeWritten,
44510
+ percentage,
44511
+ elapsedText,
44512
+ estimatedTotalText,
44513
+ estimatedLabel,
44514
+ isEstimatedTotalKnown,
44515
+ };
44516
+ }
44517
+
44518
+ /**
44519
+ * Tracks active coder-run time while excluding pauses and user-confirmation waits.
44520
+ */
44521
+ class CoderRunTimer {
44522
+ /**
44523
+ * Creates a timer anchored at the provided start time.
44524
+ */
44525
+ constructor(startTime, isPausedInitially = false) {
44526
+ this.startTime = startTime;
44527
+ /**
44528
+ * Total milliseconds spent in paused state across the run.
44529
+ */
44530
+ this.pausedMs = 0;
44531
+ if (isPausedInitially) {
44532
+ this.pausedSince = startTime.clone();
44533
+ }
44534
+ }
44535
+ /**
44536
+ * Pauses active-time tracking until `resume()` is called.
44537
+ */
44538
+ pause() {
44539
+ if (this.pausedSince === undefined) {
44540
+ this.pausedSince = moment__default["default"]();
44541
+ }
44542
+ }
44543
+ /**
44544
+ * Resumes active-time tracking after a pause.
44545
+ */
44546
+ resume() {
44547
+ if (this.pausedSince !== undefined) {
44548
+ this.pausedMs += moment__default["default"]().diff(this.pausedSince);
44549
+ this.pausedSince = undefined;
44550
+ }
44551
+ }
44552
+ /**
44553
+ * Returns the currently accumulated active duration.
44554
+ */
44555
+ getElapsedDuration() {
44556
+ const wallMs = moment__default["default"]().diff(this.startTime);
44557
+ const currentPauseMs = this.pausedSince !== undefined ? moment__default["default"]().diff(this.pausedSince) : 0;
44558
+ return moment__default["default"].duration(Math.max(0, wallMs - this.pausedMs - currentPauseMs));
44559
+ }
44560
+ }
44561
+
44562
+ /**
44563
+ * Refresh interval for the progress header in milliseconds.
44564
+ */
44565
+ const PROGRESS_REFRESH_INTERVAL_MS = 1000;
44566
+ /**
44567
+ * Number of terminal lines reserved for the sticky progress header.
44568
+ */
44569
+ const PROGRESS_HEADER_RESERVED_LINES = 3;
44427
44570
  /**
44428
44571
  * Compact CLI progress display that stays pinned at the top of the terminal.
44429
44572
  */
@@ -44431,11 +44574,12 @@
44431
44574
  /**
44432
44575
  * Creates a new display that uses the provided start time when computing estimates.
44433
44576
  */
44434
- constructor(startTime) {
44435
- this.startTime = startTime;
44577
+ constructor(startTime, minimumPriority) {
44578
+ this.minimumPriority = minimumPriority;
44436
44579
  this.stats = { done: 0, forAgent: 0, belowMinimumPriority: 0, toBeWritten: 0 };
44437
44580
  this.isHeaderReserved = false;
44438
44581
  this.isInteractive = Boolean(process.stdout.isTTY);
44582
+ this.timer = new CoderRunTimer(startTime);
44439
44583
  if (!this.isInteractive) {
44440
44584
  return;
44441
44585
  }
@@ -44454,6 +44598,20 @@
44454
44598
  this.stats = stats;
44455
44599
  this.render();
44456
44600
  }
44601
+ /**
44602
+ * Pauses the active timer while the runner is waiting for user input.
44603
+ */
44604
+ pauseTimer() {
44605
+ this.timer.pause();
44606
+ this.render();
44607
+ }
44608
+ /**
44609
+ * Resumes the active timer after a pause.
44610
+ */
44611
+ resumeTimer() {
44612
+ this.timer.resume();
44613
+ this.render();
44614
+ }
44457
44615
  /**
44458
44616
  * Stops the automatic refresh cycle and renders the final header once more.
44459
44617
  */
@@ -44471,14 +44629,17 @@
44471
44629
  * Repaint the header without disturbing the current cursor position.
44472
44630
  */
44473
44631
  render() {
44632
+ var _a;
44474
44633
  if (!this.isInteractive) {
44475
44634
  return;
44476
44635
  }
44477
- const line = this.buildProgressLine();
44636
+ const lines = this.buildProgressLines();
44478
44637
  process.stdout.write('\x1b[s');
44479
- readline.cursorTo(process.stdout, 0, 0);
44480
- readline.clearLine(process.stdout, 0);
44481
- process.stdout.write(line);
44638
+ for (let lineIndex = 0; lineIndex < PROGRESS_HEADER_RESERVED_LINES; lineIndex++) {
44639
+ readline.cursorTo(process.stdout, 0, lineIndex);
44640
+ readline.clearLine(process.stdout, 0);
44641
+ process.stdout.write((_a = lines[lineIndex]) !== null && _a !== void 0 ? _a : '');
44642
+ }
44482
44643
  process.stdout.write('\x1b[u');
44483
44644
  }
44484
44645
  /**
@@ -44492,72 +44653,74 @@
44492
44653
  this.isHeaderReserved = true;
44493
44654
  }
44494
44655
  /**
44495
- * Builds the coloured progress text padded to the terminal width.
44656
+ * Builds the colored progress text padded to the terminal width.
44496
44657
  */
44497
- buildProgressLine() {
44658
+ buildProgressLines() {
44498
44659
  var _a, _b;
44499
- const snapshot = buildProgressSnapshot(this.stats, this.startTime, (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done);
44500
- const sessionLabel = `${snapshot.sessionDone}/${snapshot.sessionTotal} Prompts`;
44501
- const totalLabel = `(${snapshot.totalPrompts} total)`;
44502
- const baseLine = `${sessionLabel} ${totalLabel} | ${snapshot.percentage}% | ${snapshot.elapsedText}/${snapshot.estimatedTotalText} | Estimated done ${snapshot.estimatedLabel}`;
44503
- const columns = (_b = process.stdout.columns) !== null && _b !== void 0 ? _b : baseLine.length;
44504
- const padded = baseLine.padEnd(columns > baseLine.length ? columns : baseLine.length);
44505
- return colors__default["default"].bgWhite(colors__default["default"].black(padded));
44660
+ const snapshot = buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done);
44661
+ const columns = Math.max(40, (_b = process.stdout.columns) !== null && _b !== void 0 ? _b : 80);
44662
+ const workingLine = snapshot.sessionTotal > 0
44663
+ ? [
44664
+ `Working on ${snapshot.currentPromptIndex}/${snapshot.sessionTotal} prompts`,
44665
+ `Priority >=${this.minimumPriority}`,
44666
+ `Repo total ${snapshot.totalPrompts}`,
44667
+ ].join(' | ')
44668
+ : [`No runnable prompts`, `Priority >=${this.minimumPriority}`, `Repo total ${snapshot.totalPrompts}`].join(' | ');
44669
+ const detailParts = [
44670
+ `Done ${snapshot.sessionDone}/${snapshot.sessionTotal} this run`,
44671
+ `Elapsed ${snapshot.elapsedText} / ${snapshot.estimatedTotalText}`,
44672
+ `Est. done ${snapshot.estimatedLabel}`,
44673
+ ];
44674
+ if (snapshot.skippedPrompts > 0) {
44675
+ detailParts.splice(1, 0, `Skipping ${snapshot.skippedPrompts} prompts with Priority <${this.minimumPriority}`);
44676
+ }
44677
+ if (snapshot.toBeWrittenPrompts > 0) {
44678
+ detailParts.splice(detailParts.length - 2, 0, `Write first ${formatPromptCount$1(snapshot.toBeWrittenPrompts)}`);
44679
+ }
44680
+ const progressLabel = `${snapshot.percentage}% complete (${snapshot.sessionDone}/${snapshot.sessionTotal} done)`;
44681
+ const progressBar = buildProgressBar$1(snapshot.percentage, progressLabel, columns);
44682
+ return [
44683
+ colors__default["default"].bgCyan.black(padPlainText(workingLine, columns)),
44684
+ colors__default["default"].bgBlack.white(padPlainText(detailParts.join(' | '), columns)),
44685
+ colors__default["default"].bgBlack(progressBar),
44686
+ ];
44506
44687
  }
44507
44688
  }
44508
44689
  /**
44509
- * Calculates progress metrics shown in the sticky header.
44690
+ * Builds a colored progress bar with an explicit completion label.
44510
44691
  */
44511
- function buildProgressSnapshot(stats, startTime, initialDone) {
44512
- const totalPrompts = stats.done + stats.forAgent + stats.toBeWritten;
44513
- const completedPrompts = stats.done;
44514
- const sessionDone = Math.max(0, completedPrompts - initialDone);
44515
- const sessionTotal = sessionDone + stats.forAgent;
44516
- const percentage = totalPrompts > 0 ? Math.round((completedPrompts / totalPrompts) * 100) : 0;
44517
- const elapsedDuration = moment__default["default"].duration(moment__default["default"]().diff(startTime));
44518
- const elapsedText = formatDurationBrief$1(elapsedDuration);
44519
- let estimatedTotalText = '—';
44520
- let estimatedLabel = 'unknown';
44521
- if (totalPrompts > 0 && completedPrompts > 0) {
44522
- const estimatedTotalMs = (elapsedDuration.asMilliseconds() * totalPrompts) / completedPrompts;
44523
- const estimatedTotalDuration = moment__default["default"].duration(estimatedTotalMs);
44524
- const estimatedCompletion = startTime.clone().add(estimatedTotalDuration);
44525
- estimatedTotalText = formatDurationBrief$1(estimatedTotalDuration);
44526
- estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS$1);
44527
- }
44528
- return {
44529
- totalPrompts,
44530
- completedPrompts,
44531
- sessionDone,
44532
- sessionTotal,
44533
- percentage,
44534
- elapsedText,
44535
- estimatedTotalText,
44536
- estimatedLabel,
44537
- };
44692
+ function buildProgressBar$1(percentage, label, width) {
44693
+ const safeLabel = ` ${label}`;
44694
+ const barWidth = Math.max(10, width - safeLabel.length);
44695
+ const filledWidth = Math.round((percentage / 100) * barWidth);
44696
+ const emptyWidth = Math.max(0, barWidth - filledWidth);
44697
+ return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].gray('░'.repeat(emptyWidth))}${safeLabel}`;
44538
44698
  }
44539
44699
  /**
44540
- * Formats a duration into a compact string such as "3h 12m" or "45s".
44700
+ * Pads or truncates one plain-text header line to the terminal width.
44541
44701
  */
44542
- function formatDurationBrief$1(duration) {
44543
- const totalSeconds = Math.max(0, Math.round(duration.asSeconds()));
44544
- const hours = Math.floor(totalSeconds / 3600);
44545
- const minutes = Math.floor((totalSeconds % 3600) / 60);
44546
- const seconds = totalSeconds % 60;
44547
- const parts = [];
44548
- if (hours > 0) {
44549
- parts.push(`${hours}h`);
44550
- }
44551
- if (minutes > 0) {
44552
- parts.push(`${minutes}m`);
44553
- }
44554
- if (!parts.length && seconds > 0) {
44555
- parts.push(`${seconds}s`);
44556
- }
44557
- if (!parts.length) {
44558
- parts.push('0s');
44702
+ function padPlainText(text, width) {
44703
+ if (text.length > width) {
44704
+ if (width <= 3) {
44705
+ return '.'.repeat(width);
44706
+ }
44707
+ return `${text.slice(0, width - 3)}...`;
44559
44708
  }
44560
- return parts.join(' ');
44709
+ return text.padEnd(width);
44710
+ }
44711
+ /**
44712
+ * Formats a prompt count with the correct singular/plural noun.
44713
+ */
44714
+ function formatPromptCount$1(count) {
44715
+ return `${count} prompt${count === 1 ? '' : 's'}`;
44716
+ }
44717
+
44718
+ /**
44719
+ * Formats commit message lines for console display.
44720
+ */
44721
+ function formatCommitMessageForDisplay(message) {
44722
+ const lines = message.split(/\r?\n/);
44723
+ return lines.map((line) => colors__default["default"].bgBlue.white(` ${line} `)).join('\n');
44561
44724
  }
44562
44725
 
44563
44726
  /**
@@ -44771,14 +44934,6 @@
44771
44934
  return normalized.subarray(0, writeIndex);
44772
44935
  }
44773
44936
 
44774
- /**
44775
- * Formats commit message lines for console display.
44776
- */
44777
- function formatCommitMessageForDisplay(message) {
44778
- const lines = message.split(/\r?\n/);
44779
- return lines.map((line) => colors__default["default"].bgBlue.white(` ${line} `)).join('\n');
44780
- }
44781
-
44782
44937
  /**
44783
44938
  * Prints the formatted commit message preview.
44784
44939
  */
@@ -44824,82 +44979,36 @@
44824
44979
  }
44825
44980
 
44826
44981
  /**
44827
- * Runs one prompt-processing round with a dedicated temporary runtime log file and optionally defers cleanup to the round tracker.
44982
+ * Decides whether one temporary prompt artifact should be deleted after a round finishes.
44983
+ */
44984
+ function shouldDeleteTemporaryArtifact({ preserveArtifactsOnSuccess, hasFailed, }) {
44985
+ return !preserveArtifactsOnSuccess && !hasFailed;
44986
+ }
44987
+
44988
+ /**
44989
+ * Runs one prompt-processing round with a dedicated temporary runtime log file that is cleaned up only after successful non-preserved runs.
44828
44990
  */
44829
- async function withPromptRuntimeLog(scriptPath, handler, promptRoundArtifacts) {
44991
+ async function withPromptRuntimeLog(scriptPath, handler, options) {
44830
44992
  const logPath = buildScriptLogPath(scriptPath);
44993
+ let hasFailed = false;
44831
44994
  await promises.unlink(logPath).catch(() => undefined);
44832
- promptRoundArtifacts === null || promptRoundArtifacts === void 0 ? void 0 : promptRoundArtifacts.track(logPath, 'runtime-log');
44833
44995
  try {
44834
44996
  return await handler(logPath);
44835
44997
  }
44998
+ catch (error) {
44999
+ hasFailed = true;
45000
+ throw error;
45001
+ }
44836
45002
  finally {
44837
- if (!promptRoundArtifacts) {
45003
+ if (shouldDeleteTemporaryArtifact({
45004
+ preserveArtifactsOnSuccess: options === null || options === void 0 ? void 0 : options.preserveArtifactsOnSuccess,
45005
+ hasFailed,
45006
+ })) {
44838
45007
  await promises.unlink(logPath).catch(() => undefined);
44839
45008
  }
44840
45009
  }
44841
45010
  }
44842
45011
 
44843
- /**
44844
- * Shared artifact kinds preserved for debugging when a prompt round fails.
44845
- */
44846
- const FAILURE_PRESERVED_ARTIFACT_KINDS = new Set(['runner-script', 'runtime-log']);
44847
- /**
44848
- * Shared artifact kinds preserved after a successful prompt round when explicitly requested.
44849
- */
44850
- const SUCCESS_PRESERVED_ARTIFACT_KINDS = new Set(['runner-script', 'runtime-log']);
44851
- /**
44852
- * Empty preserved-artifact set used for successful rounds without `--preserve-logs`.
44853
- */
44854
- const NO_PRESERVED_ARTIFACT_KINDS = new Set();
44855
- /**
44856
- * Tracks temporary prompt-round artifacts and deletes only those not preserved for the final round outcome.
44857
- */
44858
- class PromptRoundArtifacts {
44859
- /**
44860
- * Creates a new prompt-round artifact tracker.
44861
- */
44862
- constructor(preservedArtifactKindsByOutcome) {
44863
- this.preservedArtifactKindsByOutcome = preservedArtifactKindsByOutcome;
44864
- this.trackedArtifacts = new Map();
44865
- }
44866
- /**
44867
- * Registers one temporary artifact for round-final cleanup.
44868
- */
44869
- track(path, kind) {
44870
- this.trackedArtifacts.set(path, kind);
44871
- }
44872
- /**
44873
- * Cleans up all tracked artifacts that should not survive the final round outcome.
44874
- */
44875
- async cleanup(outcome) {
44876
- const preservedArtifactKinds = this.preservedArtifactKindsByOutcome[outcome];
44877
- const trackedArtifacts = [...this.trackedArtifacts.entries()];
44878
- this.trackedArtifacts.clear();
44879
- await Promise.all(trackedArtifacts.map(async ([path, kind]) => {
44880
- if (preservedArtifactKinds.has(kind)) {
44881
- return;
44882
- }
44883
- await promises.unlink(path).catch(() => undefined);
44884
- }));
44885
- }
44886
- }
44887
- /**
44888
- * Creates the default artifact-retention policy used by `ptbk coder run`.
44889
- */
44890
- function createCoderRunPromptRoundArtifacts(isPreserveLogs) {
44891
- return new PromptRoundArtifacts({
44892
- success: isPreserveLogs ? SUCCESS_PRESERVED_ARTIFACT_KINDS : NO_PRESERVED_ARTIFACT_KINDS,
44893
- failure: FAILURE_PRESERVED_ARTIFACT_KINDS,
44894
- });
44895
- }
44896
- /**
44897
- * Derives the tracked artifact kind from one temporary shell path.
44898
- */
44899
- function getPromptRoundArtifactKindFromScriptPath(scriptPath) {
44900
- return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test-script' : 'runner-script';
44901
- }
44902
-
44903
45012
  /**
44904
45013
  * Waits for the user to press Enter before continuing.
44905
45014
  */
@@ -46739,6 +46848,15 @@
46739
46848
  return `${path.relative(process.cwd(), file.path).replace(/\\/g, '/')}#${section.startLine + 1}`;
46740
46849
  }
46741
46850
 
46851
+ /**
46852
+ * Extracts a short summary line from a prompt section.
46853
+ */
46854
+ function buildPromptSummary(file, section) {
46855
+ const lines = buildCodexPrompt(file, section).split(/\r?\n/);
46856
+ const firstLine = lines.find((line) => line.trim() !== '');
46857
+ return (firstLine === null || firstLine === void 0 ? void 0 : firstLine.trim()) || '(empty prompt)';
46858
+ }
46859
+
46742
46860
  /**
46743
46861
  * Builds the script path for a prompt section.
46744
46862
  */
@@ -46798,15 +46916,6 @@
46798
46916
  return nextPrompt;
46799
46917
  }
46800
46918
 
46801
- /**
46802
- * Extracts a short summary line from a prompt section.
46803
- */
46804
- function buildPromptSummary(file, section) {
46805
- const lines = buildCodexPrompt(file, section).split(/\r?\n/);
46806
- const firstLine = lines.find((line) => line.trim() !== '');
46807
- return (firstLine === null || firstLine === void 0 ? void 0 : firstLine.trim()) || '(empty prompt)';
46808
- }
46809
-
46810
46919
  /**
46811
46920
  * Lists upcoming tasks that are ready to run (no authoring placeholders).
46812
46921
  */
@@ -47358,25 +47467,29 @@ bash "$1"
47358
47467
  }
47359
47468
 
47360
47469
  /**
47361
- * Creates a temporary script file, runs a handler, and either cleans it up immediately or defers cleanup to the round tracker.
47470
+ * Creates a temporary script file, runs a handler, and cleans it up unless preservation is requested or the run fails.
47362
47471
  */
47363
47472
  async function withTempScript(options, handler) {
47364
- const { scriptPath, scriptContent, promptRoundArtifacts } = options;
47473
+ const { scriptPath, scriptContent } = options;
47474
+ let hasFailed = false;
47365
47475
  await promises.mkdir(posix.dirname(scriptPath), { recursive: true });
47366
47476
  await promises.writeFile(scriptPath, scriptContent, 'utf-8');
47367
- promptRoundArtifacts === null || promptRoundArtifacts === void 0 ? void 0 : promptRoundArtifacts.track(scriptPath, getPromptRoundArtifactKindFromScriptPath(scriptPath));
47368
47477
  try {
47369
47478
  return await handler(scriptPath);
47370
47479
  }
47480
+ catch (error) {
47481
+ hasFailed = true;
47482
+ throw error;
47483
+ }
47371
47484
  finally {
47372
- if (!promptRoundArtifacts) {
47485
+ if (shouldDeleteTemporaryArtifact({ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess, hasFailed })) {
47373
47486
  await promises.unlink(scriptPath).catch(() => undefined);
47374
47487
  }
47375
47488
  }
47376
47489
  }
47377
47490
 
47378
47491
  /**
47379
- * Creates a temporary script file, runs it, captures output, and cleans it up immediately unless a round tracker defers that cleanup.
47492
+ * Creates a temporary script file, runs it, captures output, and cleans it up unless preservation is requested or the run fails.
47380
47493
  */
47381
47494
  async function $runGoScriptWithOutput(options) {
47382
47495
  return await withTempScript(options, async (scriptPath) => {
@@ -47479,7 +47592,7 @@ bash "$1"
47479
47592
  scriptPath: options.scriptPath,
47480
47593
  scriptContent,
47481
47594
  logPath: options.logPath,
47482
- promptRoundArtifacts: options.promptRoundArtifacts,
47595
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
47483
47596
  });
47484
47597
  const usage = parseClaudeCodeJsonOutput(output);
47485
47598
  return { usage };
@@ -47487,7 +47600,7 @@ bash "$1"
47487
47600
  }
47488
47601
 
47489
47602
  /**
47490
- * Creates a temporary script file, runs it, and cleans it up immediately unless a round tracker defers that cleanup.
47603
+ * Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
47491
47604
  */
47492
47605
  async function $runGoScript(options) {
47493
47606
  await withTempScript(options, async (scriptPath) => {
@@ -47543,7 +47656,7 @@ bash "$1"
47543
47656
  scriptPath: options.scriptPath,
47544
47657
  scriptContent,
47545
47658
  logPath: options.logPath,
47546
- promptRoundArtifacts: options.promptRoundArtifacts,
47659
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
47547
47660
  });
47548
47661
  return { usage: UNCERTAIN_USAGE };
47549
47662
  }
@@ -47668,7 +47781,7 @@ bash "$1"
47668
47781
  scriptPath: options.scriptPath,
47669
47782
  scriptContent,
47670
47783
  logPath: options.logPath,
47671
- promptRoundArtifacts: options.promptRoundArtifacts,
47784
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
47672
47785
  });
47673
47786
  const usage = parseGeminiUsageFromOutput(output, options.prompt, this.options.model);
47674
47787
  return { usage };
@@ -47725,7 +47838,7 @@ bash "$1"
47725
47838
  scriptPath: options.scriptPath,
47726
47839
  scriptContent,
47727
47840
  logPath: options.logPath,
47728
- promptRoundArtifacts: options.promptRoundArtifacts,
47841
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
47729
47842
  });
47730
47843
  return { usage: UNCERTAIN_USAGE };
47731
47844
  }
@@ -47901,7 +48014,7 @@ bash "$1"
47901
48014
  }
47902
48015
 
47903
48016
  /**
47904
- * Creates a temporary script file, runs it, waits for a completion marker and idle time, and defers cleanup when a round tracker is provided.
48017
+ * Creates a temporary script file, runs it, waits for a completion marker and idle time, and cleans it up unless preservation is requested or the run fails.
47905
48018
  * Returns the captured output for post-processing.
47906
48019
  */
47907
48020
  async function $runGoScriptUntilMarkerIdle(options) {
@@ -48290,7 +48403,7 @@ bash "$1"
48290
48403
  completionLineMatcher: CODEX_COMPLETION_LINE,
48291
48404
  idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
48292
48405
  logPath: options.logPath,
48293
- promptRoundArtifacts: options.promptRoundArtifacts,
48406
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48294
48407
  });
48295
48408
  this.rateLimitBackoff.reset();
48296
48409
  return { usage: buildCodexUsageFromOutput(output, this.options.model) };
@@ -48425,7 +48538,7 @@ bash "$1"
48425
48538
  scriptPath: options.scriptPath,
48426
48539
  scriptContent,
48427
48540
  logPath: options.logPath,
48428
- promptRoundArtifacts: options.promptRoundArtifacts,
48541
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48429
48542
  });
48430
48543
  }
48431
48544
  catch (error) {
@@ -48453,6 +48566,7 @@ bash "$1"
48453
48566
  ${options.command}
48454
48567
  `),
48455
48568
  logPath: options.logPath,
48569
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48456
48570
  });
48457
48571
  }
48458
48572
 
@@ -48477,7 +48591,7 @@ bash "$1"
48477
48591
  scriptPath: options.scriptPath,
48478
48592
  projectPath: options.projectPath,
48479
48593
  logPath: options.logPath,
48480
- promptRoundArtifacts: options.promptRoundArtifacts,
48594
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48481
48595
  });
48482
48596
  return { ...result, attemptCount: 1 };
48483
48597
  }
@@ -48490,7 +48604,7 @@ bash "$1"
48490
48604
  scriptPath: options.scriptPath,
48491
48605
  projectPath: options.projectPath,
48492
48606
  logPath: options.logPath,
48493
- promptRoundArtifacts: options.promptRoundArtifacts,
48607
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48494
48608
  });
48495
48609
  console.info(colors__default["default"].gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
48496
48610
  try {
@@ -48499,6 +48613,7 @@ bash "$1"
48499
48613
  projectPath: options.projectPath,
48500
48614
  scriptPath: buildPromptTestScriptPath(options.scriptPath),
48501
48615
  logPath: options.logPath,
48616
+ preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
48502
48617
  });
48503
48618
  return { ...result, attemptCount };
48504
48619
  }
@@ -48580,24 +48695,203 @@ bash "$1"
48580
48695
  }
48581
48696
 
48582
48697
  /**
48583
- * Maximum number of agent output lines kept in the scrolling output area.
48584
- *
48585
- * @private internal constant of coder run UI
48698
+ * Maximum number of output lines reserved for agent output in the UI.
48586
48699
  */
48587
- const MAX_AGENT_OUTPUT_LINES = 12;
48700
+ const MAX_VISIBLE_OUTPUT_LINES = 8;
48588
48701
  /**
48589
- * Calendar formats used when displaying the estimated completion time.
48702
+ * Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
48703
+ */
48704
+ function buildCoderRunUiFrame(options) {
48705
+ const totalWidth = Math.max(56, Math.min(options.terminalWidth, 96));
48706
+ const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
48707
+ const promptStatusPrefix = isPromptActive ? `${colors__default["default"].yellow(`${options.spinner} `)}` : '';
48708
+ const sessionScopeLine = options.progress.sessionTotal > 0
48709
+ ? `Working on ${options.progress.currentPromptIndex}/${options.progress.sessionTotal} prompts with Priority ≥${options.config.priority}`
48710
+ : `No runnable prompts with Priority ≥${options.config.priority}`;
48711
+ const sessionCountLine = `Done ${options.progress.sessionDone}/${options.progress.sessionTotal} this run · Repo total ${options.progress.totalPrompts}`;
48712
+ const sessionQueueParts = [];
48713
+ if (options.progress.skippedPrompts > 0) {
48714
+ sessionQueueParts.push(`Skipping ${formatPromptCount(options.progress.skippedPrompts)} with Priority <${options.config.priority}`);
48715
+ }
48716
+ if (options.progress.toBeWrittenPrompts > 0) {
48717
+ sessionQueueParts.push(`Write first ${formatPromptCount(options.progress.toBeWrittenPrompts)}`);
48718
+ }
48719
+ const sessionLines = [
48720
+ `${buildPhaseBadge(options.phase, options.pauseState)} ${fitPlainText(options.statusMessage, totalWidth - 18)}`,
48721
+ sessionScopeLine,
48722
+ sessionCountLine,
48723
+ ...(sessionQueueParts.length > 0 ? [sessionQueueParts.join(' · ')] : []),
48724
+ `Elapsed ${options.progress.elapsedText} · Est. total ${options.progress.estimatedTotalText} · Est. done ${options.progress.estimatedLabel}`,
48725
+ buildProgressBar(options.progress.percentage, totalWidth - 6, `${options.progress.percentage}% complete (${options.progress.sessionDone}/${options.progress.sessionTotal} done)`),
48726
+ ];
48727
+ const metadataParts = [options.config.agentName || 'No agent selected'];
48728
+ if (options.config.modelName) {
48729
+ metadataParts.push(options.config.modelName);
48730
+ }
48731
+ if (options.config.thinkingLevel) {
48732
+ metadataParts.push(`thinking ${options.config.thinkingLevel}`);
48733
+ }
48734
+ const runnerDetails = [
48735
+ [`${colors__default["default"].bgCyan.black(' PTBK ')}`, colors__default["default"].bgBlue.white(' CODER '), colors__default["default"].bold.white(' Promptbook Coder')]
48736
+ .join(''),
48737
+ metadataParts.join(' · '),
48738
+ buildConfigSummaryLine(options.config),
48739
+ ];
48740
+ const currentTaskLines = options.currentPromptLabel
48741
+ ? [
48742
+ `${promptStatusPrefix}${colors__default["default"].bold.white(fitPlainText(options.currentPromptLabel, totalWidth - 8))}`,
48743
+ `Attempt ${options.currentAttempt}/${options.maxAttempts} · ${options.statusMessage}`,
48744
+ ...options.detailLines.map((detailLine) => `• ${detailLine}`),
48745
+ ]
48746
+ : [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
48747
+ const visibleOutputLines = options.agentOutputLines.length > 0
48748
+ ? options.agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES).map((line) => `› ${stripAnsi(line)}`)
48749
+ : ['No live agent output yet.'];
48750
+ const controls = buildControlPills(options.pauseState, options.pendingEnterLabel).join(' ');
48751
+ const frame = [
48752
+ ...renderBox('Brand', runnerDetails, totalWidth, colors__default["default"].cyan.bold),
48753
+ ...renderBox('Session', sessionLines, totalWidth, colors__default["default"].yellow.bold),
48754
+ ...renderBox(options.currentPromptLabel ? 'Current task' : 'Queue', currentTaskLines, totalWidth, colors__default["default"].magenta.bold),
48755
+ ...renderBox('Live output', visibleOutputLines, totalWidth, colors__default["default"].green.bold),
48756
+ ];
48757
+ if (options.errors.length > 0) {
48758
+ frame.push(...renderBox('Errors', options.errors.map((errorLine) => `${colors__default["default"].red('✗')} ${errorLine}`), totalWidth, colors__default["default"].red.bold));
48759
+ }
48760
+ frame.push(...renderBox('Controls', [controls], totalWidth, colors__default["default"].white.bold));
48761
+ return frame;
48762
+ }
48763
+ /**
48764
+ * Renders a framed box with a colored title and padded body lines.
48765
+ */
48766
+ function renderBox(title, lines, totalWidth, colorizeTitle) {
48767
+ const bodyWidth = Math.max(10, totalWidth - 4);
48768
+ const titleText = ` ${title} `;
48769
+ const topBorder = colors__default["default"].gray('┌') +
48770
+ colorizeTitle(titleText) +
48771
+ colors__default["default"].gray('─'.repeat(Math.max(0, totalWidth - 2 - titleText.length)) + '┐');
48772
+ const body = lines.map((line) => {
48773
+ const paddedLine = padAnsiText(line, bodyWidth);
48774
+ return colors__default["default"].gray('│ ') + paddedLine + colors__default["default"].gray(' │');
48775
+ });
48776
+ const bottomBorder = colors__default["default"].gray(`└${'─'.repeat(totalWidth - 2)}┘`);
48777
+ return [topBorder, ...body, bottomBorder];
48778
+ }
48779
+ /**
48780
+ * Builds the compact config summary line shown in the branding box.
48781
+ */
48782
+ function buildConfigSummaryLine(config) {
48783
+ const parts = [`Priority ≥${config.priority}`];
48784
+ if (config.context) {
48785
+ parts.unshift(`Context ${config.context}`);
48786
+ }
48787
+ if (config.testCommand) {
48788
+ parts.push(`Test ${config.testCommand}`);
48789
+ }
48790
+ return parts.join(' · ');
48791
+ }
48792
+ /**
48793
+ * Builds the colored phase badge shown in the session box.
48794
+ */
48795
+ function buildPhaseBadge(phase, pauseState) {
48796
+ if (pauseState !== 'RUNNING' || phase === 'paused') {
48797
+ return colors__default["default"].bgYellow.black(' PAUSED ');
48798
+ }
48799
+ switch (phase) {
48800
+ case 'loading':
48801
+ case 'initializing':
48802
+ return colors__default["default"].bgCyan.black(' LOADING ');
48803
+ case 'running':
48804
+ return colors__default["default"].bgGreen.black(' RUNNING ');
48805
+ case 'verifying':
48806
+ return colors__default["default"].bgMagenta.white(' VERIFYING ');
48807
+ case 'waiting':
48808
+ return colors__default["default"].bgBlue.white(' WAITING ');
48809
+ case 'done':
48810
+ return colors__default["default"].bgGreen.black(' DONE ');
48811
+ case 'error':
48812
+ return colors__default["default"].bgRed.white(' ERROR ');
48813
+ default:
48814
+ return colors__default["default"].bgWhite.black(' READY ');
48815
+ }
48816
+ }
48817
+ /**
48818
+ * Builds the progress bar shown in the session box.
48819
+ */
48820
+ function buildProgressBar(percentage, availableWidth, label) {
48821
+ const percentageLabel = label;
48822
+ const barWidth = Math.max(10, availableWidth - percentageLabel.length - 1);
48823
+ const filledWidth = Math.round((percentage / 100) * barWidth);
48824
+ const emptyWidth = Math.max(0, barWidth - filledWidth);
48825
+ return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].gray('░'.repeat(emptyWidth))} ${percentageLabel}`;
48826
+ }
48827
+ /**
48828
+ * Formats a prompt count with singular/plural wording.
48829
+ */
48830
+ function formatPromptCount(count) {
48831
+ return `${count} prompt${count === 1 ? '' : 's'}`;
48832
+ }
48833
+ /**
48834
+ * Builds the control pills shown in the footer box.
48835
+ */
48836
+ function buildControlPills(pauseState, pendingEnterLabel) {
48837
+ const pills = [];
48838
+ if (pendingEnterLabel) {
48839
+ pills.push(colors__default["default"].bgWhite.black(' ENTER ') + colors__default["default"].white(` ${pendingEnterLabel}`));
48840
+ }
48841
+ pills.push(pauseState === 'RUNNING'
48842
+ ? colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Pause')
48843
+ : colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Resume'));
48844
+ pills.push(colors__default["default"].bgRed.white(' CTRL+C ') + colors__default["default"].white(' Exit'));
48845
+ return pills;
48846
+ }
48847
+ /**
48848
+ * Pads or truncates a possibly ANSI-colored line to the target visible width.
48849
+ */
48850
+ function padAnsiText(text, width) {
48851
+ const fittedText = fitAnsiText(text, width);
48852
+ return fittedText + ' '.repeat(Math.max(0, width - visibleLength(fittedText)));
48853
+ }
48854
+ /**
48855
+ * Truncates a possibly ANSI-colored line to the target visible width.
48856
+ */
48857
+ function fitAnsiText(text, width) {
48858
+ if (visibleLength(text) <= width) {
48859
+ return text;
48860
+ }
48861
+ return fitPlainText(stripAnsi(text), width);
48862
+ }
48863
+ /**
48864
+ * Truncates a plain-text line to the target width with an ellipsis.
48865
+ */
48866
+ function fitPlainText(text, width) {
48867
+ if (text.length <= width) {
48868
+ return text;
48869
+ }
48870
+ if (width <= 3) {
48871
+ return '.'.repeat(width);
48872
+ }
48873
+ return `${text.slice(0, width - 3)}...`;
48874
+ }
48875
+ /**
48876
+ * Measures visible string width by stripping ANSI escape codes.
48877
+ */
48878
+ function visibleLength(text) {
48879
+ return stripAnsi(text).length;
48880
+ }
48881
+ /**
48882
+ * Strips ANSI escape codes from a string.
48883
+ */
48884
+ function stripAnsi(text) {
48885
+ // eslint-disable-next-line no-control-regex
48886
+ return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
48887
+ }
48888
+
48889
+ /**
48890
+ * Maximum number of agent output lines kept in the scrolling output area.
48590
48891
  *
48591
48892
  * @private internal constant of coder run UI
48592
48893
  */
48593
- const ESTIMATED_DONE_CALENDAR_FORMATS = {
48594
- sameDay: '[Today] h:mm',
48595
- nextDay: '[Tomorrow] h:mm',
48596
- nextWeek: 'dddd h:mm',
48597
- lastDay: '[Yesterday] h:mm',
48598
- lastWeek: 'dddd h:mm',
48599
- sameElse: 'MMM D h:mm',
48600
- };
48894
+ const MAX_AGENT_OUTPUT_LINES = 12;
48601
48895
  /**
48602
48896
  * Reactive state manager for the coder run terminal UI.
48603
48897
  *
@@ -48613,35 +48907,27 @@ bash "$1"
48613
48907
  this.currentPromptLabel = '';
48614
48908
  this.currentAttempt = 1;
48615
48909
  this.maxAttempts = 3;
48910
+ this.detailLines = [];
48616
48911
  this.agentOutputLines = [];
48617
48912
  this.phase = 'initializing';
48618
48913
  this.statusMessage = 'Initializing...';
48619
48914
  this.errors = [];
48620
48915
  this.stats = { done: 0, forAgent: 0, belowMinimumPriority: 0, toBeWritten: 0 };
48621
- /**
48622
- * Total milliseconds the timer was paused/waiting (excluded from elapsed display).
48623
- */
48624
- this.pausedMs = 0;
48625
- this.startTime = startTime;
48626
- // Timer starts paused — callers call `resumeTimer()` when actual work begins.
48627
- this.pausedSince = startTime.clone();
48916
+ this.timer = new CoderRunTimer(startTime, true);
48628
48917
  }
48629
48918
  /**
48630
48919
  * Pauses the elapsed timer (e.g. while waiting for user input or paused state).
48631
48920
  */
48632
48921
  pauseTimer() {
48633
- if (this.pausedSince === undefined) {
48634
- this.pausedSince = moment__default["default"]();
48635
- }
48922
+ this.timer.pause();
48923
+ this.emitChange();
48636
48924
  }
48637
48925
  /**
48638
48926
  * Resumes the elapsed timer after a pause.
48639
48927
  */
48640
48928
  resumeTimer() {
48641
- if (this.pausedSince !== undefined) {
48642
- this.pausedMs += moment__default["default"]().diff(this.pausedSince);
48643
- this.pausedSince = undefined;
48644
- }
48929
+ this.timer.resume();
48930
+ this.emitChange();
48645
48931
  }
48646
48932
  /**
48647
48933
  * Replaces the configuration shown in the UI header.
@@ -48665,41 +48951,15 @@ bash "$1"
48665
48951
  */
48666
48952
  getProgress() {
48667
48953
  var _a;
48668
- const stats = this.stats;
48669
- const totalPrompts = stats.done + stats.forAgent + stats.toBeWritten;
48670
- const sessionDone = Math.max(0, stats.done - ((_a = this.initialDone) !== null && _a !== void 0 ? _a : stats.done));
48671
- const sessionTotal = sessionDone + stats.forAgent;
48672
- const percentage = totalPrompts > 0 ? Math.round((stats.done / totalPrompts) * 100) : 0;
48673
- const wallMs = moment__default["default"]().diff(this.startTime);
48674
- const currentPauseMs = this.pausedSince !== undefined ? moment__default["default"]().diff(this.pausedSince) : 0;
48675
- const activeMs = Math.max(0, wallMs - this.pausedMs - currentPauseMs);
48676
- const elapsedDuration = moment__default["default"].duration(activeMs);
48677
- const elapsedText = formatDurationBrief(elapsedDuration);
48678
- let estimatedTotalText = '\u2014';
48679
- let estimatedLabel = 'unknown';
48680
- if (totalPrompts > 0 && stats.done > 0) {
48681
- const estimatedTotalMs = (elapsedDuration.asMilliseconds() * totalPrompts) / stats.done;
48682
- const estimatedRemainingMs = estimatedTotalMs - elapsedDuration.asMilliseconds();
48683
- const estimatedTotalDuration = moment__default["default"].duration(estimatedTotalMs);
48684
- const estimatedCompletion = moment__default["default"]().add(estimatedRemainingMs, 'milliseconds');
48685
- estimatedTotalText = formatDurationBrief(estimatedTotalDuration);
48686
- estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS);
48687
- }
48688
- return {
48689
- totalPrompts,
48690
- sessionDone,
48691
- sessionTotal,
48692
- percentage,
48693
- elapsedText,
48694
- estimatedTotalText,
48695
- estimatedLabel,
48696
- };
48954
+ return buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done);
48697
48955
  }
48698
48956
  /**
48699
48957
  * Sets the label of the prompt currently being processed and resets per-prompt state.
48700
48958
  */
48701
48959
  setCurrentPrompt(label) {
48702
48960
  this.currentPromptLabel = label;
48961
+ this.detailLines = [];
48962
+ this.pendingEnterLabel = undefined;
48703
48963
  this.agentOutputLines = [];
48704
48964
  this.currentAttempt = 1;
48705
48965
  this.emitChange();
@@ -48739,6 +48999,20 @@ bash "$1"
48739
48999
  this.statusMessage = message;
48740
49000
  this.emitChange();
48741
49001
  }
49002
+ /**
49003
+ * Replaces the contextual detail lines shown beneath the current prompt status.
49004
+ */
49005
+ setDetailLines(detailLines) {
49006
+ this.detailLines = detailLines.filter((detailLine) => detailLine.trim() !== '');
49007
+ this.emitChange();
49008
+ }
49009
+ /**
49010
+ * Sets or clears the Enter-key action label shown in the controls panel.
49011
+ */
49012
+ setPendingEnterLabel(pendingEnterLabel) {
49013
+ this.pendingEnterLabel = pendingEnterLabel;
49014
+ this.emitChange();
49015
+ }
48742
49016
  /**
48743
49017
  * Appends an error message to the error list shown in the UI.
48744
49018
  */
@@ -48750,31 +49024,6 @@ bash "$1"
48750
49024
  this.emit('change');
48751
49025
  }
48752
49026
  }
48753
- /**
48754
- * Formats a duration into a compact string such as "3h 12m" or "45s".
48755
- *
48756
- * @private internal utility of coder run UI
48757
- */
48758
- function formatDurationBrief(duration) {
48759
- const totalSeconds = Math.max(0, Math.round(duration.asSeconds()));
48760
- const hours = Math.floor(totalSeconds / 3600);
48761
- const minutes = Math.floor((totalSeconds % 3600) / 60);
48762
- const seconds = totalSeconds % 60;
48763
- const parts = [];
48764
- if (hours > 0) {
48765
- parts.push(`${hours}h`);
48766
- }
48767
- if (minutes > 0) {
48768
- parts.push(`${minutes}m`);
48769
- }
48770
- if (!parts.length && seconds > 0) {
48771
- parts.push(`${seconds}s`);
48772
- }
48773
- if (!parts.length) {
48774
- parts.push('0s');
48775
- }
48776
- return parts.join(' ');
48777
- }
48778
49027
 
48779
49028
  /**
48780
49029
  * Refresh interval for the terminal UI in milliseconds.
@@ -48782,18 +49031,6 @@ bash "$1"
48782
49031
  * @private internal constant of coder run UI
48783
49032
  */
48784
49033
  const UI_REFRESH_INTERVAL_MS = 200;
48785
- /**
48786
- * Character width used for the text progress bar.
48787
- *
48788
- * @private internal constant of coder run UI
48789
- */
48790
- const PROGRESS_BAR_WIDTH = 40;
48791
- /**
48792
- * Maximum number of output lines reserved for agent output in the UI.
48793
- *
48794
- * @private internal constant of coder run UI
48795
- */
48796
- const MAX_VISIBLE_OUTPUT_LINES = 8;
48797
49034
  /**
48798
49035
  * Spinner animation frames.
48799
49036
  *
@@ -48812,31 +49049,12 @@ bash "$1"
48812
49049
  '\u280F',
48813
49050
  ];
48814
49051
  /**
48815
- * Strips ANSI escape codes from a string.
48816
- *
48817
- * @private internal utility of coder run UI
48818
- */
48819
- function stripAnsi(text) {
48820
- // eslint-disable-next-line no-control-regex
48821
- return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
48822
- }
48823
- /**
48824
- * Returns the usable terminal width, capped at 80.
49052
+ * Returns the usable terminal width, capped at 96.
48825
49053
  *
48826
49054
  * @private internal utility of coder run UI
48827
49055
  */
48828
49056
  function getTerminalWidth() {
48829
- return Math.min(process.stdout.columns || 80, 80);
48830
- }
48831
- /**
48832
- * Builds a text progress bar string from a percentage.
48833
- *
48834
- * @private internal utility of coder run UI
48835
- */
48836
- function buildProgressBar(percentage) {
48837
- const filled = Math.round((percentage / 100) * PROGRESS_BAR_WIDTH);
48838
- const empty = PROGRESS_BAR_WIDTH - filled;
48839
- return colors__default["default"].green('\u2588'.repeat(filled)) + colors__default["default"].gray('\u2591'.repeat(empty)) + ` ${percentage}%`;
49057
+ return Math.min(process.stdout.columns || 80, 96);
48840
49058
  }
48841
49059
  /**
48842
49060
  * Boots the ANSI terminal UI for `ptbk coder run`.
@@ -48857,21 +49075,20 @@ bash "$1"
48857
49075
  state,
48858
49076
  startCapturingAgentOutput: () => { },
48859
49077
  stopCapturingAgentOutput: () => { },
49078
+ waitForEnter: async () => { },
48860
49079
  cleanup: () => { },
48861
49080
  };
48862
49081
  }
48863
- // --- Console interception ---
48864
49082
  const originalConsoleInfo = console.info;
48865
49083
  const originalConsoleWarn = console.warn;
48866
49084
  const originalConsoleError = console.error;
48867
49085
  const originalConsoleLog = console.log;
48868
49086
  let isCapturing = false;
49087
+ let pendingEnterResolver;
48869
49088
  console.info = (...args) => {
48870
49089
  if (isCapturing) {
48871
49090
  state.addAgentOutput(args.map(String).join(' '));
48872
49091
  }
48873
- // In UI mode, non-captured output is intentionally suppressed
48874
- // so it does not interfere with the repainted frame.
48875
49092
  };
48876
49093
  console.warn = (...args) => {
48877
49094
  if (isCapturing) {
@@ -48888,29 +49105,11 @@ bash "$1"
48888
49105
  state.addAgentOutput(args.map(String).join(' '));
48889
49106
  }
48890
49107
  };
48891
- // --- Keyboard input (pause) ---
48892
49108
  const readline$1 = require('readline');
48893
49109
  readline$1.emitKeypressEvents(process.stdin);
48894
49110
  if (process.stdin.isTTY) {
48895
49111
  process.stdin.setRawMode(true);
48896
49112
  }
48897
- const keypressHandler = (_str, key) => {
48898
- if (key.ctrl && key.name === 'c') {
48899
- cleanup();
48900
- process.exit(0);
48901
- }
48902
- if (key.name === 'p') {
48903
- const current = getPauseState();
48904
- if (current === 'RUNNING') {
48905
- requestPause();
48906
- }
48907
- else {
48908
- requestResume();
48909
- }
48910
- }
48911
- };
48912
- process.stdin.on('keypress', keypressHandler);
48913
- // --- Rendering ---
48914
49113
  let spinnerFrame = 0;
48915
49114
  let previousFrameLineCount = 0;
48916
49115
  let isRendering = false;
@@ -48938,8 +49137,22 @@ bash "$1"
48938
49137
  }
48939
49138
  isRendering = true;
48940
49139
  try {
48941
- const lines = buildFrame();
48942
- // Move cursor up to clear the previous frame.
49140
+ const lines = buildCoderRunUiFrame({
49141
+ terminalWidth: getTerminalWidth(),
49142
+ spinner: SPINNER_FRAMES[spinnerFrame],
49143
+ pauseState: getPauseState(),
49144
+ config: state.config,
49145
+ phase: state.phase,
49146
+ currentPromptLabel: state.currentPromptLabel,
49147
+ currentAttempt: state.currentAttempt,
49148
+ maxAttempts: state.maxAttempts,
49149
+ statusMessage: state.statusMessage,
49150
+ detailLines: state.detailLines,
49151
+ pendingEnterLabel: state.pendingEnterLabel,
49152
+ agentOutputLines: state.agentOutputLines,
49153
+ errors: state.errors,
49154
+ progress: state.getProgress(),
49155
+ });
48943
49156
  if (previousFrameLineCount > 0) {
48944
49157
  process.stdout.write(`\x1b[${previousFrameLineCount}A`);
48945
49158
  }
@@ -48951,14 +49164,12 @@ bash "$1"
48951
49164
  process.stdout.write('\n');
48952
49165
  }
48953
49166
  }
48954
- // Clear any leftover lines from a previous longer frame.
48955
49167
  if (lines.length < previousFrameLineCount) {
48956
49168
  for (let i = lines.length; i < previousFrameLineCount; i++) {
48957
49169
  process.stdout.write('\n');
48958
49170
  readline.clearLine(process.stdout, 0);
48959
49171
  readline.cursorTo(process.stdout, 0);
48960
49172
  }
48961
- // Move back up to the end of the current frame.
48962
49173
  const overshoot = previousFrameLineCount - lines.length;
48963
49174
  if (overshoot > 0) {
48964
49175
  process.stdout.write(`\x1b[${overshoot}A`);
@@ -48971,94 +49182,35 @@ bash "$1"
48971
49182
  isRendering = false;
48972
49183
  }
48973
49184
  }
48974
- /**
48975
- * Builds the complete frame as an array of terminal lines.
48976
- */
48977
- function buildFrame() {
48978
- const w = getTerminalWidth();
48979
- const sep = colors__default["default"].gray('\u2500'.repeat(w - 2));
48980
- const spinner = SPINNER_FRAMES[spinnerFrame];
48981
- const { config, phase, currentPromptLabel, currentAttempt, maxAttempts, statusMessage, agentOutputLines, errors, } = state;
48982
- const progress = state.getProgress();
48983
- const isPaused = getPauseState() !== 'RUNNING';
48984
- const isActive = phase === 'running' || phase === 'verifying' || phase === 'loading';
48985
- const lines = [];
48986
- // --- Branding ---
48987
- lines.push(colors__default["default"].bold.cyan('\u2728 Promptbook Coder'));
48988
- // --- Config ---
48989
- let configLine1 = `Agent: ${colors__default["default"].bold.green(config.agentName)}`;
48990
- if (config.modelName) {
48991
- configLine1 += ` \u2502 Model: ${colors__default["default"].bold(config.modelName)}`;
48992
- }
48993
- if (config.thinkingLevel) {
48994
- configLine1 += ` \u2502 Thinking: ${colors__default["default"].bold(config.thinkingLevel)}`;
48995
- }
48996
- lines.push(configLine1);
48997
- let configLine2 = '';
48998
- if (config.context) {
48999
- configLine2 += `Context: ${colors__default["default"].yellow(config.context)} \u2502 `;
49000
- }
49001
- configLine2 += `Priority: \u2265${config.priority}`;
49002
- if (config.testCommand) {
49003
- configLine2 += ` \u2502 Test: ${colors__default["default"].gray(config.testCommand)}`;
49004
- }
49005
- lines.push(configLine2);
49006
- // --- Separator ---
49007
- lines.push(sep);
49008
- // --- Progress ---
49009
- const progressSummary = [
49010
- `${progress.sessionDone}/${progress.sessionTotal} Prompts (${progress.totalPrompts} total)`,
49011
- `${progress.elapsedText}/${progress.estimatedTotalText}`,
49012
- `Est. done ${progress.estimatedLabel}`,
49013
- ].join(' \u2502 ');
49014
- lines.push(progressSummary);
49015
- lines.push(buildProgressBar(progress.percentage));
49016
- // --- Separator ---
49017
- lines.push(sep);
49018
- // --- Current prompt ---
49019
- if (currentPromptLabel) {
49020
- const spinnerPrefix = isActive ? colors__default["default"].yellow(`${spinner} `) : ' ';
49021
- lines.push(spinnerPrefix + colors__default["default"].bold(currentPromptLabel));
49022
- lines.push(colors__default["default"].gray(`Attempt ${currentAttempt}/${maxAttempts} \u2502 ${statusMessage}`));
49185
+ const keypressHandler = (_str, key) => {
49186
+ if (key.ctrl && key.name === 'c') {
49187
+ cleanup();
49188
+ process.exit(0);
49023
49189
  }
49024
- else {
49025
- lines.push(colors__default["default"].gray(statusMessage));
49026
- }
49027
- // --- Agent output ---
49028
- if (agentOutputLines.length > 0) {
49029
- lines.push('');
49030
- lines.push(colors__default["default"].gray.bold('Agent output:'));
49031
- const visibleLines = agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES);
49032
- for (const line of visibleLines) {
49033
- const cleanLine = stripAnsi(line);
49034
- // Truncate to terminal width.
49035
- const truncated = cleanLine.length > w - 2 ? cleanLine.slice(0, w - 5) + '...' : cleanLine;
49036
- lines.push(colors__default["default"].gray(truncated));
49037
- }
49038
- }
49039
- // --- Errors ---
49040
- if (errors.length > 0) {
49041
- lines.push('');
49042
- for (const err of errors) {
49043
- lines.push(colors__default["default"].red(`\u2717 ${err}`));
49044
- }
49045
- }
49046
- // --- Separator ---
49047
- lines.push(sep);
49048
- // --- Controls ---
49049
- const pauseLabel = isPaused
49050
- ? colors__default["default"].bgYellow.black(' PAUSED ') + colors__default["default"].gray(' [P] Resume \u2502 Ctrl+C Exit')
49051
- : colors__default["default"].gray('[P] Pause \u2502 Ctrl+C Exit');
49052
- lines.push(pauseLabel);
49053
- return lines;
49054
- }
49055
- // Initial render.
49190
+ if (key.name === 'p') {
49191
+ if (getPauseState() === 'RUNNING') {
49192
+ requestPause();
49193
+ }
49194
+ else {
49195
+ requestResume();
49196
+ }
49197
+ return;
49198
+ }
49199
+ if ((key.name === 'return' || key.name === 'enter') && pendingEnterResolver) {
49200
+ const resolvePendingEnter = pendingEnterResolver;
49201
+ pendingEnterResolver = undefined;
49202
+ state.setPendingEnterLabel(undefined);
49203
+ resolvePendingEnter();
49204
+ }
49205
+ };
49206
+ process.stdin.on('keypress', keypressHandler);
49056
49207
  process.stdout.write('\n');
49057
49208
  render();
49058
49209
  const interval = setInterval(scheduleRender, UI_REFRESH_INTERVAL_MS);
49059
- // Listen for state changes and schedule a re-render (debounced).
49060
49210
  state.on('change', scheduleRender);
49061
- // --- Cleanup ---
49211
+ /**
49212
+ * Tears down the terminal UI and restores console / stdin state.
49213
+ */
49062
49214
  function cleanup() {
49063
49215
  clearInterval(interval);
49064
49216
  state.off('change', scheduleRender);
@@ -49066,12 +49218,14 @@ bash "$1"
49066
49218
  if (process.stdin.isTTY) {
49067
49219
  process.stdin.setRawMode(false);
49068
49220
  }
49221
+ const resolvePendingEnter = pendingEnterResolver;
49222
+ pendingEnterResolver = undefined;
49223
+ resolvePendingEnter === null || resolvePendingEnter === void 0 ? void 0 : resolvePendingEnter();
49069
49224
  isCapturing = false;
49070
49225
  console.info = originalConsoleInfo;
49071
49226
  console.warn = originalConsoleWarn;
49072
49227
  console.error = originalConsoleError;
49073
49228
  console.log = originalConsoleLog;
49074
- // Render one final frame so the user sees the last state.
49075
49229
  render();
49076
49230
  process.stdout.write('\n');
49077
49231
  }
@@ -49083,6 +49237,19 @@ bash "$1"
49083
49237
  stopCapturingAgentOutput() {
49084
49238
  isCapturing = false;
49085
49239
  },
49240
+ waitForEnter(actionLabel) {
49241
+ if (pendingEnterResolver) {
49242
+ throw new Error('Coder run UI is already waiting for Enter.');
49243
+ }
49244
+ state.setPendingEnterLabel(actionLabel);
49245
+ scheduleRender();
49246
+ return new Promise((resolve) => {
49247
+ pendingEnterResolver = () => {
49248
+ scheduleRender();
49249
+ resolve();
49250
+ };
49251
+ });
49252
+ },
49086
49253
  cleanup,
49087
49254
  };
49088
49255
  }
@@ -49150,11 +49317,11 @@ bash "$1"
49150
49317
  `));
49151
49318
  }
49152
49319
  const runStartDate = moment__default["default"]();
49153
- const isUiMode = !options.dryRun && Boolean(process.stdout.isTTY);
49154
- const progressDisplay = options.dryRun || isUiMode ? undefined : new CliProgressDisplay(runStartDate);
49155
- const uiHandle = isUiMode ? renderCoderRunUi(runStartDate) : undefined;
49320
+ const isRichUiEnabled = !options.dryRun && !options.noUi && Boolean(process.stdout.isTTY);
49321
+ const progressDisplay = options.dryRun || options.noUi || isRichUiEnabled ? undefined : new CliProgressDisplay(runStartDate, options.priority);
49322
+ const uiHandle = isRichUiEnabled ? renderCoderRunUi(runStartDate) : undefined;
49156
49323
  // When the Ink UI is active it handles keyboard input itself, so skip the raw stdin listener.
49157
- if (!isUiMode) {
49324
+ if (!isRichUiEnabled) {
49158
49325
  listenForPause();
49159
49326
  }
49160
49327
  try {
@@ -49270,17 +49437,19 @@ bash "$1"
49270
49437
  let hasWaitedForStart = false;
49271
49438
  while (just(true)) {
49272
49439
  await checkPause({
49273
- silent: isUiMode,
49440
+ silent: isRichUiEnabled,
49274
49441
  onPaused: () => {
49442
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
49275
49443
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
49276
49444
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
49277
49445
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
49278
49446
  },
49279
49447
  onResumed: () => {
49448
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
49280
49449
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
49281
49450
  },
49282
49451
  });
49283
- if (isUiMode) {
49452
+ if (isRichUiEnabled) {
49284
49453
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
49285
49454
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Loading prompts...');
49286
49455
  }
@@ -49288,17 +49457,17 @@ bash "$1"
49288
49457
  const stats = summarizePrompts(promptFiles, options.priority);
49289
49458
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.update(stats);
49290
49459
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(stats);
49291
- if (!isUiMode) {
49460
+ if (!isRichUiEnabled) {
49292
49461
  printStats(stats, options.priority);
49293
49462
  }
49294
49463
  const nextPrompt = findNextTodoPrompt(promptFiles, options.priority);
49295
49464
  if (!hasShownUpcomingTasks) {
49296
- if (stats.toBeWritten > 0 && !isUiMode) {
49465
+ if (stats.toBeWritten > 0 && !isRichUiEnabled) {
49297
49466
  console.info(colors__default["default"].yellow('Following prompts need to be written:'));
49298
49467
  printPromptsToBeWritten(promptFiles, options.priority);
49299
49468
  console.info('');
49300
49469
  }
49301
- if (!isUiMode) {
49470
+ if (!isRichUiEnabled) {
49302
49471
  printUpcomingTasks(listUpcomingTasks(promptFiles, options.priority));
49303
49472
  }
49304
49473
  hasShownUpcomingTasks = true;
@@ -49308,7 +49477,7 @@ bash "$1"
49308
49477
  const message = 'No prompts ready for agent.';
49309
49478
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
49310
49479
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('done');
49311
- if (!isUiMode) {
49480
+ if (!isRichUiEnabled) {
49312
49481
  console.info(colors__default["default"].yellow(message));
49313
49482
  }
49314
49483
  }
@@ -49316,16 +49485,28 @@ bash "$1"
49316
49485
  const message = 'All prompts are done.';
49317
49486
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
49318
49487
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('done');
49319
- if (!isUiMode) {
49488
+ if (!isRichUiEnabled) {
49320
49489
  console.info(colors__default["default"].green(message));
49321
49490
  }
49322
49491
  }
49323
49492
  return;
49324
49493
  }
49494
+ const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
49325
49495
  if (options.waitForUser) {
49496
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
49326
49497
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
49327
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(hasWaitedForStart ? 'Waiting... Press Enter to continue' : 'Waiting... Press Enter to start');
49328
- await waitForPromptStart(nextPrompt.file, nextPrompt.section, !hasWaitedForStart);
49498
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
49499
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
49500
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(hasWaitedForStart ? 'Waiting for confirmation to continue' : 'Waiting for confirmation to start');
49501
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setDetailLines([buildPromptSummary(nextPrompt.file, nextPrompt.section)]);
49502
+ if (isRichUiEnabled) {
49503
+ await (uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.waitForEnter(hasWaitedForStart ? 'Continue' : 'Start'));
49504
+ }
49505
+ else {
49506
+ await waitForPromptStart(nextPrompt.file, nextPrompt.section, !hasWaitedForStart);
49507
+ }
49508
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setDetailLines([]);
49509
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
49329
49510
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
49330
49511
  hasWaitedForStart = true;
49331
49512
  }
@@ -49335,9 +49516,7 @@ bash "$1"
49335
49516
  const commitMessage = buildCommitMessage(nextPrompt.file, nextPrompt.section);
49336
49517
  const codexPrompt = appendCoderContext(buildCodexPrompt(nextPrompt.file, nextPrompt.section), resolvedCoderContext);
49337
49518
  const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
49338
- const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
49339
- const promptRoundArtifacts = createCoderRunPromptRoundArtifacts(options.preserveLogs);
49340
- if (isUiMode) {
49519
+ if (isRichUiEnabled) {
49341
49520
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
49342
49521
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
49343
49522
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Running');
@@ -49347,69 +49526,74 @@ bash "$1"
49347
49526
  }
49348
49527
  const promptExecutionStartedDate = moment__default["default"]();
49349
49528
  let attemptCount = 1;
49350
- let promptRoundOutcome = 'failure';
49351
49529
  const roundChangedFilesSnapshot = options.normalizeLineEndings
49352
49530
  ? await captureChangedFilesSnapshot(process.cwd())
49353
49531
  : undefined;
49354
- try {
49355
- await withPromptRuntimeLog(scriptPath, async (logPath) => {
49356
- try {
49357
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
49358
- const result = await runPromptWithTestFeedback({
49359
- runner,
49360
- prompt: codexPrompt,
49361
- scriptPath,
49362
- projectPath: process.cwd(),
49363
- promptLabel,
49364
- testCommand: options.testCommand,
49365
- logPath,
49366
- promptRoundArtifacts,
49367
- onAttemptStarted: (nextAttemptCount) => {
49368
- attemptCount = nextAttemptCount;
49369
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
49370
- if (nextAttemptCount > 1) {
49371
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Retrying (attempt ${nextAttemptCount})`);
49372
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('verifying');
49373
- }
49374
- },
49375
- });
49376
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
49377
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Committing changes');
49378
- markPromptDone(nextPrompt.file, nextPrompt.section, result.usage, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, result.attemptCount);
49379
- await writePromptFile(nextPrompt.file);
49380
- await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
49381
- if (options.waitForUser) {
49382
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
49383
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Waiting... Press Enter to commit');
49532
+ await withPromptRuntimeLog(scriptPath, async (logPath) => {
49533
+ try {
49534
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
49535
+ const result = await runPromptWithTestFeedback({
49536
+ runner,
49537
+ prompt: codexPrompt,
49538
+ scriptPath,
49539
+ projectPath: process.cwd(),
49540
+ promptLabel,
49541
+ testCommand: options.testCommand,
49542
+ preserveArtifactsOnSuccess: options.preserveLogs,
49543
+ logPath,
49544
+ onAttemptStarted: (nextAttemptCount) => {
49545
+ attemptCount = nextAttemptCount;
49546
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
49547
+ if (nextAttemptCount > 1) {
49548
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Retrying (attempt ${nextAttemptCount})`);
49549
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('verifying');
49550
+ }
49551
+ },
49552
+ });
49553
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
49554
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Committing changes');
49555
+ markPromptDone(nextPrompt.file, nextPrompt.section, result.usage, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, result.attemptCount);
49556
+ await writePromptFile(nextPrompt.file);
49557
+ await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
49558
+ if (options.waitForUser) {
49559
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
49560
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
49561
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
49562
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Review the commit preview and confirm to continue');
49563
+ if (isRichUiEnabled) {
49564
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setDetailLines(formatCommitMessageForDisplay(commitMessage)
49565
+ .split(/\r?\n/)
49566
+ .map((line) => line.trim()));
49567
+ await (uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.waitForEnter('Commit'));
49568
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setDetailLines([]);
49569
+ }
49570
+ else {
49384
49571
  printCommitMessage(commitMessage);
49385
49572
  await waitForEnter(colors__default["default"].bgWhite('Press Enter to commit and continue...'));
49386
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
49387
49573
  }
49388
- await commitChanges(commitMessage, { autoPush: options.autoPush });
49389
- await runPostPromptAutoMigrationIfEnabled(options);
49390
- promptRoundOutcome = 'success';
49574
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
49575
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
49391
49576
  }
49392
- catch (error) {
49393
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
49394
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
49395
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
49396
- markPromptFailed(nextPrompt.file, nextPrompt.section, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, attemptCount);
49397
- await writePromptFile(nextPrompt.file);
49398
- await writePromptErrorLog({
49399
- file: nextPrompt.file,
49400
- section: nextPrompt.section,
49401
- runnerName: runnerMetadata.runnerName,
49402
- modelName: runnerMetadata.modelName,
49403
- error,
49404
- });
49405
- await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
49406
- throw error;
49407
- }
49408
- }, promptRoundArtifacts);
49409
- }
49410
- finally {
49411
- await promptRoundArtifacts.cleanup(promptRoundOutcome);
49412
- }
49577
+ await commitChanges(commitMessage, { autoPush: options.autoPush });
49578
+ await runPostPromptAutoMigrationIfEnabled(options);
49579
+ }
49580
+ catch (error) {
49581
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
49582
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
49583
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
49584
+ markPromptFailed(nextPrompt.file, nextPrompt.section, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, attemptCount);
49585
+ await writePromptFile(nextPrompt.file);
49586
+ await writePromptErrorLog({
49587
+ file: nextPrompt.file,
49588
+ section: nextPrompt.section,
49589
+ runnerName: runnerMetadata.runnerName,
49590
+ modelName: runnerMetadata.modelName,
49591
+ error,
49592
+ });
49593
+ await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
49594
+ throw error;
49595
+ }
49596
+ }, { preserveArtifactsOnSuccess: options.preserveLogs });
49413
49597
  }
49414
49598
  }
49415
49599
  finally {