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.
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,589 @@
1
+ ## Functions
2
+
3
+ ### `getAuthConfig`
4
+
5
+ ```typescript
6
+ function getAuthConfig(): AuthConfig
7
+ ```
8
+
9
+ Use this to retrieve the current authentication configuration from the local auth file. This is the primary way to check if a user is logged in, access their stored credentials, or verify token expiration before making API calls.
10
+
11
+ Returns an `AuthConfig` object with the user's stored auth data, or an empty object `{}` if no auth file exists yet (unauthenticated state).
12
+
13
+ #### Parameters
14
+
15
+ This function takes no parameters.
16
+
17
+ #### Returns
18
+
19
+ | Field | Type | Description |
20
+ |-------|------|-------------|
21
+ | `token` | `string` (optional) | The authentication token for API requests |
22
+ | `email` | `string` (optional) | The authenticated user's email address |
23
+ | `expiresAt` | `string` (optional) | ISO timestamp indicating when the token expires |
24
+ | `error` | `string` (optional) | Any error message associated with the auth state |
25
+
26
+ **Returns `{}`** (empty object) when no auth file is found — treat this as an unauthenticated state.
27
+
28
+ **Returns a populated `AuthConfig`** when the user has previously authenticated.
29
+
30
+ **Example:**
31
+
32
+ ```typescript example.ts
33
+ import { existsSync, readFileSync } from 'fs'
34
+ import { join } from 'path'
35
+ import { homedir } from 'os'
36
+
37
+ // Inline the AuthConfig type
38
+ type AuthConfig = {
39
+ token?: string
40
+ email?: string
41
+ expiresAt?: string
42
+ error?: string
43
+ }
44
+
45
+ // Inline the auth file path logic (mirrors the real implementation)
46
+ const AUTH_FILE = join(homedir(), '.config', 'autodocs', 'auth.json')
47
+
48
+ // Self-contained implementation of getAuthConfig
49
+ function getAuthConfig(): AuthConfig {
50
+ if (!existsSync(AUTH_FILE)) {
51
+ return {}
52
+ }
53
+ try {
54
+ return JSON.parse(readFileSync(AUTH_FILE, 'utf-8')) as AuthConfig
55
+ } catch {
56
+ return { error: 'Failed to parse auth config' }
57
+ }
58
+ }
59
+
60
+ // --- Usage Examples ---
61
+
62
+ function isAuthenticated(config: AuthConfig): boolean {
63
+ return !!config.token && !config.error
64
+ }
65
+
66
+ function isTokenExpired(config: AuthConfig): boolean {
67
+ if (!config.expiresAt) return false
68
+ return new Date(config.expiresAt) < new Date()
69
+ }
70
+
71
+ async function main() {
72
+ try {
73
+ const authConfig = getAuthConfig()
74
+
75
+ // Case 1: No auth file found
76
+ if (Object.keys(authConfig).length === 0) {
77
+ console.log('Status: Not authenticated — please run `autodocs login`')
78
+ // Output: Status: Not authenticated — please run `autodocs login`
79
+ return
80
+ }
81
+
82
+ // Case 2: Auth file exists, check validity
83
+ if (!isAuthenticated(authConfig)) {
84
+ console.log('Auth error:', authConfig.error ?? 'Unknown error')
85
+ return
86
+ }
87
+
88
+ if (isTokenExpired(authConfig)) {
89
+ console.log('Token expired at:', authConfig.expiresAt)
90
+ console.log('Please re-authenticate with `autodocs login`')
91
+ return
92
+ }
93
+
94
+ // Case 3: Valid, active session
95
+ console.log('Authenticated as:', authConfig.email)
96
+ console.log('Token (truncated):', authConfig.token?.slice(0, 8) + '...')
97
+ console.log('Session expires:', authConfig.expiresAt)
98
+ // Output:
99
+ // Authenticated as: user@example.com
100
+ // Token (truncated): eyJhbGci...
101
+ // Session expires: 2025-12-31T23:59:59.000Z
102
+
103
+ } catch (error) {
104
+ console.error('Unexpected error reading auth config:', error)
105
+ }
106
+ }
107
+
108
+ main()
109
+ ```
110
+
111
+ ### `saveAuthConfig`
112
+
113
+ ```typescript
114
+ function saveAuthConfig(config: AuthConfig): void
115
+ ```
116
+
117
+ Use this to persist authentication credentials to a local config file, ensuring the config directory exists before writing.
118
+
119
+ Saves an `AuthConfig` object as formatted JSON to a file on disk. Automatically creates any missing parent directories in the config path before writing, so you don't need to pre-create the directory structure.
120
+
121
+ ### Parameters
122
+
123
+ | Name | Type | Required | Description |
124
+ |------|------|----------|-------------|
125
+ | config | `AuthConfig` | ✅ | Authentication configuration object containing credentials/tokens to persist |
126
+
127
+ #### Returns
128
+
129
+ `void` — Writes synchronously to disk. Throws a filesystem error if the path is not writable or permissions are insufficient.
130
+
131
+ ### Notes
132
+ - Creates the config directory recursively if it does not exist (`mkdirSync` with `{ recursive: true }`)
133
+ - Writes JSON with 2-space indentation for human readability
134
+ - Overwrites any existing config file at the target path
135
+ - Throws if the write fails (e.g., permission denied, disk full)
136
+
137
+ **Example:**
138
+
139
+ ```typescript example.ts
140
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs'
141
+ import { join } from 'path'
142
+ import { homedir } from 'os'
143
+
144
+ // --- Inline type definition ---
145
+ interface AuthConfig {
146
+ token: string
147
+ userId: string
148
+ expiresAt?: number
149
+ refreshToken?: string
150
+ }
151
+
152
+ // --- Inline config path constants ---
153
+ const CONFIG_DIR = join(homedir(), '.config', 'myapp')
154
+ const AUTH_FILE = join(CONFIG_DIR, 'auth.json')
155
+
156
+ // --- Inlined implementation of saveAuthConfig ---
157
+ function saveAuthConfig(config: AuthConfig): void {
158
+ mkdirSync(CONFIG_DIR, { recursive: true })
159
+ writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2))
160
+ }
161
+
162
+ // --- Usage example ---
163
+ const authConfig: AuthConfig = {
164
+ token: process.env.AUTH_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example',
165
+ userId: process.env.USER_ID || 'usr_4f8a2b1c9d3e',
166
+ expiresAt: Date.now() + 1000 * 60 * 60 * 24, // 24 hours from now
167
+ refreshToken: process.env.REFRESH_TOKEN || 'ref_7k2m9p4q1r8s',
168
+ }
169
+
170
+ try {
171
+ saveAuthConfig(authConfig)
172
+ console.log(`✅ Auth config saved to: ${AUTH_FILE}`)
173
+
174
+ // Verify the file was written correctly
175
+ if (existsSync(AUTH_FILE)) {
176
+ const saved = JSON.parse(readFileSync(AUTH_FILE, 'utf-8')) as AuthConfig
177
+ console.log('Saved config:', saved)
178
+ // Output:
179
+ // {
180
+ // token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example',
181
+ // userId: 'usr_4f8a2b1c9d3e',
182
+ // expiresAt: 1718123456789,
183
+ // refreshToken: 'ref_7k2m9p4q1r8s'
184
+ // }
185
+ }
186
+ } catch (error) {
187
+ if (error instanceof Error) {
188
+ console.error('❌ Failed to save auth config:', error.message)
189
+ // Common causes: permission denied, disk full, invalid path
190
+ }
191
+ }
192
+ ```
193
+
194
+ ### `clearAuth`
195
+
196
+ ```typescript
197
+ function clearAuth(): void
198
+ ```
199
+
200
+ Use this to log a user out by wiping stored authentication credentials from the local config file. Calling this resets the auth file to an empty state (`{}`) without deleting the file itself — safe to call even if no credentials have been saved yet.
201
+
202
+ **Parameters**
203
+
204
+ None.
205
+
206
+ **Returns**
207
+
208
+ `void` — no return value. The auth config file is overwritten with `{}` if it exists; if no file is found, the function exits silently.
209
+
210
+ **Behavior**
211
+ - ✅ Auth file exists → contents replaced with `{}`
212
+ - ✅ Auth file does not exist → no-op, no error thrown
213
+
214
+ **Example:**
215
+
216
+ ```typescript example.ts
217
+ import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs'
218
+ import { join } from 'path'
219
+ import { homedir } from 'os'
220
+
221
+ // --- Inline config (mirrors autodocs internals) ---
222
+ const CONFIG_DIR = join(homedir(), '.config', 'autodocs-demo')
223
+ const AUTH_FILE = join(CONFIG_DIR, 'auth.json')
224
+
225
+ type AuthConfig = {
226
+ token?: string
227
+ userId?: string
228
+ expiresAt?: string
229
+ }
230
+
231
+ function saveAuthConfig(config: AuthConfig): void {
232
+ mkdirSync(CONFIG_DIR, { recursive: true })
233
+ writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2))
234
+ }
235
+
236
+ function clearAuth(): void {
237
+ if (existsSync(AUTH_FILE)) {
238
+ writeFileSync(AUTH_FILE, '{}')
239
+ }
240
+ }
241
+
242
+ // --- Example: save credentials, then clear them on logout ---
243
+ async function main() {
244
+ try {
245
+ // Simulate a user logging in
246
+ const credentials: AuthConfig = {
247
+ token: process.env.AUTH_TOKEN || 'tok_abc123xyz',
248
+ userId: 'user_9876',
249
+ expiresAt: new Date(Date.now() + 3600 * 1000).toISOString(),
250
+ }
251
+
252
+ saveAuthConfig(credentials)
253
+ console.log('Logged in. Auth file contents:')
254
+ console.log(readFileSync(AUTH_FILE, 'utf-8'))
255
+ // Output: { "token": "tok_abc123xyz", "userId": "user_9876", "expiresAt": "..." }
256
+
257
+ // Simulate the user logging out
258
+ clearAuth()
259
+ console.log('\nLogged out. Auth file contents after clearAuth():')
260
+ console.log(readFileSync(AUTH_FILE, 'utf-8'))
261
+ // Output: {}
262
+
263
+ // Calling clearAuth again when already cleared is safe (no-op if file missing)
264
+ clearAuth()
265
+ console.log('\nCalled clearAuth() again — no error thrown. ✅')
266
+ } catch (error) {
267
+ console.error('Unexpected error during auth lifecycle:', error)
268
+ }
269
+ }
270
+
271
+ main()
272
+ ```
273
+
274
+ ### `checkPlan`
275
+
276
+ ```typescript
277
+ async function checkPlan(apiKey: string): Promise<PlanCheckResponse>
278
+ ```
279
+
280
+ Use this to verify an API key's validity and retrieve the associated subscription plan details before making API calls.
281
+
282
+ `checkPlan` hits the `/v1/plan` endpoint with a Bearer token and returns plan metadata — useful for gating features, displaying plan info to users, or validating credentials at startup.
283
+
284
+ #### Parameters
285
+
286
+ | Name | Type | Required | Description |
287
+ |------|------|----------|-------------|
288
+ | `apiKey` | `string` | ✅ Yes | The Bearer token used to authenticate against the API |
289
+
290
+ #### Returns
291
+
292
+ Returns a `Promise<PlanCheckResponse>` that resolves with the plan details for the given API key.
293
+
294
+ | Scenario | Result |
295
+ |----------|--------|
296
+ | Valid API key | Resolves with plan metadata (e.g. plan name, limits, status) |
297
+ | Invalid / expired key | Rejects or resolves with an error response depending on server behavior |
298
+ | Network failure | Rejects with a fetch/network error |
299
+
300
+ ### `PlanCheckResponse` shape (typical)
301
+ ```typescript
302
+ type PlanCheckResponse = {
303
+ plan: string // e.g. "free", "pro", "enterprise"
304
+ valid: boolean // whether the key is active
305
+ limits?: {
306
+ requests: number
307
+ storage: number
308
+ }
309
+ error?: string // present if the key was rejected
310
+ }
311
+ ```
312
+
313
+ **Example:**
314
+
315
+ ```typescript example.ts
316
+ // Inline types — no external imports needed
317
+ type PlanCheckResponse = {
318
+ plan: string
319
+ valid: boolean
320
+ limits?: {
321
+ requests: number
322
+ storage: number
323
+ }
324
+ error?: string
325
+ }
326
+
327
+ // Self-contained implementation mirroring the real checkPlan behavior
328
+ async function checkPlan(apiKey: string): Promise<PlanCheckResponse> {
329
+ const API_BASE = 'https://api.autodocs.dev'
330
+
331
+ const response = await fetch(`${API_BASE}/v1/plan`, {
332
+ headers: {
333
+ 'Authorization': `Bearer ${apiKey}`,
334
+ 'Content-Type': 'application/json',
335
+ },
336
+ })
337
+
338
+ if (!response.ok) {
339
+ const errorBody = await response.json().catch(() => ({}))
340
+ return {
341
+ plan: 'unknown',
342
+ valid: false,
343
+ error: (errorBody as any).message || `HTTP ${response.status}: ${response.statusText}`,
344
+ }
345
+ }
346
+
347
+ return response.json() as Promise<PlanCheckResponse>
348
+ }
349
+
350
+ // --- Usage ---
351
+ async function main() {
352
+ const apiKey = process.env.AUTODOCS_API_KEY || 'sk-your-api-key-here'
353
+
354
+ try {
355
+ console.log('Checking plan for API key...')
356
+ const planInfo = await checkPlan(apiKey)
357
+
358
+ if (!planInfo.valid) {
359
+ console.error('API key is invalid or expired:', planInfo.error)
360
+ process.exit(1)
361
+ }
362
+
363
+ console.log('Plan details:', planInfo)
364
+ // Expected output:
365
+ // Plan details: { plan: 'pro', valid: true, limits: { requests: 10000, storage: 5000 } }
366
+
367
+ // Example: gate a feature based on plan
368
+ if (planInfo.plan === 'free') {
369
+ console.log('Upgrade to Pro to unlock advanced features.')
370
+ } else {
371
+ console.log(`Welcome, ${planInfo.plan} user! All features unlocked.`)
372
+ }
373
+ } catch (error) {
374
+ // Network errors, DNS failures, etc.
375
+ console.error('Failed to reach the plan endpoint:', error)
376
+ }
377
+ }
378
+
379
+ main()
380
+ ```
381
+
382
+ ### `requirePro`
383
+
384
+ ```typescript
385
+ async function requirePro(commandName: string): Promise<boolean>
386
+ ```
387
+
388
+ Use this to gate CLI commands behind a Pro subscription — it checks the user's auth config and prints a helpful upgrade message if they're not on a paid plan.
389
+
390
+ Call `requirePro` at the top of any command handler that requires a Pro account. It returns `true` if the user is authorized to proceed, or `false` (after printing an error) if they are not.
391
+
392
+ #### Parameters
393
+
394
+ | Name | Type | Required | Description |
395
+ |------|------|----------|-------------|
396
+ | `commandName` | `string` | ✅ | The name of the CLI command being gated. Used in the error message shown to the user (e.g. `"sync"`, `"generate"`). |
397
+
398
+ #### Returns
399
+
400
+ | Value | When |
401
+ |-------|------|
402
+ | `Promise<true>` | User has a valid Pro API key and is authorized |
403
+ | `Promise<false>` | User has no API key, is on the free plan, or the auth check fails — an error message is printed to `stderr` automatically |
404
+
405
+ ## Usage Pattern
406
+
407
+ ```typescript
408
+ // In your command handler:
409
+ if (!await requirePro('my-command')) return;
410
+ // ... rest of Pro-only logic
411
+ ```
412
+
413
+ **Example:**
414
+
415
+ ```typescript example.ts
416
+ import * as fs from 'fs'
417
+ import * as path from 'path'
418
+ import * as os from 'os'
419
+
420
+ // --- Inline types and helpers (self-contained, no external imports) ---
421
+
422
+ type AuthConfig = {
423
+ apiKey: string | null
424
+ }
425
+
426
+ type LicenseStatus = {
427
+ valid: boolean
428
+ plan: 'free' | 'pro'
429
+ email: string
430
+ error?: string
431
+ }
432
+
433
+ // Simulates reading auth config from ~/.skrypt/config.json
434
+ function getAuthConfig(): AuthConfig {
435
+ const configPath = path.join(os.homedir(), '.skrypt', 'config.json')
436
+ try {
437
+ if (fs.existsSync(configPath)) {
438
+ const raw = fs.readFileSync(configPath, 'utf-8')
439
+ const parsed = JSON.parse(raw)
440
+ return { apiKey: parsed.apiKey || null }
441
+ }
442
+ } catch {
443
+ // Config unreadable
444
+ }
445
+ return { apiKey: null }
446
+ }
447
+
448
+ // Simulates a license validation API call
449
+ async function validateLicense(apiKey: string): Promise<LicenseStatus> {
450
+ // In real usage this would hit an API endpoint
451
+ // Here we simulate: keys starting with "pro_" are valid Pro keys
452
+ if (apiKey.startsWith('pro_')) {
453
+ return { valid: true, plan: 'pro', email: 'user@example.com' }
454
+ }
455
+ return { valid: false, plan: 'free', email: '', error: 'Not a Pro key' }
456
+ }
457
+
458
+ // The function being documented
459
+ async function requirePro(commandName: string): Promise<boolean> {
460
+ const config = getAuthConfig()
461
+
462
+ if (!config.apiKey) {
463
+ console.error(`\n ⚡ ${commandName} requires Skrypt Pro\n`)
464
+ console.error(' Get started:')
465
+ console.error(' https://skrypt.dev/pro\n')
466
+ return false
467
+ }
468
+
469
+ let status: LicenseStatus
470
+ try {
471
+ status = await validateLicense(config.apiKey)
472
+ } catch {
473
+ status = { valid: false, plan: 'free', email: '', error: 'Failed to connect to API' }
474
+ }
475
+
476
+ if (!status.valid || status.plan !== 'pro') {
477
+ console.error(`\n ⚡ ${commandName} requires Skrypt Pro\n`)
478
+ console.error(` Current plan: ${status.plan}`)
479
+ console.error(' Upgrade at: https://skrypt.dev/pro\n')
480
+ return false
481
+ }
482
+
483
+ return true
484
+ }
485
+
486
+ // --- Demo ---
487
+
488
+ async function main() {
489
+ try {
490
+ // Scenario 1: No API key configured
491
+ console.log('--- Scenario 1: No API key ---')
492
+ const noKeyResult = await requirePro('generate')
493
+ console.log('Authorized:', noKeyResult)
494
+ // Output:
495
+ // ⚡ generate requires Skrypt Pro
496
+ // Get started:
497
+ // https://skrypt.dev/pro
498
+ // Authorized: false
499
+
500
+ // Scenario 2: Free-tier key
501
+ console.log('\n--- Scenario 2: Free-tier key ---')
502
+ // Temporarily mock getAuthConfig by overriding the config file path behavior
503
+ // (In a real test you'd inject the config; here we call validateLicense directly)
504
+ const freeStatus = await validateLicense('free_abc123')
505
+ console.log('License status:', freeStatus)
506
+ // Output: { valid: false, plan: 'free', email: '', error: 'Not a Pro key' }
507
+
508
+ // Scenario 3: Valid Pro key
509
+ console.log('\n--- Scenario 3: Valid Pro key ---')
510
+ const proStatus = await validateLicense(
511
+ process.env.SKRYPT_API_KEY || 'pro_sk_live_abc123xyz'
512
+ )
513
+ console.log('License status:', proStatus)
514
+ // Output: { valid: true, plan: 'pro', email: 'user@example.com' }
515
+
516
+ } catch (error) {
517
+ console.error('Unexpected error:', error)
518
+ }
519
+ }
520
+
521
+ main()
522
+ ```
523
+
524
+ ### `isProCommand`
525
+
526
+ ```typescript
527
+ function isProCommand(command: string): boolean
528
+ ```
529
+
530
+ Use this to check whether a given CLI command requires a Pro subscription before executing it, enabling you to gate features and show appropriate upgrade prompts.
531
+
532
+ #### Parameters
533
+
534
+ | Name | Type | Required | Description |
535
+ |------|------|----------|-------------|
536
+ | command | string | Yes | The CLI command name to check against the list of Pro-only commands |
537
+
538
+ #### Returns
539
+
540
+ - **`true`** — the command is Pro-only and requires an upgraded account
541
+ - **`false`** — the command is available on the free tier
542
+
543
+ **Example:**
544
+
545
+ ```typescript example.ts
546
+ // Define the Pro commands list inline (mirrors the source)
547
+ const PRO_COMMANDS = ['gh-action', 'ci', 'mcp']
548
+
549
+ // Inline implementation of isProCommand
550
+ function isProCommand(command: string): boolean {
551
+ return PRO_COMMANDS.includes(command)
552
+ }
553
+
554
+ // Simulate a user running a CLI command
555
+ function runCommand(command: string, isProUser: boolean) {
556
+ if (isProCommand(command)) {
557
+ if (!isProUser) {
558
+ console.log(`⛔ "${command}" is a Pro-only command. Upgrade at https://example.com/pro`)
559
+ return
560
+ }
561
+ console.log(`✅ Running Pro command: "${command}"`)
562
+ } else {
563
+ console.log(`✅ Running free command: "${command}"`)
564
+ }
565
+ }
566
+
567
+ const userCommand = process.env.CLI_COMMAND || 'ci'
568
+ const userIsPro = process.env.IS_PRO === 'true' // set IS_PRO=true to simulate a Pro user
569
+
570
+ // Check a few commands
571
+ const commands = ['gh-action', 'ci', 'mcp', 'generate', 'help']
572
+
573
+ console.log('--- isProCommand checks ---')
574
+ for (const cmd of commands) {
575
+ console.log(`isProCommand("${cmd}") =>`, isProCommand(cmd))
576
+ }
577
+ // Output:
578
+ // isProCommand("gh-action") => true
579
+ // isProCommand("ci") => true
580
+ // isProCommand("mcp") => true
581
+ // isProCommand("generate") => false
582
+ // isProCommand("help") => false
583
+
584
+ console.log('\n--- Gating example ---')
585
+ runCommand(userCommand, userIsPro)
586
+ // With CLI_COMMAND=ci and IS_PRO unset:
587
+ // ⛔ "ci" is a Pro-only command. Upgrade at https://example.com/pro
588
+ ```
589
+