skrypt-ai 0.7.0 → 0.8.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 (110) hide show
  1. package/dist/auth/index.js +3 -3
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/cron.js +0 -4
  4. package/dist/commands/generate/index.d.ts +3 -0
  5. package/dist/commands/generate/index.js +393 -0
  6. package/dist/commands/generate/scan.d.ts +41 -0
  7. package/dist/commands/generate/scan.js +256 -0
  8. package/dist/commands/generate/verify.d.ts +14 -0
  9. package/dist/commands/generate/verify.js +122 -0
  10. package/dist/commands/generate/write.d.ts +25 -0
  11. package/dist/commands/generate/write.js +120 -0
  12. package/dist/commands/import.js +4 -1
  13. package/dist/commands/llms-txt.js +6 -4
  14. package/dist/config/loader.d.ts +0 -1
  15. package/dist/config/loader.js +1 -1
  16. package/dist/generator/agents-md.d.ts +25 -0
  17. package/dist/generator/agents-md.js +122 -0
  18. package/dist/generator/index.d.ts +2 -0
  19. package/dist/generator/index.js +2 -0
  20. package/dist/generator/mdx-serializer.d.ts +11 -0
  21. package/dist/generator/mdx-serializer.js +135 -0
  22. package/dist/generator/organizer.d.ts +1 -16
  23. package/dist/generator/organizer.js +0 -38
  24. package/dist/generator/writer.js +5 -4
  25. package/dist/llm/proxy-client.d.ts +32 -0
  26. package/dist/llm/proxy-client.js +103 -0
  27. package/dist/scanner/csharp.d.ts +0 -4
  28. package/dist/scanner/csharp.js +9 -49
  29. package/dist/scanner/go.d.ts +0 -3
  30. package/dist/scanner/go.js +8 -35
  31. package/dist/scanner/java.d.ts +0 -4
  32. package/dist/scanner/java.js +9 -49
  33. package/dist/scanner/kotlin.d.ts +0 -3
  34. package/dist/scanner/kotlin.js +6 -33
  35. package/dist/scanner/php.d.ts +0 -10
  36. package/dist/scanner/php.js +11 -55
  37. package/dist/scanner/ruby.d.ts +0 -3
  38. package/dist/scanner/ruby.js +8 -38
  39. package/dist/scanner/rust.d.ts +0 -3
  40. package/dist/scanner/rust.js +10 -37
  41. package/dist/scanner/swift.d.ts +0 -3
  42. package/dist/scanner/swift.js +8 -35
  43. package/dist/scanner/utils.d.ts +41 -0
  44. package/dist/scanner/utils.js +97 -0
  45. package/dist/template/docs.json +5 -2
  46. package/dist/template/next.config.mjs +31 -0
  47. package/dist/template/package.json +5 -3
  48. package/dist/template/src/app/layout.tsx +13 -13
  49. package/dist/template/src/app/llms-full.md/route.ts +29 -0
  50. package/dist/template/src/app/llms.txt/route.ts +29 -0
  51. package/dist/template/src/app/md/[...slug]/route.ts +174 -0
  52. package/dist/template/src/app/reference/route.ts +22 -18
  53. package/dist/template/src/app/sitemap.ts +1 -1
  54. package/dist/template/src/components/ai-chat-impl.tsx +206 -0
  55. package/dist/template/src/components/ai-chat.tsx +20 -193
  56. package/dist/template/src/components/mdx/index.tsx +27 -4
  57. package/dist/template/src/lib/fonts.ts +135 -0
  58. package/dist/template/src/middleware.ts +101 -0
  59. package/dist/template/src/styles/globals.css +28 -20
  60. package/dist/utils/files.d.ts +0 -8
  61. package/dist/utils/files.js +0 -33
  62. package/package.json +1 -1
  63. package/dist/autofix/autofix.test.d.ts +0 -1
  64. package/dist/autofix/autofix.test.js +0 -487
  65. package/dist/commands/generate.d.ts +0 -9
  66. package/dist/commands/generate.js +0 -739
  67. package/dist/generator/generator.test.d.ts +0 -1
  68. package/dist/generator/generator.test.js +0 -259
  69. package/dist/generator/writer.test.d.ts +0 -1
  70. package/dist/generator/writer.test.js +0 -411
  71. package/dist/llm/llm.manual-test.d.ts +0 -1
  72. package/dist/llm/llm.manual-test.js +0 -112
  73. package/dist/llm/llm.mock-test.d.ts +0 -4
  74. package/dist/llm/llm.mock-test.js +0 -79
  75. package/dist/plugins/index.d.ts +0 -47
  76. package/dist/plugins/index.js +0 -181
  77. package/dist/scanner/content-type.test.d.ts +0 -1
  78. package/dist/scanner/content-type.test.js +0 -231
  79. package/dist/scanner/integration.test.d.ts +0 -4
  80. package/dist/scanner/integration.test.js +0 -180
  81. package/dist/scanner/scanner.test.d.ts +0 -1
  82. package/dist/scanner/scanner.test.js +0 -210
  83. package/dist/scanner/typescript.manual-test.d.ts +0 -1
  84. package/dist/scanner/typescript.manual-test.js +0 -112
  85. package/dist/template/src/app/docs/auth/page.mdx +0 -589
  86. package/dist/template/src/app/docs/autofix/page.mdx +0 -624
  87. package/dist/template/src/app/docs/cli/page.mdx +0 -217
  88. package/dist/template/src/app/docs/config/page.mdx +0 -428
  89. package/dist/template/src/app/docs/configuration/page.mdx +0 -86
  90. package/dist/template/src/app/docs/deployment/page.mdx +0 -112
  91. package/dist/template/src/app/docs/generator/generator.md +0 -504
  92. package/dist/template/src/app/docs/generator/organizer.md +0 -779
  93. package/dist/template/src/app/docs/generator/page.mdx +0 -613
  94. package/dist/template/src/app/docs/github/page.mdx +0 -502
  95. package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
  96. package/dist/template/src/app/docs/llm/index.md +0 -471
  97. package/dist/template/src/app/docs/llm/page.mdx +0 -428
  98. package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
  99. package/dist/template/src/app/docs/pro/page.mdx +0 -121
  100. package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
  101. package/dist/template/src/app/docs/scanner/content-type.md +0 -599
  102. package/dist/template/src/app/docs/scanner/index.md +0 -212
  103. package/dist/template/src/app/docs/scanner/page.mdx +0 -307
  104. package/dist/template/src/app/docs/scanner/python.md +0 -469
  105. package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
  106. package/dist/template/src/app/docs/scanner/rust.md +0 -325
  107. package/dist/template/src/app/docs/scanner/typescript.md +0 -201
  108. package/dist/template/src/app/icon.tsx +0 -29
  109. package/dist/utils/validation.d.ts +0 -1
  110. package/dist/utils/validation.js +0 -12
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'fs';
2
+ import { splitParams, getLineNumber, getSourceContext } from './utils.js';
2
3
  /**
3
4
  * Scanner for Ruby source files
4
5
  * Extracts: classes, modules, methods (public only)
@@ -86,7 +87,7 @@ export class RubyScanner {
86
87
  if (!name)
87
88
  continue;
88
89
  const superclass = match[2] ?? '';
89
- const lineNumber = this.getLineNumber(source, match.index);
90
+ const lineNumber = getLineNumber(source, match.index);
90
91
  const docstring = this.getDocComment(lines, lineNumber - 1);
91
92
  const signature = superclass
92
93
  ? `class ${name} < ${superclass}`
@@ -103,7 +104,7 @@ export class RubyScanner {
103
104
  isPublic: true,
104
105
  imports,
105
106
  packageName,
106
- sourceContext: this.getSourceContext(lines, lineNumber, 15)
107
+ sourceContext: getSourceContext(lines, lineNumber, 15)
107
108
  });
108
109
  }
109
110
  }
@@ -119,7 +120,7 @@ export class RubyScanner {
119
120
  continue;
120
121
  // Only emit modules that contain constants (FOO = ...) and do NOT
121
122
  // contain class/def definitions — i.e. "leaf" modules.
122
- const lineNumber = this.getLineNumber(source, match.index);
123
+ const lineNumber = getLineNumber(source, match.index);
123
124
  const body = this.getModuleBody(source, match.index);
124
125
  if (body === null)
125
126
  continue;
@@ -140,7 +141,7 @@ export class RubyScanner {
140
141
  isPublic: true,
141
142
  imports,
142
143
  packageName,
143
- sourceContext: this.getSourceContext(lines, lineNumber, 15)
144
+ sourceContext: getSourceContext(lines, lineNumber, 15)
144
145
  });
145
146
  }
146
147
  }
@@ -190,7 +191,7 @@ export class RubyScanner {
190
191
  // Skip initialize (constructor internals)
191
192
  if (name === 'initialize')
192
193
  continue;
193
- const lineNumber = this.getLineNumber(source, match.index);
194
+ const lineNumber = getLineNumber(source, match.index);
194
195
  // Check visibility — skip private / protected
195
196
  const visibility = visibilityMap.get(lineNumber);
196
197
  if (visibility === 'private' || visibility === 'protected')
@@ -223,7 +224,7 @@ export class RubyScanner {
223
224
  isPublic: true,
224
225
  imports,
225
226
  packageName,
226
- sourceContext: this.getSourceContext(lines, lineNumber)
227
+ sourceContext: getSourceContext(lines, lineNumber)
227
228
  });
228
229
  }
229
230
  }
@@ -305,7 +306,7 @@ export class RubyScanner {
305
306
  if (!paramsStr.trim())
306
307
  return [];
307
308
  const params = [];
308
- const parts = this.splitParams(paramsStr);
309
+ const parts = splitParams(paramsStr);
309
310
  for (const part of parts) {
310
311
  const trimmed = part.trim();
311
312
  if (!trimmed)
@@ -348,26 +349,6 @@ export class RubyScanner {
348
349
  }
349
350
  return params;
350
351
  }
351
- splitParams(str) {
352
- const parts = [];
353
- let depth = 0;
354
- let current = '';
355
- for (const char of str) {
356
- if (char === '(' || char === '[' || char === '{')
357
- depth++;
358
- else if (char === ')' || char === ']' || char === '}')
359
- depth--;
360
- else if (char === ',' && depth === 0) {
361
- parts.push(current);
362
- current = '';
363
- continue;
364
- }
365
- current += char;
366
- }
367
- if (current.trim())
368
- parts.push(current);
369
- return parts;
370
- }
371
352
  // ---------------------------------------------------------------------------
372
353
  // Doc comments (YARD and plain #)
373
354
  // ---------------------------------------------------------------------------
@@ -417,15 +398,4 @@ export class RubyScanner {
417
398
  }
418
399
  return undefined;
419
400
  }
420
- // ---------------------------------------------------------------------------
421
- // Helpers
422
- // ---------------------------------------------------------------------------
423
- getLineNumber(source, index) {
424
- return source.slice(0, index).split('\n').length;
425
- }
426
- getSourceContext(lines, lineNumber, context = 5) {
427
- const start = Math.max(0, lineNumber - context - 1);
428
- const end = Math.min(lines.length, lineNumber + context);
429
- return lines.slice(start, end).join('\n');
430
- }
431
401
  }
@@ -16,8 +16,5 @@ export declare class RustScanner implements Scanner {
16
16
  private extractImplMethods;
17
17
  private extractBlock;
18
18
  private parseRustParams;
19
- private splitParams;
20
- private getLineNumber;
21
19
  private getDocComment;
22
- private getSourceContext;
23
20
  }
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'fs';
2
+ import { splitParams, getLineNumber, getSourceContext } from './utils.js';
2
3
  /**
3
4
  * Scanner for Rust source files
4
5
  * Extracts: pub functions, pub structs, pub enums, impl blocks, traits
@@ -72,7 +73,7 @@ export class RustScanner {
72
73
  const generics = match[4] || '';
73
74
  const paramsStr = match[5];
74
75
  const returnPart = match[6]?.trim() || '';
75
- const lineNumber = this.getLineNumber(source, match.index);
76
+ const lineNumber = getLineNumber(source, match.index);
76
77
  const docstring = this.getDocComment(lines, lineNumber - 1);
77
78
  const parameters = this.parseRustParams(paramsStr);
78
79
  const returnType = returnPart ? returnPart.replace(/^->\s*/, '').trim() : undefined;
@@ -92,7 +93,7 @@ export class RustScanner {
92
93
  isPublic: true,
93
94
  imports,
94
95
  packageName,
95
- sourceContext: this.getSourceContext(lines, lineNumber)
96
+ sourceContext: getSourceContext(lines, lineNumber)
96
97
  });
97
98
  }
98
99
  }
@@ -110,7 +111,7 @@ export class RustScanner {
110
111
  const kind = match[1];
111
112
  const name = match[2];
112
113
  const generics = match[3] || '';
113
- const lineNumber = this.getLineNumber(source, match.index);
114
+ const lineNumber = getLineNumber(source, match.index);
114
115
  const docstring = this.getDocComment(lines, lineNumber - 1);
115
116
  const signature = `pub ${kind} ${name}${generics}`;
116
117
  elements.push({
@@ -125,7 +126,7 @@ export class RustScanner {
125
126
  isPublic: true,
126
127
  imports,
127
128
  packageName,
128
- sourceContext: this.getSourceContext(lines, lineNumber, 20)
129
+ sourceContext: getSourceContext(lines, lineNumber, 20)
129
130
  });
130
131
  }
131
132
  }
@@ -136,7 +137,7 @@ export class RustScanner {
136
137
  while ((match = traitRegex.exec(source)) !== null) {
137
138
  const name = match[1];
138
139
  const generics = match[2] || '';
139
- const lineNumber = this.getLineNumber(source, match.index);
140
+ const lineNumber = getLineNumber(source, match.index);
140
141
  const docstring = this.getDocComment(lines, lineNumber - 1);
141
142
  const signature = `pub trait ${name}${generics}`;
142
143
  elements.push({
@@ -151,7 +152,7 @@ export class RustScanner {
151
152
  isPublic: true,
152
153
  imports,
153
154
  packageName,
154
- sourceContext: this.getSourceContext(lines, lineNumber, 15)
155
+ sourceContext: getSourceContext(lines, lineNumber, 15)
155
156
  });
156
157
  }
157
158
  }
@@ -180,7 +181,7 @@ export class RustScanner {
180
181
  const returnPart = methodMatch[5]?.trim() || '';
181
182
  const bodyIndex = implBody.indexOf(methodMatch[0]);
182
183
  const fullIndex = implStart + implMatch[0].length + bodyIndex;
183
- const lineNumber = this.getLineNumber(source, fullIndex);
184
+ const lineNumber = getLineNumber(source, fullIndex);
184
185
  const docstring = this.getDocComment(lines, lineNumber - 1);
185
186
  const parameters = this.parseRustParams(paramsStr);
186
187
  const returnType = returnPart ? returnPart.replace(/^->\s*/, '').trim() : undefined;
@@ -201,7 +202,7 @@ export class RustScanner {
201
202
  isPublic: true,
202
203
  imports,
203
204
  packageName,
204
- sourceContext: this.getSourceContext(lines, lineNumber)
205
+ sourceContext: getSourceContext(lines, lineNumber)
205
206
  });
206
207
  }
207
208
  }
@@ -230,7 +231,7 @@ export class RustScanner {
230
231
  if (!paramsStr.trim())
231
232
  return [];
232
233
  const params = [];
233
- const parts = this.splitParams(paramsStr);
234
+ const parts = splitParams(paramsStr);
234
235
  for (const part of parts) {
235
236
  const trimmed = part.trim();
236
237
  if (!trimmed)
@@ -250,29 +251,6 @@ export class RustScanner {
250
251
  }
251
252
  return params;
252
253
  }
253
- splitParams(str) {
254
- const parts = [];
255
- let depth = 0;
256
- let current = '';
257
- for (const char of str) {
258
- if (char === '<' || char === '(' || char === '[' || char === '{')
259
- depth++;
260
- else if (char === '>' || char === ')' || char === ']' || char === '}')
261
- depth--;
262
- else if (char === ',' && depth === 0) {
263
- parts.push(current);
264
- current = '';
265
- continue;
266
- }
267
- current += char;
268
- }
269
- if (current.trim())
270
- parts.push(current);
271
- return parts;
272
- }
273
- getLineNumber(source, index) {
274
- return source.slice(0, index).split('\n').length;
275
- }
276
254
  getDocComment(lines, lineIndex) {
277
255
  const comments = [];
278
256
  let i = lineIndex - 1;
@@ -296,9 +274,4 @@ export class RustScanner {
296
274
  }
297
275
  return comments.length > 0 ? comments.join('\n') : undefined;
298
276
  }
299
- getSourceContext(lines, lineNumber, context = 5) {
300
- const start = Math.max(0, lineNumber - context - 1);
301
- const end = Math.min(lines.length, lineNumber + context);
302
- return lines.slice(start, end).join('\n');
303
- }
304
277
  }
@@ -18,8 +18,5 @@ export declare class SwiftScanner implements Scanner {
18
18
  private extractBlock;
19
19
  private parseSwiftParams;
20
20
  private findDefaultValueSplit;
21
- private splitParams;
22
- private getLineNumber;
23
21
  private getDocComment;
24
- private getSourceContext;
25
22
  }
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'fs';
2
+ import { splitParams, getLineNumber, getSourceContext } from './utils.js';
2
3
  /**
3
4
  * Scanner for Swift source files
4
5
  * Extracts: public/open classes, structs, enums, protocols, actors, functions, methods
@@ -72,7 +73,7 @@ export class SwiftScanner {
72
73
  const name = match[3];
73
74
  if (!access || !kind || !name)
74
75
  continue;
75
- const lineNumber = this.getLineNumber(source, match.index);
76
+ const lineNumber = getLineNumber(source, match.index);
76
77
  const docstring = this.getDocComment(lines, lineNumber - 1);
77
78
  const signature = `${access} ${kind} ${name}`;
78
79
  elements.push({
@@ -87,7 +88,7 @@ export class SwiftScanner {
87
88
  isPublic: true,
88
89
  imports,
89
90
  packageName,
90
- sourceContext: this.getSourceContext(lines, lineNumber, 15)
91
+ sourceContext: getSourceContext(lines, lineNumber, 15)
91
92
  });
92
93
  }
93
94
  }
@@ -143,7 +144,7 @@ export class SwiftScanner {
143
144
  if (!access || !name)
144
145
  continue;
145
146
  // Check if this is at the top level (not indented)
146
- const lineNumber = this.getLineNumber(source, match.index);
147
+ const lineNumber = getLineNumber(source, match.index);
147
148
  const lineContent = lines[lineNumber - 1];
148
149
  if (!lineContent || /^\s+/.test(lineContent))
149
150
  continue;
@@ -169,7 +170,7 @@ export class SwiftScanner {
169
170
  isPublic: true,
170
171
  imports,
171
172
  packageName,
172
- sourceContext: this.getSourceContext(lines, lineNumber)
173
+ sourceContext: getSourceContext(lines, lineNumber)
173
174
  });
174
175
  }
175
176
  }
@@ -202,7 +203,7 @@ export class SwiftScanner {
202
203
  // Calculate line number relative to the full source
203
204
  const bodyOffset = blockStart + 1;
204
205
  const fullIndex = bodyOffset + methodMatch.index;
205
- const lineNumber = this.getLineNumber(source, fullIndex);
206
+ const lineNumber = getLineNumber(source, fullIndex);
206
207
  const docstring = this.getDocComment(lines, lineNumber - 1);
207
208
  const parameters = this.parseSwiftParams(paramsStr);
208
209
  const signature = this.buildSignature(access, name, paramsStr, isAsync, isThrows, isRethrows, returnType);
@@ -221,7 +222,7 @@ export class SwiftScanner {
221
222
  isPublic: true,
222
223
  imports,
223
224
  packageName,
224
- sourceContext: this.getSourceContext(lines, lineNumber)
225
+ sourceContext: getSourceContext(lines, lineNumber)
225
226
  });
226
227
  }
227
228
  }
@@ -262,7 +263,7 @@ export class SwiftScanner {
262
263
  if (!paramsStr.trim())
263
264
  return [];
264
265
  const params = [];
265
- const parts = this.splitParams(paramsStr);
266
+ const parts = splitParams(paramsStr);
266
267
  for (const part of parts) {
267
268
  const trimmed = part.trim();
268
269
  if (!trimmed)
@@ -316,29 +317,6 @@ export class SwiftScanner {
316
317
  }
317
318
  return -1;
318
319
  }
319
- splitParams(str) {
320
- const parts = [];
321
- let depth = 0;
322
- let current = '';
323
- for (const char of str) {
324
- if (char === '<' || char === '(' || char === '[' || char === '{')
325
- depth++;
326
- else if (char === '>' || char === ')' || char === ']' || char === '}')
327
- depth--;
328
- else if (char === ',' && depth === 0) {
329
- parts.push(current);
330
- current = '';
331
- continue;
332
- }
333
- current += char;
334
- }
335
- if (current.trim())
336
- parts.push(current);
337
- return parts;
338
- }
339
- getLineNumber(source, index) {
340
- return source.slice(0, index).split('\n').length;
341
- }
342
320
  getDocComment(lines, lineIndex) {
343
321
  const comments = [];
344
322
  let i = lineIndex - 1;
@@ -384,9 +362,4 @@ export class SwiftScanner {
384
362
  }
385
363
  return comments.length > 0 ? comments.join('\n') : undefined;
386
364
  }
387
- getSourceContext(lines, lineNumber, context = 5) {
388
- const start = Math.max(0, lineNumber - context - 1);
389
- const end = Math.min(lines.length, lineNumber + context);
390
- return lines.slice(start, end).join('\n');
391
- }
392
365
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared utility functions for language scanners.
3
+ *
4
+ * These helpers are used identically (or near-identically) across
5
+ * multiple scanner implementations. Centralising them here avoids
6
+ * duplicated code and makes future fixes apply everywhere at once.
7
+ */
8
+ /**
9
+ * Split a parameter string by commas while respecting nested brackets.
10
+ * Handles: `<>`, `()`, `[]`, `{}`
11
+ *
12
+ * Used by: Java, C#, Swift, Kotlin, PHP, Rust, Ruby scanners.
13
+ */
14
+ export declare function splitParams(str: string): string[];
15
+ /**
16
+ * Split a parameter string by commas, respecting `()`, `[]`, `{}`
17
+ * but NOT `<>`. Go is the only scanner that uses this variant because
18
+ * Go has no angle-bracket generics in parameter lists.
19
+ */
20
+ export declare function splitParamsNoAngleBrackets(str: string): string[];
21
+ /**
22
+ * Convert a character-index in `source` to a 1-based line number.
23
+ *
24
+ * Used by every regex-based scanner.
25
+ */
26
+ export declare function getLineNumber(source: string, index: number): number;
27
+ /**
28
+ * Return a few lines of source context around `lineNumber` (1-based).
29
+ *
30
+ * @param lines - Pre-split source lines (`source.split('\n')`)
31
+ * @param lineNumber - 1-based line number of the declaration
32
+ * @param context - Number of lines to include before and after (default 5)
33
+ */
34
+ export declare function getSourceContext(lines: string[], lineNumber: number, context?: number): string;
35
+ /**
36
+ * Starting at the opening `{` at `openIndex`, find its matching `}`.
37
+ * Returns the index of the closing brace, or -1 if not found.
38
+ *
39
+ * Used by: Java, C#, PHP scanners (for class-range detection).
40
+ */
41
+ export declare function findMatchingBrace(source: string, openIndex: number): number;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Shared utility functions for language scanners.
3
+ *
4
+ * These helpers are used identically (or near-identically) across
5
+ * multiple scanner implementations. Centralising them here avoids
6
+ * duplicated code and makes future fixes apply everywhere at once.
7
+ */
8
+ /**
9
+ * Split a parameter string by commas while respecting nested brackets.
10
+ * Handles: `<>`, `()`, `[]`, `{}`
11
+ *
12
+ * Used by: Java, C#, Swift, Kotlin, PHP, Rust, Ruby scanners.
13
+ */
14
+ export function splitParams(str) {
15
+ const parts = [];
16
+ let depth = 0;
17
+ let current = '';
18
+ for (const char of str) {
19
+ if (char === '<' || char === '(' || char === '[' || char === '{')
20
+ depth++;
21
+ else if (char === '>' || char === ')' || char === ']' || char === '}')
22
+ depth--;
23
+ else if (char === ',' && depth === 0) {
24
+ parts.push(current);
25
+ current = '';
26
+ continue;
27
+ }
28
+ current += char;
29
+ }
30
+ if (current.trim())
31
+ parts.push(current);
32
+ return parts;
33
+ }
34
+ /**
35
+ * Split a parameter string by commas, respecting `()`, `[]`, `{}`
36
+ * but NOT `<>`. Go is the only scanner that uses this variant because
37
+ * Go has no angle-bracket generics in parameter lists.
38
+ */
39
+ export function splitParamsNoAngleBrackets(str) {
40
+ const parts = [];
41
+ let depth = 0;
42
+ let current = '';
43
+ for (const char of str) {
44
+ if (char === '(' || char === '[' || char === '{')
45
+ depth++;
46
+ else if (char === ')' || char === ']' || char === '}')
47
+ depth--;
48
+ else if (char === ',' && depth === 0) {
49
+ parts.push(current);
50
+ current = '';
51
+ continue;
52
+ }
53
+ current += char;
54
+ }
55
+ if (current.trim())
56
+ parts.push(current);
57
+ return parts;
58
+ }
59
+ /**
60
+ * Convert a character-index in `source` to a 1-based line number.
61
+ *
62
+ * Used by every regex-based scanner.
63
+ */
64
+ export function getLineNumber(source, index) {
65
+ return source.slice(0, index).split('\n').length;
66
+ }
67
+ /**
68
+ * Return a few lines of source context around `lineNumber` (1-based).
69
+ *
70
+ * @param lines - Pre-split source lines (`source.split('\n')`)
71
+ * @param lineNumber - 1-based line number of the declaration
72
+ * @param context - Number of lines to include before and after (default 5)
73
+ */
74
+ export function getSourceContext(lines, lineNumber, context = 5) {
75
+ const start = Math.max(0, lineNumber - context - 1);
76
+ const end = Math.min(lines.length, lineNumber + context);
77
+ return lines.slice(start, end).join('\n');
78
+ }
79
+ /**
80
+ * Starting at the opening `{` at `openIndex`, find its matching `}`.
81
+ * Returns the index of the closing brace, or -1 if not found.
82
+ *
83
+ * Used by: Java, C#, PHP scanners (for class-range detection).
84
+ */
85
+ export function findMatchingBrace(source, openIndex) {
86
+ let depth = 0;
87
+ for (let i = openIndex; i < source.length; i++) {
88
+ if (source[i] === '{')
89
+ depth++;
90
+ else if (source[i] === '}') {
91
+ depth--;
92
+ if (depth === 0)
93
+ return i;
94
+ }
95
+ }
96
+ return -1;
97
+ }
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "Skrypt",
3
3
  "description": "AI-powered documentation generation",
4
+ "fonts": {
5
+ "sans": "Inter",
6
+ "mono": "JetBrains Mono"
7
+ },
4
8
  "theme": {
5
9
  "primaryColor": "#3b82f6",
6
- "accentColor": "#8b5cf6",
7
- "font": "Inter"
10
+ "accentColor": "#8b5cf6"
8
11
  },
9
12
  "navigation": [
10
13
  {
@@ -14,6 +14,37 @@ const withMDX = createMDX({
14
14
  const nextConfig = {
15
15
  pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
16
16
  transpilePackages: ['@scalar/api-reference-react', '@scalar/api-reference'],
17
+ async headers() {
18
+ return [{
19
+ source: '/(.*)',
20
+ headers: [
21
+ {
22
+ key: 'Content-Security-Policy',
23
+ value: "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https: https://fonts.gstatic.com; connect-src 'self' https:; frame-src 'self' https://codesandbox.io"
24
+ },
25
+ {
26
+ key: 'X-Frame-Options',
27
+ value: 'DENY'
28
+ },
29
+ {
30
+ key: 'X-Content-Type-Options',
31
+ value: 'nosniff'
32
+ },
33
+ {
34
+ key: 'Referrer-Policy',
35
+ value: 'strict-origin-when-cross-origin'
36
+ },
37
+ {
38
+ key: 'Strict-Transport-Security',
39
+ value: 'max-age=31536000; includeSubDomains'
40
+ },
41
+ {
42
+ key: 'Permissions-Policy',
43
+ value: 'camera=(), microphone=(), geolocation=()'
44
+ }
45
+ ]
46
+ }]
47
+ },
17
48
  }
18
49
 
19
50
  export default withMDX(nextConfig)
@@ -13,16 +13,13 @@
13
13
  "@ai-sdk/anthropic": "^1.0.0",
14
14
  "@ai-sdk/openai": "^1.0.0",
15
15
  "@ai-sdk/react": "^1.0.0",
16
- "@codesandbox/sandpack-react": "^2.20.0",
17
16
  "@mdx-js/loader": "^3.1.0",
18
17
  "@mdx-js/react": "^3.1.0",
19
18
  "@next/mdx": "^15.3.0",
20
19
  "@orama/orama": "^3.1.0",
21
- "@scalar/nextjs-api-reference": "^0.4.0",
22
20
  "clsx": "^2.1.0",
23
21
  "gray-matter": "^4.0.3",
24
22
  "lucide-react": "^0.500.0",
25
- "mermaid": "^11.13.0",
26
23
  "next": "^15.3.0",
27
24
  "next-mdx-remote": "^6.0.0",
28
25
  "react": "^19.0.0",
@@ -35,6 +32,11 @@
35
32
  "tailwind-merge": "^3.0.0",
36
33
  "yaml": "^2.8.2"
37
34
  },
35
+ "optionalDependencies": {
36
+ "@codesandbox/sandpack-react": "^2.20.0",
37
+ "mermaid": "^11.13.0",
38
+ "@scalar/nextjs-api-reference": "^0.4.0"
39
+ },
38
40
  "devDependencies": {
39
41
  "@tailwindcss/postcss": "^4.1.0",
40
42
  "@types/mdx": "^2.0.0",
@@ -5,12 +5,17 @@ import { SyntaxThemeProvider } from '@/contexts/syntax-theme'
5
5
  import { readFileSync } from 'fs'
6
6
  import { join } from 'path'
7
7
  import { derivePrimaryColors } from '@/lib/theme-utils'
8
+ import { resolveFonts, buildGoogleFontsUrl } from '@/lib/fonts'
8
9
 
9
10
  const inter = Inter({ subsets: ['latin'] })
10
11
 
11
12
  function getDocsConfig() {
12
13
  const configPath = join(process.cwd(), 'docs.json')
13
- return JSON.parse(readFileSync(configPath, 'utf-8'))
14
+ try {
15
+ return JSON.parse(readFileSync(configPath, 'utf-8'))
16
+ } catch {
17
+ return {}
18
+ }
14
19
  }
15
20
 
16
21
  const docsConfig = getDocsConfig()
@@ -19,19 +24,13 @@ const siteName = docsConfig.name || 'Documentation'
19
24
  const siteDescription = docsConfig.description || 'Documentation'
20
25
 
21
26
  // Theme: derive primary color variants from docs.json
22
- const primaryHex = docsConfig.theme?.primaryColor || '#3b82f6'
27
+ const rawColor = docsConfig.theme?.primaryColor
28
+ const primaryHex = typeof rawColor === 'string' && /^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?$/.test(rawColor) ? rawColor : '#3b82f6'
23
29
  const { primary, primaryDark, primaryLight, primaryForeground } = derivePrimaryColors(primaryHex)
24
30
 
25
- // Font: read from docs.json, default to Inter
26
- const customFont = docsConfig.theme?.font || 'Inter'
27
- const safeFont = customFont ? customFont.replace(/[^a-zA-Z0-9\s-]/g, '') : null
28
- const isCustomFont = safeFont !== 'Inter'
29
- const googleFontsUrl = isCustomFont
30
- ? `https://fonts.googleapis.com/css2?family=${encodeURIComponent(safeFont!)}:wght@400;500;600;700;800&display=swap`
31
- : null
32
- const googleFontsPreloadUrl = isCustomFont
33
- ? `https://fonts.googleapis.com/css2?family=${encodeURIComponent(safeFont!)}:wght@400;600;700&display=swap`
34
- : null
31
+ // Fonts: read from docs.json (supports both new `fonts` key and legacy `theme.font`)
32
+ const { sans, mono, needsGoogleFonts } = resolveFonts(docsConfig)
33
+ const googleFontsUrl = needsGoogleFonts ? buildGoogleFontsUrl(sans, mono) : null
35
34
 
36
35
  // Build the CSS custom property overrides
37
36
  const themeStyles = `
@@ -40,7 +39,8 @@ const themeStyles = `
40
39
  --color-primary-dark: ${primaryDark};
41
40
  --color-primary-light: ${primaryLight};
42
41
  --color-primary-foreground: ${primaryForeground};
43
- --font-body: "${safeFont}", "Inter", ui-sans-serif, system-ui, sans-serif;
42
+ --font-body: "${sans.name}", "Inter", ui-sans-serif, system-ui, sans-serif;
43
+ --font-mono: "${mono.name}", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
44
44
  }
45
45
  `
46
46
 
@@ -0,0 +1,29 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { readFile } from 'fs/promises'
3
+ import { join } from 'path'
4
+
5
+ export async function GET() {
6
+ const paths = [
7
+ join(process.cwd(), 'content', 'docs', 'llms-full.md'),
8
+ join(process.cwd(), 'public', 'llms-full.md'),
9
+ ]
10
+
11
+ for (const path of paths) {
12
+ try {
13
+ const content = await readFile(path, 'utf-8')
14
+ return new NextResponse(content, {
15
+ headers: {
16
+ 'Content-Type': 'text/markdown; charset=utf-8',
17
+ 'Cache-Control': 'public, max-age=3600, s-maxage=86400',
18
+ },
19
+ })
20
+ } catch {
21
+ continue
22
+ }
23
+ }
24
+
25
+ return new NextResponse('# No llms-full.md found\n\nRun `skrypt generate` to create one.\n', {
26
+ status: 404,
27
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
28
+ })
29
+ }