shadowdocs 2.1.5 → 2.1.7

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.
@@ -5,7 +5,7 @@ import { DocResult } from '../core/types.js';
5
5
  const GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions';
6
6
  const MODEL = 'llama-3.3-70b-versatile';
7
7
 
8
- const SYSTEM_PROMPT = `You are a senior open-source maintainer and technical documentation expert. Generate detailed, professional READMEs with proper sections, realistic content (no fake features), MIT license, contribution guidelines, and security policy.`;
8
+ const SYSTEM_PROMPT = `You are a senior open-source maintainer and technical documentation expert with 15+ years of experience. You generate comprehensive, production-grade documentation that is practical, accurate, and developer-friendly. NEVER hallucinate features - only document what exists in the codebase.`;
9
9
 
10
10
  const MAX_RETRIES = 3;
11
11
  const BASE_DELAY = 1000;
@@ -61,6 +61,14 @@ async function withRetry<T>(
61
61
  throw lastError || new Error('Request failed after retries');
62
62
  }
63
63
 
64
+ export interface GeneratedDocs {
65
+ content?: string;
66
+ readme: string;
67
+ contributing: string;
68
+ license: string;
69
+ security: string;
70
+ }
71
+
64
72
  export async function generateDocs(projectData: Record<string, unknown>, mode: 'generate' | 'improve' | 'explain' = 'generate'): Promise<DocResult> {
65
73
  const apiKey = await loadApiKey();
66
74
 
@@ -68,124 +76,221 @@ export async function generateDocs(projectData: Record<string, unknown>, mode: '
68
76
  throw new Error('API key not configured. Run shadowdocs setup.');
69
77
  }
70
78
 
71
- let userPrompt = '';
72
-
73
- switch (mode) {
74
- case 'generate':
75
- userPrompt = buildGeneratePrompt(projectData);
76
- break;
77
- case 'improve':
78
- userPrompt = buildImprovePrompt(projectData);
79
- break;
80
- case 'explain':
81
- userPrompt = buildExplainPrompt(projectData);
82
- break;
83
- default:
84
- userPrompt = buildGeneratePrompt(projectData);
85
- }
86
-
87
- const response = await withRetry(() => makeRequest(apiKey, userPrompt));
88
-
89
79
  if (mode === 'explain') {
80
+ const response = await withRetry(() => makeRequest(apiKey, buildExplainPrompt(projectData)));
90
81
  return { type: 'text', content: response };
91
82
  }
92
-
93
- return { type: 'markdown', content: response };
83
+
84
+ if (mode === 'generate') {
85
+ const [readme, contributing, license, security] = await Promise.all([
86
+ withRetry(() => makeRequest(apiKey, buildReadmePrompt(projectData))),
87
+ withRetry(() => makeRequest(apiKey, buildContributingPrompt(projectData))),
88
+ withRetry(() => makeRequest(apiKey, buildLicensePrompt(projectData))),
89
+ withRetry(() => makeRequest(apiKey, buildSecurityPrompt(projectData)))
90
+ ]);
91
+
92
+ return {
93
+ type: 'markdown',
94
+ content: readme,
95
+ contributing,
96
+ license,
97
+ security
98
+ } as unknown as DocResult;
99
+ }
100
+
101
+ if (mode === 'improve') {
102
+ const [readme, contributing, license, security] = await Promise.all([
103
+ withRetry(() => makeRequest(apiKey, buildImproveReadmePrompt(projectData))),
104
+ withRetry(() => makeRequest(apiKey, buildImproveContributingPrompt(projectData))),
105
+ withRetry(() => makeRequest(apiKey, buildImproveLicensePrompt(projectData))),
106
+ withRetry(() => makeRequest(apiKey, buildImproveSecurityPrompt(projectData)))
107
+ ]);
108
+
109
+ return {
110
+ type: 'markdown',
111
+ content: readme,
112
+ contributing,
113
+ license,
114
+ security
115
+ } as unknown as DocResult;
116
+ }
117
+
118
+ throw new Error('Unknown mode');
94
119
  }
95
120
 
96
- function buildGeneratePrompt(projectData: Record<string, unknown>): string {
97
- return `Generate a detailed, professional README.md for this project with these exact sections:
121
+ function buildReadmePrompt(projectData: Record<string, unknown>): string {
122
+ return `You are an expert technical writer. Generate a comprehensive, production-ready README.md for this project.
123
+
124
+ Analyze the project structure and package.json thoroughly. Document ONLY what's actually present in the codebase.
98
125
 
99
- # Project Name
126
+ Requirements:
127
+ 1. Overview: What the project does, target audience, primary use case
128
+ 2. Features: List ACTUAL features from package.json scripts and dependencies - NO made-up features
129
+ 3. Installation: Exact commands for npm/pnpm/yarn
130
+ 4. Usage: Real code examples based on actual entry points and exports
131
+ 5. API: Document actual functions/endpoints/components found in the code
132
+ 6. Configuration: Actual config options if any
133
+ 7. Development: Real npm scripts from package.json
134
+ 8. Testing: How tests are run (from scripts)
135
+ 9. Project Structure: Actual directory layout
136
+ 10. Technologies: Real stack based on dependencies
100
137
 
101
- ## Overview
102
- 2-3 sentences about what this project does and its purpose.
138
+ IMPORTANT: Use the project data provided and INFER realistic content. Don't leave placeholder text.
103
139
 
104
- ## Version
105
- Current version: X.X.X
140
+ Project Data:
141
+ ${JSON.stringify(projectData, null, 2)}
106
142
 
107
- ## Description
108
- Detailed description of what this project does, who it's for, and why to use it.
143
+ Return ONLY complete README.md markdown.`;
144
+ }
109
145
 
110
- ## Features
111
- - List real features (infer from package.json scripts and dependencies)
112
- - Do NOT add fake features
146
+ function buildContributingPrompt(projectData: Record<string, unknown>): string {
147
+ return `Generate a professional CONTRIBUTING.md for this project.
113
148
 
114
- ## Installation
115
- Provide the exact npm/pnpm/yarn install command.
149
+ Requirements:
150
+ 1. How to contribute (fork, clone, branch workflow)
151
+ 2. Development setup steps
152
+ 3. Code style guidelines (linting, formatting)
153
+ 4. Commit message conventions
154
+ 5. PR process and requirements
155
+ 6. Testing requirements
156
+ 7. Types of contributions welcome
157
+ 8. Issue templates or guidelines
158
+ 9. Recognition for contributors
159
+ 10. Code of conduct reference
116
160
 
117
- ## Usage
118
- Show actual usage examples with code snippets.
161
+ Base it on the project's package.json scripts and known best practices.
119
162
 
120
- ## API Reference
121
- If backend: document endpoints
122
- If frontend: document components/hooks
123
- If library: document exports/functions
163
+ Project Data:
164
+ ${JSON.stringify(projectData, null, 2)}
124
165
 
125
- ## Configuration
126
- Any config options available.
166
+ Return ONLY complete CONTRIBUTING.md markdown.`;
167
+ }
127
168
 
128
- ## Development
129
- How to set up dev environment:
130
- npm install
131
- npm run [script] - description
169
+ function buildLicensePrompt(projectData: Record<string, unknown>): string {
170
+ return `Generate a complete LICENSE file. Use MIT License as default. Include:
132
171
 
133
- ## Testing
134
- How to run tests.
172
+ 1. Full MIT License text
173
+ 2. Copyright year (current year: ${new Date().getFullYear()})
174
+ 3. Project name: ${projectData.name || 'Project'}
175
+ 4. Author name if available in package.json
135
176
 
136
- ## License
137
- MIT License - include full license text
177
+ Project Data:
178
+ ${JSON.stringify(projectData, null, 2)}
138
179
 
139
- ## Contributing
140
- Contribution Guidelines:
141
- - Fork and create feature branch
142
- - Follow existing code style
143
- - Add tests if applicable
144
- - Submit PR
180
+ Return ONLY complete LICENSE file content (plain text, not markdown).`;
181
+ }
145
182
 
146
- ## Security Policy
147
- How to report vulnerabilities.
183
+ function buildSecurityPrompt(projectData: Record<string, unknown>): string {
184
+ return `Generate a professional SECURITY.md for this project.
148
185
 
149
- ## Code of Conduct
150
- Be respectful, inclusive, no harassment. violations will result in immediate suspension.
186
+ Requirements:
187
+ 1. How to report vulnerabilities
188
+ 2. Security policy scope
189
+ 3. Response timeline
190
+ 4. Disclosure guidelines
191
+ 5. Security update process
192
+ 6. Contact information
193
+ 7. Dependencies security info if applicable
151
194
 
152
195
  Project Data:
153
196
  ${JSON.stringify(projectData, null, 2)}
154
197
 
155
- Return ONLY complete markdown.`;
198
+ Return ONLY complete SECURITY.md markdown.`;
156
199
  }
157
200
 
158
- function buildImprovePrompt(projectData: Record<string, unknown>): string {
201
+ function buildImproveReadmePrompt(projectData: Record<string, unknown>): string {
159
202
  return `Improve the existing README.md for this project.
160
203
 
161
- Current README:
162
- ${projectData.existingReadme}
204
+ Requirements:
205
+ 1. Fix unclear sections
206
+ 2. Add missing important sections
207
+ 3. Improve code examples to be more practical
208
+ 4. Make installation more clear
209
+ 5. Enhance usage examples with real code
210
+ 6. Add API documentation if missing
211
+ 7. Improve structure and readability
212
+ 8. Keep existing good content
213
+ 9. Add project structure visualization if missing
214
+
215
+ EXISTING README:
216
+ ${projectData.existingReadme || 'No existing README'}
217
+
218
+ Project Data:
219
+ ${JSON.stringify(projectData, null, 2)}
220
+
221
+ Return ONLY improved README.md markdown.`;
222
+ }
223
+
224
+ function buildImproveContributingPrompt(projectData: Record<string, unknown>): string {
225
+ return `Improve the existing CONTRIBUTING.md for this project.
226
+
227
+ Requirements:
228
+ 1. Make contribution process clearer
229
+ 2. Add missing guidelines
230
+ 3. Improve development setup instructions
231
+ 4. Add testing requirements
232
+ 5. Update for current project state
233
+
234
+ EXISTING CONTRIBUTING:
235
+ ${projectData.existingContributing || 'No existing CONTRIBUTING.md'}
236
+
237
+ Project Data:
238
+ ${JSON.stringify(projectData, null, 2)}
239
+
240
+ Return ONLY improved CONTRIBUTING.md markdown.`;
241
+ }
242
+
243
+ function buildImproveLicensePrompt(projectData: Record<string, unknown>): string {
244
+ return `Review and improve the LICENSE if needed.
245
+
246
+ Requirements:
247
+ 1. Ensure correct copyright year
248
+ 2. Verify project name is correct
249
+ 3. Add any missing clauses
250
+
251
+ EXISTING LICENSE:
252
+ ${projectData.existingLicense || 'No existing LICENSE'}
163
253
 
164
254
  Project Data:
165
255
  ${JSON.stringify(projectData, null, 2)}
166
256
 
167
- Rules:
168
- - Keep improvements practical and factual
169
- - Do NOT add hallucinated features
170
- - Maintain existing structure
171
- - Make it more useful for developers
257
+ Return ONLY complete LICENSE file content.`;
258
+ }
259
+
260
+ function buildImproveSecurityPrompt(projectData: Record<string, unknown>): string {
261
+ return `Improve the existing SECURITY.md for this project.
262
+
263
+ Requirements:
264
+ 1. Make reporting process clearer
265
+ 2. Add missing security information
266
+ 3. Update contact info if needed
267
+ 4. Improve response timeline clarity
268
+
269
+ EXISTING SECURITY:
270
+ ${projectData.existingSecurity || 'No existing SECURITY.md'}
172
271
 
173
- Return ONLY the improved markdown.`;
272
+ Project Data:
273
+ ${JSON.stringify(projectData, null, 2)}
274
+
275
+ Return ONLY improved SECURITY.md markdown.`;
174
276
  }
175
277
 
176
278
  function buildExplainPrompt(projectData: Record<string, unknown>): string {
177
- return `Explain this project in simple terms for developers.
279
+ return `Explain this project in simple, practical terms for developers.
280
+
281
+ Requirements:
282
+ 1. What the project does (one sentence)
283
+ 2. Who it's for
284
+ 3. Main features (top 3-5)
285
+ 4. How to use it (quick start)
286
+ 5. Why someone would use it
287
+ 6. Tech stack briefly
288
+
289
+ Be conversational but professional. No markdown.
178
290
 
179
291
  Project Data:
180
292
  ${JSON.stringify(projectData, null, 2)}
181
293
 
182
- Rules:
183
- - Be concise
184
- - Explain what the project does
185
- - Explain how to use it
186
- - Mention key features
187
- - Keep it conversational
188
-
189
294
  Return as plain text explanation.`;
190
295
  }
191
296
 
@@ -198,7 +303,7 @@ async function makeRequest(apiKey: string, userPrompt: string): Promise<string>
198
303
  { role: 'system', content: SYSTEM_PROMPT },
199
304
  { role: 'user', content: userPrompt }
200
305
  ],
201
- temperature: 0.7,
306
+ temperature: 0.5,
202
307
  max_tokens: 8192
203
308
  },
204
309
  {
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
2
2
  import { Box, Text, useInput } from 'ink';
3
3
  import ora from 'ora';
4
4
  import { loadApiKey } from '../../core/config.js';
5
- import { generateDocs } from '../../services/ai.js';
5
+ import { generateDocs, GeneratedDocs } from '../../services/ai.js';
6
6
  import { analyzeProject, summarizeProject } from '../../services/analyzer.js';
7
7
  import fs from 'fs/promises';
8
8
  import path from 'path';
@@ -13,8 +13,8 @@ interface GeneratorProps {
13
13
  }
14
14
 
15
15
  const titles = {
16
- generate: 'Generate README',
17
- improve: 'Improve README',
16
+ generate: 'Generate Documentation',
17
+ improve: 'Improve Documentation',
18
18
  explain: 'Explain Project',
19
19
  };
20
20
 
@@ -64,16 +64,34 @@ export default function Generator({ mode, onBack }: GeneratorProps) {
64
64
  setStatus('generating');
65
65
  spinner.text = 'Generating documentation...';
66
66
 
67
- const docResult = await generateDocs(summary as unknown as Record<string, unknown>, mode);
67
+ const docResult = await generateDocs(summary as unknown as Record<string, unknown>, mode) as unknown as GeneratedDocs;
68
68
 
69
- if (mode === 'generate') {
70
- const readmePath = path.join(projectPath, 'README.md');
71
- await fs.writeFile(readmePath, docResult.content);
72
- setResult('README.md created successfully!');
69
+ if (mode === 'generate' || mode === 'improve') {
70
+ const files: string[] = [];
71
+
72
+ if (docResult.content) {
73
+ await fs.writeFile(path.join(projectPath, 'README.md'), docResult.content);
74
+ files.push('README.md');
75
+ }
76
+
77
+ if (docResult.contributing) {
78
+ await fs.writeFile(path.join(projectPath, 'CONTRIBUTING.md'), docResult.contributing);
79
+ files.push('CONTRIBUTING.md');
80
+ }
81
+
82
+ if (docResult.license) {
83
+ await fs.writeFile(path.join(projectPath, 'LICENSE'), docResult.license);
84
+ files.push('LICENSE');
85
+ }
86
+
87
+ if (docResult.security) {
88
+ await fs.writeFile(path.join(projectPath, 'SECURITY.md'), docResult.security);
89
+ files.push('SECURITY.md');
90
+ }
91
+
92
+ setResult(`${files.join(', ')} ${files.length === 1 ? 'created' : 'created'} successfully!`);
73
93
  } else if (mode === 'explain') {
74
- setResult(docResult.content);
75
- } else {
76
- setResult('Documentation improved!');
94
+ setResult(docResult.content as string);
77
95
  }
78
96
 
79
97
  spinner.succeed('Done!');
@@ -94,7 +112,7 @@ export default function Generator({ mode, onBack }: GeneratorProps) {
94
112
  {status === 'checking' && <Text>Checking API key...</Text>}
95
113
  {status === 'scanning' && <Text>Scanning project...</Text>}
96
114
  {status === 'analyzing' && <Text>Analyzing project...</Text>}
97
- {status === 'generating' && <Text>Generating documentation...</Text>}
115
+ {status === 'generating' && <Text>Generating documentation (README, CONTRIBUTING, LICENSE, SECURITY)...</Text>}
98
116
 
99
117
  {status === 'done' && (
100
118
  <>
@@ -12,8 +12,8 @@ interface MenuProps {
12
12
  }
13
13
 
14
14
  const menuItems: MenuItem[] = [
15
- { key: 'g', label: 'Generate README', desc: 'Create new documentation' },
16
- { key: 'i', label: 'Improve README', desc: 'Enhance existing docs' },
15
+ { key: 'g', label: 'Generate Docs', desc: 'Create README, CONTRIBUTING, LICENSE, SECURITY' },
16
+ { key: 'i', label: 'Improve Docs', desc: 'Enhance existing documentation' },
17
17
  { key: 'e', label: 'Explain Project', desc: 'Terminal explanation' },
18
18
  { key: 's', label: 'Settings', desc: 'Configure API key' },
19
19
  { key: 'h', label: 'Help', desc: 'Usage guide' },
@@ -41,14 +41,9 @@ export default function Menu({ onSelect }: MenuProps) {
41
41
  return (
42
42
  <Box flexDirection="column" padding={1}>
43
43
  <Text bold color="cyan">
44
-
45
- ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ███████╗███████╗
46
- ██╔════╝ ██╔══██╗██╔══██╗██║ ██║██║ ██╔════╝██╔════╝
47
- ██║ ███╗██████╔╝██████╔╝██║ ██║██║ █████╗ ███████╗
48
- ██║ ██║██╔══██╗██╔══██╗██║ ██║██║ ██╔══╝ ╚════██║
49
- ╚██████╔╝██║ ██║██║ ██║╚██████╔╝███████╗███████╗███████║
50
- ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝
51
-
44
+ .-. . . .-. .-. .-. . . . .-. .-. .-. .-.
45
+ `-. |-| |-| | ) | | | | | | ) | | | `-.
46
+ `-' ' ` ` ' `-' `-' `.'.' `-' `-' `-' `-'
52
47
  </Text>
53
48
 
54
49
  <Text dimColor>AI-Powered Documentation Generator</Text>
@@ -75,7 +70,7 @@ export default function Menu({ onSelect }: MenuProps) {
75
70
 
76
71
  <Text>{"\n"}</Text>
77
72
  <Text dimColor>Use w/s or arrow keys to navigate, Enter or key to select, q to quit</Text>
78
- <Text dimColor>Version 2.1.4</Text>
73
+ <Text dimColor>Version 2.1.6</Text>
79
74
  </Box>
80
75
  );
81
76
  }