specsmd 0.1.27 → 0.1.28
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/aidlc/parser.js +581 -0
- package/lib/dashboard/index.js +80 -46
- package/lib/dashboard/simple/parser.js +293 -0
- package/lib/dashboard/tui/app.js +470 -25
- package/package.json +1 -1
package/lib/dashboard/tui/app.js
CHANGED
|
@@ -114,18 +114,33 @@ function formatTime(value) {
|
|
|
114
114
|
return date.toLocaleTimeString();
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
function buildShortStats(snapshot) {
|
|
117
|
+
function buildShortStats(snapshot, flow) {
|
|
118
118
|
if (!snapshot?.initialized) {
|
|
119
|
+
if (flow === 'aidlc') {
|
|
120
|
+
return 'init: waiting for memory-bank scan';
|
|
121
|
+
}
|
|
122
|
+
if (flow === 'simple') {
|
|
123
|
+
return 'init: waiting for specs scan';
|
|
124
|
+
}
|
|
119
125
|
return 'init: waiting for state.yaml';
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
const stats = snapshot
|
|
123
|
-
|
|
128
|
+
const stats = snapshot?.stats || {};
|
|
129
|
+
|
|
130
|
+
if (flow === 'aidlc') {
|
|
131
|
+
return `bolts ${stats.activeBoltsCount || 0}/${stats.completedBolts || 0} | intents ${stats.completedIntents || 0}/${stats.totalIntents || 0} | stories ${stats.completedStories || 0}/${stats.totalStories || 0}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (flow === 'simple') {
|
|
135
|
+
return `specs ${stats.completedSpecs || 0}/${stats.totalSpecs || 0} | tasks ${stats.completedTasks || 0}/${stats.totalTasks || 0} | active ${stats.activeSpecsCount || 0}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return `runs ${stats.activeRunsCount || 0}/${stats.completedRuns || 0} | intents ${stats.completedIntents || 0}/${stats.totalIntents || 0} | work ${stats.completedWorkItems || 0}/${stats.totalWorkItems || 0}`;
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
function buildHeaderLine(snapshot, flow, watchEnabled, watchStatus, lastRefreshAt, view, runFilter, width) {
|
|
127
|
-
const projectName = snapshot?.project?.name || 'Unnamed
|
|
128
|
-
const shortStats = buildShortStats(snapshot);
|
|
142
|
+
const projectName = snapshot?.project?.name || 'Unnamed project';
|
|
143
|
+
const shortStats = buildShortStats(snapshot, flow);
|
|
129
144
|
|
|
130
145
|
const line = `${flow.toUpperCase()} | ${projectName} | ${shortStats} | watch:${watchEnabled ? watchStatus : 'off'} | ${view}/${runFilter} | ${formatTime(lastRefreshAt)}`;
|
|
131
146
|
|
|
@@ -192,7 +207,7 @@ function buildPhaseTrack(currentPhase) {
|
|
|
192
207
|
return labels.map((label, index) => (index === currentIndex ? `[${label}]` : ` ${label} `)).join(' - ');
|
|
193
208
|
}
|
|
194
209
|
|
|
195
|
-
function
|
|
210
|
+
function buildFireCurrentRunLines(snapshot, width) {
|
|
196
211
|
const run = getCurrentRun(snapshot);
|
|
197
212
|
if (!run) {
|
|
198
213
|
return [truncate('No active run', width)];
|
|
@@ -218,7 +233,7 @@ function buildCurrentRunLines(snapshot, width) {
|
|
|
218
233
|
return lines.map((line) => truncate(line, width));
|
|
219
234
|
}
|
|
220
235
|
|
|
221
|
-
function
|
|
236
|
+
function buildFireRunFilesLines(snapshot, width, icons) {
|
|
222
237
|
const run = getCurrentRun(snapshot);
|
|
223
238
|
if (!run) {
|
|
224
239
|
return [truncate('No run files (no active run)', width)];
|
|
@@ -232,7 +247,7 @@ function buildRunFilesLines(snapshot, width, icons) {
|
|
|
232
247
|
return files.map((file) => truncate(`${icons.runFile} ${file}`, width));
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
function
|
|
250
|
+
function buildFirePendingLines(snapshot, runFilter, width) {
|
|
236
251
|
if (runFilter === 'completed') {
|
|
237
252
|
return [truncate('Hidden by run filter: completed', width)];
|
|
238
253
|
}
|
|
@@ -248,7 +263,7 @@ function buildPendingLines(snapshot, runFilter, width) {
|
|
|
248
263
|
});
|
|
249
264
|
}
|
|
250
265
|
|
|
251
|
-
function
|
|
266
|
+
function buildFireCompletedLines(snapshot, runFilter, width) {
|
|
252
267
|
if (runFilter === 'active') {
|
|
253
268
|
return [truncate('Hidden by run filter: active', width)];
|
|
254
269
|
}
|
|
@@ -265,7 +280,7 @@ function buildCompletedLines(snapshot, runFilter, width) {
|
|
|
265
280
|
});
|
|
266
281
|
}
|
|
267
282
|
|
|
268
|
-
function
|
|
283
|
+
function buildFireStatsLines(snapshot, width) {
|
|
269
284
|
if (!snapshot?.initialized) {
|
|
270
285
|
return [truncate('Waiting for .specs-fire/state.yaml initialization.', width)];
|
|
271
286
|
}
|
|
@@ -287,7 +302,7 @@ function buildWarningsLines(snapshot, width) {
|
|
|
287
302
|
return warnings.map((warning) => truncate(warning, width));
|
|
288
303
|
}
|
|
289
304
|
|
|
290
|
-
function
|
|
305
|
+
function buildFireOverviewProjectLines(snapshot, width) {
|
|
291
306
|
if (!snapshot?.initialized) {
|
|
292
307
|
return [
|
|
293
308
|
truncate('FIRE folder detected, but state.yaml is missing.', width),
|
|
@@ -305,7 +320,7 @@ function buildOverviewProjectLines(snapshot, width) {
|
|
|
305
320
|
].map((line) => truncate(line, width));
|
|
306
321
|
}
|
|
307
322
|
|
|
308
|
-
function
|
|
323
|
+
function buildFireOverviewIntentLines(snapshot, width) {
|
|
309
324
|
const intents = snapshot?.intents || [];
|
|
310
325
|
if (intents.length === 0) {
|
|
311
326
|
return [truncate('No intents found', width)];
|
|
@@ -318,7 +333,7 @@ function buildOverviewIntentLines(snapshot, width) {
|
|
|
318
333
|
});
|
|
319
334
|
}
|
|
320
335
|
|
|
321
|
-
function
|
|
336
|
+
function buildFireOverviewStandardsLines(snapshot, width) {
|
|
322
337
|
const expected = ['constitution', 'tech-stack', 'coding-standards', 'testing-standards', 'system-architecture'];
|
|
323
338
|
const actual = new Set((snapshot?.standards || []).map((item) => item.type));
|
|
324
339
|
|
|
@@ -328,6 +343,435 @@ function buildOverviewStandardsLines(snapshot, width) {
|
|
|
328
343
|
});
|
|
329
344
|
}
|
|
330
345
|
|
|
346
|
+
function getEffectiveFlow(flow, snapshot) {
|
|
347
|
+
const explicitFlow = typeof flow === 'string' && flow !== '' ? flow : null;
|
|
348
|
+
const snapshotFlow = typeof snapshot?.flow === 'string' && snapshot.flow !== '' ? snapshot.flow : null;
|
|
349
|
+
return (snapshotFlow || explicitFlow || 'fire').toLowerCase();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getCurrentBolt(snapshot) {
|
|
353
|
+
const activeBolts = Array.isArray(snapshot?.activeBolts) ? [...snapshot.activeBolts] : [];
|
|
354
|
+
if (activeBolts.length === 0) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
activeBolts.sort((a, b) => {
|
|
359
|
+
const aTime = a?.startedAt ? Date.parse(a.startedAt) : 0;
|
|
360
|
+
const bTime = b?.startedAt ? Date.parse(b.startedAt) : 0;
|
|
361
|
+
if (bTime !== aTime) {
|
|
362
|
+
return bTime - aTime;
|
|
363
|
+
}
|
|
364
|
+
return String(a?.id || '').localeCompare(String(b?.id || ''));
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return activeBolts[0] || null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function buildAidlcStageTrack(bolt) {
|
|
371
|
+
const stages = Array.isArray(bolt?.stages) ? bolt.stages : [];
|
|
372
|
+
if (stages.length === 0) {
|
|
373
|
+
return 'n/a';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return stages.map((stage) => {
|
|
377
|
+
const label = String(stage?.name || '?').charAt(0).toUpperCase();
|
|
378
|
+
if (stage?.status === 'completed') {
|
|
379
|
+
return `[${label}]`;
|
|
380
|
+
}
|
|
381
|
+
if (stage?.status === 'in_progress') {
|
|
382
|
+
return `<${label}>`;
|
|
383
|
+
}
|
|
384
|
+
return ` ${label} `;
|
|
385
|
+
}).join('-');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function buildAidlcCurrentRunLines(snapshot, width) {
|
|
389
|
+
const bolt = getCurrentBolt(snapshot);
|
|
390
|
+
if (!bolt) {
|
|
391
|
+
return [truncate('No active bolt', width)];
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const stages = Array.isArray(bolt.stages) ? bolt.stages : [];
|
|
395
|
+
const completedStages = stages.filter((stage) => stage.status === 'completed').length;
|
|
396
|
+
const phaseTrack = buildAidlcStageTrack(bolt);
|
|
397
|
+
const location = `${bolt.intent || 'unknown-intent'} / ${bolt.unit || 'unknown-unit'}`;
|
|
398
|
+
|
|
399
|
+
const lines = [
|
|
400
|
+
`${bolt.id} [${bolt.type}] ${completedStages}/${stages.length} stages done`,
|
|
401
|
+
`scope: ${location}`,
|
|
402
|
+
`stage: ${bolt.currentStage || 'n/a'} | status: ${bolt.status}`,
|
|
403
|
+
`phase: ${phaseTrack}`
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
return lines.map((line) => truncate(line, width));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function buildAidlcRunFilesLines(snapshot, width, icons) {
|
|
410
|
+
const bolt = getCurrentBolt(snapshot);
|
|
411
|
+
if (!bolt) {
|
|
412
|
+
return [truncate('No bolt files (no active bolt)', width)];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const files = Array.isArray(bolt.files) ? bolt.files : [];
|
|
416
|
+
if (files.length === 0) {
|
|
417
|
+
return [truncate('No markdown files found in active bolt', width)];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return files.map((file) => truncate(`${icons.runFile} ${file}`, width));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function buildAidlcPendingLines(snapshot, runFilter, width) {
|
|
424
|
+
if (runFilter === 'completed') {
|
|
425
|
+
return [truncate('Hidden by run filter: completed', width)];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const pendingBolts = Array.isArray(snapshot?.pendingBolts) ? snapshot.pendingBolts : [];
|
|
429
|
+
if (pendingBolts.length === 0) {
|
|
430
|
+
return [truncate('No queued bolts', width)];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return pendingBolts.map((bolt) => {
|
|
434
|
+
const deps = Array.isArray(bolt.blockedBy) && bolt.blockedBy.length > 0
|
|
435
|
+
? ` blocked_by:${bolt.blockedBy.join(',')}`
|
|
436
|
+
: '';
|
|
437
|
+
const location = `${bolt.intent || 'unknown'}/${bolt.unit || 'unknown'}`;
|
|
438
|
+
return truncate(`${bolt.id} (${bolt.status}) in ${location}${deps}`, width);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function buildAidlcCompletedLines(snapshot, runFilter, width) {
|
|
443
|
+
if (runFilter === 'active') {
|
|
444
|
+
return [truncate('Hidden by run filter: active', width)];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const completedBolts = Array.isArray(snapshot?.completedBolts) ? snapshot.completedBolts : [];
|
|
448
|
+
if (completedBolts.length === 0) {
|
|
449
|
+
return [truncate('No completed bolts yet', width)];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return completedBolts.map((bolt) =>
|
|
453
|
+
truncate(`${bolt.id} [${bolt.type}] done at ${bolt.completedAt || 'unknown'}`, width)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function buildAidlcStatsLines(snapshot, width) {
|
|
458
|
+
const stats = snapshot?.stats || {};
|
|
459
|
+
|
|
460
|
+
return [
|
|
461
|
+
`intents: ${stats.completedIntents || 0}/${stats.totalIntents || 0} done | in_progress: ${stats.inProgressIntents || 0} | blocked: ${stats.blockedIntents || 0}`,
|
|
462
|
+
`stories: ${stats.completedStories || 0}/${stats.totalStories || 0} done | in_progress: ${stats.inProgressStories || 0} | pending: ${stats.pendingStories || 0} | blocked: ${stats.blockedStories || 0}`,
|
|
463
|
+
`bolts: ${stats.activeBoltsCount || 0} active | ${stats.queuedBolts || 0} queued | ${stats.blockedBolts || 0} blocked | ${stats.completedBolts || 0} done`
|
|
464
|
+
].map((line) => truncate(line, width));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function buildAidlcOverviewProjectLines(snapshot, width) {
|
|
468
|
+
const project = snapshot?.project || {};
|
|
469
|
+
const stats = snapshot?.stats || {};
|
|
470
|
+
|
|
471
|
+
return [
|
|
472
|
+
`project: ${project.name || 'unknown'} | project_type: ${project.projectType || 'unknown'}`,
|
|
473
|
+
`memory-bank: intents ${stats.totalIntents || 0} | units ${stats.totalUnits || 0} | stories ${stats.totalStories || 0}`,
|
|
474
|
+
`progress: ${stats.progressPercent || 0}% stories complete | standards: ${(snapshot?.standards || []).length}`
|
|
475
|
+
].map((line) => truncate(line, width));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function buildAidlcOverviewIntentLines(snapshot, width) {
|
|
479
|
+
const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
|
|
480
|
+
if (intents.length === 0) {
|
|
481
|
+
return [truncate('No intents found', width)];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return intents.map((intent) => {
|
|
485
|
+
return truncate(
|
|
486
|
+
`${intent.id}: ${intent.status} (${intent.completedStories || 0}/${intent.storyCount || 0} stories, ${intent.completedUnits || 0}/${intent.unitCount || 0} units)`,
|
|
487
|
+
width
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function buildAidlcOverviewStandardsLines(snapshot, width) {
|
|
493
|
+
const standards = Array.isArray(snapshot?.standards) ? snapshot.standards : [];
|
|
494
|
+
if (standards.length === 0) {
|
|
495
|
+
return [truncate('No standards found under memory-bank/standards', width)];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return standards.map((standard) =>
|
|
499
|
+
truncate(`[x] ${standard.name || standard.type || 'unknown'}.md`, width)
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function getCurrentSpec(snapshot) {
|
|
504
|
+
const specs = Array.isArray(snapshot?.activeSpecs) ? snapshot.activeSpecs : [];
|
|
505
|
+
if (specs.length === 0) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
return specs[0] || null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function simplePhaseIndex(state) {
|
|
512
|
+
if (state === 'requirements_pending') {
|
|
513
|
+
return 0;
|
|
514
|
+
}
|
|
515
|
+
if (state === 'design_pending') {
|
|
516
|
+
return 1;
|
|
517
|
+
}
|
|
518
|
+
return 2;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function buildSimplePhaseTrack(spec) {
|
|
522
|
+
if (spec?.state === 'completed') {
|
|
523
|
+
return '[R] - [D] - [T]';
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const labels = ['R', 'D', 'T'];
|
|
527
|
+
const current = simplePhaseIndex(spec?.state);
|
|
528
|
+
return labels.map((label, index) => (index === current ? `[${label}]` : ` ${label} `)).join(' - ');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function buildSimpleCurrentRunLines(snapshot, width) {
|
|
532
|
+
const spec = getCurrentSpec(snapshot);
|
|
533
|
+
if (!spec) {
|
|
534
|
+
return [truncate('No active spec', width)];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const files = [
|
|
538
|
+
spec.hasRequirements ? 'req' : '-',
|
|
539
|
+
spec.hasDesign ? 'design' : '-',
|
|
540
|
+
spec.hasTasks ? 'tasks' : '-'
|
|
541
|
+
].join('/');
|
|
542
|
+
|
|
543
|
+
const lines = [
|
|
544
|
+
`${spec.name} [${spec.state}] ${spec.tasksCompleted}/${spec.tasksTotal} tasks done`,
|
|
545
|
+
`phase: ${spec.phase}`,
|
|
546
|
+
`files: ${files}`,
|
|
547
|
+
`track: ${buildSimplePhaseTrack(spec)}`
|
|
548
|
+
];
|
|
549
|
+
|
|
550
|
+
return lines.map((line) => truncate(line, width));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function buildSimpleRunFilesLines(snapshot, width, icons) {
|
|
554
|
+
const spec = getCurrentSpec(snapshot);
|
|
555
|
+
if (!spec) {
|
|
556
|
+
return [truncate('No spec files (no active spec)', width)];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const files = [];
|
|
560
|
+
if (spec.hasRequirements) files.push('requirements.md');
|
|
561
|
+
if (spec.hasDesign) files.push('design.md');
|
|
562
|
+
if (spec.hasTasks) files.push('tasks.md');
|
|
563
|
+
|
|
564
|
+
if (files.length === 0) {
|
|
565
|
+
return [truncate('No files found in active spec folder', width)];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return files.map((file) => truncate(`${icons.runFile} ${file}`, width));
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function buildSimplePendingLines(snapshot, runFilter, width) {
|
|
572
|
+
if (runFilter === 'completed') {
|
|
573
|
+
return [truncate('Hidden by run filter: completed', width)];
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const pendingSpecs = Array.isArray(snapshot?.pendingSpecs) ? snapshot.pendingSpecs : [];
|
|
577
|
+
if (pendingSpecs.length === 0) {
|
|
578
|
+
return [truncate('No pending specs', width)];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return pendingSpecs.map((spec) =>
|
|
582
|
+
truncate(`${spec.name} (${spec.state}) ${spec.tasksCompleted}/${spec.tasksTotal} tasks`, width)
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function buildSimpleCompletedLines(snapshot, runFilter, width) {
|
|
587
|
+
if (runFilter === 'active') {
|
|
588
|
+
return [truncate('Hidden by run filter: active', width)];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const completedSpecs = Array.isArray(snapshot?.completedSpecs) ? snapshot.completedSpecs : [];
|
|
592
|
+
if (completedSpecs.length === 0) {
|
|
593
|
+
return [truncate('No completed specs yet', width)];
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return completedSpecs.map((spec) =>
|
|
597
|
+
truncate(`${spec.name} done at ${spec.updatedAt || 'unknown'} (${spec.tasksCompleted}/${spec.tasksTotal})`, width)
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function buildSimpleStatsLines(snapshot, width) {
|
|
602
|
+
const stats = snapshot?.stats || {};
|
|
603
|
+
|
|
604
|
+
return [
|
|
605
|
+
`specs: ${stats.completedSpecs || 0}/${stats.totalSpecs || 0} complete | in_progress: ${stats.inProgressSpecs || 0} | pending: ${stats.pendingSpecs || 0}`,
|
|
606
|
+
`pipeline: ready ${stats.readySpecs || 0} | design_pending ${stats.designPendingSpecs || 0} | tasks_pending ${stats.tasksPendingSpecs || 0}`,
|
|
607
|
+
`tasks: ${stats.completedTasks || 0}/${stats.totalTasks || 0} complete | pending: ${stats.pendingTasks || 0} | optional: ${stats.optionalTasks || 0}`
|
|
608
|
+
].map((line) => truncate(line, width));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function buildSimpleOverviewProjectLines(snapshot, width) {
|
|
612
|
+
const project = snapshot?.project || {};
|
|
613
|
+
const stats = snapshot?.stats || {};
|
|
614
|
+
|
|
615
|
+
return [
|
|
616
|
+
`project: ${project.name || 'unknown'} | simple flow`,
|
|
617
|
+
`specs: ${stats.totalSpecs || 0} total | active: ${stats.activeSpecsCount || 0} | completed: ${stats.completedSpecs || 0}`,
|
|
618
|
+
`tasks: ${stats.completedTasks || 0}/${stats.totalTasks || 0} complete (${stats.progressPercent || 0}%)`
|
|
619
|
+
].map((line) => truncate(line, width));
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function buildSimpleOverviewIntentLines(snapshot, width) {
|
|
623
|
+
const specs = Array.isArray(snapshot?.specs) ? snapshot.specs : [];
|
|
624
|
+
if (specs.length === 0) {
|
|
625
|
+
return [truncate('No specs found', width)];
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return specs.map((spec) =>
|
|
629
|
+
truncate(`${spec.name}: ${spec.state} (${spec.tasksCompleted}/${spec.tasksTotal} tasks)`, width)
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function buildSimpleOverviewStandardsLines(snapshot, width) {
|
|
634
|
+
const specs = Array.isArray(snapshot?.specs) ? snapshot.specs : [];
|
|
635
|
+
if (specs.length === 0) {
|
|
636
|
+
return [truncate('No spec artifacts found', width)];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const reqCount = specs.filter((spec) => spec.hasRequirements).length;
|
|
640
|
+
const designCount = specs.filter((spec) => spec.hasDesign).length;
|
|
641
|
+
const tasksCount = specs.filter((spec) => spec.hasTasks).length;
|
|
642
|
+
const total = specs.length;
|
|
643
|
+
|
|
644
|
+
return [
|
|
645
|
+
`[x] requirements.md coverage ${reqCount}/${total}`,
|
|
646
|
+
`[x] design.md coverage ${designCount}/${total}`,
|
|
647
|
+
`[x] tasks.md coverage ${tasksCount}/${total}`
|
|
648
|
+
].map((line) => truncate(line, width));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function buildCurrentRunLines(snapshot, width, flow) {
|
|
652
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
653
|
+
if (effectiveFlow === 'aidlc') {
|
|
654
|
+
return buildAidlcCurrentRunLines(snapshot, width);
|
|
655
|
+
}
|
|
656
|
+
if (effectiveFlow === 'simple') {
|
|
657
|
+
return buildSimpleCurrentRunLines(snapshot, width);
|
|
658
|
+
}
|
|
659
|
+
return buildFireCurrentRunLines(snapshot, width);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function buildRunFilesLines(snapshot, width, icons, flow) {
|
|
663
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
664
|
+
if (effectiveFlow === 'aidlc') {
|
|
665
|
+
return buildAidlcRunFilesLines(snapshot, width, icons);
|
|
666
|
+
}
|
|
667
|
+
if (effectiveFlow === 'simple') {
|
|
668
|
+
return buildSimpleRunFilesLines(snapshot, width, icons);
|
|
669
|
+
}
|
|
670
|
+
return buildFireRunFilesLines(snapshot, width, icons);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function buildPendingLines(snapshot, runFilter, width, flow) {
|
|
674
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
675
|
+
if (effectiveFlow === 'aidlc') {
|
|
676
|
+
return buildAidlcPendingLines(snapshot, runFilter, width);
|
|
677
|
+
}
|
|
678
|
+
if (effectiveFlow === 'simple') {
|
|
679
|
+
return buildSimplePendingLines(snapshot, runFilter, width);
|
|
680
|
+
}
|
|
681
|
+
return buildFirePendingLines(snapshot, runFilter, width);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function buildCompletedLines(snapshot, runFilter, width, flow) {
|
|
685
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
686
|
+
if (effectiveFlow === 'aidlc') {
|
|
687
|
+
return buildAidlcCompletedLines(snapshot, runFilter, width);
|
|
688
|
+
}
|
|
689
|
+
if (effectiveFlow === 'simple') {
|
|
690
|
+
return buildSimpleCompletedLines(snapshot, runFilter, width);
|
|
691
|
+
}
|
|
692
|
+
return buildFireCompletedLines(snapshot, runFilter, width);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function buildStatsLines(snapshot, width, flow) {
|
|
696
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
697
|
+
if (!snapshot?.initialized) {
|
|
698
|
+
if (effectiveFlow === 'aidlc') {
|
|
699
|
+
return [truncate('Waiting for memory-bank initialization.', width)];
|
|
700
|
+
}
|
|
701
|
+
if (effectiveFlow === 'simple') {
|
|
702
|
+
return [truncate('Waiting for specs/ initialization.', width)];
|
|
703
|
+
}
|
|
704
|
+
return [truncate('Waiting for .specs-fire/state.yaml initialization.', width)];
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (effectiveFlow === 'aidlc') {
|
|
708
|
+
return buildAidlcStatsLines(snapshot, width);
|
|
709
|
+
}
|
|
710
|
+
if (effectiveFlow === 'simple') {
|
|
711
|
+
return buildSimpleStatsLines(snapshot, width);
|
|
712
|
+
}
|
|
713
|
+
return buildFireStatsLines(snapshot, width);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function buildOverviewProjectLines(snapshot, width, flow) {
|
|
717
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
718
|
+
if (effectiveFlow === 'aidlc') {
|
|
719
|
+
return buildAidlcOverviewProjectLines(snapshot, width);
|
|
720
|
+
}
|
|
721
|
+
if (effectiveFlow === 'simple') {
|
|
722
|
+
return buildSimpleOverviewProjectLines(snapshot, width);
|
|
723
|
+
}
|
|
724
|
+
return buildFireOverviewProjectLines(snapshot, width);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function buildOverviewIntentLines(snapshot, width, flow) {
|
|
728
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
729
|
+
if (effectiveFlow === 'aidlc') {
|
|
730
|
+
return buildAidlcOverviewIntentLines(snapshot, width);
|
|
731
|
+
}
|
|
732
|
+
if (effectiveFlow === 'simple') {
|
|
733
|
+
return buildSimpleOverviewIntentLines(snapshot, width);
|
|
734
|
+
}
|
|
735
|
+
return buildFireOverviewIntentLines(snapshot, width);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function buildOverviewStandardsLines(snapshot, width, flow) {
|
|
739
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
740
|
+
if (effectiveFlow === 'aidlc') {
|
|
741
|
+
return buildAidlcOverviewStandardsLines(snapshot, width);
|
|
742
|
+
}
|
|
743
|
+
if (effectiveFlow === 'simple') {
|
|
744
|
+
return buildSimpleOverviewStandardsLines(snapshot, width);
|
|
745
|
+
}
|
|
746
|
+
return buildFireOverviewStandardsLines(snapshot, width);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function getPanelTitles(flow, snapshot) {
|
|
750
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
751
|
+
if (effectiveFlow === 'aidlc') {
|
|
752
|
+
return {
|
|
753
|
+
current: 'Current Bolt',
|
|
754
|
+
files: 'Bolt Files',
|
|
755
|
+
pending: 'Queued Bolts',
|
|
756
|
+
completed: 'Recent Completed Bolts'
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
if (effectiveFlow === 'simple') {
|
|
760
|
+
return {
|
|
761
|
+
current: 'Current Spec',
|
|
762
|
+
files: 'Spec Files',
|
|
763
|
+
pending: 'Pending Specs',
|
|
764
|
+
completed: 'Recent Completed Specs'
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
current: 'Current Run',
|
|
769
|
+
files: 'Run Files',
|
|
770
|
+
pending: 'Pending Queue',
|
|
771
|
+
completed: 'Recent Completed Runs'
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
331
775
|
function allocateSingleColumnPanels(candidates, rowsBudget) {
|
|
332
776
|
const filtered = (candidates || []).filter(Boolean);
|
|
333
777
|
if (filtered.length === 0) {
|
|
@@ -634,6 +1078,7 @@ function createDashboardApp(deps) {
|
|
|
634
1078
|
const reservedRows = 2 + (showHelpLine ? 1 : 0) + (showErrorPanel ? 5 : 0) + (showErrorInline ? 1 : 0);
|
|
635
1079
|
const contentRowsBudget = Math.max(4, rows - reservedRows);
|
|
636
1080
|
const ultraCompact = rows <= 14;
|
|
1081
|
+
const panelTitles = getPanelTitles(flow, snapshot);
|
|
637
1082
|
|
|
638
1083
|
let panelCandidates;
|
|
639
1084
|
if (ui.view === 'overview') {
|
|
@@ -641,19 +1086,19 @@ function createDashboardApp(deps) {
|
|
|
641
1086
|
{
|
|
642
1087
|
key: 'project',
|
|
643
1088
|
title: 'Project + Workspace',
|
|
644
|
-
lines: buildOverviewProjectLines(snapshot, compactWidth),
|
|
1089
|
+
lines: buildOverviewProjectLines(snapshot, compactWidth, flow),
|
|
645
1090
|
borderColor: 'green'
|
|
646
1091
|
},
|
|
647
1092
|
{
|
|
648
1093
|
key: 'intent-status',
|
|
649
1094
|
title: 'Intent Status',
|
|
650
|
-
lines: buildOverviewIntentLines(snapshot, compactWidth),
|
|
1095
|
+
lines: buildOverviewIntentLines(snapshot, compactWidth, flow),
|
|
651
1096
|
borderColor: 'yellow'
|
|
652
1097
|
},
|
|
653
1098
|
{
|
|
654
1099
|
key: 'standards',
|
|
655
1100
|
title: 'Standards',
|
|
656
|
-
lines: buildOverviewStandardsLines(snapshot, compactWidth),
|
|
1101
|
+
lines: buildOverviewStandardsLines(snapshot, compactWidth, flow),
|
|
657
1102
|
borderColor: 'blue'
|
|
658
1103
|
}
|
|
659
1104
|
];
|
|
@@ -662,7 +1107,7 @@ function createDashboardApp(deps) {
|
|
|
662
1107
|
{
|
|
663
1108
|
key: 'stats',
|
|
664
1109
|
title: 'Stats',
|
|
665
|
-
lines: buildStatsLines(snapshot, compactWidth),
|
|
1110
|
+
lines: buildStatsLines(snapshot, compactWidth, flow),
|
|
666
1111
|
borderColor: 'magenta'
|
|
667
1112
|
},
|
|
668
1113
|
{
|
|
@@ -685,26 +1130,26 @@ function createDashboardApp(deps) {
|
|
|
685
1130
|
panelCandidates = [
|
|
686
1131
|
{
|
|
687
1132
|
key: 'current-run',
|
|
688
|
-
title:
|
|
689
|
-
lines: buildCurrentRunLines(snapshot, compactWidth),
|
|
1133
|
+
title: panelTitles.current,
|
|
1134
|
+
lines: buildCurrentRunLines(snapshot, compactWidth, flow),
|
|
690
1135
|
borderColor: 'green'
|
|
691
1136
|
},
|
|
692
1137
|
{
|
|
693
1138
|
key: 'run-files',
|
|
694
|
-
title:
|
|
695
|
-
lines: buildRunFilesLines(snapshot, compactWidth, icons),
|
|
1139
|
+
title: panelTitles.files,
|
|
1140
|
+
lines: buildRunFilesLines(snapshot, compactWidth, icons, flow),
|
|
696
1141
|
borderColor: 'yellow'
|
|
697
1142
|
},
|
|
698
1143
|
{
|
|
699
1144
|
key: 'pending',
|
|
700
|
-
title:
|
|
701
|
-
lines: buildPendingLines(snapshot, ui.runFilter, compactWidth),
|
|
1145
|
+
title: panelTitles.pending,
|
|
1146
|
+
lines: buildPendingLines(snapshot, ui.runFilter, compactWidth, flow),
|
|
702
1147
|
borderColor: 'yellow'
|
|
703
1148
|
},
|
|
704
1149
|
{
|
|
705
1150
|
key: 'completed',
|
|
706
|
-
title:
|
|
707
|
-
lines: buildCompletedLines(snapshot, ui.runFilter, compactWidth),
|
|
1151
|
+
title: panelTitles.completed,
|
|
1152
|
+
lines: buildCompletedLines(snapshot, ui.runFilter, compactWidth, flow),
|
|
708
1153
|
borderColor: 'blue'
|
|
709
1154
|
}
|
|
710
1155
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
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": {
|