specsmd 0.1.71 → 0.1.72

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.
@@ -710,12 +710,16 @@ function createSetDataMessage(data) {
710
710
  };
711
711
  }
712
712
 
713
- const flowInfo = {
714
- id: data.flow,
715
- displayName: flowDisplayName(data.flow),
716
- icon: flowIcon(data.flow),
717
- rootFolder: flowRootFolder(data.flow)
718
- };
713
+ const availableFlows = (data.availableFlows && data.availableFlows.length > 0
714
+ ? data.availableFlows
715
+ : [data.flow])
716
+ .filter(Boolean)
717
+ .map((flow) => ({
718
+ id: flow,
719
+ displayName: flowDisplayName(flow),
720
+ icon: flowIcon(flow),
721
+ rootFolder: flowRootFolder(flow)
722
+ }));
719
723
 
720
724
  if (data.flow === 'fire') {
721
725
  return {
@@ -737,7 +741,7 @@ function createSetDataMessage(data) {
737
741
  specsHtml: '',
738
742
  overviewHtml: '',
739
743
  fireData: buildFireViewData(data.snapshot),
740
- availableFlows: [flowInfo],
744
+ availableFlows,
741
745
  activeFlowId: data.flow
742
746
  };
743
747
  }
@@ -761,7 +765,7 @@ function createSetDataMessage(data) {
761
765
  },
762
766
  specsHtml: getSpecsViewHtml(webviewData),
763
767
  overviewHtml: getOverviewViewHtml(webviewData),
764
- availableFlows: [flowInfo],
768
+ availableFlows,
765
769
  activeFlowId: data.flow
766
770
  };
767
771
  }
@@ -6167,6 +6167,31 @@
6167
6167
 
6168
6168
  <!-- Resources Footer -->
6169
6169
  <div class="resources-footer">
6170
+ <div class="fabriqa-card">
6171
+ <div class="fabriqa-brand">
6172
+ <div class="fabriqa-mark">FA</div>
6173
+ <div>
6174
+ <div class="fabriqa-title">specs.md by Fabriqa.AI</div>
6175
+ <div class="fabriqa-subtitle">Spec-native agentic development environment</div>
6176
+ </div>
6177
+ </div>
6178
+ <div class="fabriqa-copy">
6179
+ Use Fabriqa.AI with your existing AI subscription to design, run, and reuse agentic workflows around your specs. It is free to try.
6180
+ </div>
6181
+ <div class="fabriqa-actions">
6182
+ <div class="fabriqa-link" @click=${() => this._openExternal("https://fabriqa.ai")}>Explore Fabriqa.AI</div>
6183
+ <div class="fabriqa-link secondary" @click=${() => this._openExternal("https://specs.md")}>Open specs.md</div>
6184
+ </div>
6185
+ </div>
6186
+ <div class="dashboard-tip">
6187
+ <div class="dashboard-tip-title">Did you know?</div>
6188
+ <div class="dashboard-tip-copy">
6189
+ You can use the specs.md dashboard outside VS Code and VS Code variants. Run <code>npx specsmd@latest dashboard</code> from your project folder.
6190
+ </div>
6191
+ <div class="fabriqa-actions">
6192
+ <div class="fabriqa-link secondary" @click=${() => this._openExternal("https://specs.md/getting-started/cli-dashboard")}>Dashboard docs</div>
6193
+ </div>
6194
+ </div>
6170
6195
  <div class="resources-title">Links</div>
6171
6196
  <div class="resources-links">
6172
6197
  <div class="resource-link" @click=${() => this._openExternal("https://specs.md")} title="Website">
@@ -6497,6 +6522,104 @@
6497
6522
  .feedback-link:hover {
6498
6523
  opacity: 0.8;
6499
6524
  }
6525
+
6526
+ .fabriqa-card {
6527
+ padding: 12px;
6528
+ margin-bottom: 12px;
6529
+ border: 1px solid rgba(249, 115, 22, 0.35);
6530
+ border-radius: 6px;
6531
+ background: linear-gradient(135deg, rgba(249, 115, 22, 0.12), rgba(34, 197, 94, 0.08));
6532
+ }
6533
+
6534
+ .fabriqa-brand {
6535
+ display: flex;
6536
+ align-items: center;
6537
+ gap: 10px;
6538
+ margin-bottom: 8px;
6539
+ }
6540
+
6541
+ .fabriqa-mark {
6542
+ display: flex;
6543
+ align-items: center;
6544
+ justify-content: center;
6545
+ flex: 0 0 30px;
6546
+ height: 30px;
6547
+ border-radius: 6px;
6548
+ background: var(--status-active);
6549
+ color: #ffffff;
6550
+ font-size: 11px;
6551
+ font-weight: 700;
6552
+ }
6553
+
6554
+ .fabriqa-title {
6555
+ font-size: 13px;
6556
+ font-weight: 700;
6557
+ color: var(--foreground);
6558
+ }
6559
+
6560
+ .fabriqa-subtitle,
6561
+ .fabriqa-copy,
6562
+ .dashboard-tip-copy {
6563
+ font-size: 11px;
6564
+ line-height: 1.45;
6565
+ color: var(--description-foreground);
6566
+ }
6567
+
6568
+ .fabriqa-copy {
6569
+ margin-bottom: 10px;
6570
+ }
6571
+
6572
+ .fabriqa-actions {
6573
+ display: flex;
6574
+ flex-wrap: wrap;
6575
+ gap: 8px;
6576
+ }
6577
+
6578
+ .fabriqa-link {
6579
+ display: inline-flex;
6580
+ align-items: center;
6581
+ justify-content: center;
6582
+ padding: 6px 9px;
6583
+ border-radius: 5px;
6584
+ background: var(--status-active);
6585
+ color: #ffffff;
6586
+ font-size: 11px;
6587
+ font-weight: 600;
6588
+ cursor: pointer;
6589
+ }
6590
+
6591
+ .fabriqa-link.secondary {
6592
+ border: 1px solid var(--border-color);
6593
+ background: var(--editor-background);
6594
+ color: var(--foreground);
6595
+ }
6596
+
6597
+ .fabriqa-link:hover {
6598
+ opacity: 0.86;
6599
+ }
6600
+
6601
+ .dashboard-tip {
6602
+ padding: 10px;
6603
+ margin-bottom: 12px;
6604
+ border: 1px solid var(--border-color);
6605
+ border-radius: 6px;
6606
+ background: var(--editor-background);
6607
+ }
6608
+
6609
+ .dashboard-tip-title {
6610
+ margin-bottom: 4px;
6611
+ color: var(--foreground);
6612
+ font-size: 11px;
6613
+ font-weight: 700;
6614
+ }
6615
+
6616
+ .dashboard-tip code {
6617
+ padding: 1px 4px;
6618
+ border-radius: 4px;
6619
+ background: var(--background);
6620
+ color: var(--foreground);
6621
+ font-family: var(--font-family);
6622
+ }
6500
6623
  `
6501
6624
  ];
6502
6625
  __decorateClass([
@@ -5,7 +5,7 @@ const { URL } = require('url');
5
5
  const { spawn } = require('child_process');
6
6
  const crypto = require('crypto');
7
7
  const { createWatchRuntime } = require('../runtime/watch-runtime');
8
- const { detectFlow } = require('../flow-detect');
8
+ const { detectAvailableFlows, detectFlow } = require('../flow-detect');
9
9
  const { loadWebDashboardData } = require('./snapshot');
10
10
 
11
11
  const PUBLIC_DIR = path.join(__dirname, 'public');
@@ -179,9 +179,13 @@ async function startDashboardWeb(options = {}) {
179
179
  const clients = new Set();
180
180
  let watcher = null;
181
181
  let lastData = null;
182
+ let activeFlow = options.flow || null;
182
183
 
183
184
  async function loadAndBroadcast() {
184
- lastData = await loadWebDashboardData({ workspacePath, flow: options.flow });
185
+ lastData = await loadWebDashboardData({ workspacePath, flow: activeFlow });
186
+ if (lastData.flow) {
187
+ activeFlow = lastData.flow;
188
+ }
185
189
  const message = lastData.webviewMessage || lastData;
186
190
  const payload = `event: message\ndata: ${JSON.stringify(message)}\n\n`;
187
191
  for (const client of clients) {
@@ -218,6 +222,19 @@ async function startDashboardWeb(options = {}) {
218
222
  sendJson(res, 200, { ok: true, data });
219
223
  return;
220
224
  }
225
+ if (message.type === 'switchFlow') {
226
+ const availableFlows = detectAvailableFlows(workspacePath);
227
+ const requestedFlow = typeof message.flowId === 'string' ? message.flowId : null;
228
+ if (requestedFlow && availableFlows.includes(requestedFlow)) {
229
+ activeFlow = requestedFlow;
230
+ } else if (availableFlows.length > 0) {
231
+ const currentIndex = availableFlows.indexOf(activeFlow);
232
+ activeFlow = availableFlows[(currentIndex + 1) % availableFlows.length];
233
+ }
234
+ const data = await loadAndBroadcast();
235
+ sendJson(res, 200, { ok: true, data });
236
+ return;
237
+ }
221
238
  if (message.type === 'openExternal') {
222
239
  const opened = openExternal(message.url);
223
240
  sendJson(res, opened ? 200 : 400, opened
@@ -308,14 +325,9 @@ async function startDashboardWeb(options = {}) {
308
325
 
309
326
  const initialData = await loadAndBroadcast();
310
327
  if (options.watch !== false && initialData.flow) {
311
- let detection = null;
312
- try {
313
- detection = detectFlow(workspacePath, options.flow);
314
- } catch {
315
- detection = null;
316
- }
317
- const flow = detection?.flow || initialData.flow;
318
- const roots = buildWatchRoots(workspacePath, flow).filter((root) => fs.existsSync(root));
328
+ const roots = detectAvailableFlows(workspacePath)
329
+ .flatMap((flow) => buildWatchRoots(workspacePath, flow))
330
+ .filter((root) => fs.existsSync(root));
319
331
  if (roots.length > 0) {
320
332
  watcher = createWatchRuntime({
321
333
  rootPaths: roots,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.71",
3
+ "version": "0.1.72",
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": {