@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/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Contributing to LazyDocs
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to LazyDocs! 🎉
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
- Node.js >= 18
|
|
9
|
+
- npm or yarn
|
|
10
|
+
- Git
|
|
11
|
+
- A Groq API key (for testing)
|
|
12
|
+
|
|
13
|
+
### Development Setup
|
|
14
|
+
|
|
15
|
+
1. **Fork and clone the repository**
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/YOUR_USERNAME/lazydocs.git
|
|
18
|
+
cd lazydocs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. **Install dependencies**
|
|
22
|
+
```bash
|
|
23
|
+
npm install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. **Build the project**
|
|
27
|
+
```bash
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
4. **Run tests**
|
|
32
|
+
```bash
|
|
33
|
+
npm test
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
5. **Set up your API key**
|
|
37
|
+
```bash
|
|
38
|
+
export GROQ_API_KEY=your_api_key_here
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Development Workflow
|
|
42
|
+
|
|
43
|
+
### Project Structure
|
|
44
|
+
```
|
|
45
|
+
src/
|
|
46
|
+
├── cli.ts # CLI entry point
|
|
47
|
+
├── ai.ts # AI integration with Groq
|
|
48
|
+
├── a.ts # Code analyzer
|
|
49
|
+
├── o/ # Output generators (short names for brevity)
|
|
50
|
+
│ ├── r.ts # README generator
|
|
51
|
+
│ ├── p.ts # PR description generator
|
|
52
|
+
│ └── c.ts # Changelog generator
|
|
53
|
+
└── t/ # Templates
|
|
54
|
+
└── r.hbs # README Handlebars template
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Making Changes
|
|
58
|
+
|
|
59
|
+
1. **Create a feature branch**
|
|
60
|
+
```bash
|
|
61
|
+
git checkout -b feature/your-feature-name
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **Make your changes**
|
|
65
|
+
- Write clean, readable code
|
|
66
|
+
- Follow existing code style
|
|
67
|
+
- Add tests for new features
|
|
68
|
+
- Update documentation
|
|
69
|
+
|
|
70
|
+
3. **Test your changes**
|
|
71
|
+
```bash
|
|
72
|
+
npm test
|
|
73
|
+
npm run build
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
4. **Commit your changes**
|
|
77
|
+
```bash
|
|
78
|
+
git commit -m "feat: add amazing feature"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use conventional commit messages:
|
|
82
|
+
- `feat:` - New feature
|
|
83
|
+
- `fix:` - Bug fix
|
|
84
|
+
- `docs:` - Documentation changes
|
|
85
|
+
- `test:` - Test changes
|
|
86
|
+
- `refactor:` - Code refactoring
|
|
87
|
+
- `chore:` - Maintenance tasks
|
|
88
|
+
|
|
89
|
+
5. **Push and create a PR**
|
|
90
|
+
```bash
|
|
91
|
+
git push origin feature/your-feature-name
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Code Style
|
|
95
|
+
|
|
96
|
+
- Use TypeScript
|
|
97
|
+
- Follow existing formatting
|
|
98
|
+
- Use meaningful variable names
|
|
99
|
+
- Add comments for complex logic
|
|
100
|
+
- Keep functions small and focused
|
|
101
|
+
|
|
102
|
+
## Testing
|
|
103
|
+
|
|
104
|
+
- Write tests for new features
|
|
105
|
+
- Ensure all tests pass before submitting PR
|
|
106
|
+
- Aim for good test coverage
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Run tests
|
|
110
|
+
npm test
|
|
111
|
+
|
|
112
|
+
# Run tests in watch mode
|
|
113
|
+
npm run test:watch
|
|
114
|
+
|
|
115
|
+
# Run tests with coverage
|
|
116
|
+
npm run test:coverage
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Adding New Features
|
|
120
|
+
|
|
121
|
+
### Adding a New AI Model
|
|
122
|
+
1. Update `AVAILABLE_MODELS` in `src/ai.ts`
|
|
123
|
+
2. Test with the new model
|
|
124
|
+
3. Update documentation
|
|
125
|
+
|
|
126
|
+
### Adding a New File Type
|
|
127
|
+
1. Update `SUPPORTED_EXTENSIONS` in `src/a.ts`
|
|
128
|
+
2. Add appropriate Babel plugins if needed
|
|
129
|
+
3. Test with sample files
|
|
130
|
+
4. Update documentation
|
|
131
|
+
|
|
132
|
+
### Adding a New Output Type
|
|
133
|
+
1. Create a new generator in `src/o/`
|
|
134
|
+
2. Add template in `src/t/`
|
|
135
|
+
3. Update CLI in `src/cli.ts`
|
|
136
|
+
4. Add tests
|
|
137
|
+
5. Update documentation
|
|
138
|
+
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
- Update README.md for user-facing changes
|
|
142
|
+
- Update CHANGELOG.md following Keep a Changelog format
|
|
143
|
+
- Add JSDoc comments for public APIs
|
|
144
|
+
- Include code examples where helpful
|
|
145
|
+
|
|
146
|
+
## Pull Request Process
|
|
147
|
+
|
|
148
|
+
1. Ensure your PR description clearly describes the problem and solution
|
|
149
|
+
2. Include relevant issue numbers if applicable
|
|
150
|
+
3. Update documentation as needed
|
|
151
|
+
4. Ensure all tests pass
|
|
152
|
+
5. Request review from maintainers
|
|
153
|
+
|
|
154
|
+
### PR Checklist
|
|
155
|
+
- [ ] Code follows project style
|
|
156
|
+
- [ ] Tests added/updated
|
|
157
|
+
- [ ] Documentation updated
|
|
158
|
+
- [ ] CHANGELOG.md updated
|
|
159
|
+
- [ ] All tests passing
|
|
160
|
+
- [ ] No TypeScript errors
|
|
161
|
+
- [ ] Commit messages follow convention
|
|
162
|
+
|
|
163
|
+
## Reporting Bugs
|
|
164
|
+
|
|
165
|
+
### Before Submitting
|
|
166
|
+
- Check existing issues
|
|
167
|
+
- Try the latest version
|
|
168
|
+
- Gather relevant information
|
|
169
|
+
|
|
170
|
+
### Bug Report Template
|
|
171
|
+
```markdown
|
|
172
|
+
**Describe the bug**
|
|
173
|
+
A clear description of the bug.
|
|
174
|
+
|
|
175
|
+
**To Reproduce**
|
|
176
|
+
Steps to reproduce:
|
|
177
|
+
1. Run command '...'
|
|
178
|
+
2. See error
|
|
179
|
+
|
|
180
|
+
**Expected behavior**
|
|
181
|
+
What you expected to happen.
|
|
182
|
+
|
|
183
|
+
**Environment:**
|
|
184
|
+
- OS: [e.g., macOS 14.0]
|
|
185
|
+
- Node version: [e.g., 18.0.0]
|
|
186
|
+
- LazyDocs version: [e.g., 1.0.0]
|
|
187
|
+
|
|
188
|
+
**Additional context**
|
|
189
|
+
Any other relevant information.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Feature Requests
|
|
193
|
+
|
|
194
|
+
We welcome feature requests! Please:
|
|
195
|
+
- Check if it's already requested
|
|
196
|
+
- Describe the use case
|
|
197
|
+
- Explain why it would be useful
|
|
198
|
+
- Provide examples if possible
|
|
199
|
+
|
|
200
|
+
## Questions?
|
|
201
|
+
|
|
202
|
+
- Open a GitHub issue
|
|
203
|
+
- Check existing documentation
|
|
204
|
+
- Review closed issues for similar questions
|
|
205
|
+
|
|
206
|
+
## Code of Conduct
|
|
207
|
+
|
|
208
|
+
- Be respectful and inclusive
|
|
209
|
+
- Welcome newcomers
|
|
210
|
+
- Focus on constructive feedback
|
|
211
|
+
- Help others learn and grow
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
Thank you for contributing to LazyDocs! 🚀
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kedar Sathe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# LazyDocs
|
|
2
|
+
|
|
3
|
+
AI-powered documentation generator using Groq. Generate READMEs, PR descriptions, and changelogs in seconds.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@tfkedar/lazydocs)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @tfkedar/lazydocs
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
Get a free API key from [console.groq.com](https://console.groq.com):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
lazydocs config set GROQ_API_KEY=your_key_here
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Interactive mode (recommended)
|
|
26
|
+
lazydocs generate --interactive
|
|
27
|
+
|
|
28
|
+
# Generate README
|
|
29
|
+
lazydocs generate --type readme
|
|
30
|
+
|
|
31
|
+
# Generate PR description
|
|
32
|
+
lazydocs generate --type pr
|
|
33
|
+
|
|
34
|
+
# Generate changelog
|
|
35
|
+
lazydocs generate --type changelog
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Fast** - Powered by Groq's LLM inference.
|
|
41
|
+
- **Smart Merging** - Automatically updates sections within comment anchors (`<!-- lazydocs:start:section -->`), preserving manual edits.
|
|
42
|
+
- **Custom Templates** - Use your own Handlebars layouts to structure README files.
|
|
43
|
+
- **Branch Comparison** - Compare custom branch ranges for precise PR descriptions.
|
|
44
|
+
- **Reliable** - Automatic retry logic with exponential backoff.
|
|
45
|
+
- **Easy** - Interactive CLI with helpful prompts.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
lazydocs config set GROQ_API_KEY=your_key
|
|
51
|
+
lazydocs config list
|
|
52
|
+
lazydocs config get GROQ_API_KEY
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Options
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
lazydocs generate [options]
|
|
59
|
+
|
|
60
|
+
-i, --input <dir> Code directory (default: "./src")
|
|
61
|
+
-o, --output <file> Output file (auto-detected)
|
|
62
|
+
-t, --type <type> readme | pr | changelog
|
|
63
|
+
-m, --model <model> AI model to use
|
|
64
|
+
--temperature <temp> Creativity 0-1 (default: 0.7)
|
|
65
|
+
--max-tokens <tokens> Max response length (default: 2048)
|
|
66
|
+
--interactive Interactive mode
|
|
67
|
+
--verbose Show details
|
|
68
|
+
--template <path> Custom Handlebars template path (README only)
|
|
69
|
+
--base <branch> Base branch for PR diff comparison
|
|
70
|
+
--head <branch> Head branch for PR diff comparison
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Advanced Usage
|
|
74
|
+
|
|
75
|
+
### 🔄 Smart Merging (Incremental Updates)
|
|
76
|
+
|
|
77
|
+
To prevent `lazydocs` from overwriting custom edits in your README, wrap dynamic sections in comment anchors. On subsequent runs, `lazydocs` will only update content within these tags:
|
|
78
|
+
|
|
79
|
+
```markdown
|
|
80
|
+
# My Project
|
|
81
|
+
|
|
82
|
+
This is a manually written description that will never be overwritten.
|
|
83
|
+
|
|
84
|
+
## 📋 Overview
|
|
85
|
+
<!-- lazydocs:start:overview -->
|
|
86
|
+
Overview content automatically managed by lazydocs.
|
|
87
|
+
<!-- lazydocs:end:overview -->
|
|
88
|
+
|
|
89
|
+
Some other manual developer notes...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Supported default anchors:
|
|
93
|
+
- `<!-- lazydocs:start:overview -->` / `<!-- lazydocs:end:overview -->`
|
|
94
|
+
- `<!-- lazydocs:start:stats -->` / `<!-- lazydocs:end:stats -->`
|
|
95
|
+
- `<!-- lazydocs:start:usage -->` / `<!-- lazydocs:end:usage -->`
|
|
96
|
+
- `<!-- lazydocs:start:api -->` / `<!-- lazydocs:end:api -->`
|
|
97
|
+
|
|
98
|
+
### 🎨 Custom Templates
|
|
99
|
+
|
|
100
|
+
Create a custom Handlebars file (e.g. `my-readme.hbs`) and specify it with the `--template` option:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
lazydocs generate --type readme --template ./my-readme.hbs
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Available variables in the template:
|
|
107
|
+
- `{{projectName}}` - Name of the project.
|
|
108
|
+
- `{{{overview}}}` - Generated overview section.
|
|
109
|
+
- `{{{usage}}}` - Generated usage guidelines.
|
|
110
|
+
- `{{stats}}` - Object containing `fileCount`, `totalLines`, `functions`, `classes`, and `complexity`.
|
|
111
|
+
- `{{apis}}` - List of parsed functions and classes containing `name` and `desc`.
|
|
112
|
+
|
|
113
|
+
### 🔀 Git PR Branch Comparisons
|
|
114
|
+
|
|
115
|
+
Generate PR descriptions by comparing custom git branches:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Compare the current branch against main
|
|
119
|
+
lazydocs generate --type pr --base main
|
|
120
|
+
|
|
121
|
+
# Compare a feature branch against production
|
|
122
|
+
lazydocs generate --type pr --base production --head feature-login
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Models
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# List available models
|
|
129
|
+
lazydocs models
|
|
130
|
+
|
|
131
|
+
# Fetch latest from API
|
|
132
|
+
lazydocs models --refresh
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Popular models:
|
|
136
|
+
- `llama-3.3-70b-versatile` (default) - Best quality
|
|
137
|
+
- `llama-3.1-8b-instant` - Fastest
|
|
138
|
+
- `mixtral-8x7b-32768` - Huge context window
|
|
139
|
+
|
|
140
|
+
## Requirements
|
|
141
|
+
|
|
142
|
+
- Node.js 18+
|
|
143
|
+
- Free Groq API key
|
|
144
|
+
|
|
145
|
+
## Links
|
|
146
|
+
|
|
147
|
+
- [NPM Package](https://www.npmjs.com/package/@tfkedar/lazydocs)
|
|
148
|
+
- [GitHub](https://github.com/kedar49/lazydocs)
|
|
149
|
+
- [Issues](https://github.com/kedar49/lazydocs/issues)
|
|
150
|
+
- [Groq Console](https://console.groq.com)
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT © [Kedar Sathe](https://github.com/kedar49)
|
package/dist/a.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
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.estimateTokenCount = void 0;
|
|
37
|
+
exports.analyzeCode = analyzeCode;
|
|
38
|
+
const parser = __importStar(require("@babel/parser"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const SUPPORTED_EXTENSIONS = ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs', '.py'];
|
|
43
|
+
// Estimate token count (1 token approximately equals 4 characters)
|
|
44
|
+
const estimateTokenCount = (text) => {
|
|
45
|
+
return Math.ceil(text.length / 4);
|
|
46
|
+
};
|
|
47
|
+
exports.estimateTokenCount = estimateTokenCount;
|
|
48
|
+
function analyzeCode(dir, maxTokens = 6000) {
|
|
49
|
+
let functions = [];
|
|
50
|
+
let classes = [];
|
|
51
|
+
let fullSnippet = '';
|
|
52
|
+
let fileCount = 0;
|
|
53
|
+
let totalLines = 0;
|
|
54
|
+
let totalSize = 0;
|
|
55
|
+
let complexitySum = 0;
|
|
56
|
+
let complexityCount = 0;
|
|
57
|
+
const fileStats = [];
|
|
58
|
+
// Load project config
|
|
59
|
+
let projectConfig = {};
|
|
60
|
+
const configPath = path.join(process.cwd(), '.lazydocs.json');
|
|
61
|
+
if (fs.existsSync(configPath)) {
|
|
62
|
+
try {
|
|
63
|
+
projectConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.warn('Failed to parse .lazydocs.json');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const allowedFileTypes = projectConfig.fileTypes || SUPPORTED_EXTENSIONS;
|
|
70
|
+
const logError = (message) => {
|
|
71
|
+
const logDir = path.join(process.cwd(), 'logs');
|
|
72
|
+
if (!fs.existsSync(logDir))
|
|
73
|
+
fs.mkdirSync(logDir);
|
|
74
|
+
const logFile = path.join(logDir, 'error.log');
|
|
75
|
+
fs.appendFileSync(logFile, `${new Date().toISOString()} - ${message}\n`);
|
|
76
|
+
};
|
|
77
|
+
function analyzeDirectory(currentDir) {
|
|
78
|
+
if (!fs.existsSync(currentDir)) {
|
|
79
|
+
logError(`Directory not found: ${currentDir}`);
|
|
80
|
+
console.warn(`Directory not found: ${currentDir}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage', '.next', '__pycache__'].includes(entry.name)) {
|
|
88
|
+
analyzeDirectory(fullPath);
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const ext = path.extname(entry.name);
|
|
93
|
+
if (!allowedFileTypes.includes(ext)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const code = fs.readFileSync(fullPath, 'utf-8');
|
|
98
|
+
const stats = fs.statSync(fullPath);
|
|
99
|
+
totalSize += stats.size;
|
|
100
|
+
const lines = code.split('\n').length;
|
|
101
|
+
totalLines += lines;
|
|
102
|
+
fileCount++;
|
|
103
|
+
let fileFunctions = 0;
|
|
104
|
+
let fileClasses = 0;
|
|
105
|
+
// Only add full code if we're under token limit for efficient API usage
|
|
106
|
+
const currentTokens = (0, exports.estimateTokenCount)(fullSnippet);
|
|
107
|
+
if (currentTokens < maxTokens) {
|
|
108
|
+
fullSnippet += `\n// File: ${path.relative(dir, fullPath)}\n${code.slice(0, 2000)}\n`;
|
|
109
|
+
}
|
|
110
|
+
// JavaScript/TypeScript analysis
|
|
111
|
+
if (['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'].includes(ext)) {
|
|
112
|
+
const plugins = ['typescript'];
|
|
113
|
+
if (ext === '.tsx' || ext === '.jsx') {
|
|
114
|
+
plugins.push('jsx');
|
|
115
|
+
}
|
|
116
|
+
const ast = parser.parse(code, {
|
|
117
|
+
sourceType: 'module',
|
|
118
|
+
plugins,
|
|
119
|
+
errorRecovery: true,
|
|
120
|
+
});
|
|
121
|
+
let localComplexity = 1;
|
|
122
|
+
ast.program.body.forEach(node => {
|
|
123
|
+
if (node.type === 'FunctionDeclaration') {
|
|
124
|
+
functions.push(node.id?.name || 'anonymous');
|
|
125
|
+
fileFunctions++;
|
|
126
|
+
localComplexity += 1;
|
|
127
|
+
}
|
|
128
|
+
if (node.type === 'ClassDeclaration') {
|
|
129
|
+
classes.push(node.id?.name || 'anonymous');
|
|
130
|
+
fileClasses++;
|
|
131
|
+
localComplexity += 2;
|
|
132
|
+
}
|
|
133
|
+
if (node.type === 'VariableDeclaration') {
|
|
134
|
+
node.declarations.forEach(decl => {
|
|
135
|
+
if (decl.init && (decl.init.type === 'ArrowFunctionExpression' || decl.init.type === 'FunctionExpression')) {
|
|
136
|
+
if (decl.id.type === 'Identifier') {
|
|
137
|
+
functions.push(decl.id.name);
|
|
138
|
+
fileFunctions++;
|
|
139
|
+
localComplexity += 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (node.type === 'IfStatement' || node.type === 'ForStatement' || node.type === 'WhileStatement') {
|
|
145
|
+
localComplexity += 1;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
complexitySum += localComplexity;
|
|
149
|
+
complexityCount += 1;
|
|
150
|
+
// Track detailed file statistics
|
|
151
|
+
fileStats.push({
|
|
152
|
+
file: path.relative(dir, fullPath),
|
|
153
|
+
lines,
|
|
154
|
+
size: stats.size,
|
|
155
|
+
functions: fileFunctions,
|
|
156
|
+
classes: fileClasses,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// Python analysis
|
|
160
|
+
if (ext === '.py') {
|
|
161
|
+
try {
|
|
162
|
+
const pythonScript = `
|
|
163
|
+
import ast
|
|
164
|
+
import json
|
|
165
|
+
import sys
|
|
166
|
+
try:
|
|
167
|
+
with open('${fullPath.replace(/\\/g, '\\\\')}', 'r', encoding='utf-8') as f:
|
|
168
|
+
tree = ast.parse(f.read())
|
|
169
|
+
functions = [node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)]
|
|
170
|
+
classes = [node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)]
|
|
171
|
+
complexity = sum(1 for node in ast.walk(tree) if isinstance(node, (ast.If, ast.For, ast.While))) + len(functions) + 2 * len(classes)
|
|
172
|
+
print(json.dumps({"functions": functions, "classes": classes, "complexity": complexity}))
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(json.dumps({"functions": [], "classes": [], "complexity": 1}))
|
|
175
|
+
`;
|
|
176
|
+
const tempFile = path.join(process.cwd(), 'temp_analyze.py');
|
|
177
|
+
fs.writeFileSync(tempFile, pythonScript);
|
|
178
|
+
const result = (0, child_process_1.execSync)('python temp_analyze.py', { encoding: 'utf-8', timeout: 5000 });
|
|
179
|
+
fs.unlinkSync(tempFile);
|
|
180
|
+
const { functions: pyFunctions, classes: pyClasses, complexity: pyComplexity } = JSON.parse(result);
|
|
181
|
+
functions.push(...pyFunctions);
|
|
182
|
+
classes.push(...pyClasses);
|
|
183
|
+
fileFunctions = pyFunctions.length;
|
|
184
|
+
fileClasses = pyClasses.length;
|
|
185
|
+
complexitySum += pyComplexity;
|
|
186
|
+
complexityCount += 1;
|
|
187
|
+
// Track Python file stats
|
|
188
|
+
fileStats.push({
|
|
189
|
+
file: path.relative(dir, fullPath),
|
|
190
|
+
lines,
|
|
191
|
+
size: stats.size,
|
|
192
|
+
functions: fileFunctions,
|
|
193
|
+
classes: fileClasses,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logError(`Python analysis failed for ${fullPath}: ${error.message}`);
|
|
198
|
+
console.warn(`Failed to analyze Python file ${fullPath}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
logError(`Failed to parse ${fullPath}: ${error.message}`);
|
|
204
|
+
console.warn(`Failed to parse ${fullPath}: ${error.message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
analyzeDirectory(dir);
|
|
209
|
+
// Build compact summary for token-efficient API calls
|
|
210
|
+
const compactSummary = buildCompactSummary(fileStats, fileCount, totalLines, totalSize, functions.length, classes.length);
|
|
211
|
+
return {
|
|
212
|
+
functions: [...new Set(functions)],
|
|
213
|
+
classes: [...new Set(classes)],
|
|
214
|
+
snippets: fullSnippet.slice(0, 8000),
|
|
215
|
+
fileCount,
|
|
216
|
+
totalLines,
|
|
217
|
+
totalSize,
|
|
218
|
+
complexity: complexityCount ? complexitySum / complexityCount : 0,
|
|
219
|
+
fileStats,
|
|
220
|
+
compactSummary,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// Build compact summary for token efficiency
|
|
224
|
+
function buildCompactSummary(fileStats, fileCount, totalLines, totalSize, totalFunctions, totalClasses) {
|
|
225
|
+
const sorted = [...fileStats].sort((a, b) => b.lines - a.lines);
|
|
226
|
+
const top = sorted.slice(0, 15); // Top 15 files by lines
|
|
227
|
+
const lines = [];
|
|
228
|
+
lines.push(`Project Summary:`);
|
|
229
|
+
lines.push(`- Files: ${fileCount}`);
|
|
230
|
+
lines.push(`- Total lines: ${totalLines.toLocaleString()}`);
|
|
231
|
+
lines.push(`- Total size: ${(totalSize / 1024).toFixed(1)} KB`);
|
|
232
|
+
lines.push(`- Functions: ${totalFunctions}`);
|
|
233
|
+
lines.push(`- Classes: ${totalClasses}`);
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push('Key files:');
|
|
236
|
+
for (const f of top) {
|
|
237
|
+
const details = [];
|
|
238
|
+
if (f.functions > 0)
|
|
239
|
+
details.push(`${f.functions} fn`);
|
|
240
|
+
if (f.classes > 0)
|
|
241
|
+
details.push(`${f.classes} cls`);
|
|
242
|
+
const detailStr = details.length > 0 ? ` (${details.join(', ')})` : '';
|
|
243
|
+
lines.push(`- ${f.file}: ${f.lines} lines${detailStr}`);
|
|
244
|
+
}
|
|
245
|
+
if (sorted.length > top.length) {
|
|
246
|
+
lines.push(`...and ${sorted.length - top.length} more files`);
|
|
247
|
+
}
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
}
|