skrypt-ai 0.4.2 → 0.6.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 (159) hide show
  1. package/dist/auth/index.d.ts +13 -3
  2. package/dist/auth/index.js +101 -9
  3. package/dist/auth/keychain.d.ts +5 -0
  4. package/dist/auth/keychain.js +82 -0
  5. package/dist/auth/notices.d.ts +3 -0
  6. package/dist/auth/notices.js +42 -0
  7. package/dist/autofix/index.d.ts +0 -4
  8. package/dist/autofix/index.js +10 -24
  9. package/dist/capture/browser.d.ts +11 -0
  10. package/dist/capture/browser.js +173 -0
  11. package/dist/capture/diff.d.ts +23 -0
  12. package/dist/capture/diff.js +52 -0
  13. package/dist/capture/index.d.ts +23 -0
  14. package/dist/capture/index.js +210 -0
  15. package/dist/capture/naming.d.ts +17 -0
  16. package/dist/capture/naming.js +45 -0
  17. package/dist/capture/parser.d.ts +15 -0
  18. package/dist/capture/parser.js +80 -0
  19. package/dist/capture/types.d.ts +57 -0
  20. package/dist/capture/types.js +1 -0
  21. package/dist/cli.js +20 -3
  22. package/dist/commands/autofix.js +136 -120
  23. package/dist/commands/cron.js +58 -47
  24. package/dist/commands/deploy.js +123 -102
  25. package/dist/commands/generate.js +125 -7
  26. package/dist/commands/heal.d.ts +10 -0
  27. package/dist/commands/heal.js +201 -0
  28. package/dist/commands/i18n.js +146 -111
  29. package/dist/commands/import.d.ts +2 -0
  30. package/dist/commands/import.js +157 -0
  31. package/dist/commands/init.js +19 -7
  32. package/dist/commands/lint.js +50 -44
  33. package/dist/commands/llms-txt.js +59 -49
  34. package/dist/commands/login.js +63 -34
  35. package/dist/commands/mcp.js +6 -0
  36. package/dist/commands/monitor.js +13 -8
  37. package/dist/commands/qa.d.ts +2 -0
  38. package/dist/commands/qa.js +43 -0
  39. package/dist/commands/review-pr.js +108 -92
  40. package/dist/commands/sdk.js +128 -122
  41. package/dist/commands/security.d.ts +2 -0
  42. package/dist/commands/security.js +109 -0
  43. package/dist/commands/test.js +91 -92
  44. package/dist/commands/version.js +104 -75
  45. package/dist/commands/watch.js +130 -114
  46. package/dist/config/types.js +2 -2
  47. package/dist/context-hub/index.d.ts +23 -0
  48. package/dist/context-hub/index.js +179 -0
  49. package/dist/context-hub/mappings.d.ts +8 -0
  50. package/dist/context-hub/mappings.js +55 -0
  51. package/dist/context-hub/types.d.ts +33 -0
  52. package/dist/context-hub/types.js +1 -0
  53. package/dist/generator/generator.js +39 -6
  54. package/dist/generator/types.d.ts +7 -0
  55. package/dist/generator/writer.d.ts +3 -1
  56. package/dist/generator/writer.js +36 -7
  57. package/dist/importers/confluence.d.ts +5 -0
  58. package/dist/importers/confluence.js +137 -0
  59. package/dist/importers/detect.d.ts +20 -0
  60. package/dist/importers/detect.js +121 -0
  61. package/dist/importers/docusaurus.d.ts +5 -0
  62. package/dist/importers/docusaurus.js +279 -0
  63. package/dist/importers/gitbook.d.ts +5 -0
  64. package/dist/importers/gitbook.js +189 -0
  65. package/dist/importers/github.d.ts +8 -0
  66. package/dist/importers/github.js +99 -0
  67. package/dist/importers/index.d.ts +15 -0
  68. package/dist/importers/index.js +30 -0
  69. package/dist/importers/markdown.d.ts +6 -0
  70. package/dist/importers/markdown.js +105 -0
  71. package/dist/importers/mintlify.d.ts +5 -0
  72. package/dist/importers/mintlify.js +172 -0
  73. package/dist/importers/notion.d.ts +5 -0
  74. package/dist/importers/notion.js +174 -0
  75. package/dist/importers/readme.d.ts +5 -0
  76. package/dist/importers/readme.js +184 -0
  77. package/dist/importers/transform.d.ts +90 -0
  78. package/dist/importers/transform.js +457 -0
  79. package/dist/importers/types.d.ts +37 -0
  80. package/dist/importers/types.js +1 -0
  81. package/dist/llm/anthropic-client.d.ts +1 -0
  82. package/dist/llm/anthropic-client.js +3 -1
  83. package/dist/llm/index.d.ts +6 -4
  84. package/dist/llm/index.js +76 -261
  85. package/dist/llm/openai-client.d.ts +1 -0
  86. package/dist/llm/openai-client.js +7 -2
  87. package/dist/plugins/index.js +7 -0
  88. package/dist/qa/checks.d.ts +10 -0
  89. package/dist/qa/checks.js +492 -0
  90. package/dist/qa/fixes.d.ts +30 -0
  91. package/dist/qa/fixes.js +277 -0
  92. package/dist/qa/index.d.ts +29 -0
  93. package/dist/qa/index.js +187 -0
  94. package/dist/qa/types.d.ts +24 -0
  95. package/dist/qa/types.js +1 -0
  96. package/dist/scanner/csharp.d.ts +23 -0
  97. package/dist/scanner/csharp.js +421 -0
  98. package/dist/scanner/index.js +53 -26
  99. package/dist/scanner/java.d.ts +39 -0
  100. package/dist/scanner/java.js +318 -0
  101. package/dist/scanner/kotlin.d.ts +23 -0
  102. package/dist/scanner/kotlin.js +389 -0
  103. package/dist/scanner/php.d.ts +57 -0
  104. package/dist/scanner/php.js +351 -0
  105. package/dist/scanner/python.js +17 -0
  106. package/dist/scanner/ruby.d.ts +36 -0
  107. package/dist/scanner/ruby.js +431 -0
  108. package/dist/scanner/swift.d.ts +25 -0
  109. package/dist/scanner/swift.js +392 -0
  110. package/dist/scanner/types.d.ts +1 -1
  111. package/dist/template/content/docs/_navigation.json +46 -0
  112. package/dist/template/content/docs/_sidebars.json +684 -0
  113. package/dist/template/content/docs/core.md +4544 -0
  114. package/dist/template/content/docs/index.mdx +89 -0
  115. package/dist/template/content/docs/integrations.md +1158 -0
  116. package/dist/template/content/docs/llms-full.md +403 -0
  117. package/dist/template/content/docs/llms.txt +4588 -0
  118. package/dist/template/content/docs/other.md +10379 -0
  119. package/dist/template/content/docs/tools.md +746 -0
  120. package/dist/template/content/docs/types.md +531 -0
  121. package/dist/template/docs.json +13 -11
  122. package/dist/template/mdx-components.tsx +27 -2
  123. package/dist/template/package.json +6 -0
  124. package/dist/template/public/search-index.json +1 -1
  125. package/dist/template/scripts/build-search-index.mjs +149 -13
  126. package/dist/template/src/app/api/chat/route.ts +83 -128
  127. package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
  128. package/dist/template/src/app/docs/llms-full.md +151 -4
  129. package/dist/template/src/app/docs/llms.txt +2464 -847
  130. package/dist/template/src/app/docs/page.mdx +48 -38
  131. package/dist/template/src/app/layout.tsx +3 -1
  132. package/dist/template/src/app/page.tsx +22 -8
  133. package/dist/template/src/components/ai-chat.tsx +73 -64
  134. package/dist/template/src/components/breadcrumbs.tsx +21 -23
  135. package/dist/template/src/components/copy-button.tsx +13 -9
  136. package/dist/template/src/components/copy-page-button.tsx +54 -0
  137. package/dist/template/src/components/docs-layout.tsx +37 -25
  138. package/dist/template/src/components/header.tsx +51 -10
  139. package/dist/template/src/components/mdx/card.tsx +17 -3
  140. package/dist/template/src/components/mdx/code-block.tsx +13 -9
  141. package/dist/template/src/components/mdx/code-group.tsx +13 -8
  142. package/dist/template/src/components/mdx/heading.tsx +15 -2
  143. package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
  144. package/dist/template/src/components/mdx/index.tsx +2 -0
  145. package/dist/template/src/components/mdx/mermaid.tsx +110 -0
  146. package/dist/template/src/components/mdx/screenshot.tsx +150 -0
  147. package/dist/template/src/components/scroll-to-hash.tsx +48 -0
  148. package/dist/template/src/components/sidebar.tsx +12 -18
  149. package/dist/template/src/components/table-of-contents.tsx +9 -0
  150. package/dist/template/src/lib/highlight.ts +3 -88
  151. package/dist/template/src/lib/navigation.ts +159 -0
  152. package/dist/template/src/lib/search-types.ts +4 -1
  153. package/dist/template/src/lib/search.ts +30 -7
  154. package/dist/template/src/styles/globals.css +17 -6
  155. package/dist/utils/files.d.ts +9 -1
  156. package/dist/utils/files.js +59 -10
  157. package/dist/utils/validation.d.ts +0 -3
  158. package/dist/utils/validation.js +0 -26
  159. package/package.json +5 -1
@@ -0,0 +1,121 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'fs';
2
+ import { join, extname } from 'path';
3
+ /**
4
+ * Auto-detect documentation format from a directory by checking marker files.
5
+ * First match wins (priority order).
6
+ */
7
+ export function detectFormat(dir) {
8
+ // 1. Mintlify: mint.json or docs.json with Mintlify structure
9
+ if (existsSync(join(dir, 'mint.json')))
10
+ return 'mintlify';
11
+ if (existsSync(join(dir, 'docs.json'))) {
12
+ try {
13
+ const content = JSON.parse(readFileSync(join(dir, 'docs.json'), 'utf-8'));
14
+ // Mintlify docs.json has navigation with group/pages AND one of: anchors, tabs, topbarCtaButton
15
+ if (content.navigation && Array.isArray(content.navigation) &&
16
+ (content.anchors || content.tabs || content.topbarCtaButton || content.topbarLinks)) {
17
+ return 'mintlify';
18
+ }
19
+ }
20
+ catch { /* not mintlify */ }
21
+ }
22
+ // 2. Docusaurus: docusaurus.config.js or .ts
23
+ if (existsSync(join(dir, 'docusaurus.config.js')) || existsSync(join(dir, 'docusaurus.config.ts'))) {
24
+ return 'docusaurus';
25
+ }
26
+ // 3. GitBook: SUMMARY.md or .gitbook.yaml
27
+ if (existsSync(join(dir, 'SUMMARY.md')) || existsSync(join(dir, '.gitbook.yaml'))) {
28
+ return 'gitbook';
29
+ }
30
+ // 4. ReadMe: files with [block: syntax or _order.yaml
31
+ if (hasReadmeMarkers(dir))
32
+ return 'readme';
33
+ // 5. Notion: files with <aside> + 32-char hex UUID filenames
34
+ if (hasNotionMarkers(dir))
35
+ return 'notion';
36
+ // 6. Confluence: .html files with <ac:structured-macro
37
+ if (hasConfluenceMarkers(dir))
38
+ return 'confluence';
39
+ // 7. MkDocs (handled by markdown importer)
40
+ if (existsSync(join(dir, 'mkdocs.yml')))
41
+ return 'markdown';
42
+ // 8. Fallback: any .md/.mdx files present
43
+ return 'markdown';
44
+ }
45
+ function hasReadmeMarkers(dir) {
46
+ try {
47
+ const entries = readdirSync(dir, { recursive: true });
48
+ for (const entry of entries) {
49
+ const fullPath = join(dir, entry);
50
+ if (entry === '_order.yaml' || entry.endsWith('/_order.yaml'))
51
+ return true;
52
+ if (extname(entry) === '.md') {
53
+ try {
54
+ const content = readFileSync(fullPath, 'utf-8');
55
+ if (content.includes('[block:'))
56
+ return true;
57
+ }
58
+ catch { /* skip */ }
59
+ }
60
+ }
61
+ }
62
+ catch { /* skip */ }
63
+ return false;
64
+ }
65
+ function hasNotionMarkers(dir) {
66
+ const UUID_PATTERN = /[0-9a-f]{32}/;
67
+ try {
68
+ const entries = readdirSync(dir);
69
+ for (const entry of entries) {
70
+ if (extname(entry) === '.md' && UUID_PATTERN.test(entry)) {
71
+ try {
72
+ const content = readFileSync(join(dir, entry), 'utf-8');
73
+ if (content.includes('<aside>'))
74
+ return true;
75
+ }
76
+ catch { /* skip */ }
77
+ }
78
+ }
79
+ }
80
+ catch { /* skip */ }
81
+ return false;
82
+ }
83
+ function hasConfluenceMarkers(dir) {
84
+ try {
85
+ const entries = readdirSync(dir);
86
+ for (const entry of entries) {
87
+ if (extname(entry) === '.html') {
88
+ try {
89
+ const content = readFileSync(join(dir, entry), 'utf-8');
90
+ if (content.includes('<ac:structured-macro'))
91
+ return true;
92
+ }
93
+ catch { /* skip */ }
94
+ }
95
+ }
96
+ }
97
+ catch { /* skip */ }
98
+ return false;
99
+ }
100
+ /**
101
+ * Check if a string is a GitHub URL
102
+ */
103
+ export function isGitHubUrl(input) {
104
+ return /^https?:\/\/(www\.)?github\.com\//.test(input);
105
+ }
106
+ /**
107
+ * Parse a GitHub URL into components
108
+ * Supports: https://github.com/owner/repo/tree/branch/path
109
+ */
110
+ export function parseGitHubUrl(url) {
111
+ const match = url.match(/^https?:\/\/(www\.)?github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.*))?)?/);
112
+ if (!match) {
113
+ throw new Error(`Invalid GitHub URL: ${url}`);
114
+ }
115
+ return {
116
+ owner: match[2],
117
+ repo: match[3],
118
+ ref: match[4] || 'main',
119
+ path: match[5] || '',
120
+ };
121
+ }
@@ -0,0 +1,5 @@
1
+ import type { ImportResult } from './types.js';
2
+ /**
3
+ * Import Docusaurus documentation.
4
+ */
5
+ export declare function importDocusaurus(dir: string, name?: string): ImportResult;
@@ -0,0 +1,279 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
+ import { join, relative, basename, extname } from 'path';
3
+ import { findMdxFiles } from '../utils/files.js';
4
+ import { transformDocusaurusAdmonitions, transformDocusaurusTabs, stripDocusaurusImports, normalizeFrontmatter, getSortWeight, rewriteImagePaths, } from './transform.js';
5
+ /**
6
+ * Import Docusaurus documentation.
7
+ */
8
+ export function importDocusaurus(dir, name) {
9
+ const result = createEmptyResult(name);
10
+ const stats = { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 };
11
+ // Extract project info from docusaurus.config.js
12
+ const configPath = existsSync(join(dir, 'docusaurus.config.ts'))
13
+ ? join(dir, 'docusaurus.config.ts')
14
+ : join(dir, 'docusaurus.config.js');
15
+ if (existsSync(configPath)) {
16
+ const configContent = readFileSync(configPath, 'utf-8');
17
+ const titleMatch = configContent.match(/title:\s*['"]([^'"]+)['"]/);
18
+ const taglineMatch = configContent.match(/tagline:\s*['"]([^'"]+)['"]/);
19
+ if (titleMatch)
20
+ result.name = titleMatch[1];
21
+ if (taglineMatch)
22
+ result.description = taglineMatch[1];
23
+ }
24
+ if (name)
25
+ result.name = name;
26
+ // Find docs directory
27
+ const docsDir = existsSync(join(dir, 'docs')) ? join(dir, 'docs') : dir;
28
+ // Try to parse sidebars.js for navigation
29
+ const sidebarNav = parseSidebars(dir, docsDir);
30
+ if (sidebarNav && sidebarNav.length > 0) {
31
+ for (const group of sidebarNav) {
32
+ const pages = [];
33
+ for (const pageId of group.pageIds) {
34
+ const page = processDocusaurusPage(docsDir, pageId, stats, result);
35
+ if (page)
36
+ pages.push(page);
37
+ }
38
+ if (pages.length > 0) {
39
+ result.navigation.push({ group: group.label, pages });
40
+ }
41
+ }
42
+ }
43
+ else {
44
+ // Fallback: walk docs/ with _category_.json
45
+ const groups = walkDocsDirectory(docsDir);
46
+ for (const group of groups) {
47
+ const pages = [];
48
+ for (const file of group.files) {
49
+ const pageId = relative(docsDir, file).replace(/\.(md|mdx)$/, '').replace(/\\/g, '/');
50
+ const page = processDocusaurusPage(docsDir, pageId, stats, result);
51
+ if (page)
52
+ pages.push(page);
53
+ }
54
+ if (pages.length > 0) {
55
+ result.navigation.push({ group: group.label, pages });
56
+ }
57
+ }
58
+ }
59
+ // Copy static assets
60
+ const staticDir = join(dir, 'static');
61
+ if (existsSync(staticDir)) {
62
+ collectStaticAssets(staticDir, result, stats);
63
+ }
64
+ result.stats = {
65
+ pages: result.files.size,
66
+ groups: result.navigation.length,
67
+ transforms: stats,
68
+ };
69
+ return result;
70
+ }
71
+ function processDocusaurusPage(docsDir, pageId, stats, result) {
72
+ let filePath = join(docsDir, `${pageId}.mdx`);
73
+ if (!existsSync(filePath))
74
+ filePath = join(docsDir, `${pageId}.md`);
75
+ if (!existsSync(filePath))
76
+ filePath = join(docsDir, pageId, 'index.mdx');
77
+ if (!existsSync(filePath))
78
+ filePath = join(docsDir, pageId, 'index.md');
79
+ if (!existsSync(filePath)) {
80
+ result.warnings.push(`Page not found: ${pageId}`);
81
+ return null;
82
+ }
83
+ let content = readFileSync(filePath, 'utf-8');
84
+ const originalContent = content;
85
+ // Apply transforms
86
+ content = stripDocusaurusImports(content);
87
+ content = transformDocusaurusAdmonitions(content);
88
+ content = transformDocusaurusTabs(content);
89
+ content = normalizeFrontmatter(content);
90
+ // Strip Docusaurus-specific code fence metadata ({1,3-5} line highlighting)
91
+ content = content.replace(/^(```\w+)(?:\s+\{[\d,-]+\})/gm, '$1');
92
+ // Count transforms
93
+ stats.callouts += countNewOccurrences(originalContent, content, '<Callout');
94
+ stats.tabs += countNewOccurrences(originalContent, content, '<TabPanel');
95
+ const title = extractTitle(content, filePath);
96
+ const slug = pageId.replace(/\\/g, '/');
97
+ const outputPath = `content/docs/${slug}.mdx`;
98
+ result.files.set(outputPath, content);
99
+ return { title, slug, sourcePath: relative(docsDir, filePath), content };
100
+ }
101
+ function parseSidebars(rootDir, docsDir) {
102
+ const sidebarPath = join(rootDir, 'sidebars.js');
103
+ if (!existsSync(sidebarPath))
104
+ return null;
105
+ try {
106
+ const content = readFileSync(sidebarPath, 'utf-8');
107
+ const groups = [];
108
+ // Match category objects: { type: 'category', label: 'X', items: [...] }
109
+ const categoryRegex = /\{\s*type:\s*['"]category['"],\s*label:\s*['"]([^'"]+)['"]\s*,\s*items:\s*\[([\s\S]*?)\]\s*\}/g;
110
+ let match;
111
+ while ((match = categoryRegex.exec(content)) !== null) {
112
+ const label = match[1];
113
+ const itemsStr = match[2];
114
+ const pageIds = extractPageIds(itemsStr);
115
+ groups.push({ label, pageIds });
116
+ }
117
+ // Match autogenerated: { type: 'autogenerated', dirName: 'x' }
118
+ const autoRegex = /\{\s*type:\s*['"]autogenerated['"],\s*dirName:\s*['"]([^'"]+)['"]\s*\}/g;
119
+ while ((match = autoRegex.exec(content)) !== null) {
120
+ const dirName = match[1];
121
+ const subDir = join(docsDir, dirName);
122
+ if (existsSync(subDir)) {
123
+ const files = findMdxFiles(subDir);
124
+ const pageIds = files.map(f => relative(docsDir, f).replace(/\.(md|mdx)$/, '').replace(/\\/g, '/'));
125
+ const label = dirName.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
126
+ groups.push({ label, pageIds });
127
+ }
128
+ }
129
+ // Match top-level string references: 'intro', 'getting-started'
130
+ if (groups.length === 0) {
131
+ const stringRefs = content.match(/['"]([a-zA-Z0-9/_-]+)['"]/g);
132
+ if (stringRefs) {
133
+ const pageIds = stringRefs.map(s => s.replace(/['"]/g, '')).filter(s => !['category', 'doc', 'autogenerated'].includes(s));
134
+ if (pageIds.length > 0) {
135
+ groups.push({ label: 'Documentation', pageIds });
136
+ }
137
+ }
138
+ }
139
+ return groups.length > 0 ? groups : null;
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ function extractPageIds(itemsStr) {
146
+ const ids = [];
147
+ // String shorthand: 'page-id'
148
+ const stringRegex = /['"]([a-zA-Z0-9/_-]+)['"]/g;
149
+ let match;
150
+ while ((match = stringRegex.exec(itemsStr)) !== null) {
151
+ const val = match[1];
152
+ if (!['doc', 'category', 'autogenerated'].includes(val)) {
153
+ ids.push(val);
154
+ }
155
+ }
156
+ return ids;
157
+ }
158
+ function walkDocsDirectory(docsDir) {
159
+ const groups = [];
160
+ // Root-level files
161
+ const rootFiles = getMdFilesInDir(docsDir);
162
+ if (rootFiles.length > 0) {
163
+ groups.push({ label: 'Documentation', files: rootFiles, position: 0 });
164
+ }
165
+ // Subdirectories
166
+ try {
167
+ const entries = readdirSync(docsDir);
168
+ for (const entry of entries) {
169
+ const fullPath = join(docsDir, entry);
170
+ if (!statSync(fullPath).isDirectory() || entry.startsWith('.') || entry === 'node_modules')
171
+ continue;
172
+ const files = findMdxFiles(fullPath);
173
+ if (files.length === 0)
174
+ continue;
175
+ // Read _category_.json for label and position
176
+ let label = entry.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
177
+ let position = Infinity;
178
+ const categoryPath = join(fullPath, '_category_.json');
179
+ if (existsSync(categoryPath)) {
180
+ try {
181
+ const cat = JSON.parse(readFileSync(categoryPath, 'utf-8'));
182
+ if (cat.label)
183
+ label = cat.label;
184
+ if (typeof cat.position === 'number')
185
+ position = cat.position;
186
+ }
187
+ catch { /* skip */ }
188
+ }
189
+ groups.push({ label, files: sortByWeight(files), position });
190
+ }
191
+ }
192
+ catch { /* skip */ }
193
+ return groups.sort((a, b) => a.position - b.position);
194
+ }
195
+ function getMdFilesInDir(dir) {
196
+ try {
197
+ return readdirSync(dir)
198
+ .filter(f => /\.(md|mdx)$/.test(f))
199
+ .map(f => join(dir, f));
200
+ }
201
+ catch {
202
+ return [];
203
+ }
204
+ }
205
+ function sortByWeight(files) {
206
+ return files.sort((a, b) => {
207
+ const aName = basename(a, extname(a)).toLowerCase();
208
+ const bName = basename(b, extname(b)).toLowerCase();
209
+ if (aName === 'index' || aName === 'readme')
210
+ return -1;
211
+ if (bName === 'index' || bName === 'readme')
212
+ return 1;
213
+ try {
214
+ const aWeight = getSortWeight(readFileSync(a, 'utf-8'));
215
+ const bWeight = getSortWeight(readFileSync(b, 'utf-8'));
216
+ if (aWeight !== bWeight)
217
+ return aWeight - bWeight;
218
+ }
219
+ catch { /* skip */ }
220
+ return aName.localeCompare(bName);
221
+ });
222
+ }
223
+ function collectStaticAssets(staticDir, result, stats) {
224
+ const imgExts = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']);
225
+ const imageMapping = new Map();
226
+ function walk(dir) {
227
+ try {
228
+ for (const entry of readdirSync(dir)) {
229
+ const fullPath = join(dir, entry);
230
+ if (statSync(fullPath).isDirectory()) {
231
+ walk(fullPath);
232
+ }
233
+ else if (imgExts.has(extname(entry).toLowerCase())) {
234
+ const rel = relative(staticDir, fullPath);
235
+ const dest = `public/${rel}`;
236
+ result.assets.set(dest, fullPath);
237
+ imageMapping.set(`/static/${rel}`, `/${rel}`);
238
+ imageMapping.set(`static/${rel}`, `/${rel}`);
239
+ stats.images++;
240
+ }
241
+ }
242
+ }
243
+ catch { /* skip */ }
244
+ }
245
+ walk(staticDir);
246
+ // Rewrite image paths
247
+ if (imageMapping.size > 0) {
248
+ for (const [path, content] of result.files) {
249
+ result.files.set(path, rewriteImagePaths(content, imageMapping));
250
+ }
251
+ }
252
+ }
253
+ function extractTitle(content, filePath) {
254
+ const fmMatch = content.match(/^---\n[\s\S]*?title:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
255
+ if (fmMatch)
256
+ return fmMatch[1].trim();
257
+ const h1Match = content.match(/^#\s+(.+)$/m);
258
+ if (h1Match)
259
+ return h1Match[1].trim();
260
+ return basename(filePath, extname(filePath)).replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
261
+ }
262
+ function countNewOccurrences(original, transformed, marker) {
263
+ const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
264
+ const origCount = (original.match(new RegExp(escaped, 'g')) || []).length;
265
+ const newCount = (transformed.match(new RegExp(escaped, 'g')) || []).length;
266
+ return Math.max(0, newCount - origCount);
267
+ }
268
+ function createEmptyResult(name) {
269
+ return {
270
+ navigation: [],
271
+ name: name || 'Documentation',
272
+ description: 'Imported from Docusaurus',
273
+ files: new Map(),
274
+ assets: new Map(),
275
+ warnings: [],
276
+ stats: { pages: 0, groups: 0, transforms: { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 } },
277
+ sourceFormat: 'docusaurus',
278
+ };
279
+ }
@@ -0,0 +1,5 @@
1
+ import type { ImportResult } from './types.js';
2
+ /**
3
+ * Import GitBook documentation.
4
+ */
5
+ export declare function importGitBook(dir: string, name?: string): ImportResult;
@@ -0,0 +1,189 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
+ import { join, relative, basename, extname } from 'path';
3
+ import { findMdxFiles } from '../utils/files.js';
4
+ import { transformGitBookHints, transformGitBookTabs, transformGitBookSteps, transformGitBookExpandable, transformGitBookContentRef, transformGitBookEmbed, normalizeFrontmatter, rewriteImagePaths, } from './transform.js';
5
+ /**
6
+ * Import GitBook documentation.
7
+ */
8
+ export function importGitBook(dir, name) {
9
+ const result = createEmptyResult(name);
10
+ const stats = { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 };
11
+ // Check for .gitbook.yaml root config
12
+ let rootPath = '';
13
+ const gitbookYaml = join(dir, '.gitbook.yaml');
14
+ if (existsSync(gitbookYaml)) {
15
+ try {
16
+ const yamlContent = readFileSync(gitbookYaml, 'utf-8');
17
+ const rootMatch = yamlContent.match(/root:\s*(.+)/);
18
+ if (rootMatch)
19
+ rootPath = rootMatch[1].trim().replace(/^\.\//, '');
20
+ }
21
+ catch { /* skip */ }
22
+ }
23
+ const contentDir = rootPath ? join(dir, rootPath) : dir;
24
+ // Parse SUMMARY.md for navigation
25
+ const summaryPath = join(contentDir, 'SUMMARY.md');
26
+ if (existsSync(summaryPath)) {
27
+ const summaryContent = readFileSync(summaryPath, 'utf-8');
28
+ const groups = parseSummaryMd(summaryContent);
29
+ for (const group of groups) {
30
+ const pages = [];
31
+ for (const ref of group.pageRefs) {
32
+ const page = processGitBookPage(contentDir, ref.path, ref.title, stats, result);
33
+ if (page)
34
+ pages.push(page);
35
+ }
36
+ if (pages.length > 0) {
37
+ result.navigation.push({ group: group.label, pages });
38
+ }
39
+ }
40
+ }
41
+ else {
42
+ // No SUMMARY.md — walk files
43
+ const files = findMdxFiles(contentDir);
44
+ const pages = [];
45
+ for (const filePath of files) {
46
+ if (basename(filePath).toUpperCase() === 'SUMMARY.MD')
47
+ continue;
48
+ const rel = relative(contentDir, filePath);
49
+ const page = processGitBookPage(contentDir, rel, undefined, stats, result);
50
+ if (page)
51
+ pages.push(page);
52
+ }
53
+ if (pages.length > 0) {
54
+ result.navigation.push({ group: 'Documentation', pages });
55
+ }
56
+ }
57
+ // Copy .gitbook/assets/
58
+ const assetsDir = join(dir, '.gitbook', 'assets');
59
+ if (existsSync(assetsDir)) {
60
+ collectGitBookAssets(assetsDir, result, stats);
61
+ }
62
+ result.stats = {
63
+ pages: result.files.size,
64
+ groups: result.navigation.length,
65
+ transforms: stats,
66
+ };
67
+ return result;
68
+ }
69
+ function parseSummaryMd(content) {
70
+ const groups = [];
71
+ let currentGroup = null;
72
+ const lines = content.split('\n');
73
+ for (const line of lines) {
74
+ // Group headers: ## Group Name or non-indented text without link
75
+ const headerMatch = line.match(/^#{1,3}\s+(.+)/);
76
+ if (headerMatch) {
77
+ if (currentGroup && currentGroup.pageRefs.length > 0) {
78
+ groups.push(currentGroup);
79
+ }
80
+ currentGroup = { label: headerMatch[1].trim(), pageRefs: [] };
81
+ continue;
82
+ }
83
+ // Page references: * [Title](path.md) or - [Title](path.md)
84
+ const linkMatch = line.match(/^[\s]*[*-]\s+\[([^\]]+)\]\(([^)]+)\)/);
85
+ if (linkMatch) {
86
+ if (!currentGroup) {
87
+ currentGroup = { label: 'Documentation', pageRefs: [] };
88
+ }
89
+ currentGroup.pageRefs.push({ title: linkMatch[1], path: linkMatch[2] });
90
+ }
91
+ }
92
+ if (currentGroup && currentGroup.pageRefs.length > 0) {
93
+ groups.push(currentGroup);
94
+ }
95
+ // If no groups found, treat all links as one group
96
+ if (groups.length === 0) {
97
+ const allLinks = [];
98
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
99
+ let match;
100
+ while ((match = linkRegex.exec(content)) !== null) {
101
+ allLinks.push({ title: match[1], path: match[2] });
102
+ }
103
+ if (allLinks.length > 0) {
104
+ groups.push({ label: 'Documentation', pageRefs: allLinks });
105
+ }
106
+ }
107
+ return groups;
108
+ }
109
+ function processGitBookPage(contentDir, pagePath, title, stats, result) {
110
+ const filePath = join(contentDir, pagePath);
111
+ // Guard: prevent path traversal outside content directory
112
+ if (!filePath.startsWith(contentDir)) {
113
+ result.warnings.push(`Path traversal blocked: ${pagePath}`);
114
+ return null;
115
+ }
116
+ if (!existsSync(filePath)) {
117
+ result.warnings.push(`Page not found: ${pagePath}`);
118
+ return null;
119
+ }
120
+ let content = readFileSync(filePath, 'utf-8');
121
+ const originalContent = content;
122
+ // Apply all GitBook transforms
123
+ content = transformGitBookHints(content);
124
+ content = transformGitBookTabs(content);
125
+ content = transformGitBookSteps(content);
126
+ content = transformGitBookExpandable(content);
127
+ content = transformGitBookContentRef(content);
128
+ content = transformGitBookEmbed(content);
129
+ content = normalizeFrontmatter(content);
130
+ // Count transforms
131
+ stats.callouts += countNewOccurrences(originalContent, content, '<Callout');
132
+ stats.tabs += countNewOccurrences(originalContent, content, '<TabPanel');
133
+ stats.steps += countNewOccurrences(originalContent, content, '<Step>');
134
+ stats.accordions += countNewOccurrences(originalContent, content, '<Accordion');
135
+ const pageTitle = title || extractTitle(content, filePath);
136
+ const slug = pagePath.replace(/\.(md|mdx)$/, '').replace(/\\/g, '/');
137
+ const outputPath = `content/docs/${slug}.mdx`;
138
+ result.files.set(outputPath, content);
139
+ return { title: pageTitle, slug, sourcePath: pagePath, content };
140
+ }
141
+ function collectGitBookAssets(assetsDir, result, stats) {
142
+ const imageMapping = new Map();
143
+ try {
144
+ for (const entry of readdirSync(assetsDir)) {
145
+ const fullPath = join(assetsDir, entry);
146
+ if (statSync(fullPath).isFile()) {
147
+ const dest = `public/images/${entry}`;
148
+ result.assets.set(dest, fullPath);
149
+ imageMapping.set(`.gitbook/assets/${entry}`, `/images/${entry}`);
150
+ imageMapping.set(`../.gitbook/assets/${entry}`, `/images/${entry}`);
151
+ stats.images++;
152
+ }
153
+ }
154
+ }
155
+ catch { /* skip */ }
156
+ // Rewrite image paths
157
+ if (imageMapping.size > 0) {
158
+ for (const [path, content] of result.files) {
159
+ result.files.set(path, rewriteImagePaths(content, imageMapping));
160
+ }
161
+ }
162
+ }
163
+ function extractTitle(content, filePath) {
164
+ const fmMatch = content.match(/^---\n[\s\S]*?title:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
165
+ if (fmMatch)
166
+ return fmMatch[1].trim();
167
+ const h1Match = content.match(/^#\s+(.+)$/m);
168
+ if (h1Match)
169
+ return h1Match[1].trim();
170
+ return basename(filePath, extname(filePath)).replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
171
+ }
172
+ function countNewOccurrences(original, transformed, marker) {
173
+ const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
174
+ const origCount = (original.match(new RegExp(escaped, 'g')) || []).length;
175
+ const newCount = (transformed.match(new RegExp(escaped, 'g')) || []).length;
176
+ return Math.max(0, newCount - origCount);
177
+ }
178
+ function createEmptyResult(name) {
179
+ return {
180
+ navigation: [],
181
+ name: name || 'Documentation',
182
+ description: 'Imported from GitBook',
183
+ files: new Map(),
184
+ assets: new Map(),
185
+ warnings: [],
186
+ stats: { pages: 0, groups: 0, transforms: { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 } },
187
+ sourceFormat: 'gitbook',
188
+ };
189
+ }
@@ -0,0 +1,8 @@
1
+ import type { ImportResult, ImportFormat } from './types.js';
2
+ /**
3
+ * Fetch docs from a GitHub URL and import them.
4
+ */
5
+ export declare function importFromGitHub(owner: string, repo: string, path: string, ref: string, options?: {
6
+ format?: ImportFormat;
7
+ name?: string;
8
+ }): Promise<ImportResult>;