specweave 0.30.12 → 0.30.14

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 (127) hide show
  1. package/.claude-plugin/marketplace.json +0 -11
  2. package/CLAUDE.md +1 -1
  3. package/bin/fix-marketplace-errors.sh +1 -1
  4. package/dist/src/cli/commands/init.d.ts.map +1 -1
  5. package/dist/src/cli/commands/init.js +13 -0
  6. package/dist/src/cli/commands/init.js.map +1 -1
  7. package/dist/src/cli/helpers/ado-area-selector.d.ts.map +1 -1
  8. package/dist/src/cli/helpers/ado-area-selector.js +13 -0
  9. package/dist/src/cli/helpers/ado-area-selector.js.map +1 -1
  10. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  11. package/dist/src/cli/helpers/issue-tracker/index.js +7 -2
  12. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  13. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts +7 -0
  14. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -1
  15. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +33 -2
  16. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -1
  17. package/dist/src/cli/workers/clone-worker.js +19 -3
  18. package/dist/src/cli/workers/clone-worker.js.map +1 -1
  19. package/dist/src/core/living-docs/board-matcher.d.ts +120 -0
  20. package/dist/src/core/living-docs/board-matcher.d.ts.map +1 -0
  21. package/dist/src/core/living-docs/board-matcher.js +466 -0
  22. package/dist/src/core/living-docs/board-matcher.js.map +1 -0
  23. package/dist/src/core/living-docs/foundation-builder.js +1 -1
  24. package/dist/src/core/living-docs/foundation-builder.js.map +1 -1
  25. package/dist/src/core/living-docs/living-docs-sync.d.ts +19 -8
  26. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  27. package/dist/src/core/living-docs/living-docs-sync.js +148 -52
  28. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  29. package/dist/src/core/living-docs/suggestions-generator.js +1 -1
  30. package/dist/src/core/living-docs/suggestions-generator.js.map +1 -1
  31. package/dist/src/core/living-docs/umbrella-detector.d.ts +4 -0
  32. package/dist/src/core/living-docs/umbrella-detector.d.ts.map +1 -1
  33. package/dist/src/core/living-docs/umbrella-detector.js +20 -1
  34. package/dist/src/core/living-docs/umbrella-detector.js.map +1 -1
  35. package/dist/src/core/living-docs/workitem-matcher.js +5 -5
  36. package/dist/src/core/living-docs/workitem-matcher.js.map +1 -1
  37. package/dist/src/importers/item-converter.d.ts +4 -0
  38. package/dist/src/importers/item-converter.d.ts.map +1 -1
  39. package/dist/src/importers/item-converter.js +4 -0
  40. package/dist/src/importers/item-converter.js.map +1 -1
  41. package/dist/src/init/repo/types.d.ts +1 -1
  42. package/dist/src/living-docs/enterprise-analyzer.d.ts.map +1 -1
  43. package/dist/src/living-docs/enterprise-analyzer.js +70 -19
  44. package/dist/src/living-docs/enterprise-analyzer.js.map +1 -1
  45. package/dist/src/living-docs/epic-id-allocator.d.ts +4 -0
  46. package/dist/src/living-docs/epic-id-allocator.d.ts.map +1 -1
  47. package/dist/src/living-docs/epic-id-allocator.js +4 -0
  48. package/dist/src/living-docs/epic-id-allocator.js.map +1 -1
  49. package/dist/src/living-docs/fs-id-allocator.d.ts +4 -0
  50. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
  51. package/dist/src/living-docs/fs-id-allocator.js +4 -0
  52. package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
  53. package/dist/src/living-docs/smart-doc-organizer.d.ts +114 -0
  54. package/dist/src/living-docs/smart-doc-organizer.d.ts.map +1 -0
  55. package/dist/src/living-docs/smart-doc-organizer.js +535 -0
  56. package/dist/src/living-docs/smart-doc-organizer.js.map +1 -0
  57. package/package.json +13 -13
  58. package/plugins/PLUGINS-INDEX.md +2 -3
  59. package/plugins/specweave/commands/specweave-judge.md +265 -0
  60. package/plugins/specweave/commands/specweave-organize-docs.md +185 -0
  61. package/plugins/specweave/hooks/hooks.json +3 -3
  62. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -0
  63. package/plugins/specweave/hooks/universal/hook-wrapper.sh +67 -0
  64. package/plugins/specweave-ado/commands/{specweave-ado-close-workitem.md → close.md} +9 -5
  65. package/plugins/specweave-ado/commands/{specweave-ado-create-workitem.md → create.md} +9 -5
  66. package/plugins/specweave-ado/commands/pull.md +459 -0
  67. package/plugins/specweave-ado/commands/push.md +361 -0
  68. package/plugins/specweave-ado/commands/{specweave-ado-status.md → status.md} +12 -0
  69. package/plugins/specweave-ado/commands/{specweave-ado-sync.md → sync.md} +64 -3
  70. package/plugins/specweave-ado/hooks/README.md +1 -1
  71. package/plugins/specweave-docs/commands/build.md +158 -0
  72. package/plugins/specweave-docs/commands/{docs-generate.md → generate.md} +10 -5
  73. package/plugins/specweave-docs/commands/health.md +268 -0
  74. package/plugins/specweave-docs/commands/{docs-init.md → init.md} +11 -6
  75. package/plugins/specweave-docs/commands/organize.md +184 -0
  76. package/plugins/specweave-docs/commands/preview.md +138 -0
  77. package/plugins/specweave-docs/skills/preview/SKILL.md +105 -0
  78. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +1 -1
  79. package/plugins/specweave-github/commands/{specweave-github-close-issue.md → close.md} +2 -2
  80. package/plugins/specweave-github/commands/{specweave-github-create-issue.md → create.md} +2 -2
  81. package/plugins/specweave-github/commands/pull.md +142 -0
  82. package/plugins/specweave-github/commands/push.md +154 -0
  83. package/plugins/specweave-github/commands/{specweave-github-sync.md → sync.md} +19 -5
  84. package/plugins/specweave-github/commands/{specweave-github-update-user-story.md → update-user-story.md} +1 -1
  85. package/plugins/specweave-github/hooks/README.md +1 -1
  86. package/plugins/specweave-jira/commands/pull.md +164 -0
  87. package/plugins/specweave-jira/commands/push.md +170 -0
  88. package/plugins/specweave-jira/commands/{specweave-jira-sync.md → sync.md} +18 -3
  89. package/plugins/specweave-jira/hooks/README.md +1 -1
  90. package/plugins/specweave-kafka/README.md +20 -0
  91. package/plugins/specweave-kafka/benchmarks/kafka-throughput.benchmark.ts +551 -0
  92. package/plugins/specweave-kafka/examples/README.md +191 -0
  93. package/plugins/specweave-kafka/examples/avro-schema-registry/.env.example +8 -0
  94. package/plugins/specweave-kafka/examples/avro-schema-registry/README.md +69 -0
  95. package/plugins/specweave-kafka/examples/avro-schema-registry/consumer.js +37 -0
  96. package/plugins/specweave-kafka/examples/avro-schema-registry/package.json +14 -0
  97. package/plugins/specweave-kafka/examples/avro-schema-registry/producer.js +57 -0
  98. package/plugins/specweave-kafka/examples/exactly-once-semantics/.env.example +5 -0
  99. package/plugins/specweave-kafka/examples/exactly-once-semantics/README.md +30 -0
  100. package/plugins/specweave-kafka/examples/exactly-once-semantics/eos-pipeline.js +79 -0
  101. package/plugins/specweave-kafka/examples/exactly-once-semantics/package.json +11 -0
  102. package/plugins/specweave-kafka/examples/kafka-streams-app/.env.example +4 -0
  103. package/plugins/specweave-kafka/examples/kafka-streams-app/README.md +30 -0
  104. package/plugins/specweave-kafka/examples/kafka-streams-app/package.json +11 -0
  105. package/plugins/specweave-kafka/examples/kafka-streams-app/windowed-aggregation.js +66 -0
  106. package/plugins/specweave-kafka/examples/n8n-workflow/README.md +54 -0
  107. package/plugins/specweave-kafka/examples/n8n-workflow/docker-compose.yml +19 -0
  108. package/plugins/specweave-kafka/examples/n8n-workflow/kafka-to-slack.json +50 -0
  109. package/plugins/specweave-kafka/examples/simple-producer-consumer/.env.example +15 -0
  110. package/plugins/specweave-kafka/examples/simple-producer-consumer/README.md +183 -0
  111. package/plugins/specweave-kafka/examples/simple-producer-consumer/consumer.js +60 -0
  112. package/plugins/specweave-kafka/examples/simple-producer-consumer/docker-compose.yml +30 -0
  113. package/plugins/specweave-kafka/examples/simple-producer-consumer/package.json +18 -0
  114. package/plugins/specweave-kafka/examples/simple-producer-consumer/producer.js +52 -0
  115. package/plugins/specweave-release/commands/specweave-release-npm.md +26 -239
  116. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +0 -21
  117. package/plugins/specweave-docs-preview/commands/build.md +0 -489
  118. package/plugins/specweave-docs-preview/commands/preview.md +0 -355
  119. package/plugins/specweave-docs-preview/skills/docs-preview/SKILL.md +0 -386
  120. /package/plugins/specweave-ado/commands/{specweave-ado-clone-repos.md → clone.md} +0 -0
  121. /package/plugins/specweave-ado/commands/{specweave-ado-import-areas.md → import-areas.md} +0 -0
  122. /package/plugins/specweave-ado/commands/{specweave-ado-import-projects.md → import-projects.md} +0 -0
  123. /package/plugins/specweave-github/commands/{specweave-github-cleanup-duplicates.md → cleanup-duplicates.md} +0 -0
  124. /package/plugins/specweave-github/commands/{specweave-github-reconcile.md → reconcile.md} +0 -0
  125. /package/plugins/specweave-github/commands/{specweave-github-status.md → status.md} +0 -0
  126. /package/plugins/specweave-jira/commands/{specweave-jira-import-boards.md → import-boards.md} +0 -0
  127. /package/plugins/specweave-jira/commands/{specweave-jira-import-projects.md → import-projects-full.md} +0 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Smart Documentation Organizer
3
+ *
4
+ * Intelligently organizes large documentation folders by:
5
+ * 1. Detecting themes from file content and titles
6
+ * 2. Generating themed category index files for navigation
7
+ * 3. Creating Docusaurus-compatible sidebar structures
8
+ * 4. Auto-organizing when folder exceeds threshold (default: 30 files)
9
+ *
10
+ * Philosophy: Generate indexes, don't move files (preserves URLs)
11
+ */
12
+ import { Logger } from '../utils/logger.js';
13
+ export interface ThemeDefinition {
14
+ id: string;
15
+ name: string;
16
+ keywords: string[];
17
+ icon: string;
18
+ }
19
+ export interface ThemedFile {
20
+ path: string;
21
+ name: string;
22
+ title: string;
23
+ themes: string[];
24
+ primaryTheme: string;
25
+ lastModified: Date;
26
+ }
27
+ export interface ThemeCategory {
28
+ theme: ThemeDefinition;
29
+ files: ThemedFile[];
30
+ count: number;
31
+ }
32
+ export interface OrganizationPlan {
33
+ folder: string;
34
+ totalFiles: number;
35
+ themeCategories: ThemeCategory[];
36
+ uncategorized: ThemedFile[];
37
+ suggestedIndexes: string[];
38
+ needsOrganization: boolean;
39
+ }
40
+ export interface SmartDocOrganizerOptions {
41
+ projectPath: string;
42
+ logger?: Logger;
43
+ thresholdForOrganization?: number;
44
+ generateIndexes?: boolean;
45
+ dryRun?: boolean;
46
+ }
47
+ /**
48
+ * Smart Documentation Organizer
49
+ */
50
+ export declare class SmartDocOrganizer {
51
+ private projectPath;
52
+ private logger;
53
+ private threshold;
54
+ private generateIndexes;
55
+ private dryRun;
56
+ constructor(options: SmartDocOrganizerOptions);
57
+ /**
58
+ * Analyze a documentation folder and create organization plan
59
+ */
60
+ analyzeFolder(folderPath: string): Promise<OrganizationPlan>;
61
+ /**
62
+ * Classify a file by detecting themes from content and filename
63
+ */
64
+ private classifyFile;
65
+ /**
66
+ * Group files by their primary theme
67
+ */
68
+ private groupByTheme;
69
+ /**
70
+ * Generate themed index files for Docusaurus navigation
71
+ */
72
+ generateCategoryIndexes(plan: OrganizationPlan): Promise<string[]>;
73
+ /**
74
+ * Generate main category index with links to themed indexes
75
+ */
76
+ private generateMainCategoryIndex;
77
+ /**
78
+ * Generate themed index with all files in that category
79
+ */
80
+ private generateThemeIndex;
81
+ /**
82
+ * Sub-group files by keyword for large categories
83
+ */
84
+ private subGroupByKeyword;
85
+ /**
86
+ * Format string to title case, preserving known acronyms
87
+ */
88
+ private formatTitle;
89
+ /**
90
+ * Update Docusaurus sidebar configuration
91
+ */
92
+ updateDocusaurusSidebar(plans: OrganizationPlan[]): Promise<string>;
93
+ /**
94
+ * Run full organization on internal docs
95
+ */
96
+ organizeInternalDocs(): Promise<{
97
+ plans: OrganizationPlan[];
98
+ generatedFiles: string[];
99
+ summary: string;
100
+ }>;
101
+ /**
102
+ * Generate organization summary
103
+ */
104
+ private generateSummary;
105
+ }
106
+ /**
107
+ * Convenience function to organize docs
108
+ */
109
+ export declare function organizeDocumentation(projectPath: string, options?: Partial<SmartDocOrganizerOptions>): Promise<{
110
+ plans: OrganizationPlan[];
111
+ generatedFiles: string[];
112
+ summary: string;
113
+ }>;
114
+ //# sourceMappingURL=smart-doc-organizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-doc-organizer.d.ts","sourceRoot":"","sources":["../../../src/living-docs/smart-doc-organizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,MAAM,EAAiB,MAAM,oBAAoB,CAAC;AA8E3D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,MAAM,CAAU;gBAEZ,OAAO,EAAE,wBAAwB;IAQ7C;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAmDlE;;OAEG;YACW,YAAY;IAgE1B;;OAEG;IACH,OAAO,CAAC,YAAY;IA8BpB;;OAEG;IACG,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqCxE;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA4DjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6C1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;OAEG;IACG,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA2CzE;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC;QACpC,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IA2CF;;OAEG;IACH,OAAO,CAAC,eAAe;CAuCxB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAC1C,OAAO,CAAC;IACT,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAOD"}
@@ -0,0 +1,535 @@
1
+ /**
2
+ * Smart Documentation Organizer
3
+ *
4
+ * Intelligently organizes large documentation folders by:
5
+ * 1. Detecting themes from file content and titles
6
+ * 2. Generating themed category index files for navigation
7
+ * 3. Creating Docusaurus-compatible sidebar structures
8
+ * 4. Auto-organizing when folder exceeds threshold (default: 30 files)
9
+ *
10
+ * Philosophy: Generate indexes, don't move files (preserves URLs)
11
+ */
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { glob } from 'glob';
15
+ import { consoleLogger } from '../utils/logger.js';
16
+ // Theme definitions with keywords for detection
17
+ const THEME_DEFINITIONS = [
18
+ {
19
+ id: 'sync',
20
+ name: 'Synchronization & Integration',
21
+ keywords: ['sync', 'integration', 'bidirectional', 'import', 'export', 'pull', 'push'],
22
+ icon: '🔄',
23
+ },
24
+ {
25
+ id: 'hooks',
26
+ name: 'Hooks & Events',
27
+ keywords: ['hook', 'event', 'trigger', 'callback', 'post-task', 'pre-tool', 'session'],
28
+ icon: '🪝',
29
+ },
30
+ {
31
+ id: 'github',
32
+ name: 'GitHub Integration',
33
+ keywords: ['github', 'gh-', 'issue', 'pull-request', 'pr-', 'actions', 'workflow'],
34
+ icon: '🐙',
35
+ },
36
+ {
37
+ id: 'external-tools',
38
+ name: 'External Tools (ADO, JIRA)',
39
+ keywords: ['jira', 'ado', 'azure-devops', 'external-tool', 'area-path', 'work-item', 'epic'],
40
+ icon: '🔌',
41
+ },
42
+ {
43
+ id: 'testing',
44
+ name: 'Testing & Quality',
45
+ keywords: ['test', 'fixture', 'mock', 'coverage', 'tdd', 'isolation', 'assertion', 'qa'],
46
+ icon: '🧪',
47
+ },
48
+ {
49
+ id: 'brownfield',
50
+ name: 'Brownfield & Migration',
51
+ keywords: ['brownfield', 'migration', 'legacy', 'onboard', 'existing', 'import'],
52
+ icon: '🏗️',
53
+ },
54
+ {
55
+ id: 'architecture',
56
+ name: 'Core Architecture',
57
+ keywords: ['pattern', 'factory', 'abstraction', 'layer', 'modular', 'structure', 'design'],
58
+ icon: '🏛️',
59
+ },
60
+ {
61
+ id: 'performance',
62
+ name: 'Performance & Optimization',
63
+ keywords: ['cache', 'performance', 'optimization', 'pagination', 'batch', 'lazy', 'ttl'],
64
+ icon: '⚡',
65
+ },
66
+ {
67
+ id: 'security',
68
+ name: 'Security & Permissions',
69
+ keywords: ['permission', 'security', 'gate', 'auth', 'token', 'secret', 'credential'],
70
+ icon: '🔒',
71
+ },
72
+ {
73
+ id: 'increments',
74
+ name: 'Increment Lifecycle',
75
+ keywords: ['increment', 'status', 'lifecycle', 'backlog', 'phase', 'workflow', 'completion'],
76
+ icon: '📦',
77
+ },
78
+ {
79
+ id: 'config',
80
+ name: 'Configuration & Setup',
81
+ keywords: ['config', 'env', 'setup', 'init', 'setting', 'option', 'parameter'],
82
+ icon: '⚙️',
83
+ },
84
+ {
85
+ id: 'docs',
86
+ name: 'Documentation & Living Docs',
87
+ keywords: ['doc', 'living', 'spec', 'readme', 'naming', 'convention', 'folder'],
88
+ icon: '📚',
89
+ },
90
+ ];
91
+ /**
92
+ * Smart Documentation Organizer
93
+ */
94
+ export class SmartDocOrganizer {
95
+ constructor(options) {
96
+ this.projectPath = options.projectPath;
97
+ this.logger = options.logger ?? consoleLogger;
98
+ this.threshold = options.thresholdForOrganization ?? 30;
99
+ this.generateIndexes = options.generateIndexes ?? true;
100
+ this.dryRun = options.dryRun ?? false;
101
+ }
102
+ /**
103
+ * Analyze a documentation folder and create organization plan
104
+ */
105
+ async analyzeFolder(folderPath) {
106
+ const absolutePath = path.isAbsolute(folderPath)
107
+ ? folderPath
108
+ : path.join(this.projectPath, folderPath);
109
+ if (!fs.existsSync(absolutePath)) {
110
+ throw new Error(`Folder not found: ${absolutePath}`);
111
+ }
112
+ // Get all markdown files (exclude index files we generate)
113
+ const files = await glob('*.md', {
114
+ cwd: absolutePath,
115
+ nodir: true,
116
+ ignore: ['README.md', '_index-*.md', 'category-*.md'],
117
+ });
118
+ this.logger.info(`Analyzing ${files.length} files in ${folderPath}`);
119
+ // Classify each file by theme
120
+ const themedFiles = [];
121
+ for (const file of files) {
122
+ const fullPath = path.join(absolutePath, file);
123
+ const themed = await this.classifyFile(fullPath);
124
+ themedFiles.push(themed);
125
+ }
126
+ // Group by theme
127
+ const themeCategories = this.groupByTheme(themedFiles);
128
+ // Identify uncategorized files
129
+ const uncategorized = themedFiles.filter(f => f.primaryTheme === 'uncategorized');
130
+ // Determine if organization is needed
131
+ const needsOrganization = files.length > this.threshold;
132
+ // Generate suggested indexes
133
+ const suggestedIndexes = themeCategories
134
+ .filter(tc => tc.count >= 3) // Only suggest for themes with 3+ files
135
+ .map(tc => `_index-${tc.theme.id}.md`);
136
+ return {
137
+ folder: folderPath,
138
+ totalFiles: files.length,
139
+ themeCategories,
140
+ uncategorized,
141
+ suggestedIndexes,
142
+ needsOrganization,
143
+ };
144
+ }
145
+ /**
146
+ * Classify a file by detecting themes from content and filename
147
+ */
148
+ async classifyFile(filePath) {
149
+ const fileName = path.basename(filePath, '.md');
150
+ const stats = fs.statSync(filePath);
151
+ let content = '';
152
+ let title = fileName;
153
+ try {
154
+ content = fs.readFileSync(filePath, 'utf-8');
155
+ // Extract title from first heading
156
+ const titleMatch = content.match(/^#\s+(.+)$/m);
157
+ if (titleMatch) {
158
+ title = titleMatch[1].replace(/^ADR-\d+:\s*/, ''); // Remove ADR prefix
159
+ }
160
+ }
161
+ catch {
162
+ // Skip files that can't be read
163
+ }
164
+ // Combine filename and content for theme detection
165
+ const searchText = `${fileName} ${title} ${content}`.toLowerCase();
166
+ // Score each theme
167
+ const themeScores = [];
168
+ for (const theme of THEME_DEFINITIONS) {
169
+ let score = 0;
170
+ for (const keyword of theme.keywords) {
171
+ // Count keyword occurrences
172
+ const regex = new RegExp(keyword, 'gi');
173
+ const matches = searchText.match(regex);
174
+ if (matches) {
175
+ score += matches.length;
176
+ // Boost score if keyword in filename
177
+ if (fileName.toLowerCase().includes(keyword)) {
178
+ score += 5;
179
+ }
180
+ // Boost score if keyword in title
181
+ if (title.toLowerCase().includes(keyword)) {
182
+ score += 3;
183
+ }
184
+ }
185
+ }
186
+ if (score > 0) {
187
+ themeScores.push({ theme, score });
188
+ }
189
+ }
190
+ // Sort by score descending
191
+ themeScores.sort((a, b) => b.score - a.score);
192
+ // Get primary theme and all matching themes
193
+ const themes = themeScores.slice(0, 3).map(ts => ts.theme.id);
194
+ const primaryTheme = themes[0] ?? 'uncategorized';
195
+ return {
196
+ path: filePath,
197
+ name: fileName,
198
+ title,
199
+ themes,
200
+ primaryTheme,
201
+ lastModified: stats.mtime,
202
+ };
203
+ }
204
+ /**
205
+ * Group files by their primary theme
206
+ */
207
+ groupByTheme(files) {
208
+ const themeMap = new Map();
209
+ for (const file of files) {
210
+ const theme = file.primaryTheme;
211
+ if (!themeMap.has(theme)) {
212
+ themeMap.set(theme, []);
213
+ }
214
+ themeMap.get(theme).push(file);
215
+ }
216
+ const categories = [];
217
+ for (const [themeId, themeFiles] of themeMap) {
218
+ if (themeId === 'uncategorized')
219
+ continue;
220
+ const themeDef = THEME_DEFINITIONS.find(t => t.id === themeId);
221
+ if (themeDef) {
222
+ categories.push({
223
+ theme: themeDef,
224
+ files: themeFiles.sort((a, b) => a.name.localeCompare(b.name)),
225
+ count: themeFiles.length,
226
+ });
227
+ }
228
+ }
229
+ // Sort by count descending
230
+ return categories.sort((a, b) => b.count - a.count);
231
+ }
232
+ /**
233
+ * Generate themed index files for Docusaurus navigation
234
+ */
235
+ async generateCategoryIndexes(plan) {
236
+ if (!plan.needsOrganization && !this.generateIndexes) {
237
+ return [];
238
+ }
239
+ const generatedFiles = [];
240
+ const folderPath = path.isAbsolute(plan.folder)
241
+ ? plan.folder
242
+ : path.join(this.projectPath, plan.folder);
243
+ // Generate main category index
244
+ const mainIndexPath = path.join(folderPath, '_categories.md');
245
+ const mainIndexContent = this.generateMainCategoryIndex(plan);
246
+ if (!this.dryRun) {
247
+ fs.writeFileSync(mainIndexPath, mainIndexContent);
248
+ this.logger.info(`Generated: ${mainIndexPath}`);
249
+ }
250
+ generatedFiles.push(mainIndexPath);
251
+ // Generate individual theme indexes
252
+ for (const category of plan.themeCategories) {
253
+ if (category.count < 3)
254
+ continue; // Skip themes with too few files
255
+ const indexPath = path.join(folderPath, `_index-${category.theme.id}.md`);
256
+ const indexContent = this.generateThemeIndex(category, plan.folder);
257
+ if (!this.dryRun) {
258
+ fs.writeFileSync(indexPath, indexContent);
259
+ this.logger.info(`Generated: ${indexPath}`);
260
+ }
261
+ generatedFiles.push(indexPath);
262
+ }
263
+ return generatedFiles;
264
+ }
265
+ /**
266
+ * Generate main category index with links to themed indexes
267
+ */
268
+ generateMainCategoryIndex(plan) {
269
+ const lines = [];
270
+ const folderName = path.basename(plan.folder);
271
+ lines.push('---');
272
+ lines.push('sidebar_position: 1');
273
+ lines.push(`title: "${this.formatTitle(folderName)} by Category"`);
274
+ lines.push('---');
275
+ lines.push('');
276
+ lines.push(`# ${this.formatTitle(folderName)} by Category`);
277
+ lines.push('');
278
+ lines.push(`This folder contains **${plan.totalFiles}** documents organized into the following categories:`);
279
+ lines.push('');
280
+ // Category table
281
+ lines.push('| Category | Count | Description |');
282
+ lines.push('|----------|-------|-------------|');
283
+ for (const category of plan.themeCategories) {
284
+ if (category.count < 3)
285
+ continue;
286
+ const link = `[${category.theme.icon} ${category.theme.name}](./_index-${category.theme.id}.md)`;
287
+ lines.push(`| ${link} | ${category.count} | View all ${category.theme.name.toLowerCase()} documents |`);
288
+ }
289
+ // Uncategorized section
290
+ if (plan.uncategorized.length > 0) {
291
+ lines.push(`| 📄 Other | ${plan.uncategorized.length} | Documents not matching specific themes |`);
292
+ }
293
+ lines.push('');
294
+ lines.push('## Quick Stats');
295
+ lines.push('');
296
+ lines.push(`- **Total Documents**: ${plan.totalFiles}`);
297
+ lines.push(`- **Categorized**: ${plan.totalFiles - plan.uncategorized.length}`);
298
+ lines.push(`- **Categories**: ${plan.themeCategories.filter(c => c.count >= 3).length}`);
299
+ lines.push('');
300
+ // Recent documents
301
+ const allFiles = plan.themeCategories.flatMap(c => c.files);
302
+ const recent = allFiles
303
+ .sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime())
304
+ .slice(0, 5);
305
+ if (recent.length > 0) {
306
+ lines.push('## Recently Updated');
307
+ lines.push('');
308
+ for (const file of recent) {
309
+ const themeDef = THEME_DEFINITIONS.find(t => t.id === file.primaryTheme);
310
+ const icon = themeDef?.icon ?? '📄';
311
+ lines.push(`- ${icon} [${file.title}](./${file.name}.md) - ${file.lastModified.toLocaleDateString()}`);
312
+ }
313
+ lines.push('');
314
+ }
315
+ lines.push('---');
316
+ lines.push('*Auto-generated by SmartDocOrganizer*');
317
+ return lines.join('\n');
318
+ }
319
+ /**
320
+ * Generate themed index with all files in that category
321
+ */
322
+ generateThemeIndex(category, folderPath) {
323
+ const lines = [];
324
+ lines.push('---');
325
+ lines.push('sidebar_position: 2');
326
+ lines.push(`title: "${category.theme.icon} ${category.theme.name}"`);
327
+ lines.push('---');
328
+ lines.push('');
329
+ lines.push(`# ${category.theme.icon} ${category.theme.name}`);
330
+ lines.push('');
331
+ lines.push(`**${category.count}** documents in this category.`);
332
+ lines.push('');
333
+ // Group by sub-theme if many files
334
+ if (category.count > 15) {
335
+ // Further sub-group by first keyword match
336
+ const subGroups = this.subGroupByKeyword(category.files, category.theme);
337
+ for (const [subGroup, files] of subGroups) {
338
+ if (files.length > 0) {
339
+ lines.push(`## ${subGroup}`);
340
+ lines.push('');
341
+ for (const file of files) {
342
+ lines.push(`- [${file.title}](./${file.name}.md)`);
343
+ }
344
+ lines.push('');
345
+ }
346
+ }
347
+ }
348
+ else {
349
+ // Simple list
350
+ lines.push('## Documents');
351
+ lines.push('');
352
+ for (const file of category.files) {
353
+ const date = file.lastModified.toLocaleDateString();
354
+ lines.push(`- [${file.title}](./${file.name}.md) *(${date})*`);
355
+ }
356
+ lines.push('');
357
+ }
358
+ lines.push('---');
359
+ lines.push(`[← Back to Categories](./_categories.md)`);
360
+ return lines.join('\n');
361
+ }
362
+ /**
363
+ * Sub-group files by keyword for large categories
364
+ */
365
+ subGroupByKeyword(files, theme) {
366
+ const groups = new Map();
367
+ // Initialize groups for main keywords
368
+ const mainKeywords = theme.keywords.slice(0, 4);
369
+ for (const kw of mainKeywords) {
370
+ groups.set(this.formatTitle(kw), []);
371
+ }
372
+ groups.set('Other', []);
373
+ for (const file of files) {
374
+ const content = `${file.name} ${file.title}`.toLowerCase();
375
+ let assigned = false;
376
+ for (const kw of mainKeywords) {
377
+ if (content.includes(kw)) {
378
+ groups.get(this.formatTitle(kw)).push(file);
379
+ assigned = true;
380
+ break;
381
+ }
382
+ }
383
+ if (!assigned) {
384
+ groups.get('Other').push(file);
385
+ }
386
+ }
387
+ // Remove empty groups
388
+ for (const [key, value] of groups) {
389
+ if (value.length === 0) {
390
+ groups.delete(key);
391
+ }
392
+ }
393
+ return groups;
394
+ }
395
+ /**
396
+ * Format string to title case, preserving known acronyms
397
+ */
398
+ formatTitle(str) {
399
+ const acronyms = ['adr', 'api', 'cli', 'hld', 'tdd', 'ado', 'pr', 'ci', 'cd', 'url', 'jwt', 'iac', 'sla', 'slo'];
400
+ return str
401
+ .replace(/[-_]/g, ' ')
402
+ .split(' ')
403
+ .map(word => {
404
+ const lower = word.toLowerCase();
405
+ if (acronyms.includes(lower)) {
406
+ return word.toUpperCase();
407
+ }
408
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
409
+ })
410
+ .join(' ');
411
+ }
412
+ /**
413
+ * Update Docusaurus sidebar configuration
414
+ */
415
+ async updateDocusaurusSidebar(plans) {
416
+ const sidebarConfig = [];
417
+ for (const plan of plans) {
418
+ if (!plan.needsOrganization)
419
+ continue;
420
+ const folderName = path.basename(plan.folder);
421
+ const categoryItems = [];
422
+ // Add main categories index first
423
+ categoryItems.push({
424
+ type: 'doc',
425
+ id: `${folderName}/_categories`,
426
+ label: '📚 Browse by Category',
427
+ });
428
+ // Add theme-specific indexes
429
+ for (const category of plan.themeCategories) {
430
+ if (category.count < 3)
431
+ continue;
432
+ categoryItems.push({
433
+ type: 'doc',
434
+ id: `${folderName}/_index-${category.theme.id}`,
435
+ label: `${category.theme.icon} ${category.theme.name}`,
436
+ });
437
+ }
438
+ // Add auto-generated items
439
+ categoryItems.push({
440
+ type: 'autogenerated',
441
+ dirName: folderName,
442
+ });
443
+ sidebarConfig.push({
444
+ type: 'category',
445
+ label: this.formatTitle(folderName),
446
+ items: categoryItems,
447
+ });
448
+ }
449
+ return JSON.stringify(sidebarConfig, null, 2);
450
+ }
451
+ /**
452
+ * Run full organization on internal docs
453
+ */
454
+ async organizeInternalDocs() {
455
+ const internalDocsPath = path.join(this.projectPath, '.specweave/docs/internal');
456
+ if (!fs.existsSync(internalDocsPath)) {
457
+ throw new Error('No .specweave/docs/internal directory found');
458
+ }
459
+ const plans = [];
460
+ const generatedFiles = [];
461
+ // Analyze key folders that might need organization
462
+ const foldersToAnalyze = [
463
+ 'architecture/adr',
464
+ 'architecture/hld',
465
+ 'architecture/concepts',
466
+ 'delivery/guides',
467
+ 'operations',
468
+ 'governance',
469
+ ];
470
+ for (const folder of foldersToAnalyze) {
471
+ const fullPath = path.join(internalDocsPath, folder);
472
+ if (!fs.existsSync(fullPath))
473
+ continue;
474
+ try {
475
+ const plan = await this.analyzeFolder(fullPath);
476
+ plans.push(plan);
477
+ if (plan.needsOrganization) {
478
+ const files = await this.generateCategoryIndexes(plan);
479
+ generatedFiles.push(...files);
480
+ }
481
+ }
482
+ catch (err) {
483
+ this.logger.warn(`Failed to analyze ${folder}: ${err}`);
484
+ }
485
+ }
486
+ // Generate summary
487
+ const summary = this.generateSummary(plans, generatedFiles);
488
+ return { plans, generatedFiles, summary };
489
+ }
490
+ /**
491
+ * Generate organization summary
492
+ */
493
+ generateSummary(plans, generatedFiles) {
494
+ const lines = [];
495
+ lines.push('# Documentation Organization Summary');
496
+ lines.push('');
497
+ const needsOrg = plans.filter(p => p.needsOrganization);
498
+ const totalFiles = plans.reduce((sum, p) => sum + p.totalFiles, 0);
499
+ lines.push(`- **Folders Analyzed**: ${plans.length}`);
500
+ lines.push(`- **Total Documents**: ${totalFiles}`);
501
+ lines.push(`- **Folders Needing Organization**: ${needsOrg.length}`);
502
+ lines.push(`- **Index Files Generated**: ${generatedFiles.length}`);
503
+ lines.push('');
504
+ if (needsOrg.length > 0) {
505
+ lines.push('## Organized Folders');
506
+ lines.push('');
507
+ for (const plan of needsOrg) {
508
+ lines.push(`### ${plan.folder}`);
509
+ lines.push(`- Files: ${plan.totalFiles}`);
510
+ lines.push(`- Categories: ${plan.themeCategories.filter(c => c.count >= 3).length}`);
511
+ lines.push('- Top themes:');
512
+ for (const cat of plan.themeCategories.slice(0, 5)) {
513
+ lines.push(` - ${cat.theme.icon} ${cat.theme.name}: ${cat.count} files`);
514
+ }
515
+ lines.push('');
516
+ }
517
+ }
518
+ lines.push('## Next Steps');
519
+ lines.push('');
520
+ lines.push('Run `/specweave-docs:preview` to view the organized documentation.');
521
+ lines.push('');
522
+ return lines.join('\n');
523
+ }
524
+ }
525
+ /**
526
+ * Convenience function to organize docs
527
+ */
528
+ export async function organizeDocumentation(projectPath, options) {
529
+ const organizer = new SmartDocOrganizer({
530
+ projectPath,
531
+ ...options,
532
+ });
533
+ return organizer.organizeInternalDocs();
534
+ }
535
+ //# sourceMappingURL=smart-doc-organizer.js.map