specsmd 0.1.41 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dashboard/tui/app.js +144 -37
- package/package.json +1 -1
package/lib/dashboard/tui/app.js
CHANGED
|
@@ -905,6 +905,46 @@ function buildIntentScopedLabel(snapshot, intentId, filePath, fallbackName = 'fi
|
|
|
905
905
|
return safeIntentId ? `${safeIntentId}/${safeFallback}` : safeFallback;
|
|
906
906
|
}
|
|
907
907
|
|
|
908
|
+
function findIntentIdForWorkItem(snapshot, workItemId) {
|
|
909
|
+
if (typeof workItemId !== 'string' || workItemId.trim() === '') {
|
|
910
|
+
return '';
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
|
|
914
|
+
for (const intent of intents) {
|
|
915
|
+
const items = Array.isArray(intent?.workItems) ? intent.workItems : [];
|
|
916
|
+
if (items.some((item) => item?.id === workItemId)) {
|
|
917
|
+
return intent?.id || '';
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
return '';
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function resolveFireWorkItemPath(snapshot, intentId, workItemId, explicitPath) {
|
|
925
|
+
if (typeof explicitPath === 'string' && explicitPath.trim() !== '') {
|
|
926
|
+
return explicitPath;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (typeof snapshot?.rootPath !== 'string' || snapshot.rootPath.trim() === '') {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (typeof workItemId !== 'string' || workItemId.trim() === '') {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
const safeIntentId = typeof intentId === 'string' && intentId.trim() !== ''
|
|
938
|
+
? intentId
|
|
939
|
+
: findIntentIdForWorkItem(snapshot, workItemId);
|
|
940
|
+
|
|
941
|
+
if (!safeIntentId) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return path.join(snapshot.rootPath, 'intents', safeIntentId, 'work-items', `${workItemId}.md`);
|
|
946
|
+
}
|
|
947
|
+
|
|
908
948
|
function collectFireRunFiles(run) {
|
|
909
949
|
if (!run || typeof run.folderPath !== 'string') {
|
|
910
950
|
return [];
|
|
@@ -1137,6 +1177,77 @@ function getNoCurrentMessage(flow) {
|
|
|
1137
1177
|
return 'No active run';
|
|
1138
1178
|
}
|
|
1139
1179
|
|
|
1180
|
+
function buildFireCurrentRunGroups(snapshot) {
|
|
1181
|
+
const run = getCurrentRun(snapshot);
|
|
1182
|
+
if (!run) {
|
|
1183
|
+
return [];
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const workItems = Array.isArray(run.workItems) ? run.workItems : [];
|
|
1187
|
+
const completed = workItems.filter((item) => item.status === 'completed').length;
|
|
1188
|
+
const currentWorkItem = workItems.find((item) => item.id === run.currentItem)
|
|
1189
|
+
|| workItems.find((item) => item.status === 'in_progress')
|
|
1190
|
+
|| workItems[0]
|
|
1191
|
+
|| null;
|
|
1192
|
+
|
|
1193
|
+
const currentPhase = getCurrentPhaseLabel(run, currentWorkItem);
|
|
1194
|
+
const phaseTrack = buildPhaseTrack(currentPhase);
|
|
1195
|
+
const mode = String(currentWorkItem?.mode || 'confirm').toUpperCase();
|
|
1196
|
+
const status = currentWorkItem?.status || 'pending';
|
|
1197
|
+
const statusTag = status === 'in_progress' ? 'current' : status;
|
|
1198
|
+
|
|
1199
|
+
const runIntentId = typeof run?.intent === 'string' ? run.intent : '';
|
|
1200
|
+
const currentWorkItemFiles = workItems.map((item, index) => {
|
|
1201
|
+
const itemId = typeof item?.id === 'string' && item.id !== '' ? item.id : `work-item-${index + 1}`;
|
|
1202
|
+
const intentId = typeof item?.intent === 'string' && item.intent !== ''
|
|
1203
|
+
? item.intent
|
|
1204
|
+
: (runIntentId || findIntentIdForWorkItem(snapshot, itemId));
|
|
1205
|
+
const filePath = resolveFireWorkItemPath(snapshot, intentId, itemId, item?.filePath);
|
|
1206
|
+
if (!filePath) {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
const itemMode = String(item?.mode || 'confirm').toUpperCase();
|
|
1211
|
+
const itemStatus = item?.status || 'pending';
|
|
1212
|
+
const isCurrent = Boolean(currentWorkItem?.id) && itemId === currentWorkItem.id;
|
|
1213
|
+
const itemScope = isCurrent
|
|
1214
|
+
? 'active'
|
|
1215
|
+
: (itemStatus === 'completed' ? 'completed' : 'upcoming');
|
|
1216
|
+
const itemStatusTag = isCurrent ? 'current' : itemStatus;
|
|
1217
|
+
const labelPath = buildIntentScopedLabel(snapshot, intentId, filePath, `${itemId}.md`);
|
|
1218
|
+
|
|
1219
|
+
return {
|
|
1220
|
+
label: `${labelPath} [${itemMode}] [${itemStatusTag}]`,
|
|
1221
|
+
path: filePath,
|
|
1222
|
+
scope: itemScope
|
|
1223
|
+
};
|
|
1224
|
+
}).filter(Boolean);
|
|
1225
|
+
|
|
1226
|
+
const currentRunFiles = collectFireRunFiles(run).map((fileEntry) => ({
|
|
1227
|
+
...fileEntry,
|
|
1228
|
+
label: path.basename(fileEntry.path || fileEntry.label || ''),
|
|
1229
|
+
scope: 'active'
|
|
1230
|
+
}));
|
|
1231
|
+
|
|
1232
|
+
return [
|
|
1233
|
+
{
|
|
1234
|
+
key: `current:run:${run.id}:summary`,
|
|
1235
|
+
label: `${run.id} [${run.scope}] ${completed}/${workItems.length} items`,
|
|
1236
|
+
files: []
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
key: `current:run:${run.id}:work-items`,
|
|
1240
|
+
label: `WORK ITEMS (${currentWorkItemFiles.length})`,
|
|
1241
|
+
files: filterExistingFiles(currentWorkItemFiles)
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
key: `current:run:${run.id}:run-files`,
|
|
1245
|
+
label: 'RUN FILES',
|
|
1246
|
+
files: filterExistingFiles(currentRunFiles)
|
|
1247
|
+
}
|
|
1248
|
+
];
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1140
1251
|
function buildCurrentGroups(snapshot, flow) {
|
|
1141
1252
|
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1142
1253
|
|
|
@@ -1169,17 +1280,7 @@ function buildCurrentGroups(snapshot, flow) {
|
|
|
1169
1280
|
}];
|
|
1170
1281
|
}
|
|
1171
1282
|
|
|
1172
|
-
|
|
1173
|
-
if (!run) {
|
|
1174
|
-
return [];
|
|
1175
|
-
}
|
|
1176
|
-
const workItems = Array.isArray(run.workItems) ? run.workItems : [];
|
|
1177
|
-
const completed = workItems.filter((item) => item.status === 'completed').length;
|
|
1178
|
-
return [{
|
|
1179
|
-
key: `current:run:${run.id}`,
|
|
1180
|
-
label: `${run.id} [${run.scope}] ${completed}/${workItems.length} items`,
|
|
1181
|
-
files: filterExistingFiles(collectFireRunFiles(run).map((file) => ({ ...file, scope: 'active' })))
|
|
1182
|
-
}];
|
|
1283
|
+
return buildFireCurrentRunGroups(snapshot);
|
|
1183
1284
|
}
|
|
1184
1285
|
|
|
1185
1286
|
function buildRunFileGroups(fileEntries) {
|
|
@@ -1375,26 +1476,41 @@ function buildOverviewIntentGroups(snapshot, flow, filter = 'next') {
|
|
|
1375
1476
|
});
|
|
1376
1477
|
}
|
|
1377
1478
|
|
|
1378
|
-
function
|
|
1479
|
+
function buildStandardsRows(snapshot, flow) {
|
|
1379
1480
|
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1380
1481
|
if (effectiveFlow === 'simple') {
|
|
1381
|
-
return [
|
|
1482
|
+
return [{
|
|
1483
|
+
kind: 'info',
|
|
1484
|
+
key: 'standards:empty:simple',
|
|
1485
|
+
label: 'No standards for SIMPLE flow',
|
|
1486
|
+
selectable: false
|
|
1487
|
+
}];
|
|
1382
1488
|
}
|
|
1383
1489
|
|
|
1384
1490
|
const standards = Array.isArray(snapshot?.standards) ? snapshot.standards : [];
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1491
|
+
const files = filterExistingFiles(standards.map((standard, index) => ({
|
|
1492
|
+
label: `${standard?.name || standard?.type || `standard-${index}`}.md`,
|
|
1493
|
+
path: standard?.filePath,
|
|
1494
|
+
scope: 'file'
|
|
1495
|
+
})));
|
|
1496
|
+
|
|
1497
|
+
if (files.length === 0) {
|
|
1498
|
+
return [{
|
|
1499
|
+
kind: 'info',
|
|
1500
|
+
key: 'standards:empty',
|
|
1501
|
+
label: 'No standards found',
|
|
1502
|
+
selectable: false
|
|
1503
|
+
}];
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
return files.map((file, index) => ({
|
|
1507
|
+
kind: 'file',
|
|
1508
|
+
key: `standards:file:${file.path}:${index}`,
|
|
1509
|
+
label: file.label,
|
|
1510
|
+
path: file.path,
|
|
1511
|
+
scope: 'file',
|
|
1512
|
+
selectable: true
|
|
1513
|
+
}));
|
|
1398
1514
|
}
|
|
1399
1515
|
|
|
1400
1516
|
function buildProjectGroups(snapshot, flow) {
|
|
@@ -2281,11 +2397,7 @@ function createDashboardApp(deps) {
|
|
|
2281
2397
|
)
|
|
2282
2398
|
: toLoadingRows('Loading completed items...', 'completed-loading');
|
|
2283
2399
|
const standardsRows = shouldHydrateSecondaryTabs
|
|
2284
|
-
?
|
|
2285
|
-
buildStandardsGroups(snapshot, activeFlow),
|
|
2286
|
-
effectiveFlow === 'simple' ? 'No standards for SIMPLE flow' : 'No standards found',
|
|
2287
|
-
expandedGroups
|
|
2288
|
-
)
|
|
2400
|
+
? buildStandardsRows(snapshot, activeFlow)
|
|
2289
2401
|
: toLoadingRows('Loading standards...', 'standards-loading');
|
|
2290
2402
|
const statsRows = shouldHydrateSecondaryTabs
|
|
2291
2403
|
? toInfoRows(
|
|
@@ -2721,11 +2833,6 @@ function createDashboardApp(deps) {
|
|
|
2721
2833
|
}, [activeFlow, rowLengthSignature, snapshot?.generatedAt]);
|
|
2722
2834
|
|
|
2723
2835
|
useEffect(() => {
|
|
2724
|
-
if (ui.view !== 'runs') {
|
|
2725
|
-
setDeferredTabsReady(true);
|
|
2726
|
-
return undefined;
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
2836
|
setDeferredTabsReady(false);
|
|
2730
2837
|
const timer = setTimeout(() => {
|
|
2731
2838
|
setDeferredTabsReady(true);
|
|
@@ -2733,7 +2840,7 @@ function createDashboardApp(deps) {
|
|
|
2733
2840
|
return () => {
|
|
2734
2841
|
clearTimeout(timer);
|
|
2735
2842
|
};
|
|
2736
|
-
}, [activeFlow
|
|
2843
|
+
}, [activeFlow]);
|
|
2737
2844
|
|
|
2738
2845
|
useEffect(() => {
|
|
2739
2846
|
setPaneFocus('main');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
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": {
|