skrypt-ai 0.4.2 → 0.6.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 (159) hide show
  1. package/dist/auth/index.d.ts +13 -3
  2. package/dist/auth/index.js +101 -9
  3. package/dist/auth/keychain.d.ts +5 -0
  4. package/dist/auth/keychain.js +82 -0
  5. package/dist/auth/notices.d.ts +3 -0
  6. package/dist/auth/notices.js +42 -0
  7. package/dist/autofix/index.d.ts +0 -4
  8. package/dist/autofix/index.js +10 -24
  9. package/dist/capture/browser.d.ts +11 -0
  10. package/dist/capture/browser.js +173 -0
  11. package/dist/capture/diff.d.ts +23 -0
  12. package/dist/capture/diff.js +52 -0
  13. package/dist/capture/index.d.ts +23 -0
  14. package/dist/capture/index.js +210 -0
  15. package/dist/capture/naming.d.ts +17 -0
  16. package/dist/capture/naming.js +45 -0
  17. package/dist/capture/parser.d.ts +15 -0
  18. package/dist/capture/parser.js +80 -0
  19. package/dist/capture/types.d.ts +57 -0
  20. package/dist/capture/types.js +1 -0
  21. package/dist/cli.js +20 -3
  22. package/dist/commands/autofix.js +136 -120
  23. package/dist/commands/cron.js +58 -47
  24. package/dist/commands/deploy.js +123 -102
  25. package/dist/commands/generate.js +125 -7
  26. package/dist/commands/heal.d.ts +10 -0
  27. package/dist/commands/heal.js +201 -0
  28. package/dist/commands/i18n.js +146 -111
  29. package/dist/commands/import.d.ts +2 -0
  30. package/dist/commands/import.js +157 -0
  31. package/dist/commands/init.js +19 -7
  32. package/dist/commands/lint.js +50 -44
  33. package/dist/commands/llms-txt.js +59 -49
  34. package/dist/commands/login.js +63 -34
  35. package/dist/commands/mcp.js +6 -0
  36. package/dist/commands/monitor.js +13 -8
  37. package/dist/commands/qa.d.ts +2 -0
  38. package/dist/commands/qa.js +43 -0
  39. package/dist/commands/review-pr.js +108 -92
  40. package/dist/commands/sdk.js +128 -122
  41. package/dist/commands/security.d.ts +2 -0
  42. package/dist/commands/security.js +109 -0
  43. package/dist/commands/test.js +91 -92
  44. package/dist/commands/version.js +104 -75
  45. package/dist/commands/watch.js +130 -114
  46. package/dist/config/types.js +2 -2
  47. package/dist/context-hub/index.d.ts +23 -0
  48. package/dist/context-hub/index.js +179 -0
  49. package/dist/context-hub/mappings.d.ts +8 -0
  50. package/dist/context-hub/mappings.js +55 -0
  51. package/dist/context-hub/types.d.ts +33 -0
  52. package/dist/context-hub/types.js +1 -0
  53. package/dist/generator/generator.js +39 -6
  54. package/dist/generator/types.d.ts +7 -0
  55. package/dist/generator/writer.d.ts +3 -1
  56. package/dist/generator/writer.js +36 -7
  57. package/dist/importers/confluence.d.ts +5 -0
  58. package/dist/importers/confluence.js +137 -0
  59. package/dist/importers/detect.d.ts +20 -0
  60. package/dist/importers/detect.js +121 -0
  61. package/dist/importers/docusaurus.d.ts +5 -0
  62. package/dist/importers/docusaurus.js +279 -0
  63. package/dist/importers/gitbook.d.ts +5 -0
  64. package/dist/importers/gitbook.js +189 -0
  65. package/dist/importers/github.d.ts +8 -0
  66. package/dist/importers/github.js +99 -0
  67. package/dist/importers/index.d.ts +15 -0
  68. package/dist/importers/index.js +30 -0
  69. package/dist/importers/markdown.d.ts +6 -0
  70. package/dist/importers/markdown.js +105 -0
  71. package/dist/importers/mintlify.d.ts +5 -0
  72. package/dist/importers/mintlify.js +172 -0
  73. package/dist/importers/notion.d.ts +5 -0
  74. package/dist/importers/notion.js +174 -0
  75. package/dist/importers/readme.d.ts +5 -0
  76. package/dist/importers/readme.js +184 -0
  77. package/dist/importers/transform.d.ts +90 -0
  78. package/dist/importers/transform.js +457 -0
  79. package/dist/importers/types.d.ts +37 -0
  80. package/dist/importers/types.js +1 -0
  81. package/dist/llm/anthropic-client.d.ts +1 -0
  82. package/dist/llm/anthropic-client.js +3 -1
  83. package/dist/llm/index.d.ts +6 -4
  84. package/dist/llm/index.js +76 -261
  85. package/dist/llm/openai-client.d.ts +1 -0
  86. package/dist/llm/openai-client.js +7 -2
  87. package/dist/plugins/index.js +7 -0
  88. package/dist/qa/checks.d.ts +10 -0
  89. package/dist/qa/checks.js +492 -0
  90. package/dist/qa/fixes.d.ts +30 -0
  91. package/dist/qa/fixes.js +277 -0
  92. package/dist/qa/index.d.ts +29 -0
  93. package/dist/qa/index.js +187 -0
  94. package/dist/qa/types.d.ts +24 -0
  95. package/dist/qa/types.js +1 -0
  96. package/dist/scanner/csharp.d.ts +23 -0
  97. package/dist/scanner/csharp.js +421 -0
  98. package/dist/scanner/index.js +53 -26
  99. package/dist/scanner/java.d.ts +39 -0
  100. package/dist/scanner/java.js +318 -0
  101. package/dist/scanner/kotlin.d.ts +23 -0
  102. package/dist/scanner/kotlin.js +389 -0
  103. package/dist/scanner/php.d.ts +57 -0
  104. package/dist/scanner/php.js +351 -0
  105. package/dist/scanner/python.js +17 -0
  106. package/dist/scanner/ruby.d.ts +36 -0
  107. package/dist/scanner/ruby.js +431 -0
  108. package/dist/scanner/swift.d.ts +25 -0
  109. package/dist/scanner/swift.js +392 -0
  110. package/dist/scanner/types.d.ts +1 -1
  111. package/dist/template/content/docs/_navigation.json +46 -0
  112. package/dist/template/content/docs/_sidebars.json +684 -0
  113. package/dist/template/content/docs/core.md +4544 -0
  114. package/dist/template/content/docs/index.mdx +89 -0
  115. package/dist/template/content/docs/integrations.md +1158 -0
  116. package/dist/template/content/docs/llms-full.md +403 -0
  117. package/dist/template/content/docs/llms.txt +4588 -0
  118. package/dist/template/content/docs/other.md +10379 -0
  119. package/dist/template/content/docs/tools.md +746 -0
  120. package/dist/template/content/docs/types.md +531 -0
  121. package/dist/template/docs.json +13 -11
  122. package/dist/template/mdx-components.tsx +27 -2
  123. package/dist/template/package.json +6 -0
  124. package/dist/template/public/search-index.json +1 -1
  125. package/dist/template/scripts/build-search-index.mjs +149 -13
  126. package/dist/template/src/app/api/chat/route.ts +83 -128
  127. package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
  128. package/dist/template/src/app/docs/llms-full.md +151 -4
  129. package/dist/template/src/app/docs/llms.txt +2464 -847
  130. package/dist/template/src/app/docs/page.mdx +48 -38
  131. package/dist/template/src/app/layout.tsx +3 -1
  132. package/dist/template/src/app/page.tsx +22 -8
  133. package/dist/template/src/components/ai-chat.tsx +73 -64
  134. package/dist/template/src/components/breadcrumbs.tsx +21 -23
  135. package/dist/template/src/components/copy-button.tsx +13 -9
  136. package/dist/template/src/components/copy-page-button.tsx +54 -0
  137. package/dist/template/src/components/docs-layout.tsx +37 -25
  138. package/dist/template/src/components/header.tsx +51 -10
  139. package/dist/template/src/components/mdx/card.tsx +17 -3
  140. package/dist/template/src/components/mdx/code-block.tsx +13 -9
  141. package/dist/template/src/components/mdx/code-group.tsx +13 -8
  142. package/dist/template/src/components/mdx/heading.tsx +15 -2
  143. package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
  144. package/dist/template/src/components/mdx/index.tsx +2 -0
  145. package/dist/template/src/components/mdx/mermaid.tsx +110 -0
  146. package/dist/template/src/components/mdx/screenshot.tsx +150 -0
  147. package/dist/template/src/components/scroll-to-hash.tsx +48 -0
  148. package/dist/template/src/components/sidebar.tsx +12 -18
  149. package/dist/template/src/components/table-of-contents.tsx +9 -0
  150. package/dist/template/src/lib/highlight.ts +3 -88
  151. package/dist/template/src/lib/navigation.ts +159 -0
  152. package/dist/template/src/lib/search-types.ts +4 -1
  153. package/dist/template/src/lib/search.ts +30 -7
  154. package/dist/template/src/styles/globals.css +17 -6
  155. package/dist/utils/files.d.ts +9 -1
  156. package/dist/utils/files.js +59 -10
  157. package/dist/utils/validation.d.ts +0 -3
  158. package/dist/utils/validation.js +0 -26
  159. package/package.json +5 -1
@@ -1,10 +1,11 @@
1
1
  import { Command } from 'commander';
2
2
  import { readFileSync, writeFileSync } from 'fs';
3
- import { resolve } from 'path';
3
+ import { resolve, dirname } from 'path';
4
4
  import { loadConfig, checkApiKey } from '../config/index.js';
5
5
  import { createLLMClient } from '../llm/index.js';
6
6
  import { autoFixExample, createTypeScriptValidator, createPythonValidator } from '../autofix/index.js';
7
7
  import { requirePro } from '../auth/index.js';
8
+ import { runQA, printQAReport, fixQAIssues, printFixReport } from '../qa/index.js';
8
9
  export const autofixCommand = new Command('autofix')
9
10
  .description('Auto-fix broken code examples in documentation')
10
11
  .argument('<file>', 'Documentation file to fix (markdown/mdx)')
@@ -15,134 +16,149 @@ export const autofixCommand = new Command('autofix')
15
16
  .option('--dry-run', 'Show fixes without writing')
16
17
  .option('--language <lang>', 'Force language for validation')
17
18
  .action(async (file, options) => {
18
- // Pro feature - requires subscription
19
- if (!await requirePro('autofix')) {
20
- process.exit(1);
21
- }
22
- const config = loadConfig(options.config);
23
- if (options.provider)
24
- config.llm.provider = options.provider;
25
- if (options.model)
26
- config.llm.model = options.model;
27
- const { ok, envKey } = checkApiKey(config.llm.provider);
28
- if (!ok && envKey) {
29
- console.error(`Error: ${envKey} environment variable required`);
30
- process.exit(1);
31
- }
32
- const filePath = resolve(file);
33
- console.log(`skrypt autofix`);
34
- console.log(` file: ${filePath}`);
35
- console.log(` provider: ${config.llm.provider}`);
36
- console.log(` max iterations: ${options.maxIterations}`);
37
- console.log('');
38
- // Read the file
39
- let content;
40
19
  try {
41
- content = readFileSync(filePath, 'utf-8');
42
- }
43
- catch (err) {
44
- const errObj = err;
45
- if (errObj.code === 'ENOENT') {
46
- console.error(`Error: File not found: ${filePath}`);
20
+ // Pro feature - requires subscription
21
+ if (!await requirePro('autofix')) {
22
+ process.exit(1);
47
23
  }
48
- else if (errObj.code === 'EACCES') {
49
- console.error(`Error: Permission denied: ${filePath}`);
24
+ const config = loadConfig(options.config);
25
+ if (options.provider)
26
+ config.llm.provider = options.provider;
27
+ if (options.model)
28
+ config.llm.model = options.model;
29
+ const { ok, envKey } = checkApiKey(config.llm.provider);
30
+ if (!ok && envKey) {
31
+ console.error(`Error: ${envKey} environment variable required`);
32
+ process.exit(1);
50
33
  }
51
- else {
52
- const message = err instanceof Error ? err.message : String(err);
53
- console.error(`Error: Could not read file: ${filePath} (${message})`);
34
+ const filePath = resolve(file);
35
+ console.log(`skrypt autofix`);
36
+ console.log(` file: ${filePath}`);
37
+ console.log(` provider: ${config.llm.provider}`);
38
+ console.log(` max iterations: ${options.maxIterations}`);
39
+ console.log('');
40
+ // Read the file
41
+ let content;
42
+ try {
43
+ content = readFileSync(filePath, 'utf-8');
54
44
  }
55
- process.exit(1);
56
- }
57
- // Find all code blocks
58
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
59
- const codeBlocks = [];
60
- let match;
61
- while ((match = codeBlockRegex.exec(content)) !== null) {
62
- const code = match[2];
63
- if (!code)
64
- continue;
65
- codeBlocks.push({
66
- match: match[0],
67
- language: options.language || match[1] || 'javascript',
68
- code,
69
- index: match.index,
70
- });
71
- }
72
- if (codeBlocks.length === 0) {
73
- console.log('No code blocks found in file.');
74
- process.exit(0);
75
- }
76
- console.log(`Found ${codeBlocks.length} code block(s)`);
77
- console.log('');
78
- const client = createLLMClient({
79
- provider: config.llm.provider,
80
- model: config.llm.model,
81
- baseUrl: config.llm.baseUrl,
82
- });
83
- const maxIterations = parseInt(options.maxIterations);
84
- // Create validators
85
- const validators = {
86
- javascript: createTypeScriptValidator(),
87
- typescript: createTypeScriptValidator(),
88
- ts: createTypeScriptValidator(),
89
- js: createTypeScriptValidator(),
90
- python: createPythonValidator(),
91
- py: createPythonValidator(),
92
- };
93
- let fixedCount = 0;
94
- let failedCount = 0;
95
- let newContent = content;
96
- for (let i = 0; i < codeBlocks.length; i++) {
97
- const block = codeBlocks[i];
98
- if (!block)
99
- continue;
100
- const validator = validators[block.language.toLowerCase()];
101
- if (!validator) {
102
- console.log(`[${i + 1}/${codeBlocks.length}] Skipping ${block.language} (no validator)`);
103
- continue;
45
+ catch (err) {
46
+ const errObj = err;
47
+ if (errObj.code === 'ENOENT') {
48
+ console.error(`Error: File not found: ${filePath}`);
49
+ }
50
+ else if (errObj.code === 'EACCES') {
51
+ console.error(`Error: Permission denied: ${filePath}`);
52
+ }
53
+ else {
54
+ const message = err instanceof Error ? err.message : String(err);
55
+ console.error(`Error: Could not read file: ${filePath} (${message})`);
56
+ }
57
+ process.exit(1);
104
58
  }
105
- console.log(`[${i + 1}/${codeBlocks.length}] Checking ${block.language} example...`);
106
- // First check if it's valid
107
- const initial = await validator(block.code);
108
- if (initial.valid) {
109
- console.log(` ✓ Already valid`);
110
- continue;
59
+ // Find all code blocks
60
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
61
+ const codeBlocks = [];
62
+ let match;
63
+ while ((match = codeBlockRegex.exec(content)) !== null) {
64
+ const code = match[2];
65
+ if (!code)
66
+ continue;
67
+ codeBlocks.push({
68
+ match: match[0],
69
+ language: options.language || match[1] || 'javascript',
70
+ code,
71
+ index: match.index,
72
+ });
111
73
  }
112
- console.log(` ✗ Invalid: ${initial.errors[0]?.slice(0, 60)}...`);
113
- console.log(` → Attempting to fix...`);
114
- const result = await autoFixExample({
115
- code: block.code,
116
- language: block.language,
117
- context: `Code example from documentation file: ${file}`,
118
- }, client, {
119
- maxIterations,
120
- validateFn: validator,
121
- language: block.language,
74
+ if (codeBlocks.length === 0) {
75
+ console.log('No code blocks found in file.');
76
+ process.exit(0);
77
+ }
78
+ console.log(`Found ${codeBlocks.length} code block(s)`);
79
+ console.log('');
80
+ const client = createLLMClient({
81
+ provider: config.llm.provider,
82
+ model: config.llm.model,
83
+ baseUrl: config.llm.baseUrl,
122
84
  });
123
- if (result.success) {
124
- console.log(` ✓ Fixed in ${result.iterations} iteration(s)`);
125
- fixedCount++;
126
- // Replace in content
127
- const newBlock = '```' + block.language + '\n' + result.fixedCode + '```';
128
- newContent = newContent.replace(block.match, newBlock);
85
+ const maxIterations = parseInt(options.maxIterations, 10);
86
+ if (isNaN(maxIterations) || maxIterations < 1) {
87
+ console.error('Error: --max-iterations must be a positive number');
88
+ process.exit(1);
129
89
  }
130
- else {
131
- console.log(` ✗ Could not fix after ${result.iterations} iterations`);
132
- failedCount++;
90
+ // Create validators
91
+ const validators = {
92
+ javascript: createTypeScriptValidator(),
93
+ typescript: createTypeScriptValidator(),
94
+ ts: createTypeScriptValidator(),
95
+ js: createTypeScriptValidator(),
96
+ python: createPythonValidator(),
97
+ py: createPythonValidator(),
98
+ };
99
+ let fixedCount = 0;
100
+ let failedCount = 0;
101
+ let newContent = content;
102
+ for (let i = 0; i < codeBlocks.length; i++) {
103
+ const block = codeBlocks[i];
104
+ if (!block)
105
+ continue;
106
+ const validator = validators[block.language.toLowerCase()];
107
+ if (!validator) {
108
+ console.log(`[${i + 1}/${codeBlocks.length}] Skipping ${block.language} (no validator)`);
109
+ continue;
110
+ }
111
+ console.log(`[${i + 1}/${codeBlocks.length}] Checking ${block.language} example...`);
112
+ // First check if it's valid
113
+ const initial = await validator(block.code);
114
+ if (initial.valid) {
115
+ console.log(` ✓ Already valid`);
116
+ continue;
117
+ }
118
+ console.log(` ✗ Invalid: ${initial.errors[0]?.slice(0, 60)}...`);
119
+ console.log(` → Attempting to fix...`);
120
+ const result = await autoFixExample({
121
+ code: block.code,
122
+ language: block.language,
123
+ context: `Code example from documentation file: ${file}`,
124
+ }, client, {
125
+ maxIterations,
126
+ validateFn: validator,
127
+ language: block.language,
128
+ });
129
+ if (result.success) {
130
+ console.log(` ✓ Fixed in ${result.iterations} iteration(s)`);
131
+ fixedCount++;
132
+ // Replace in content
133
+ const newBlock = '```' + block.language + '\n' + result.fixedCode + '```';
134
+ newContent = newContent.replace(block.match, newBlock);
135
+ }
136
+ else {
137
+ console.log(` ✗ Could not fix after ${result.iterations} iterations`);
138
+ failedCount++;
139
+ }
133
140
  }
141
+ console.log('');
142
+ console.log('=== Summary ===');
143
+ console.log(` Checked: ${codeBlocks.length}`);
144
+ console.log(` Fixed: ${fixedCount}`);
145
+ console.log(` Failed: ${failedCount}`);
146
+ if (fixedCount > 0 && !options.dryRun) {
147
+ writeFileSync(filePath, newContent);
148
+ console.log(`\nWrote fixes to ${filePath}`);
149
+ // Auto-fix then run QA on the fixed file's directory
150
+ const fixReport = fixQAIssues(dirname(filePath));
151
+ printFixReport(fixReport);
152
+ const qaReport = runQA(dirname(filePath));
153
+ printQAReport(qaReport);
154
+ }
155
+ else if (fixedCount > 0 && options.dryRun) {
156
+ console.log(`\n[dry run - no changes written]`);
157
+ }
158
+ console.log('\nDone!');
134
159
  }
135
- console.log('');
136
- console.log('=== Summary ===');
137
- console.log(` Checked: ${codeBlocks.length}`);
138
- console.log(` Fixed: ${fixedCount}`);
139
- console.log(` Failed: ${failedCount}`);
140
- if (fixedCount > 0 && !options.dryRun) {
141
- writeFileSync(filePath, newContent);
142
- console.log(`\nWrote fixes to ${filePath}`);
143
- }
144
- else if (fixedCount > 0 && options.dryRun) {
145
- console.log(`\n[dry run - no changes written]`);
160
+ catch (err) {
161
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
162
+ process.exit(1);
146
163
  }
147
- console.log('\nDone!');
148
164
  });
@@ -54,53 +54,64 @@ export const cronCommand = new Command('cron')
54
54
  .option('-s, --schedule <cron>', 'Cron schedule (default: daily at 2am UTC)', '0 2 * * *')
55
55
  .option('-f, --force', 'Overwrite existing workflow')
56
56
  .action(async (repoPath, options) => {
57
- const resolvedPath = resolve(repoPath);
58
- const workflowDir = join(resolvedPath, '.github', 'workflows');
59
- const workflowPath = join(workflowDir, 'skrypt-docs.yml');
60
- console.log('skrypt cron');
61
- console.log(` repo: ${resolvedPath}`);
62
- console.log(` schedule: ${options.schedule}`);
63
- console.log('');
64
- // Check if workflow exists
65
- if (existsSync(workflowPath) && !options.force) {
66
- console.log('Workflow already exists. Use --force to overwrite.');
67
- console.log(` ${workflowPath}`);
68
- return;
57
+ try {
58
+ const resolvedPath = resolve(repoPath);
59
+ const workflowDir = join(resolvedPath, '.github', 'workflows');
60
+ const workflowPath = join(workflowDir, 'skrypt-docs.yml');
61
+ console.log('skrypt cron');
62
+ console.log(` repo: ${resolvedPath}`);
63
+ console.log(` schedule: ${options.schedule}`);
64
+ console.log('');
65
+ // Validate cron schedule format
66
+ if (options.schedule && !/^[\d\s*,\-/]+$/.test(options.schedule)) {
67
+ console.error('Error: Invalid cron schedule format');
68
+ process.exit(1);
69
+ }
70
+ // Check if workflow exists
71
+ if (existsSync(workflowPath) && !options.force) {
72
+ console.log('Workflow already exists. Use --force to overwrite.');
73
+ console.log(` ${workflowPath}`);
74
+ return;
75
+ }
76
+ // Create workflow directory
77
+ mkdirSync(workflowDir, { recursive: true });
78
+ // Customize schedule if provided
79
+ let workflow = CRON_WORKFLOW;
80
+ if (options.schedule && options.schedule !== '0 2 * * *') {
81
+ workflow = workflow.replace("cron: '0 2 * * *'", `cron: '${options.schedule}'`);
82
+ }
83
+ // Write workflow
84
+ writeFileSync(workflowPath, workflow);
85
+ console.log(`✓ Created: ${workflowPath}`);
86
+ console.log('');
87
+ console.log('=== Setup Instructions ===');
88
+ console.log('');
89
+ console.log('1. Add secrets to your GitHub repository:');
90
+ console.log(' Settings > Secrets and variables > Actions');
91
+ console.log('');
92
+ console.log(' Required:');
93
+ console.log(' - SKRYPT_API_KEY: Get from https://skrypt.sh/dashboard/settings');
94
+ console.log('');
95
+ console.log(' For AI generation (one of):');
96
+ console.log(' - OPENAI_API_KEY');
97
+ console.log(' - ANTHROPIC_API_KEY');
98
+ console.log('');
99
+ console.log('2. Commit and push:');
100
+ console.log(' git add .github/');
101
+ console.log(' git commit -m "Add Skrypt auto-update workflow"');
102
+ console.log(' git push');
103
+ console.log('');
104
+ console.log('3. Trigger manually (optional):');
105
+ console.log(' Go to Actions > Skrypt Auto-Update Docs > Run workflow');
106
+ console.log('');
107
+ console.log('Common schedules:');
108
+ console.log(' "0 2 * * *" - Daily at 2am UTC');
109
+ console.log(' "0 2 * * 1" - Weekly on Monday');
110
+ console.log(' "0 2 1 * *" - Monthly on the 1st');
111
+ console.log(' "0 */6 * * *" - Every 6 hours');
69
112
  }
70
- // Create workflow directory
71
- mkdirSync(workflowDir, { recursive: true });
72
- // Customize schedule if provided
73
- let workflow = CRON_WORKFLOW;
74
- if (options.schedule && options.schedule !== '0 2 * * *') {
75
- workflow = workflow.replace("cron: '0 2 * * *'", `cron: '${options.schedule}'`);
113
+ catch (err) {
114
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
115
+ process.exit(1);
76
116
  }
77
- // Write workflow
78
- writeFileSync(workflowPath, workflow);
79
- console.log(`✓ Created: ${workflowPath}`);
80
- console.log('');
81
- console.log('=== Setup Instructions ===');
82
- console.log('');
83
- console.log('1. Add secrets to your GitHub repository:');
84
- console.log(' Settings > Secrets and variables > Actions');
85
- console.log('');
86
- console.log(' Required:');
87
- console.log(' - SKRYPT_API_KEY: Get from https://skrypt.sh/dashboard/settings');
88
- console.log('');
89
- console.log(' For AI generation (one of):');
90
- console.log(' - OPENAI_API_KEY');
91
- console.log(' - ANTHROPIC_API_KEY');
92
- console.log('');
93
- console.log('2. Commit and push:');
94
- console.log(' git add .github/');
95
- console.log(' git commit -m "Add Skrypt auto-update workflow"');
96
- console.log(' git push');
97
- console.log('');
98
- console.log('3. Trigger manually (optional):');
99
- console.log(' Go to Actions > Skrypt Auto-Update Docs > Run workflow');
100
- console.log('');
101
- console.log('Common schedules:');
102
- console.log(' "0 2 * * *" - Daily at 2am UTC');
103
- console.log(' "0 2 * * 1" - Weekly on Monday');
104
- console.log(' "0 2 1 * *" - Monthly on the 1st');
105
- console.log(' "0 */6 * * *" - Every 6 hours');
106
117
  });
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { spawnSync, spawn } from 'child_process';
3
- import { existsSync, readFileSync, statSync } from 'fs';
3
+ import { existsSync, readFileSync, statSync, unlinkSync } from 'fs';
4
4
  import { resolve, join } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { createHash } from 'crypto';
@@ -81,8 +81,7 @@ async function buildSite(docsPath) {
81
81
  return new Promise((resolve, reject) => {
82
82
  const child = spawn('npm', ['run', 'build'], {
83
83
  cwd: docsPath,
84
- stdio: ['inherit', 'pipe', 'pipe'],
85
- shell: true
84
+ stdio: ['inherit', 'pipe', 'pipe']
86
85
  });
87
86
  child.stdout?.on('data', (data) => {
88
87
  process.stdout.write(` ${data.toString().trim()}\n`);
@@ -208,113 +207,135 @@ export const deployCommand = new Command('deploy')
208
207
  .option('--project <slug>', 'Project slug (e.g., my-docs)')
209
208
  .option('--token <key>', 'API token (or set SKRYPT_API_KEY env var)')
210
209
  .action(async (directory, options) => {
211
- const startTime = Date.now();
212
- const docsPath = resolve(directory);
213
- console.log('skrypt deploy');
214
- console.log(` directory: ${docsPath}`);
215
- console.log('');
216
- // Validate directory
217
- if (!existsSync(docsPath)) {
218
- console.error(`Error: Directory not found: ${docsPath}`);
219
- process.exit(1);
220
- }
221
- // Check for package.json (Next.js project)
222
- const packageJsonPath = join(docsPath, 'package.json');
223
- if (!existsSync(packageJsonPath)) {
224
- console.error('Error: No package.json found. Is this a Skrypt docs site?');
225
- console.error(' Run: skrypt init <directory>');
226
- process.exit(1);
227
- }
228
- // Get API token
229
- const apiToken = getApiToken(options);
230
- if (!apiToken) {
231
- console.error('Error: No API token found.');
232
- console.error('');
233
- console.error(' Provide a token using one of:');
234
- console.error(' --token <key>');
235
- console.error(' SKRYPT_API_KEY environment variable');
236
- console.error(' skrypt login');
237
- console.error('');
238
- process.exit(1);
239
- }
240
- // Get project slug
241
- const projectSlug = getProjectSlug(options, docsPath);
242
- if (!projectSlug) {
243
- console.error('Error: No project slug found.');
244
- console.error('');
245
- console.error(' Provide a project slug using one of:');
246
- console.error(' --project <slug>');
247
- console.error(' "slug" field in docs.json');
248
- console.error('');
249
- process.exit(1);
250
- }
251
- console.log(` project: ${projectSlug}`);
252
- console.log('');
253
- // Step 1: Build the site
254
- console.log('Step 1: Building documentation site...');
255
- let outDir;
210
+ let bundlePath;
256
211
  try {
257
- outDir = await buildSite(docsPath);
258
- }
259
- catch (err) {
260
- console.error('');
261
- console.error(`Error: Build failed`);
262
- if (err instanceof Error) {
263
- console.error(` ${err.message}`);
212
+ const startTime = Date.now();
213
+ const docsPath = resolve(directory);
214
+ console.log('skrypt deploy');
215
+ console.log(` directory: ${docsPath}`);
216
+ console.log('');
217
+ // Validate directory
218
+ if (!existsSync(docsPath)) {
219
+ console.error(`Error: Directory not found: ${docsPath}`);
220
+ process.exit(1);
264
221
  }
265
- process.exit(1);
266
- }
267
- // Check build output exists
268
- if (!existsSync(outDir)) {
269
- // Try .next/static for non-static export
270
- const nextDir = join(docsPath, '.next');
271
- if (existsSync(nextDir)) {
222
+ // Check for package.json (Next.js project)
223
+ const packageJsonPath = join(docsPath, 'package.json');
224
+ if (!existsSync(packageJsonPath)) {
225
+ console.error('Error: No package.json found. Is this a Skrypt docs site?');
226
+ console.error(' Run: skrypt init <directory>');
227
+ process.exit(1);
228
+ }
229
+ // Get API token
230
+ const apiToken = getApiToken(options);
231
+ if (!apiToken) {
232
+ console.error('Error: No API token found.');
233
+ console.error('');
234
+ console.error(' Provide a token using one of:');
235
+ console.error(' --token <key>');
236
+ console.error(' SKRYPT_API_KEY environment variable');
237
+ console.error(' skrypt login');
272
238
  console.error('');
273
- console.error('Error: Static export not found.');
274
- console.error(' Add to next.config.mjs: output: "export"');
275
239
  process.exit(1);
276
240
  }
277
- console.error('');
278
- console.error('Error: Build output not found.');
279
- process.exit(1);
280
- }
281
- console.log(' Build completed successfully');
282
- console.log('');
283
- // Step 2: Bundle the output
284
- console.log('Step 2: Creating deployment bundle...');
285
- let bundlePath;
286
- try {
287
- bundlePath = await bundleOutput(outDir);
241
+ // Get project slug
242
+ const projectSlug = getProjectSlug(options, docsPath);
243
+ if (!projectSlug) {
244
+ console.error('Error: No project slug found.');
245
+ console.error('');
246
+ console.error(' Provide a project slug using one of:');
247
+ console.error(' --project <slug>');
248
+ console.error(' "slug" field in docs.json');
249
+ console.error('');
250
+ process.exit(1);
251
+ }
252
+ console.log(` project: ${projectSlug}`);
253
+ console.log('');
254
+ // Step 1: Build the site
255
+ console.log('Step 1: Building documentation site...');
256
+ let outDir;
257
+ try {
258
+ outDir = await buildSite(docsPath);
259
+ }
260
+ catch (err) {
261
+ console.error('');
262
+ console.error(`Error: Build failed`);
263
+ if (err instanceof Error) {
264
+ console.error(` ${err.message}`);
265
+ }
266
+ process.exit(1);
267
+ }
268
+ // Check build output exists
269
+ if (!existsSync(outDir)) {
270
+ // Try .next/static for non-static export
271
+ const nextDir = join(docsPath, '.next');
272
+ if (existsSync(nextDir)) {
273
+ console.error('');
274
+ console.error('Error: Static export not found.');
275
+ console.error(' Add to next.config.mjs: output: "export"');
276
+ process.exit(1);
277
+ }
278
+ console.error('');
279
+ console.error('Error: Build output not found.');
280
+ process.exit(1);
281
+ }
282
+ console.log(' Build completed successfully');
283
+ console.log('');
284
+ // Step 2: Bundle the output
285
+ console.log('Step 2: Creating deployment bundle...');
286
+ try {
287
+ bundlePath = await bundleOutput(outDir);
288
+ }
289
+ catch (err) {
290
+ console.error('');
291
+ console.error('Error: Failed to create bundle');
292
+ if (err instanceof Error) {
293
+ console.error(` ${err.message}`);
294
+ }
295
+ process.exit(1);
296
+ }
297
+ console.log('');
298
+ // Step 3: Upload
299
+ console.log('Step 3: Deploying to Skrypt...');
300
+ const result = await uploadBundle(bundlePath, projectSlug, apiToken);
301
+ if (!result.success) {
302
+ console.error('');
303
+ console.error(`Error: Deployment failed`);
304
+ console.error(` ${result.error}`);
305
+ // Clean up temp bundle
306
+ try {
307
+ unlinkSync(bundlePath);
308
+ }
309
+ catch { /* ignore cleanup errors */ }
310
+ process.exit(1);
311
+ }
312
+ // Clean up temp bundle
313
+ try {
314
+ unlinkSync(bundlePath);
315
+ }
316
+ catch { /* ignore cleanup errors */ }
317
+ // Success
318
+ const duration = Math.round((Date.now() - startTime) / 1000);
319
+ console.log('');
320
+ console.log('=== Deployment Successful ===');
321
+ console.log('');
322
+ console.log(` URL: ${result.url}`);
323
+ if (result.customDomain) {
324
+ console.log(` Custom domain: ${result.customDomain}`);
325
+ }
326
+ console.log(` Duration: ${duration}s`);
327
+ console.log('');
328
+ console.log('Your documentation is now live!');
329
+ console.log('');
288
330
  }
289
331
  catch (err) {
290
- console.error('');
291
- console.error('Error: Failed to create bundle');
292
- if (err instanceof Error) {
293
- console.error(` ${err.message}`);
332
+ if (bundlePath) {
333
+ try {
334
+ unlinkSync(bundlePath);
335
+ }
336
+ catch { /* ignore cleanup errors */ }
294
337
  }
338
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
295
339
  process.exit(1);
296
340
  }
297
- console.log('');
298
- // Step 3: Upload
299
- console.log('Step 3: Deploying to Skrypt...');
300
- const result = await uploadBundle(bundlePath, projectSlug, apiToken);
301
- if (!result.success) {
302
- console.error('');
303
- console.error(`Error: Deployment failed`);
304
- console.error(` ${result.error}`);
305
- process.exit(1);
306
- }
307
- // Success
308
- const duration = Math.round((Date.now() - startTime) / 1000);
309
- console.log('');
310
- console.log('=== Deployment Successful ===');
311
- console.log('');
312
- console.log(` URL: ${result.url}`);
313
- if (result.customDomain) {
314
- console.log(` Custom domain: ${result.customDomain}`);
315
- }
316
- console.log(` Duration: ${duration}s`);
317
- console.log('');
318
- console.log('Your documentation is now live!');
319
- console.log('');
320
341
  });