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.
Files changed (3) hide show
  1. package/README.md +110 -0
  2. package/index.js +340 -0
  3. 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
+ }