specweave 1.0.262 → 1.0.264

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.
Files changed (40) hide show
  1. package/CLAUDE.md +31 -27
  2. package/dist/src/cli/commands/docs.js +4 -4
  3. package/dist/src/cli/commands/docs.js.map +1 -1
  4. package/dist/src/dashboard/server/dashboard-server.d.ts.map +1 -1
  5. package/dist/src/dashboard/server/dashboard-server.js +2 -1
  6. package/dist/src/dashboard/server/dashboard-server.js.map +1 -1
  7. package/dist/src/dashboard/server/data/dashboard-data-aggregator.d.ts +9 -1
  8. package/dist/src/dashboard/server/data/dashboard-data-aggregator.d.ts.map +1 -1
  9. package/dist/src/dashboard/server/data/dashboard-data-aggregator.js +140 -13
  10. package/dist/src/dashboard/server/data/dashboard-data-aggregator.js.map +1 -1
  11. package/dist/src/dashboard/server/data/plugin-scanner.d.ts +1 -1
  12. package/dist/src/dashboard/server/data/plugin-scanner.d.ts.map +1 -1
  13. package/dist/src/dashboard/server/data/plugin-scanner.js +2 -2
  14. package/dist/src/dashboard/server/data/plugin-scanner.js.map +1 -1
  15. package/dist/src/utils/docs-preview/config-generator.d.ts +14 -6
  16. package/dist/src/utils/docs-preview/config-generator.d.ts.map +1 -1
  17. package/dist/src/utils/docs-preview/config-generator.js +504 -126
  18. package/dist/src/utils/docs-preview/config-generator.js.map +1 -1
  19. package/dist/src/utils/docs-preview/docusaurus-setup.d.ts +1 -1
  20. package/dist/src/utils/docs-preview/docusaurus-setup.d.ts.map +1 -1
  21. package/dist/src/utils/docs-preview/docusaurus-setup.js +92 -48
  22. package/dist/src/utils/docs-preview/docusaurus-setup.js.map +1 -1
  23. package/dist/src/utils/docs-preview/index.d.ts +2 -1
  24. package/dist/src/utils/docs-preview/index.d.ts.map +1 -1
  25. package/dist/src/utils/docs-preview/index.js +2 -1
  26. package/dist/src/utils/docs-preview/index.js.map +1 -1
  27. package/dist/src/utils/docs-preview/project-detector.d.ts +16 -0
  28. package/dist/src/utils/docs-preview/project-detector.d.ts.map +1 -0
  29. package/dist/src/utils/docs-preview/project-detector.js +215 -0
  30. package/dist/src/utils/docs-preview/project-detector.js.map +1 -0
  31. package/dist/src/utils/docs-preview/types.d.ts +26 -0
  32. package/dist/src/utils/docs-preview/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/plugins/specweave/hooks/hooks.json +9 -0
  35. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +83 -0
  36. package/plugins/specweave/scripts/progress.js +148 -50
  37. package/plugins/specweave/scripts/read-progress.sh +128 -52
  38. package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +23 -17
  39. package/plugins/specweave/scripts/track-analytics.sh +4 -0
  40. package/plugins/specweave/skills/progress/SKILL.md +7 -21
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Docusaurus configuration generator
3
- * Generates docusaurus.config.js with sensible defaults
3
+ * Generates docusaurus.config.js with professional defaults
4
4
  */
5
5
  import * as path from 'path';
6
6
  import * as fs from '../../utils/fs-native.js';
@@ -8,10 +8,23 @@ import * as fs from '../../utils/fs-native.js';
8
8
  * Generate docusaurus.config.js content
9
9
  */
10
10
  export function generateDocusaurusConfig(config) {
11
- const { title, tagline, url, baseUrl, docsPath, theme = 'default' } = config;
11
+ const { title, tagline, url, baseUrl, docsPath } = config;
12
+ const projectName = config.projectMetadata?.name || title;
13
+ // Generate footer links from detected categories (max 4)
14
+ const categories = config.projectMetadata?.categories || [];
15
+ const footerItems = categories.slice(0, 4).map(cat => ` {\n label: '${cat.label}',\n to: '/${cat.id}',\n }`).join(',\n');
16
+ const footerLinksBlock = footerItems
17
+ ? `[
18
+ {
19
+ title: 'Documentation',
20
+ items: [
21
+ ${footerItems},
22
+ ],
23
+ },
24
+ ]`
25
+ : `[]`;
12
26
  return `// @ts-check
13
- // This is a Docusaurus site configuration object.
14
- // Auto-generated by SpecWeave docs-preview plugin
27
+ // Auto-generated documentation site configuration
15
28
  // Generated: ${new Date().toISOString()}
16
29
 
17
30
  import {themes as prismThemes} from 'prism-react-renderer';
@@ -19,17 +32,13 @@ import {themes as prismThemes} from 'prism-react-renderer';
19
32
  /** @type {import('@docusaurus/types').Config} */
20
33
  const config = {
21
34
  title: '${title}',
22
- tagline: '${tagline}',
23
- favicon: 'img/favicon.ico',
35
+ tagline: '${escapeQuotes(tagline)}',
36
+ favicon: 'img/favicon.svg',
24
37
 
25
38
  // Production URL
26
39
  url: '${url}',
27
40
  baseUrl: '${baseUrl}',
28
41
 
29
- // GitHub Pages deployment config
30
- organizationName: 'your-org',
31
- projectName: 'your-repo',
32
-
33
42
  onBrokenLinks: 'warn',
34
43
  onBrokenMarkdownLinks: 'warn',
35
44
  onBrokenAnchors: 'warn',
@@ -49,17 +58,14 @@ const config = {
49
58
  routeBasePath: '/',
50
59
  path: '${docsPath}',
51
60
  sidebarPath: './sidebars.js',
52
- editUrl: undefined, // Disable "Edit this page" for internal docs
53
- // CRITICAL: Override default exclude to include _ folders (_archive, _orphans, etc.)
54
- // Docusaurus default excludes: ['**/_*.{js,jsx,ts,tsx,md,mdx}', '**/_*/**', '**/__tests__/**']
55
- // We want ALL folders and files visible, so we set exclude to empty array
61
+ editUrl: undefined,
56
62
  exclude: [],
57
63
  remarkPlugins: [],
58
64
  rehypePlugins: [],
59
65
  beforeDefaultRemarkPlugins: [],
60
66
  beforeDefaultRehypePlugins: [],
61
67
  },
62
- blog: false, // Disable blog
68
+ blog: false,
63
69
  theme: {
64
70
  customCss: './src/css/custom.css',
65
71
  },
@@ -70,13 +76,10 @@ const config = {
70
76
  themeConfig:
71
77
  /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
72
78
  ({
73
- // Social card image (optional)
74
- image: 'img/docusaurus-social-card.jpg',
75
-
76
79
  navbar: {
77
80
  title: '${title}',
78
81
  logo: {
79
- alt: '${title} Logo',
82
+ alt: '${projectName} Logo',
80
83
  src: 'img/logo.svg',
81
84
  },
82
85
  items: [
@@ -91,22 +94,8 @@ const config = {
91
94
 
92
95
  footer: {
93
96
  style: 'dark',
94
- links: [
95
- {
96
- title: 'Documentation',
97
- items: [
98
- {
99
- label: 'Strategy',
100
- to: '/strategy',
101
- },
102
- {
103
- label: 'Architecture',
104
- to: '/architecture',
105
- },
106
- ],
107
- },
108
- ],
109
- copyright: \`Copyright © \${new Date().getFullYear()} SpecWeave. Built with Docusaurus.\`,
97
+ links: ${footerLinksBlock},
98
+ copyright: \`Copyright \u00a9 \${new Date().getFullYear()} ${escapeQuotes(projectName)}. Built with Docusaurus.\`,
110
99
  },
111
100
 
112
101
  prism: {
@@ -114,10 +103,14 @@ const config = {
114
103
  darkTheme: prismThemes.dracula,
115
104
  additionalLanguages: ['bash', 'typescript', 'javascript', 'json', 'yaml'],
116
105
  },
106
+
107
+ colorMode: {
108
+ defaultMode: 'light',
109
+ respectPrefersColorScheme: true,
110
+ },
117
111
  }),
118
112
 
119
113
  // Mermaid diagrams support
120
- // Use 'md' format since SpecWeave docs are .md files, not .mdx
121
114
  markdown: {
122
115
  mermaid: true,
123
116
  format: 'md',
@@ -142,7 +135,7 @@ export async function writeDocusaurusConfig(targetDir, config) {
142
135
  */
143
136
  export function generatePackageJSON(title) {
144
137
  return JSON.stringify({
145
- name: 'specweave-docs',
138
+ name: 'docs-site',
146
139
  version: '1.0.0',
147
140
  description: `${title} - Documentation Site`,
148
141
  private: true,
@@ -186,83 +179,340 @@ export async function writePackageJSON(targetDir, title) {
186
179
  await fs.writeFile(packagePath, content, 'utf-8');
187
180
  }
188
181
  /**
189
- * Generate custom CSS for theme customization
182
+ * Generate a professional SVG logo from project initials
183
+ */
184
+ export function generateLogoSVG(initials, primaryColor = '#4f46e5') {
185
+ const darkerColor = darkenColor(primaryColor, 20);
186
+ const fontSize = initials.length > 1 ? 76 : 90;
187
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
188
+ <defs>
189
+ <linearGradient id="logo-bg" x1="0%" y1="0%" x2="100%" y2="100%">
190
+ <stop offset="0%" style="stop-color:${primaryColor}" />
191
+ <stop offset="100%" style="stop-color:${darkerColor}" />
192
+ </linearGradient>
193
+ </defs>
194
+ <rect x="8" y="8" width="184" height="184" rx="44" ry="44" fill="url(#logo-bg)" />
195
+ <text x="100" y="100" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif"
196
+ font-size="${fontSize}" font-weight="700" fill="white"
197
+ text-anchor="middle" dominant-baseline="central" letter-spacing="-2">${initials}</text>
198
+ </svg>`;
199
+ }
200
+ /**
201
+ * Generate a favicon SVG (simplified, optimized for small sizes)
202
+ */
203
+ export function generateFaviconSVG(initials, primaryColor = '#4f46e5') {
204
+ const firstChar = initials.charAt(0);
205
+ const darkerColor = darkenColor(primaryColor, 20);
206
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
207
+ <defs>
208
+ <linearGradient id="fav-bg" x1="0%" y1="0%" x2="100%" y2="100%">
209
+ <stop offset="0%" style="stop-color:${primaryColor}" />
210
+ <stop offset="100%" style="stop-color:${darkerColor}" />
211
+ </linearGradient>
212
+ </defs>
213
+ <rect x="1" y="1" width="30" height="30" rx="7" ry="7" fill="url(#fav-bg)" />
214
+ <text x="16" y="16" font-family="system-ui, sans-serif"
215
+ font-size="18" font-weight="700" fill="white"
216
+ text-anchor="middle" dominant-baseline="central">${firstChar}</text>
217
+ </svg>`;
218
+ }
219
+ /**
220
+ * Generate custom CSS for professional theme
190
221
  */
191
222
  export function generateCustomCSS(theme) {
192
223
  const themes = {
193
- default: `
224
+ default: {
225
+ light: `
226
+ --ifm-color-primary: #4f46e5;
227
+ --ifm-color-primary-dark: #4338ca;
228
+ --ifm-color-primary-darker: #3730a3;
229
+ --ifm-color-primary-darkest: #312e81;
230
+ --ifm-color-primary-light: #6366f1;
231
+ --ifm-color-primary-lighter: #818cf8;
232
+ --ifm-color-primary-lightest: #a5b4fc;`,
233
+ dark: `
234
+ --ifm-color-primary: #818cf8;
235
+ --ifm-color-primary-dark: #6366f1;
236
+ --ifm-color-primary-darker: #4f46e5;
237
+ --ifm-color-primary-darkest: #4338ca;
238
+ --ifm-color-primary-light: #a5b4fc;
239
+ --ifm-color-primary-lighter: #c7d2fe;
240
+ --ifm-color-primary-lightest: #e0e7ff;`,
241
+ },
242
+ classic: {
243
+ light: `
244
+ --ifm-color-primary: #2563eb;
245
+ --ifm-color-primary-dark: #1d4ed8;
246
+ --ifm-color-primary-darker: #1e40af;
247
+ --ifm-color-primary-darkest: #1e3a8a;
248
+ --ifm-color-primary-light: #3b82f6;
249
+ --ifm-color-primary-lighter: #60a5fa;
250
+ --ifm-color-primary-lightest: #93bbfd;`,
251
+ dark: `
252
+ --ifm-color-primary: #60a5fa;
253
+ --ifm-color-primary-dark: #3b82f6;
254
+ --ifm-color-primary-darker: #2563eb;
255
+ --ifm-color-primary-darkest: #1d4ed8;
256
+ --ifm-color-primary-light: #93bbfd;
257
+ --ifm-color-primary-lighter: #bfdbfe;
258
+ --ifm-color-primary-lightest: #dbeafe;`,
259
+ },
260
+ dark: {
261
+ light: `
262
+ --ifm-color-primary: #6366f1;
263
+ --ifm-color-primary-dark: #4f46e5;
264
+ --ifm-color-primary-darker: #4338ca;
265
+ --ifm-color-primary-darkest: #3730a3;
266
+ --ifm-color-primary-light: #818cf8;
267
+ --ifm-color-primary-lighter: #a5b4fc;
268
+ --ifm-color-primary-lightest: #c7d2fe;`,
269
+ dark: `
270
+ --ifm-color-primary: #a5b4fc;
271
+ --ifm-color-primary-dark: #818cf8;
272
+ --ifm-color-primary-darker: #6366f1;
273
+ --ifm-color-primary-darkest: #4f46e5;
274
+ --ifm-color-primary-light: #c7d2fe;
275
+ --ifm-color-primary-lighter: #e0e7ff;
276
+ --ifm-color-primary-lightest: #eef2ff;`,
277
+ },
278
+ };
279
+ const selected = themes[theme] || themes.default;
280
+ return `/**
281
+ * Professional documentation theme
282
+ * Auto-generated — customize as needed
283
+ */
284
+
285
+ /* ===== Color Palette ===== */
194
286
  :root {
195
- --ifm-color-primary: #2e8555;
196
- --ifm-color-primary-dark: #29784c;
197
- --ifm-color-primary-darker: #277148;
198
- --ifm-color-primary-darkest: #205d3b;
199
- --ifm-color-primary-light: #33925d;
200
- --ifm-color-primary-lighter: #359962;
201
- --ifm-color-primary-lightest: #3cad6e;
287
+ ${selected.light}
202
288
  --ifm-code-font-size: 95%;
203
- --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
289
+ --ifm-heading-font-weight: 600;
290
+ --ifm-font-size-base: 16px;
291
+ --ifm-line-height-base: 1.65;
292
+ --docusaurus-highlighted-code-line-bg: rgba(79, 70, 229, 0.1);
204
293
  }
205
294
 
206
295
  [data-theme='dark'] {
207
- --ifm-color-primary: #25c2a0;
208
- --ifm-color-primary-dark: #21af90;
209
- --ifm-color-primary-darker: #1fa588;
210
- --ifm-color-primary-darkest: #1a8870;
211
- --ifm-color-primary-light: #29d5b0;
212
- --ifm-color-primary-lighter: #32d8b4;
213
- --ifm-color-primary-lightest: #4fddbf;
214
- --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
215
- }
216
- `,
217
- classic: `
218
- :root {
219
- --ifm-color-primary: #0066cc;
220
- --ifm-color-primary-dark: #005cb8;
221
- --ifm-color-primary-darker: #0056ad;
222
- --ifm-color-primary-darkest: #00478f;
223
- --ifm-color-primary-light: #0070e0;
224
- --ifm-color-primary-lighter: #0076eb;
225
- --ifm-color-primary-lightest: #1a85ff;
226
- --ifm-code-font-size: 95%;
296
+ ${selected.dark}
297
+ --docusaurus-highlighted-code-line-bg: rgba(99, 102, 241, 0.15);
227
298
  }
228
- `,
229
- dark: `
230
- :root {
231
- --ifm-color-primary: #1e1e1e;
232
- --ifm-background-color: #0d1117;
233
- --ifm-code-font-size: 95%;
299
+
300
+ /* ===== Global ===== */
301
+ * {
302
+ transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
234
303
  }
235
304
 
236
- [data-theme='dark'] {
237
- --ifm-color-primary: #58a6ff;
238
- --ifm-background-color: #0d1117;
305
+ html {
306
+ scroll-behavior: smooth;
239
307
  }
240
- `
241
- };
242
- return `/**
243
- * Custom CSS for SpecWeave documentation
244
- * Theme: ${theme}
245
- * Auto-generated by SpecWeave docs-preview plugin
246
- */
247
308
 
248
- ${themes[theme]}
309
+ /* ===== Navbar ===== */
310
+ .navbar {
311
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
312
+ border-bottom: 1px solid var(--ifm-color-emphasis-200);
313
+ backdrop-filter: blur(8px);
314
+ }
315
+
316
+ .navbar__title {
317
+ font-weight: 700;
318
+ letter-spacing: -0.01em;
319
+ }
249
320
 
250
- /* Custom styles */
321
+ .navbar__logo img {
322
+ border-radius: 8px;
323
+ }
324
+
325
+ /* ===== Sidebar ===== */
326
+ .theme-doc-sidebar-container {
327
+ border-right: 1px solid var(--ifm-color-emphasis-200);
328
+ }
329
+
330
+ .menu__link {
331
+ border-radius: 6px;
332
+ font-size: 0.9rem;
333
+ padding: 0.4rem 0.75rem;
334
+ transition: all 0.15s ease;
335
+ }
336
+
337
+ .menu__link:hover {
338
+ background: var(--ifm-color-emphasis-100);
339
+ }
340
+
341
+ .menu__link--active {
342
+ font-weight: 600;
343
+ color: var(--ifm-color-primary);
344
+ background: var(--ifm-color-primary-lightest);
345
+ }
346
+
347
+ [data-theme='dark'] .menu__link--active {
348
+ background: rgba(99, 102, 241, 0.12);
349
+ }
350
+
351
+ .menu__list-item-collapsible .menu__caret::before {
352
+ filter: var(--ifm-menu-color-active);
353
+ }
354
+
355
+ /* ===== Content ===== */
251
356
  .markdown {
252
357
  --ifm-h1-font-size: 2rem;
253
358
  --ifm-h2-font-size: 1.5rem;
254
359
  --ifm-h3-font-size: 1.25rem;
255
360
  }
256
361
 
257
- /* Code blocks */
362
+ .markdown h1, .markdown h2, .markdown h3 {
363
+ letter-spacing: -0.02em;
364
+ }
365
+
366
+ .markdown h2 {
367
+ margin-top: 2.5rem;
368
+ padding-bottom: 0.5rem;
369
+ border-bottom: 1px solid var(--ifm-color-emphasis-200);
370
+ }
371
+
372
+ /* ===== Links ===== */
373
+ .markdown a {
374
+ text-decoration: none;
375
+ border-bottom: 1px solid transparent;
376
+ transition: border-color 0.15s ease;
377
+ }
378
+
379
+ .markdown a:hover {
380
+ border-bottom-color: var(--ifm-color-primary);
381
+ }
382
+
383
+ /* ===== Code Blocks ===== */
258
384
  .prism-code {
259
- font-size: 0.9rem;
385
+ font-size: 0.875rem;
386
+ border-radius: 8px;
387
+ }
388
+
389
+ pre {
390
+ border-radius: 8px !important;
391
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
392
+ }
393
+
394
+ [data-theme='dark'] pre {
395
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
396
+ }
397
+
398
+ code {
399
+ border-radius: 4px;
400
+ padding: 2px 6px;
401
+ }
402
+
403
+ /* ===== Tables ===== */
404
+ table {
405
+ display: table;
406
+ width: 100%;
407
+ border-collapse: separate;
408
+ border-spacing: 0;
409
+ border-radius: 8px;
410
+ overflow: hidden;
411
+ border: 1px solid var(--ifm-color-emphasis-200);
412
+ }
413
+
414
+ table thead tr {
415
+ background: var(--ifm-color-emphasis-100);
416
+ }
417
+
418
+ table thead th {
419
+ font-weight: 600;
420
+ font-size: 0.875rem;
421
+ text-transform: uppercase;
422
+ letter-spacing: 0.03em;
423
+ padding: 0.75rem 1rem;
424
+ border-bottom: 2px solid var(--ifm-color-emphasis-200);
425
+ }
426
+
427
+ table tbody td {
428
+ padding: 0.65rem 1rem;
429
+ border-bottom: 1px solid var(--ifm-color-emphasis-100);
260
430
  }
261
431
 
262
- /* Mermaid diagrams */
432
+ table tbody tr:last-child td {
433
+ border-bottom: none;
434
+ }
435
+
436
+ table tbody tr:hover {
437
+ background: var(--ifm-color-emphasis-50, rgba(0, 0, 0, 0.02));
438
+ }
439
+
440
+ /* ===== Blockquotes ===== */
441
+ blockquote {
442
+ border-left: 4px solid var(--ifm-color-primary);
443
+ background: var(--ifm-color-emphasis-100);
444
+ border-radius: 0 8px 8px 0;
445
+ padding: 1rem 1.25rem;
446
+ font-style: normal;
447
+ }
448
+
449
+ /* ===== Admonitions ===== */
450
+ .admonition {
451
+ border-radius: 8px;
452
+ border: none;
453
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
454
+ }
455
+
456
+ /* ===== Cards (prev/next navigation) ===== */
457
+ .pagination-nav__link {
458
+ border-radius: 10px;
459
+ border: 1px solid var(--ifm-color-emphasis-200);
460
+ transition: all 0.2s ease;
461
+ }
462
+
463
+ .pagination-nav__link:hover {
464
+ border-color: var(--ifm-color-primary);
465
+ box-shadow: 0 4px 16px rgba(79, 70, 229, 0.1);
466
+ }
467
+
468
+ /* ===== Mermaid ===== */
263
469
  .docusaurus-mermaid-container {
264
470
  text-align: center;
265
471
  margin: 2rem 0;
472
+ padding: 1rem;
473
+ background: var(--ifm-color-emphasis-100);
474
+ border-radius: 8px;
475
+ }
476
+
477
+ /* ===== Table of Contents ===== */
478
+ .table-of-contents__link--active {
479
+ font-weight: 600;
480
+ color: var(--ifm-color-primary);
481
+ }
482
+
483
+ /* ===== Footer ===== */
484
+ .footer--dark {
485
+ background: #1e1e2e;
486
+ }
487
+
488
+ [data-theme='dark'] .footer--dark {
489
+ background: #111118;
490
+ }
491
+
492
+ /* ===== Scrollbar ===== */
493
+ ::-webkit-scrollbar {
494
+ width: 6px;
495
+ height: 6px;
496
+ }
497
+
498
+ ::-webkit-scrollbar-track {
499
+ background: transparent;
500
+ }
501
+
502
+ ::-webkit-scrollbar-thumb {
503
+ background: var(--ifm-color-emphasis-300);
504
+ border-radius: 3px;
505
+ }
506
+
507
+ ::-webkit-scrollbar-thumb:hover {
508
+ background: var(--ifm-color-emphasis-500);
509
+ }
510
+
511
+ /* ===== Responsive ===== */
512
+ @media screen and (max-width: 996px) {
513
+ .markdown h2 {
514
+ margin-top: 2rem;
515
+ }
266
516
  }
267
517
  `;
268
518
  }
@@ -276,9 +526,20 @@ export async function writeCustomCSS(targetDir, theme) {
276
526
  await fs.writeFile(cssPath, content, 'utf-8');
277
527
  }
278
528
  /**
279
- * Generate index page (landing page)
529
+ * Generate index page (landing page) with dynamic categories
280
530
  */
281
- export function generateIndexPage(title, tagline) {
531
+ export function generateIndexPage(title, tagline, categories) {
532
+ const firstCategoryLink = categories.length > 0 ? `/${categories[0].id}` : '/';
533
+ // Build category cards JSX
534
+ const categoryCards = categories.map(cat => {
535
+ const escapedDesc = escapeQuotes(cat.description);
536
+ return ` <a href="${cat.id}" className={styles.categoryCard} key="${cat.id}">
537
+ <span className={styles.categoryIcon}>${cat.icon}</span>
538
+ <h3>${cat.label}</h3>
539
+ <p>${escapedDesc}</p>
540
+ <span className={styles.docCount}>${cat.docCount} document${cat.docCount !== 1 ? 's' : ''}</span>
541
+ </a>`;
542
+ }).join('\n');
282
543
  return `import React from 'react';
283
544
  import clsx from 'clsx';
284
545
  import Link from '@docusaurus/Link';
@@ -289,15 +550,15 @@ import styles from './index.module.css';
289
550
  function HomepageHeader() {
290
551
  const {siteConfig} = useDocusaurusContext();
291
552
  return (
292
- <header className={clsx('hero hero--primary', styles.heroBanner)}>
553
+ <header className={clsx('hero', styles.heroBanner)}>
293
554
  <div className="container">
294
- <h1 className="hero__title">{siteConfig.title}</h1>
295
- <p className="hero__subtitle">{siteConfig.tagline}</p>
555
+ <h1 className={styles.heroTitle}>{siteConfig.title}</h1>
556
+ <p className={styles.heroSubtitle}>{siteConfig.tagline}</p>
296
557
  <div className={styles.buttons}>
297
558
  <Link
298
- className="button button--secondary button--lg"
299
- to="/strategy">
300
- View Documentation
559
+ className="button button--primary button--lg"
560
+ to="${firstCategoryLink}">
561
+ Browse Documentation
301
562
  </Link>
302
563
  </div>
303
564
  </div>
@@ -309,31 +570,14 @@ export default function Home() {
309
570
  const {siteConfig} = useDocusaurusContext();
310
571
  return (
311
572
  <Layout
312
- title={\`\${siteConfig.title}\`}
313
- description="${tagline}">
573
+ title={siteConfig.title}
574
+ description="${escapeQuotes(tagline)}">
314
575
  <HomepageHeader />
315
576
  <main>
316
- <div className="container" style={{marginTop: '2rem', marginBottom: '2rem'}}>
317
- <div className="row">
318
- <div className="col col--12">
319
- <h2>Welcome to ${title}</h2>
320
- <p>
321
- This is your SpecWeave documentation site, automatically generated from your
322
- <code>.specweave/docs/</code> folder.
323
- </p>
324
- <p>
325
- Use the sidebar to navigate through your documentation:
326
- </p>
327
- <ul>
328
- <li><strong>Strategy</strong>: Business rationale, PRDs, OKRs</li>
329
- <li><strong>Specs</strong>: Feature specifications and user stories</li>
330
- <li><strong>Architecture</strong>: Technical design, ADRs, diagrams</li>
331
- <li><strong>Delivery</strong>: Build & release processes</li>
332
- <li><strong>Operations</strong>: Runbooks, SLOs, incidents</li>
333
- <li><strong>Governance</strong>: Policies, security, compliance</li>
334
- </ul>
335
- </div>
336
- </div>
577
+ <div className="container" style={{padding: '3rem 0'}}>
578
+ ${categories.length > 0 ? ` <div className={styles.categoryGrid}>
579
+ ${categoryCards}
580
+ </div>` : ''}
337
581
  </div>
338
582
  </main>
339
583
  </Layout>
@@ -344,9 +588,9 @@ export default function Home() {
344
588
  /**
345
589
  * Write index page to file
346
590
  */
347
- export async function writeIndexPage(targetDir, title, tagline) {
591
+ export async function writeIndexPage(targetDir, title, tagline, categories = []) {
348
592
  const indexPath = path.join(targetDir, 'src', 'pages', 'index.js');
349
- const content = generateIndexPage(title, tagline);
593
+ const content = generateIndexPage(title, tagline, categories);
350
594
  await fs.ensureDir(path.dirname(indexPath));
351
595
  await fs.writeFile(indexPath, content, 'utf-8');
352
596
  }
@@ -355,16 +599,32 @@ export async function writeIndexPage(targetDir, title, tagline) {
355
599
  */
356
600
  export function generateIndexModuleCSS() {
357
601
  return `.heroBanner {
358
- padding: 4rem 0;
602
+ padding: 5rem 0 4rem;
359
603
  text-align: center;
360
604
  position: relative;
361
605
  overflow: hidden;
606
+ background: linear-gradient(135deg, #4f46e5 0%, #6366f1 50%, #818cf8 100%);
607
+ color: white;
362
608
  }
363
609
 
364
- @media screen and (max-width: 996px) {
365
- .heroBanner {
366
- padding: 2rem;
367
- }
610
+ [data-theme='dark'] .heroBanner {
611
+ background: linear-gradient(135deg, #312e81 0%, #3730a3 50%, #4338ca 100%);
612
+ }
613
+
614
+ .heroTitle {
615
+ font-size: 3rem;
616
+ font-weight: 800;
617
+ letter-spacing: -0.03em;
618
+ margin-bottom: 0.75rem;
619
+ color: white;
620
+ }
621
+
622
+ .heroSubtitle {
623
+ font-size: 1.25rem;
624
+ opacity: 0.9;
625
+ max-width: 600px;
626
+ margin: 0 auto 1.5rem;
627
+ color: white;
368
628
  }
369
629
 
370
630
  .buttons {
@@ -374,6 +634,108 @@ export function generateIndexModuleCSS() {
374
634
  gap: 1rem;
375
635
  margin-top: 2rem;
376
636
  }
637
+
638
+ .buttons a {
639
+ background: white;
640
+ color: #4f46e5;
641
+ border: none;
642
+ font-weight: 600;
643
+ padding: 0.75rem 2rem;
644
+ border-radius: 8px;
645
+ font-size: 1rem;
646
+ transition: all 0.2s ease;
647
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
648
+ }
649
+
650
+ .buttons a:hover {
651
+ background: #f8fafc;
652
+ color: #4338ca;
653
+ transform: translateY(-2px);
654
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
655
+ text-decoration: none;
656
+ }
657
+
658
+ /* Category Grid */
659
+ .categoryGrid {
660
+ display: grid;
661
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
662
+ gap: 1.5rem;
663
+ padding: 1rem 0 2rem;
664
+ }
665
+
666
+ .categoryCard {
667
+ background: var(--ifm-card-background-color, white);
668
+ border: 1px solid var(--ifm-color-emphasis-200);
669
+ border-radius: 12px;
670
+ padding: 1.75rem;
671
+ transition: all 0.2s ease;
672
+ text-decoration: none !important;
673
+ color: inherit;
674
+ display: block;
675
+ cursor: pointer;
676
+ }
677
+
678
+ .categoryCard:hover {
679
+ transform: translateY(-4px);
680
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
681
+ border-color: var(--ifm-color-primary);
682
+ text-decoration: none !important;
683
+ color: inherit;
684
+ }
685
+
686
+ [data-theme='dark'] .categoryCard:hover {
687
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
688
+ }
689
+
690
+ .categoryCard h3 {
691
+ margin: 0.5rem 0 0.25rem;
692
+ font-size: 1.15rem;
693
+ font-weight: 600;
694
+ }
695
+
696
+ .categoryCard p {
697
+ margin: 0 0 0.75rem;
698
+ font-size: 0.9rem;
699
+ color: var(--ifm-color-emphasis-700);
700
+ line-height: 1.5;
701
+ }
702
+
703
+ .categoryIcon {
704
+ font-size: 2rem;
705
+ display: block;
706
+ margin-bottom: 0.25rem;
707
+ }
708
+
709
+ .docCount {
710
+ font-size: 0.8rem;
711
+ color: var(--ifm-color-emphasis-500);
712
+ font-weight: 500;
713
+ }
714
+
715
+ /* Responsive */
716
+ @media screen and (max-width: 996px) {
717
+ .heroBanner {
718
+ padding: 3rem 1.5rem;
719
+ }
720
+
721
+ .heroTitle {
722
+ font-size: 2.25rem;
723
+ }
724
+
725
+ .categoryGrid {
726
+ grid-template-columns: 1fr;
727
+ }
728
+ }
729
+
730
+ @media screen and (max-width: 600px) {
731
+ .heroTitle {
732
+ font-size: 1.75rem;
733
+ }
734
+
735
+ .heroSubtitle {
736
+ font-size: 1rem;
737
+ }
738
+ }
377
739
  `;
378
740
  }
379
741
  /**
@@ -385,4 +747,20 @@ export async function writeIndexModuleCSS(targetDir) {
385
747
  await fs.ensureDir(path.dirname(cssPath));
386
748
  await fs.writeFile(cssPath, content, 'utf-8');
387
749
  }
750
+ /**
751
+ * Escape single quotes for template string safety
752
+ */
753
+ function escapeQuotes(str) {
754
+ return str.replace(/'/g, "\\'").replace(/\n/g, ' ');
755
+ }
756
+ /**
757
+ * Darken a hex color by a percentage
758
+ */
759
+ function darkenColor(hex, percent) {
760
+ const num = parseInt(hex.replace('#', ''), 16);
761
+ const r = Math.max(0, (num >> 16) - Math.round(2.55 * percent));
762
+ const g = Math.max(0, ((num >> 8) & 0x00ff) - Math.round(2.55 * percent));
763
+ const b = Math.max(0, (num & 0x0000ff) - Math.round(2.55 * percent));
764
+ return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, '0')}`;
765
+ }
388
766
  //# sourceMappingURL=config-generator.js.map