progga 1.0.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/README.md +110 -0
- package/index.js +340 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# progga (প্রজ্ঞা)
|
|
2
|
+
|
|
3
|
+
> *Progga* means "wisdom" or "insight" in Bengali
|
|
4
|
+
|
|
5
|
+
Generate comprehensive project documentation in a single markdown file - perfect for sharing your entire codebase context with AI assistants like ChatGPT, Claude, or Gemini.
|
|
6
|
+
|
|
7
|
+
## 🎯 Purpose
|
|
8
|
+
|
|
9
|
+
Upload one file, understand the entire project. **progga** creates a complete project snapshot that AI assistants can instantly comprehend, making it easy to:
|
|
10
|
+
|
|
11
|
+
- 💬 Get AI help with your entire codebase
|
|
12
|
+
- 📤 Share project context without multiple file uploads
|
|
13
|
+
- 🤖 Enable ChatGPT/Claude/Gemini to understand your project structure
|
|
14
|
+
- 📚 Create comprehensive documentation snapshots
|
|
15
|
+
|
|
16
|
+
## ✨ Features
|
|
17
|
+
|
|
18
|
+
- 📁 Visual folder tree structure
|
|
19
|
+
- 📄 All file contents with syntax highlighting
|
|
20
|
+
- 🚫 Automatically ignores dependencies and build artifacts
|
|
21
|
+
- ⚡ One command, one file, complete context
|
|
22
|
+
- 🎯 Optimized for AI consumption
|
|
23
|
+
|
|
24
|
+
## 🚀 Installation
|
|
25
|
+
|
|
26
|
+
### Using npx (Recommended - No Installation!)
|
|
27
|
+
```bash
|
|
28
|
+
npx progga
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Global Installation
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g progga
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 📖 Usage
|
|
37
|
+
|
|
38
|
+
### Basic Usage
|
|
39
|
+
|
|
40
|
+
Generate documentation for current directory:
|
|
41
|
+
```bash
|
|
42
|
+
npx progga
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This creates `PROJECT_DOCUMENTATION.md` in your current directory.
|
|
46
|
+
|
|
47
|
+
### Specify Project Path
|
|
48
|
+
```bash
|
|
49
|
+
npx progga /path/to/your/project
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Custom Output File
|
|
53
|
+
```bash
|
|
54
|
+
npx progga . my-ai-context.md
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Full Example
|
|
58
|
+
```bash
|
|
59
|
+
npx progga ./my-app ./docs/ai-context.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 💡 How to Use with AI Assistants
|
|
63
|
+
|
|
64
|
+
1. Run `npx progga` in your project directory
|
|
65
|
+
2. Upload the generated `PROJECT_DOCUMENTATION.md` to ChatGPT, Claude, or Gemini
|
|
66
|
+
3. Ask the AI anything about your project!
|
|
67
|
+
|
|
68
|
+
Example prompts after upload:
|
|
69
|
+
- "Review my code architecture"
|
|
70
|
+
- "Find potential bugs"
|
|
71
|
+
- "Suggest improvements"
|
|
72
|
+
- "Explain how this project works"
|
|
73
|
+
- "Help me add a new feature"
|
|
74
|
+
|
|
75
|
+
## 🚫 What Gets Ignored
|
|
76
|
+
|
|
77
|
+
Automatically excludes:
|
|
78
|
+
- `node_modules/`, `.git/`
|
|
79
|
+
- Build directories (`dist/`, `build/`, `.next/`)
|
|
80
|
+
- Virtual environments (`venv/`, `env/`)
|
|
81
|
+
- Cache directories
|
|
82
|
+
- Lock files
|
|
83
|
+
- Binary files (images, videos, fonts)
|
|
84
|
+
|
|
85
|
+
## 📊 Output Format
|
|
86
|
+
```markdown
|
|
87
|
+
# Project Documentation: your-project
|
|
88
|
+
|
|
89
|
+
## 📁 Folder Structure
|
|
90
|
+
[Visual tree of all files and folders]
|
|
91
|
+
|
|
92
|
+
## 📄 File Contents
|
|
93
|
+
[Complete contents of each file with syntax highlighting]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 🌍 Requirements
|
|
97
|
+
|
|
98
|
+
- Node.js >= 12.0.0
|
|
99
|
+
|
|
100
|
+
## 📝 License
|
|
101
|
+
|
|
102
|
+
MIT
|
|
103
|
+
|
|
104
|
+
## 🤝 Contributing
|
|
105
|
+
|
|
106
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
107
|
+
|
|
108
|
+
## 💖 Name Origin
|
|
109
|
+
|
|
110
|
+
**Progga** (প্রজ্ঞা) is a Bengali word meaning "wisdom" or "insight" - representing the wisdom you share with AI assistants about your codebase.
|
package/index.js
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Project Documentation Generator
|
|
5
|
+
* Generates a single markdown file with folder structure and all file contents
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
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
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if path should be ignored
|
|
51
|
+
*/
|
|
52
|
+
function shouldIgnore(filePath, basePath) {
|
|
53
|
+
const relativePath = path.relative(basePath, filePath);
|
|
54
|
+
const parts = relativePath.split(path.sep);
|
|
55
|
+
|
|
56
|
+
// Check each part of the path
|
|
57
|
+
for (const part of parts) {
|
|
58
|
+
if (IGNORE_PATTERNS.has(part)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check file extension
|
|
64
|
+
const ext = path.extname(filePath);
|
|
65
|
+
if (IGNORE_EXTENSIONS.has(ext)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if directory is empty (ignoring ignored items)
|
|
74
|
+
*/
|
|
75
|
+
function isDirectoryEmpty(directory, basePath) {
|
|
76
|
+
try {
|
|
77
|
+
const items = fs.readdirSync(directory);
|
|
78
|
+
const validItems = items.filter(item => {
|
|
79
|
+
const fullPath = path.join(directory, item);
|
|
80
|
+
return !shouldIgnore(fullPath, basePath);
|
|
81
|
+
});
|
|
82
|
+
return validItems.length === 0;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate tree structure recursively
|
|
90
|
+
*/
|
|
91
|
+
function generateTree(directory, prefix = '', isLast = true, basePath = null) {
|
|
92
|
+
if (basePath === null) {
|
|
93
|
+
basePath = directory;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const treeLines = [];
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
let items = fs.readdirSync(directory).map(name => {
|
|
100
|
+
const fullPath = path.join(directory, name);
|
|
101
|
+
const stats = fs.statSync(fullPath);
|
|
102
|
+
return { name, fullPath, isDir: stats.isDirectory() };
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Filter ignored items
|
|
106
|
+
items = items.filter(item => !shouldIgnore(item.fullPath, basePath));
|
|
107
|
+
|
|
108
|
+
// Sort: directories first, then alphabetically
|
|
109
|
+
items.sort((a, b) => {
|
|
110
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
111
|
+
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < items.length; i++) {
|
|
115
|
+
const item = items[i];
|
|
116
|
+
const isLastItem = i === items.length - 1;
|
|
117
|
+
|
|
118
|
+
// Tree characters
|
|
119
|
+
const connector = isLastItem ? '└── ' : '├── ';
|
|
120
|
+
const extension = isLastItem ? ' ' : '│ ';
|
|
121
|
+
|
|
122
|
+
if (item.isDir) {
|
|
123
|
+
if (isDirectoryEmpty(item.fullPath, basePath)) {
|
|
124
|
+
treeLines.push(`${prefix}${connector}${item.name}/ (empty)`);
|
|
125
|
+
} else {
|
|
126
|
+
treeLines.push(`${prefix}${connector}${item.name}/`);
|
|
127
|
+
const subtree = generateTree(item.fullPath, prefix + extension, isLastItem, basePath);
|
|
128
|
+
treeLines.push(...subtree);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// Check if file is empty
|
|
132
|
+
const stats = fs.statSync(item.fullPath);
|
|
133
|
+
if (stats.size === 0) {
|
|
134
|
+
treeLines.push(`${prefix}${connector}${item.name} (empty)`);
|
|
135
|
+
} else {
|
|
136
|
+
treeLines.push(`${prefix}${connector}${item.name}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
// Permission error or other issues
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return treeLines;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Check if file is binary
|
|
149
|
+
*/
|
|
150
|
+
function isBinaryFile(filePath) {
|
|
151
|
+
const ext = path.extname(filePath);
|
|
152
|
+
if (BINARY_EXTENSIONS.has(ext)) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const buffer = Buffer.alloc(1024);
|
|
158
|
+
const fd = fs.openSync(filePath, 'r');
|
|
159
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 1024, 0);
|
|
160
|
+
fs.closeSync(fd);
|
|
161
|
+
|
|
162
|
+
// Check for null bytes
|
|
163
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
164
|
+
if (buffer[i] === 0) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read file content safely
|
|
177
|
+
*/
|
|
178
|
+
function readFileContent(filePath) {
|
|
179
|
+
try {
|
|
180
|
+
const stats = fs.statSync(filePath);
|
|
181
|
+
if (stats.size === 0) {
|
|
182
|
+
return '(empty file)';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
186
|
+
} catch (err) {
|
|
187
|
+
if (err.message.includes('invalid')) {
|
|
188
|
+
try {
|
|
189
|
+
return fs.readFileSync(filePath, 'latin1');
|
|
190
|
+
} catch (e) {
|
|
191
|
+
return '[Error: Unable to read file]';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return `[Error: ${err.message}]`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get markdown language identifier from file extension
|
|
200
|
+
*/
|
|
201
|
+
function getLanguageFromExtension(filePath) {
|
|
202
|
+
const extMap = {
|
|
203
|
+
'.py': 'python',
|
|
204
|
+
'.js': 'javascript',
|
|
205
|
+
'.jsx': 'jsx',
|
|
206
|
+
'.ts': 'typescript',
|
|
207
|
+
'.tsx': 'tsx',
|
|
208
|
+
'.html': 'html',
|
|
209
|
+
'.css': 'css',
|
|
210
|
+
'.scss': 'scss',
|
|
211
|
+
'.json': 'json',
|
|
212
|
+
'.md': 'markdown',
|
|
213
|
+
'.yml': 'yaml',
|
|
214
|
+
'.yaml': 'yaml',
|
|
215
|
+
'.sh': 'bash',
|
|
216
|
+
'.bash': 'bash',
|
|
217
|
+
'.sql': 'sql',
|
|
218
|
+
'.java': 'java',
|
|
219
|
+
'.cpp': 'cpp',
|
|
220
|
+
'.c': 'c',
|
|
221
|
+
'.go': 'go',
|
|
222
|
+
'.rs': 'rust',
|
|
223
|
+
'.rb': 'ruby',
|
|
224
|
+
'.php': 'php',
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const ext = path.extname(filePath);
|
|
228
|
+
return extMap[ext] || '';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Collect all files recursively
|
|
233
|
+
*/
|
|
234
|
+
function collectFiles(directory, basePath) {
|
|
235
|
+
const files = [];
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const items = fs.readdirSync(directory);
|
|
239
|
+
|
|
240
|
+
for (const item of items.sort()) {
|
|
241
|
+
const fullPath = path.join(directory, item);
|
|
242
|
+
|
|
243
|
+
if (shouldIgnore(fullPath, basePath)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const stats = fs.statSync(fullPath);
|
|
248
|
+
|
|
249
|
+
if (stats.isFile()) {
|
|
250
|
+
if (!isBinaryFile(fullPath)) {
|
|
251
|
+
files.push(fullPath);
|
|
252
|
+
}
|
|
253
|
+
} else if (stats.isDirectory()) {
|
|
254
|
+
files.push(...collectFiles(fullPath, basePath));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
// Permission error or other issues
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return files;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Generate complete documentation markdown file
|
|
266
|
+
*/
|
|
267
|
+
function generateDocumentation(projectPath, outputFile) {
|
|
268
|
+
const absProjectPath = path.resolve(projectPath);
|
|
269
|
+
|
|
270
|
+
if (!fs.existsSync(absProjectPath)) {
|
|
271
|
+
console.error(`Error: Path '${absProjectPath}' does not exist`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log(`Generating documentation for: ${absProjectPath}`);
|
|
276
|
+
console.log(`Output file: ${outputFile}`);
|
|
277
|
+
|
|
278
|
+
const projectName = path.basename(absProjectPath);
|
|
279
|
+
let output = '';
|
|
280
|
+
|
|
281
|
+
// Write header
|
|
282
|
+
output += `# Project Documentation: ${projectName}\n\n`;
|
|
283
|
+
output += `**Generated from:** \`${absProjectPath}\`\n\n`;
|
|
284
|
+
output += '---\n\n';
|
|
285
|
+
|
|
286
|
+
// Write folder structure
|
|
287
|
+
output += '## 📁 Folder Structure\n\n';
|
|
288
|
+
output += '```\n';
|
|
289
|
+
output += `${projectName}/\n`;
|
|
290
|
+
|
|
291
|
+
const treeLines = generateTree(absProjectPath, '', true, absProjectPath);
|
|
292
|
+
for (const line of treeLines) {
|
|
293
|
+
output += `${line}\n`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
output += '```\n\n';
|
|
297
|
+
output += '---\n\n';
|
|
298
|
+
|
|
299
|
+
// Write file contents
|
|
300
|
+
output += '## 📄 File Contents\n\n';
|
|
301
|
+
|
|
302
|
+
const files = collectFiles(absProjectPath, absProjectPath);
|
|
303
|
+
|
|
304
|
+
for (let i = 0; i < files.length; i++) {
|
|
305
|
+
const filePath = files[i];
|
|
306
|
+
const relPath = path.relative(absProjectPath, filePath);
|
|
307
|
+
console.log(`Processing (${i + 1}/${files.length}): ${relPath}`);
|
|
308
|
+
|
|
309
|
+
output += `### \`${relPath}\`\n\n`;
|
|
310
|
+
|
|
311
|
+
const content = readFileContent(filePath);
|
|
312
|
+
const language = getLanguageFromExtension(filePath);
|
|
313
|
+
|
|
314
|
+
output += `\`\`\`${language}\n`;
|
|
315
|
+
output += content;
|
|
316
|
+
if (!content.endsWith('\n')) {
|
|
317
|
+
output += '\n';
|
|
318
|
+
}
|
|
319
|
+
output += '```\n\n';
|
|
320
|
+
output += '---\n\n';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Write to file
|
|
324
|
+
fs.writeFileSync(outputFile, output, 'utf-8');
|
|
325
|
+
|
|
326
|
+
console.log(`\n✅ Documentation generated successfully: ${outputFile}`);
|
|
327
|
+
console.log(`📊 Total files processed: ${files.length}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Main execution
|
|
331
|
+
function main() {
|
|
332
|
+
const args = process.argv.slice(2);
|
|
333
|
+
|
|
334
|
+
const projectPath = args[0] || '.';
|
|
335
|
+
const outputFile = args[1] || 'PROJECT_DOCUMENTATION.md';
|
|
336
|
+
|
|
337
|
+
generateDocumentation(projectPath, outputFile);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "progga",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generate comprehensive project documentation for AI assistants - Share your entire codebase context in one markdown file",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"progga": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node index.js ."
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ai",
|
|
14
|
+
"documentation",
|
|
15
|
+
"generator",
|
|
16
|
+
"markdown",
|
|
17
|
+
"project",
|
|
18
|
+
"context",
|
|
19
|
+
"chatgpt",
|
|
20
|
+
"claude",
|
|
21
|
+
"gemini",
|
|
22
|
+
"llm",
|
|
23
|
+
"codebase",
|
|
24
|
+
"snapshot",
|
|
25
|
+
"cli",
|
|
26
|
+
"bengali"
|
|
27
|
+
],
|
|
28
|
+
"author": "Your Name yousufbasir@outlook.com",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=12.0.0"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Yousuf-Basir/progga"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/Yousuf-Basir/progga/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/Yousuf-Basir/progga#readme"
|
|
41
|
+
}
|