specsmd 0.1.69 → 0.1.71

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.
@@ -114,6 +114,25 @@ function normalizeTimestamp(value) {
114
114
  return parsed.toISOString();
115
115
  }
116
116
 
117
+ function compareByCreatedAtThenId(a, b) {
118
+ const aTime = a?.createdAt ? Date.parse(a.createdAt) : NaN;
119
+ const bTime = b?.createdAt ? Date.parse(b.createdAt) : NaN;
120
+ const aHasTime = !Number.isNaN(aTime);
121
+ const bHasTime = !Number.isNaN(bTime);
122
+
123
+ if (aHasTime && bHasTime && aTime !== bTime) {
124
+ return aTime - bTime;
125
+ }
126
+ if (aHasTime && !bHasTime) {
127
+ return -1;
128
+ }
129
+ if (!aHasTime && bHasTime) {
130
+ return 1;
131
+ }
132
+
133
+ return String(a?.id || '').localeCompare(String(b?.id || ''));
134
+ }
135
+
117
136
  function parseIntentFolderName(folderName) {
118
137
  const match = String(folderName).match(/^(\d{3})-(.+)$/);
119
138
  if (!match) {
@@ -272,7 +291,8 @@ function parseIntent(intentPath, warnings) {
272
291
  completedStories: storyStats.completed,
273
292
  inProgressStories: storyStats.inProgress,
274
293
  pendingStories: storyStats.pending,
275
- blockedStories: storyStats.blocked
294
+ blockedStories: storyStats.blocked,
295
+ createdAt: normalizeTimestamp(requirementsFrontmatter.created)
276
296
  };
277
297
  }
278
298
 
@@ -486,7 +506,7 @@ function parseAidlcDashboard(workspacePath) {
486
506
  const intents = intentFolders
487
507
  .map((intentFolder) => parseIntent(path.join(intentsPath, intentFolder), warnings))
488
508
  .filter(Boolean)
489
- .sort((a, b) => a.id.localeCompare(b.id));
509
+ .sort(compareByCreatedAtThenId);
490
510
 
491
511
  if (intentFolders.length === 0) {
492
512
  warnings.push('No intents found under memory-bank/intents.');
@@ -350,7 +350,8 @@ function buildPendingItems(intents) {
350
350
  mode: item.mode,
351
351
  complexity: item.complexity,
352
352
  dependencies: item.dependencies || [],
353
- filePath: item.filePath
353
+ filePath: item.filePath,
354
+ createdAt: item.createdAt
354
355
  });
355
356
  }
356
357
  }
@@ -360,6 +361,19 @@ function buildPendingItems(intents) {
360
361
  if (depDiff !== 0) {
361
362
  return depDiff;
362
363
  }
364
+ const aTime = a.createdAt ? Date.parse(a.createdAt) : NaN;
365
+ const bTime = b.createdAt ? Date.parse(b.createdAt) : NaN;
366
+ const aHasTime = !Number.isNaN(aTime);
367
+ const bHasTime = !Number.isNaN(bTime);
368
+ if (aHasTime && bHasTime && aTime !== bTime) {
369
+ return aTime - bTime;
370
+ }
371
+ if (aHasTime && !bHasTime) {
372
+ return -1;
373
+ }
374
+ if (!aHasTime && bHasTime) {
375
+ return 1;
376
+ }
363
377
  return a.id.localeCompare(b.id);
364
378
  });
365
379
 
@@ -12,7 +12,8 @@ const {
12
12
  calculateStats,
13
13
  parseDependencies,
14
14
  buildPendingItems,
15
- normalizeRunWorkItem
15
+ normalizeRunWorkItem,
16
+ normalizeTimestamp
16
17
  } = require('./model');
17
18
 
18
19
  const STANDARD_TYPES = [
@@ -80,6 +81,46 @@ function getFirstStringValue(record, keys) {
80
81
  return undefined;
81
82
  }
82
83
 
84
+ function compareByCreatedAtThenId(a, b) {
85
+ const aTime = a?.createdAt ? Date.parse(a.createdAt) : NaN;
86
+ const bTime = b?.createdAt ? Date.parse(b.createdAt) : NaN;
87
+ const aHasTime = !Number.isNaN(aTime);
88
+ const bHasTime = !Number.isNaN(bTime);
89
+
90
+ if (aHasTime && bHasTime && aTime !== bTime) {
91
+ return aTime - bTime;
92
+ }
93
+ if (aHasTime && !bHasTime) {
94
+ return -1;
95
+ }
96
+ if (!aHasTime && bHasTime) {
97
+ return 1;
98
+ }
99
+
100
+ return String(a?.id || '').localeCompare(String(b?.id || ''));
101
+ }
102
+
103
+ function compareRunDatesDesc(a, b) {
104
+ const aDate = a?.completedAt || a?.startedAt;
105
+ const bDate = b?.completedAt || b?.startedAt;
106
+ const aTime = aDate ? Date.parse(aDate) : NaN;
107
+ const bTime = bDate ? Date.parse(bDate) : NaN;
108
+ const aHasTime = !Number.isNaN(aTime);
109
+ const bHasTime = !Number.isNaN(bTime);
110
+
111
+ if (aHasTime && bHasTime && aTime !== bTime) {
112
+ return bTime - aTime;
113
+ }
114
+ if (aHasTime && !bHasTime) {
115
+ return -1;
116
+ }
117
+ if (!aHasTime && bHasTime) {
118
+ return 1;
119
+ }
120
+
121
+ return String(b?.id || '').localeCompare(String(a?.id || ''));
122
+ }
123
+
83
124
  function parseRunLog(runLogPath) {
84
125
  const content = readFileSafe(runLogPath);
85
126
  if (!content) {
@@ -210,8 +251,8 @@ function scanWorkItems(intentPath, intentId, stateWorkItems, warnings) {
210
251
  filePath,
211
252
  description: typeof frontmatter.description === 'string' ? frontmatter.description : undefined,
212
253
  dependencies,
213
- createdAt: typeof frontmatter.created === 'string' ? frontmatter.created : undefined,
214
- completedAt: typeof frontmatter.completed_at === 'string' ? frontmatter.completed_at : undefined
254
+ createdAt: normalizeTimestamp(frontmatter.created),
255
+ completedAt: normalizeTimestamp(frontmatter.completed_at)
215
256
  };
216
257
  });
217
258
  }
@@ -252,8 +293,8 @@ function scanIntents(rootPath, normalizedState, warnings) {
252
293
  filePath: briefPath,
253
294
  description: typeof frontmatter.description === 'string' ? frontmatter.description : undefined,
254
295
  workItems,
255
- createdAt: typeof frontmatter.created === 'string' ? frontmatter.created : undefined,
256
- completedAt: typeof frontmatter.completed_at === 'string' ? frontmatter.completed_at : undefined
296
+ createdAt: normalizeTimestamp(frontmatter.created),
297
+ completedAt: normalizeTimestamp(frontmatter.completed_at)
257
298
  };
258
299
  });
259
300
  }
@@ -325,7 +366,8 @@ function buildActiveRuns(runs, normalizedState) {
325
366
 
326
367
  return (normalizedState.runs?.active || [])
327
368
  .map((active) => byId.get(active.id) || null)
328
- .filter(Boolean);
369
+ .filter(Boolean)
370
+ .sort(compareRunDatesDesc);
329
371
  }
330
372
 
331
373
  function buildCompletedRuns(runs) {
@@ -430,8 +472,8 @@ function parseFireDashboard(workspacePath) {
430
472
 
431
473
  const warnings = [];
432
474
  const normalizedState = normalizeState(rawState);
433
- const intents = scanIntents(rootPath, normalizedState, warnings);
434
- const runs = scanRuns(rootPath, normalizedState);
475
+ const intents = scanIntents(rootPath, normalizedState, warnings).sort(compareByCreatedAtThenId);
476
+ const runs = scanRuns(rootPath, normalizedState).sort(compareRunDatesDesc);
435
477
  const activeRuns = buildActiveRuns(runs, normalizedState);
436
478
  const completedRuns = buildCompletedRuns(runs);
437
479
  const standards = scanStandards(rootPath);
@@ -248,7 +248,8 @@ function buildSpecsData(snapshot) {
248
248
  path: intent.path,
249
249
  storiesComplete: units.reduce((sum, unit) => sum + unit.storiesComplete, 0),
250
250
  storiesTotal: units.reduce((sum, unit) => sum + unit.storiesTotal, 0),
251
- units
251
+ units,
252
+ createdAt: intent.createdAt
252
253
  };
253
254
  });
254
255
 
@@ -400,7 +401,8 @@ function buildFireViewData(snapshot) {
400
401
  mode: normalizeFireMode(item.mode),
401
402
  complexity: normalizeFireComplexity(item.complexity),
402
403
  filePath: item.filePath,
403
- dependencies: item.dependencies || []
404
+ dependencies: item.dependencies || [],
405
+ createdAt: item.createdAt
404
406
  }));
405
407
 
406
408
  const completedRuns = (snapshot.completedRuns || []).map((run) => ({
@@ -418,13 +420,15 @@ function buildFireViewData(snapshot) {
418
420
  status: normalizeFireStatus(intent.status),
419
421
  filePath: intent.filePath,
420
422
  description: intent.description,
423
+ createdAt: intent.createdAt,
421
424
  workItems: (intent.workItems || []).map((item) => ({
422
425
  id: item.id,
423
426
  title: item.title || item.id,
424
427
  status: normalizeFireStatus(item.status),
425
428
  mode: normalizeFireMode(item.mode),
426
429
  complexity: normalizeFireComplexity(item.complexity),
427
- filePath: item.filePath
430
+ filePath: item.filePath,
431
+ createdAt: item.createdAt
428
432
  }))
429
433
  }));
430
434
 
@@ -634,6 +638,50 @@ function getOverviewViewHtml(data) {
634
638
  `).join('') : '<div class="empty-state"><div class="empty-state-text">No standards defined</div></div>'}
635
639
  </div>
636
640
  </div>
641
+ <div class="overview-resources-footer">
642
+ <div class="overview-fabriqa-card">
643
+ <div class="overview-fabriqa-brand">
644
+ <div class="overview-fabriqa-mark">FA</div>
645
+ <div>
646
+ <div class="overview-fabriqa-title">specs.md by Fabriqa.AI</div>
647
+ <div class="overview-fabriqa-subtitle">Spec-native agentic development environment</div>
648
+ </div>
649
+ </div>
650
+ <div class="overview-fabriqa-copy">
651
+ Use Fabriqa.AI with your existing AI subscription to design, run, and reuse agentic workflows around your specs. It is free to try.
652
+ </div>
653
+ <div class="overview-fabriqa-actions">
654
+ <div class="overview-fabriqa-link" data-url="https://fabriqa.ai">Explore Fabriqa.AI</div>
655
+ <div class="overview-fabriqa-link secondary" data-url="https://specs.md">Open specs.md</div>
656
+ </div>
657
+ </div>
658
+ <div class="overview-dashboard-tip">
659
+ <div>
660
+ <div class="overview-dashboard-title">Did you know?</div>
661
+ <div class="overview-dashboard-copy">
662
+ You can use the specsmd dashboard outside VS Code and VS Code variants. Run this from your project folder:
663
+ </div>
664
+ <code>npx specsmd@latest dashboard</code>
665
+ </div>
666
+ <div class="overview-fabriqa-link secondary" data-url="https://specs.md/getting-started/cli-dashboard">Dashboard docs</div>
667
+ </div>
668
+ <div class="overview-resources-title">Community</div>
669
+ <div class="overview-resources-links">
670
+ <div class="overview-resource-link" data-url="https://discord.specs.md" title="Discord">
671
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
672
+ <path fill="currentColor" d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
673
+ </svg>
674
+ </div>
675
+ <div class="overview-resource-link" data-url="https://x.com/specsmd" title="X (Twitter)">
676
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
677
+ <path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
678
+ </svg>
679
+ </div>
680
+ </div>
681
+ <div class="overview-feedback-message">
682
+ We're new! Help us improve — <span class="overview-feedback-link" data-url="https://specs.md/feedback">share your feedback</span>
683
+ </div>
684
+ </div>
637
685
  </div>`;
638
686
  }
639
687
 
@@ -28,6 +28,103 @@
28
28
  document.documentElement.style.colorScheme = theme;
29
29
  }
30
30
 
31
+ function copyText(text) {
32
+ if (navigator.clipboard && navigator.clipboard.writeText) {
33
+ return navigator.clipboard.writeText(text);
34
+ }
35
+
36
+ var textarea = document.createElement('textarea');
37
+ textarea.value = text;
38
+ textarea.setAttribute('readonly', 'readonly');
39
+ textarea.style.position = 'fixed';
40
+ textarea.style.opacity = '0';
41
+ document.body.appendChild(textarea);
42
+ textarea.select();
43
+
44
+ try {
45
+ document.execCommand('copy');
46
+ return Promise.resolve();
47
+ } catch (error) {
48
+ return Promise.reject(error);
49
+ } finally {
50
+ textarea.remove();
51
+ }
52
+ }
53
+
54
+ function closeCommandDialog() {
55
+ var existing = document.querySelector('.specsmd-command-dialog');
56
+ if (existing) {
57
+ existing.remove();
58
+ }
59
+ }
60
+
61
+ function showCommandDialog(command) {
62
+ closeCommandDialog();
63
+
64
+ var overlay = document.createElement('div');
65
+ overlay.className = 'specsmd-command-dialog';
66
+ overlay.setAttribute('role', 'dialog');
67
+ overlay.setAttribute('aria-modal', 'true');
68
+ overlay.setAttribute('aria-label', 'Start FIRE run command');
69
+
70
+ var panel = document.createElement('div');
71
+ panel.className = 'specsmd-command-dialog-panel';
72
+
73
+ var title = document.createElement('div');
74
+ title.className = 'specsmd-command-dialog-title';
75
+ title.textContent = 'Start FIRE run';
76
+
77
+ var description = document.createElement('div');
78
+ description.className = 'specsmd-command-dialog-description';
79
+ description.textContent = 'Run this command from your project folder.';
80
+
81
+ var commandBox = document.createElement('textarea');
82
+ commandBox.className = 'specsmd-command-dialog-command';
83
+ commandBox.value = command;
84
+ commandBox.readOnly = true;
85
+ commandBox.rows = 2;
86
+
87
+ var actions = document.createElement('div');
88
+ actions.className = 'specsmd-command-dialog-actions';
89
+
90
+ var copyButton = document.createElement('button');
91
+ copyButton.type = 'button';
92
+ copyButton.className = 'specsmd-command-dialog-copy';
93
+ copyButton.textContent = 'Copy Command';
94
+
95
+ var closeButton = document.createElement('button');
96
+ closeButton.type = 'button';
97
+ closeButton.className = 'specsmd-command-dialog-close';
98
+ closeButton.textContent = 'Close';
99
+
100
+ copyButton.addEventListener('click', function () {
101
+ copyText(command).then(function () {
102
+ copyButton.textContent = 'Copied';
103
+ }).catch(function () {
104
+ commandBox.focus();
105
+ commandBox.select();
106
+ });
107
+ });
108
+
109
+ closeButton.addEventListener('click', closeCommandDialog);
110
+ overlay.addEventListener('click', function (event) {
111
+ if (event.target === overlay) {
112
+ closeCommandDialog();
113
+ }
114
+ });
115
+
116
+ actions.appendChild(copyButton);
117
+ actions.appendChild(closeButton);
118
+ panel.appendChild(title);
119
+ panel.appendChild(description);
120
+ panel.appendChild(commandBox);
121
+ panel.appendChild(actions);
122
+ overlay.appendChild(panel);
123
+ document.body.appendChild(overlay);
124
+ commandBox.focus();
125
+ commandBox.select();
126
+ }
127
+
31
128
  document.documentElement.dataset.host = 'dashboard-web';
32
129
  applyTheme(readTheme());
33
130
 
@@ -44,4 +141,18 @@
44
141
  document.documentElement.dataset.loaded = 'true';
45
142
  }
46
143
  });
144
+
145
+ window.addEventListener('specsmd-dashboard-command', function (event) {
146
+ if (!event.detail || !event.detail.command) {
147
+ return;
148
+ }
149
+
150
+ showCommandDialog(String(event.detail.command));
151
+ });
152
+
153
+ window.addEventListener('keydown', function (event) {
154
+ if (event.key === 'Escape') {
155
+ closeCommandDialog();
156
+ }
157
+ });
47
158
  }());
@@ -64,3 +64,77 @@ specsmd-app {
64
64
  width: 100vw;
65
65
  height: 100vh;
66
66
  }
67
+
68
+ .specsmd-command-dialog {
69
+ position: fixed;
70
+ inset: 0;
71
+ z-index: 1000;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ padding: 24px;
76
+ background: rgba(0, 0, 0, 0.45);
77
+ }
78
+
79
+ .specsmd-command-dialog-panel {
80
+ width: min(520px, 100%);
81
+ padding: 16px;
82
+ border: 1px solid var(--vscode-sideBarSectionHeader-border);
83
+ border-radius: 8px;
84
+ background: var(--vscode-sideBar-background);
85
+ color: var(--vscode-foreground);
86
+ box-shadow: 0 18px 56px rgba(0, 0, 0, 0.28);
87
+ }
88
+
89
+ .specsmd-command-dialog-title {
90
+ font-size: 15px;
91
+ font-weight: 700;
92
+ margin-bottom: 4px;
93
+ }
94
+
95
+ .specsmd-command-dialog-description {
96
+ margin-bottom: 12px;
97
+ color: var(--vscode-descriptionForeground);
98
+ font-size: 12px;
99
+ }
100
+
101
+ .specsmd-command-dialog-command {
102
+ box-sizing: border-box;
103
+ width: 100%;
104
+ min-height: 58px;
105
+ resize: vertical;
106
+ padding: 10px;
107
+ border: 1px solid var(--vscode-input-border);
108
+ border-radius: 6px;
109
+ background: var(--vscode-input-background);
110
+ color: var(--vscode-foreground);
111
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
112
+ }
113
+
114
+ .specsmd-command-dialog-actions {
115
+ display: flex;
116
+ justify-content: flex-end;
117
+ gap: 8px;
118
+ margin-top: 12px;
119
+ }
120
+
121
+ .specsmd-command-dialog-actions button {
122
+ min-width: 88px;
123
+ padding: 7px 10px;
124
+ border: 1px solid var(--vscode-input-border);
125
+ border-radius: 6px;
126
+ color: var(--vscode-foreground);
127
+ background: transparent;
128
+ font: inherit;
129
+ cursor: pointer;
130
+ }
131
+
132
+ .specsmd-command-dialog-actions button:hover {
133
+ background: var(--vscode-list-hoverBackground);
134
+ }
135
+
136
+ .specsmd-command-dialog-copy {
137
+ border-color: var(--vscode-button-background) !important;
138
+ background: var(--vscode-button-background) !important;
139
+ color: var(--vscode-button-foreground) !important;
140
+ }
@@ -6644,6 +6644,15 @@
6644
6644
  connectEvents();
6645
6645
  return {
6646
6646
  postMessage(message) {
6647
+ if (isStandaloneStartRunMessage(message)) {
6648
+ window.dispatchEvent(new CustomEvent("specsmd-dashboard-command", {
6649
+ detail: {
6650
+ command: buildFireStartRunCommand(message.workItemIds),
6651
+ workItemIds: message.workItemIds
6652
+ }
6653
+ }));
6654
+ return;
6655
+ }
6647
6656
  fetch("/api/message", {
6648
6657
  method: "POST",
6649
6658
  headers: { "content-type": "application/json" },
@@ -6665,6 +6674,13 @@
6665
6674
  }
6666
6675
  };
6667
6676
  }
6677
+ function isStandaloneStartRunMessage(message) {
6678
+ return typeof message === "object" && message !== null && message.type === "startRun" && Array.isArray(message.workItemIds);
6679
+ }
6680
+ function buildFireStartRunCommand(workItemIds) {
6681
+ const ids = workItemIds.map((id) => String(id).trim()).filter(Boolean);
6682
+ return ["/specsmd-fire-builder", ...ids].join(" ");
6683
+ }
6668
6684
  var vscode = typeof acquireVsCodeApi === "function" ? acquireVsCodeApi() : createStandaloneApi();
6669
6685
 
6670
6686
  // src/webview/components/app.ts
@@ -6888,7 +6904,7 @@
6888
6904
  }
6889
6905
  });
6890
6906
  });
6891
- overviewView.querySelectorAll(".overview-resource-link").forEach((link) => {
6907
+ overviewView.querySelectorAll(".overview-resource-link, .overview-fabriqa-link").forEach((link) => {
6892
6908
  const htmlLink = link;
6893
6909
  link.addEventListener("click", () => {
6894
6910
  const url = htmlLink.dataset.url;
@@ -7105,6 +7121,15 @@
7105
7121
  }
7106
7122
  }
7107
7123
  _handleFireFilterChange(e7) {
7124
+ if (this._fireData) {
7125
+ this._fireData = {
7126
+ ...this._fireData,
7127
+ intentsData: {
7128
+ ...this._fireData.intentsData,
7129
+ filter: e7.detail.filter
7130
+ }
7131
+ };
7132
+ }
7108
7133
  vscode.postMessage({ type: "fireIntentsFilter", filter: e7.detail.filter });
7109
7134
  }
7110
7135
  _handleFireToggleExpand(e7) {
@@ -7200,18 +7225,22 @@
7200
7225
  ...BaseElement.baseStyles,
7201
7226
  i`
7202
7227
  :host {
7203
- display: flex;
7204
- flex-direction: column;
7228
+ display: block;
7205
7229
  height: 100vh;
7206
7230
  overflow: hidden;
7231
+ position: relative;
7207
7232
  background: var(--background);
7208
7233
  }
7209
7234
 
7210
7235
  .shell {
7211
7236
  display: flex;
7212
7237
  flex-direction: column;
7213
- flex: 1;
7238
+ position: absolute;
7239
+ inset: 0;
7240
+ width: 100%;
7241
+ height: 100%;
7214
7242
  min-height: 0;
7243
+ overflow: hidden;
7215
7244
  }
7216
7245
 
7217
7246
  .shell-chrome {
@@ -7310,10 +7339,12 @@
7310
7339
  flex-direction: column;
7311
7340
  flex: 1;
7312
7341
  min-height: 0;
7342
+ overflow: hidden;
7313
7343
  }
7314
7344
 
7315
7345
  .view-container {
7316
7346
  flex: 1;
7347
+ min-height: 0;
7317
7348
  overflow-y: auto;
7318
7349
  display: none;
7319
7350
  }
@@ -7321,6 +7352,7 @@
7321
7352
  .view-container.active {
7322
7353
  display: flex;
7323
7354
  flex-direction: column;
7355
+ min-height: 0;
7324
7356
  }
7325
7357
 
7326
7358
  bolts-view {
@@ -7745,6 +7777,114 @@
7745
7777
  border-top: 1px solid var(--border-color);
7746
7778
  }
7747
7779
 
7780
+ .overview-fabriqa-card {
7781
+ margin-bottom: 14px;
7782
+ padding: 14px;
7783
+ border: 1px solid var(--border-color);
7784
+ border-radius: 8px;
7785
+ background: var(--editor-background);
7786
+ }
7787
+
7788
+ .overview-fabriqa-brand {
7789
+ display: flex;
7790
+ align-items: center;
7791
+ gap: 10px;
7792
+ margin-bottom: 10px;
7793
+ }
7794
+
7795
+ .overview-fabriqa-mark {
7796
+ width: 34px;
7797
+ height: 34px;
7798
+ border-radius: 8px;
7799
+ display: inline-flex;
7800
+ align-items: center;
7801
+ justify-content: center;
7802
+ background: var(--accent-primary);
7803
+ color: #ffffff;
7804
+ font-size: 12px;
7805
+ font-weight: 700;
7806
+ flex-shrink: 0;
7807
+ }
7808
+
7809
+ .overview-fabriqa-title {
7810
+ font-size: 13px;
7811
+ font-weight: 700;
7812
+ color: var(--foreground);
7813
+ line-height: 1.25;
7814
+ }
7815
+
7816
+ .overview-fabriqa-subtitle,
7817
+ .overview-fabriqa-copy,
7818
+ .overview-dashboard-copy {
7819
+ color: var(--description-foreground);
7820
+ font-size: 11px;
7821
+ line-height: 1.45;
7822
+ }
7823
+
7824
+ .overview-fabriqa-copy {
7825
+ margin-bottom: 12px;
7826
+ }
7827
+
7828
+ .overview-fabriqa-actions {
7829
+ display: flex;
7830
+ flex-wrap: wrap;
7831
+ gap: 8px;
7832
+ }
7833
+
7834
+ .overview-fabriqa-link {
7835
+ display: inline-flex;
7836
+ align-items: center;
7837
+ justify-content: center;
7838
+ min-height: 30px;
7839
+ padding: 0 10px;
7840
+ border-radius: 6px;
7841
+ background: var(--accent-primary);
7842
+ color: #ffffff;
7843
+ font-size: 11px;
7844
+ font-weight: 700;
7845
+ cursor: pointer;
7846
+ }
7847
+
7848
+ .overview-fabriqa-link.secondary {
7849
+ background: var(--vscode-input-background);
7850
+ color: var(--foreground);
7851
+ border: 1px solid var(--border-color);
7852
+ }
7853
+
7854
+ .overview-fabriqa-link:hover {
7855
+ opacity: 0.88;
7856
+ }
7857
+
7858
+ .overview-dashboard-tip {
7859
+ display: flex;
7860
+ align-items: flex-start;
7861
+ justify-content: space-between;
7862
+ gap: 12px;
7863
+ margin-bottom: 14px;
7864
+ padding: 12px;
7865
+ border: 1px solid var(--border-color);
7866
+ border-radius: 8px;
7867
+ background: var(--vscode-input-background);
7868
+ }
7869
+
7870
+ .overview-dashboard-title {
7871
+ margin-bottom: 4px;
7872
+ color: var(--foreground);
7873
+ font-size: 12px;
7874
+ font-weight: 700;
7875
+ }
7876
+
7877
+ .overview-dashboard-tip code {
7878
+ display: inline-block;
7879
+ margin-top: 8px;
7880
+ padding: 5px 7px;
7881
+ border-radius: 4px;
7882
+ background: var(--editor-background);
7883
+ color: var(--foreground);
7884
+ font-size: 10px;
7885
+ white-space: nowrap;
7886
+ }
7887
+
7748
7888
  .overview-resources-title {
7749
7889
  font-size: 9px;
7750
7890
  font-weight: 600;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.69",
3
+ "version": "0.1.71",
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": {