skrypt-ai 0.3.4 → 0.4.1

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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/index.d.ts +0 -1
  3. package/dist/auth/index.js +3 -5
  4. package/dist/autofix/index.js +15 -3
  5. package/dist/cli.js +19 -4
  6. package/dist/commands/check-links.js +164 -174
  7. package/dist/commands/deploy.js +5 -2
  8. package/dist/commands/generate.js +206 -199
  9. package/dist/commands/i18n.js +3 -20
  10. package/dist/commands/init.js +47 -40
  11. package/dist/commands/lint.js +3 -20
  12. package/dist/commands/mcp.js +125 -122
  13. package/dist/commands/monitor.js +125 -108
  14. package/dist/commands/review-pr.js +1 -1
  15. package/dist/commands/sdk.js +1 -1
  16. package/dist/config/loader.js +21 -2
  17. package/dist/generator/organizer.d.ts +3 -0
  18. package/dist/generator/organizer.js +4 -9
  19. package/dist/generator/writer.js +2 -10
  20. package/dist/github/pr-comments.js +21 -8
  21. package/dist/plugins/index.js +1 -0
  22. package/dist/scanner/index.js +8 -2
  23. package/dist/template/docs.json +2 -1
  24. package/dist/template/next.config.mjs +2 -1
  25. package/dist/template/package.json +17 -15
  26. package/dist/template/public/favicon.svg +4 -0
  27. package/dist/template/public/search-index.json +1 -1
  28. package/dist/template/scripts/build-search-index.mjs +120 -25
  29. package/dist/template/src/app/api/chat/route.ts +11 -3
  30. package/dist/template/src/app/docs/README.md +28 -0
  31. package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
  32. package/dist/template/src/app/docs/auth/page.mdx +589 -0
  33. package/dist/template/src/app/docs/autofix/page.mdx +624 -0
  34. package/dist/template/src/app/docs/cli/page.mdx +217 -0
  35. package/dist/template/src/app/docs/config/page.mdx +428 -0
  36. package/dist/template/src/app/docs/configuration/page.mdx +86 -0
  37. package/dist/template/src/app/docs/deployment/page.mdx +112 -0
  38. package/dist/template/src/app/docs/error.tsx +20 -0
  39. package/dist/template/src/app/docs/generator/generator.md +504 -0
  40. package/dist/template/src/app/docs/generator/organizer.md +779 -0
  41. package/dist/template/src/app/docs/generator/page.mdx +613 -0
  42. package/dist/template/src/app/docs/github/page.mdx +502 -0
  43. package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
  44. package/dist/template/src/app/docs/llm/index.md +471 -0
  45. package/dist/template/src/app/docs/llm/page.mdx +428 -0
  46. package/dist/template/src/app/docs/llms-full.md +256 -0
  47. package/dist/template/src/app/docs/llms.txt +2971 -0
  48. package/dist/template/src/app/docs/not-found.tsx +23 -0
  49. package/dist/template/src/app/docs/page.mdx +0 -3
  50. package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
  51. package/dist/template/src/app/docs/pro/page.mdx +121 -0
  52. package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
  53. package/dist/template/src/app/docs/scanner/content-type.md +599 -0
  54. package/dist/template/src/app/docs/scanner/index.md +212 -0
  55. package/dist/template/src/app/docs/scanner/page.mdx +307 -0
  56. package/dist/template/src/app/docs/scanner/python.md +469 -0
  57. package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
  58. package/dist/template/src/app/docs/scanner/rust.md +325 -0
  59. package/dist/template/src/app/docs/scanner/typescript.md +201 -0
  60. package/dist/template/src/app/error.tsx +3 -3
  61. package/dist/template/src/app/icon.tsx +29 -0
  62. package/dist/template/src/app/layout.tsx +42 -0
  63. package/dist/template/src/app/not-found.tsx +35 -0
  64. package/dist/template/src/app/page.tsx +62 -28
  65. package/dist/template/src/components/ai-chat.tsx +26 -21
  66. package/dist/template/src/components/breadcrumbs.tsx +46 -2
  67. package/dist/template/src/components/copy-button.tsx +17 -3
  68. package/dist/template/src/components/docs-layout.tsx +142 -8
  69. package/dist/template/src/components/feedback.tsx +4 -2
  70. package/dist/template/src/components/footer.tsx +42 -0
  71. package/dist/template/src/components/header.tsx +29 -5
  72. package/dist/template/src/components/mdx/accordion.tsx +7 -6
  73. package/dist/template/src/components/mdx/card.tsx +19 -7
  74. package/dist/template/src/components/mdx/code-block.tsx +17 -3
  75. package/dist/template/src/components/mdx/code-group.tsx +65 -18
  76. package/dist/template/src/components/mdx/code-playground.tsx +3 -0
  77. package/dist/template/src/components/mdx/go-playground.tsx +3 -0
  78. package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
  79. package/dist/template/src/components/mdx/python-playground.tsx +2 -0
  80. package/dist/template/src/components/mdx/tabs.tsx +74 -6
  81. package/dist/template/src/components/page-header.tsx +19 -0
  82. package/dist/template/src/components/scroll-to-top.tsx +33 -0
  83. package/dist/template/src/components/search-dialog.tsx +206 -52
  84. package/dist/template/src/components/sidebar.tsx +136 -77
  85. package/dist/template/src/components/table-of-contents.tsx +23 -7
  86. package/dist/template/src/lib/highlight.ts +90 -31
  87. package/dist/template/src/lib/search.ts +14 -4
  88. package/dist/template/src/lib/theme-utils.ts +140 -0
  89. package/dist/template/src/styles/globals.css +307 -166
  90. package/dist/template/src/types/remark-gfm.d.ts +2 -0
  91. package/dist/utils/files.d.ts +9 -0
  92. package/dist/utils/files.js +33 -0
  93. package/dist/utils/validation.d.ts +4 -0
  94. package/dist/utils/validation.js +38 -0
  95. package/package.json +1 -4
@@ -0,0 +1,624 @@
1
+ ## Functions
2
+
3
+ ### `autoFixExample`
4
+
5
+ ```typescript
6
+ async function autoFixExample(example: CodeExample, client: LLMClient, options: AutoFixOptions = {}): Promise<FixResult>
7
+ ```
8
+
9
+ Use this to automatically repair broken or invalid code examples by iteratively applying LLM-powered fixes until the code is valid or the maximum retry limit is reached.
10
+
11
+ This is ideal for CI pipelines, documentation generators, or developer tools that need to ensure code examples stay compilable and correct as APIs evolve.
12
+
13
+ #### Parameters
14
+
15
+ | Name | Type | Required | Description |
16
+ |------|------|----------|-------------|
17
+ | `example` | `CodeExample` | ✅ | The code example to fix, including its source code, language, and optional error context |
18
+ | `client` | `LLMClient` | ✅ | An LLM client instance used to generate fix suggestions |
19
+ | `options` | `AutoFixOptions` | ❌ | Configuration options such as `maxIterations` (default: `3`) to control retry attempts |
20
+
21
+ #### Returns
22
+
23
+ Returns a `Promise<FixResult>` that resolves with:
24
+
25
+ | Field | Type | Description |
26
+ |-------|------|-------------|
27
+ | `success` | `boolean` | Whether the example was successfully fixed |
28
+ | `fixedCode` | `string \| null` | The repaired code if successful, otherwise `null` |
29
+ | `iterations` | `number` | How many fix attempts were made |
30
+ | `error` | `string \| undefined` | Description of the failure if `success` is `false` |
31
+
32
+ **Example:**
33
+
34
+ ```typescript example.ts
35
+ // ── Inline types (no external imports needed) ──────────────────────────────
36
+
37
+ type CodeExample = {
38
+ code: string
39
+ language: string
40
+ errorContext?: string
41
+ }
42
+
43
+ type AutoFixOptions = {
44
+ maxIterations?: number
45
+ }
46
+
47
+ type FixResult = {
48
+ success: boolean
49
+ fixedCode: string | null
50
+ iterations: number
51
+ error?: string
52
+ }
53
+
54
+ type LLMClient = {
55
+ complete: (prompt: string) => Promise<string>
56
+ }
57
+
58
+ // ── Simulated LLM client (replace with your real client) ───────────────────
59
+
60
+ function createLLMClient(apiKey: string): LLMClient {
61
+ return {
62
+ complete: async (prompt: string): Promise<string> => {
63
+ // In production this would call OpenAI, Anthropic, etc.
64
+ console.log(`[LLM] Sending prompt (${prompt.length} chars) to API...`)
65
+
66
+ // Simulate a fix: replace the broken fetch call with a corrected version
67
+ return `
68
+ async function fetchUser(id: string) {
69
+ const response = await fetch(\`https://api.example.com/users/\${id}\`, {
70
+ headers: { Authorization: \`Bearer \${process.env.API_TOKEN}\` }
71
+ });
72
+ if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
73
+ return response.json();
74
+ }
75
+ `.trim()
76
+ }
77
+ }
78
+ }
79
+
80
+ // ── Simulated autoFixExample (mirrors the real implementation) ─────────────
81
+
82
+ async function autoFixExample(
83
+ example: CodeExample,
84
+ client: LLMClient,
85
+ options: AutoFixOptions = {}
86
+ ): Promise<FixResult> {
87
+ const maxIterations = options.maxIterations ?? 3
88
+ let currentCode = example.code
89
+ let iterations = 0
90
+
91
+ while (iterations < maxIterations) {
92
+ iterations++
93
+
94
+ const prompt = [
95
+ `Fix the following ${example.language} code example.`,
96
+ example.errorContext ? `Error: ${example.errorContext}` : '',
97
+ `\`\`\`${example.language}`,
98
+ currentCode,
99
+ '```',
100
+ 'Return only the corrected code, no explanation.'
101
+ ]
102
+ .filter(Boolean)
103
+ .join('\n')
104
+
105
+ try {
106
+ const fixedCode = await client.complete(prompt)
107
+
108
+ // Simulate a basic validation check (real impl would compile/lint)
109
+ const isValid = !fixedCode.includes('SYNTAX_ERROR') && fixedCode.trim().length > 0
110
+
111
+ if (isValid) {
112
+ return { success: true, fixedCode, iterations }
113
+ }
114
+
115
+ currentCode = fixedCode // retry with the partially-fixed code
116
+ } catch (err) {
117
+ return {
118
+ success: false,
119
+ fixedCode: null,
120
+ iterations,
121
+ error: err instanceof Error ? err.message : 'Unknown LLM error'
122
+ }
123
+ }
124
+ }
125
+
126
+ return {
127
+ success: false,
128
+ fixedCode: null,
129
+ iterations,
130
+ error: `Could not fix example after ${maxIterations} iteration(s)`
131
+ }
132
+ }
133
+
134
+ // ── Usage ──────────────────────────────────────────────────────────────────
135
+
136
+ const brokenExample: CodeExample = {
137
+ language: 'typescript',
138
+ errorContext: "Cannot read properties of undefined (reading 'json')",
139
+ code: `
140
+ async function fetchUser(id: string) {
141
+ const response = fetch(\`https://api.example.com/users/\${id}\`) // missing await
142
+ return response.json()
143
+ }
144
+ `.trim()
145
+ }
146
+
147
+ async function main() {
148
+ const client = createLLMClient(process.env.OPENAI_API_KEY || 'sk-your-api-key-here')
149
+
150
+ try {
151
+ const result = await autoFixExample(brokenExample, client, { maxIterations: 3 })
152
+
153
+ if (result.success) {
154
+ console.log(`✅ Fixed in ${result.iterations} iteration(s):\n`)
155
+ console.log(result.fixedCode)
156
+ } else {
157
+ console.warn(`❌ Fix failed after ${result.iterations} attempt(s): ${result.error}`)
158
+ }
159
+
160
+ // Expected output:
161
+ // [LLM] Sending prompt (... chars) to API...
162
+ // ✅ Fixed in 1 iteration(s):
163
+ //
164
+ // async function fetchUser(id: string) {
165
+ // const response = await fetch(`https://api.example.com/users/${id}`, {
166
+ // headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
167
+ // });
168
+ // if (!response.ok) throw new Error(`HTTP ${response.status}`);
169
+ // return response.json();
170
+ // }
171
+ } catch (error) {
172
+ console.error('Unexpected error during auto-fix:', error)
173
+ process.exit(1)
174
+ }
175
+ }
176
+
177
+ main()
178
+ ```
179
+
180
+ ### `autoFixBatch`
181
+
182
+ ```typescript
183
+ async function autoFixBatch(examples: CodeExample[], client: LLMClient, options: AutoFixOptions = {}): Promise<Map<number, FixResult>>
184
+ ```
185
+
186
+ Use this to automatically fix multiple broken code examples in a single batch operation, getting back a map of results indexed by position.
187
+
188
+ This is the batch version of `autoFix` — ideal when you have a collection of code examples that need validation and repair, such as during a documentation build pipeline or CI check.
189
+
190
+ #### Parameters
191
+
192
+ | Name | Type | Required | Description |
193
+ |------|------|----------|-------------|
194
+ | `examples` | `CodeExample[]` | ✅ Yes | Array of code examples to fix, each with a `code` string and optional metadata |
195
+ | `client` | `LLMClient` | ✅ Yes | LLM client instance used to analyze and rewrite broken examples |
196
+ | `options` | `AutoFixOptions` | ❌ No | Configuration options such as `maxRetries`, `language`, and `context` hints |
197
+
198
+ #### Returns
199
+
200
+ Returns `Promise<Map<number, FixResult>>` — a `Map` where:
201
+ - **Key** (`number`): The zero-based index of the example in the input array
202
+ - **Value** (`FixResult`): An object containing:
203
+ - `fixed` (`boolean`): Whether the example was successfully repaired
204
+ - `code` (`string`): The resulting code (fixed or original if unfixable)
205
+ - `error` (`string | undefined`): Description of what was wrong, if applicable
206
+ - `attempts` (`number`): How many fix attempts were made
207
+
208
+ Only examples that were processed appear in the map. If an example was skipped or errored fatally, it may be absent.
209
+
210
+ **Example:**
211
+
212
+ ```typescript example.ts
213
+ // ── Inline types (do not import from autodocs) ──────────────────────────────
214
+
215
+ type CodeExample = {
216
+ code: string
217
+ language?: string
218
+ filename?: string
219
+ }
220
+
221
+ type FixResult = {
222
+ fixed: boolean
223
+ code: string
224
+ error?: string
225
+ attempts: number
226
+ }
227
+
228
+ type AutoFixOptions = {
229
+ maxRetries?: number
230
+ language?: string
231
+ contextHint?: string
232
+ dryRun?: boolean
233
+ }
234
+
235
+ type LLMClient = {
236
+ complete: (prompt: string) => Promise<string>
237
+ }
238
+
239
+ // ── Simulated autoFixBatch implementation ───────────────────────────────────
240
+
241
+ async function autoFixBatch(
242
+ examples: CodeExample[],
243
+ client: LLMClient,
244
+ options: AutoFixOptions = {}
245
+ ): Promise<Map<number, FixResult>> {
246
+ const { maxRetries = 2, dryRun = false } = options
247
+ const results = new Map<number, FixResult>()
248
+
249
+ for (let i = 0; i < examples.length; i++) {
250
+ const example = examples[i]
251
+ let attempts = 0
252
+ let fixed = false
253
+ let finalCode = example.code
254
+ let errorMsg: string | undefined
255
+
256
+ // Simulate detecting and fixing issues
257
+ const hasIssue = example.code.includes('???') || example.code.includes('BROKEN')
258
+
259
+ if (!hasIssue) {
260
+ results.set(i, { fixed: true, code: finalCode, attempts: 0 })
261
+ continue
262
+ }
263
+
264
+ while (attempts < maxRetries && !fixed) {
265
+ attempts++
266
+ if (!dryRun) {
267
+ // In real usage, the LLM client rewrites the broken code
268
+ const prompt = `Fix this ${example.language ?? 'code'} example:\n${example.code}`
269
+ const suggestion = await client.complete(prompt)
270
+ finalCode = suggestion
271
+ fixed = !finalCode.includes('???') && !finalCode.includes('BROKEN')
272
+ }
273
+ }
274
+
275
+ errorMsg = fixed ? undefined : `Could not repair example after ${attempts} attempt(s)`
276
+ results.set(i, { fixed, code: finalCode, error: errorMsg, attempts })
277
+ }
278
+
279
+ return results
280
+ }
281
+
282
+ // ── Mock LLM client ──────────────────────────────────────────────────────────
283
+
284
+ function createMockLLMClient(apiKey: string): LLMClient {
285
+ return {
286
+ complete: async (prompt: string): Promise<string> => {
287
+ // Simulate a repaired response from the LLM
288
+ console.log(` [LLM] Sending fix request (key: ${apiKey.slice(0, 8)}...)`)
289
+ return `const greet = (name: string) => \`Hello, \${name}!\`;\nconsole.log(greet('World'));`
290
+ },
291
+ }
292
+ }
293
+
294
+ // ── Main ─────────────────────────────────────────────────────────────────────
295
+
296
+ const examples: CodeExample[] = [
297
+ {
298
+ code: `const greet = (name: string) => \`Hello, \${name}!\`;\nconsole.log(greet('World'));`,
299
+ language: 'typescript',
300
+ filename: 'greet.ts',
301
+ },
302
+ {
303
+ code: `function add(a, b) { return ???; }`, // BROKEN
304
+ language: 'javascript',
305
+ filename: 'add.js',
306
+ },
307
+ {
308
+ code: `export const PI = BROKEN_VALUE;`, // BROKEN
309
+ language: 'typescript',
310
+ filename: 'constants.ts',
311
+ },
312
+ ]
313
+
314
+ async function main() {
315
+ const apiKey = process.env.LLM_API_KEY || 'sk-demo-1234567890abcdef'
316
+ const client = createMockLLMClient(apiKey)
317
+
318
+ const options: AutoFixOptions = {
319
+ maxRetries: 3,
320
+ language: 'typescript',
321
+ contextHint: 'These are documentation examples for a math utility library',
322
+ }
323
+
324
+ try {
325
+ console.log(`Processing ${examples.length} examples...\n`)
326
+ const results = await autoFixBatch(examples, client, options)
327
+
328
+ results.forEach((result, index) => {
329
+ const example = examples[index]
330
+ console.log(`── Example ${index} (${example.filename}) ──`)
331
+ console.log(` Fixed: ${result.fixed}`)
332
+ console.log(` Attempts: ${result.attempts}`)
333
+ if (result.error) {
334
+ console.log(` Error: ${result.error}`)
335
+ }
336
+ console.log(` Code: ${result.code.split('\n')[0]}...`)
337
+ console.log()
338
+ })
339
+
340
+ // Summary
341
+ const fixedCount = [...results.values()].filter((r) => r.fixed).length
342
+ console.log(`✅ ${fixedCount}/${results.size} examples fixed successfully`)
343
+
344
+ // Expected output:
345
+ // Processing 3 examples...
346
+ //
347
+ // ── Example 0 (greet.ts) ──
348
+ // Fixed: true
349
+ // Attempts: 0
350
+ // Code: const greet = (name: string) => `Hello, ${name}!`;...
351
+ //
352
+ // ── Example 1 (add.js) ──
353
+ // Fixed: true
354
+ // Attempts: 1
355
+ // Code: const greet = (name: string) => `Hello, ${name}!`;...
356
+ //
357
+ // ── Example 2 (constants.ts) ──
358
+ // Fixed: true
359
+ // Attempts: 1
360
+ // Code: const greet = (name: string) => `Hello, ${name}!`;...
361
+ //
362
+ // ✅ 3/3 examples fixed successfully
363
+ } catch (error) {
364
+ console.error('Batch fix failed:', error instanceof Error ? error.message : error)
365
+ process.exit(1)
366
+ }
367
+ }
368
+
369
+ main()
370
+ ```
371
+
372
+ ### `createTypeScriptValidator`
373
+
374
+ ```typescript
375
+ function createTypeScriptValidator(): (code: string) => Promise<ValidationResult>
376
+ ```
377
+
378
+ Use this to validate TypeScript code strings at runtime — perfect for checking LLM-generated code, user-submitted snippets, or dynamically built TypeScript before execution.
379
+
380
+ Returns a validator function that accepts a TypeScript code string and resolves with a `ValidationResult` indicating whether the code is valid and any diagnostic errors found.
381
+
382
+ #### Parameters
383
+
384
+ This factory function takes no parameters.
385
+
386
+ #### Returned Validator Function
387
+
388
+ | Name | Type | Required | Description |
389
+ |------|------|----------|-------------|
390
+ | `code` | `string` | ✅ | The TypeScript source code string to validate |
391
+
392
+ #### Returns
393
+
394
+ **`(code: string) => Promise<ValidationResult>`** — A reusable async validator function.
395
+
396
+ Each call to the validator returns a `Promise<ValidationResult>`:
397
+
398
+ | Field | Type | Description |
399
+ |-------|------|-------------|
400
+ | `valid` | `boolean` | `true` if the code compiled without errors |
401
+ | `errors` | `string[]` | List of TypeScript diagnostic messages; empty array if valid |
402
+
403
+ #### When results are returned
404
+
405
+ | Scenario | `valid` | `errors` |
406
+ |----------|---------|----------|
407
+ | Code compiles cleanly | `true` | `[]` |
408
+ | Type errors or syntax issues found | `false` | Array of diagnostic messages |
409
+ | Unexpected runtime failure | `false` | Single-element array with the caught error message |
410
+
411
+ **Example:**
412
+
413
+ ```typescript example.ts
414
+ import * as ts from 'typescript'
415
+
416
+ // --- Inline types (do not import from autodocs) ---
417
+ interface ValidationResult {
418
+ valid: boolean
419
+ errors: string[]
420
+ }
421
+
422
+ // --- Inline implementation matching the library's behavior ---
423
+ function createTypeScriptValidator(): (code: string) => Promise<ValidationResult> {
424
+ return async (code: string): Promise<ValidationResult> => {
425
+ try {
426
+ const diagnostics: string[] = []
427
+
428
+ const result = ts.transpileModule(code, {
429
+ compilerOptions: {
430
+ strict: true,
431
+ noImplicitAny: true,
432
+ target: ts.ScriptTarget.ES2020,
433
+ },
434
+ reportDiagnostics: true,
435
+ })
436
+
437
+ if (result.diagnostics && result.diagnostics.length > 0) {
438
+ for (const diag of result.diagnostics) {
439
+ const message =
440
+ typeof diag.messageText === 'string'
441
+ ? diag.messageText
442
+ : diag.messageText.messageText
443
+ diagnostics.push(message)
444
+ }
445
+ return { valid: false, errors: diagnostics }
446
+ }
447
+
448
+ return { valid: true, errors: [] }
449
+ } catch (err) {
450
+ return {
451
+ valid: false,
452
+ errors: [err instanceof Error ? err.message : String(err)],
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ // --- Usage ---
459
+ const validate = createTypeScriptValidator()
460
+
461
+ async function main() {
462
+ try {
463
+ // ✅ Valid TypeScript
464
+ const validCode = `
465
+ const greet = (name: string): string => {
466
+ return \`Hello, \${name}!\`
467
+ }
468
+ console.log(greet('World'))
469
+ `
470
+
471
+ const validResult = await validate(validCode)
472
+ console.log('Valid code result:', validResult)
473
+ // Output: { valid: true, errors: [] }
474
+
475
+ // ❌ Invalid TypeScript — type mismatch
476
+ const invalidCode = `
477
+ const add = (a: number, b: number): number => {
478
+ return a + b
479
+ }
480
+ const result: string = add(1, 2)
481
+ `
482
+
483
+ const invalidResult = await validate(invalidCode)
484
+ console.log('Invalid code result:', invalidResult)
485
+ // Output: { valid: false, errors: ["Type 'number' is not assignable to type 'string'."] }
486
+
487
+ // ❌ Syntax error
488
+ const brokenCode = `
489
+ function broken( {
490
+ return 42
491
+ `
492
+
493
+ const brokenResult = await validate(brokenCode)
494
+ console.log('Broken code result:', brokenResult)
495
+ // Output: { valid: false, errors: ["',' expected.", ...] }
496
+
497
+ } catch (error) {
498
+ console.error('Unexpected failure:', error)
499
+ }
500
+ }
501
+
502
+ main()
503
+ ```
504
+
505
+ ### `createPythonValidator`
506
+
507
+ ```typescript
508
+ function createPythonValidator(): (code: string) => Promise<ValidationResult>
509
+ ```
510
+
511
+ Use this to validate Python code syntax and catch errors before execution, without needing a Python runtime wrapper or external validation service — just `python3` in your PATH.
512
+
513
+ Returns a reusable validator function you can call repeatedly with different code strings. Each call spawns a `python3` process to perform real syntax checking.
514
+
515
+ #### Parameters
516
+
517
+ This factory function takes no parameters.
518
+
519
+ #### Returns
520
+
521
+ | Return | Type | Description |
522
+ |--------|------|-------------|
523
+ | validator | `(code: string) => Promise<ValidationResult>` | Async function that validates a Python code string |
524
+
525
+ #### `ValidationResult` Shape
526
+
527
+ | Field | Type | Description |
528
+ |-------|------|-------------|
529
+ | `valid` | `boolean` | `true` if the code passed validation, `false` if errors were found |
530
+ | `errors` | `string[]` | List of error messages; empty array when `valid` is `true` |
531
+
532
+ #### When each value is returned
533
+
534
+ - **`valid: true`** — Python code is syntactically correct and parseable
535
+ - **`valid: false`** — Code contains syntax errors, indentation issues, or other parse-time problems; `errors` will contain the Python error output
536
+
537
+ #### Requirements
538
+
539
+ - `python3` must be available in your system `PATH`
540
+ - Works by writing code to a temp file and running `python3 -m py_compile` against it
541
+
542
+ **Example:**
543
+
544
+ ```typescript example.ts
545
+ import { spawnSync } from 'child_process'
546
+ import { writeFileSync, unlinkSync } from 'fs'
547
+ import { join } from 'path'
548
+ import { tmpdir } from 'os'
549
+
550
+ // Inline the ValidationResult type (matches the library's shape)
551
+ type ValidationResult = {
552
+ valid: boolean
553
+ errors: string[]
554
+ }
555
+
556
+ // Self-contained implementation matching the library's behavior
557
+ function createPythonValidator(): (code: string) => Promise<ValidationResult> {
558
+ return async (code: string): Promise<ValidationResult> => {
559
+ const tmpFile = join(tmpdir(), `validate_${Date.now()}.py`)
560
+
561
+ try {
562
+ writeFileSync(tmpFile, code, 'utf8')
563
+
564
+ const result = spawnSync('python3', ['-m', 'py_compile', tmpFile], {
565
+ encoding: 'utf8',
566
+ timeout: 10_000,
567
+ })
568
+
569
+ if (result.status === 0) {
570
+ return { valid: true, errors: [] }
571
+ }
572
+
573
+ const errorOutput = result.stderr || result.stdout || 'Unknown error'
574
+ // Strip the temp file path from error messages for cleaner output
575
+ const cleanedError = errorOutput.replace(tmpFile, '<code>')
576
+ return { valid: false, errors: [cleanedError.trim()] }
577
+ } catch (err) {
578
+ return {
579
+ valid: false,
580
+ errors: [`Validator error: ${err instanceof Error ? err.message : String(err)}`],
581
+ }
582
+ } finally {
583
+ try { unlinkSync(tmpFile) } catch { /* ignore cleanup errors */ }
584
+ }
585
+ }
586
+ }
587
+
588
+ // --- Usage ---
589
+
590
+ const validate = createPythonValidator()
591
+
592
+ async function main() {
593
+ try {
594
+ // ✅ Valid Python
595
+ const validCode = `
596
+ def greet(name: str) -> str:
597
+ return f"Hello, {name}!"
598
+
599
+ print(greet("world"))
600
+ `.trim()
601
+
602
+ const validResult = await validate(validCode)
603
+ console.log('Valid code result:', validResult)
604
+ // Output: { valid: true, errors: [] }
605
+
606
+ // ❌ Invalid Python (bad indentation + missing colon)
607
+ const invalidCode = `
608
+ def broken(x)
609
+ return x * 2
610
+ `.trim()
611
+
612
+ const invalidResult = await validate(invalidCode)
613
+ console.log('Invalid code result:', invalidResult)
614
+ // Output: { valid: false, errors: [' File "<code>", line 1\n def broken(x)\n ^\nSyntaxError: expected \':\'' ] }
615
+
616
+ } catch (error) {
617
+ console.error('Unexpected failure:', error)
618
+ process.exit(1)
619
+ }
620
+ }
621
+
622
+ main()
623
+ ```
624
+