specsmd 0.1.59 → 0.1.60

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.
@@ -31,7 +31,8 @@ function clearTerminalOutput(stream = process.stdout) {
31
31
  if (typeof console.clear === 'function') {
32
32
  console.clear();
33
33
  }
34
- stream.write('\u001B[2J\u001B[3J\u001B[H');
34
+ // Avoid wiping scrollback; just clear the current visible frame.
35
+ stream.write('\u001B[H\u001B[J');
35
36
  }
36
37
 
37
38
  function createInkStdout(stream = process.stdout) {
@@ -1129,9 +1129,9 @@ function createDashboardApp(deps) {
1129
1129
  });
1130
1130
 
1131
1131
  // Resize in some terminals can leave stale frame rows behind.
1132
- // Force clear so next render paints from a clean origin.
1132
+ // Keep the clear operation minimal to avoid triggering scrollback churn.
1133
1133
  if (typeof stdout.write === 'function' && stdout.isTTY !== false) {
1134
- stdout.write('\u001B[2J\u001B[3J\u001B[H');
1134
+ stdout.write('\u001B[H\u001B[J');
1135
1135
  }
1136
1136
  };
1137
1137
 
@@ -1205,16 +1205,6 @@ function createDashboardApp(deps) {
1205
1205
  };
1206
1206
  }, [watchEnabled, refreshMs, refresh, rootPath, workspacePath, resolveRootPathForFlow, resolveRootPathsForFlow, activeFlow, ui.view, worktreeWatchSignature, selectedWorktreeId]);
1207
1207
 
1208
- useEffect(() => {
1209
- if (!stdout || typeof stdout.write !== 'function') {
1210
- return;
1211
- }
1212
- if (stdout.isTTY === false) {
1213
- return;
1214
- }
1215
- stdout.write('\u001B[2J\u001B[3J\u001B[H');
1216
- }, [stdout]);
1217
-
1218
1208
  const cols = Number.isFinite(terminalSize.columns) ? terminalSize.columns : (process.stdout.columns || 120);
1219
1209
  const rows = Number.isFinite(terminalSize.rows) ? terminalSize.rows : (process.stdout.rows || 40);
1220
1210
 
@@ -1228,6 +1218,8 @@ function createDashboardApp(deps) {
1228
1218
  const showApprovalBanner = approvalGateLine !== '' && !ui.showHelp && !worktreeOverlayOpen;
1229
1219
  const showLegacyStatusLine = statusLine !== '' && !showCommandLogLine;
1230
1220
  const densePanels = rows <= 28 || cols <= 120;
1221
+ const panelFrameRows = 3;
1222
+ const resolvePanelBodyRows = (rowBudget) => Math.max(1, Math.floor(Math.max(1, rowBudget) - panelFrameRows));
1231
1223
 
1232
1224
  const reservedRows =
1233
1225
  2 +
@@ -1509,7 +1501,7 @@ function createDashboardApp(deps) {
1509
1501
  const leftPanels = allocateSingleColumnPanels(leftCandidates, contentRowsBudget);
1510
1502
  const rightPanel = {
1511
1503
  ...rightPanelBase,
1512
- maxLines: Math.max(4, contentRowsBudget)
1504
+ maxLines: resolvePanelBodyRows(contentRowsBudget)
1513
1505
  };
1514
1506
 
1515
1507
  contentNode = React.createElement(
@@ -1563,7 +1555,7 @@ function createDashboardApp(deps) {
1563
1555
  const mainPanels = allocateSingleColumnPanels(mainCandidates, contentRowsBudget);
1564
1556
  const previewPanel = {
1565
1557
  ...previewPanelBase,
1566
- maxLines: Math.max(4, contentRowsBudget)
1558
+ maxLines: resolvePanelBodyRows(contentRowsBudget)
1567
1559
  };
1568
1560
 
1569
1561
  contentNode = React.createElement(
@@ -1,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const { spawnSync } = require('child_process');
3
3
  const { truncate, normalizePanelLine, clampIndex, fileExists } = require('./helpers');
4
+ const { sanitizeRenderLine } = require('./overlays');
4
5
  const {
5
6
  getEffectiveFlow,
6
7
  getCurrentRun,
@@ -594,8 +595,9 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
594
595
  const marker = row.expandable
595
596
  ? (row.expanded ? (icons.groupExpanded || 'v') : (icons.groupCollapsed || '>'))
596
597
  : '-';
598
+ const safeLabel = sanitizeRenderLine(row.label || '');
597
599
  return {
598
- text: truncate(`${cursor} ${marker} ${row.label}`, width),
600
+ text: truncate(`${cursor} ${marker} ${safeLabel}`, width),
599
601
  color: isSelected ? (isFocusedSection ? 'green' : 'cyan') : undefined,
600
602
  bold: isSelected,
601
603
  selected: isSelected
@@ -604,8 +606,9 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
604
606
 
605
607
  if (row.kind === 'file' || row.kind === 'git-file' || row.kind === 'git-commit') {
606
608
  const scope = row.scope ? `[${formatScope(row.scope)}] ` : '';
609
+ const safeLabel = sanitizeRenderLine(row.label || '');
607
610
  return {
608
- text: truncate(`${cursor} ${icons.runFile} ${scope}${row.label}`, width),
611
+ text: truncate(`${cursor} ${icons.runFile} ${scope}${safeLabel}`, width),
609
612
  color: isSelected ? (isFocusedSection ? 'green' : 'cyan') : 'gray',
610
613
  bold: isSelected,
611
614
  selected: isSelected
@@ -623,7 +626,7 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
623
626
  }
624
627
 
625
628
  return {
626
- text: truncate(`${isSelected ? `${cursor} ` : ' '}${row.label || ''}`, width),
629
+ text: truncate(`${isSelected ? `${cursor} ` : ' '}${sanitizeRenderLine(row.label || '')}`, width),
627
630
  color: isSelected ? (isFocusedSection ? 'green' : 'cyan') : (row.color || 'gray'),
628
631
  bold: isSelected || Boolean(row.bold),
629
632
  selected: isSelected
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {