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 +21 -0
- package/README.md +263 -0
- package/dist/commands/check.js +18 -0
- package/dist/commands/deps.js +69 -0
- package/dist/commands/doc.js +47 -0
- package/dist/commands/scan.js +60 -0
- package/dist/index.js +30 -0
- package/dist/services/ai.js +80 -0
- package/dist/services/gemini.js +62 -0
- package/dist/services/scanner.js +57 -0
- package/dist/services/summarizer.js +47 -0
- package/dist/services/updater.js +34 -0
- package/dist/utils/logger.js +20 -0
- package/package.json +66 -0
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
|
+

|
|
8
|
+

|
|
9
|
+

|
|
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
|
+
}
|