feat: CLI & MCP progress tracking for parse-prd command (#1048)
* initial cutover * update log to debug * update tracker to pass units * update test to match new base tracker format * add streamTextService mocks * remove unused imports * Ensure the CLI waits for async main() completion * refactor to reduce code duplication * update comment * reuse function * ensure targetTag is defined in streaming mode * avoid throwing inside process.exit spy * check for null * remove reference to generate * fix formatting * fix textStream assignment * ensure no division by 0 * fix jest chalk mocks * refactor for maintainability * Improve bar chart calculation logic for consistent visual representation * use custom streaming error types; fix mocks * Update streamText extraction in parse-prd.js to match actual service response * remove check - doesn't belong here * update mocks * remove streaming test that wasn't really doing anything * add comment * make parsing logic more DRY * fix formatting * Fix textStream extraction to match actual service response * fix mock * Add a cleanup method to ensure proper resource disposal and prevent memory leaks * debounce progress updates to reduce UI flicker during rapid updates * Implement timeout protection for streaming operations (60-second timeout) with automatic fallback to non-streaming mode. * clear timeout properly * Add a maximum buffer size limit (1MB) to prevent unbounded memory growth with very large streaming responses. * fix formatting * remove duplicate mock * better docs * fix formatting * sanitize the dynamic property name * Fix incorrect remaining progress calculation * Use onError callback instead of console.warn * Remove unused chalk import * Add missing custom validator in fallback parsing configuration * add custom validator parameter in fallback parsing * chore: fix package-lock.json * chore: large code refactor * chore: increase timeout from 1 minute to 3 minutes * fix: refactor and fix streaming * Merge remote-tracking branch 'origin/next' into joedanz/parse-prd-progress * fix: cleanup and fix unit tests * chore: fix unit tests * chore: fix format * chore: run format * chore: fix weird CI unit test error * chore: fix format --------- Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
This commit is contained in:
159
src/progress/tracker-ui.js
Normal file
159
src/progress/tracker-ui.js
Normal file
@@ -0,0 +1,159 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Factory for creating progress bar elements
|
||||
*/
|
||||
class ProgressBarFactory {
|
||||
constructor(multibar) {
|
||||
if (!multibar) {
|
||||
throw new Error('Multibar instance is required');
|
||||
}
|
||||
this.multibar = multibar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a progress bar with the given format
|
||||
*/
|
||||
createBar(format, payload = {}) {
|
||||
if (typeof format !== 'string') {
|
||||
throw new Error('Format must be a string');
|
||||
}
|
||||
|
||||
const bar = this.multibar.create(
|
||||
1, // total
|
||||
1, // current
|
||||
{},
|
||||
{
|
||||
format,
|
||||
barsize: 1,
|
||||
hideCursor: true,
|
||||
clearOnComplete: false
|
||||
}
|
||||
);
|
||||
|
||||
bar.update(1, payload);
|
||||
return bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a header with borders
|
||||
*/
|
||||
createHeader(headerFormat, borderFormat) {
|
||||
this.createBar(borderFormat); // Top border
|
||||
this.createBar(headerFormat); // Header
|
||||
this.createBar(borderFormat); // Bottom border
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a data row
|
||||
*/
|
||||
createRow(rowFormat, payload) {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
throw new Error('Payload must be an object');
|
||||
}
|
||||
return this.createBar(rowFormat, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a border element
|
||||
*/
|
||||
createBorder(borderFormat) {
|
||||
return this.createBar(borderFormat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bordered header for progress tables.
|
||||
* @param {Object} multibar - The multibar instance.
|
||||
* @param {string} headerFormat - Format string for the header row.
|
||||
* @param {string} borderFormat - Format string for the top and bottom borders.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function createProgressHeader(multibar, headerFormat, borderFormat) {
|
||||
const factory = new ProgressBarFactory(multibar);
|
||||
factory.createHeader(headerFormat, borderFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted data row for progress tables.
|
||||
* @param {Object} multibar - The multibar instance.
|
||||
* @param {string} rowFormat - Format string for the row.
|
||||
* @param {Object} payload - Data payload for the row format.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function createProgressRow(multibar, rowFormat, payload) {
|
||||
const factory = new ProgressBarFactory(multibar);
|
||||
factory.createRow(rowFormat, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a border row for progress tables.
|
||||
* @param {Object} multibar - The multibar instance.
|
||||
* @param {string} borderFormat - Format string for the border.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function createBorder(multibar, borderFormat) {
|
||||
const factory = new ProgressBarFactory(multibar);
|
||||
factory.createBorder(borderFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for creating progress tables with consistent formatting
|
||||
*/
|
||||
export class ProgressTableBuilder {
|
||||
constructor(multibar) {
|
||||
this.factory = new ProgressBarFactory(multibar);
|
||||
this.borderStyle = '─';
|
||||
this.columnSeparator = '|';
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a formatted table header
|
||||
*/
|
||||
showHeader(columns = null) {
|
||||
// Default columns for task display
|
||||
const defaultColumns = [
|
||||
{ text: 'TASK', width: 6 },
|
||||
{ text: 'PRI', width: 5 },
|
||||
{ text: 'TITLE', width: 64 }
|
||||
];
|
||||
|
||||
const cols = columns || defaultColumns;
|
||||
const headerText = ' ' + cols.map((c) => c.text).join(' | ') + ' ';
|
||||
const borderLine = this.createBorderLine(cols.map((c) => c.width));
|
||||
|
||||
this.factory.createHeader(headerText, borderLine);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a border line based on column widths
|
||||
*/
|
||||
createBorderLine(columnWidths) {
|
||||
return columnWidths
|
||||
.map((width) => this.borderStyle.repeat(width))
|
||||
.join('─┼─');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a task row to the table
|
||||
*/
|
||||
addTaskRow(taskId, priority, title) {
|
||||
const format = ` ${taskId} | ${priority} | {title}`;
|
||||
this.factory.createRow(format, { title });
|
||||
|
||||
// Add separator after each row
|
||||
const borderLine = '------+-----+' + '─'.repeat(64);
|
||||
this.factory.createBorder(borderLine);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a summary row
|
||||
*/
|
||||
addSummaryRow(label, value) {
|
||||
const format = ` ${label}: {value}`;
|
||||
this.factory.createRow(format, { value });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user