scanorepo 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Benjamin Onyia
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,263 @@
1
+ # 🔍 ScanoRepo
2
+
3
+ > **AI-powered CLI tool for instant project scanning and documentation generation.**
4
+
5
+ ScanoRepo is a cross-platform command-line tool built with Node.js and TypeScript that scans your JavaScript/TypeScript projects for common issues and generates documentation using AI-powered insights.
6
+
7
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
8
+ ![Node](https://img.shields.io/badge/node-%3E%3D16.0.0-green.svg)
9
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue.svg)
10
+
11
+ ---
12
+
13
+ ## ✨ Features
14
+
15
+ - **🔎 Project Scanning** - Detects missing npm scripts, environment variables, dependencies, and TypeScript configuration issues.
16
+ - **🤖 AI-Powered Insights** - Uses Groq's ultra-fast AI inference to explain _why_ each issue matters and how to fix it.
17
+ - **📄 Auto Documentation** - Generates a `SCANNED.md` file summarizing your project structure, components, and getting started guide.
18
+ - **📦 Dependency Management** - Interactive CLI to install, update, or check outdated packages.
19
+ - **🌐 Cross-Platform** - Works on Windows, macOS, and Linux.
20
+
21
+ ---
22
+
23
+ ## 📦 Installation
24
+
25
+ ### Global Installation (Recommended)
26
+
27
+ ```bash
28
+ npm install -g scanorepo
29
+ ```
30
+
31
+ ### Local Development
32
+
33
+ ```bash
34
+ git clone https://github.com/classicManCode/ScanoRepo.git
35
+ cd ScanoRepo
36
+ npm install
37
+ npm run build
38
+ npm link
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 🔑 Zero Configuration
44
+
45
+ ScanoRepo works **instantly**!
46
+
47
+ Unlike other CLI tools, you don't need to sign up for API keys or set up environment variables. AI-powered insights are delivered through a secure proxy server maintained by the developer.
48
+
49
+ ### How it works:
50
+
51
+ 1. You run a command.
52
+ 2. ScanoRepo sends the project metadata to a secure backend.
53
+ 3. The backend processes the request using Groq's high-performance models.
54
+ 4. You receive instant insights in your terminal.
55
+
56
+ ---
57
+
58
+ ## 🚀 Usage
59
+
60
+ Navigate to any Node.js project and run:
61
+
62
+ ```bash
63
+ cd /path/to/your/project
64
+ scanorepo <command>
65
+ ```
66
+
67
+ ### Available Commands
68
+
69
+ | Command | Description |
70
+ | --------------------- | -------------------------------------------- |
71
+ | `scanorepo scan` | Scan project for issues with AI explanations |
72
+ | `scanorepo doc` | Generate `SCANNED.md` documentation using AI |
73
+ | `scanorepo deps` | Interactive dependency management |
74
+ | `scanorepo check` | Run full health check (scan + doc) |
75
+ | `scanorepo --help` | Display help information |
76
+ | `scanorepo --version` | Display version number |
77
+
78
+ ---
79
+
80
+ ## 📋 Command Details
81
+
82
+ ### `scanorepo scan`
83
+
84
+ Scans your project for common issues:
85
+
86
+ - **Missing npm scripts** - Checks for `start`, `dev`, `build`, `test`
87
+ - **Environment issues** - Detects missing `.env` files
88
+ - **TypeScript errors** - Flags `.ts` files without `tsconfig.json`
89
+ - **Dependency issues** - Checks if `node_modules` exists
90
+
91
+ **Example Output:**
92
+
93
+ ```
94
+ ____ ____
95
+ / ___| ___ __ _ _ __ ___ | _ \ ___ _ __ ___
96
+ \___ \ / __/ _` | '_ \ / _ \| |_) / _ \ '_ \ / _ \
97
+ ___) | (_| (_| | | | | (_) | _ < __/ |_) | (_) |
98
+ |____/ \___\__,_|_| |_|\___/|_| \_\___| .__/ \___/
99
+ |_|
100
+ ✔ Scan complete!
101
+
102
+ --- Scan Results ---
103
+
104
+ [!] Missing Scripts:
105
+ - test
106
+
107
+ AI Insight:
108
+ **`npm test`** is essential because it automates verification,
109
+ integrates with CI/CD pipelines, and standardizes testing workflow...
110
+ ```
111
+
112
+ ### `scanorepo doc`
113
+
114
+ Analyzes your project structure and generates a comprehensive `SCANNED.md` file containing:
115
+
116
+ - Project overview
117
+ - Key components and their purposes
118
+ - Getting started instructions
119
+
120
+ ### `scanorepo deps`
121
+
122
+ Opens an interactive menu:
123
+
124
+ ```
125
+ ? What would you like to do with dependencies?
126
+ ❯ Install missing dependencies
127
+ Update dependencies
128
+ Check outdated packages
129
+ Cancel
130
+ ```
131
+
132
+ - Automatically detects npm vs yarn
133
+ - Handles all package manager commands
134
+
135
+ ### `scanorepo check`
136
+
137
+ Runs both `scan` and `doc` commands in sequence for a complete project audit.
138
+
139
+ ---
140
+
141
+ ## 📁 Project Structure
142
+
143
+ ```
144
+ scanorepo/
145
+ ├── src/
146
+ │ ├── index.ts # CLI entry point
147
+ │ ├── commands/
148
+ │ │ ├── scan.ts # Scan command
149
+ │ │ ├── doc.ts # Documentation command
150
+ │ │ ├── deps.ts # Dependencies command
151
+ │ │ └── check.ts # Full check command
152
+ │ ├── services/
153
+ │ │ ├── ai.ts # Groq AI integration
154
+ │ │ ├── scanner.ts # Project scanning logic
155
+ │ │ ├── summarizer.ts # Project structure summarizer
156
+ │ │ └── updater.ts # Version update checker
157
+ │ └── utils/
158
+ │ └── logger.ts # Colored console output
159
+ ├── dist/ # Compiled JavaScript
160
+ ├── package.json
161
+ ├── tsconfig.json
162
+ └── README.md
163
+ ```
164
+
165
+ ---
166
+
167
+ ## ⚠️ What NOT To Do
168
+
169
+ ### ❌ Don't commit your API key
170
+
171
+ Never commit your `.env` file or hardcode API keys in source code.
172
+
173
+ ```bash
174
+ # Add to .gitignore
175
+ .env
176
+ ```
177
+
178
+ ### ❌ Don't run on non-Node.js projects
179
+
180
+ ScanoRepo is designed for JavaScript/TypeScript projects with `package.json`.
181
+
182
+ ### ❌ Don't expect deterministic AI outputs
183
+
184
+ AI responses may vary between runs. The insights are meant to be helpful, not definitive.
185
+
186
+ ### ❌ Don't skip the build step
187
+
188
+ After making changes, always rebuild:
189
+
190
+ ```bash
191
+ npm run build
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 🛠️ Development
197
+
198
+ ### Build the project
199
+
200
+ ```bash
201
+ npm run build
202
+ ```
203
+
204
+ ### Run in development mode
205
+
206
+ ```bash
207
+ npm run dev
208
+ ```
209
+
210
+ ### Test locally without publishing
211
+
212
+ ```bash
213
+ npm link
214
+ scanorepo scan
215
+ ```
216
+
217
+ ### Unlink when done
218
+
219
+ ```bash
220
+ npm unlink -g scanorepo
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 🤝 Contributing
226
+
227
+ Contributions are welcome! Please follow these steps:
228
+
229
+ 1. Fork the repository
230
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
231
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
232
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
233
+ 5. Open a Pull Request
234
+
235
+ ---
236
+
237
+ ## 📄 License
238
+
239
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
240
+
241
+ ---
242
+
243
+ ## 👤 Author
244
+
245
+ **Benjamin Onyia**
246
+
247
+ - 🌐 Portfolio: [benjamin-onyia.vercel.app](https://benjamin-onyia.vercel.app/)
248
+ - 💻 GitHub: [@classicManCode](https://github.com/classicManCode)
249
+
250
+ ---
251
+
252
+ ## 🙏 Acknowledgments
253
+
254
+ - [Groq](https://groq.com/) for ultra-fast AI inference
255
+ - [Commander.js](https://github.com/tj/commander.js) for CLI framework
256
+ - [Inquirer.js](https://github.com/SBoudrias/Inquirer.js) for interactive prompts
257
+ - [Chalk](https://github.com/chalk/chalk) & [Ora](https://github.com/sindresorhus/ora) for beautiful terminal output
258
+
259
+ ---
260
+
261
+ <p align="center">
262
+ Made with ❤️ by <a href="https://benjamin-onyia.vercel.app/">Benjamin Onyia</a>
263
+ </p>
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCommand = void 0;
4
+ const commander_1 = require("commander");
5
+ const scan_1 = require("./scan"); // Imported directly
6
+ const doc_1 = require("./doc"); // Imported directly
7
+ const logger_1 = require("../utils/logger");
8
+ exports.checkCommand = new commander_1.Command("check")
9
+ .description("Runs full health check (scan + doc generation)")
10
+ .action(async () => {
11
+ logger_1.logger.info("Starting full health check...");
12
+ logger_1.logger.general("\n--- Step 1: Scanning ---");
13
+ await (0, scan_1.runScan)();
14
+ logger_1.logger.general("\n--- Step 2: Documentation ---");
15
+ await (0, doc_1.runDoc)();
16
+ logger_1.logger.success("\nFull check complete!");
17
+ logger_1.logger.footer();
18
+ });
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.depsCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const cross_spawn_1 = __importDefault(require("cross-spawn"));
10
+ const logger_1 = require("../utils/logger");
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ exports.depsCommand = new commander_1.Command("deps")
14
+ .description("Manage project dependencies")
15
+ .action(async () => {
16
+ const projectPath = process.cwd();
17
+ const pkgPath = path_1.default.join(projectPath, "package.json");
18
+ if (!fs_1.default.existsSync(pkgPath)) {
19
+ logger_1.logger.error("No package.json found.");
20
+ return;
21
+ }
22
+ const { action } = await inquirer_1.default.prompt([
23
+ {
24
+ type: "list",
25
+ name: "action",
26
+ message: "What would you like to do with dependencies?",
27
+ choices: [
28
+ { name: "Install missing dependencies", value: "install" },
29
+ { name: "Update dependencies", value: "update" },
30
+ { name: "Check outdated packages", value: "outdated" },
31
+ { name: "Cancel", value: "cancel" },
32
+ ],
33
+ },
34
+ ]);
35
+ if (action === "cancel")
36
+ return;
37
+ logger_1.logger.info(`Running npm ${action}...`);
38
+ // Determine package manager (simple check for yarn.lock)
39
+ let pm = "npm";
40
+ if (fs_1.default.existsSync(path_1.default.join(projectPath, "yarn.lock"))) {
41
+ pm = "yarn";
42
+ }
43
+ const cmd = pm;
44
+ const args = [action];
45
+ // 'npm outdated' returns exit code 1 if there are outdated variables, which can throw error if not handled.
46
+ // 'npm install' and 'npm update' should be fine.
47
+ try {
48
+ const child = cross_spawn_1.default.sync(cmd, args, {
49
+ stdio: "inherit",
50
+ cwd: projectPath,
51
+ });
52
+ if (child.status === 0) {
53
+ logger_1.logger.success(`Successfully ran ${action}.`);
54
+ }
55
+ else {
56
+ // npm outdated exits with 1 if outdated stuff found, which is "success" in this context
57
+ if (action === "outdated" && child.status === 1) {
58
+ logger_1.logger.info("Outdated packages found (see above).");
59
+ }
60
+ else {
61
+ logger_1.logger.error(`Command failed with status code ${child.status}`);
62
+ }
63
+ }
64
+ }
65
+ catch (e) {
66
+ logger_1.logger.error(`Failed to run command: ${e.message}`);
67
+ }
68
+ logger_1.logger.footer();
69
+ });
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.docCommand = exports.runDoc = void 0;
7
+ const commander_1 = require("commander");
8
+ const summarizer_1 = require("../services/summarizer");
9
+ const ai_1 = require("../services/ai");
10
+ const logger_1 = require("../utils/logger");
11
+ const ora_1 = __importDefault(require("ora"));
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const runDoc = async () => {
15
+ const spinner = (0, ora_1.default)("Generating documentation...").start();
16
+ const projectPath = process.cwd();
17
+ try {
18
+ const structure = (0, summarizer_1.summarizeProject)(projectPath);
19
+ const prompt = `
20
+ You are a technical documenter.
21
+ Analyze the following project structure and snippets:
22
+
23
+ ${structure}
24
+
25
+ Generate a "SCANNED.md" file content in Markdown format.
26
+ Include:
27
+ 1. Project Overview (infer from filenames and snippets).
28
+ 2. Key Components (guess what the directories do).
29
+ 3. Getting Started (how to install/run based on package.json).
30
+
31
+ Keep it professional and concise.
32
+ `;
33
+ const documentation = await (0, ai_1.generateExplanation)(prompt);
34
+ fs_1.default.writeFileSync(path_1.default.join(projectPath, "SCANNED.md"), documentation);
35
+ spinner.succeed("SCANNED.md generated successfully!");
36
+ logger_1.logger.info("\nCheck SCANNED.md for the result.");
37
+ }
38
+ catch (error) {
39
+ spinner.fail("Documentation generation failed.");
40
+ logger_1.logger.error(error.message);
41
+ }
42
+ logger_1.logger.footer();
43
+ };
44
+ exports.runDoc = runDoc;
45
+ exports.docCommand = new commander_1.Command("doc")
46
+ .description("Generate SCANNED.md information file using AI")
47
+ .action(exports.runDoc);
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scanCommand = exports.runScan = void 0;
7
+ const commander_1 = require("commander");
8
+ const scanner_1 = require("../services/scanner");
9
+ const ai_1 = require("../services/ai");
10
+ const logger_1 = require("../utils/logger");
11
+ const ora_1 = __importDefault(require("ora"));
12
+ const runScan = async () => {
13
+ const spinner = (0, ora_1.default)("Scanning project...").start();
14
+ const projectPath = process.cwd();
15
+ try {
16
+ const issues = await (0, scanner_1.scanProject)(projectPath);
17
+ spinner.succeed("Scan complete!");
18
+ if (issues.framework === "unknown") {
19
+ logger_1.logger.warn("Could not detect a Node.js project (missing package.json).");
20
+ return;
21
+ }
22
+ logger_1.logger.general("\n--- Scan Results ---");
23
+ // Helper to explain issues
24
+ const explain = async (title, items, promptContext) => {
25
+ if (items.length > 0) {
26
+ logger_1.logger.warn(`\n[!] ${title}:`);
27
+ items.forEach((item) => logger_1.logger.general(` - ${item}`));
28
+ const prompt = `Explain why these ${promptContext} are important in a Node.js project: ${items.join(", ")}. Keep it brief.`;
29
+ const aiExplanation = await (0, ai_1.generateExplanation)(prompt);
30
+ logger_1.logger.info(`\nAI Insight:\n${aiExplanation}\n`);
31
+ }
32
+ };
33
+ await explain("Missing Scripts", issues.missingScripts, "npm scripts");
34
+ await explain("Environment Issues", issues.missingEnvVars, "env configurations");
35
+ if (issues.missingDeps.length > 0) {
36
+ logger_1.logger.warn(`\n[!] Dependency Issues:`);
37
+ issues.missingDeps.forEach((d) => logger_1.logger.general(` - ${d}`));
38
+ }
39
+ if (issues.typescriptError) {
40
+ logger_1.logger.error(`\n[!] TypeScript Config Missing: Found .ts files but no tsconfig.json.`);
41
+ const aiExplanation = await (0, ai_1.generateExplanation)("Why is tsconfig.json required for TypeScript projects?");
42
+ logger_1.logger.info(`\nAI Insight:\n${aiExplanation}\n`);
43
+ }
44
+ if (issues.missingScripts.length === 0 &&
45
+ issues.missingEnvVars.length === 0 &&
46
+ !issues.typescriptError &&
47
+ issues.missingDeps.length === 0) {
48
+ logger_1.logger.success("\n✅ No obvious issues found! Good job.");
49
+ }
50
+ }
51
+ catch (error) {
52
+ spinner.fail("Scan failed.");
53
+ logger_1.logger.error(error.message);
54
+ }
55
+ logger_1.logger.footer();
56
+ };
57
+ exports.runScan = runScan;
58
+ exports.scanCommand = new commander_1.Command("scan")
59
+ .description("Scan the current project for issues")
60
+ .action(exports.runScan);
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const figlet_1 = __importDefault(require("figlet"));
9
+ const scan_1 = require("./commands/scan");
10
+ const deps_1 = require("./commands/deps");
11
+ const doc_1 = require("./commands/doc");
12
+ const check_1 = require("./commands/check");
13
+ const updater_1 = require("./services/updater");
14
+ const package_json_1 = __importDefault(require("../package.json"));
15
+ // Display ASCII banner
16
+ console.log(figlet_1.default.textSync("ScanoRepo"));
17
+ const program = new commander_1.Command();
18
+ program
19
+ .name("scanorepo")
20
+ .description("AI-powered CLI tool for project scanning and documentation")
21
+ .version(package_json_1.default.version)
22
+ .hook("preAction", async () => {
23
+ // Check for updates before running any command
24
+ await (0, updater_1.checkForUpdates)(package_json_1.default.version);
25
+ });
26
+ program.addCommand(scan_1.scanCommand);
27
+ program.addCommand(deps_1.depsCommand);
28
+ program.addCommand(doc_1.docCommand);
29
+ program.addCommand(check_1.checkCommand);
30
+ program.parse(process.argv);
@@ -0,0 +1,80 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.generateExplanation = void 0;
40
+ const axios_1 = __importDefault(require("axios"));
41
+ const dotenv = __importStar(require("dotenv"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const logger_1 = require("../utils/logger");
44
+ // Load .env from the ScanoRepo installation directory (for developer testing)
45
+ dotenv.config({ path: path_1.default.join(__dirname, "../../.env") });
46
+ // ⚠️ YOUR PROXY SERVER URL (e.g. your Vercel deployment URL)
47
+ const PROXY_SERVER_URL = "https://scano-repo.vercel.app/api/explain";
48
+ /**
49
+ * Generate an AI explanation by calling a secure proxy server.
50
+ * This ensures the Groq API key is NEVER exposed to the client terminal.
51
+ */
52
+ const generateExplanation = async (prompt) => {
53
+ try {
54
+ // 1. Check if user provided their own key (local override)
55
+ const localApiKey = process.env.GROQ_API_KEY;
56
+ if (localApiKey) {
57
+ // If developer has a local key, we could use Groq SDK directly,
58
+ // but for consistency we'll use the proxy or prompt them.
59
+ // To keep it simple and secure, we'll try the proxy first.
60
+ }
61
+ const response = await axios_1.default.post(PROXY_SERVER_URL, {
62
+ prompt: prompt,
63
+ // You could add an optional secret key here to prevent unauthorized API use
64
+ token: "optional-cli-secret",
65
+ });
66
+ return response.data.explanation || "No explanation generated.";
67
+ }
68
+ catch (error) {
69
+ if (error.response) {
70
+ // Server responded with an error status
71
+ logger_1.logger.error(`AI Proxy Error: ${error.response.data.message || "Server error"}`);
72
+ }
73
+ else {
74
+ // Network error or timeout
75
+ logger_1.logger.error(`AI Proxy Connection failed. Ensure you are online.`);
76
+ }
77
+ return "Failed to generate AI explanation.";
78
+ }
79
+ };
80
+ exports.generateExplanation = generateExplanation;
@@ -0,0 +1,62 @@
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.generateExplanation = void 0;
37
+ const genai_1 = require("@google/genai");
38
+ const dotenv = __importStar(require("dotenv"));
39
+ const logger_1 = require("../utils/logger");
40
+ dotenv.config();
41
+ const apiKey = process.env.GEMINI_API_KEY;
42
+ if (!apiKey) {
43
+ logger_1.logger.warn("Warning: GEMINI_API_KEY is not set in environment variables. AI features will not work.");
44
+ }
45
+ const client = apiKey ? new genai_1.GoogleGenAI({ apiKey }) : null;
46
+ const generateExplanation = async (prompt) => {
47
+ if (!client)
48
+ return "AI features disabled: Missing API Key.";
49
+ try {
50
+ const result = await client.models.generateContent({
51
+ model: "gemini-2.0-flash",
52
+ contents: prompt,
53
+ });
54
+ // The new SDK exposes text as a getter property, not a method
55
+ return result.text || "No explanation generated.";
56
+ }
57
+ catch (error) {
58
+ logger_1.logger.error(`Gemini API Error: ${error.message}`);
59
+ return "Failed to generate AI explanation.";
60
+ }
61
+ };
62
+ exports.generateExplanation = generateExplanation;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scanProject = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const scanProject = async (projectPath) => {
10
+ const result = {
11
+ missingEnvVars: [],
12
+ missingDeps: [],
13
+ typescriptError: false,
14
+ missingScripts: [],
15
+ portConflicts: [],
16
+ framework: "unknown",
17
+ };
18
+ // Check package.json
19
+ const pkgPath = path_1.default.join(projectPath, "package.json");
20
+ if (fs_1.default.existsSync(pkgPath)) {
21
+ result.framework = "node";
22
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, "utf-8"));
23
+ // Check Scripts
24
+ const requiredScripts = ["start", "dev", "build", "test"];
25
+ const scripts = pkg.scripts || {};
26
+ requiredScripts.forEach((script) => {
27
+ if (!scripts[script]) {
28
+ result.missingScripts.push(script);
29
+ }
30
+ });
31
+ // Simple dependency check (just consistency, real check needs npm audit or similar logic if needed,
32
+ // but here we might just check if node_modules exists or if deps are installed)
33
+ if (!fs_1.default.existsSync(path_1.default.join(projectPath, "node_modules"))) {
34
+ result.missingDeps.push("node_modules folder missing (run npm install)");
35
+ }
36
+ }
37
+ // Check .env
38
+ if (!fs_1.default.existsSync(path_1.default.join(projectPath, ".env"))) {
39
+ if (fs_1.default.existsSync(path_1.default.join(projectPath, ".env.example"))) {
40
+ result.missingEnvVars.push(".env missing (copy .env.example)");
41
+ }
42
+ else {
43
+ result.missingEnvVars.push(".env missing");
44
+ }
45
+ }
46
+ // Check TypeScript
47
+ if (fs_1.default.existsSync(path_1.default.join(projectPath, "tsconfig.json"))) {
48
+ // Could try to dry-run tsc or just check presence
49
+ // For now, let's assume if tsconfig exists, it's a TS project.
50
+ }
51
+ else if (result.framework === "node" &&
52
+ fs_1.default.existsSync(path_1.default.join(projectPath, "src", "index.ts"))) {
53
+ result.typescriptError = true; // TS file but no config
54
+ }
55
+ return result;
56
+ };
57
+ exports.scanProject = scanProject;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.summarizeProject = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ // Ignore list for summarization
10
+ const IGNORE_DIRS = [
11
+ "node_modules",
12
+ ".git",
13
+ "dist",
14
+ "coverage",
15
+ ".idea",
16
+ ".vscode",
17
+ ];
18
+ const IGNORE_FILES = ["package-lock.json", "yarn.lock", ".DS_Store"];
19
+ const summarizeProject = (dir, depth = 0, maxDepth = 2) => {
20
+ if (depth > maxDepth)
21
+ return "";
22
+ let summary = "";
23
+ const files = fs_1.default.readdirSync(dir);
24
+ for (const file of files) {
25
+ if (IGNORE_DIRS.includes(file) || IGNORE_FILES.includes(file))
26
+ continue;
27
+ const fullPath = path_1.default.join(dir, file);
28
+ const stat = fs_1.default.statSync(fullPath);
29
+ if (stat.isDirectory()) {
30
+ summary += `${" ".repeat(depth)}- [DIR] ${file}\n`;
31
+ summary += (0, exports.summarizeProject)(fullPath, depth + 1, maxDepth);
32
+ }
33
+ else {
34
+ summary += `${" ".repeat(depth)}- [FILE] ${file}\n`;
35
+ // Read critical files content for better context
36
+ if (["package.json", "tsconfig.json", "README.md"].includes(file)) {
37
+ try {
38
+ const content = fs_1.default.readFileSync(fullPath, "utf-8").slice(0, 500); // Limit to 500 chars
39
+ summary += `${" ".repeat(depth + 1)}Content snippet: ${content.replace(/\n/g, " ")}...\n`;
40
+ }
41
+ catch (e) { }
42
+ }
43
+ }
44
+ }
45
+ return summary;
46
+ };
47
+ exports.summarizeProject = summarizeProject;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkForUpdates = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const semver_1 = __importDefault(require("semver"));
9
+ const logger_1 = require("../utils/logger");
10
+ // import packageJson from '../../package.json'; // Importing outside src might be tricky with generic resolve, but let's try relative
11
+ const checkForUpdates = async (currentVersion) => {
12
+ try {
13
+ // For now, scanorepo is not published, so this will likely 404.
14
+ // We'll mock it or handle the error gracefully.
15
+ // Replace with actual package name if different.
16
+ const pkgName = "scanorepo";
17
+ const registryUrl = `https://registry.npmjs.org/${pkgName}/latest`;
18
+ // specific timeout to avoid blocking CLI
19
+ const response = await axios_1.default.get(registryUrl, { timeout: 1500 });
20
+ const latestVersion = response.data.version;
21
+ if (semver_1.default.gt(latestVersion, currentVersion)) {
22
+ logger_1.logger.warn(`
23
+ \n*************************************************
24
+ Update available: ${latestVersion} (current: ${currentVersion})
25
+ Run: npm install -g ${pkgName}
26
+ *************************************************\n`);
27
+ }
28
+ }
29
+ catch (error) {
30
+ // Silent fail or debug log
31
+ // logger.error("Failed to check for updates.");
32
+ }
33
+ };
34
+ exports.checkForUpdates = checkForUpdates;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ exports.logger = {
9
+ info: (msg) => console.log(chalk_1.default.blue(msg)),
10
+ success: (msg) => console.log(chalk_1.default.green(msg)),
11
+ warn: (msg) => console.log(chalk_1.default.yellow(msg)),
12
+ error: (msg) => console.log(chalk_1.default.red(msg)),
13
+ general: (msg) => console.log(msg),
14
+ footer: () => {
15
+ console.log(chalk_1.default.gray("\n---"));
16
+ console.log(chalk_1.default.gray("Built by Benjamin Onyia"));
17
+ console.log(chalk_1.default.cyan("🌐 https://benjamin-onyia.vercel.app/"));
18
+ console.log(chalk_1.default.cyan("💻 https://github.com/classicManCode"));
19
+ },
20
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "scanorepo",
3
+ "version": "1.0.0",
4
+ "description": "Cross-platform AI-powered CLI tool for project scanning and documentation",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "scanorepo": "./dist/index.js"
8
+ },
9
+ "author": "Benjamin Onyia <classicManCode>",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "cli",
13
+ "ai",
14
+ "scanner",
15
+ "documentation",
16
+ "groq",
17
+ "typescript",
18
+ "nodejs",
19
+ "developer-tools",
20
+ "code-analysis"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/classicManCode/ScanoRepo.git"
25
+ },
26
+ "homepage": "https://github.com/classicManCode/ScanoRepo#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/classicManCode/ScanoRepo/issues"
29
+ },
30
+ "engines": {
31
+ "node": ">=16.0.0"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "start": "node dist/index.js",
41
+ "dev": "ts-node-dev src/index.ts",
42
+ "scanorepo": "ts-node src/index.ts"
43
+ },
44
+ "dependencies": {
45
+ "axios": "^1.13.2",
46
+ "chalk": "^4.1.2",
47
+ "commander": "^12.0.0",
48
+ "cross-spawn": "^7.0.3",
49
+ "dotenv": "^16.4.1",
50
+ "figlet": "^1.7.0",
51
+ "groq-sdk": "^0.37.0",
52
+ "inquirer": "^8.2.6",
53
+ "ora": "^5.4.1",
54
+ "semver": "^7.6.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/cross-spawn": "^6.0.6",
58
+ "@types/figlet": "^1.5.8",
59
+ "@types/inquirer": "^9.0.7",
60
+ "@types/node": "^20.11.16",
61
+ "@types/semver": "^7.5.7",
62
+ "ts-node": "^10.9.2",
63
+ "ts-node-dev": "^2.0.0",
64
+ "typescript": "^5.3.3"
65
+ }
66
+ }