specweave 1.0.263 → 1.0.265

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