@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/o/p.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
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.generatePrDesc = generatePrDesc;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
42
|
+
const ai_1 = require("../ai");
|
|
43
|
+
async function generatePrDesc(inputDir, outputFile, apiKey, aiOptions, gitOptions) {
|
|
44
|
+
console.log('Analyzing git changes...');
|
|
45
|
+
const git = (0, simple_git_1.default)(inputDir);
|
|
46
|
+
try {
|
|
47
|
+
const diffArgs = [];
|
|
48
|
+
if (gitOptions?.base) {
|
|
49
|
+
if (gitOptions.head) {
|
|
50
|
+
diffArgs.push(`${gitOptions.base}...${gitOptions.head}`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
diffArgs.push(gitOptions.base);
|
|
54
|
+
}
|
|
55
|
+
console.log(` Comparing: ${diffArgs[0]}`);
|
|
56
|
+
}
|
|
57
|
+
// Get diff summary with compact format
|
|
58
|
+
const diff = await git.diffSummary(diffArgs);
|
|
59
|
+
const diffText = await git.diff(diffArgs);
|
|
60
|
+
const fileChanges = diff.files.map((f) => {
|
|
61
|
+
const ins = 'insertions' in f ? f.insertions : 0;
|
|
62
|
+
const del = 'deletions' in f ? f.deletions : 0;
|
|
63
|
+
return `- ${f.file} (+${ins}/-${del})`;
|
|
64
|
+
}).join('\n');
|
|
65
|
+
// Build compact summary for token efficiency
|
|
66
|
+
const compactSummary = `Files changed: ${diff.files.length}
|
|
67
|
+
Insertions: ${diff.insertions}
|
|
68
|
+
Deletions: ${diff.deletions}
|
|
69
|
+
|
|
70
|
+
Changed files:
|
|
71
|
+
${fileChanges}`;
|
|
72
|
+
// Limit diff size for token efficiency
|
|
73
|
+
const diffPreview = diffText.length > 5000
|
|
74
|
+
? diffText.slice(0, 5000) + '\n\n... (diff truncated for brevity)'
|
|
75
|
+
: diffText;
|
|
76
|
+
const snippet = `${compactSummary}\n\nDiff preview:\n${diffPreview}`;
|
|
77
|
+
console.log(` ${diff.files.length} files changed (${diff.insertions} insertions, ${diff.deletions} deletions)`);
|
|
78
|
+
console.log('Generating PR description...');
|
|
79
|
+
const desc = await (0, ai_1.generateDocSection)(snippet, 'pr', apiKey, undefined, aiOptions, compactSummary);
|
|
80
|
+
fs.writeFileSync(outputFile, desc);
|
|
81
|
+
console.log(`PR description saved to ${outputFile}`);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.message.includes('not a git repository')) {
|
|
85
|
+
throw new Error('Not a git repository. Please run this command in a git repository.');
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
package/dist/o/r.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
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.generateReadme = generateReadme;
|
|
37
|
+
const handlebars = __importStar(require("handlebars"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const ai_1 = require("../ai");
|
|
41
|
+
const a_1 = require("../a");
|
|
42
|
+
const merger_1 = require("../utils/merger");
|
|
43
|
+
async function generateReadme(inputDir, outputFile, apiKey, aiOptions, customTemplatePath) {
|
|
44
|
+
console.log('Analyzing codebase...');
|
|
45
|
+
const analysis = (0, a_1.analyzeCode)(inputDir);
|
|
46
|
+
console.log(` Found ${analysis.fileCount} files, ${analysis.totalLines} lines`);
|
|
47
|
+
console.log(` Functions: ${analysis.functions.length}, Classes: ${analysis.classes.length}`);
|
|
48
|
+
// Show compact summary if available
|
|
49
|
+
if (analysis.compactSummary) {
|
|
50
|
+
console.log('\nProject Structure:');
|
|
51
|
+
console.log(analysis.compactSummary.split('\n').map(l => ` ${l}`).join('\n'));
|
|
52
|
+
}
|
|
53
|
+
console.log('\nGenerating documentation...');
|
|
54
|
+
// Use compact summary for better token efficiency
|
|
55
|
+
const contextSnippet = analysis.compactSummary
|
|
56
|
+
? `${analysis.compactSummary}\n\nCode samples:\n${analysis.snippets.slice(0, 4000)}`
|
|
57
|
+
: analysis.snippets;
|
|
58
|
+
const overview = await (0, ai_1.generateDocSection)(contextSnippet, 'readme', apiKey, undefined, aiOptions, analysis.compactSummary);
|
|
59
|
+
const usage = await (0, ai_1.generateDocSection)(contextSnippet, 'readme', apiKey, 'Generate practical usage examples with code snippets showing how to use this project', aiOptions, analysis.compactSummary);
|
|
60
|
+
// Generate API docs for top functions/classes (limit to avoid rate limits)
|
|
61
|
+
const topItems = [...analysis.functions, ...analysis.classes].slice(0, 8);
|
|
62
|
+
const apis = topItems.map(name => ({
|
|
63
|
+
name,
|
|
64
|
+
desc: `${name} - Core functionality (see code for details)`,
|
|
65
|
+
}));
|
|
66
|
+
// Try to read project name from package.json
|
|
67
|
+
let projectName = 'Your Project';
|
|
68
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
69
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
72
|
+
projectName = pkg.name || projectName;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// Ignore
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Load template
|
|
79
|
+
const resolvedTemplatePath = customTemplatePath
|
|
80
|
+
? path.resolve(process.cwd(), customTemplatePath)
|
|
81
|
+
: path.join(__dirname, '../t/r.hbs');
|
|
82
|
+
if (!fs.existsSync(resolvedTemplatePath)) {
|
|
83
|
+
throw new Error(`Template file not found: ${resolvedTemplatePath}`);
|
|
84
|
+
}
|
|
85
|
+
const template = handlebars.compile(fs.readFileSync(resolvedTemplatePath, 'utf-8'));
|
|
86
|
+
const content = template({
|
|
87
|
+
projectName,
|
|
88
|
+
overview,
|
|
89
|
+
usage,
|
|
90
|
+
apis,
|
|
91
|
+
stats: {
|
|
92
|
+
fileCount: analysis.fileCount,
|
|
93
|
+
totalLines: analysis.totalLines,
|
|
94
|
+
functions: analysis.functions.length,
|
|
95
|
+
classes: analysis.classes.length,
|
|
96
|
+
totalSize: (analysis.totalSize / 1024).toFixed(1),
|
|
97
|
+
complexity: analysis.complexity.toFixed(1),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
let finalContent = content;
|
|
101
|
+
if (fs.existsSync(outputFile)) {
|
|
102
|
+
try {
|
|
103
|
+
const existingContent = fs.readFileSync(outputFile, 'utf-8');
|
|
104
|
+
finalContent = (0, merger_1.mergeContent)(existingContent, content);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.warn(`Failed to merge content, overwriting instead: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
fs.writeFileSync(outputFile, finalContent);
|
|
111
|
+
}
|
package/dist/t/r.hbs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
## 📋 Overview
|
|
4
|
+
<!-- lazydocs:start:overview -->
|
|
5
|
+
{{{overview}}}
|
|
6
|
+
<!-- lazydocs:end:overview -->
|
|
7
|
+
|
|
8
|
+
<!-- lazydocs:start:stats -->
|
|
9
|
+
{{#if stats}}
|
|
10
|
+
## 📊 Project Statistics
|
|
11
|
+
- **Files analyzed:** {{stats.fileCount}}
|
|
12
|
+
- **Total lines of code:** {{stats.totalLines}}
|
|
13
|
+
- **Total size:** {{stats.totalSize}} KB
|
|
14
|
+
- **Functions:** {{stats.functions}}
|
|
15
|
+
- **Classes:** {{stats.classes}}
|
|
16
|
+
- **Avg complexity:** {{stats.complexity}}
|
|
17
|
+
{{/if}}
|
|
18
|
+
<!-- lazydocs:end:stats -->
|
|
19
|
+
|
|
20
|
+
## 🚀 Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install {{projectName}}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 💡 Usage
|
|
27
|
+
<!-- lazydocs:start:usage -->
|
|
28
|
+
{{{usage}}}
|
|
29
|
+
<!-- lazydocs:end:usage -->
|
|
30
|
+
|
|
31
|
+
<!-- lazydocs:start:api -->
|
|
32
|
+
{{#if apis.length}}
|
|
33
|
+
## 📚 API Reference
|
|
34
|
+
|
|
35
|
+
| Function/Class | Description |
|
|
36
|
+
|----------------|-------------|
|
|
37
|
+
{{#each apis}}
|
|
38
|
+
| `{{name}}` | {{{desc}}} |
|
|
39
|
+
{{/each}}
|
|
40
|
+
{{/if}}
|
|
41
|
+
<!-- lazydocs:end:api -->
|
|
42
|
+
|
|
43
|
+
## 🤝 Contributing
|
|
44
|
+
|
|
45
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
46
|
+
|
|
47
|
+
## 📄 License
|
|
48
|
+
|
|
49
|
+
This project is licensed under the MIT License.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
*Documentation generated with ❤️ by [LazyDocs](https://github.com/kedar49/lazydocs)*
|
|
@@ -0,0 +1,184 @@
|
|
|
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.configExists = exports.deleteConfig = exports.listConfig = exports.getConfigValue = exports.setConfig = exports.getConfig = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const CONFIG_FILE = path.join(os.homedir(), '.lazydocs');
|
|
41
|
+
class ConfigError extends Error {
|
|
42
|
+
constructor(message) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = 'ConfigError';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const parseAssert = (name, condition, message) => {
|
|
48
|
+
if (!condition) {
|
|
49
|
+
throw new ConfigError(`Invalid config property ${name}: ${message}`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const configParsers = {
|
|
53
|
+
GROQ_API_KEY(key) {
|
|
54
|
+
if (!key) {
|
|
55
|
+
throw new ConfigError('Please set your Groq API key via `lazydocs config set GROQ_API_KEY=<your token>`');
|
|
56
|
+
}
|
|
57
|
+
parseAssert('GROQ_API_KEY', key.startsWith('gsk_'), 'Must start with "gsk_"');
|
|
58
|
+
parseAssert('GROQ_API_KEY', key.length > 20, 'Invalid API key format');
|
|
59
|
+
return key;
|
|
60
|
+
},
|
|
61
|
+
DEFAULT_MODEL(model) {
|
|
62
|
+
if (!model || model.length === 0) {
|
|
63
|
+
return 'llama-3.3-70b-versatile';
|
|
64
|
+
}
|
|
65
|
+
return model;
|
|
66
|
+
},
|
|
67
|
+
MAX_TOKENS(tokens) {
|
|
68
|
+
if (!tokens) {
|
|
69
|
+
return 2048;
|
|
70
|
+
}
|
|
71
|
+
const value = typeof tokens === 'string' ? tokens : String(tokens);
|
|
72
|
+
parseAssert('MAX_TOKENS', /^\d+$/.test(value), 'Must be an integer');
|
|
73
|
+
const parsed = Number(value);
|
|
74
|
+
parseAssert('MAX_TOKENS', parsed >= 100, 'Must be at least 100');
|
|
75
|
+
parseAssert('MAX_TOKENS', parsed <= 131072, 'Must be at most 131072');
|
|
76
|
+
return parsed;
|
|
77
|
+
},
|
|
78
|
+
TEMPERATURE(temp) {
|
|
79
|
+
if (!temp) {
|
|
80
|
+
return 0.7;
|
|
81
|
+
}
|
|
82
|
+
const value = typeof temp === 'string' ? temp : String(temp);
|
|
83
|
+
parseAssert('TEMPERATURE', /^[0-9.]+$/.test(value), 'Must be a number');
|
|
84
|
+
const parsed = Number(value);
|
|
85
|
+
parseAssert('TEMPERATURE', parsed >= 0, 'Must be at least 0');
|
|
86
|
+
parseAssert('TEMPERATURE', parsed <= 2, 'Must be at most 2');
|
|
87
|
+
return parsed;
|
|
88
|
+
},
|
|
89
|
+
GENERATE_COUNT(count) {
|
|
90
|
+
if (!count) {
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
const value = typeof count === 'string' ? count : String(count);
|
|
94
|
+
parseAssert('GENERATE_COUNT', /^\d+$/.test(value), 'Must be an integer');
|
|
95
|
+
const parsed = Number(value);
|
|
96
|
+
parseAssert('GENERATE_COUNT', parsed > 0, 'Must be greater than 0');
|
|
97
|
+
parseAssert('GENERATE_COUNT', parsed <= 5, 'Must be at most 5');
|
|
98
|
+
return parsed;
|
|
99
|
+
},
|
|
100
|
+
TIMEOUT(timeout) {
|
|
101
|
+
if (!timeout) {
|
|
102
|
+
return 30000; // 30 seconds
|
|
103
|
+
}
|
|
104
|
+
const value = typeof timeout === 'string' ? timeout : String(timeout);
|
|
105
|
+
parseAssert('TIMEOUT', /^\d+$/.test(value), 'Must be an integer');
|
|
106
|
+
const parsed = Number(value);
|
|
107
|
+
parseAssert('TIMEOUT', parsed >= 1000, 'Must be at least 1000ms (1 second)');
|
|
108
|
+
parseAssert('TIMEOUT', parsed <= 300000, 'Must be at most 300000ms (5 minutes)');
|
|
109
|
+
return parsed;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const readConfigFile = () => {
|
|
113
|
+
try {
|
|
114
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
118
|
+
return JSON.parse(content);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn('Failed to read config file, using defaults');
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const getConfig = (cliConfig, suppressErrors = false) => {
|
|
126
|
+
const fileConfig = readConfigFile();
|
|
127
|
+
const envConfig = {
|
|
128
|
+
GROQ_API_KEY: process.env.GROQ_API_KEY,
|
|
129
|
+
};
|
|
130
|
+
const parsedConfig = {};
|
|
131
|
+
for (const key of Object.keys(configParsers)) {
|
|
132
|
+
const parser = configParsers[key];
|
|
133
|
+
// Priority: CLI > Env > File
|
|
134
|
+
const value = cliConfig?.[key] ?? envConfig[key] ?? fileConfig[key];
|
|
135
|
+
if (suppressErrors) {
|
|
136
|
+
try {
|
|
137
|
+
parsedConfig[key] = parser(value);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
// Silently use default
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
parsedConfig[key] = parser(value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return parsedConfig;
|
|
148
|
+
};
|
|
149
|
+
exports.getConfig = getConfig;
|
|
150
|
+
const setConfig = (key, value) => {
|
|
151
|
+
if (!(key in configParsers)) {
|
|
152
|
+
throw new ConfigError(`Invalid config property: ${key}`);
|
|
153
|
+
}
|
|
154
|
+
// Validate the value
|
|
155
|
+
const parser = configParsers[key];
|
|
156
|
+
const parsed = parser(value);
|
|
157
|
+
// Read existing config
|
|
158
|
+
const config = readConfigFile();
|
|
159
|
+
// Update config
|
|
160
|
+
config[key] = parsed;
|
|
161
|
+
// Write back
|
|
162
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
163
|
+
};
|
|
164
|
+
exports.setConfig = setConfig;
|
|
165
|
+
const getConfigValue = (key) => {
|
|
166
|
+
const config = readConfigFile();
|
|
167
|
+
const value = config[key];
|
|
168
|
+
return value !== undefined ? String(value) : undefined;
|
|
169
|
+
};
|
|
170
|
+
exports.getConfigValue = getConfigValue;
|
|
171
|
+
const listConfig = () => {
|
|
172
|
+
return readConfigFile();
|
|
173
|
+
};
|
|
174
|
+
exports.listConfig = listConfig;
|
|
175
|
+
const deleteConfig = (key) => {
|
|
176
|
+
const config = readConfigFile();
|
|
177
|
+
delete config[key];
|
|
178
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
179
|
+
};
|
|
180
|
+
exports.deleteConfig = deleteConfig;
|
|
181
|
+
const configExists = () => {
|
|
182
|
+
return fs.existsSync(CONFIG_FILE);
|
|
183
|
+
};
|
|
184
|
+
exports.configExists = configExists;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeContent = mergeContent;
|
|
4
|
+
/**
|
|
5
|
+
* Smartly merges newly generated documentation content into existing documentation content
|
|
6
|
+
* by replacing content between comment anchors.
|
|
7
|
+
*
|
|
8
|
+
* Anchors are in the format:
|
|
9
|
+
* <!-- lazydocs:start:<section> -->
|
|
10
|
+
* <!-- lazydocs:end:<section> -->
|
|
11
|
+
*
|
|
12
|
+
* If the existing content contains no matching anchors, the function returns the new content completely
|
|
13
|
+
* (falling back to a full overwrite).
|
|
14
|
+
*/
|
|
15
|
+
function mergeContent(existingContent, newContent) {
|
|
16
|
+
// Regex to find all start blocks in the new content
|
|
17
|
+
const startRegex = /<!-- lazydocs:start:([\w-]+) -->/g;
|
|
18
|
+
let match;
|
|
19
|
+
let mergedContent = existingContent;
|
|
20
|
+
const sections = {};
|
|
21
|
+
// Find all sections in the new generated content
|
|
22
|
+
while ((match = startRegex.exec(newContent)) !== null) {
|
|
23
|
+
const sectionName = match[1];
|
|
24
|
+
const startTag = `<!-- lazydocs:start:${sectionName} -->`;
|
|
25
|
+
const endTag = `<!-- lazydocs:end:${sectionName} -->`;
|
|
26
|
+
const startIndex = newContent.indexOf(startTag);
|
|
27
|
+
const endIndex = newContent.indexOf(endTag);
|
|
28
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
29
|
+
// Extract the content inside the tags
|
|
30
|
+
const newSectionContent = newContent.substring(startIndex + startTag.length, endIndex);
|
|
31
|
+
sections[sectionName] = newSectionContent;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// If the existing content doesn't contain any of the lazydocs start tags,
|
|
35
|
+
// return the new content completely (overwrite)
|
|
36
|
+
let hasAnyAnchor = false;
|
|
37
|
+
for (const sectionName of Object.keys(sections)) {
|
|
38
|
+
const startTag = `<!-- lazydocs:start:${sectionName} -->`;
|
|
39
|
+
if (existingContent.includes(startTag)) {
|
|
40
|
+
hasAnyAnchor = true;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!hasAnyAnchor) {
|
|
45
|
+
return newContent;
|
|
46
|
+
}
|
|
47
|
+
// Replace only the matched sections in the existing content
|
|
48
|
+
for (const [sectionName, newSectionContent] of Object.entries(sections)) {
|
|
49
|
+
const startTag = `<!-- lazydocs:start:${sectionName} -->`;
|
|
50
|
+
const endTag = `<!-- lazydocs:end:${sectionName} -->`;
|
|
51
|
+
const startIndex = mergedContent.indexOf(startTag);
|
|
52
|
+
const endIndex = mergedContent.indexOf(endTag);
|
|
53
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
54
|
+
const before = mergedContent.substring(0, startIndex + startTag.length);
|
|
55
|
+
const after = mergedContent.substring(endIndex);
|
|
56
|
+
mergedContent = before + newSectionContent + after;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return mergedContent;
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lazykedar/lazydocs",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "AI-powered documentation generator using Groq. Generate READMEs, PR descriptions, and changelogs in seconds.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lazydocs": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "npx -y serve web",
|
|
11
|
+
"build": "tsc && npm run copy-templates",
|
|
12
|
+
"copy-templates": "node -e \"const fs=require('fs');const path=require('path');const src='src/t';const dest='dist/t';if(!fs.existsSync(dest))fs.mkdirSync(dest,{recursive:true});fs.readdirSync(src).forEach(f=>{if(f.endsWith('.hbs'))fs.copyFileSync(path.join(src,f),path.join(dest,f))})\"",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"prepublishOnly": "npm run build && npm test",
|
|
18
|
+
"postinstall": "echo 'LazyDocs installed! Run: lazydocs --help'"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"ai",
|
|
22
|
+
"documentation",
|
|
23
|
+
"groq",
|
|
24
|
+
"readme",
|
|
25
|
+
"changelog",
|
|
26
|
+
"pr",
|
|
27
|
+
"generator",
|
|
28
|
+
"cli",
|
|
29
|
+
"markdown",
|
|
30
|
+
"llm",
|
|
31
|
+
"typescript",
|
|
32
|
+
"javascript"
|
|
33
|
+
],
|
|
34
|
+
"author": "Kedar Sathe <kedarsathe49@gmail.com>",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/kedar49/lazydocs.git"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/commander": "^2.12.0",
|
|
45
|
+
"@types/inquirer": "^9.0.9",
|
|
46
|
+
"@types/jest": "^30.0.0",
|
|
47
|
+
"@types/node": "^24.7.0",
|
|
48
|
+
"jest": "^30.2.0",
|
|
49
|
+
"ts-jest": "^29.4.4",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@babel/parser": "^7.28.4",
|
|
54
|
+
"commander": "^14.0.1",
|
|
55
|
+
"groq-sdk": "^0.33.0",
|
|
56
|
+
"handlebars": "^4.7.8",
|
|
57
|
+
"inquirer": "^12.9.6",
|
|
58
|
+
"simple-git": "^3.28.0"
|
|
59
|
+
}
|
|
60
|
+
}
|