readmeg 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 +217 -0
- package/bin/readmeg.js +679 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<a name="readme-top"></a>
|
|
2
|
+
|
|
3
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
4
|
+
[![Forks][forks-shield]][forks-url]
|
|
5
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
6
|
+
[![Issues][issues-shield]][issues-url]
|
|
7
|
+
[![MIT License][license-shield]][license-url]
|
|
8
|
+
[![LinkedIn][linkedin-shield]][linkedin-url]
|
|
9
|
+
|
|
10
|
+
<br />
|
|
11
|
+
<div align="center">
|
|
12
|
+
<a href="https://github.com/your_username/readmeg">
|
|
13
|
+
<img src="https://images.unsplash.com/photo-1618401471353-b98aedd07871?q=80&w=120&auto=format&fit=crop" alt="Logo" width="80" height="80">
|
|
14
|
+
</a>
|
|
15
|
+
|
|
16
|
+
<h3 align="center">readmeg</h3>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
An AI-powered CLI tool to instantly generate beautiful, comprehensive, and professional README.md files for your repositories.
|
|
20
|
+
<br />
|
|
21
|
+
<a href="https://github.com/your_username/readmeg"><strong>Explore the docs »</strong></a>
|
|
22
|
+
<br />
|
|
23
|
+
<br />
|
|
24
|
+
<a href="https://github.com/your_username/readmeg">View Demo</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="https://github.com/your_username/readmeg/issues">Report Bug</a>
|
|
27
|
+
·
|
|
28
|
+
<a href="https://github.com/your_username/readmeg/issues">Request Feature</a>
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary>Table of Contents</summary>
|
|
34
|
+
<ol>
|
|
35
|
+
<li>
|
|
36
|
+
<a href="#about-the-project">About The Project</a>
|
|
37
|
+
<ul>
|
|
38
|
+
<li><a href="#built-with">Built With</a></li>
|
|
39
|
+
</ul>
|
|
40
|
+
</li>
|
|
41
|
+
<li>
|
|
42
|
+
<a href="#getting-started">Getting Started</a>
|
|
43
|
+
<ul>
|
|
44
|
+
<li><a href="#prerequisites">Prerequisites</a></li>
|
|
45
|
+
<li><a href="#installation">Installation</a></li>
|
|
46
|
+
</ul>
|
|
47
|
+
</li>
|
|
48
|
+
<li><a href="#usage">Usage</a></li>
|
|
49
|
+
<li><a href="#roadmap">Roadmap</a></li>
|
|
50
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
51
|
+
<li><a href="#license">License</a></li>
|
|
52
|
+
<li><a href="#contact">Contact</a></li>
|
|
53
|
+
<li><a href="#acknowledgments">Acknowledgments</a></li>
|
|
54
|
+
</ol>
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
## About The Project
|
|
58
|
+
|
|
59
|
+
[![readmeg Screen Shot][product-screenshot]](https://github.com/your_username/readmeg)
|
|
60
|
+
|
|
61
|
+
Writing a high-quality README is often the most tedious part of finishing a project. `readmeg` is an interactive Command Line Interface (CLI) tool designed to automate this process. By leveraging Google's advanced Gemini generative AI models, `readmeg` analyzes your project structure, prompts you for key details, and outputs a perfectly formatted, professional `README.md` file in seconds.
|
|
62
|
+
|
|
63
|
+
Here's why you should use `readmeg`:
|
|
64
|
+
* **Save Time**: Stop writing boilerplate markdown sections from scratch.
|
|
65
|
+
* **Interactive Prompts**: Simple, step-by-step terminal questions guide you through defining your project's scope.
|
|
66
|
+
* **AI-Powered**: Generates intelligent descriptions, installation steps, and usage guides based on your actual project configuration.
|
|
67
|
+
* **Standardized Layouts**: Outputs clean markdown following industry-standard templates.
|
|
68
|
+
|
|
69
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
70
|
+
|
|
71
|
+
### Built With
|
|
72
|
+
|
|
73
|
+
This project is built using modern Node.js and TypeScript, leveraging powerful CLI and AI libraries:
|
|
74
|
+
|
|
75
|
+
* [![Node.js][Node.js]][Node-url]
|
|
76
|
+
* [![TypeScript][TypeScript]][TypeScript-url]
|
|
77
|
+
* [![Google Gemini][Gemini]][Gemini-url]
|
|
78
|
+
* [![Commander.js][Commander]][Commander-url]
|
|
79
|
+
* [![Inquirer.js][Inquirer]][Inquirer-url]
|
|
80
|
+
|
|
81
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
82
|
+
|
|
83
|
+
## Getting Started
|
|
84
|
+
|
|
85
|
+
To get a local copy up and running, follow these simple steps.
|
|
86
|
+
|
|
87
|
+
### Prerequisites
|
|
88
|
+
|
|
89
|
+
Ensure you have Node.js installed (version 18.0.0 or higher is recommended).
|
|
90
|
+
|
|
91
|
+
* npm
|
|
92
|
+
```sh
|
|
93
|
+
npm install npm@latest -g
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Installation
|
|
97
|
+
|
|
98
|
+
1. Get a free Gemini API Key from [Google AI Studio](https://aistudio.google.com/).
|
|
99
|
+
2. Clone the repository:
|
|
100
|
+
```sh
|
|
101
|
+
git clone https://github.com/your_username/readmeg.git
|
|
102
|
+
```
|
|
103
|
+
3. Install NPM packages:
|
|
104
|
+
```sh
|
|
105
|
+
npm install
|
|
106
|
+
```
|
|
107
|
+
4. Create a `.env` file in the root directory and add your Gemini API key:
|
|
108
|
+
```env
|
|
109
|
+
GEMINI_API_KEY=your_actual_api_key_here
|
|
110
|
+
```
|
|
111
|
+
5. Build the TypeScript files (if modifying source files):
|
|
112
|
+
```sh
|
|
113
|
+
npm run build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
You can run `readmeg` directly using Node.js. The CLI will guide you through an interactive prompt session to gather information about your repository before generating the markdown file.
|
|
121
|
+
|
|
122
|
+
### Running the CLI
|
|
123
|
+
|
|
124
|
+
To start the interactive generation process:
|
|
125
|
+
```sh
|
|
126
|
+
node ./bin/readmeg.js
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
To view available CLI commands and options:
|
|
130
|
+
```sh
|
|
131
|
+
npm start
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Example Interactive Flow
|
|
135
|
+
|
|
136
|
+
1. **Project Name**: Enter the name of your project (defaults to your directory name).
|
|
137
|
+
2. **Description**: Provide a brief summary of what your application does.
|
|
138
|
+
3. **Technologies**: Select or enter the main frameworks and languages used.
|
|
139
|
+
4. **License**: Choose your project's license (e.g., MIT, Apache 2.0, GPL v3).
|
|
140
|
+
5. **Output**: The tool will call the Gemini API and write a polished `README.md` directly to your root directory.
|
|
141
|
+
|
|
142
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
143
|
+
|
|
144
|
+
## Roadmap
|
|
145
|
+
|
|
146
|
+
- [ ] Add support for automatic codebase parsing (reading `package.json`, `requirements.txt`, etc.)
|
|
147
|
+
- [ ] Provide multiple pre-designed README templates (Minimal, Detailed, API-focused)
|
|
148
|
+
- [ ] Add support for alternative LLM providers (OpenAI, Anthropic)
|
|
149
|
+
- [ ] Implement a dry-run mode to preview markdown in the terminal before writing to disk
|
|
150
|
+
|
|
151
|
+
See the [open issues](https://github.com/your_username/readmeg/issues) for a full list of proposed features and known issues.
|
|
152
|
+
|
|
153
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
154
|
+
|
|
155
|
+
## Contributing
|
|
156
|
+
|
|
157
|
+
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
|
158
|
+
|
|
159
|
+
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
|
|
160
|
+
Don't forget to give the project a star! Thanks again!
|
|
161
|
+
|
|
162
|
+
1. Fork the Project
|
|
163
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
164
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
165
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
166
|
+
5. Open a Pull Request
|
|
167
|
+
|
|
168
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
173
|
+
|
|
174
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
175
|
+
|
|
176
|
+
## Contact
|
|
177
|
+
|
|
178
|
+
Your Name - [@your_twitter](https://twitter.com/your_twitter) - email@example.com
|
|
179
|
+
|
|
180
|
+
Project Link: [https://github.com/your_username/readmeg](https://github.com/your_username/readmeg)
|
|
181
|
+
|
|
182
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
183
|
+
|
|
184
|
+
## Acknowledgments
|
|
185
|
+
|
|
186
|
+
* [Google Gen AI SDK](https://github.com/google/generative-ai-js)
|
|
187
|
+
* [Commander.js](https://github.com/tj/commander.js/)
|
|
188
|
+
* [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/)
|
|
189
|
+
* [Shields.io](https://shields.io)
|
|
190
|
+
* [Best-README-Template](https://github.com/othneildrew/Best-README-Template)
|
|
191
|
+
|
|
192
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
193
|
+
|
|
194
|
+
[contributors-shield]: https://img.shields.io/github/contributors/your_username/readmeg.svg?style=for-the-badge
|
|
195
|
+
[contributors-url]: https://github.com/your_username/readmeg/graphs/contributors
|
|
196
|
+
[forks-shield]: https://img.shields.io/github/forks/your_username/readmeg.svg?style=for-the-badge
|
|
197
|
+
[forks-url]: https://github.com/your_username/readmeg/network/members
|
|
198
|
+
[stars-shield]: https://img.shields.io/github/stars/your_username/readmeg.svg?style=for-the-badge
|
|
199
|
+
[stars-url]: https://github.com/your_username/readmeg/stargazers
|
|
200
|
+
[issues-shield]: https://img.shields.io/github/issues/your_username/readmeg.svg?style=for-the-badge
|
|
201
|
+
[issues-url]: https://github.com/your_username/readmeg/issues
|
|
202
|
+
[license-shield]: https://img.shields.io/github/license/your_username/readmeg.svg?style=for-the-badge
|
|
203
|
+
[license-url]: https://github.com/your_username/readmeg/blob/master/LICENSE.txt
|
|
204
|
+
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
|
|
205
|
+
[linkedin-url]: https://linkedin.com/in/your_username
|
|
206
|
+
[product-screenshot]: https://images.unsplash.com/photo-1618401471353-b98aedd07871?q=80&w=600&auto=format&fit=crop
|
|
207
|
+
|
|
208
|
+
[Node.js]: https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white
|
|
209
|
+
[Node-url]: https://nodejs.org/
|
|
210
|
+
[TypeScript]: https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white
|
|
211
|
+
[TypeScript-url]: https://www.typescriptlang.org/
|
|
212
|
+
[Gemini]: https://img.shields.io/badge/Google%20Gemini-8E75C2?style=for-the-badge&logo=googlegemini&logoColor=white
|
|
213
|
+
[Gemini-url]: https://deepmind.google/technologies/gemini/
|
|
214
|
+
[Commander]: https://img.shields.io/badge/Commander.js-black?style=for-the-badge&logo=commander
|
|
215
|
+
[Commander-url]: https://github.com/tj/commander.js/
|
|
216
|
+
[Inquirer]: https://img.shields.io/badge/Inquirer.js-red?style=for-the-badge&logo=npm
|
|
217
|
+
[Inquirer-url]: https://github.com/SBoudrias/Inquirer.js/
|
package/bin/readmeg.js
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ReadMe-Gen CLI
|
|
5
|
+
* Zero-config terminal execution tool powered by Gemini with interactive menus.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import inquirer from "inquirer";
|
|
12
|
+
import { GoogleGenAI } from "@google/genai";
|
|
13
|
+
import dotenv from "dotenv";
|
|
14
|
+
|
|
15
|
+
// Load local .env configurations if present
|
|
16
|
+
dotenv.config();
|
|
17
|
+
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name("readmeg")
|
|
22
|
+
.description("Automatically generate comprehensive production-grade README.md files for your local directory repositories using Gemini.")
|
|
23
|
+
.version("1.0.0")
|
|
24
|
+
.option("-k, --api-key <key>", "Provide your Gemini API key manually")
|
|
25
|
+
.option("-o, --output <file>", "Define custom output file path", "README.md")
|
|
26
|
+
.option("-n, --non-interactive", "Run directly without interactive menu loop", false)
|
|
27
|
+
.parse(process.argv);
|
|
28
|
+
|
|
29
|
+
const options = program.opts();
|
|
30
|
+
|
|
31
|
+
const IGNORED_DIRS = new Set([
|
|
32
|
+
".git",
|
|
33
|
+
"node_modules",
|
|
34
|
+
"dist",
|
|
35
|
+
"build",
|
|
36
|
+
"__pycache__",
|
|
37
|
+
".venv",
|
|
38
|
+
"venv",
|
|
39
|
+
"env",
|
|
40
|
+
".aistudio",
|
|
41
|
+
"assets",
|
|
42
|
+
".next",
|
|
43
|
+
".svelte-kit",
|
|
44
|
+
".nuxt",
|
|
45
|
+
".cache",
|
|
46
|
+
"tmp",
|
|
47
|
+
".DS_Store"
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const MANIFEST_NAMES = new Set([
|
|
51
|
+
"package.json",
|
|
52
|
+
"requirements.txt",
|
|
53
|
+
"go.mod",
|
|
54
|
+
"Cargo.toml",
|
|
55
|
+
"docker-compose.yml",
|
|
56
|
+
"pyproject.toml",
|
|
57
|
+
"Gemfile",
|
|
58
|
+
"pom.xml",
|
|
59
|
+
"build.gradle",
|
|
60
|
+
"composer.json",
|
|
61
|
+
"setup.py"
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const SNIPPET_NAMES = new Set([
|
|
65
|
+
"server.ts",
|
|
66
|
+
"main.py",
|
|
67
|
+
"main.go",
|
|
68
|
+
"main.rs",
|
|
69
|
+
"App.tsx",
|
|
70
|
+
"main.tsx",
|
|
71
|
+
"index.html",
|
|
72
|
+
"index.js",
|
|
73
|
+
"index.ts",
|
|
74
|
+
"App.js"
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
// 1. Traverse directory to form visual path layout
|
|
78
|
+
function buildTreeString(currentPath, prefix = "") {
|
|
79
|
+
try {
|
|
80
|
+
const stats = fs.statSync(currentPath);
|
|
81
|
+
if (!stats.isDirectory()) return "";
|
|
82
|
+
|
|
83
|
+
const items = fs.readdirSync(currentPath)
|
|
84
|
+
.filter((item) => !IGNORED_DIRS.has(item) && !item.startsWith("."))
|
|
85
|
+
.sort((a, b) => {
|
|
86
|
+
const aPath = path.join(currentPath, a);
|
|
87
|
+
const bPath = path.join(currentPath, b);
|
|
88
|
+
let aIsDir = false, bIsDir = false;
|
|
89
|
+
try {
|
|
90
|
+
aIsDir = fs.statSync(aPath).isDirectory();
|
|
91
|
+
bIsDir = fs.statSync(bPath).isDirectory();
|
|
92
|
+
} catch (_) {}
|
|
93
|
+
if (aIsDir && !bIsDir) return -1;
|
|
94
|
+
if (!aIsDir && bIsDir) return 1;
|
|
95
|
+
return a.localeCompare(b);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let tree = "";
|
|
99
|
+
for (let i = 0; i < items.length; i++) {
|
|
100
|
+
const item = items[i];
|
|
101
|
+
const itemPath = path.join(currentPath, item);
|
|
102
|
+
const isLast = i === items.length - 1;
|
|
103
|
+
const connector = isLast ? "└── " : "├── ";
|
|
104
|
+
|
|
105
|
+
let isDir = false;
|
|
106
|
+
try {
|
|
107
|
+
isDir = fs.statSync(itemPath).isDirectory();
|
|
108
|
+
} catch (_) {}
|
|
109
|
+
|
|
110
|
+
tree += `${prefix}${connector}${item}${isDir ? "/" : ""}\n`;
|
|
111
|
+
|
|
112
|
+
if (isDir) {
|
|
113
|
+
const nextPrefix = prefix + (isLast ? " " : "│ ");
|
|
114
|
+
tree += buildTreeString(itemPath, nextPrefix);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return tree;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.warn(`\x1b[33m[Warning] Failed to generate tree for directory '${currentPath}': ${err.message}\x1b[0m`);
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 2. Traverses and reads file information
|
|
125
|
+
function scanDirectoryFiles(currentPath, relativePath, result) {
|
|
126
|
+
try {
|
|
127
|
+
const items = fs.readdirSync(currentPath);
|
|
128
|
+
for (const item of items) {
|
|
129
|
+
if (IGNORED_DIRS.has(item) || item.startsWith(".")) continue;
|
|
130
|
+
|
|
131
|
+
const itemPath = path.join(currentPath, item);
|
|
132
|
+
const itemRelPath = relativePath ? path.join(relativePath, item) : item;
|
|
133
|
+
const stats = fs.statSync(itemPath);
|
|
134
|
+
|
|
135
|
+
if (stats.isDirectory()) {
|
|
136
|
+
scanDirectoryFiles(itemPath, itemRelPath, result);
|
|
137
|
+
} else if (stats.isFile()) {
|
|
138
|
+
const ext = path.extname(item).toLowerCase();
|
|
139
|
+
|
|
140
|
+
if (ext === ".ts" || ext === ".tsx") result.languages.add("TypeScript");
|
|
141
|
+
else if (ext === ".js" || ext === ".jsx") result.languages.add("JavaScript");
|
|
142
|
+
else if (ext === ".py") result.languages.add("Python");
|
|
143
|
+
else if (ext === ".go") result.languages.add("Go");
|
|
144
|
+
else if (ext === ".rs") result.languages.add("Rust");
|
|
145
|
+
|
|
146
|
+
if (MANIFEST_NAMES.has(item)) {
|
|
147
|
+
const content = fs.readFileSync(itemPath, "utf-8").substring(0, 10000);
|
|
148
|
+
result.manifests.push({ relativePath: itemRelPath, name: item, content });
|
|
149
|
+
} else if (SNIPPET_NAMES.has(item) && stats.size < 12000) {
|
|
150
|
+
const content = fs.readFileSync(itemPath, "utf-8");
|
|
151
|
+
result.snippets.push({ relativePath: itemRelPath, name: item, content });
|
|
152
|
+
} else if (ext === ".md" && item.toLowerCase() !== "readme.md" && stats.size < 15000) {
|
|
153
|
+
const content = fs.readFileSync(itemPath, "utf-8");
|
|
154
|
+
result.markdowns.push({ relativePath: itemRelPath, name: item, content });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error(`\x1b[31m[Error] Failed to read directory '${currentPath}': ${err.message}\x1b[0m`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const showBanner = () => {
|
|
164
|
+
console.log(`\x1b[36m
|
|
165
|
+
_____ _ __ __
|
|
166
|
+
| __ \\ | | \\/ |
|
|
167
|
+
| |__) |___ __ _ __| | |\\/| |/ _ \\/ _\` |
|
|
168
|
+
| _ // _ \\/ _\` |/ _\` | | | | __/ (_| |
|
|
169
|
+
| | \\ \\ __/ (_| | (_| | | | | \\___|\\__, |
|
|
170
|
+
|_| \\_\\___|\\__,_|\\__,_|_| |_| |___/
|
|
171
|
+
\x1b[0m`);
|
|
172
|
+
console.log(` \x1b[45m\x1b[37m ReadMeg — Interactive CLI v1.0.0 \x1b[0m Powered by Google Gemini AI\n`);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
async function main() {
|
|
176
|
+
if (options.nonInteractive) {
|
|
177
|
+
// Standard direct execution if specified --non-interactive
|
|
178
|
+
await runDirectScanAndGen();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
showBanner();
|
|
183
|
+
|
|
184
|
+
// 1. Core API KEY Resolution
|
|
185
|
+
let apiKey = options.apiKey || process.env.GEMINI_API_KEY;
|
|
186
|
+
if (!apiKey || !apiKey.trim()) {
|
|
187
|
+
console.log("\x1b[33mGemini API Key is not configured yet or is empty.\x1b[0m");
|
|
188
|
+
const keyAnswer = await inquirer.prompt([
|
|
189
|
+
{
|
|
190
|
+
type: "password",
|
|
191
|
+
name: "key",
|
|
192
|
+
message: "Please enter your Google Gemini API Key:",
|
|
193
|
+
mask: "*"
|
|
194
|
+
}
|
|
195
|
+
]);
|
|
196
|
+
apiKey = keyAnswer.key;
|
|
197
|
+
|
|
198
|
+
if (!apiKey || !apiKey.trim()) {
|
|
199
|
+
console.error("\x1b[31mERROR: GEMINI_API_KEY is required to generate README files.\x1b[0m");
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
apiKey = apiKey.trim();
|
|
204
|
+
|
|
205
|
+
// 2. Active directory scanner
|
|
206
|
+
console.log("\n\x1b[36mScanning repository layers...\x1b[0m");
|
|
207
|
+
const activeCwd = process.cwd();
|
|
208
|
+
const treeBody = buildTreeString(activeCwd);
|
|
209
|
+
const scanResult = {
|
|
210
|
+
manifests: [],
|
|
211
|
+
snippets: [],
|
|
212
|
+
markdowns: [],
|
|
213
|
+
languages: new Set()
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
scanDirectoryFiles(activeCwd, "", scanResult);
|
|
217
|
+
|
|
218
|
+
let projectType = "Unknown Project Core";
|
|
219
|
+
if (scanResult.manifests.some(m => m.name === "package.json")) projectType = "Node.js (JavaScript/TypeScript)";
|
|
220
|
+
else if (scanResult.manifests.some(m => m.name === "requirements.txt" || m.name === "pyproject.toml")) projectType = "Python";
|
|
221
|
+
else if (scanResult.manifests.some(m => m.name === "go.mod")) projectType = "Go (Golang)";
|
|
222
|
+
else if (scanResult.manifests.some(m => m.name === "Cargo.toml")) projectType = "Rust";
|
|
223
|
+
|
|
224
|
+
console.log(`\x1b[32mSuccessfully mapped codebase structure as:\x1b[0m ${projectType}`);
|
|
225
|
+
console.log(`Tree Depth loaded. Captured manifests: ${scanResult.manifests.map(m => m.name).join(", ") || "None"}\n`);
|
|
226
|
+
|
|
227
|
+
// CLI Selection Loop states
|
|
228
|
+
let selectedManifestPaths = scanResult.manifests.map(m => m.relativePath);
|
|
229
|
+
let selectedSnippetPaths = scanResult.snippets.map(s => s.relativePath);
|
|
230
|
+
let selectedMarkdownPaths = scanResult.markdowns.map(md => md.relativePath);
|
|
231
|
+
let addGuidelines = [];
|
|
232
|
+
let customInstructions = "";
|
|
233
|
+
let chosenModel = "gemini-3.5-flash";
|
|
234
|
+
let generatedContent = "";
|
|
235
|
+
|
|
236
|
+
// Interactive Menu Loop
|
|
237
|
+
while (true) {
|
|
238
|
+
const listChoices = [
|
|
239
|
+
{ name: "View scanned File Tree diagram", value: "view_tree" },
|
|
240
|
+
{ name: "Select configuration manifest files to analyze", value: "select_manifests" },
|
|
241
|
+
{ name: "Select code snippet guides to inspect", value: "select_snippets" },
|
|
242
|
+
{ name: "Select reference markdown files to inspect", value: "select_markdowns" },
|
|
243
|
+
{ name: "Select supplementary layout headings", value: "select_headings" },
|
|
244
|
+
{ name: "Enter custom text highlight instructions", value: "custom_notes" },
|
|
245
|
+
{ name: "GENERATE README DOCUMENT RIGHT NOW", value: "trigger_gen" },
|
|
246
|
+
{ name: "Exit ReadMeg", value: "exit_cli" }
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
const promptMenu = await inquirer.prompt([
|
|
250
|
+
{
|
|
251
|
+
type: "select",
|
|
252
|
+
name: "action",
|
|
253
|
+
message: "ReadMeg Main Menu — Choose a configuration command:",
|
|
254
|
+
choices: listChoices,
|
|
255
|
+
loop: false
|
|
256
|
+
}
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
const activeChoice = promptMenu.action;
|
|
260
|
+
|
|
261
|
+
if (activeChoice === "exit_cli") {
|
|
262
|
+
console.log("Thanks for using ReadMeg. Keep coding!");
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (activeChoice === "view_tree") {
|
|
267
|
+
console.log("\n\x1b[36m--- Repository Tree Diagram ---\x1b[0m");
|
|
268
|
+
console.log(treeBody || "(Directory is empty)");
|
|
269
|
+
console.log("\x1b[36m-------------------------------\x1b[0m\n");
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (activeChoice === "select_manifests") {
|
|
274
|
+
if (scanResult.manifests.length === 0) {
|
|
275
|
+
console.log("\x1b[33mWarning: No manifest files (package.json, requirements.txt, setup.py, Cargo.toml etc) found to check.\x1b[0m\n");
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const manifestAsk = await inquirer.prompt([
|
|
279
|
+
{
|
|
280
|
+
type: "checkbox",
|
|
281
|
+
name: "paths",
|
|
282
|
+
message: "Check config files to feed into Gemini parameters:",
|
|
283
|
+
choices: scanResult.manifests.map(m => ({ name: m.relativePath, value: m.relativePath, checked: selectedManifestPaths.includes(m.relativePath) })),
|
|
284
|
+
loop: false
|
|
285
|
+
}
|
|
286
|
+
]);
|
|
287
|
+
selectedManifestPaths = manifestAsk.paths;
|
|
288
|
+
console.log(`\x1b[32m✔ Manifest files updated. Total: ${selectedManifestPaths.length}\x1b[0m\n`);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (activeChoice === "select_snippets") {
|
|
293
|
+
if (scanResult.snippets.length === 0) {
|
|
294
|
+
console.log("\x1b[33mWarning: No target snippet sources found. Copy key files to analyze.\x1b[0m\n");
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const snippetAsk = await inquirer.prompt([
|
|
298
|
+
{
|
|
299
|
+
type: "checkbox",
|
|
300
|
+
name: "paths",
|
|
301
|
+
message: "Check code files to inject for code syntax style scanning:",
|
|
302
|
+
choices: scanResult.snippets.map(s => ({ name: s.relativePath, value: s.relativePath, checked: selectedSnippetPaths.includes(s.relativePath) })),
|
|
303
|
+
loop: false
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
selectedSnippetPaths = snippetAsk.paths;
|
|
307
|
+
console.log(`\x1b[32m✔ Key code snippets updated. Total: ${selectedSnippetPaths.length}\x1b[0m\n`);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (activeChoice === "select_markdowns") {
|
|
312
|
+
if (scanResult.markdowns.length === 0) {
|
|
313
|
+
console.log("\x1b[33mWarning: No reference markdown files (.md) found to check. Add some customized docs first!\x1b[0m\n");
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const markdownAsk = await inquirer.prompt([
|
|
317
|
+
{
|
|
318
|
+
type: "checkbox",
|
|
319
|
+
name: "paths",
|
|
320
|
+
message: "Check markdown documents to inject as key architectural sources:",
|
|
321
|
+
choices: scanResult.markdowns.map(md => ({ name: md.relativePath, value: md.relativePath, checked: selectedMarkdownPaths.includes(md.relativePath) })),
|
|
322
|
+
loop: false
|
|
323
|
+
}
|
|
324
|
+
]);
|
|
325
|
+
selectedMarkdownPaths = markdownAsk.paths;
|
|
326
|
+
console.log(`\x1b[32m✔ Reference markdown files updated. Total: ${selectedMarkdownPaths.length}\x1b[0m\n`);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (activeChoice === "select_headings") {
|
|
331
|
+
const headingAsk = await inquirer.prompt([
|
|
332
|
+
{
|
|
333
|
+
type: "checkbox",
|
|
334
|
+
name: "sections",
|
|
335
|
+
message: "Verify additional custom layout sections to mandate:",
|
|
336
|
+
choices: [
|
|
337
|
+
{ name: "Contribution Guidelines", checked: addGuidelines.includes("Contribution Guidelines") },
|
|
338
|
+
{ name: "Docker & Kubernetes Deployment Guidelines", checked: addGuidelines.includes("Docker & Kubernetes Deployment Guidelines") },
|
|
339
|
+
{ name: "Architecture Layout Details", checked: addGuidelines.includes("Architecture Layout Details") },
|
|
340
|
+
{ name: "Security Warnings", checked: addGuidelines.includes("Security Warnings") },
|
|
341
|
+
{ name: "Testing suite & Benchmarks", checked: addGuidelines.includes("Testing suite & Benchmarks") }
|
|
342
|
+
],
|
|
343
|
+
loop: false
|
|
344
|
+
}
|
|
345
|
+
]);
|
|
346
|
+
addGuidelines = headingAsk.sections;
|
|
347
|
+
console.log(`\x1b[32m✔ Supplementary headings updated. Total: ${addGuidelines.length}\x1b[0m\n`);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (activeChoice === "custom_notes") {
|
|
352
|
+
const customsAsk = await inquirer.prompt([
|
|
353
|
+
{
|
|
354
|
+
type: "input",
|
|
355
|
+
name: "notes",
|
|
356
|
+
message: "Input details or focal directions for this layout:",
|
|
357
|
+
default: customInstructions
|
|
358
|
+
}
|
|
359
|
+
]);
|
|
360
|
+
customInstructions = customsAsk.notes;
|
|
361
|
+
console.log("\x1b[32m✔ Focal guidelines registered.\x1b[0m\n");
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (activeChoice === "trigger_gen") {
|
|
366
|
+
console.log("\n\x1b[36mCalling Google Gemini API models to analyze repo architectures...\x1b[0m");
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const ai = new GoogleGenAI({
|
|
370
|
+
apiKey,
|
|
371
|
+
httpOptions: {
|
|
372
|
+
headers: {
|
|
373
|
+
'User-Agent': 'aistudio-build',
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const filteredManifests = scanResult.manifests.filter(m => selectedManifestPaths.includes(m.relativePath));
|
|
379
|
+
const filteredSnippets = scanResult.snippets.filter(s => selectedSnippetPaths.includes(s.relativePath));
|
|
380
|
+
const filteredMarkdowns = scanResult.markdowns.filter(md => selectedMarkdownPaths.includes(md.relativePath));
|
|
381
|
+
|
|
382
|
+
let manifestsBlock = "";
|
|
383
|
+
filteredManifests.forEach(m => {
|
|
384
|
+
manifestsBlock += `\n--- Manifest File: ${m.relativePath} ---\n\`\`\`\n${m.content}\n\`\`\`\n`;
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
let snippetsBlock = "";
|
|
388
|
+
filteredSnippets.forEach(s => {
|
|
389
|
+
snippetsBlock += `\n--- Code Snippet: ${s.relativePath} ---\n\`\`\`\n${s.content}\n\`\`\`\n`;
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
let markdownsBlock = "";
|
|
393
|
+
filteredMarkdowns.forEach(md => {
|
|
394
|
+
markdownsBlock += `\n--- Reference Markdown File: ${md.relativePath} ---\n\`\`\`markdown\n${md.content}\n\`\`\`\n`;
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const systemInstruction = `Role: You are an expert technical writer and principal software architect.
|
|
398
|
+
Task: Your job is to generate a comprehensive, highly polished, professional README.md file based only on the provided directory structure, project configuration files, and references, formatted EXACTLY using the Best-README-Template by @othneildrew.
|
|
399
|
+
|
|
400
|
+
Visual Layout / Structure Requirements (Derived from othneildrew/Best-README-Template):
|
|
401
|
+
The document MUST strictly follow this exact outline and formatting sequence:
|
|
402
|
+
|
|
403
|
+
1. Top Anchors & Shields:
|
|
404
|
+
- Add a top anchor: \`<a name="readme-top"></a>\` at the absolute beginning of the file.
|
|
405
|
+
- Centered shields/badges using raw Markdown with stylish links: Contributors, Forks, Stargazers, Issues, License, LinkedIn. Specify reference URL identifiers at the absolute bottom of the file (e.g. \`[contributors-shield]: ...\`).
|
|
406
|
+
- Put badges cleanly on top.
|
|
407
|
+
|
|
408
|
+
2. Project Logo & Headers (Centered):
|
|
409
|
+
- Leave space for logo using \`<br />\` and then a centered div block containing:
|
|
410
|
+
- An image tag targeting a generic logo placeholder (or repository logo) with dimensions 80x80. Use "https://images.unsplash.com/photo-1618401471353-b98aedd07871?q=80&w=120&auto=format&fit=crop" as a source placeholder if no logo is found.
|
|
411
|
+
- A centered \`<h3 align="center">Project Title</h3>\`
|
|
412
|
+
- A short, descriptive project subtitle.
|
|
413
|
+
- Links to Explore the Docs, View Demo, Report Bug, Request Feature centering on appropriate URLs.
|
|
414
|
+
|
|
415
|
+
3. Interactive Detail-TOC (Table of Contents):
|
|
416
|
+
- Add a table of contents enclosed in a Collapsible Details panel (\`<details><summary>Table of Contents</summary>...\` using list elements with anchors pointing to respective sections: About The Project, Getting Started (Prerequisites, Installation), Usage, Roadmap, Contributing, License, Contact, Acknowledgments).
|
|
417
|
+
|
|
418
|
+
4. About the Project:
|
|
419
|
+
- Place a section title \`## About The Project\` with a paragraph detailing what issues the library solves and why.
|
|
420
|
+
- Provide a placeholder screenshot element (\`[![Product Name Screen Shot][product-screenshot]](https://example.com)\`).
|
|
421
|
+
- Add a \`### Built With\` subsection with raw shields list linking to core framework technologies identified in the manifests or languages (e.g. Next.js, React, Python, Go, Rust, etc., with shields like \`* [![React][React.js]][React-url]\`).
|
|
422
|
+
- Add \`<p align="right">(<a href="#readme-top">back to top</a>)</p>\` at the end of this and ALL subsequent major sections.
|
|
423
|
+
|
|
424
|
+
5. Getting Started:
|
|
425
|
+
- Place \`## Getting Started\` heading.
|
|
426
|
+
- Include \`### Prerequisites\` listing key runtime versions or engines with raw commands (e.g., npm or pip).
|
|
427
|
+
- Include \`### Installation\` with custom sequential steps: Clone repository, Install dependencies/packages using correct package manager commands, Setup key constants/configs.
|
|
428
|
+
|
|
429
|
+
6. Usage:
|
|
430
|
+
- Place \`## Usage\` heading with direct code instructions, visual scripts invocation, and explanation.
|
|
431
|
+
|
|
432
|
+
7. Roadmap:
|
|
433
|
+
- Place \`## Roadmap\` heading highlighting future planning milestones or upcoming revisions. Use standard markdown task lists (e.g., \`- [ ] Feature 1\`).
|
|
434
|
+
|
|
435
|
+
8. Contributing:
|
|
436
|
+
- Place \`## Contributing\` heading detailing the exact five-step guidelines for forks, branching, commits, and pull request initiation.
|
|
437
|
+
|
|
438
|
+
9. License:
|
|
439
|
+
- Place \`## License\` heading highlighting the standard licensing info (e.g. MIT License).
|
|
440
|
+
|
|
441
|
+
10. Contact:
|
|
442
|
+
- Place \`## Contact\` heading listing author/maintainer contact points, project links, or email tags.
|
|
443
|
+
|
|
444
|
+
11. Acknowledgments:
|
|
445
|
+
- Place \`## Acknowledgments\` heading listing standard resources (e.g., Shields.io, select libraries, or other credits).
|
|
446
|
+
|
|
447
|
+
12. Bottom Link References:
|
|
448
|
+
- At the absolute bottom of the document, define ALL referenced links and shield assets used at the top and Built With section dynamically to keep the rest of the text bodies pristine (e.g., \`[contributors-shield]: ...\`).
|
|
449
|
+
|
|
450
|
+
${addGuidelines.length > 0 ? `Include these extra specific layout headings inside the layout: ${addGuidelines.map(sec => `- ${sec}`).join("\n")}` : ""}
|
|
451
|
+
|
|
452
|
+
Do not write markdown block fences inside the generated readme itself unless wrapping code examples. Do not add any conversational meta-talk. Output the raw Best-README-Template markdown directly.`;
|
|
453
|
+
|
|
454
|
+
const prompt = `Generate a README.md file based on the following codebase analysis:
|
|
455
|
+
|
|
456
|
+
Project Structure:
|
|
457
|
+
${treeBody}
|
|
458
|
+
|
|
459
|
+
${manifestsBlock}
|
|
460
|
+
|
|
461
|
+
${snippetsBlock}
|
|
462
|
+
|
|
463
|
+
${markdownsBlock}
|
|
464
|
+
|
|
465
|
+
${customInstructions ? `Custom User directives:\n${customInstructions}\n` : ""}
|
|
466
|
+
|
|
467
|
+
Generate the complete, polished Markdown output directly without any extra conversation.`;
|
|
468
|
+
|
|
469
|
+
const response = await ai.models.generateContent({
|
|
470
|
+
model: chosenModel,
|
|
471
|
+
contents: prompt,
|
|
472
|
+
config: {
|
|
473
|
+
systemInstruction,
|
|
474
|
+
temperature: 0.2
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
generatedContent = response.text || "";
|
|
479
|
+
if (!generatedContent.trim()) {
|
|
480
|
+
throw new Error("Empty response received. The Gemini API returned no text output. This may indicate a content safety block or translation filter restriction.");
|
|
481
|
+
}
|
|
482
|
+
console.log("\n\x1b[32mSuccess: Complete README.md script synthesized successfully!\x1b[0m\n");
|
|
483
|
+
|
|
484
|
+
// Open custom post-generation menu to prevent orthodox layout issues
|
|
485
|
+
let backToMain = false;
|
|
486
|
+
while (!backToMain) {
|
|
487
|
+
const postChoices = [
|
|
488
|
+
{ name: "Preview generated Markdown code", value: "preview_output" },
|
|
489
|
+
{ name: "Save output documentation to root README.md", value: "save_disk" },
|
|
490
|
+
{ name: "Return to Main Menu", value: "return_main" },
|
|
491
|
+
{ name: "Exit ReadMeg", value: "exit_cli" }
|
|
492
|
+
];
|
|
493
|
+
|
|
494
|
+
const postMenu = await inquirer.prompt([
|
|
495
|
+
{
|
|
496
|
+
type: "select",
|
|
497
|
+
name: "action",
|
|
498
|
+
message: "Document Actions Menu — Choose action for compiled guide:",
|
|
499
|
+
choices: postChoices,
|
|
500
|
+
loop: false
|
|
501
|
+
}
|
|
502
|
+
]);
|
|
503
|
+
|
|
504
|
+
const postAction = postMenu.action;
|
|
505
|
+
|
|
506
|
+
if (postAction === "exit_cli") {
|
|
507
|
+
console.log("Thanks for using ReadMeg. Keep coding!");
|
|
508
|
+
process.exit(0);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (postAction === "return_main") {
|
|
512
|
+
backToMain = true;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (postAction === "preview_output") {
|
|
517
|
+
console.log("\n\x1b[35m=== Generated Markdown Output Preview ===\x1b[0m\n");
|
|
518
|
+
console.log(generatedContent || "(No content compiled yet)");
|
|
519
|
+
console.log("\n\x1b[35m==========================================\x1b[0m\n");
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (postAction === "save_disk") {
|
|
524
|
+
const activeOutput = options.output || "README.md";
|
|
525
|
+
const targetFilePath = path.resolve(activeCwd, activeOutput);
|
|
526
|
+
let fileExists = false;
|
|
527
|
+
try {
|
|
528
|
+
fileExists = fs.existsSync(targetFilePath);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
console.error(`\x1b[31m[Error] Failed to verify existence of ${activeOutput}: ${err.message}\x1b[0m\n`);
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (fileExists) {
|
|
535
|
+
console.log(`\n\x1b[33mWarning: File already exists at workspace path: ${path.basename(targetFilePath)}\x1b[0m`);
|
|
536
|
+
const confirmSave = await inquirer.prompt([
|
|
537
|
+
{
|
|
538
|
+
type: "select",
|
|
539
|
+
name: "overwriteAction",
|
|
540
|
+
message: "Verify overwrite protection settings:",
|
|
541
|
+
choices: [
|
|
542
|
+
{ name: "Overwrite file fully (Wipe and replace)", value: "overwrite" },
|
|
543
|
+
{ name: "Append documentation content at bottom", value: "append" },
|
|
544
|
+
{ name: "Cancel operation", value: "cancel" }
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
]);
|
|
548
|
+
|
|
549
|
+
const saveAction = confirmSave.overwriteAction;
|
|
550
|
+
if (saveAction === "cancel") {
|
|
551
|
+
console.log("File modification canceled.\n");
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (saveAction === "append") {
|
|
556
|
+
try {
|
|
557
|
+
const originalText = fs.readFileSync(targetFilePath, "utf-8");
|
|
558
|
+
fs.writeFileSync(targetFilePath, originalText + "\n\n" + generatedContent, "utf-8");
|
|
559
|
+
console.log(`\x1b[32mSuccess: Document successfully appended to ${activeOutput}!\x1b[0m\n`);
|
|
560
|
+
} catch (writeErr) {
|
|
561
|
+
console.error(`\x1b[31m[Error] Failed to read or append to ${activeOutput}: ${writeErr.message}\x1b[0m\n`);
|
|
562
|
+
}
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
fs.writeFileSync(targetFilePath, generatedContent, "utf-8");
|
|
569
|
+
console.log(`\x1b[32mSuccess: Output written successfully to root ${activeOutput} path!\x1b[0m\n`);
|
|
570
|
+
} catch (writeErr) {
|
|
571
|
+
console.error(`\x1b[31m[Error] Failed to write file to ${activeOutput}: ${writeErr.message}\x1b[0m\n`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
} catch (err) {
|
|
577
|
+
console.error(`\n\x1b[31m✖ Gemini API Generation Failed:\x1b[0m`);
|
|
578
|
+
console.error(`\x1b[33mReason:\x1b[0m ${err.message}`);
|
|
579
|
+
if (err.status === 400 || err.message?.toLowerCase().includes("api key") || err.message?.toLowerCase().includes("apikey")) {
|
|
580
|
+
console.error(`\x1b[35mTip:\x1b[0m Please verify that your Gemini API Key is active, valid, and has correct permissions.`);
|
|
581
|
+
} else if (err.message?.toLowerCase().includes("model")) {
|
|
582
|
+
console.error(`\x1b[35mTip:\x1b[0m The chosen model '${chosenModel}' might be unavailable or invalid.`);
|
|
583
|
+
} else if (err.message?.toLowerCase().includes("fetch") || err.message?.toLowerCase().includes("enotfound") || err.message?.toLowerCase().includes("network")) {
|
|
584
|
+
console.error(`\x1b[35mTip:\x1b[0m Network connectivity issue detected. Please check your internet connection.`);
|
|
585
|
+
}
|
|
586
|
+
console.log("");
|
|
587
|
+
}
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Direct non-interactive execution logic
|
|
595
|
+
*/
|
|
596
|
+
async function runDirectScanAndGen() {
|
|
597
|
+
console.log("Running zero-config non-interactive codebase scans...");
|
|
598
|
+
const activeCwd = process.cwd();
|
|
599
|
+
|
|
600
|
+
let treeBody = "";
|
|
601
|
+
try {
|
|
602
|
+
treeBody = buildTreeString(activeCwd);
|
|
603
|
+
} catch (err) {
|
|
604
|
+
console.error(`\x1b[31mERROR: Directory tree scanning failed completely: ${err.message}\x1b[0m`);
|
|
605
|
+
process.exit(1);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const scanResult = {
|
|
609
|
+
manifests: [],
|
|
610
|
+
snippets: [],
|
|
611
|
+
languages: new Set()
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
try {
|
|
615
|
+
scanDirectoryFiles(activeCwd, "", scanResult);
|
|
616
|
+
} catch (err) {
|
|
617
|
+
console.error(`\x1b[31mERROR: Detailed file scanning failed: ${err.message}\x1b[0m`);
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const apiKey = options.apiKey || process.env.GEMINI_API_KEY;
|
|
622
|
+
if (!apiKey || !apiKey.trim()) {
|
|
623
|
+
console.error("\x1b[31mERROR: Gemini API key is missing. Run with --api-key=KEY or set the GEMINI_API_KEY environment variable.\x1b[0m");
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
const ai = new GoogleGenAI({
|
|
629
|
+
apiKey: apiKey.trim(),
|
|
630
|
+
httpOptions: {
|
|
631
|
+
headers: {
|
|
632
|
+
'User-Agent': 'aistudio-build',
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
let manifestsBlock = "";
|
|
638
|
+
scanResult.manifests.forEach(m => {
|
|
639
|
+
manifestsBlock += `\n--- file: ${m.relativePath} ---\n\`\`\`\n${m.content}\n\`\`\`\n`;
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
const prompt = `Generate a README.md file based on the following codebase layout tree:
|
|
643
|
+
|
|
644
|
+
Project Structure:
|
|
645
|
+
${treeBody}
|
|
646
|
+
|
|
647
|
+
${manifestsBlock}
|
|
648
|
+
|
|
649
|
+
Generate the complete, polished Markdown output directly without any extra conversation.`;
|
|
650
|
+
|
|
651
|
+
const response = await ai.models.generateContent({
|
|
652
|
+
model: "gemini-3.5-flash",
|
|
653
|
+
contents: prompt,
|
|
654
|
+
config: {
|
|
655
|
+
systemInstruction: "You are an expert technical writer. Write a comprehensive, polished production-ready README.md file directly.",
|
|
656
|
+
temperature: 0.2
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
const markdownOutput = response.text || "";
|
|
661
|
+
if (!markdownOutput.trim()) {
|
|
662
|
+
throw new Error("Empty response received. The Gemini API returned no text output. This may indicate a content safety block or translation filter restriction.");
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const targetFile = path.resolve(activeCwd, options.output);
|
|
666
|
+
fs.writeFileSync(targetFile, markdownOutput, "utf-8");
|
|
667
|
+
console.log(`Success: Direct Generation saved cleanly to: ${options.output}`);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.error(`\n\x1b[31m✖ Direct Generative Error:\x1b[0m ${error.message}`);
|
|
670
|
+
if (error.status === 400 || error.message?.toLowerCase().includes("api key") || error.message?.toLowerCase().includes("apikey")) {
|
|
671
|
+
console.error(`\x1b[35mTip:\x1b[0m Please verify that your Gemini API Key is active, valid, and has correct permissions.`);
|
|
672
|
+
} else if (error.message?.toLowerCase().includes("fetch") || error.message?.toLowerCase().includes("enotfound") || error.message?.toLowerCase().includes("network")) {
|
|
673
|
+
console.error(`\x1b[35mTip:\x1b[0m Network connectivity issue detected. Please check your internet connection.`);
|
|
674
|
+
}
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "readmeg",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-config terminal tool powered by Gemini that automatically generates comprehensive, production-grade README files for local repositories.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"readmeg": "./bin/readmeg.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/readmeg.js"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "echo 'Build successful'",
|
|
14
|
+
"start": "node ./bin/readmeg.js --help",
|
|
15
|
+
"lint": "echo 'No lint needed'"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@google/genai": "^2.4.0",
|
|
19
|
+
"commander": "^15.0.0",
|
|
20
|
+
"dotenv": "^17.2.3",
|
|
21
|
+
"inquirer": "^14.0.2"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.14.0"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"readme",
|
|
28
|
+
"generator",
|
|
29
|
+
"gemini",
|
|
30
|
+
"cli",
|
|
31
|
+
"ai",
|
|
32
|
+
"documentation"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|