progga 1.0.2 → 1.0.4
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/README.md +5 -5
- package/index.js +39 -79
- package/package.json +1 -1
- package/profiles/GenericProfile.js +52 -0
- package/profiles/ProfileRegistry.js +19 -0
- package/profiles/ProjectProfile.js +31 -0
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Upload one file, understand the entire project. **progga** creates a complete pr
|
|
|
25
25
|
|
|
26
26
|
### Using npx (Recommended - No Installation!)
|
|
27
27
|
```bash
|
|
28
|
-
npx progga
|
|
28
|
+
npx progga@latest
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
### Global Installation
|
|
@@ -39,24 +39,24 @@ npm install -g progga
|
|
|
39
39
|
|
|
40
40
|
Generate documentation for current directory:
|
|
41
41
|
```bash
|
|
42
|
-
npx progga
|
|
42
|
+
npx progga@latest
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
This creates `PROJECT_DOCUMENTATION.md` in your current directory.
|
|
46
46
|
|
|
47
47
|
### Specify Project Path
|
|
48
48
|
```bash
|
|
49
|
-
npx progga /path/to/your/project
|
|
49
|
+
npx progga@latest /path/to/your/project
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
### Custom Output File
|
|
53
53
|
```bash
|
|
54
|
-
npx progga . my-ai-context.md
|
|
54
|
+
npx progga@latest . my-ai-context.md
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
### Full Example
|
|
58
58
|
```bash
|
|
59
|
-
npx progga ./my-app ./docs/ai-context.md
|
|
59
|
+
npx progga@latest ./my-app ./docs/ai-context.md
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## 💡 How to Use with AI Assistants
|
package/index.js
CHANGED
|
@@ -7,65 +7,27 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const ProfileRegistry = require('./profiles/ProfileRegistry');
|
|
10
11
|
|
|
11
|
-
// Configure what to ignore
|
|
12
|
-
const IGNORE_PATTERNS = new Set([
|
|
13
|
-
'node_modules',
|
|
14
|
-
'.git',
|
|
15
|
-
'__pycache__',
|
|
16
|
-
'.vscode',
|
|
17
|
-
'dist',
|
|
18
|
-
'build',
|
|
19
|
-
'.next',
|
|
20
|
-
'venv',
|
|
21
|
-
'env',
|
|
22
|
-
'.env',
|
|
23
|
-
'coverage',
|
|
24
|
-
'.pytest_cache',
|
|
25
|
-
'.DS_Store',
|
|
26
|
-
'package-lock.json',
|
|
27
|
-
'yarn.lock',
|
|
28
|
-
'pnpm-lock.yaml',
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
// File extensions to exclude
|
|
32
|
-
const IGNORE_EXTENSIONS = new Set([
|
|
33
|
-
'.pyc',
|
|
34
|
-
'.pyo',
|
|
35
|
-
'.so',
|
|
36
|
-
'.dylib',
|
|
37
|
-
'.exe',
|
|
38
|
-
'.dll',
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
// Binary file extensions to skip
|
|
42
|
-
const BINARY_EXTENSIONS = new Set([
|
|
43
|
-
'.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg',
|
|
44
|
-
'.pdf', '.zip', '.tar', '.gz', '.rar',
|
|
45
|
-
'.mp4', '.mp3', '.wav',
|
|
46
|
-
'.woff', '.woff2', '.ttf', '.eot',
|
|
47
|
-
]);
|
|
48
12
|
|
|
49
13
|
/**
|
|
50
14
|
* Check if path should be ignored
|
|
51
15
|
*/
|
|
52
|
-
function shouldIgnore(filePath, basePath) {
|
|
16
|
+
function shouldIgnore(filePath, basePath, profile) {
|
|
53
17
|
const relativePath = path.relative(basePath, filePath);
|
|
54
18
|
const parts = relativePath.split(path.sep);
|
|
55
|
-
|
|
56
|
-
// Check each part of the path
|
|
19
|
+
|
|
57
20
|
for (const part of parts) {
|
|
58
|
-
if (
|
|
21
|
+
if (profile.ignorePaths().includes(part)) {
|
|
59
22
|
return true;
|
|
60
23
|
}
|
|
61
24
|
}
|
|
62
|
-
|
|
63
|
-
// Check file extension
|
|
25
|
+
|
|
64
26
|
const ext = path.extname(filePath);
|
|
65
|
-
if (
|
|
27
|
+
if (profile.ignoreExtensions().includes(ext)) {
|
|
66
28
|
return true;
|
|
67
29
|
}
|
|
68
|
-
|
|
30
|
+
|
|
69
31
|
return false;
|
|
70
32
|
}
|
|
71
33
|
|
|
@@ -88,7 +50,7 @@ function isDirectoryEmpty(directory, basePath) {
|
|
|
88
50
|
/**
|
|
89
51
|
* Generate tree structure recursively
|
|
90
52
|
*/
|
|
91
|
-
function generateTree(directory, prefix = '', isLast = true, basePath = null) {
|
|
53
|
+
function generateTree( directory, prefix = '', isLast = true, basePath = null, profile ) {
|
|
92
54
|
if (basePath === null) {
|
|
93
55
|
basePath = directory;
|
|
94
56
|
}
|
|
@@ -103,7 +65,7 @@ function generateTree(directory, prefix = '', isLast = true, basePath = null) {
|
|
|
103
65
|
});
|
|
104
66
|
|
|
105
67
|
// Filter ignored items
|
|
106
|
-
items = items.filter(item => !shouldIgnore(item.fullPath, basePath));
|
|
68
|
+
items = items.filter(item => !shouldIgnore(item.fullPath, basePath, profile));
|
|
107
69
|
|
|
108
70
|
// Sort: directories first, then alphabetically
|
|
109
71
|
items.sort((a, b) => {
|
|
@@ -124,7 +86,7 @@ function generateTree(directory, prefix = '', isLast = true, basePath = null) {
|
|
|
124
86
|
treeLines.push(`${prefix}${connector}${item.name}/ (empty)`);
|
|
125
87
|
} else {
|
|
126
88
|
treeLines.push(`${prefix}${connector}${item.name}/`);
|
|
127
|
-
const subtree = generateTree(item.fullPath, prefix + extension, isLastItem, basePath);
|
|
89
|
+
const subtree = generateTree( item.fullPath, prefix + extension, isLastItem, basePath, profile );
|
|
128
90
|
treeLines.push(...subtree);
|
|
129
91
|
}
|
|
130
92
|
} else {
|
|
@@ -147,28 +109,25 @@ function generateTree(directory, prefix = '', isLast = true, basePath = null) {
|
|
|
147
109
|
/**
|
|
148
110
|
* Check if file is binary
|
|
149
111
|
*/
|
|
150
|
-
function isBinaryFile(filePath) {
|
|
112
|
+
function isBinaryFile(filePath, profile) {
|
|
151
113
|
const ext = path.extname(filePath);
|
|
152
|
-
if (
|
|
114
|
+
if (profile.binaryExtensions().includes(ext)) {
|
|
153
115
|
return true;
|
|
154
116
|
}
|
|
155
|
-
|
|
117
|
+
|
|
156
118
|
try {
|
|
157
119
|
const buffer = Buffer.alloc(1024);
|
|
158
120
|
const fd = fs.openSync(filePath, 'r');
|
|
159
121
|
const bytesRead = fs.readSync(fd, buffer, 0, 1024, 0);
|
|
160
122
|
fs.closeSync(fd);
|
|
161
|
-
|
|
162
|
-
// Check for null bytes
|
|
123
|
+
|
|
163
124
|
for (let i = 0; i < bytesRead; i++) {
|
|
164
|
-
if (buffer[i] === 0)
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
125
|
+
if (buffer[i] === 0) return true;
|
|
167
126
|
}
|
|
168
|
-
} catch
|
|
127
|
+
} catch {
|
|
169
128
|
return true;
|
|
170
129
|
}
|
|
171
|
-
|
|
130
|
+
|
|
172
131
|
return false;
|
|
173
132
|
}
|
|
174
133
|
|
|
@@ -231,40 +190,36 @@ function getLanguageFromExtension(filePath) {
|
|
|
231
190
|
/**
|
|
232
191
|
* Collect all files recursively
|
|
233
192
|
*/
|
|
234
|
-
function collectFiles(directory, basePath) {
|
|
193
|
+
function collectFiles(directory, basePath, profile) {
|
|
235
194
|
const files = [];
|
|
236
|
-
|
|
195
|
+
|
|
237
196
|
try {
|
|
238
197
|
const items = fs.readdirSync(directory);
|
|
239
|
-
|
|
198
|
+
|
|
240
199
|
for (const item of items.sort()) {
|
|
241
200
|
const fullPath = path.join(directory, item);
|
|
242
|
-
|
|
243
|
-
if (shouldIgnore(fullPath, basePath))
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
201
|
+
|
|
202
|
+
if (shouldIgnore(fullPath, basePath, profile)) continue;
|
|
203
|
+
|
|
247
204
|
const stats = fs.statSync(fullPath);
|
|
248
|
-
|
|
205
|
+
|
|
249
206
|
if (stats.isFile()) {
|
|
250
|
-
if (!isBinaryFile(fullPath)) {
|
|
207
|
+
if (!isBinaryFile(fullPath, profile)) {
|
|
251
208
|
files.push(fullPath);
|
|
252
209
|
}
|
|
253
210
|
} else if (stats.isDirectory()) {
|
|
254
|
-
files.push(...collectFiles(fullPath, basePath));
|
|
211
|
+
files.push(...collectFiles(fullPath, basePath, profile));
|
|
255
212
|
}
|
|
256
213
|
}
|
|
257
|
-
} catch
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
214
|
+
} catch {}
|
|
215
|
+
|
|
261
216
|
return files;
|
|
262
217
|
}
|
|
263
218
|
|
|
264
219
|
/**
|
|
265
220
|
* Generate complete documentation markdown file
|
|
266
221
|
*/
|
|
267
|
-
function generateDocumentation(projectPath, outputFile) {
|
|
222
|
+
function generateDocumentation(projectPath, outputFile, profile) {
|
|
268
223
|
const absProjectPath = path.resolve(projectPath);
|
|
269
224
|
|
|
270
225
|
if (!fs.existsSync(absProjectPath)) {
|
|
@@ -298,7 +253,7 @@ function generateDocumentation(projectPath, outputFile) {
|
|
|
298
253
|
output += '```\n';
|
|
299
254
|
output += `${projectName}/\n`;
|
|
300
255
|
|
|
301
|
-
const treeLines = generateTree(absProjectPath, '', true, absProjectPath);
|
|
256
|
+
const treeLines = generateTree(absProjectPath, '', true, absProjectPath, profile);
|
|
302
257
|
for (const line of treeLines) {
|
|
303
258
|
output += `${line}\n`;
|
|
304
259
|
}
|
|
@@ -309,7 +264,7 @@ function generateDocumentation(projectPath, outputFile) {
|
|
|
309
264
|
// Write file contents
|
|
310
265
|
output += '## 📄 File Contents\n\n';
|
|
311
266
|
|
|
312
|
-
const files = collectFiles(absProjectPath, absProjectPath);
|
|
267
|
+
const files = collectFiles(absProjectPath, absProjectPath, profile);
|
|
313
268
|
|
|
314
269
|
for (let i = 0; i < files.length; i++) {
|
|
315
270
|
const filePath = files[i];
|
|
@@ -340,11 +295,16 @@ function generateDocumentation(projectPath, outputFile) {
|
|
|
340
295
|
// Main execution
|
|
341
296
|
function main() {
|
|
342
297
|
const args = process.argv.slice(2);
|
|
343
|
-
|
|
298
|
+
|
|
344
299
|
const projectPath = args[0] || '.';
|
|
345
300
|
const outputFile = args[1] || 'PROJECT_DOCUMENTATION.md';
|
|
346
|
-
|
|
347
|
-
|
|
301
|
+
|
|
302
|
+
// future: parse --project-type
|
|
303
|
+
const profile =
|
|
304
|
+
ProfileRegistry.getByName(null, projectPath) ||
|
|
305
|
+
ProfileRegistry.fallback(projectPath);
|
|
306
|
+
|
|
307
|
+
generateDocumentation(projectPath, outputFile, profile);
|
|
348
308
|
}
|
|
349
309
|
|
|
350
310
|
main();
|
package/package.json
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const ProjectProfile = require('./ProjectProfile');
|
|
2
|
+
|
|
3
|
+
class GenericProfile extends ProjectProfile {
|
|
4
|
+
get name() {
|
|
5
|
+
return 'generic';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
ignorePaths() {
|
|
9
|
+
return [
|
|
10
|
+
'node_modules',
|
|
11
|
+
'.git',
|
|
12
|
+
'__pycache__',
|
|
13
|
+
'.vscode',
|
|
14
|
+
'dist',
|
|
15
|
+
'build',
|
|
16
|
+
'.next',
|
|
17
|
+
'venv',
|
|
18
|
+
'env',
|
|
19
|
+
'.env',
|
|
20
|
+
'coverage',
|
|
21
|
+
'.pytest_cache',
|
|
22
|
+
'.DS_Store',
|
|
23
|
+
'package-lock.json',
|
|
24
|
+
'yarn.lock',
|
|
25
|
+
'pnpm-lock.yaml',
|
|
26
|
+
'bun.lock',
|
|
27
|
+
'.turbo'
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ignoreExtensions() {
|
|
32
|
+
return [
|
|
33
|
+
'.pyc',
|
|
34
|
+
'.pyo',
|
|
35
|
+
'.so',
|
|
36
|
+
'.dylib',
|
|
37
|
+
'.exe',
|
|
38
|
+
'.dll',
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
binaryExtensions() {
|
|
43
|
+
return [
|
|
44
|
+
'.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg',
|
|
45
|
+
'.pdf', '.zip', '.tar', '.gz', '.rar',
|
|
46
|
+
'.mp4', '.mp3', '.wav',
|
|
47
|
+
'.woff', '.woff2', '.ttf', '.eot',
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = GenericProfile;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const GenericProfile = require('./GenericProfile');
|
|
2
|
+
|
|
3
|
+
class ProfileRegistry {
|
|
4
|
+
static getByName(name, projectRoot) {
|
|
5
|
+
if (!name) return null;
|
|
6
|
+
|
|
7
|
+
if (name === 'generic') {
|
|
8
|
+
return new GenericProfile(projectRoot);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static fallback(projectRoot) {
|
|
15
|
+
return new GenericProfile(projectRoot);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = ProfileRegistry;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class ProjectProfile {
|
|
2
|
+
constructor(projectRoot) {
|
|
3
|
+
this.projectRoot = projectRoot;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
get name() {
|
|
7
|
+
return 'base';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Paths to fully ignore */
|
|
11
|
+
ignorePaths() {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Extensions to ignore */
|
|
16
|
+
ignoreExtensions() {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Binary extensions */
|
|
21
|
+
binaryExtensions() {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Should this profile auto-detect the project */
|
|
26
|
+
detect() {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = ProjectProfile;
|