@lazykedar/lazydocs 1.3.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.
- package/.github/FUNDING.yml +7 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
- package/.github/dependabot.yml +50 -0
- package/.github/workflows/ci.yml +67 -0
- package/.github/workflows/codeql.yml +38 -0
- package/.github/workflows/dependency-review.yml +24 -0
- package/.github/workflows/docs.yml +65 -0
- package/.github/workflows/publish.yml +60 -0
- package/.github/workflows/release.yml +102 -0
- package/CHANGELOG.md +137 -0
- package/CONTRIBUTING.md +219 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/a.js +249 -0
- package/dist/ai.js +223 -0
- package/dist/cli.js +379 -0
- package/dist/o/c.js +83 -0
- package/dist/o/p.js +89 -0
- package/dist/o/r.js +111 -0
- package/dist/t/r.hbs +53 -0
- package/dist/utils/config-manager.js +184 -0
- package/dist/utils/merger.js +60 -0
- package/package.json +60 -0
package/dist/ai.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getDefaultModel = getDefaultModel;
|
|
37
|
+
exports.getFallbackModels = getFallbackModels;
|
|
38
|
+
exports.getAvailableModels = getAvailableModels;
|
|
39
|
+
exports.generateDocSection = generateDocSection;
|
|
40
|
+
const groq_sdk_1 = require("groq-sdk");
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const DEFAULT_MODEL = 'llama-3.3-70b-versatile';
|
|
44
|
+
// Fallback models for when API fetch fails
|
|
45
|
+
const FALLBACK_MODELS = [
|
|
46
|
+
'llama-3.3-70b-versatile',
|
|
47
|
+
'llama-3.1-8b-instant',
|
|
48
|
+
'gemma2-9b-it',
|
|
49
|
+
'mixtral-8x7b-32768',
|
|
50
|
+
'llama-3.1-70b-versatile',
|
|
51
|
+
];
|
|
52
|
+
function getDefaultModel() {
|
|
53
|
+
return DEFAULT_MODEL;
|
|
54
|
+
}
|
|
55
|
+
function getFallbackModels() {
|
|
56
|
+
return FALLBACK_MODELS;
|
|
57
|
+
}
|
|
58
|
+
async function getAvailableModels(apiKey) {
|
|
59
|
+
try {
|
|
60
|
+
const groq = new groq_sdk_1.Groq({ apiKey });
|
|
61
|
+
const models = await groq.models.list();
|
|
62
|
+
return models.data
|
|
63
|
+
.filter((model) => model.active !== false)
|
|
64
|
+
.map((model) => model.id)
|
|
65
|
+
.sort();
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.warn('Failed to fetch models from API, using fallback list');
|
|
69
|
+
return FALLBACK_MODELS;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const logError = (message) => {
|
|
73
|
+
const logDir = path.join(process.cwd(), 'logs');
|
|
74
|
+
if (!fs.existsSync(logDir))
|
|
75
|
+
fs.mkdirSync(logDir);
|
|
76
|
+
const logFile = path.join(logDir, 'error.log');
|
|
77
|
+
fs.appendFileSync(logFile, `${new Date().toISOString()} - ${message}\n`);
|
|
78
|
+
};
|
|
79
|
+
// Enhanced prompt builder with structured requirements
|
|
80
|
+
function buildEnhancedPrompt(codeSnippet, type, compactSummary) {
|
|
81
|
+
const prompts = {
|
|
82
|
+
readme: `You are a technical documentation expert. Analyze this codebase and generate a comprehensive, professional README in Markdown.
|
|
83
|
+
|
|
84
|
+
${compactSummary ? `PROJECT SUMMARY:\n${compactSummary}\n\n` : ''}CODE ANALYSIS:
|
|
85
|
+
${codeSnippet}
|
|
86
|
+
|
|
87
|
+
Generate a complete README with these sections:
|
|
88
|
+
1. Project title and tagline
|
|
89
|
+
2. Overview - What the project does and why it exists
|
|
90
|
+
3. Key Features - Bullet points of main capabilities
|
|
91
|
+
4. Installation - Clear setup instructions
|
|
92
|
+
5. Usage - Code examples showing how to use it
|
|
93
|
+
6. API Reference - Document main functions/classes with descriptions
|
|
94
|
+
7. Contributing - Brief guidelines
|
|
95
|
+
8. License - Standard license section
|
|
96
|
+
|
|
97
|
+
Requirements:
|
|
98
|
+
- Use clear Markdown formatting with proper headers
|
|
99
|
+
- Include code blocks with syntax highlighting
|
|
100
|
+
- Keep it concise but comprehensive (800-1200 words)
|
|
101
|
+
- Use tables for API documentation
|
|
102
|
+
- Professional yet approachable tone
|
|
103
|
+
- Focus on what developers need to know`,
|
|
104
|
+
pr: `You are a code review expert. Analyze these code changes and generate a clear, professional Pull Request description in Markdown.
|
|
105
|
+
|
|
106
|
+
${compactSummary ? `CHANGES SUMMARY:\n${compactSummary}\n\n` : ''}DIFF ANALYSIS:
|
|
107
|
+
${codeSnippet}
|
|
108
|
+
|
|
109
|
+
Generate a PR description with these sections:
|
|
110
|
+
## Summary
|
|
111
|
+
Brief overview of what changed and why
|
|
112
|
+
|
|
113
|
+
## Changes Made
|
|
114
|
+
- Bullet points of specific changes
|
|
115
|
+
- Group related changes together
|
|
116
|
+
- Be specific about what was added/modified/removed
|
|
117
|
+
|
|
118
|
+
## Impact
|
|
119
|
+
- How this affects the codebase
|
|
120
|
+
- Any breaking changes
|
|
121
|
+
- Performance implications
|
|
122
|
+
|
|
123
|
+
## Testing
|
|
124
|
+
- How to verify these changes
|
|
125
|
+
- Test scenarios covered
|
|
126
|
+
|
|
127
|
+
Requirements:
|
|
128
|
+
- Clear, scannable format
|
|
129
|
+
- Use conventional commit style language
|
|
130
|
+
- Highlight breaking changes prominently
|
|
131
|
+
- Professional tone suitable for team review`,
|
|
132
|
+
changelog: `You are a release notes expert. Analyze these commits and generate a well-organized changelog entry in Markdown.
|
|
133
|
+
|
|
134
|
+
${compactSummary ? `COMMIT SUMMARY:\n${compactSummary}\n\n` : ''}COMMIT HISTORY:
|
|
135
|
+
${codeSnippet}
|
|
136
|
+
|
|
137
|
+
Generate a changelog following Keep a Changelog format:
|
|
138
|
+
|
|
139
|
+
## [Version] - YYYY-MM-DD
|
|
140
|
+
|
|
141
|
+
### 🚀 Features
|
|
142
|
+
- New user-facing features
|
|
143
|
+
|
|
144
|
+
### 🐛 Bug Fixes
|
|
145
|
+
- Issues resolved
|
|
146
|
+
|
|
147
|
+
### 📚 Documentation
|
|
148
|
+
- Doc updates
|
|
149
|
+
|
|
150
|
+
### 🔧 Maintenance
|
|
151
|
+
- Internal improvements, refactoring, dependencies
|
|
152
|
+
|
|
153
|
+
### ⚠️ Breaking Changes
|
|
154
|
+
- Changes that break backward compatibility
|
|
155
|
+
|
|
156
|
+
Requirements:
|
|
157
|
+
- Use emoji for visual scanning
|
|
158
|
+
- Group similar changes together
|
|
159
|
+
- Use imperative mood ("Add feature" not "Added feature")
|
|
160
|
+
- Be specific but concise
|
|
161
|
+
- Highlight breaking changes clearly
|
|
162
|
+
- Follow semantic versioning principles`,
|
|
163
|
+
};
|
|
164
|
+
return prompts[type];
|
|
165
|
+
}
|
|
166
|
+
async function generateDocSection(codeSnippet, type, apiKey, customPrompt, options = {}, compactSummary) {
|
|
167
|
+
const { model = DEFAULT_MODEL, temperature = 0.7, maxTokens = 2048, retries = 3, timeout = 30000, } = options;
|
|
168
|
+
// Build enhanced prompt with structured requirements
|
|
169
|
+
const prompt = customPrompt || buildEnhancedPrompt(codeSnippet, type, compactSummary);
|
|
170
|
+
// Retry logic with exponential backoff for reliability
|
|
171
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
172
|
+
try {
|
|
173
|
+
const groq = new groq_sdk_1.Groq({
|
|
174
|
+
apiKey,
|
|
175
|
+
timeout,
|
|
176
|
+
});
|
|
177
|
+
const completion = await groq.chat.completions.create({
|
|
178
|
+
messages: [
|
|
179
|
+
{ role: 'system', content: 'You are an expert technical writer who creates clear, professional documentation.' },
|
|
180
|
+
{ role: 'user', content: prompt }
|
|
181
|
+
],
|
|
182
|
+
model,
|
|
183
|
+
temperature,
|
|
184
|
+
max_tokens: maxTokens,
|
|
185
|
+
});
|
|
186
|
+
const content = completion.choices[0]?.message?.content;
|
|
187
|
+
if (!content) {
|
|
188
|
+
throw new Error('No content generated from AI');
|
|
189
|
+
}
|
|
190
|
+
return content;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const errorMsg = `Attempt ${attempt}/${retries} failed: ${error.message}`;
|
|
194
|
+
logError(errorMsg);
|
|
195
|
+
// Last attempt - throw detailed error
|
|
196
|
+
if (attempt === retries) {
|
|
197
|
+
if (error.status === 401 || error.message?.includes('401')) {
|
|
198
|
+
throw new Error('Invalid API key. Get one from console.groq.com and set with: lazydocs config set GROQ_API_KEY=your_key');
|
|
199
|
+
}
|
|
200
|
+
if (error.status === 429 || error.message?.includes('rate_limit')) {
|
|
201
|
+
throw new Error('Rate limit exceeded. Try:\n' +
|
|
202
|
+
'1. Wait a few minutes and try again\n' +
|
|
203
|
+
'2. Use a faster model: --model llama-3.1-8b-instant\n' +
|
|
204
|
+
'3. Reduce max tokens: --max-tokens 1024');
|
|
205
|
+
}
|
|
206
|
+
if (error.status === 413 || error.message?.includes('too large')) {
|
|
207
|
+
throw new Error('Request too large. Try:\n' +
|
|
208
|
+
'1. Analyze a smaller directory\n' +
|
|
209
|
+
'2. Use a model with larger context window\n' +
|
|
210
|
+
'3. Reduce the amount of code being analyzed');
|
|
211
|
+
}
|
|
212
|
+
if (error.code === 'ENOTFOUND') {
|
|
213
|
+
throw new Error(`Network error: Cannot connect to ${error.hostname}. Check your internet connection.`);
|
|
214
|
+
}
|
|
215
|
+
throw new Error(`AI generation failed after ${retries} attempts: ${error.message}`);
|
|
216
|
+
}
|
|
217
|
+
// Not last attempt - log warning and retry with backoff
|
|
218
|
+
console.warn(`⚠️ ${errorMsg} - retrying...`);
|
|
219
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // Exponential backoff
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return 'Error generating content after multiple attempts';
|
|
223
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander = __importStar(require("commander"));
|
|
38
|
+
const inquirer = __importStar(require("inquirer"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const r_1 = require("./o/r");
|
|
42
|
+
const p_1 = require("./o/p");
|
|
43
|
+
const c_1 = require("./o/c");
|
|
44
|
+
const ai_1 = require("./ai");
|
|
45
|
+
const config_manager_1 = require("./utils/config-manager");
|
|
46
|
+
const logError = (message) => {
|
|
47
|
+
const logDir = path.join(process.cwd(), 'logs');
|
|
48
|
+
if (!fs.existsSync(logDir))
|
|
49
|
+
fs.mkdirSync(logDir);
|
|
50
|
+
const logFile = path.join(logDir, 'error.log');
|
|
51
|
+
fs.appendFileSync(logFile, `${new Date().toISOString()} - ${message}\n`);
|
|
52
|
+
};
|
|
53
|
+
const program = new commander.Command();
|
|
54
|
+
program
|
|
55
|
+
.name('lazydocs')
|
|
56
|
+
.description('AI-powered documentation generator using Groq')
|
|
57
|
+
.version('1.1.0');
|
|
58
|
+
// Config commands
|
|
59
|
+
const configCmd = program.command('config');
|
|
60
|
+
configCmd
|
|
61
|
+
.command('set')
|
|
62
|
+
.argument('<key=value>', 'Set config (e.g., GROQ_API_KEY=gsk_...)')
|
|
63
|
+
.action((kv) => {
|
|
64
|
+
try {
|
|
65
|
+
const [key, value] = kv.split('=');
|
|
66
|
+
if (!key || !value) {
|
|
67
|
+
throw new Error('Invalid format. Use: key=value');
|
|
68
|
+
}
|
|
69
|
+
(0, config_manager_1.setConfig)(key, value);
|
|
70
|
+
console.log(`Set ${key} in ~/.lazydocs`);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logError(`Config set error: ${error.message}`);
|
|
74
|
+
console.error(`Error: ${error.message}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
configCmd
|
|
79
|
+
.command('get')
|
|
80
|
+
.argument('<key>', 'Get config value')
|
|
81
|
+
.action((key) => {
|
|
82
|
+
try {
|
|
83
|
+
const value = (0, config_manager_1.getConfigValue)(key);
|
|
84
|
+
if (value) {
|
|
85
|
+
const displayValue = key.includes('KEY') ? value.substring(0, 10) + '***' : value;
|
|
86
|
+
console.log(displayValue);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log('Not set');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
logError(`Config get error: ${error.message}`);
|
|
94
|
+
console.error(`Error: ${error.message}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
configCmd
|
|
99
|
+
.command('list')
|
|
100
|
+
.description('List all configuration')
|
|
101
|
+
.action(() => {
|
|
102
|
+
try {
|
|
103
|
+
const config = (0, config_manager_1.listConfig)();
|
|
104
|
+
if (Object.keys(config).length === 0) {
|
|
105
|
+
console.log('Current Configuration: (empty - using defaults)');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.log('Current Configuration:\n');
|
|
109
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
110
|
+
const displayValue = key.includes('KEY') ? String(value).substring(0, 10) + '***' : value;
|
|
111
|
+
console.log(` ${key}: ${displayValue}`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
logError(`Config list error: ${error.message}`);
|
|
117
|
+
console.error(`Error: ${error.message}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
configCmd
|
|
122
|
+
.command('delete')
|
|
123
|
+
.argument('<key>', 'Delete config value')
|
|
124
|
+
.action((key) => {
|
|
125
|
+
try {
|
|
126
|
+
const value = (0, config_manager_1.getConfigValue)(key);
|
|
127
|
+
if (value) {
|
|
128
|
+
(0, config_manager_1.deleteConfig)(key);
|
|
129
|
+
console.log(`Deleted ${key} from ~/.lazydocs`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log('Not set');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
logError(`Config delete error: ${error.message}`);
|
|
137
|
+
console.error(`Error: ${error.message}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// Generate command
|
|
142
|
+
program
|
|
143
|
+
.command('generate')
|
|
144
|
+
.description('Generate documentation')
|
|
145
|
+
.option('-i, --input <dir>', 'Input code directory', './src')
|
|
146
|
+
.option('-o, --output <file>', 'Output file')
|
|
147
|
+
.option('-t, --type <type>', 'Doc type: readme, pr, changelog', 'readme')
|
|
148
|
+
.option('-m, --model <model>', 'AI model to use', (0, ai_1.getDefaultModel)())
|
|
149
|
+
.option('--temperature <temp>', 'AI temperature (0-1)', parseFloat, 0.7)
|
|
150
|
+
.option('--max-tokens <tokens>', 'Maximum tokens', parseInt, 2048)
|
|
151
|
+
.option('--interactive', 'Interactive mode')
|
|
152
|
+
.option('--verbose', 'Verbose output')
|
|
153
|
+
.option('--template <path>', 'Custom Handlebars template file path')
|
|
154
|
+
.option('--base <branch>', 'Base branch for PR diff comparison')
|
|
155
|
+
.option('--head <branch>', 'Head branch for PR diff comparison')
|
|
156
|
+
.action(async (options) => {
|
|
157
|
+
try {
|
|
158
|
+
const startTime = Date.now();
|
|
159
|
+
// Interactive mode
|
|
160
|
+
if (options.interactive) {
|
|
161
|
+
let apiKey;
|
|
162
|
+
try {
|
|
163
|
+
const config = (0, config_manager_1.getConfig)({}, true);
|
|
164
|
+
apiKey = config.GROQ_API_KEY;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
const keyAnswer = await inquirer.default.prompt([
|
|
168
|
+
{
|
|
169
|
+
type: 'password',
|
|
170
|
+
name: 'apiKey',
|
|
171
|
+
message: 'Enter your Groq API key (from console.groq.com):',
|
|
172
|
+
validate: (input) => (input ? true : 'API key is required!'),
|
|
173
|
+
},
|
|
174
|
+
]);
|
|
175
|
+
apiKey = keyAnswer.apiKey;
|
|
176
|
+
}
|
|
177
|
+
console.log('Fetching available models...');
|
|
178
|
+
const availableModels = await (0, ai_1.getAvailableModels)(apiKey);
|
|
179
|
+
const answers = await inquirer.default.prompt([
|
|
180
|
+
{
|
|
181
|
+
type: 'list',
|
|
182
|
+
name: 'type',
|
|
183
|
+
message: 'What would you like to generate?',
|
|
184
|
+
choices: [
|
|
185
|
+
{ name: 'README.md', value: 'readme' },
|
|
186
|
+
{ name: 'PR Description', value: 'pr' },
|
|
187
|
+
{ name: 'Changelog', value: 'changelog' },
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'input',
|
|
192
|
+
name: 'input',
|
|
193
|
+
message: 'Input directory:',
|
|
194
|
+
default: './src',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
type: 'list',
|
|
198
|
+
name: 'model',
|
|
199
|
+
message: 'Select AI model:',
|
|
200
|
+
choices: availableModels.map((m) => ({
|
|
201
|
+
name: m,
|
|
202
|
+
value: m,
|
|
203
|
+
})),
|
|
204
|
+
default: (0, ai_1.getDefaultModel)(),
|
|
205
|
+
},
|
|
206
|
+
]);
|
|
207
|
+
Object.assign(options, answers);
|
|
208
|
+
}
|
|
209
|
+
// Set default output
|
|
210
|
+
if (!options.output) {
|
|
211
|
+
const defaults = {
|
|
212
|
+
readme: './README.md',
|
|
213
|
+
pr: './PR_DESCRIPTION.md',
|
|
214
|
+
changelog: './CHANGELOG.md',
|
|
215
|
+
};
|
|
216
|
+
options.output = defaults[options.type];
|
|
217
|
+
}
|
|
218
|
+
// Get API key
|
|
219
|
+
let apiKey;
|
|
220
|
+
try {
|
|
221
|
+
const config = (0, config_manager_1.getConfig)({}, true);
|
|
222
|
+
apiKey = config.GROQ_API_KEY;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
const answers = await inquirer.default.prompt([
|
|
226
|
+
{
|
|
227
|
+
type: 'password',
|
|
228
|
+
name: 'apiKey',
|
|
229
|
+
message: 'Enter your Groq API key (from console.groq.com):',
|
|
230
|
+
validate: (input) => (input ? true : 'API key is required!'),
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: 'confirm',
|
|
234
|
+
name: 'save',
|
|
235
|
+
message: 'Save API key to ~/.lazydocs?',
|
|
236
|
+
default: true,
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
apiKey = answers.apiKey;
|
|
240
|
+
if (answers.save) {
|
|
241
|
+
(0, config_manager_1.setConfig)('GROQ_API_KEY', apiKey);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (options.verbose) {
|
|
245
|
+
console.log('Configuration:');
|
|
246
|
+
console.log(` Type: ${options.type}`);
|
|
247
|
+
console.log(` Input: ${options.input}`);
|
|
248
|
+
console.log(` Output: ${options.output}`);
|
|
249
|
+
console.log(` Model: ${options.model}`);
|
|
250
|
+
console.log(` Temperature: ${options.temperature}`);
|
|
251
|
+
console.log(` Max Tokens: ${options.maxTokens}`);
|
|
252
|
+
}
|
|
253
|
+
console.log(`Generating ${options.type} from ${options.input}...`);
|
|
254
|
+
const aiOptions = {
|
|
255
|
+
model: options.model,
|
|
256
|
+
temperature: options.temperature,
|
|
257
|
+
maxTokens: options.maxTokens,
|
|
258
|
+
};
|
|
259
|
+
// Generate
|
|
260
|
+
if (options.type === 'readme') {
|
|
261
|
+
await (0, r_1.generateReadme)(options.input, options.output, apiKey, aiOptions, options.template);
|
|
262
|
+
}
|
|
263
|
+
else if (options.type === 'pr') {
|
|
264
|
+
const gitOptions = { base: options.base, head: options.head };
|
|
265
|
+
await (0, p_1.generatePrDesc)(options.input, options.output, apiKey, aiOptions, gitOptions);
|
|
266
|
+
}
|
|
267
|
+
else if (options.type === 'changelog') {
|
|
268
|
+
await (0, c_1.generateChangelog)(options.input, options.output, apiKey, aiOptions);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
throw new Error(`Invalid type: ${options.type}`);
|
|
272
|
+
}
|
|
273
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
274
|
+
console.log(`Generated ${options.type} at ${options.output} (${duration}s)`);
|
|
275
|
+
if (fs.existsSync(options.output)) {
|
|
276
|
+
const stats = fs.statSync(options.output);
|
|
277
|
+
console.log(`File size: ${(stats.size / 1024).toFixed(1)} KB`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
logError(`Generate error: ${error.message}`);
|
|
282
|
+
console.error(`Error: ${error.message}`);
|
|
283
|
+
if (options.verbose && error.stack) {
|
|
284
|
+
console.error(error.stack);
|
|
285
|
+
}
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
// Init command
|
|
290
|
+
program
|
|
291
|
+
.command('init')
|
|
292
|
+
.description('Initialize lazydocs in current project')
|
|
293
|
+
.action(async () => {
|
|
294
|
+
try {
|
|
295
|
+
const answers = await inquirer.default.prompt([
|
|
296
|
+
{
|
|
297
|
+
type: 'input',
|
|
298
|
+
name: 'projectName',
|
|
299
|
+
message: 'Project name:',
|
|
300
|
+
default: path.basename(process.cwd()),
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
type: 'input',
|
|
304
|
+
name: 'description',
|
|
305
|
+
message: 'Project description:',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
type: 'checkbox',
|
|
309
|
+
name: 'features',
|
|
310
|
+
message: 'Select features to document:',
|
|
311
|
+
choices: [
|
|
312
|
+
'Installation guide',
|
|
313
|
+
'Usage examples',
|
|
314
|
+
'API documentation',
|
|
315
|
+
'Contributing guidelines',
|
|
316
|
+
'License information',
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
]);
|
|
320
|
+
const projectConfig = {
|
|
321
|
+
projectName: answers.projectName,
|
|
322
|
+
description: answers.description,
|
|
323
|
+
features: answers.features,
|
|
324
|
+
createdAt: new Date().toISOString(),
|
|
325
|
+
};
|
|
326
|
+
fs.writeFileSync('.lazydocs.json', JSON.stringify(projectConfig, null, 2));
|
|
327
|
+
console.log('Initialized lazydocs configuration at .lazydocs.json');
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
logError(`Init error: ${error.message}`);
|
|
331
|
+
console.error(`Error: ${error.message}`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Models command
|
|
336
|
+
program
|
|
337
|
+
.command('models')
|
|
338
|
+
.description('List available AI models')
|
|
339
|
+
.option('--refresh', 'Fetch latest models from API')
|
|
340
|
+
.action(async (options) => {
|
|
341
|
+
try {
|
|
342
|
+
if (options.refresh) {
|
|
343
|
+
let apiKey;
|
|
344
|
+
try {
|
|
345
|
+
const config = (0, config_manager_1.getConfig)({}, true);
|
|
346
|
+
apiKey = config.GROQ_API_KEY;
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
console.error('API key required. Set with: lazydocs config set GROQ_API_KEY=your_key');
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
console.log('Fetching latest models from Groq API...\n');
|
|
353
|
+
const models = await (0, ai_1.getAvailableModels)(apiKey);
|
|
354
|
+
console.log('Available AI Models:\n');
|
|
355
|
+
models.forEach((model, index) => {
|
|
356
|
+
const isDefault = model === (0, ai_1.getDefaultModel)();
|
|
357
|
+
const marker = isDefault ? '*' : ' ';
|
|
358
|
+
console.log(`${marker} ${index + 1}. ${model}${isDefault ? ' (default)' : ''}`);
|
|
359
|
+
});
|
|
360
|
+
console.log(`\nFound ${models.length} active models`);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
console.log('Available AI Models (fallback list):\n');
|
|
364
|
+
const models = (0, ai_1.getFallbackModels)();
|
|
365
|
+
models.forEach((model, index) => {
|
|
366
|
+
const isDefault = model === (0, ai_1.getDefaultModel)();
|
|
367
|
+
const marker = isDefault ? '*' : ' ';
|
|
368
|
+
console.log(`${marker} ${index + 1}. ${model}${isDefault ? ' (default)' : ''}`);
|
|
369
|
+
});
|
|
370
|
+
console.log('\nUse --refresh to fetch latest models from API');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
logError(`Models error: ${error.message}`);
|
|
375
|
+
console.error(`Error: ${error.message}`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
program.parse();
|
package/dist/o/c.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.generateChangelog = generateChangelog;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
42
|
+
const ai_1 = require("../ai");
|
|
43
|
+
async function generateChangelog(inputDir, outputFile, apiKey, aiOptions) {
|
|
44
|
+
console.log('Analyzing git history...');
|
|
45
|
+
const git = (0, simple_git_1.default)(inputDir);
|
|
46
|
+
try {
|
|
47
|
+
// Get recent commits (last 50)
|
|
48
|
+
const logs = await git.log({ maxCount: 50 });
|
|
49
|
+
const snippet = `
|
|
50
|
+
Total commits analyzed: ${logs.total}
|
|
51
|
+
|
|
52
|
+
Recent commits:
|
|
53
|
+
${logs.all
|
|
54
|
+
.map((l) => `
|
|
55
|
+
- ${l.date}: ${l.message}
|
|
56
|
+
Author: ${l.author_name}
|
|
57
|
+
Hash: ${l.hash.substring(0, 7)}
|
|
58
|
+
`)
|
|
59
|
+
.join('\n')}
|
|
60
|
+
`;
|
|
61
|
+
console.log(` Analyzed ${logs.total} commits`);
|
|
62
|
+
console.log('Generating changelog...');
|
|
63
|
+
const changelog = await (0, ai_1.generateDocSection)(snippet, 'changelog', apiKey, undefined, aiOptions);
|
|
64
|
+
// Prepend version and date
|
|
65
|
+
const today = new Date().toISOString().split('T')[0];
|
|
66
|
+
const fullChangelog = `# Changelog
|
|
67
|
+
|
|
68
|
+
## [Unreleased] - ${today}
|
|
69
|
+
|
|
70
|
+
${changelog}
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
*Generated by LazyDocs*
|
|
74
|
+
`;
|
|
75
|
+
fs.writeFileSync(outputFile, fullChangelog);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error.message.includes('not a git repository')) {
|
|
79
|
+
throw new Error('Not a git repository. Please run this command in a git repository.');
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|