skrypt-ai 0.3.4 → 0.4.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.
- package/README.md +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +3 -5
- package/dist/autofix/index.js +15 -3
- package/dist/cli.js +19 -4
- package/dist/commands/check-links.js +164 -174
- package/dist/commands/deploy.js +5 -2
- package/dist/commands/generate.js +206 -199
- package/dist/commands/i18n.js +3 -20
- package/dist/commands/init.js +47 -40
- package/dist/commands/lint.js +3 -20
- package/dist/commands/mcp.js +125 -122
- package/dist/commands/monitor.js +125 -108
- package/dist/commands/review-pr.js +1 -1
- package/dist/commands/sdk.js +1 -1
- package/dist/config/loader.js +21 -2
- package/dist/generator/organizer.d.ts +3 -0
- package/dist/generator/organizer.js +4 -9
- package/dist/generator/writer.js +2 -10
- package/dist/github/pr-comments.js +21 -8
- package/dist/plugins/index.js +1 -0
- package/dist/scanner/index.js +8 -2
- package/dist/template/docs.json +2 -1
- package/dist/template/next.config.mjs +2 -1
- package/dist/template/package.json +17 -15
- package/dist/template/public/favicon.svg +4 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +120 -25
- package/dist/template/src/app/api/chat/route.ts +11 -3
- package/dist/template/src/app/docs/README.md +28 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
- package/dist/template/src/app/docs/auth/page.mdx +589 -0
- package/dist/template/src/app/docs/autofix/page.mdx +624 -0
- package/dist/template/src/app/docs/cli/page.mdx +217 -0
- package/dist/template/src/app/docs/config/page.mdx +428 -0
- package/dist/template/src/app/docs/configuration/page.mdx +86 -0
- package/dist/template/src/app/docs/deployment/page.mdx +112 -0
- package/dist/template/src/app/docs/error.tsx +20 -0
- package/dist/template/src/app/docs/generator/generator.md +504 -0
- package/dist/template/src/app/docs/generator/organizer.md +779 -0
- package/dist/template/src/app/docs/generator/page.mdx +613 -0
- package/dist/template/src/app/docs/github/page.mdx +502 -0
- package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
- package/dist/template/src/app/docs/llm/index.md +471 -0
- package/dist/template/src/app/docs/llm/page.mdx +428 -0
- package/dist/template/src/app/docs/llms-full.md +256 -0
- package/dist/template/src/app/docs/llms.txt +2971 -0
- package/dist/template/src/app/docs/not-found.tsx +23 -0
- package/dist/template/src/app/docs/page.mdx +0 -3
- package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
- package/dist/template/src/app/docs/pro/page.mdx +121 -0
- package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
- package/dist/template/src/app/docs/scanner/content-type.md +599 -0
- package/dist/template/src/app/docs/scanner/index.md +212 -0
- package/dist/template/src/app/docs/scanner/page.mdx +307 -0
- package/dist/template/src/app/docs/scanner/python.md +469 -0
- package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
- package/dist/template/src/app/docs/scanner/rust.md +325 -0
- package/dist/template/src/app/docs/scanner/typescript.md +201 -0
- package/dist/template/src/app/error.tsx +3 -3
- package/dist/template/src/app/icon.tsx +29 -0
- package/dist/template/src/app/layout.tsx +42 -0
- package/dist/template/src/app/not-found.tsx +35 -0
- package/dist/template/src/app/page.tsx +62 -28
- package/dist/template/src/components/ai-chat.tsx +26 -21
- package/dist/template/src/components/breadcrumbs.tsx +46 -2
- package/dist/template/src/components/copy-button.tsx +17 -3
- package/dist/template/src/components/docs-layout.tsx +142 -8
- package/dist/template/src/components/feedback.tsx +4 -2
- package/dist/template/src/components/footer.tsx +42 -0
- package/dist/template/src/components/header.tsx +29 -5
- package/dist/template/src/components/mdx/accordion.tsx +7 -6
- package/dist/template/src/components/mdx/card.tsx +19 -7
- package/dist/template/src/components/mdx/code-block.tsx +17 -3
- package/dist/template/src/components/mdx/code-group.tsx +65 -18
- package/dist/template/src/components/mdx/code-playground.tsx +3 -0
- package/dist/template/src/components/mdx/go-playground.tsx +3 -0
- package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
- package/dist/template/src/components/mdx/python-playground.tsx +2 -0
- package/dist/template/src/components/mdx/tabs.tsx +74 -6
- package/dist/template/src/components/page-header.tsx +19 -0
- package/dist/template/src/components/scroll-to-top.tsx +33 -0
- package/dist/template/src/components/search-dialog.tsx +206 -52
- package/dist/template/src/components/sidebar.tsx +136 -77
- package/dist/template/src/components/table-of-contents.tsx +23 -7
- package/dist/template/src/lib/highlight.ts +90 -31
- package/dist/template/src/lib/search.ts +14 -4
- package/dist/template/src/lib/theme-utils.ts +140 -0
- package/dist/template/src/styles/globals.css +307 -166
- package/dist/template/src/types/remark-gfm.d.ts +2 -0
- package/dist/utils/files.d.ts +9 -0
- package/dist/utils/files.js +33 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.js +38 -0
- package/package.json +1 -4
|
@@ -102,219 +102,226 @@ export const generateCommand = new Command('generate')
|
|
|
102
102
|
.option('--llms-txt', 'Generate llms.txt for Answer Engine Optimization (AEO)')
|
|
103
103
|
.option('--project-name <name>', 'Project name for llms.txt header')
|
|
104
104
|
.action(async (source, options) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
105
|
+
try {
|
|
106
|
+
const startTime = Date.now();
|
|
107
|
+
// Load config (file or defaults)
|
|
108
|
+
const config = loadConfig(options.config);
|
|
109
|
+
// CLI flags override config
|
|
110
|
+
if (source)
|
|
111
|
+
config.source.path = source;
|
|
112
|
+
if (options.output)
|
|
113
|
+
config.output.path = options.output;
|
|
114
|
+
if (options.provider) {
|
|
115
|
+
config.llm.provider = options.provider;
|
|
116
|
+
// Use provider's default model unless explicitly specified
|
|
117
|
+
if (!options.model) {
|
|
118
|
+
config.llm.model = DEFAULT_MODELS[config.llm.provider];
|
|
119
|
+
}
|
|
118
120
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
errors.forEach(e => console.error(` - ${e}`));
|
|
129
|
-
process.exit(1);
|
|
130
|
-
}
|
|
131
|
-
// Check for API key (not needed for Ollama or dry-run)
|
|
132
|
-
if (!options.dryRun) {
|
|
133
|
-
const { ok, envKey } = checkApiKey(config.llm.provider);
|
|
134
|
-
if (!ok && envKey) {
|
|
135
|
-
console.error(`Error: ${envKey} environment variable required for ${config.llm.provider}`);
|
|
121
|
+
if (options.model)
|
|
122
|
+
config.llm.model = options.model;
|
|
123
|
+
if (options.baseUrl)
|
|
124
|
+
config.llm.baseUrl = options.baseUrl;
|
|
125
|
+
// Validate
|
|
126
|
+
const errors = validateConfig(config);
|
|
127
|
+
if (errors.length > 0) {
|
|
128
|
+
console.error('Config errors:');
|
|
129
|
+
errors.forEach(e => console.error(` - ${e}`));
|
|
136
130
|
process.exit(1);
|
|
137
131
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
console.log(` base url: ${config.llm.baseUrl}`);
|
|
146
|
-
}
|
|
147
|
-
console.log('');
|
|
148
|
-
// Check source exists
|
|
149
|
-
const sourcePath = resolve(config.source.path);
|
|
150
|
-
if (!existsSync(sourcePath)) {
|
|
151
|
-
console.error(`Error: Source directory not found: ${sourcePath}`);
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
154
|
-
// Step 1: Scan source code
|
|
155
|
-
console.log('Step 1: Scanning source code...');
|
|
156
|
-
const scanResult = await scanDirectory(sourcePath, {
|
|
157
|
-
include: config.source.include,
|
|
158
|
-
exclude: config.source.exclude,
|
|
159
|
-
onProgress: (current, total, file) => {
|
|
160
|
-
process.stdout.write(`\r [${current}/${total}] ${file.slice(-50).padStart(50)}`);
|
|
132
|
+
// Check for API key (not needed for Ollama or dry-run)
|
|
133
|
+
if (!options.dryRun) {
|
|
134
|
+
const { ok, envKey } = checkApiKey(config.llm.provider);
|
|
135
|
+
if (!ok && envKey) {
|
|
136
|
+
console.error(`Error: ${envKey} environment variable required for ${config.llm.provider}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
161
139
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
console.log(
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
console.log(`
|
|
140
|
+
console.log('skrypt generate');
|
|
141
|
+
console.log(` source: ${config.source.path}`);
|
|
142
|
+
console.log(` output: ${config.output.path}`);
|
|
143
|
+
console.log(` provider: ${config.llm.provider}`);
|
|
144
|
+
console.log(` model: ${config.llm.model}`);
|
|
145
|
+
if (config.llm.baseUrl) {
|
|
146
|
+
console.log(` base url: ${config.llm.baseUrl}`);
|
|
169
147
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// Collect all elements
|
|
177
|
-
let allElements = [];
|
|
178
|
-
for (const file of scanResult.files) {
|
|
179
|
-
for (const el of file.elements) {
|
|
180
|
-
allElements.push(el);
|
|
148
|
+
console.log('');
|
|
149
|
+
// Check source exists
|
|
150
|
+
const sourcePath = resolve(config.source.path);
|
|
151
|
+
if (!existsSync(sourcePath)) {
|
|
152
|
+
console.error(`Error: Source directory not found: ${sourcePath}`);
|
|
153
|
+
process.exit(1);
|
|
181
154
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
allElements = allElements.filter(el => !shouldExcludeElement(el, excludePatterns));
|
|
199
|
-
if (options.exclude?.length) {
|
|
200
|
-
console.log(` --exclude: applied ${options.exclude.length} additional patterns`);
|
|
155
|
+
// Step 1: Scan source code
|
|
156
|
+
console.log('Step 1: Scanning source code...');
|
|
157
|
+
const scanResult = await scanDirectory(sourcePath, {
|
|
158
|
+
include: config.source.include,
|
|
159
|
+
exclude: config.source.exclude,
|
|
160
|
+
onProgress: (current, total, file) => {
|
|
161
|
+
process.stdout.write(`\r [${current}/${total}] ${file.slice(-50).padStart(50)}`);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
console.log('');
|
|
165
|
+
if (scanResult.errors.length > 0) {
|
|
166
|
+
console.log('\n Scan warnings:');
|
|
167
|
+
scanResult.errors.slice(0, 5).forEach(e => console.log(` - ${e}`));
|
|
168
|
+
if (scanResult.errors.length > 5) {
|
|
169
|
+
console.log(` ... and ${scanResult.errors.length - 5} more`);
|
|
170
|
+
}
|
|
201
171
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (count === 1)
|
|
213
|
-
return word;
|
|
214
|
-
if (word === 'class')
|
|
215
|
-
return 'classes';
|
|
216
|
-
return word + 's';
|
|
217
|
-
};
|
|
218
|
-
console.log(' ' + Object.entries(byKind).map(([k, v]) => `${v} ${pluralize(k, v)}`).join(', '));
|
|
219
|
-
// Dry run - stop here
|
|
220
|
-
if (options.dryRun) {
|
|
221
|
-
console.log('\n[dry run - stopping before generation]');
|
|
222
|
-
process.exit(0);
|
|
223
|
-
}
|
|
224
|
-
// Step 2: Generate docs
|
|
225
|
-
console.log('\nStep 2: Generating documentation...');
|
|
226
|
-
const client = createLLMClient({
|
|
227
|
-
provider: config.llm.provider,
|
|
228
|
-
model: config.llm.model,
|
|
229
|
-
baseUrl: config.llm.baseUrl
|
|
230
|
-
});
|
|
231
|
-
let lastElement = '';
|
|
232
|
-
const multiLanguage = options.multiLang ?? false;
|
|
233
|
-
if (multiLanguage) {
|
|
234
|
-
console.log(' mode: multi-language (TypeScript + Python)');
|
|
235
|
-
}
|
|
236
|
-
const docs = await generateForElements(allElements, client, {
|
|
237
|
-
multiLanguage,
|
|
238
|
-
onProgress: (progress) => {
|
|
239
|
-
if (progress.element !== lastElement) {
|
|
240
|
-
if (lastElement)
|
|
241
|
-
console.log('');
|
|
242
|
-
lastElement = progress.element;
|
|
172
|
+
console.log(`\n Found ${scanResult.totalElements} API elements in ${scanResult.files.length} files`);
|
|
173
|
+
if (scanResult.totalElements === 0) {
|
|
174
|
+
console.log(' No API elements found. Nothing to generate.');
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
// Collect all elements
|
|
178
|
+
let allElements = [];
|
|
179
|
+
for (const file of scanResult.files) {
|
|
180
|
+
for (const el of file.elements) {
|
|
181
|
+
allElements.push(el);
|
|
243
182
|
}
|
|
244
|
-
process.stdout.write(`\r [${progress.current}/${progress.total}] ${progress.element}: ${progress.status}`.padEnd(80));
|
|
245
183
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
let totalDocs;
|
|
253
|
-
if (options.byTopic) {
|
|
254
|
-
console.log(' mode: by-topic (grouped by concept)');
|
|
255
|
-
const result = await writeDocsByTopic(docs, outputPath);
|
|
256
|
-
filesWritten = result.filesWritten;
|
|
257
|
-
totalDocs = result.totalDocs;
|
|
258
|
-
console.log(` topics: ${result.topics.map(t => t.name).join(', ')}`);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// Default: file-based output
|
|
262
|
-
const fileResults = groupDocsByFile(docs);
|
|
263
|
-
const result = await writeDocsToDirectory(fileResults, outputPath, sourcePath);
|
|
264
|
-
filesWritten = result.filesWritten;
|
|
265
|
-
totalDocs = result.totalDocs;
|
|
266
|
-
}
|
|
267
|
-
const errorCount = docs.filter(d => d.error).length;
|
|
268
|
-
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
269
|
-
console.log(`\n Wrote ${filesWritten} documentation files to ${outputPath}`);
|
|
270
|
-
// Copy OpenAPI spec (provided or auto-detected)
|
|
271
|
-
let specPath = options.openapi ? resolve(options.openapi) : null;
|
|
272
|
-
// Auto-detect if not provided
|
|
273
|
-
if (!specPath) {
|
|
274
|
-
const detected = findOpenAPISpec(sourcePath);
|
|
275
|
-
if (detected) {
|
|
276
|
-
specPath = detected;
|
|
277
|
-
console.log(`\n Auto-detected OpenAPI spec: ${basename(detected)}`);
|
|
184
|
+
// Apply privacy filters
|
|
185
|
+
const initialCount = allElements.length;
|
|
186
|
+
// 1. --public-only: filter to exported/public APIs only
|
|
187
|
+
if (options.publicOnly) {
|
|
188
|
+
allElements = allElements.filter(el => el.isExported === true || el.isPublic === true);
|
|
189
|
+
console.log(` --public-only: filtered to ${allElements.length} exported APIs`);
|
|
278
190
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
const contentDir = dirname(outputPath);
|
|
284
|
-
const destPath = resolve(contentDir, specFilename);
|
|
285
|
-
mkdirSync(dirname(destPath), { recursive: true });
|
|
286
|
-
copyFileSync(specPath, destPath);
|
|
287
|
-
console.log(` Copied OpenAPI spec: ${specFilename} -> ${destPath}`);
|
|
288
|
-
console.log(' API Playground will be available at /reference');
|
|
191
|
+
// 2. Load .skryptignore patterns
|
|
192
|
+
const ignorePatterns = readIgnorePatterns(sourcePath);
|
|
193
|
+
if (ignorePatterns.length > 0) {
|
|
194
|
+
console.log(` .skryptignore: loaded ${ignorePatterns.length} patterns`);
|
|
289
195
|
}
|
|
290
|
-
|
|
291
|
-
|
|
196
|
+
// 3. Combine with --exclude patterns
|
|
197
|
+
const excludePatterns = [...ignorePatterns, ...(options.exclude || [])];
|
|
198
|
+
if (excludePatterns.length > 0) {
|
|
199
|
+
allElements = allElements.filter(el => !shouldExcludeElement(el, excludePatterns));
|
|
200
|
+
if (options.exclude?.length) {
|
|
201
|
+
console.log(` --exclude: applied ${options.exclude.length} additional patterns`);
|
|
202
|
+
}
|
|
292
203
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
204
|
+
if (initialCount !== allElements.length) {
|
|
205
|
+
console.log(` Filtered: ${initialCount} -> ${allElements.length} elements`);
|
|
206
|
+
}
|
|
207
|
+
// Show summary by kind
|
|
208
|
+
const byKind = {};
|
|
209
|
+
for (const el of allElements) {
|
|
210
|
+
byKind[el.kind] = (byKind[el.kind] || 0) + 1;
|
|
211
|
+
}
|
|
212
|
+
const pluralize = (word, count) => {
|
|
213
|
+
if (count === 1)
|
|
214
|
+
return word;
|
|
215
|
+
if (word === 'class')
|
|
216
|
+
return 'classes';
|
|
217
|
+
return word + 's';
|
|
218
|
+
};
|
|
219
|
+
console.log(' ' + Object.entries(byKind).map(([k, v]) => `${v} ${pluralize(k, v)}`).join(', '));
|
|
220
|
+
// Dry run - stop here
|
|
221
|
+
if (options.dryRun) {
|
|
222
|
+
console.log('\n[dry run - stopping before generation]');
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
// Step 2: Generate docs
|
|
226
|
+
console.log('\nStep 2: Generating documentation...');
|
|
227
|
+
const client = createLLMClient({
|
|
228
|
+
provider: config.llm.provider,
|
|
229
|
+
model: config.llm.model,
|
|
230
|
+
baseUrl: config.llm.baseUrl
|
|
299
231
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
232
|
+
let lastElement = '';
|
|
233
|
+
const multiLanguage = options.multiLang ?? false;
|
|
234
|
+
if (multiLanguage) {
|
|
235
|
+
console.log(' mode: multi-language (TypeScript + Python)');
|
|
236
|
+
}
|
|
237
|
+
const docs = await generateForElements(allElements, client, {
|
|
238
|
+
multiLanguage,
|
|
239
|
+
onProgress: (progress) => {
|
|
240
|
+
if (progress.element !== lastElement) {
|
|
241
|
+
if (lastElement)
|
|
242
|
+
console.log('');
|
|
243
|
+
lastElement = progress.element;
|
|
244
|
+
}
|
|
245
|
+
process.stdout.write(`\r [${progress.current}/${progress.total}] ${progress.element}: ${progress.status}`.padEnd(80));
|
|
246
|
+
}
|
|
314
247
|
});
|
|
315
|
-
|
|
316
|
-
|
|
248
|
+
console.log('\n');
|
|
249
|
+
// Step 3: Write output
|
|
250
|
+
console.log('Step 3: Writing documentation...');
|
|
251
|
+
const outputPath = resolve(config.output.path);
|
|
252
|
+
let filesWritten;
|
|
253
|
+
let totalDocs;
|
|
254
|
+
if (options.byTopic) {
|
|
255
|
+
console.log(' mode: by-topic (grouped by concept)');
|
|
256
|
+
const result = await writeDocsByTopic(docs, outputPath);
|
|
257
|
+
filesWritten = result.filesWritten;
|
|
258
|
+
totalDocs = result.totalDocs;
|
|
259
|
+
console.log(` topics: ${result.topics.map(t => t.name).join(', ')}`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Default: file-based output
|
|
263
|
+
const fileResults = groupDocsByFile(docs);
|
|
264
|
+
const result = await writeDocsToDirectory(fileResults, outputPath, sourcePath);
|
|
265
|
+
filesWritten = result.filesWritten;
|
|
266
|
+
totalDocs = result.totalDocs;
|
|
317
267
|
}
|
|
268
|
+
const errorCount = docs.filter(d => d.error).length;
|
|
269
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
270
|
+
console.log(`\n Wrote ${filesWritten} documentation files to ${outputPath}`);
|
|
271
|
+
// Copy OpenAPI spec (provided or auto-detected)
|
|
272
|
+
let specPath = options.openapi ? resolve(options.openapi) : null;
|
|
273
|
+
// Auto-detect if not provided
|
|
274
|
+
if (!specPath) {
|
|
275
|
+
const detected = findOpenAPISpec(sourcePath);
|
|
276
|
+
if (detected) {
|
|
277
|
+
specPath = detected;
|
|
278
|
+
console.log(`\n Auto-detected OpenAPI spec: ${basename(detected)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (specPath) {
|
|
282
|
+
if (existsSync(specPath)) {
|
|
283
|
+
const specFilename = basename(specPath);
|
|
284
|
+
const contentDir = dirname(outputPath);
|
|
285
|
+
const destPath = resolve(contentDir, specFilename);
|
|
286
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
287
|
+
copyFileSync(specPath, destPath);
|
|
288
|
+
console.log(` Copied OpenAPI spec: ${specFilename} -> ${destPath}`);
|
|
289
|
+
console.log(' API Playground will be available at /reference');
|
|
290
|
+
}
|
|
291
|
+
else if (options.openapi) {
|
|
292
|
+
console.log(`\n Warning: OpenAPI spec not found: ${specPath}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// Generate llms.txt for AEO (Answer Engine Optimization)
|
|
296
|
+
if (options.llmsTxt) {
|
|
297
|
+
await writeLlmsTxt(docs, outputPath, {
|
|
298
|
+
projectName: options.projectName,
|
|
299
|
+
description: `API documentation for ${options.projectName || basename(sourcePath)}`
|
|
300
|
+
});
|
|
301
|
+
console.log(`\n Generated llms.txt and llms-full.md for AEO`);
|
|
302
|
+
}
|
|
303
|
+
console.log('\n=== Summary ===');
|
|
304
|
+
console.log(` Total elements: ${totalDocs}`);
|
|
305
|
+
console.log(` Generated: ${totalDocs - errorCount}`);
|
|
306
|
+
if (errorCount > 0) {
|
|
307
|
+
console.log(` Errors: ${errorCount}`);
|
|
308
|
+
}
|
|
309
|
+
console.log(` Duration: ${duration}s`);
|
|
310
|
+
console.log(` Output: ${outputPath}`);
|
|
311
|
+
if (errorCount > 0) {
|
|
312
|
+
console.log('\n Elements with errors:');
|
|
313
|
+
docs.filter(d => d.error).slice(0, 10).forEach(d => {
|
|
314
|
+
console.log(` - ${d.element.name}: ${d.error?.slice(0, 50)}`);
|
|
315
|
+
});
|
|
316
|
+
if (errorCount > 10) {
|
|
317
|
+
console.log(` ... and ${errorCount - 10} more`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
console.log('\nDone!');
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
324
|
+
console.error(`Error: ${message}`);
|
|
325
|
+
process.exit(1);
|
|
318
326
|
}
|
|
319
|
-
console.log('\nDone!');
|
|
320
327
|
});
|
package/dist/commands/i18n.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync
|
|
3
|
-
import { resolve, join,
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
3
|
+
import { resolve, join, relative } from 'path';
|
|
4
|
+
import { findMdxFiles } from '../utils/files.js';
|
|
4
5
|
const CONFIG_FILE = 'skrypt.i18n.json';
|
|
5
6
|
function loadI18nConfig(docsPath) {
|
|
6
7
|
const configPath = join(docsPath, CONFIG_FILE);
|
|
@@ -13,24 +14,6 @@ function saveI18nConfig(docsPath, config) {
|
|
|
13
14
|
const configPath = join(docsPath, CONFIG_FILE);
|
|
14
15
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
15
16
|
}
|
|
16
|
-
function findMdxFiles(dir) {
|
|
17
|
-
const files = [];
|
|
18
|
-
function walk(currentDir) {
|
|
19
|
-
const entries = readdirSync(currentDir);
|
|
20
|
-
for (const entry of entries) {
|
|
21
|
-
const fullPath = join(currentDir, entry);
|
|
22
|
-
const stat = statSync(fullPath);
|
|
23
|
-
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
24
|
-
walk(fullPath);
|
|
25
|
-
}
|
|
26
|
-
else if (stat.isFile() && (extname(entry) === '.mdx' || extname(entry) === '.md')) {
|
|
27
|
-
files.push(fullPath);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
walk(dir);
|
|
32
|
-
return files;
|
|
33
|
-
}
|
|
34
17
|
export const i18nCommand = new Command('i18n')
|
|
35
18
|
.description('Manage documentation translations');
|
|
36
19
|
i18nCommand
|
package/dist/commands/init.js
CHANGED
|
@@ -9,49 +9,56 @@ export const initCommand = new Command('init')
|
|
|
9
9
|
.argument('[directory]', 'Target directory', '.')
|
|
10
10
|
.option('--name <name>', 'Project name', 'my-docs')
|
|
11
11
|
.action(async (directory, options) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
try {
|
|
13
|
+
const targetDir = resolve(directory);
|
|
14
|
+
console.log('skrypt init');
|
|
15
|
+
console.log(` directory: ${targetDir}`);
|
|
16
|
+
console.log(` name: ${options.name}`);
|
|
17
|
+
console.log('');
|
|
18
|
+
// Check if directory exists and is not empty
|
|
19
|
+
if (existsSync(targetDir)) {
|
|
20
|
+
const files = readdirSync(targetDir);
|
|
21
|
+
if (files.length > 0 && !files.every((f) => f.startsWith('.'))) {
|
|
22
|
+
console.error('Error: Target directory is not empty');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Create target directory
|
|
27
|
+
mkdirSync(targetDir, { recursive: true });
|
|
28
|
+
// Copy template (from dist/template/, one level up from dist/commands/)
|
|
29
|
+
const templateDir = join(__dirname, '..', 'template');
|
|
30
|
+
if (!existsSync(templateDir)) {
|
|
31
|
+
console.error('Error: Template not found. Please reinstall skrypt.');
|
|
22
32
|
process.exit(1);
|
|
23
33
|
}
|
|
34
|
+
console.log('Creating documentation site...');
|
|
35
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
36
|
+
// Update package.json with project name
|
|
37
|
+
const packageJsonPath = join(targetDir, 'package.json');
|
|
38
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
39
|
+
packageJson.name = options.name;
|
|
40
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
41
|
+
// Update docs.json with project name
|
|
42
|
+
const docsJsonPath = join(targetDir, 'docs.json');
|
|
43
|
+
const docsJson = JSON.parse(readFileSync(docsJsonPath, 'utf-8'));
|
|
44
|
+
docsJson.name = options.name;
|
|
45
|
+
docsJson.description = `${options.name} documentation`;
|
|
46
|
+
writeFileSync(docsJsonPath, JSON.stringify(docsJson, null, 2));
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log('Done! Next steps:');
|
|
49
|
+
console.log('');
|
|
50
|
+
console.log(` cd ${directory === '.' ? '.' : directory}`);
|
|
51
|
+
console.log(' npm install');
|
|
52
|
+
console.log(' npm run dev');
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log('Then open http://localhost:3000 to see your docs.');
|
|
55
|
+
console.log('');
|
|
56
|
+
console.log('To generate API documentation:');
|
|
57
|
+
console.log(' skrypt generate ./src -o ./content/docs');
|
|
24
58
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const templateDir = join(__dirname, '..', 'template');
|
|
29
|
-
if (!existsSync(templateDir)) {
|
|
30
|
-
console.error('Error: Template not found. Please reinstall skrypt.');
|
|
59
|
+
catch (err) {
|
|
60
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
61
|
+
console.error(`Error: ${message}`);
|
|
31
62
|
process.exit(1);
|
|
32
63
|
}
|
|
33
|
-
console.log('Creating documentation site...');
|
|
34
|
-
cpSync(templateDir, targetDir, { recursive: true });
|
|
35
|
-
// Update package.json with project name
|
|
36
|
-
const packageJsonPath = join(targetDir, 'package.json');
|
|
37
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
38
|
-
packageJson.name = options.name;
|
|
39
|
-
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
40
|
-
// Update docs.json with project name
|
|
41
|
-
const docsJsonPath = join(targetDir, 'docs.json');
|
|
42
|
-
const docsJson = JSON.parse(readFileSync(docsJsonPath, 'utf-8'));
|
|
43
|
-
docsJson.name = options.name;
|
|
44
|
-
docsJson.description = `${options.name} documentation`;
|
|
45
|
-
writeFileSync(docsJsonPath, JSON.stringify(docsJson, null, 2));
|
|
46
|
-
console.log('');
|
|
47
|
-
console.log('Done! Next steps:');
|
|
48
|
-
console.log('');
|
|
49
|
-
console.log(` cd ${directory === '.' ? '.' : directory}`);
|
|
50
|
-
console.log(' npm install');
|
|
51
|
-
console.log(' npm run dev');
|
|
52
|
-
console.log('');
|
|
53
|
-
console.log('Then open http://localhost:3000 to see your docs.');
|
|
54
|
-
console.log('');
|
|
55
|
-
console.log('To generate API documentation:');
|
|
56
|
-
console.log(' skrypt generate ./src -o ./content/docs');
|
|
57
64
|
});
|
package/dist/commands/lint.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { existsSync, readFileSync,
|
|
3
|
-
import { resolve
|
|
2
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { findMdxFiles } from '../utils/files.js';
|
|
4
5
|
const RULES = {
|
|
5
6
|
'heading-structure': {
|
|
6
7
|
description: 'Headings should follow proper hierarchy (h1 > h2 > h3)',
|
|
@@ -35,24 +36,6 @@ const RULES = {
|
|
|
35
36
|
severity: 'warning',
|
|
36
37
|
},
|
|
37
38
|
};
|
|
38
|
-
function findMdxFiles(dir) {
|
|
39
|
-
const files = [];
|
|
40
|
-
function walk(currentDir) {
|
|
41
|
-
const entries = readdirSync(currentDir);
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
const fullPath = join(currentDir, entry);
|
|
44
|
-
const stat = statSync(fullPath);
|
|
45
|
-
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
46
|
-
walk(fullPath);
|
|
47
|
-
}
|
|
48
|
-
else if (stat.isFile() && (extname(entry) === '.mdx' || extname(entry) === '.md')) {
|
|
49
|
-
files.push(fullPath);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
walk(dir);
|
|
54
|
-
return files;
|
|
55
|
-
}
|
|
56
39
|
function lintFile(filePath) {
|
|
57
40
|
const content = readFileSync(filePath, 'utf-8');
|
|
58
41
|
const lines = content.split('\n');
|