botdocs 0.1.0

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 (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/bin/botdocs.js +6 -0
  4. package/dist/src/builder/chunker.d.ts +41 -0
  5. package/dist/src/builder/chunker.js +139 -0
  6. package/dist/src/builder/embedder.d.ts +26 -0
  7. package/dist/src/builder/embedder.js +75 -0
  8. package/dist/src/builder/index.d.ts +5 -0
  9. package/dist/src/builder/index.js +142 -0
  10. package/dist/src/builder/markdown-processor.d.ts +26 -0
  11. package/dist/src/builder/markdown-processor.js +161 -0
  12. package/dist/src/builder/site-generator.d.ts +24 -0
  13. package/dist/src/builder/site-generator.js +152 -0
  14. package/dist/src/builder/template-engine.d.ts +30 -0
  15. package/dist/src/builder/template-engine.js +77 -0
  16. package/dist/src/builder/vector-db-builder.d.ts +20 -0
  17. package/dist/src/builder/vector-db-builder.js +63 -0
  18. package/dist/src/cli/index.d.ts +1 -0
  19. package/dist/src/cli/index.js +47 -0
  20. package/dist/src/cli/options.d.ts +7 -0
  21. package/dist/src/cli/options.js +5 -0
  22. package/dist/src/types/config.d.ts +24 -0
  23. package/dist/src/types/config.js +16 -0
  24. package/dist/src/types/document.d.ts +20 -0
  25. package/dist/src/types/document.js +1 -0
  26. package/dist/src/types/vector-db.d.ts +21 -0
  27. package/dist/src/types/vector-db.js +1 -0
  28. package/dist-client/assets/chatbox-CmTTiB2Z.js +1 -0
  29. package/dist-client/assets/rag-engine-L2vj3Y0G.js +7 -0
  30. package/dist-client/bundle.js +1 -0
  31. package/package.json +71 -0
  32. package/src/styles/chat.css +420 -0
  33. package/src/styles/main.css +532 -0
  34. package/src/styles/themes.css +78 -0
  35. package/src/templates/doc-page.html +27 -0
  36. package/src/templates/index.html +28 -0
  37. package/src/templates/layout.html +59 -0
@@ -0,0 +1,161 @@
1
+ import MarkdownIt from 'markdown-it';
2
+ import anchor from 'markdown-it-anchor';
3
+ import toc from 'markdown-it-toc-done-right';
4
+ import alerts from 'markdown-it-github-alerts';
5
+ import taskLists from 'markdown-it-task-lists';
6
+ import footnote from 'markdown-it-footnote';
7
+ import { full as emoji } from 'markdown-it-emoji';
8
+ import sub from 'markdown-it-sub';
9
+ import sup from 'markdown-it-sup';
10
+ import { bundledLanguages, getHighlighter } from 'shiki';
11
+ import matter from 'gray-matter';
12
+ import { relative, basename } from 'path';
13
+ export class MarkdownProcessor {
14
+ md;
15
+ shikiInitialized = false;
16
+ constructor() {
17
+ this.md = new MarkdownIt({
18
+ html: true,
19
+ linkify: true,
20
+ typographer: true,
21
+ breaks: false,
22
+ highlight: (code, lang, attrs) => {
23
+ // Fallback for when Shiki isn't initialized or lang not found
24
+ if (!lang) {
25
+ return `<pre><code>${this.escapeHtml(code)}</code></pre>`;
26
+ }
27
+ return `<pre><code class="language-${lang}">${this.escapeHtml(code)}</code></pre>`;
28
+ },
29
+ })
30
+ // Enable strikethrough (built-in feature)
31
+ .enable('strikethrough');
32
+ // Add anchor plugin for heading links
33
+ this.md.use(anchor, {
34
+ permalink: anchor.permalink.linkInsideHeader({
35
+ symbol: '#',
36
+ placement: 'before',
37
+ }),
38
+ });
39
+ // Add table of contents plugin
40
+ this.md.use(toc, {
41
+ containerClass: 'toc',
42
+ listType: 'ul',
43
+ });
44
+ // Add GitHub alerts plugin for [!NOTE], [!WARNING], etc.
45
+ this.md.use(alerts);
46
+ // Add task lists plugin for - [ ] and - [x]
47
+ this.md.use(taskLists, {
48
+ enabled: true,
49
+ label: true,
50
+ labelAfter: true,
51
+ });
52
+ // Add footnotes plugin for [^1] style references
53
+ this.md.use(footnote);
54
+ // Add emoji shortcuts plugin for :smile: → 😄
55
+ this.md.use(emoji);
56
+ // Add subscript and superscript support
57
+ this.md.use(sub);
58
+ this.md.use(sup);
59
+ }
60
+ escapeHtml(text) {
61
+ return text
62
+ .replace(/&/g, '&amp;')
63
+ .replace(/</g, '&lt;')
64
+ .replace(/>/g, '&gt;')
65
+ .replace(/"/g, '&quot;')
66
+ .replace(/'/g, '&#039;');
67
+ }
68
+ async setupShiki() {
69
+ if (this.shikiInitialized)
70
+ return;
71
+ try {
72
+ const highlighter = await getHighlighter({
73
+ themes: ['github-light', 'github-dark'],
74
+ langs: Object.keys(bundledLanguages),
75
+ });
76
+ // Override markdown-it highlight with Shiki, but with error handling
77
+ const originalHighlight = this.md.options.highlight;
78
+ this.md.options.highlight = (code, lang, attrs) => {
79
+ try {
80
+ if (!lang)
81
+ return originalHighlight(code, lang, attrs);
82
+ // Try to get the language, fall back to txt if not found
83
+ const languages = highlighter.getLoadedLanguages();
84
+ const safeLang = languages.includes(lang) ? lang : 'txt';
85
+ return highlighter.codeToHtml(code, {
86
+ lang: safeLang,
87
+ themes: {
88
+ light: 'github-light',
89
+ dark: 'github-dark',
90
+ },
91
+ });
92
+ }
93
+ catch (error) {
94
+ // Fall back to default highlighting if Shiki fails
95
+ return originalHighlight(code, lang, attrs);
96
+ }
97
+ };
98
+ this.shikiInitialized = true;
99
+ }
100
+ catch (error) {
101
+ console.warn('Failed to initialize Shiki, falling back to default code rendering');
102
+ }
103
+ }
104
+ /**
105
+ * Process a markdown file and extract front matter
106
+ */
107
+ async processFile(filePath, inputDir, content) {
108
+ // Ensure Shiki is initialized
109
+ await this.setupShiki();
110
+ // Parse front matter
111
+ const { data: metadata, content: markdownContent } = matter(content);
112
+ // Convert markdown to HTML
113
+ const html = this.md.render(markdownContent);
114
+ // Generate relative path and URL
115
+ const relativePath = relative(inputDir, filePath);
116
+ const url = this.generateUrl(relativePath);
117
+ // Extract title from metadata or first h1
118
+ const title = metadata.title || this.extractTitle(markdownContent, relativePath);
119
+ return {
120
+ filePath,
121
+ relativePath,
122
+ content: markdownContent,
123
+ html,
124
+ metadata: {
125
+ ...metadata,
126
+ title,
127
+ },
128
+ url,
129
+ };
130
+ }
131
+ /**
132
+ * Extract title from markdown content (first h1) or filename
133
+ */
134
+ extractTitle(content, relativePath) {
135
+ const h1Match = content.match(/^#\s+(.+)$/m);
136
+ if (h1Match) {
137
+ return h1Match[1];
138
+ }
139
+ // Fallback to filename
140
+ return basename(relativePath, '.md')
141
+ .replace(/-/g, ' ')
142
+ .replace(/\b\w/g, (char) => char.toUpperCase());
143
+ }
144
+ /**
145
+ * Generate URL from relative file path
146
+ * e.g., "getting-started.md" -> "/getting-started.html"
147
+ * e.g., "api/overview.md" -> "/api/overview.html"
148
+ */
149
+ generateUrl(relativePath) {
150
+ const url = relativePath
151
+ .replace(/\.md$/, '.html')
152
+ .replace(/\\/g, '/');
153
+ return url === 'index.html' ? '/' : `/${url}`;
154
+ }
155
+ /**
156
+ * Render markdown string to HTML
157
+ */
158
+ render(markdown) {
159
+ return this.md.render(markdown);
160
+ }
161
+ }
@@ -0,0 +1,24 @@
1
+ import { ProcessedDocument } from '../types/document.js';
2
+ import { BotdocsConfig } from '../types/config.js';
3
+ export declare class SiteGenerator {
4
+ private processor;
5
+ private templateEngine;
6
+ private documents;
7
+ constructor();
8
+ /**
9
+ * Generate the complete site
10
+ */
11
+ generate(inputDir: string, outputDir: string, config: BotdocsConfig): Promise<ProcessedDocument[]>;
12
+ /**
13
+ * Build navigation structure from documents
14
+ */
15
+ private buildNavigation;
16
+ /**
17
+ * Render navigation HTML
18
+ */
19
+ private renderNavigation;
20
+ /**
21
+ * Get all processed documents
22
+ */
23
+ getDocuments(): ProcessedDocument[];
24
+ }
@@ -0,0 +1,152 @@
1
+ import { glob } from 'glob';
2
+ import { readFileSync, writeFileSync } from 'fs';
3
+ import fs from 'fs-extra';
4
+ import { join, dirname, resolve } from 'path';
5
+ import { MarkdownProcessor } from './markdown-processor.js';
6
+ import { TemplateEngine } from './template-engine.js';
7
+ import { fileURLToPath } from 'url';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ export class SiteGenerator {
11
+ processor;
12
+ templateEngine;
13
+ documents = [];
14
+ constructor() {
15
+ this.processor = new MarkdownProcessor();
16
+ this.templateEngine = new TemplateEngine();
17
+ }
18
+ /**
19
+ * Generate the complete site
20
+ */
21
+ async generate(inputDir, outputDir, config) {
22
+ console.log('Processing markdown files...');
23
+ // Find all markdown files
24
+ const markdownFiles = await glob('**/*.md', {
25
+ cwd: inputDir,
26
+ absolute: true,
27
+ ignore: ['node_modules/**', '**/node_modules/**'],
28
+ });
29
+ if (markdownFiles.length === 0) {
30
+ throw new Error(`No markdown files found in ${inputDir}`);
31
+ }
32
+ console.log(`Found ${markdownFiles.length} markdown files`);
33
+ // Process all markdown files
34
+ for (const filePath of markdownFiles) {
35
+ const content = readFileSync(filePath, 'utf-8');
36
+ const doc = await this.processor.processFile(filePath, inputDir, content);
37
+ this.documents.push(doc);
38
+ }
39
+ // Sort documents by path for consistent ordering
40
+ this.documents.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
41
+ // Generate navigation
42
+ const navigation = this.buildNavigation(this.documents);
43
+ // Load templates from source directory (not dist)
44
+ // From dist/src/builder, go to project root, then to src/templates
45
+ const templatesDir = resolve(__dirname, '../../../src/templates');
46
+ const layoutTemplate = readFileSync(join(templatesDir, 'layout.html'), 'utf-8');
47
+ const docPageTemplate = readFileSync(join(templatesDir, 'doc-page.html'), 'utf-8');
48
+ const indexTemplate = readFileSync(join(templatesDir, 'index.html'), 'utf-8');
49
+ // Generate HTML pages
50
+ console.log('Generating HTML pages...');
51
+ for (let i = 0; i < this.documents.length; i++) {
52
+ const doc = this.documents[i];
53
+ const isIndex = doc.relativePath === 'README.md' || doc.relativePath === 'index.md';
54
+ // Prepare navigation data
55
+ const prevDoc = i > 0 ? this.documents[i - 1] : null;
56
+ const nextDoc = i < this.documents.length - 1 ? this.documents[i + 1] : null;
57
+ // Render document content
58
+ const contentTemplate = isIndex ? indexTemplate : docPageTemplate;
59
+ const content = this.templateEngine.renderWithLoops(contentTemplate, {
60
+ html: doc.html,
61
+ metadata: doc.metadata,
62
+ title: doc.metadata.title,
63
+ description: doc.metadata.description,
64
+ pages: isIndex
65
+ ? this.documents.filter(d => d !== doc).map(d => ({
66
+ title: d.metadata.title,
67
+ description: d.metadata.description,
68
+ url: d.url,
69
+ }))
70
+ : undefined,
71
+ prevPage: prevDoc
72
+ ? {
73
+ title: prevDoc.metadata.title,
74
+ url: prevDoc.url,
75
+ }
76
+ : undefined,
77
+ nextPage: nextDoc
78
+ ? {
79
+ title: nextDoc.metadata.title,
80
+ url: nextDoc.url,
81
+ }
82
+ : undefined,
83
+ });
84
+ // Render full page with layout
85
+ const html = this.templateEngine.renderWithLoops(layoutTemplate, {
86
+ title: doc.metadata.title || 'Documentation',
87
+ description: doc.metadata.description || config.description || '',
88
+ siteTitle: config.title || 'Documentation',
89
+ siteDescription: config.description || '',
90
+ content,
91
+ navigation: this.renderNavigation(navigation, doc.url),
92
+ chatEnabled: config.chat?.enabled,
93
+ });
94
+ // Write HTML file
95
+ const outputPath = join(outputDir, doc.relativePath.replace(/\.md$/, '.html'));
96
+ fs.ensureDirSync(dirname(outputPath));
97
+ writeFileSync(outputPath, html, 'utf-8');
98
+ }
99
+ // Copy index.html if README.md exists
100
+ const readmeDoc = this.documents.find((d) => d.relativePath === 'README.md' || d.relativePath === 'index.md');
101
+ if (readmeDoc) {
102
+ const readmePath = join(outputDir, readmeDoc.relativePath.replace(/\.md$/, '.html'));
103
+ const indexPath = join(outputDir, 'index.html');
104
+ if (readmePath !== indexPath) {
105
+ fs.copySync(readmePath, indexPath);
106
+ }
107
+ }
108
+ console.log(`Generated ${this.documents.length} HTML pages`);
109
+ return this.documents;
110
+ }
111
+ /**
112
+ * Build navigation structure from documents
113
+ */
114
+ buildNavigation(documents) {
115
+ const nav = [];
116
+ for (const doc of documents) {
117
+ const parts = doc.relativePath.split('/');
118
+ const title = doc.metadata.title || parts[parts.length - 1].replace(/\.md$/, '');
119
+ nav.push({
120
+ title,
121
+ url: doc.url,
122
+ });
123
+ }
124
+ return nav;
125
+ }
126
+ /**
127
+ * Render navigation HTML
128
+ */
129
+ renderNavigation(items, currentUrl) {
130
+ if (items.length === 0)
131
+ return '';
132
+ let html = '<ul class="nav-list">';
133
+ for (const item of items) {
134
+ const isActive = item.url === currentUrl;
135
+ const activeClass = isActive ? ' class="active"' : '';
136
+ html += `<li${activeClass}>`;
137
+ html += `<a href="${item.url}"${activeClass}>${item.title}</a>`;
138
+ if (item.children && item.children.length > 0) {
139
+ html += this.renderNavigation(item.children, currentUrl);
140
+ }
141
+ html += '</li>';
142
+ }
143
+ html += '</ul>';
144
+ return html;
145
+ }
146
+ /**
147
+ * Get all processed documents
148
+ */
149
+ getDocuments() {
150
+ return this.documents;
151
+ }
152
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Simple template engine using string interpolation
3
+ */
4
+ export declare class TemplateEngine {
5
+ /**
6
+ * Render a template with variables
7
+ * Replaces {{variableName}} with values from data object
8
+ */
9
+ render(template: string, data: Record<string, any>): string;
10
+ /**
11
+ * Render a template with nested variables
12
+ * Supports {{object.property}} syntax
13
+ */
14
+ renderAdvanced(template: string, data: Record<string, any>): string;
15
+ /**
16
+ * Get nested property from object using dot notation
17
+ */
18
+ private getNestedProperty;
19
+ /**
20
+ * Render template with conditional blocks
21
+ * Supports {{#if variable}}...{{/if}} syntax
22
+ * Handles nested conditionals recursively
23
+ */
24
+ renderWithConditionals(template: string, data: Record<string, any>): string;
25
+ /**
26
+ * Render template with loops
27
+ * Supports {{#each items}}...{{/each}} syntax
28
+ */
29
+ renderWithLoops(template: string, data: Record<string, any>): string;
30
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Simple template engine using string interpolation
3
+ */
4
+ export class TemplateEngine {
5
+ /**
6
+ * Render a template with variables
7
+ * Replaces {{variableName}} with values from data object
8
+ */
9
+ render(template, data) {
10
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
11
+ return data[key] !== undefined ? String(data[key]) : match;
12
+ });
13
+ }
14
+ /**
15
+ * Render a template with nested variables
16
+ * Supports {{object.property}} syntax
17
+ */
18
+ renderAdvanced(template, data) {
19
+ return template.replace(/\{\{([\w.]+)\}\}/g, (match, path) => {
20
+ const value = this.getNestedProperty(data, path);
21
+ return value !== undefined ? String(value) : match;
22
+ });
23
+ }
24
+ /**
25
+ * Get nested property from object using dot notation
26
+ */
27
+ getNestedProperty(obj, path) {
28
+ return path.split('.').reduce((current, key) => current?.[key], obj);
29
+ }
30
+ /**
31
+ * Render template with conditional blocks
32
+ * Supports {{#if variable}}...{{/if}} syntax
33
+ * Handles nested conditionals recursively
34
+ */
35
+ renderWithConditionals(template, data) {
36
+ let result = template;
37
+ let previousResult = '';
38
+ // Keep processing until no more conditionals are found (handles nesting)
39
+ while (result !== previousResult) {
40
+ previousResult = result;
41
+ // Match innermost if blocks first (non-greedy, no nested {{#if}} inside)
42
+ result = result.replace(/\{\{#if\s+([\w.]+)\}\}((?:(?!\{\{#if)[\s\S])*?)\{\{\/if\}\}/g, (match, key, content) => {
43
+ const value = this.getNestedProperty(data, key);
44
+ return value ? content : '';
45
+ });
46
+ }
47
+ // Handle variable substitution
48
+ result = this.renderAdvanced(result, data);
49
+ return result;
50
+ }
51
+ /**
52
+ * Render template with loops
53
+ * Supports {{#each items}}...{{/each}} syntax
54
+ */
55
+ renderWithLoops(template, data) {
56
+ // Handle each blocks
57
+ let result = template.replace(/\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (match, key, content) => {
58
+ const items = data[key];
59
+ if (!Array.isArray(items))
60
+ return '';
61
+ return items
62
+ .map((item, index) => {
63
+ // Process conditionals AND variables for each item
64
+ return this.renderWithConditionals(content, {
65
+ ...data,
66
+ ...item,
67
+ index,
68
+ '@index': index,
69
+ });
70
+ })
71
+ .join('');
72
+ });
73
+ // Handle conditionals and variables outside loops
74
+ result = this.renderWithConditionals(result, data);
75
+ return result;
76
+ }
77
+ }
@@ -0,0 +1,20 @@
1
+ import { ProcessedDocument } from '../types/document.js';
2
+ import { VectorDatabase } from '../types/vector-db.js';
3
+ export interface VectorDBBuilderOptions {
4
+ chunkSize?: number;
5
+ chunkOverlap?: number;
6
+ modelName?: string;
7
+ }
8
+ export declare class VectorDBBuilder {
9
+ private chunker;
10
+ private embedder;
11
+ constructor(options?: VectorDBBuilderOptions);
12
+ /**
13
+ * Build vector database from documents
14
+ */
15
+ build(documents: ProcessedDocument[], outputDir: string, verbose?: boolean): Promise<VectorDatabase>;
16
+ /**
17
+ * Generate unique ID for a chunk
18
+ */
19
+ private generateChunkId;
20
+ }
@@ -0,0 +1,63 @@
1
+ import { Chunker } from './chunker.js';
2
+ import { Embedder } from './embedder.js';
3
+ import { writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { createHash } from 'crypto';
6
+ export class VectorDBBuilder {
7
+ chunker;
8
+ embedder;
9
+ constructor(options = {}) {
10
+ this.chunker = new Chunker({
11
+ maxChunkSize: options.chunkSize || 500,
12
+ chunkOverlap: options.chunkOverlap || 50,
13
+ });
14
+ this.embedder = new Embedder(options.modelName);
15
+ }
16
+ /**
17
+ * Build vector database from documents
18
+ */
19
+ async build(documents, outputDir, verbose = false) {
20
+ if (verbose) {
21
+ console.log('Building vector database...');
22
+ }
23
+ // Step 1: Chunk documents
24
+ console.log('Chunking documents...');
25
+ const chunks = this.chunker.chunkDocuments(documents);
26
+ console.log(`Created ${chunks.length} chunks`);
27
+ // Step 2: Generate embeddings
28
+ console.log('Generating embeddings...');
29
+ await this.embedder.initialize();
30
+ const texts = chunks.map((chunk) => chunk.text);
31
+ const embeddings = await this.embedder.embedBatch(texts, 10);
32
+ // Step 3: Create document chunks with embeddings
33
+ const documentChunks = chunks.map((chunk, index) => ({
34
+ id: this.generateChunkId(chunk.text, chunk.metadata.sourceFile, index),
35
+ text: chunk.text,
36
+ embedding: embeddings[index],
37
+ metadata: chunk.metadata,
38
+ }));
39
+ // Step 4: Create vector database
40
+ const vectorDB = {
41
+ version: '1.0',
42
+ model: this.embedder.getModelName(),
43
+ dimension: this.embedder.getDimension(),
44
+ chunks: documentChunks,
45
+ };
46
+ // Step 5: Write to file
47
+ const outputPath = join(outputDir, 'vector-db.json');
48
+ writeFileSync(outputPath, JSON.stringify(vectorDB, null, 2), 'utf-8');
49
+ console.log(`Vector database saved: ${outputPath}`);
50
+ console.log(`Total chunks: ${vectorDB.chunks.length}`);
51
+ console.log(`Embedding dimension: ${vectorDB.dimension}`);
52
+ return vectorDB;
53
+ }
54
+ /**
55
+ * Generate unique ID for a chunk
56
+ */
57
+ generateChunkId(text, sourceFile, index) {
58
+ const hash = createHash('md5')
59
+ .update(`${sourceFile}:${index}:${text.substring(0, 100)}`)
60
+ .digest('hex');
61
+ return hash.substring(0, 16);
62
+ }
63
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { Command } from 'commander';
2
+ import { build } from '../builder/index.js';
3
+ import { defaultOptions } from './options.js';
4
+ import { readFileSync } from 'fs';
5
+ import { resolve, dirname } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ // Read package.json for version
10
+ // From dist/src/cli/index.js, go up 3 levels to project root
11
+ const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../../../package.json'), 'utf-8'));
12
+ const program = new Command();
13
+ program
14
+ .name('botdocs')
15
+ .description('Convert markdown documentation into a static website with AI chatbot')
16
+ .version(packageJson.version)
17
+ .argument('<input>', 'Input directory containing markdown files')
18
+ .option('-o, --output <dir>', 'Output directory for generated site', defaultOptions.output)
19
+ .option('--no-chat', 'Disable AI chatbot functionality')
20
+ .option('-c, --config <file>', 'Path to config file (botdocs.config.json)')
21
+ .option('-v, --verbose', 'Enable verbose logging')
22
+ .action(async (input, options) => {
23
+ try {
24
+ const inputDir = resolve(process.cwd(), input);
25
+ const outputDir = resolve(process.cwd(), options.output || defaultOptions.output);
26
+ if (options.verbose) {
27
+ console.log('Botdocs starting...');
28
+ console.log(`Input: ${inputDir}`);
29
+ console.log(`Output: ${outputDir}`);
30
+ console.log(`Chat enabled: ${!options.noChat}`);
31
+ }
32
+ await build({
33
+ inputDir,
34
+ outputDir,
35
+ chatEnabled: !options.noChat,
36
+ configPath: options.config,
37
+ verbose: options.verbose || false,
38
+ });
39
+ console.log('Build complete!');
40
+ console.log(`Site generated at: ${outputDir}`);
41
+ }
42
+ catch (error) {
43
+ console.error('Build failed:', error);
44
+ process.exit(1);
45
+ }
46
+ });
47
+ program.parse();
@@ -0,0 +1,7 @@
1
+ export interface CliOptions {
2
+ output?: string;
3
+ noChat?: boolean;
4
+ config?: string;
5
+ verbose?: boolean;
6
+ }
7
+ export declare const defaultOptions: Partial<CliOptions>;
@@ -0,0 +1,5 @@
1
+ export const defaultOptions = {
2
+ output: 'output',
3
+ noChat: false,
4
+ verbose: false,
5
+ };
@@ -0,0 +1,24 @@
1
+ export interface BotdocsConfig {
2
+ title?: string;
3
+ description?: string;
4
+ theme?: {
5
+ primaryColor?: string;
6
+ };
7
+ chat?: {
8
+ enabled?: boolean;
9
+ welcomeMessage?: string;
10
+ };
11
+ build?: {
12
+ chunkSize?: number;
13
+ chunkOverlap?: number;
14
+ topK?: number;
15
+ };
16
+ }
17
+ export interface BuildOptions {
18
+ inputDir: string;
19
+ outputDir: string;
20
+ chatEnabled: boolean;
21
+ configPath?: string;
22
+ verbose: boolean;
23
+ }
24
+ export declare const defaultConfig: BotdocsConfig;
@@ -0,0 +1,16 @@
1
+ export const defaultConfig = {
2
+ title: 'Documentation',
3
+ description: 'Project documentation',
4
+ theme: {
5
+ primaryColor: '#3b82f6',
6
+ },
7
+ chat: {
8
+ enabled: true,
9
+ welcomeMessage: 'Ask me anything about the docs!',
10
+ },
11
+ build: {
12
+ chunkSize: 500,
13
+ chunkOverlap: 50,
14
+ topK: 5,
15
+ },
16
+ };
@@ -0,0 +1,20 @@
1
+ export interface DocumentMetadata {
2
+ title?: string;
3
+ description?: string;
4
+ date?: string;
5
+ author?: string;
6
+ [key: string]: any;
7
+ }
8
+ export interface ProcessedDocument {
9
+ filePath: string;
10
+ relativePath: string;
11
+ content: string;
12
+ html: string;
13
+ metadata: DocumentMetadata;
14
+ url: string;
15
+ }
16
+ export interface NavigationItem {
17
+ title: string;
18
+ url: string;
19
+ children?: NavigationItem[];
20
+ }
@@ -0,0 +1 @@
1
+ export {};