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,469 @@
1
+ # Python.ts
2
+
3
+ ## Classes
4
+
5
+ ### `PythonScanner`
6
+
7
+ ```typescript
8
+ class PythonScanner implements Scanner
9
+ ```
10
+
11
+ Use this to scan Python source files and extract structured metadata (functions, classes, imports, etc.) by delegating parsing to a Python3 subprocess.
12
+
13
+ `PythonScanner` implements the `Scanner` interface and is the go-to handler for any `.py` file in an autodocs pipeline. It spawns a `python3` process running an internal parser script and returns a `ScanResult` with the extracted symbols and structure.
14
+
15
+ ---
16
+
17
+ ### Properties
18
+
19
+ | Property | Type | Description |
20
+ |---|---|---|
21
+ | `languages` | `string[]` | Always `['python']` — declares which languages this scanner handles |
22
+
23
+ ---
24
+
25
+ ### Methods
26
+
27
+ #### `canHandle(filePath)`
28
+
29
+ | Name | Type | Required | Description |
30
+ |---|---|---|---|
31
+ | `filePath` | `string` | ✅ | Path to the file to check |
32
+
33
+ **Returns:** `boolean` — `true` if the file ends with `.py`, `false` otherwise. Use this as a fast pre-check before calling `scanFile`.
34
+
35
+ ---
36
+
37
+ #### `scanFile(filePath)`
38
+
39
+ | Name | Type | Required | Description |
40
+ |---|---|---|---|
41
+ | `filePath` | `string` | ✅ | Absolute or relative path to the `.py` file to parse |
42
+
43
+ **Returns:** `Promise<ScanResult>` — resolves with the parsed structure of the file (symbols, classes, functions, imports, etc.). Resolves (never rejects) even on parse failure — check the result for error fields.
44
+
45
+ > ⚠️ **Requires `python3`** to be available on the system `PATH`. The scanner spawns a subprocess for every file scanned — avoid calling it in tight loops without concurrency control.
46
+
47
+ ---
48
+
49
+ **Example:**
50
+
51
+ ```typescript example.ts
52
+ import { spawn } from 'child_process'
53
+ import { writeFileSync, unlinkSync } from 'fs'
54
+ import { join } from 'path'
55
+ import { tmpdir } from 'os'
56
+
57
+ // --- Inline types (mirrors autodocs Scanner interface) ---
58
+ type ScanResult = {
59
+ filePath: string
60
+ language: string
61
+ symbols?: Array<{ name: string; kind: string; line: number }>
62
+ error?: string
63
+ }
64
+
65
+ interface Scanner {
66
+ languages: string[]
67
+ canHandle(filePath: string): boolean
68
+ scanFile(filePath: string): Promise<ScanResult>
69
+ }
70
+
71
+ // --- Self-contained PythonScanner implementation ---
72
+ class PythonScanner implements Scanner {
73
+ languages = ['python']
74
+
75
+ canHandle(filePath: string): boolean {
76
+ return filePath.endsWith('.py')
77
+ }
78
+
79
+ async scanFile(filePath: string): Promise<ScanResult> {
80
+ // Inline parser script as a string — no external .py file needed
81
+ const parserScript = `
82
+ import ast, json, sys
83
+
84
+ def scan(path):
85
+ try:
86
+ with open(path, 'r') as f:
87
+ source = f.read()
88
+ tree = ast.parse(source)
89
+ symbols = []
90
+ for node in ast.walk(tree):
91
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
92
+ symbols.append({'name': node.name, 'kind': 'function', 'line': node.lineno})
93
+ elif isinstance(node, ast.ClassDef):
94
+ symbols.append({'name': node.name, 'kind': 'class', 'line': node.lineno})
95
+ print(json.dumps({'filePath': path, 'language': 'python', 'symbols': symbols}))
96
+ except Exception as e:
97
+ print(json.dumps({'filePath': path, 'language': 'python', 'error': str(e)}))
98
+
99
+ scan(sys.argv[1])
100
+ `
101
+ // Write the inline parser to a temp file
102
+ const parserPath = join(tmpdir(), `_autodocs_parser_${process.pid}.py`)
103
+ writeFileSync(parserPath, parserScript)
104
+
105
+ return new Promise((resolve) => {
106
+ let stdout = ''
107
+ let stderr = ''
108
+
109
+ const proc = spawn('python3', [parserPath, filePath], {
110
+ stdio: ['ignore', 'pipe', 'pipe'],
111
+ })
112
+
113
+ proc.stdout.on('data', (chunk: Buffer) => { stdout += chunk.toString() })
114
+ proc.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString() })
115
+
116
+ proc.on('close', (code: number) => {
117
+ try { unlinkSync(parserPath) } catch { /* cleanup best-effort */ }
118
+
119
+ if (code !== 0 || !stdout.trim()) {
120
+ resolve({
121
+ filePath,
122
+ language: 'python',
123
+ error: stderr.trim() || `python3 exited with code ${code}`,
124
+ })
125
+ return
126
+ }
127
+
128
+ try {
129
+ resolve(JSON.parse(stdout.trim()) as ScanResult)
130
+ } catch {
131
+ resolve({ filePath, language: 'python', error: 'Failed to parse scanner output' })
132
+ }
133
+ })
134
+
135
+ proc.on('error', (err: Error) => {
136
+ resolve({ filePath, language: 'python', error: err.message })
137
+ })
138
+ })
139
+ }
140
+ }
141
+
142
+ // --- Demo ---
143
+ async function main() {
144
+ const scanner = new PythonScanner()
145
+
146
+ // Write a sample Python file to scan
147
+ const samplePy = join(tmpdir(), 'sample_autodocs.py')
148
+ writeFileSync(samplePy, `
149
+ class DataProcessor:
150
+ def __init__(self, config):
151
+ self.config = config
152
+
153
+ def process(self, data):
154
+ return [x * 2 for x in data]
155
+
156
+ async def fetch_records(url: str) -> list:
157
+ pass
158
+
159
+ def helper():
160
+ return True
161
+ `)
162
+
163
+ console.log('canHandle(".py"):', scanner.canHandle(samplePy)) // true
164
+ console.log('canHandle(".js"):', scanner.canHandle('index.js')) // false
165
+
166
+ try {
167
+ const result = await scanner.scanFile(samplePy)
168
+ console.log('\nScan result:')
169
+ console.log(JSON.stringify(result, null, 2))
170
+ // Expected output:
171
+ // {
172
+ // "filePath": "/tmp/sample_autodocs.py",
173
+ // "language": "python",
174
+ // "symbols": [
175
+ // { "name": "DataProcessor", "kind": "class", "line": 2 },
176
+ // { "name": "__init__", "kind": "function", "line": 3 },
177
+ // { "name": "process", "kind": "function", "line": 6 },
178
+ // { "name": "fetch_records", "kind": "function", "line": 9 },
179
+ // { "name": "helper", "kind": "function", "line": 12 }
180
+ // ]
181
+ // }
182
+ } catch (error) {
183
+ console.error('Scan failed unexpectedly:', error)
184
+ } finally {
185
+ try { unlinkSync(samplePy) } catch { /* cleanup */ }
186
+ }
187
+ }
188
+
189
+ main()
190
+ ```
191
+
192
+ ### Methods
193
+
194
+ #### `canHandle`
195
+
196
+ ```typescript
197
+ canHandle(filePath: string): boolean
198
+ ```
199
+
200
+ Use this to quickly check whether a given file path should be processed by the Python scanner before committing to a full file scan.
201
+
202
+ Call `canHandle` as a pre-flight check to filter out non-Python files in a pipeline, avoiding unnecessary scan overhead.
203
+
204
+ ### Parameters
205
+
206
+ | Name | Type | Required | Description |
207
+ |------|------|----------|-------------|
208
+ | `filePath` | `string` | ✅ | The file path to evaluate. Can be a relative path, absolute path, or just a filename. |
209
+
210
+ ### Returns
211
+
212
+ | Value | Condition |
213
+ |-------|-----------|
214
+ | `true` | The `filePath` ends with `.py` |
215
+ | `false` | The `filePath` ends with any other extension (or no extension) |
216
+
217
+ **Example:**
218
+
219
+ ```typescript example.ts
220
+ // Inline implementation of PythonScanner.canHandle
221
+ class PythonScanner {
222
+ languages = ['python']
223
+
224
+ canHandle(filePath: string): boolean {
225
+ return filePath.endsWith('.py')
226
+ }
227
+ }
228
+
229
+ // Simulate a list of files discovered in a project directory
230
+ const discoveredFiles = [
231
+ '/project/src/main.py',
232
+ '/project/src/utils.py',
233
+ '/project/src/config.json',
234
+ '/project/src/README.md',
235
+ '/project/src/server.ts',
236
+ '/project/scripts/deploy.py',
237
+ '/project/scripts/build.sh',
238
+ ]
239
+
240
+ const scanner = new PythonScanner()
241
+
242
+ try {
243
+ // Filter only the files this scanner can handle
244
+ const pythonFiles = discoveredFiles.filter((file) => scanner.canHandle(file))
245
+
246
+ console.log('All discovered files:', discoveredFiles.length)
247
+ console.log('Python files to scan:', pythonFiles)
248
+ // Output:
249
+ // All discovered files: 7
250
+ // Python files to scan: [
251
+ // '/project/src/main.py',
252
+ // '/project/src/utils.py',
253
+ // '/project/scripts/deploy.py'
254
+ // ]
255
+
256
+ // Spot-check individual cases
257
+ console.log('\nSpot checks:')
258
+ console.log(scanner.canHandle('analysis.py')) // true
259
+ console.log(scanner.canHandle('analysis.py.bak')) // false — .bak wins
260
+ console.log(scanner.canHandle('script.ts')) // false
261
+ console.log(scanner.canHandle('')) // false
262
+ } catch (error) {
263
+ console.error('Unexpected error during file filtering:', error)
264
+ }
265
+ ```
266
+
267
+ #### `scanFile`
268
+
269
+ ```typescript
270
+ async scanFile(filePath: string): Promise<ScanResult>
271
+ ```
272
+
273
+ Use this to scan a Python source file and extract structured metadata — functions, classes, imports, and other code elements — by running a Python parser subprocess and returning the results asynchronously.
274
+
275
+ This is the core method of `PythonScanner`. It delegates parsing to a `python3` child process, making it suitable for deep, AST-level analysis of `.py` files without reimplementing Python parsing in TypeScript.
276
+
277
+ ## Parameters
278
+
279
+ | Name | Type | Required | Description |
280
+ |------|------|----------|-------------|
281
+ | `filePath` | `string` | ✅ | Absolute or relative path to the `.py` file to scan |
282
+
283
+ ## Returns
284
+
285
+ Returns a `Promise<ScanResult>` that resolves when the Python subprocess completes.
286
+
287
+ | Scenario | Result |
288
+ |----------|--------|
289
+ | File parsed successfully | Resolves with a `ScanResult` containing extracted code metadata |
290
+ | File not found / parse error | Resolves (or rejects) with an error-state `ScanResult` or throws |
291
+ | Non-`.py` file passed | Undefined behavior — use `canHandle(filePath)` to guard first |
292
+
293
+ ### `ScanResult` Shape (typical)
294
+ ```ts
295
+ {
296
+ filePath: string // path that was scanned
297
+ language: string // e.g. "python"
298
+ functions: string[] // extracted function names
299
+ classes: string[] // extracted class names
300
+ imports: string[] // top-level imports
301
+ errors: string[] // any parse errors encountered
302
+ }
303
+ ```
304
+
305
+ ## Notes
306
+ - Internally spawns `python3` as a subprocess — ensure Python 3 is available in the runtime environment.
307
+ - Always call `canHandle(filePath)` before `scanFile()` to confirm the file is a `.py` file.
308
+ - The method is async; always `await` it or chain `.then()`.
309
+
310
+ **Example:**
311
+
312
+ ```typescript example.ts
313
+ import { spawn } from 'child_process'
314
+ import { writeFileSync, unlinkSync } from 'fs'
315
+ import { tmpdir } from 'os'
316
+ import { join } from 'path'
317
+
318
+ // --- Inline types (mirrors the real ScanResult shape) ---
319
+ interface ScanResult {
320
+ filePath: string
321
+ language: string
322
+ functions: string[]
323
+ classes: string[]
324
+ imports: string[]
325
+ errors: string[]
326
+ }
327
+
328
+ // --- Inline PythonScanner (self-contained, no external imports) ---
329
+ class PythonScanner {
330
+ canHandle(filePath: string): boolean {
331
+ return filePath.endsWith('.py')
332
+ }
333
+
334
+ async scanFile(filePath: string): Promise<ScanResult> {
335
+ // Inline parser script: prints JSON to stdout
336
+ const inlineParserScript = `
337
+ import ast, json, sys
338
+
339
+ def scan(path):
340
+ try:
341
+ with open(path, 'r') as f:
342
+ source = f.read()
343
+ tree = ast.parse(source)
344
+ functions = [n.name for n in ast.walk(tree) if isinstance(n, ast.FunctionDef)]
345
+ classes = [n.name for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]
346
+ imports = []
347
+ for n in ast.walk(tree):
348
+ if isinstance(n, ast.Import):
349
+ imports += [a.name for a in n.names]
350
+ elif isinstance(n, ast.ImportFrom):
351
+ imports.append(n.module or '')
352
+ print(json.dumps({
353
+ "filePath": path,
354
+ "language": "python",
355
+ "functions": functions,
356
+ "classes": classes,
357
+ "imports": imports,
358
+ "errors": []
359
+ }))
360
+ except Exception as e:
361
+ print(json.dumps({
362
+ "filePath": path,
363
+ "language": "python",
364
+ "functions": [],
365
+ "classes": [],
366
+ "imports": [],
367
+ "errors": [str(e)]
368
+ }))
369
+
370
+ scan(sys.argv[1])
371
+ `
372
+ // Write the inline parser to a temp file
373
+ const parserPath = join(tmpdir(), `_scanner_parser_${Date.now()}.py`)
374
+ writeFileSync(parserPath, inlineParserScript)
375
+
376
+ return new Promise((resolve, reject) => {
377
+ const proc = spawn('python3', [parserPath, filePath], {
378
+ stdio: ['ignore', 'pipe', 'pipe']
379
+ })
380
+
381
+ let stdout = ''
382
+ let stderr = ''
383
+
384
+ proc.stdout.on('data', (chunk: Buffer) => { stdout += chunk.toString() })
385
+ proc.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString() })
386
+
387
+ proc.on('close', (code: number) => {
388
+ try { unlinkSync(parserPath) } catch { /* cleanup best-effort */ }
389
+
390
+ if (code !== 0) {
391
+ return reject(new Error(`python3 exited with code ${code}: ${stderr}`))
392
+ }
393
+ try {
394
+ const result: ScanResult = JSON.parse(stdout.trim())
395
+ resolve(result)
396
+ } catch {
397
+ reject(new Error(`Failed to parse scanner output: ${stdout}`))
398
+ }
399
+ })
400
+
401
+ proc.on('error', (err: Error) => {
402
+ try { unlinkSync(parserPath) } catch { /* cleanup best-effort */ }
403
+ reject(new Error(`Failed to spawn python3: ${err.message}`))
404
+ })
405
+ })
406
+ }
407
+ }
408
+
409
+ // --- Sample .py file to scan ---
410
+ const samplePyPath = join(tmpdir(), `sample_${Date.now()}.py`)
411
+ writeFileSync(samplePyPath, `
412
+ import os
413
+ import json
414
+ from pathlib import Path
415
+
416
+ class DataProcessor:
417
+ def __init__(self, config: dict):
418
+ self.config = config
419
+
420
+ def process(self, data: list) -> list:
421
+ return [item.strip() for item in data]
422
+
423
+ def load_config(path: str) -> dict:
424
+ with open(path) as f:
425
+ return json.load(f)
426
+
427
+ def main():
428
+ config = load_config("config.json")
429
+ processor = DataProcessor(config)
430
+ print(processor.process(["hello ", " world"]))
431
+ `)
432
+
433
+ // --- Run the scanner ---
434
+ async function main() {
435
+ const scanner = new PythonScanner()
436
+
437
+ // Guard: only scan .py files
438
+ if (!scanner.canHandle(samplePyPath)) {
439
+ console.error('File is not a Python file — skipping.')
440
+ process.exit(1)
441
+ }
442
+
443
+ try {
444
+ console.log(`Scanning: ${samplePyPath}\n`)
445
+ const result = await scanner.scanFile(samplePyPath)
446
+
447
+ console.log('Scan complete!')
448
+ console.log('Language :', result.language)
449
+ console.log('Classes :', result.classes)
450
+ console.log('Functions:', result.functions)
451
+ console.log('Imports :', result.imports)
452
+ console.log('Errors :', result.errors)
453
+
454
+ // Expected output:
455
+ // Language : python
456
+ // Classes : [ 'DataProcessor' ]
457
+ // Functions: [ '__init__', 'process', 'load_config', 'main' ]
458
+ // Imports : [ 'os', 'json', 'pathlib' ]
459
+ // Errors : []
460
+ } catch (error) {
461
+ console.error('Scan failed:', error instanceof Error ? error.message : error)
462
+ } finally {
463
+ try { unlinkSync(samplePyPath) } catch { /* cleanup */ }
464
+ }
465
+ }
466
+
467
+ main()
468
+ ```
469
+