devfix-cli 1.0.0 β†’ 1.0.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.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # DevFix CLI πŸš€
2
+ AI-powered CLI tool that helps you debug errors, logs, and DevOps issues directly from your terminal.
3
+
4
+ DevFix analyzes your error output (Node, Docker, Kubernetes, Minikube, Git, etc.) and suggests the most likely fix with copy-paste commands.
5
+
6
+ ---
7
+
8
+ ## ✨ Features
9
+
10
+ - πŸ” Login once (saved locally for 7 days)
11
+ - πŸ€– Uses OpenRouter API (supports many models)
12
+ - 🧠 Auto-detects stack (Node, Docker, Kubernetes, Git, Python, React)
13
+ - πŸ“¦ Optional auto context mode (`--context`)
14
+ - 🎬 Animated DevFix logo during analysis (Minikube-style)
15
+ - 🧾 Clean Markdown output in terminal
16
+ - 🧩 Works on macOS / Linux / Windows
17
+
18
+ ---
19
+
20
+ ### 1) Install DevFix globally
21
+ ```bash
22
+ npm install -g devfix
23
+ ```
24
+
25
+ Check installation:
26
+ ```bash
27
+ devfix --version
28
+ ```
29
+
30
+ ---
31
+
32
+ ### 2) Login (required)
33
+
34
+ DevFix needs your OpenRouter API key to work.
35
+
36
+ Run:
37
+ ```bash
38
+ devfix login
39
+ ```
40
+
41
+ It will ask for:
42
+ - Username
43
+ - Email
44
+ - OpenRouter API Key
45
+
46
+ Your login is saved locally for **7 days**, so you don’t need to login again daily.
47
+
48
+ ---
49
+
50
+ ### 3) Start using DevFix
51
+
52
+ Analyze an error directly:
53
+ ```bash
54
+ devfix analyze "npm install failing"
55
+ ```
56
+
57
+ Analyze with automatic context collection (recommended):
58
+ ```bash
59
+ devfix analyze "minikube ingress not working" --context
60
+ ```
61
+
62
+ Analyze a log file:
63
+ ```bash
64
+ devfix analyze --file error.log
65
+ ```
66
+
67
+ ---
68
+
69
+ ### 4) Session commands
70
+
71
+ Check current login:
72
+ ```bash
73
+ devfix whoami
74
+ ```
75
+
76
+ Logout:
77
+ ```bash
78
+ devfix logout
79
+ ```
80
+
81
+ scan -> collects context + auto errors:
82
+ ```bash
83
+ devfix scan
84
+ ```
85
+
86
+ scan preview -> shows what it found:
87
+ ```bash
88
+ devfix scan --preview
89
+ ```
90
+
91
+ sacn analyze -> Send scan results to AI:
92
+ ```bash
93
+ devfix scan -a
94
+ ```
95
+
package/bin/index.js CHANGED
@@ -5,6 +5,7 @@ import { loginCommand } from "../src/commands/login.js";
5
5
  import { logoutCommand } from "../src/commands/logout.js";
6
6
  import { whoamiCommand } from "../src/commands/whoami.js";
7
7
  import { analyzeCommand } from "../src/commands/analyze.js";
8
+ import { scanCommand } from "../src/commands/scan.js";
8
9
 
9
10
 
10
11
  const program = new Command();
@@ -38,4 +39,18 @@ program
38
39
  });
39
40
  });
40
41
 
42
+ program
43
+ .command("scan")
44
+ .description("Scan your system/project for recent errors and context")
45
+ .option("-p, --preview", "Preview collected data only (recommended)")
46
+ .option("-a, --analyze", "Send scan results to AI")
47
+ .option("-m, --model <model>", "OpenRouter model override")
48
+ .action((options) => {
49
+ scanCommand({
50
+ preview: options.preview,
51
+ analyze: options.analyze,
52
+ model: options.model,
53
+ });
54
+ });
55
+
41
56
  program.parse(process.argv);
package/package.json CHANGED
@@ -18,5 +18,5 @@
18
18
  "marked-terminal": "^7.3.0",
19
19
  "ora": "^9.3.0"
20
20
  },
21
- "version": "1.0.0"
21
+ "version": "1.0.1"
22
22
  }
@@ -10,6 +10,7 @@ import TerminalRenderer from "marked-terminal";
10
10
  import { readConfig } from "../utils/config.js";
11
11
  import { isSessionValid } from "../utils/session.js";
12
12
  import { decrypt } from "../utils/cryptoStore.js";
13
+ import { collectAutoError } from "../utils/autoError.js";
13
14
 
14
15
  import { collectContext } from "../utils/context.js";
15
16
  import { detectStack } from "../utils/detectStack.js";
@@ -44,10 +45,18 @@ export async function analyzeCommand({ text, file, stack, model, useContext }) {
44
45
  input = fs.readFileSync(file, "utf-8");
45
46
  }
46
47
 
47
- if (!input || input.trim().length < 2) {
48
- console.log(chalk.red("\n❌ Please provide error text or use --file\n"));
49
- process.exit(1);
50
- }
48
+ // If no text + no file, try auto error collection
49
+ if ((!input || input.trim().length < 2) && useContext) {
50
+ const autoErr = collectAutoError();
51
+ if (autoErr) input = autoErr;
52
+ }
53
+
54
+ if (!input || input.trim().length < 2) {
55
+ console.log(chalk.red("\n❌ Please provide error text or use --file\n"));
56
+ console.log(chalk.gray("Tip: You can also run: devfix analyze --context"));
57
+ process.exit(1);
58
+ }
59
+
51
60
 
52
61
  const detected = stack || detectStack(input);
53
62
  const usedModel = model || "openai/gpt-4o-mini";
@@ -0,0 +1,94 @@
1
+ import chalk from "chalk";
2
+ import boxen from "boxen";
3
+
4
+ import { readConfig } from "../utils/config.js";
5
+ import { isSessionValid } from "../utils/session.js";
6
+ import { decrypt } from "../utils/cryptoStore.js";
7
+
8
+ import { collectContext } from "../utils/context.js";
9
+ import { collectAutoError } from "../utils/autoError.js";
10
+ import { detectStack } from "../utils/detectStack.js";
11
+ import { buildPrompt } from "../utils/prompt.js";
12
+ import { askAI } from "../utils/ai.js";
13
+
14
+ export async function scanCommand({ preview, analyze, model }) {
15
+ const config = readConfig();
16
+
17
+ if (!isSessionValid(config)) {
18
+ console.log(chalk.red("\n❌ Not logged in or session expired.\nRun: devfix login\n"));
19
+ process.exit(1);
20
+ }
21
+
22
+ const apiKey = decrypt(config.apiKeyEncrypted);
23
+ if (!apiKey) {
24
+ console.log(chalk.red("\n❌ API key missing. Run: devfix login\n"));
25
+ process.exit(1);
26
+ }
27
+
28
+ const context = collectContext();
29
+ const autoErr = collectAutoError();
30
+
31
+ const detected = detectStack(autoErr || "");
32
+ const usedModel = model || "openai/gpt-4o-mini";
33
+
34
+ console.log(
35
+ boxen(chalk.bold.cyan("πŸ”Ž DevFix Scan Results"), {
36
+ padding: 1,
37
+ borderStyle: "round",
38
+ })
39
+ );
40
+
41
+ console.log(chalk.white("\nContext collected:"));
42
+ console.log(chalk.gray(JSON.stringify(context, null, 2)));
43
+
44
+ console.log(chalk.white("\nAuto error detected:"));
45
+ if (autoErr) {
46
+ console.log(chalk.yellow(autoErr.slice(0, 2500)));
47
+ if (autoErr.length > 2500) console.log(chalk.gray("\n...trimmed output...\n"));
48
+ } else {
49
+ console.log(chalk.red("❌ No recent error log found."));
50
+ }
51
+
52
+ console.log(chalk.white("\nDetected stack: ") + chalk.cyan(detected));
53
+ console.log(chalk.white("Model: ") + chalk.magenta(usedModel));
54
+ console.log();
55
+
56
+ // If only preview requested
57
+ if (preview && !analyze) {
58
+ console.log(chalk.green("βœ… Preview complete. Nothing was sent to AI.\n"));
59
+ return;
60
+ }
61
+
62
+ // If analyze requested
63
+ if (analyze) {
64
+ if (!autoErr) {
65
+ console.log(chalk.red("\n❌ No error found to analyze.\n"));
66
+ console.log(chalk.gray("Tip: Run devfix analyze \"your error\" --context\n"));
67
+ process.exit(1);
68
+ }
69
+
70
+ console.log(chalk.cyan("πŸ€– Sending scan data to AI...\n"));
71
+
72
+ const prompt = buildPrompt({
73
+ stack: detected,
74
+ input: autoErr,
75
+ context,
76
+ });
77
+
78
+ const answer = await askAI({
79
+ apiKey,
80
+ model: usedModel,
81
+ prompt,
82
+ });
83
+
84
+ console.log(
85
+ boxen(chalk.bold.green("βœ… DevFix Suggested Fix"), {
86
+ padding: 1,
87
+ borderStyle: "round",
88
+ })
89
+ );
90
+
91
+ console.log(answer);
92
+ console.log();
93
+ }
94
+ }
@@ -0,0 +1,53 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { execSync } from "child_process";
5
+
6
+ function safeExec(cmd) {
7
+ try {
8
+ return execSync(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+
14
+ export function collectAutoError() {
15
+ const cwd = process.cwd();
16
+
17
+ // 1) npm debug log (most common)
18
+ const npmLog = path.join(os.homedir(), ".npm", "_logs");
19
+ if (fs.existsSync(npmLog)) {
20
+ const files = fs
21
+ .readdirSync(npmLog)
22
+ .filter((f) => f.endsWith(".log"))
23
+ .map((f) => ({
24
+ name: f,
25
+ full: path.join(npmLog, f),
26
+ time: fs.statSync(path.join(npmLog, f)).mtimeMs,
27
+ }))
28
+ .sort((a, b) => b.time - a.time);
29
+
30
+ if (files.length > 0) {
31
+ const latest = files[0].full;
32
+ const content = fs.readFileSync(latest, "utf-8").slice(-6000);
33
+ return `πŸ“Œ Auto-collected npm debug log:\nFile: ${latest}\n\n${content}`;
34
+ }
35
+ }
36
+
37
+ // 2) Git status (sometimes shows merge conflicts)
38
+ const git = safeExec("git status --porcelain=v1");
39
+ if (git && git.trim().length > 0) {
40
+ return `πŸ“Œ Auto-collected Git status:\n\n${git}`;
41
+ }
42
+
43
+ // 3) Kubernetes events (if cluster exists)
44
+ const events = safeExec(
45
+ "kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -n 25"
46
+ );
47
+ if (events) {
48
+ return `πŸ“Œ Auto-collected Kubernetes events:\n\n${events}`;
49
+ }
50
+
51
+ // Nothing found
52
+ return null;
53
+ }