@heetmehta18/autodev 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +80 -22
  2. package/bin/index.js +138 -76
  3. package/package.json +6 -6
  4. package/LICENSE +0 -21
package/README.md CHANGED
@@ -1,33 +1,91 @@
1
- # AutoDev NPM CLI Wrapper
1
+ # AutoDev CLI — The App Store for Developers ⚡
2
2
 
3
- This package provides a lightweight Node.js wrapper around the native Go compiled binaries for AutoDev. It allows developers to run AutoDev seamlessly using standard Node package execution engines like `npx` or `pnpm dlx`, or by installing it globally via npm.
3
+ [![npm version](https://img.shields.io/npm/v/@heetmehta18/autodev.svg)](https://www.npmjs.com/package/@heetmehta18/autodev)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- ## Usage
6
+ **AutoDev** is an open-source, cross-platform developer environment bootstrapper. It acts as an **App Store for Developers**, simplifying complex toolchain setups through intelligent, profile-based automation.
6
7
 
7
- ### Run via npx (No Installation Needed)
8
+ ---
9
+
10
+ ## 🚀 Key Features
11
+
12
+ * 🔍 **Polyglot Codebase Scanner**: Detects 30+ languages, frameworks, package managers, and DevOps infrastructure.
13
+ * 🛡️ **Supply-Chain Security Audits**: Queries the OSV (Open Source Vulnerabilities) database to find safety risks in your dependencies.
14
+ * ⚙️ **Ballast Installer**: Automatically extracts and links compilers/runtimes (like Node.js, Go, Python, Rust) to your path.
15
+ * 📦 **Monorepo / Multi-project Scanner**: Groups and maps nested modules dynamically within a monorepo structure.
16
+ * 🛰️ **Cloud IDE Scaffolding**: Runs `autodev containerize` to generate `.devcontainer.json` environment setups.
17
+ * 🔄 **Config Migrator**: Seamlessly upgrades legacy profile configurations to standard YAML.
18
+
19
+ ---
20
+
21
+ ## 📦 Installation & Quick Start
22
+
23
+ You can run AutoDev on the fly using Node's package executor, or install it globally.
24
+
25
+ ### 1. Run Instantly (No Installation)
26
+ Scan your workspace and bootstrap dependencies without installing anything permanently:
8
27
  ```bash
9
- npx @heetmehta18/autodev --help
28
+ npx @heetmehta18/autodev setup
10
29
  ```
11
30
 
12
- ### Install Globally
31
+ ### 2. Install Globally
32
+ Install the package globally for instant local terminal access:
13
33
  ```bash
14
34
  npm install -g @heetmehta18/autodev
35
+ ```
36
+
37
+ ### 3. Usage
38
+ Verify that the CLI is installed and ready:
39
+ ```bash
15
40
  autodev --help
16
41
  ```
17
42
 
18
- ## How It Works
19
- 1. **Platform Detection:** The JavaScript wrapper reads `process.platform` and `process.arch` to map the user's platform to the target release asset names (e.g. `linux/amd64`, `windows/arm64`, `darwin/arm64`).
20
- 2. **Dynamic Download:** If the native binary is not yet cached locally in this package's `bin/` directory, the wrapper automatically downloads the correct compressed release (`.tar.gz` or `.zip`) directly from the corresponding GitHub Release tag (matching the `package.json` version).
21
- 3. **Execution Delegation:** The wrapper spawns the native binary as a subprocess, forwarding all arguments, stdio streams, and exit codes. Future runs bypass the download step entirely for instant execution.
22
- 4. **Development DX Mode:** During local development, if a compiled binary is found under `packages/cli/bin/autodev`, the wrapper forwards execution directly to the local dev build, bypassing remote GitHub requests.
23
-
24
- ## Publishing
25
- To publish updates to the npm registry:
26
- 1. Ensure the package version matches the GitHub release tag:
27
- ```bash
28
- pnpm --filter=@heetmehta18/autodev version <new-version>
29
- ```
30
- 2. Publish to npm:
31
- ```bash
32
- pnpm --filter=@heetmehta18/autodev publish
33
- ```
43
+ ---
44
+
45
+ ## 🛠️ Main CLI Commands
46
+
47
+ ### 🔍 scan
48
+ Analyzes your current working directory for configuration markers, lockfiles, and monorepo folders:
49
+ ```bash
50
+ autodev scan
51
+ ```
52
+
53
+ ### 📦 setup
54
+ Scans the project and aligns your local development environment by downloading missing runtimes:
55
+ ```bash
56
+ autodev setup
57
+ autodev setup --yes # Skip confirmation prompts
58
+ ```
59
+
60
+ ### 🩺 doctor
61
+ Inspects your system configuration, checks tool versions against the `.autodev.lock.json` lockfile, and scans for exposed secrets (like AWS keys or GitHub tokens):
62
+ ```bash
63
+ autodev doctor
64
+ autodev doctor --fix # Restore lockfile mismatches automatically
65
+ ```
66
+
67
+ ### 🛡️ audit
68
+ Scans lockfiles and dependencies for known supply-chain vulnerabilities using the OSV database:
69
+ ```bash
70
+ autodev audit
71
+ ```
72
+
73
+ ### 💻 containerize
74
+ Generates dev container setup configurations (`.devcontainer.json`) and VSCode plugin recommendations based on the detected stack:
75
+ ```bash
76
+ autodev containerize
77
+ ```
78
+
79
+ ### 🔄 migrate
80
+ Upgrades legacy `.json` profile configs to the standard `.autodev.yaml` schema:
81
+ ```bash
82
+ autodev migrate
83
+ ```
84
+
85
+ ---
86
+
87
+ ## ⚙️ How It Works
88
+
89
+ 1. **Platform Detection**: The wrapper maps your OS and CPU architecture (e.g. `linux/amd64`, `darwin/arm64`) to the corresponding compiled Go binary.
90
+ 2. **Binary Caching**: Downloads the pre-compiled binary directly from the matching GitHub Release tag. Subsequent executions are run instantly from cache.
91
+ 3. **Process Delegation**: Delegated execution forwards all streams, signals, exit codes, and Model Context Protocol (MCP) servers seamlessly.
package/bin/index.js CHANGED
@@ -1,66 +1,71 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawnSync, execSync } = require('child_process');
4
- const path = require('path');
5
- const fs = require('fs');
6
- const os = require('os');
7
- const https = require('https');
8
- const http = require('http');
3
+ const { spawn, execSync, execFileSync } = require("child_process");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const os = require("os");
7
+ const https = require("https");
8
+ const http = require("http");
9
9
 
10
10
  // Determine OS & Arch mapping
11
11
  const platformMap = {
12
- darwin: 'darwin',
13
- linux: 'linux',
14
- win32: 'windows',
12
+ darwin: "darwin",
13
+ linux: "linux",
14
+ win32: "windows",
15
15
  };
16
16
 
17
17
  const archMap = {
18
- x64: 'amd64',
19
- arm64: 'arm64',
18
+ x64: "amd64",
19
+ arm64: "arm64",
20
20
  };
21
21
 
22
22
  const platform = platformMap[process.platform];
23
23
  const arch = archMap[process.arch];
24
24
 
25
25
  if (!platform || !arch) {
26
- console.error(`[autodev] Unsupported platform/architecture: ${process.platform}/${process.arch}`);
26
+ console.error(
27
+ `[autodev] Unsupported platform/architecture: ${process.platform}/${process.arch}`,
28
+ );
27
29
  process.exit(1);
28
30
  }
29
31
 
30
- const ext = platform === 'windows' ? 'zip' : 'tar.gz';
31
- const binaryName = platform === 'windows' ? 'autodev.exe' : 'autodev';
32
+ const ext = platform === "windows" ? "zip" : "tar.gz";
33
+ const binaryName = platform === "windows" ? "autodev.exe" : "autodev";
32
34
 
33
35
  // Version: prefer the latest GitHub release tag; fall back to package.json
34
- const pkgJson = require('../package.json');
36
+ const pkgJson = require("../package.json");
35
37
  const fallbackVersion = `v${pkgJson.version}`;
36
38
 
37
39
  function getLatestReleaseTag() {
38
40
  return new Promise((resolve) => {
39
41
  const options = {
40
- hostname: 'api.github.com',
41
- path: '/repos/HEETMEHTA18/autodev/releases/latest',
42
+ hostname: "api.github.com",
43
+ path: "/repos/HEETMEHTA18/autodev/releases/latest",
42
44
  headers: {
43
- 'User-Agent': 'autodev-npm-cli'
45
+ "User-Agent": "autodev-npm-cli",
44
46
  },
45
- timeout: 5000
47
+ timeout: 5000,
46
48
  };
47
49
 
48
- https.get(options, (res) => {
49
- let body = '';
50
- res.on('data', (chunk) => body += chunk);
51
- res.on('end', () => {
52
- try {
53
- const json = JSON.parse(body);
54
- if (json.tag_name) {
55
- resolve(json.tag_name);
56
- return;
57
- }
58
- } catch (_) {}
50
+ https
51
+ .get(options, (res) => {
52
+ let body = "";
53
+ res.on("data", (chunk) => (body += chunk));
54
+ res.on("end", () => {
55
+ try {
56
+ const json = JSON.parse(body);
57
+ const versionRegex = /^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
58
+ if (json.tag_name && versionRegex.test(json.tag_name)) {
59
+ resolve(json.tag_name);
60
+ return;
61
+ }
62
+ } catch (_) {}
63
+ resolve(fallbackVersion);
64
+ });
65
+ })
66
+ .on("error", () => {
59
67
  resolve(fallbackVersion);
60
68
  });
61
- }).on('error', () => {
62
- resolve(fallbackVersion);
63
- });
64
69
  });
65
70
  }
66
71
 
@@ -70,9 +75,9 @@ const binaryPath = path.join(binDir, binaryName);
70
75
 
71
76
  // Development fallback paths
72
77
  const devPaths = [
73
- path.join(__dirname, '..', '..', 'cli', 'bin', binaryName),
74
- path.join(__dirname, '..', '..', '..', 'bin', binaryName),
75
- path.join(__dirname, '..', '..', '..', 'packages', 'cli', 'bin', binaryName),
78
+ path.join(__dirname, "..", "..", "cli", "bin", binaryName),
79
+ path.join(__dirname, "..", "..", "..", "bin", binaryName),
80
+ path.join(__dirname, "..", "..", "..", "packages", "cli", "bin", binaryName),
76
81
  ];
77
82
 
78
83
  let activeBinaryPath = binaryPath;
@@ -92,35 +97,43 @@ for (const devPath of devPaths) {
92
97
  */
93
98
  function download(url, destPath, maxRedirects = 5) {
94
99
  return new Promise((resolve, reject) => {
95
- if (maxRedirects <= 0) return reject(new Error('Too many redirects'));
96
-
97
- const client = url.startsWith('https') ? https : http;
98
- client.get(url, (res) => {
99
- // Follow redirects (GitHub releases return 302)
100
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
101
- return download(res.headers.location, destPath, maxRedirects - 1)
102
- .then(resolve)
103
- .catch(reject);
104
- }
100
+ if (maxRedirects <= 0) return reject(new Error("Too many redirects"));
105
101
 
106
- if (res.statusCode !== 200) {
107
- return reject(new Error(`HTTP ${res.statusCode} from ${url}`));
108
- }
102
+ const client = url.startsWith("https") ? https : http;
103
+ client
104
+ .get(url, (res) => {
105
+ // Follow redirects (GitHub releases return 302)
106
+ if (
107
+ res.statusCode >= 300 &&
108
+ res.statusCode < 400 &&
109
+ res.headers.location
110
+ ) {
111
+ return download(res.headers.location, destPath, maxRedirects - 1)
112
+ .then(resolve)
113
+ .catch(reject);
114
+ }
109
115
 
110
- const fileStream = fs.createWriteStream(destPath);
111
- res.pipe(fileStream);
112
- fileStream.on('finish', () => {
113
- fileStream.close();
114
- resolve();
115
- });
116
- fileStream.on('error', reject);
117
- }).on('error', reject);
116
+ if (res.statusCode !== 200) {
117
+ return reject(new Error(`HTTP ${res.statusCode} from ${url}`));
118
+ }
119
+
120
+ const fileStream = fs.createWriteStream(destPath);
121
+ res.pipe(fileStream);
122
+ fileStream.on("finish", () => {
123
+ fileStream.close();
124
+ resolve();
125
+ });
126
+ fileStream.on("error", reject);
127
+ })
128
+ .on("error", reject);
118
129
  });
119
130
  }
120
131
 
121
132
  async function downloadBinary() {
122
133
  let version = await getLatestReleaseTag();
123
- console.log(`\n[autodev] Native binary not found. Downloading AutoDev ${version} for ${platform}/${arch}...`);
134
+ console.log(
135
+ `\n[autodev] Native binary not found. Downloading AutoDev ${version} for ${platform}/${arch}...`,
136
+ );
124
137
 
125
138
  if (!fs.existsSync(binDir)) {
126
139
  fs.mkdirSync(binDir, { recursive: true });
@@ -131,29 +144,40 @@ async function downloadBinary() {
131
144
  const archiveFile = `${archiveName}.${ext}`;
132
145
  let url = `https://github.com/HEETMEHTA18/autodev/releases/download/${version}/${archiveFile}`;
133
146
 
134
- const tempFile = path.join(os.tmpdir(), `autodev_download_${Date.now()}.${ext}`);
147
+ const tempFile = path.join(
148
+ os.tmpdir(),
149
+ `autodev_download_${Date.now()}.${ext}`,
150
+ );
135
151
 
136
152
  // Download using Node.js built-in HTTPS (handles redirects properly)
137
153
  try {
138
154
  console.log(`[autodev] Downloading from: ${url}`);
139
155
  await download(url, tempFile);
140
156
  } catch (err) {
141
- const stableFallback = 'v0.2.0';
157
+ const stableFallback = "v0.3.0";
142
158
  if (version !== stableFallback) {
143
- console.warn(`\n[autodev] Failed to download version ${version}: ${err.message}`);
144
- console.warn(`[autodev] Falling back to last known stable release: ${stableFallback}...`);
159
+ console.warn(
160
+ `\n[autodev] Failed to download version ${version}: ${err.message}`,
161
+ );
162
+ console.warn(
163
+ `[autodev] Falling back to last known stable release: ${stableFallback}...`,
164
+ );
145
165
  version = stableFallback;
146
166
  url = `https://github.com/HEETMEHTA18/autodev/releases/download/${version}/${archiveFile}`;
147
167
  try {
148
168
  console.log(`[autodev] Downloading from: ${url}`);
149
169
  await download(url, tempFile);
150
170
  } catch (retryErr) {
151
- console.error(`\n[autodev] Error downloading stable release asset: ${retryErr.message}`);
171
+ console.error(
172
+ `\n[autodev] Error downloading stable release asset: ${retryErr.message}`,
173
+ );
152
174
  console.error(`[autodev] Please verify your network connection.`);
153
175
  process.exit(1);
154
176
  }
155
177
  } else {
156
- console.error(`\n[autodev] Error downloading release asset: ${err.message}`);
178
+ console.error(
179
+ `\n[autodev] Error downloading release asset: ${err.message}`,
180
+ );
157
181
  console.error(`[autodev] URL: ${url}`);
158
182
  process.exit(1);
159
183
  }
@@ -162,13 +186,17 @@ async function downloadBinary() {
162
186
  try {
163
187
  // Verify the file was actually downloaded
164
188
  if (!fs.existsSync(tempFile)) {
165
- throw new Error('Download completed but file not found on disk.');
189
+ throw new Error("Download completed but file not found on disk.");
166
190
  }
167
191
  const stat = fs.statSync(tempFile);
168
192
  if (stat.size < 1000) {
169
- throw new Error(`Downloaded file is too small (${stat.size} bytes), likely an error page.`);
193
+ throw new Error(
194
+ `Downloaded file is too small (${stat.size} bytes), likely an error page.`,
195
+ );
170
196
  }
171
- console.log(`[autodev] Downloaded ${(stat.size / 1024 / 1024).toFixed(1)} MB`);
197
+ console.log(
198
+ `[autodev] Downloaded ${(stat.size / 1024 / 1024).toFixed(1)} MB`,
199
+ );
172
200
  } catch (err) {
173
201
  console.error(`\n[autodev] Error verifying download: ${err.message}`);
174
202
  process.exit(1);
@@ -177,14 +205,23 @@ async function downloadBinary() {
177
205
  // Extract
178
206
  console.log(`[autodev] Extracting binary...`);
179
207
  try {
180
- if (ext === 'zip') {
181
- if (process.platform === 'win32') {
182
- execSync(`powershell -Command "Expand-Archive -Path '${tempFile}' -DestinationPath '${binDir}' -Force"`, { stdio: 'inherit' });
208
+ if (ext === "zip") {
209
+ if (process.platform === "win32") {
210
+ const escapedTempFile = tempFile.replace(/'/g, "''");
211
+ const escapedBinDir = binDir.replace(/'/g, "''");
212
+ execSync(
213
+ `powershell -Command "Expand-Archive -Path '${escapedTempFile}' -DestinationPath '${escapedBinDir}' -Force"`,
214
+ { stdio: "inherit" },
215
+ );
183
216
  } else {
184
- execSync(`unzip -o "${tempFile}" -d "${binDir}"`, { stdio: 'inherit' });
217
+ execFileSync("unzip", ["-o", tempFile, "-d", binDir], {
218
+ stdio: "inherit",
219
+ });
185
220
  }
186
221
  } else {
187
- execSync(`tar -xzf "${tempFile}" -C "${binDir}"`, { stdio: 'inherit' });
222
+ execFileSync("tar", ["-xzf", tempFile, "-C", binDir], {
223
+ stdio: "inherit",
224
+ });
188
225
  }
189
226
 
190
227
  // Clean up temp archive
@@ -193,7 +230,7 @@ async function downloadBinary() {
193
230
  }
194
231
 
195
232
  // Set execution permissions on Linux/macOS
196
- if (process.platform !== 'win32' && fs.existsSync(binaryPath)) {
233
+ if (process.platform !== "win32" && fs.existsSync(binaryPath)) {
197
234
  fs.chmodSync(binaryPath, 0o755);
198
235
  }
199
236
  console.log(`[autodev] Installation successful.\n`);
@@ -213,11 +250,36 @@ async function main() {
213
250
  await downloadBinary();
214
251
  }
215
252
 
216
- // Forward execution
253
+ // Forward execution asynchronously to handle long-running / interactive processes properly
217
254
  const args = process.argv.slice(2);
218
- const result = spawnSync(activeBinaryPath, args, { stdio: 'inherit' });
255
+ const child = spawn(activeBinaryPath, args, { stdio: "inherit" });
256
+
257
+ // Forward termination signals to the child process (critical for long-running servers / MCP)
258
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
259
+ signals.forEach((signal) => {
260
+ process.on(signal, () => {
261
+ if (!child.killed) {
262
+ child.kill(signal);
263
+ }
264
+ });
265
+ });
266
+
267
+ child.on("close", (code, signal) => {
268
+ if (code !== null) {
269
+ process.exit(code);
270
+ } else if (signal) {
271
+ // Exit with standard 128 + signal number
272
+ const signalCodes = { SIGINT: 2, SIGTERM: 15, SIGHUP: 1, SIGQUIT: 3 };
273
+ process.exit(128 + (signalCodes[signal] || 0));
274
+ } else {
275
+ process.exit(0);
276
+ }
277
+ });
219
278
 
220
- process.exit(result.status ?? 0);
279
+ child.on("error", (err) => {
280
+ console.error(`[autodev] Failed to run binary: ${err.message}`);
281
+ process.exit(1);
282
+ });
221
283
  }
222
284
 
223
285
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heetmehta18/autodev",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "The App Store for Developers. Install languages, frameworks, and databases with a single command.",
5
5
  "main": "bin/index.js",
6
6
  "bin": {
@@ -9,6 +9,9 @@
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
+ "scripts": {
13
+ "test": "node bin/index.js --help"
14
+ },
12
15
  "repository": {
13
16
  "type": "git",
14
17
  "url": "git+https://github.com/HEETMEHTA18/autodev.git"
@@ -27,8 +30,5 @@
27
30
  "bugs": {
28
31
  "url": "https://github.com/HEETMEHTA18/autodev/issues"
29
32
  },
30
- "homepage": "https://github.com/HEETMEHTA18/autodev#readme",
31
- "scripts": {
32
- "test": "node bin/index.js --help"
33
- }
34
- }
33
+ "homepage": "https://github.com/HEETMEHTA18/autodev#readme"
34
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 AutoDev Contributors
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.