specsmd 0.1.35 → 0.1.37

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.
@@ -793,13 +793,16 @@ function getPanelTitles(flow, snapshot) {
793
793
  }
794
794
 
795
795
  function getSectionOrderForView(view) {
796
- if (view === 'overview') {
797
- return ['intent-status', 'completed-runs', 'standards', 'project'];
796
+ if (view === 'intents') {
797
+ return ['intent-status'];
798
+ }
799
+ if (view === 'completed') {
800
+ return ['completed-runs'];
798
801
  }
799
802
  if (view === 'health') {
800
- return ['stats', 'warnings', 'error-details'];
803
+ return ['standards', 'stats', 'warnings', 'error-details'];
801
804
  }
802
- return ['current-run', 'run-files', 'pending'];
805
+ return ['current-run', 'run-files'];
803
806
  }
804
807
 
805
808
  function cycleSection(view, currentSectionKey, direction = 1, availableSections = null) {
@@ -1054,24 +1057,288 @@ function getNoCompletedMessage(flow) {
1054
1057
  return 'No completed runs yet';
1055
1058
  }
1056
1059
 
1057
- function toRunFileRows(fileEntries, flow) {
1058
- if (!Array.isArray(fileEntries) || fileEntries.length === 0) {
1060
+ function getNoCurrentMessage(flow) {
1061
+ if (flow === 'aidlc') return 'No active bolt';
1062
+ if (flow === 'simple') return 'No active spec';
1063
+ return 'No active run';
1064
+ }
1065
+
1066
+ function buildCurrentGroups(snapshot, flow) {
1067
+ const effectiveFlow = getEffectiveFlow(flow, snapshot);
1068
+
1069
+ if (effectiveFlow === 'aidlc') {
1070
+ const bolt = getCurrentBolt(snapshot);
1071
+ if (!bolt) {
1072
+ return [];
1073
+ }
1074
+ const stages = Array.isArray(bolt.stages) ? bolt.stages : [];
1075
+ const completedStages = stages.filter((stage) => stage.status === 'completed').length;
1076
+ return [{
1077
+ key: `current:bolt:${bolt.id}`,
1078
+ label: `${bolt.id} [${bolt.type}] ${completedStages}/${stages.length} stages`,
1079
+ files: filterExistingFiles([
1080
+ ...collectAidlcBoltFiles(bolt),
1081
+ ...collectAidlcIntentContextFiles(snapshot, bolt.intent)
1082
+ ])
1083
+ }];
1084
+ }
1085
+
1086
+ if (effectiveFlow === 'simple') {
1087
+ const spec = getCurrentSpec(snapshot);
1088
+ if (!spec) {
1089
+ return [];
1090
+ }
1091
+ return [{
1092
+ key: `current:spec:${spec.name}`,
1093
+ label: `${spec.name} [${spec.state}] ${spec.tasksCompleted}/${spec.tasksTotal} tasks`,
1094
+ files: filterExistingFiles(collectSimpleSpecFiles(spec))
1095
+ }];
1096
+ }
1097
+
1098
+ const run = getCurrentRun(snapshot);
1099
+ if (!run) {
1100
+ return [];
1101
+ }
1102
+ const workItems = Array.isArray(run.workItems) ? run.workItems : [];
1103
+ const completed = workItems.filter((item) => item.status === 'completed').length;
1104
+ return [{
1105
+ key: `current:run:${run.id}`,
1106
+ label: `${run.id} [${run.scope}] ${completed}/${workItems.length} items`,
1107
+ files: filterExistingFiles(collectFireRunFiles(run).map((file) => ({ ...file, scope: 'active' })))
1108
+ }];
1109
+ }
1110
+
1111
+ function buildRunFileGroups(fileEntries) {
1112
+ const order = ['active', 'upcoming', 'completed', 'intent', 'other'];
1113
+ const buckets = new Map(order.map((scope) => [scope, []]));
1114
+
1115
+ for (const fileEntry of Array.isArray(fileEntries) ? fileEntries : []) {
1116
+ const scope = order.includes(fileEntry?.scope) ? fileEntry.scope : 'other';
1117
+ buckets.get(scope).push(fileEntry);
1118
+ }
1119
+
1120
+ const groups = [];
1121
+ for (const scope of order) {
1122
+ const files = buckets.get(scope) || [];
1123
+ if (files.length === 0) {
1124
+ continue;
1125
+ }
1126
+ groups.push({
1127
+ key: `run-files:scope:${scope}`,
1128
+ label: `${formatScope(scope)} files (${files.length})`,
1129
+ files: filterExistingFiles(files)
1130
+ });
1131
+ }
1132
+ return groups;
1133
+ }
1134
+
1135
+ function getFileEntityLabel(fileEntry, fallbackIndex = 0) {
1136
+ const rawLabel = typeof fileEntry?.label === 'string' ? fileEntry.label : '';
1137
+ if (rawLabel.includes('/')) {
1138
+ return rawLabel.split('/')[0] || `item-${fallbackIndex + 1}`;
1139
+ }
1140
+
1141
+ const filePath = typeof fileEntry?.path === 'string' ? fileEntry.path : '';
1142
+ if (filePath !== '') {
1143
+ const parentDir = path.basename(path.dirname(filePath));
1144
+ if (parentDir && parentDir !== '.' && parentDir !== path.sep) {
1145
+ return parentDir;
1146
+ }
1147
+
1148
+ const baseName = path.basename(filePath);
1149
+ if (baseName) {
1150
+ return baseName;
1151
+ }
1152
+ }
1153
+
1154
+ return `item-${fallbackIndex + 1}`;
1155
+ }
1156
+
1157
+ function buildRunFileEntityGroups(snapshot, flow) {
1158
+ const order = ['active', 'upcoming', 'completed', 'intent', 'other'];
1159
+ const rankByScope = new Map(order.map((scope, index) => [scope, index]));
1160
+ const entries = filterExistingFiles(getRunFileEntries(snapshot, flow));
1161
+ const groupsByEntity = new Map();
1162
+
1163
+ for (let index = 0; index < entries.length; index += 1) {
1164
+ const fileEntry = entries[index];
1165
+ const entity = getFileEntityLabel(fileEntry, index);
1166
+ const scope = order.includes(fileEntry?.scope) ? fileEntry.scope : 'other';
1167
+ const scopeRank = rankByScope.get(scope) ?? rankByScope.get('other');
1168
+
1169
+ if (!groupsByEntity.has(entity)) {
1170
+ groupsByEntity.set(entity, {
1171
+ entity,
1172
+ files: [],
1173
+ scope,
1174
+ scopeRank
1175
+ });
1176
+ }
1177
+
1178
+ const group = groupsByEntity.get(entity);
1179
+ group.files.push(fileEntry);
1180
+
1181
+ if (scopeRank < group.scopeRank) {
1182
+ group.scopeRank = scopeRank;
1183
+ group.scope = scope;
1184
+ }
1185
+ }
1186
+
1187
+ return Array.from(groupsByEntity.values())
1188
+ .sort((a, b) => {
1189
+ if (a.scopeRank !== b.scopeRank) {
1190
+ return a.scopeRank - b.scopeRank;
1191
+ }
1192
+ return String(a.entity).localeCompare(String(b.entity));
1193
+ })
1194
+ .map((group) => ({
1195
+ key: `run-files:entity:${group.entity}`,
1196
+ label: `${group.entity} [${formatScope(group.scope)}] (${group.files.length})`,
1197
+ files: filterExistingFiles(group.files)
1198
+ }))
1199
+ .filter((group) => group.files.length > 0);
1200
+ }
1201
+
1202
+ function normalizeInfoLine(line) {
1203
+ const normalized = normalizePanelLine(line);
1204
+ return {
1205
+ label: normalized.text,
1206
+ color: normalized.color,
1207
+ bold: normalized.bold
1208
+ };
1209
+ }
1210
+
1211
+ function toInfoRows(lines, keyPrefix, emptyLabel = 'No data') {
1212
+ const safe = Array.isArray(lines) ? lines : [];
1213
+ if (safe.length === 0) {
1059
1214
  return [{
1060
1215
  kind: 'info',
1061
- key: 'run-files:empty',
1062
- label: getNoFileMessage(flow),
1216
+ key: `${keyPrefix}:empty`,
1217
+ label: emptyLabel,
1063
1218
  selectable: false
1064
1219
  }];
1065
1220
  }
1066
1221
 
1067
- return fileEntries.map((file, index) => ({
1068
- kind: 'file',
1069
- key: `run-files:${file.path}:${index}`,
1070
- label: file.label,
1071
- path: file.path,
1072
- scope: file.scope || 'file',
1073
- selectable: true
1074
- }));
1222
+ return safe.map((line, index) => {
1223
+ const normalized = normalizeInfoLine(line);
1224
+ return {
1225
+ kind: 'info',
1226
+ key: `${keyPrefix}:${index}`,
1227
+ label: normalized.label,
1228
+ color: normalized.color,
1229
+ bold: normalized.bold,
1230
+ selectable: true
1231
+ };
1232
+ });
1233
+ }
1234
+
1235
+ function buildOverviewIntentGroups(snapshot, flow, filter = 'next') {
1236
+ const effectiveFlow = getEffectiveFlow(flow, snapshot);
1237
+ const normalizedFilter = filter === 'completed' ? 'completed' : 'next';
1238
+ const isIncluded = (status) => {
1239
+ if (normalizedFilter === 'completed') {
1240
+ return status === 'completed';
1241
+ }
1242
+ return status !== 'completed';
1243
+ };
1244
+
1245
+ if (effectiveFlow === 'aidlc') {
1246
+ const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
1247
+ return intents
1248
+ .filter((intent) => isIncluded(intent?.status || 'pending'))
1249
+ .map((intent, index) => ({
1250
+ key: `overview:intent:${intent?.id || index}`,
1251
+ label: `${intent?.id || 'unknown'}: ${intent?.status || 'pending'} (${intent?.completedStories || 0}/${intent?.storyCount || 0} stories, ${intent?.completedUnits || 0}/${intent?.unitCount || 0} units)`,
1252
+ files: filterExistingFiles(collectAidlcIntentContextFiles(snapshot, intent?.id))
1253
+ }));
1254
+ }
1255
+
1256
+ if (effectiveFlow === 'simple') {
1257
+ const specs = Array.isArray(snapshot?.specs) ? snapshot.specs : [];
1258
+ return specs
1259
+ .filter((spec) => isIncluded(spec?.state || 'pending'))
1260
+ .map((spec, index) => ({
1261
+ key: `overview:spec:${spec?.name || index}`,
1262
+ label: `${spec?.name || 'unknown'}: ${spec?.state || 'pending'} (${spec?.tasksCompleted || 0}/${spec?.tasksTotal || 0} tasks)`,
1263
+ files: filterExistingFiles(collectSimpleSpecFiles(spec))
1264
+ }));
1265
+ }
1266
+
1267
+ const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
1268
+ return intents
1269
+ .filter((intent) => isIncluded(intent?.status || 'pending'))
1270
+ .map((intent, index) => {
1271
+ const workItems = Array.isArray(intent?.workItems) ? intent.workItems : [];
1272
+ const done = workItems.filter((item) => item.status === 'completed').length;
1273
+ const files = [{
1274
+ label: `${intent?.id || 'intent'}/brief.md`,
1275
+ path: intent?.filePath,
1276
+ scope: 'intent'
1277
+ }, ...workItems.map((item) => ({
1278
+ label: `${intent?.id || 'intent'}/${item?.id || 'work-item'}.md`,
1279
+ path: item?.filePath,
1280
+ scope: item?.status === 'completed' ? 'completed' : 'upcoming'
1281
+ }))];
1282
+ return {
1283
+ key: `overview:intent:${intent?.id || index}`,
1284
+ label: `${intent?.id || 'unknown'}: ${intent?.status || 'pending'} (${done}/${workItems.length} work items)`,
1285
+ files: filterExistingFiles(files)
1286
+ };
1287
+ });
1288
+ }
1289
+
1290
+ function buildStandardsGroups(snapshot, flow) {
1291
+ const effectiveFlow = getEffectiveFlow(flow, snapshot);
1292
+ if (effectiveFlow === 'simple') {
1293
+ return [];
1294
+ }
1295
+
1296
+ const standards = Array.isArray(snapshot?.standards) ? snapshot.standards : [];
1297
+ return standards.map((standard, index) => {
1298
+ const id = standard?.type || standard?.name || String(index);
1299
+ const name = `${standard?.name || standard?.type || 'standard'}.md`;
1300
+ return {
1301
+ key: `standards:${id}`,
1302
+ label: name,
1303
+ files: filterExistingFiles([{
1304
+ label: name,
1305
+ path: standard?.filePath,
1306
+ scope: 'file'
1307
+ }])
1308
+ };
1309
+ });
1310
+ }
1311
+
1312
+ function buildProjectGroups(snapshot, flow) {
1313
+ const effectiveFlow = getEffectiveFlow(flow, snapshot);
1314
+ const files = [];
1315
+
1316
+ if (effectiveFlow === 'aidlc') {
1317
+ files.push({
1318
+ label: 'memory-bank/project.yaml',
1319
+ path: path.join(snapshot?.rootPath || '', 'project.yaml'),
1320
+ scope: 'file'
1321
+ });
1322
+ } else if (effectiveFlow === 'simple') {
1323
+ files.push({
1324
+ label: 'package.json',
1325
+ path: path.join(snapshot?.workspacePath || '', 'package.json'),
1326
+ scope: 'file'
1327
+ });
1328
+ } else {
1329
+ files.push({
1330
+ label: '.specs-fire/state.yaml',
1331
+ path: path.join(snapshot?.rootPath || '', 'state.yaml'),
1332
+ scope: 'file'
1333
+ });
1334
+ }
1335
+
1336
+ const projectName = snapshot?.project?.name || 'unknown-project';
1337
+ return [{
1338
+ key: `project:${projectName}`,
1339
+ label: `project ${projectName}`,
1340
+ files: filterExistingFiles(files)
1341
+ }];
1075
1342
  }
1076
1343
 
1077
1344
  function collectAidlcIntentContextFiles(snapshot, intentId) {
@@ -1303,10 +1570,10 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
1303
1570
  }
1304
1571
 
1305
1572
  return {
1306
- text: truncate(` ${row.label || ''}`, width),
1307
- color: 'gray',
1308
- bold: false,
1309
- selected: false
1573
+ text: truncate(`${isSelected ? `${cursor} ` : ' '}${row.label || ''}`, width),
1574
+ color: isSelected ? (isFocusedSection ? 'green' : 'cyan') : (row.color || 'gray'),
1575
+ bold: isSelected || Boolean(row.bold),
1576
+ selected: isSelected
1310
1577
  };
1311
1578
  });
1312
1579
  }
@@ -1399,19 +1666,27 @@ function openFileWithDefaultApp(filePath) {
1399
1666
 
1400
1667
  function buildQuickHelpText(view, options = {}) {
1401
1668
  const {
1669
+ flow = 'fire',
1402
1670
  previewOpen = false,
1403
1671
  availableFlowCount = 1
1404
1672
  } = options;
1673
+ const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
1674
+ const isSimple = String(flow || '').toLowerCase() === 'simple';
1675
+ const activeLabel = isAidlc ? 'active bolt' : (isSimple ? 'active spec' : 'active run');
1405
1676
 
1406
- const parts = ['1/2/3 views', 'g/G sections'];
1677
+ const parts = ['1/2/3/4 tabs', 'g/G sections'];
1407
1678
 
1408
- if (view === 'runs') {
1679
+ if (view === 'runs' || view === 'intents' || view === 'completed' || view === 'health') {
1409
1680
  if (previewOpen) {
1410
- parts.push('tab pane', '↑/↓ nav/scroll', 'v close');
1681
+ parts.push('tab pane', '↑/↓ nav/scroll', 'v/space close');
1411
1682
  } else {
1412
- parts.push('↑/↓ navigate', 'enter expand', 'v preview');
1683
+ parts.push('↑/↓ navigate', 'enter expand', 'v/space preview');
1413
1684
  }
1414
1685
  }
1686
+ if (view === 'runs') {
1687
+ parts.push('a current', 'f files');
1688
+ }
1689
+ parts.push(`tab1 ${activeLabel}`);
1415
1690
 
1416
1691
  if (availableFlowCount > 1) {
1417
1692
  parts.push('[/] flow');
@@ -1424,26 +1699,32 @@ function buildQuickHelpText(view, options = {}) {
1424
1699
  function buildHelpOverlayLines(options = {}) {
1425
1700
  const {
1426
1701
  view = 'runs',
1702
+ flow = 'fire',
1427
1703
  previewOpen = false,
1428
1704
  paneFocus = 'main',
1429
1705
  availableFlowCount = 1,
1430
1706
  showErrorSection = false
1431
1707
  } = options;
1708
+ const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
1709
+ const isSimple = String(flow || '').toLowerCase() === 'simple';
1710
+ const itemLabel = isAidlc ? 'bolt' : (isSimple ? 'spec' : 'run');
1711
+ const itemPlural = isAidlc ? 'bolts' : (isSimple ? 'specs' : 'runs');
1432
1712
 
1433
1713
  const lines = [
1434
1714
  { text: 'Global', color: 'cyan', bold: true },
1435
1715
  'q or Ctrl+C quit',
1436
1716
  'r refresh snapshot',
1437
- '1 runs | 2 overview | 3 health',
1717
+ `1 active ${itemLabel} | 2 intents | 3 completed ${itemPlural} | 4 standards/health`,
1438
1718
  'g next section | G previous section',
1439
1719
  'h/? toggle this shortcuts overlay',
1440
1720
  'esc close overlays (help/preview/fullscreen)',
1441
1721
  { text: '', color: undefined, bold: false },
1442
- { text: 'Runs View', color: 'yellow', bold: true },
1443
- 'a current | f files | p pending',
1722
+ { text: 'Tab 1 Active', color: 'yellow', bold: true },
1723
+ `a focus active ${itemLabel}`,
1724
+ `f focus ${itemLabel} files`,
1444
1725
  'up/down or j/k move selection',
1445
- 'enter expand/collapse pending/completed groups',
1446
- 'v preview selected file',
1726
+ 'enter expand/collapse selected folder row',
1727
+ 'v or space preview selected file',
1447
1728
  'v twice quickly opens fullscreen preview overlay',
1448
1729
  'tab switch focus between main and preview panes',
1449
1730
  'o open selected file in system default app'
@@ -1459,12 +1740,16 @@ function buildHelpOverlayLines(options = {}) {
1459
1740
 
1460
1741
  lines.push(
1461
1742
  { text: '', color: undefined, bold: false },
1462
- { text: 'Overview View', color: 'green', bold: true },
1463
- 'i intents | c completed | s standards | p project',
1464
- 'when Intents is focused: n next | x completed | left/right toggle filter',
1743
+ { text: 'Tab 2 Intents', color: 'green', bold: true },
1744
+ 'i focus intents',
1745
+ 'n next intents | x completed intents',
1746
+ 'left/right toggles next/completed when intents is focused',
1747
+ { text: '', color: undefined, bold: false },
1748
+ { text: 'Tab 3 Completed', color: 'blue', bold: true },
1749
+ 'c focus completed items',
1465
1750
  { text: '', color: undefined, bold: false },
1466
- { text: 'Health View', color: 'magenta', bold: true },
1467
- `t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
1751
+ { text: 'Tab 4 Standards/Health', color: 'magenta', bold: true },
1752
+ `s standards | t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
1468
1753
  { text: '', color: undefined, bold: false },
1469
1754
  { text: `Current view: ${String(view || 'runs').toUpperCase()}`, color: 'gray', bold: false }
1470
1755
  );
@@ -1685,11 +1970,15 @@ function createDashboardApp(deps) {
1685
1970
  }
1686
1971
 
1687
1972
  function TabsBar(props) {
1688
- const { view, width, icons } = props;
1973
+ const { view, width, icons, flow: activeFlow } = props;
1974
+ const effectiveFlow = String(activeFlow || '').toLowerCase();
1975
+ const primaryLabel = effectiveFlow === 'aidlc' ? 'BOLTS' : (effectiveFlow === 'simple' ? 'SPECS' : 'RUNS');
1976
+ const completedLabel = effectiveFlow === 'aidlc' ? 'COMPLETED BOLTS' : (effectiveFlow === 'simple' ? 'COMPLETED SPECS' : 'COMPLETED RUNS');
1689
1977
  const tabs = [
1690
- { id: 'runs', label: ` 1 ${icons.runs} RUNS ` },
1691
- { id: 'overview', label: ` 2 ${icons.overview} OVERVIEW ` },
1692
- { id: 'health', label: ` 3 ${icons.health} HEALTH ` }
1978
+ { id: 'runs', label: ` 1 ${icons.runs} ${primaryLabel} ` },
1979
+ { id: 'intents', label: ` 2 ${icons.overview} INTENTS ` },
1980
+ { id: 'completed', label: ` 3 ${icons.runs} ${completedLabel} ` },
1981
+ { id: 'health', label: ` 4 ${icons.health} STANDARDS/HEALTH ` }
1693
1982
  ];
1694
1983
 
1695
1984
  return React.createElement(
@@ -1757,18 +2046,25 @@ function createDashboardApp(deps) {
1757
2046
  const [error, setError] = useState(initialNormalizedError);
1758
2047
  const [ui, setUi] = useState(createInitialUIState());
1759
2048
  const [sectionFocus, setSectionFocus] = useState({
1760
- runs: 'run-files',
1761
- overview: 'project',
1762
- health: 'stats'
2049
+ runs: 'current-run',
2050
+ intents: 'intent-status',
2051
+ completed: 'completed-runs',
2052
+ health: 'standards'
1763
2053
  });
1764
2054
  const [selectionBySection, setSelectionBySection] = useState({
2055
+ 'current-run': 0,
1765
2056
  'run-files': 0,
1766
- pending: 0,
1767
- completed: 0
2057
+ 'intent-status': 0,
2058
+ 'completed-runs': 0,
2059
+ standards: 0,
2060
+ stats: 0,
2061
+ warnings: 0,
2062
+ 'error-details': 0
1768
2063
  });
1769
2064
  const [expandedGroups, setExpandedGroups] = useState({});
1770
2065
  const [previewTarget, setPreviewTarget] = useState(null);
1771
2066
  const [overviewIntentFilter, setOverviewIntentFilter] = useState('next');
2067
+ const [deferredTabsReady, setDeferredTabsReady] = useState(false);
1772
2068
  const [previewOpen, setPreviewOpen] = useState(false);
1773
2069
  const [paneFocus, setPaneFocus] = useState('main');
1774
2070
  const [overlayPreviewOpen, setOverlayPreviewOpen] = useState(false);
@@ -1804,24 +2100,99 @@ function createDashboardApp(deps) {
1804
2100
  return base.filter((sectionKey) => sectionKey !== 'error-details' || showErrorPanelForSections);
1805
2101
  }, [showErrorPanelForSections]);
1806
2102
 
1807
- const runFileEntries = getRunFileEntries(snapshot, activeFlow);
1808
- const runFileRows = toRunFileRows(runFileEntries, activeFlow);
1809
- const pendingRows = toExpandableRows(
1810
- buildPendingGroups(snapshot, activeFlow),
1811
- getNoPendingMessage(getEffectiveFlow(activeFlow, snapshot)),
1812
- expandedGroups
2103
+ const effectiveFlow = getEffectiveFlow(activeFlow, snapshot);
2104
+ const currentGroups = buildCurrentGroups(snapshot, activeFlow);
2105
+ const currentExpandedGroups = { ...expandedGroups };
2106
+ for (const group of currentGroups) {
2107
+ if (currentExpandedGroups[group.key] == null) {
2108
+ currentExpandedGroups[group.key] = true;
2109
+ }
2110
+ }
2111
+
2112
+ const currentRunRows = toExpandableRows(
2113
+ currentGroups,
2114
+ getNoCurrentMessage(effectiveFlow),
2115
+ currentExpandedGroups
1813
2116
  );
1814
- const completedRows = toExpandableRows(
1815
- buildCompletedGroups(snapshot, activeFlow),
1816
- getNoCompletedMessage(getEffectiveFlow(activeFlow, snapshot)),
1817
- expandedGroups
2117
+ const shouldHydrateSecondaryTabs = deferredTabsReady || ui.view !== 'runs';
2118
+ const runFileGroups = buildRunFileEntityGroups(snapshot, activeFlow);
2119
+ const runFileExpandedGroups = { ...expandedGroups };
2120
+ for (const group of runFileGroups) {
2121
+ if (runFileExpandedGroups[group.key] == null) {
2122
+ runFileExpandedGroups[group.key] = true;
2123
+ }
2124
+ }
2125
+ const runFileRows = toExpandableRows(
2126
+ runFileGroups,
2127
+ getNoFileMessage(effectiveFlow),
2128
+ runFileExpandedGroups
1818
2129
  );
2130
+ const intentRows = shouldHydrateSecondaryTabs
2131
+ ? [
2132
+ {
2133
+ kind: 'info',
2134
+ key: 'intent-filter',
2135
+ label: `filter ${overviewIntentFilter === 'completed' ? 'next | [COMPLETED]' : '[NEXT] | completed'} (n/x)`,
2136
+ color: 'cyan',
2137
+ bold: true,
2138
+ selectable: false
2139
+ },
2140
+ ...toExpandableRows(
2141
+ buildOverviewIntentGroups(snapshot, activeFlow, overviewIntentFilter),
2142
+ overviewIntentFilter === 'completed' ? 'No completed intents yet' : 'No upcoming intents',
2143
+ expandedGroups
2144
+ )
2145
+ ]
2146
+ : toInfoRows(['Loading intents...'], 'intent-loading');
2147
+ const completedRows = shouldHydrateSecondaryTabs
2148
+ ? toExpandableRows(
2149
+ buildCompletedGroups(snapshot, activeFlow),
2150
+ getNoCompletedMessage(effectiveFlow),
2151
+ expandedGroups
2152
+ )
2153
+ : toInfoRows(['Loading completed items...'], 'completed-loading');
2154
+ const standardsRows = shouldHydrateSecondaryTabs
2155
+ ? toExpandableRows(
2156
+ buildStandardsGroups(snapshot, activeFlow),
2157
+ effectiveFlow === 'simple' ? 'No standards for SIMPLE flow' : 'No standards found',
2158
+ expandedGroups
2159
+ )
2160
+ : toInfoRows(['Loading standards...'], 'standards-loading');
2161
+ const statsRows = shouldHydrateSecondaryTabs
2162
+ ? toInfoRows(
2163
+ buildStatsLines(snapshot, 200, activeFlow),
2164
+ 'stats',
2165
+ 'No stats available'
2166
+ )
2167
+ : toInfoRows(['Loading stats...'], 'stats-loading');
2168
+ const warningsRows = shouldHydrateSecondaryTabs
2169
+ ? toInfoRows(
2170
+ buildWarningsLines(snapshot, 200),
2171
+ 'warnings',
2172
+ 'No warnings'
2173
+ )
2174
+ : toInfoRows(['Loading warnings...'], 'warnings-loading');
2175
+ const errorDetailsRows = shouldHydrateSecondaryTabs
2176
+ ? toInfoRows(
2177
+ buildErrorLines(error, 200),
2178
+ 'error-details',
2179
+ 'No error details'
2180
+ )
2181
+ : toInfoRows(['Loading error details...'], 'error-loading');
1819
2182
 
1820
2183
  const rowsBySection = {
2184
+ 'current-run': currentRunRows,
1821
2185
  'run-files': runFileRows,
1822
- pending: pendingRows,
1823
- completed: completedRows
2186
+ 'intent-status': intentRows,
2187
+ 'completed-runs': completedRows,
2188
+ standards: standardsRows,
2189
+ stats: statsRows,
2190
+ warnings: warningsRows,
2191
+ 'error-details': errorDetailsRows
1824
2192
  };
2193
+ const rowLengthSignature = Object.entries(rowsBySection)
2194
+ .map(([key, rowsForSection]) => `${key}:${Array.isArray(rowsForSection) ? rowsForSection.length : 0}`)
2195
+ .join('|');
1825
2196
 
1826
2197
  const currentSectionOrder = getAvailableSections(ui.view);
1827
2198
  const focusedSection = currentSectionOrder.includes(sectionFocus[ui.view])
@@ -1914,12 +2285,18 @@ function createDashboardApp(deps) {
1914
2285
  }
1915
2286
 
1916
2287
  if (input === '2') {
1917
- setUi((previous) => ({ ...previous, view: 'overview' }));
2288
+ setUi((previous) => ({ ...previous, view: 'intents' }));
1918
2289
  setPaneFocus('main');
1919
2290
  return;
1920
2291
  }
1921
2292
 
1922
2293
  if (input === '3') {
2294
+ setUi((previous) => ({ ...previous, view: 'completed' }));
2295
+ setPaneFocus('main');
2296
+ return;
2297
+ }
2298
+
2299
+ if (input === '4') {
1923
2300
  setUi((previous) => ({ ...previous, view: 'health' }));
1924
2301
  setPaneFocus('main');
1925
2302
  return;
@@ -1938,14 +2315,20 @@ function createDashboardApp(deps) {
1938
2315
  return availableFlowIds[nextIndex];
1939
2316
  });
1940
2317
  setSelectionBySection({
2318
+ 'current-run': 0,
1941
2319
  'run-files': 0,
1942
- pending: 0,
1943
- completed: 0
2320
+ 'intent-status': 0,
2321
+ 'completed-runs': 0,
2322
+ standards: 0,
2323
+ stats: 0,
2324
+ warnings: 0,
2325
+ 'error-details': 0
1944
2326
  });
1945
2327
  setSectionFocus({
1946
- runs: 'run-files',
1947
- overview: 'project',
1948
- health: 'stats'
2328
+ runs: 'current-run',
2329
+ intents: 'intent-status',
2330
+ completed: 'completed-runs',
2331
+ health: 'standards'
1949
2332
  });
1950
2333
  setOverviewIntentFilter('next');
1951
2334
  setExpandedGroups({});
@@ -1970,14 +2353,20 @@ function createDashboardApp(deps) {
1970
2353
  return availableFlowIds[nextIndex];
1971
2354
  });
1972
2355
  setSelectionBySection({
2356
+ 'current-run': 0,
1973
2357
  'run-files': 0,
1974
- pending: 0,
1975
- completed: 0
2358
+ 'intent-status': 0,
2359
+ 'completed-runs': 0,
2360
+ standards: 0,
2361
+ stats: 0,
2362
+ warnings: 0,
2363
+ 'error-details': 0
1976
2364
  });
1977
2365
  setSectionFocus({
1978
- runs: 'run-files',
1979
- overview: 'project',
1980
- health: 'stats'
2366
+ runs: 'current-run',
2367
+ intents: 'intent-status',
2368
+ completed: 'completed-runs',
2369
+ health: 'standards'
1981
2370
  });
1982
2371
  setOverviewIntentFilter('next');
1983
2372
  setExpandedGroups({});
@@ -1994,12 +2383,12 @@ function createDashboardApp(deps) {
1994
2383
  ? sectionFocus[ui.view]
1995
2384
  : (availableSections[0] || 'current-run');
1996
2385
 
1997
- if (key.tab && ui.view === 'runs' && previewOpen) {
2386
+ if (key.tab && previewOpen) {
1998
2387
  setPaneFocus((previous) => (previous === 'main' ? 'preview' : 'main'));
1999
2388
  return;
2000
2389
  }
2001
2390
 
2002
- if (ui.view === 'overview' && activeSection === 'intent-status') {
2391
+ if (ui.view === 'intents' && activeSection === 'intent-status') {
2003
2392
  if (input === 'n') {
2004
2393
  setOverviewIntentFilter('next');
2005
2394
  return;
@@ -2043,29 +2432,21 @@ function createDashboardApp(deps) {
2043
2432
  setPaneFocus('main');
2044
2433
  return;
2045
2434
  }
2046
- if (input === 'p') {
2047
- setSectionFocus((previous) => ({ ...previous, runs: 'pending' }));
2048
- setPaneFocus('main');
2049
- return;
2050
- }
2051
- } else if (ui.view === 'overview') {
2052
- if (input === 'p') {
2053
- setSectionFocus((previous) => ({ ...previous, overview: 'project' }));
2054
- return;
2055
- }
2435
+ } else if (ui.view === 'intents') {
2056
2436
  if (input === 'i') {
2057
- setSectionFocus((previous) => ({ ...previous, overview: 'intent-status' }));
2058
- return;
2059
- }
2060
- if (input === 's') {
2061
- setSectionFocus((previous) => ({ ...previous, overview: 'standards' }));
2437
+ setSectionFocus((previous) => ({ ...previous, intents: 'intent-status' }));
2062
2438
  return;
2063
2439
  }
2440
+ } else if (ui.view === 'completed') {
2064
2441
  if (input === 'c') {
2065
- setSectionFocus((previous) => ({ ...previous, overview: 'completed-runs' }));
2442
+ setSectionFocus((previous) => ({ ...previous, completed: 'completed-runs' }));
2066
2443
  return;
2067
2444
  }
2068
2445
  } else if (ui.view === 'health') {
2446
+ if (input === 's') {
2447
+ setSectionFocus((previous) => ({ ...previous, health: 'standards' }));
2448
+ return;
2449
+ }
2069
2450
  if (input === 't') {
2070
2451
  setSectionFocus((previous) => ({ ...previous, health: 'stats' }));
2071
2452
  return;
@@ -2094,7 +2475,7 @@ function createDashboardApp(deps) {
2094
2475
  }
2095
2476
  }
2096
2477
 
2097
- if (ui.view === 'runs' && (key.upArrow || key.downArrow || input === 'j' || input === 'k')) {
2478
+ if (key.upArrow || key.downArrow || input === 'j' || input === 'k') {
2098
2479
  const moveDown = key.downArrow || input === 'j';
2099
2480
  const moveUp = key.upArrow || input === 'k';
2100
2481
 
@@ -2107,11 +2488,7 @@ function createDashboardApp(deps) {
2107
2488
  return;
2108
2489
  }
2109
2490
 
2110
- const targetSection = activeSection === 'current-run' ? 'run-files' : activeSection;
2111
- if (targetSection !== activeSection) {
2112
- setSectionFocus((previous) => ({ ...previous, runs: targetSection }));
2113
- }
2114
-
2491
+ const targetSection = activeSection;
2115
2492
  const targetRows = rowsBySection[targetSection] || [];
2116
2493
  if (targetRows.length === 0) {
2117
2494
  return;
@@ -2129,24 +2506,22 @@ function createDashboardApp(deps) {
2129
2506
  return;
2130
2507
  }
2131
2508
 
2132
- if (ui.view === 'runs' && (key.return || key.enter)) {
2133
- if (activeSection === 'pending' || activeSection === 'completed') {
2134
- const rowsForSection = rowsBySection[activeSection] || [];
2135
- const selectedRow = getSelectedRow(rowsForSection, selectionBySection[activeSection] || 0);
2136
- if (selectedRow?.kind === 'group' && selectedRow.expandable) {
2137
- setExpandedGroups((previous) => ({
2138
- ...previous,
2139
- [selectedRow.key]: !previous[selectedRow.key]
2140
- }));
2141
- }
2509
+ if (key.return || key.enter) {
2510
+ const rowsForSection = rowsBySection[activeSection] || [];
2511
+ const selectedRow = getSelectedRow(rowsForSection, selectionBySection[activeSection] || 0);
2512
+ if (selectedRow?.kind === 'group' && selectedRow.expandable) {
2513
+ setExpandedGroups((previous) => ({
2514
+ ...previous,
2515
+ [selectedRow.key]: !previous[selectedRow.key]
2516
+ }));
2142
2517
  }
2143
2518
  return;
2144
2519
  }
2145
2520
 
2146
- if (input === 'v' && ui.view === 'runs') {
2521
+ if (input === 'v' || input === ' ' || key.space) {
2147
2522
  const target = selectedFocusedFile || previewTarget;
2148
2523
  if (!target) {
2149
- setStatusLine('Select a file row first (run files, pending, or completed).');
2524
+ setStatusLine('Select a file row first.');
2150
2525
  return;
2151
2526
  }
2152
2527
 
@@ -2184,7 +2559,7 @@ function createDashboardApp(deps) {
2184
2559
  return;
2185
2560
  }
2186
2561
 
2187
- if (input === 'o' && ui.view === 'runs') {
2562
+ if (input === 'o') {
2188
2563
  const target = selectedFocusedFile || previewTarget;
2189
2564
  const result = openFileWithDefaultApp(target?.path);
2190
2565
  setStatusLine(result.message);
@@ -2196,21 +2571,38 @@ function createDashboardApp(deps) {
2196
2571
  }, [refresh]);
2197
2572
 
2198
2573
  useEffect(() => {
2199
- setSelectionBySection((previous) => ({
2200
- ...previous,
2201
- 'run-files': clampIndex(previous['run-files'] || 0, runFileRows.length),
2202
- pending: clampIndex(previous.pending || 0, pendingRows.length),
2203
- completed: clampIndex(previous.completed || 0, completedRows.length)
2204
- }));
2205
- }, [activeFlow, runFileRows.length, pendingRows.length, completedRows.length, snapshot?.generatedAt]);
2574
+ setSelectionBySection((previous) => {
2575
+ let changed = false;
2576
+ const next = { ...previous };
2577
+
2578
+ for (const [sectionKey, sectionRows] of Object.entries(rowsBySection)) {
2579
+ const previousValue = Number.isFinite(previous[sectionKey]) ? previous[sectionKey] : 0;
2580
+ const clampedValue = clampIndex(previousValue, sectionRows.length);
2581
+ if (previousValue !== clampedValue) {
2582
+ next[sectionKey] = clampedValue;
2583
+ changed = true;
2584
+ } else if (!(sectionKey in next)) {
2585
+ next[sectionKey] = clampedValue;
2586
+ changed = true;
2587
+ }
2588
+ }
2589
+
2590
+ return changed ? next : previous;
2591
+ });
2592
+ }, [activeFlow, rowLengthSignature, snapshot?.generatedAt]);
2206
2593
 
2207
2594
  useEffect(() => {
2208
- if (ui.view !== 'runs') {
2209
- setPreviewOpen(false);
2210
- setOverlayPreviewOpen(false);
2211
- setPreviewScroll(0);
2212
- setPaneFocus('main');
2213
- }
2595
+ setDeferredTabsReady(false);
2596
+ const timer = setTimeout(() => {
2597
+ setDeferredTabsReady(true);
2598
+ }, 0);
2599
+ return () => {
2600
+ clearTimeout(timer);
2601
+ };
2602
+ }, [activeFlow, snapshot?.generatedAt]);
2603
+
2604
+ useEffect(() => {
2605
+ setPaneFocus('main');
2214
2606
  }, [ui.view]);
2215
2607
 
2216
2608
  useEffect(() => {
@@ -2324,16 +2716,25 @@ function createDashboardApp(deps) {
2324
2716
  const rows = Number.isFinite(terminalSize.rows) ? terminalSize.rows : (process.stdout.rows || 40);
2325
2717
 
2326
2718
  const fullWidth = Math.max(40, cols - 1);
2719
+ const showFlowBar = availableFlowIds.length > 1;
2327
2720
  const showFooterHelpLine = rows >= 10;
2328
2721
  const showErrorPanel = Boolean(error) && rows >= 18;
2722
+ const showGlobalErrorPanel = showErrorPanel && ui.view !== 'health' && !ui.showHelp;
2329
2723
  const showErrorInline = Boolean(error) && !showErrorPanel;
2724
+ const showStatusLine = statusLine !== '';
2330
2725
  const densePanels = rows <= 28 || cols <= 120;
2331
2726
 
2332
- const reservedRows = 2 + (showFooterHelpLine ? 1 : 0) + (showErrorPanel ? 5 : 0) + (showErrorInline ? 1 : 0);
2727
+ const reservedRows =
2728
+ 2 +
2729
+ (showFlowBar ? 1 : 0) +
2730
+ (showFooterHelpLine ? 1 : 0) +
2731
+ (showGlobalErrorPanel ? 5 : 0) +
2732
+ (showErrorInline ? 1 : 0) +
2733
+ (showStatusLine ? 1 : 0);
2333
2734
  const contentRowsBudget = Math.max(4, rows - reservedRows);
2334
2735
  const ultraCompact = rows <= 14;
2335
2736
  const panelTitles = getPanelTitles(activeFlow, snapshot);
2336
- const splitPreviewLayout = ui.view === 'runs' && previewOpen && !overlayPreviewOpen && !ui.showHelp && cols >= 110 && rows >= 16;
2737
+ const splitPreviewLayout = previewOpen && !overlayPreviewOpen && !ui.showHelp && cols >= 110 && rows >= 16;
2337
2738
  const mainPaneWidth = splitPreviewLayout
2338
2739
  ? Math.max(34, Math.floor((fullWidth - 1) * 0.52))
2339
2740
  : fullWidth;
@@ -2343,19 +2744,17 @@ function createDashboardApp(deps) {
2343
2744
  const mainCompactWidth = Math.max(18, mainPaneWidth - 4);
2344
2745
  const previewCompactWidth = Math.max(18, previewPaneWidth - 4);
2345
2746
 
2346
- const runFileLines = buildInteractiveRowsLines(
2347
- runFileRows,
2348
- selectionBySection['run-files'] || 0,
2349
- icons,
2350
- mainCompactWidth,
2351
- ui.view === 'runs' && focusedSection === 'run-files' && paneFocus === 'main'
2352
- );
2353
- const pendingLines = buildInteractiveRowsLines(
2354
- pendingRows,
2355
- selectionBySection.pending || 0,
2356
- icons,
2357
- mainCompactWidth,
2358
- ui.view === 'runs' && focusedSection === 'pending' && paneFocus === 'main'
2747
+ const sectionLines = Object.fromEntries(
2748
+ Object.entries(rowsBySection).map(([sectionKey, sectionRows]) => [
2749
+ sectionKey,
2750
+ buildInteractiveRowsLines(
2751
+ sectionRows,
2752
+ selectionBySection[sectionKey] || 0,
2753
+ icons,
2754
+ mainCompactWidth,
2755
+ paneFocus === 'main' && focusedSection === sectionKey
2756
+ )
2757
+ ])
2359
2758
  );
2360
2759
  const effectivePreviewTarget = previewTarget || selectedFocusedFile;
2361
2760
  const previewLines = previewOpen
@@ -2366,12 +2765,14 @@ function createDashboardApp(deps) {
2366
2765
 
2367
2766
  const shortcutsOverlayLines = buildHelpOverlayLines({
2368
2767
  view: ui.view,
2768
+ flow: activeFlow,
2369
2769
  previewOpen,
2370
2770
  paneFocus,
2371
2771
  availableFlowCount: availableFlowIds.length,
2372
2772
  showErrorSection: showErrorPanel
2373
2773
  });
2374
2774
  const quickHelpText = buildQuickHelpText(ui.view, {
2775
+ flow: activeFlow,
2375
2776
  previewOpen,
2376
2777
  paneFocus,
2377
2778
  availableFlowCount: availableFlowIds.length
@@ -2387,7 +2788,7 @@ function createDashboardApp(deps) {
2387
2788
  borderColor: 'cyan'
2388
2789
  }
2389
2790
  ];
2390
- } else if (ui.view === 'runs' && previewOpen && overlayPreviewOpen) {
2791
+ } else if (previewOpen && overlayPreviewOpen) {
2391
2792
  panelCandidates = [
2392
2793
  {
2393
2794
  key: 'preview-overlay',
@@ -2396,45 +2797,42 @@ function createDashboardApp(deps) {
2396
2797
  borderColor: 'magenta'
2397
2798
  }
2398
2799
  ];
2399
- } else if (ui.view === 'overview') {
2800
+ } else if (ui.view === 'intents') {
2400
2801
  panelCandidates = [
2401
2802
  {
2402
2803
  key: 'intent-status',
2403
2804
  title: 'Intents',
2404
- lines: buildOverviewIntentLines(snapshot, mainCompactWidth, activeFlow, overviewIntentFilter),
2805
+ lines: sectionLines['intent-status'],
2405
2806
  borderColor: 'yellow'
2406
- },
2807
+ }
2808
+ ];
2809
+ } else if (ui.view === 'completed') {
2810
+ panelCandidates = [
2407
2811
  {
2408
2812
  key: 'completed-runs',
2409
2813
  title: panelTitles.completed,
2410
- lines: buildCompletedLines(snapshot, mainCompactWidth, activeFlow),
2814
+ lines: sectionLines['completed-runs'],
2411
2815
  borderColor: 'blue'
2412
- },
2816
+ }
2817
+ ];
2818
+ } else if (ui.view === 'health') {
2819
+ panelCandidates = [
2413
2820
  {
2414
2821
  key: 'standards',
2415
2822
  title: 'Standards',
2416
- lines: buildOverviewStandardsLines(snapshot, mainCompactWidth, activeFlow),
2823
+ lines: sectionLines.standards,
2417
2824
  borderColor: 'blue'
2418
2825
  },
2419
- {
2420
- key: 'project',
2421
- title: 'Project + Workspace',
2422
- lines: buildOverviewProjectLines(snapshot, mainCompactWidth, activeFlow),
2423
- borderColor: 'green'
2424
- }
2425
- ];
2426
- } else if (ui.view === 'health') {
2427
- panelCandidates = [
2428
2826
  {
2429
2827
  key: 'stats',
2430
2828
  title: 'Stats',
2431
- lines: buildStatsLines(snapshot, mainCompactWidth, activeFlow),
2829
+ lines: sectionLines.stats,
2432
2830
  borderColor: 'magenta'
2433
2831
  },
2434
2832
  {
2435
2833
  key: 'warnings',
2436
2834
  title: 'Warnings',
2437
- lines: buildWarningsLines(snapshot, mainCompactWidth),
2835
+ lines: sectionLines.warnings,
2438
2836
  borderColor: 'red'
2439
2837
  }
2440
2838
  ];
@@ -2443,47 +2841,42 @@ function createDashboardApp(deps) {
2443
2841
  panelCandidates.push({
2444
2842
  key: 'error-details',
2445
2843
  title: 'Error Details',
2446
- lines: buildErrorLines(error, mainCompactWidth),
2844
+ lines: sectionLines['error-details'],
2447
2845
  borderColor: 'red'
2448
2846
  });
2449
2847
  }
2450
2848
  } else {
2451
- const includeInlinePreviewPanel = previewOpen && !splitPreviewLayout;
2452
2849
  panelCandidates = [
2453
2850
  {
2454
2851
  key: 'current-run',
2455
2852
  title: panelTitles.current,
2456
- lines: buildCurrentRunLines(snapshot, mainCompactWidth, activeFlow),
2853
+ lines: sectionLines['current-run'],
2457
2854
  borderColor: 'green'
2458
2855
  },
2459
2856
  {
2460
2857
  key: 'run-files',
2461
2858
  title: panelTitles.files,
2462
- lines: runFileLines,
2463
- borderColor: 'yellow'
2464
- },
2465
- includeInlinePreviewPanel
2466
- ? {
2467
- key: 'preview',
2468
- title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
2469
- lines: previewLines,
2470
- borderColor: 'magenta'
2471
- }
2472
- : null,
2473
- {
2474
- key: 'pending',
2475
- title: panelTitles.pending,
2476
- lines: pendingLines,
2859
+ lines: sectionLines['run-files'],
2477
2860
  borderColor: 'yellow'
2478
2861
  }
2479
2862
  ];
2480
2863
  }
2481
2864
 
2865
+ if (!ui.showHelp && previewOpen && !overlayPreviewOpen && !splitPreviewLayout) {
2866
+ panelCandidates.push({
2867
+ key: 'preview',
2868
+ title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
2869
+ lines: previewLines,
2870
+ borderColor: 'magenta'
2871
+ });
2872
+ }
2873
+
2482
2874
  if (ultraCompact && !splitPreviewLayout) {
2483
2875
  if (previewOpen) {
2484
- panelCandidates = panelCandidates.filter((panel) => panel && (panel.key === 'current-run' || panel.key === 'preview'));
2876
+ panelCandidates = panelCandidates.filter((panel) => panel && (panel.key === focusedSection || panel.key === 'preview'));
2485
2877
  } else {
2486
- panelCandidates = [panelCandidates[0]];
2878
+ const focusedPanel = panelCandidates.find((panel) => panel?.key === focusedSection);
2879
+ panelCandidates = [focusedPanel || panelCandidates[0]];
2487
2880
  }
2488
2881
  }
2489
2882
 
@@ -2502,7 +2895,7 @@ function createDashboardApp(deps) {
2502
2895
  });
2503
2896
 
2504
2897
  let contentNode;
2505
- if (splitPreviewLayout && ui.view === 'runs' && !overlayPreviewOpen) {
2898
+ if (splitPreviewLayout && !overlayPreviewOpen) {
2506
2899
  const previewPanel = {
2507
2900
  key: 'preview-split',
2508
2901
  title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
@@ -2564,11 +2957,11 @@ function createDashboardApp(deps) {
2564
2957
  { flexDirection: 'column', width: fullWidth },
2565
2958
  React.createElement(Text, { color: 'cyan' }, buildHeaderLine(snapshot, activeFlow, watchEnabled, watchStatus, lastRefreshAt, ui.view, fullWidth)),
2566
2959
  React.createElement(FlowBar, { activeFlow, width: fullWidth, flowIds: availableFlowIds }),
2567
- React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons }),
2960
+ React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons, flow: activeFlow }),
2568
2961
  showErrorInline
2569
2962
  ? React.createElement(Text, { color: 'red' }, truncate(buildErrorLines(error, fullWidth)[0] || 'Error', fullWidth))
2570
2963
  : null,
2571
- showErrorPanel
2964
+ showGlobalErrorPanel
2572
2965
  ? React.createElement(SectionPanel, {
2573
2966
  title: 'Errors',
2574
2967
  lines: buildErrorLines(error, Math.max(18, fullWidth - 4)),
@@ -1,15 +1,18 @@
1
1
  function createInitialUIState() {
2
2
  return {
3
3
  view: 'runs',
4
- showHelp: true
4
+ showHelp: false
5
5
  };
6
6
  }
7
7
 
8
8
  function cycleView(current) {
9
9
  if (current === 'runs') {
10
- return 'overview';
10
+ return 'intents';
11
11
  }
12
- if (current === 'overview') {
12
+ if (current === 'intents') {
13
+ return 'completed';
14
+ }
15
+ if (current === 'completed') {
13
16
  return 'health';
14
17
  }
15
18
  return 'runs';
@@ -19,10 +22,13 @@ function cycleViewBackward(current) {
19
22
  if (current === 'runs') {
20
23
  return 'health';
21
24
  }
22
- if (current === 'overview') {
25
+ if (current === 'intents') {
23
26
  return 'runs';
24
27
  }
25
- return 'overview';
28
+ if (current === 'completed') {
29
+ return 'intents';
30
+ }
31
+ return 'completed';
26
32
  }
27
33
 
28
34
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
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": {