depfix-ai 0.1.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 (41) hide show
  1. package/dist/commands/audit.d.ts +7 -0
  2. package/dist/commands/audit.js +86 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +49 -0
  5. package/dist/lib/audit/npmAudit.d.ts +12 -0
  6. package/dist/lib/audit/npmAudit.js +25 -0
  7. package/dist/lib/audit/summarize.d.ts +29 -0
  8. package/dist/lib/audit/summarize.js +84 -0
  9. package/dist/lib/audit/types.d.ts +19 -0
  10. package/dist/lib/audit/types.js +2 -0
  11. package/dist/lib/config/store.d.ts +5 -0
  12. package/dist/lib/config/store.js +13 -0
  13. package/dist/lib/env/render.d.ts +2 -0
  14. package/dist/lib/env/render.js +37 -0
  15. package/dist/lib/env/scan.d.ts +4 -0
  16. package/dist/lib/env/scan.js +42 -0
  17. package/dist/lib/env/write.d.ts +1 -0
  18. package/dist/lib/env/write.js +110 -0
  19. package/dist/lib/git/commit.d.ts +1 -0
  20. package/dist/lib/git/commit.js +8 -0
  21. package/dist/lib/git/stash.d.ts +1 -0
  22. package/dist/lib/git/stash.js +7 -0
  23. package/dist/lib/git/status.d.ts +1 -0
  24. package/dist/lib/git/status.js +8 -0
  25. package/dist/lib/pm/detect.d.ts +2 -0
  26. package/dist/lib/pm/detect.js +25 -0
  27. package/dist/lib/pm/detect.test.d.ts +1 -0
  28. package/dist/lib/pm/detect.test.js +10 -0
  29. package/dist/lib/pm/run.d.ts +2 -0
  30. package/dist/lib/pm/run.js +13 -0
  31. package/dist/lib/pm/types.d.ts +10 -0
  32. package/dist/lib/pm/types.js +2 -0
  33. package/dist/lib/safety/backup.d.ts +1 -0
  34. package/dist/lib/safety/backup.js +10 -0
  35. package/dist/lib/safety/dryRun.d.ts +2 -0
  36. package/dist/lib/safety/dryRun.js +11 -0
  37. package/dist/lib/ui/log.d.ts +2 -0
  38. package/dist/lib/ui/log.js +14 -0
  39. package/dist/lib/ui/spinner.d.ts +1 -0
  40. package/dist/lib/ui/spinner.js +10 -0
  41. package/package.json +32 -0
@@ -0,0 +1,7 @@
1
+ import { Severity } from "../lib/audit/summarize";
2
+ export interface AuditFlags {
3
+ json: boolean;
4
+ severity?: Severity;
5
+ fail: boolean;
6
+ }
7
+ export declare function runAuditCommand(argv?: string[]): Promise<void>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAuditCommand = runAuditCommand;
4
+ const npmAudit_1 = require("../lib/audit/npmAudit");
5
+ const summarize_1 = require("../lib/audit/summarize");
6
+ const log_1 = require("../lib/ui/log");
7
+ function parseArgs(argv) {
8
+ const flags = {
9
+ json: false,
10
+ fail: false,
11
+ };
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const arg = argv[i];
14
+ switch (arg) {
15
+ case "--json":
16
+ flags.json = true;
17
+ break;
18
+ case "--severity": {
19
+ const value = argv[i + 1];
20
+ if (value && ["low", "moderate", "high", "critical"].includes(value)) {
21
+ flags.severity = value;
22
+ i++;
23
+ }
24
+ else {
25
+ (0, log_1.logError)("Invalid value for --severity. Use one of: low, moderate, high, critical.");
26
+ }
27
+ break;
28
+ }
29
+ case "--fail":
30
+ flags.fail = true;
31
+ break;
32
+ default:
33
+ (0, log_1.logError)(`Unknown flag or argument: ${arg}`);
34
+ break;
35
+ }
36
+ }
37
+ return flags;
38
+ }
39
+ async function runAuditCommand(argv = []) {
40
+ const flags = parseArgs(argv);
41
+ const severity = flags.severity ?? "low";
42
+ const { pm, rawJson, exitCode } = await (0, npmAudit_1.runNpmAuditJson)();
43
+ if (pm !== "npm") {
44
+ (0, log_1.logInfo)(`Audit is only implemented for npm right now. Detected ${pm}.`);
45
+ return;
46
+ }
47
+ if (!rawJson) {
48
+ (0, log_1.logError)("npm audit did not return JSON output.");
49
+ if (typeof exitCode === "number") {
50
+ process.exitCode = exitCode;
51
+ }
52
+ return;
53
+ }
54
+ let parsed;
55
+ try {
56
+ parsed = JSON.parse(rawJson);
57
+ }
58
+ catch {
59
+ (0, log_1.logError)("Failed to parse npm audit JSON output.");
60
+ if (flags.json) {
61
+ // Still honour the request for raw output.
62
+ // eslint-disable-next-line no-console
63
+ console.log(rawJson);
64
+ }
65
+ if (typeof exitCode === "number") {
66
+ process.exitCode = exitCode;
67
+ }
68
+ return;
69
+ }
70
+ const summaryOptions = { minSeverity: severity };
71
+ const summary = (0, summarize_1.summarizeNpmAudit)(parsed, summaryOptions);
72
+ (0, summarize_1.printAuditSummary)(summary);
73
+ if (flags.json) {
74
+ // eslint-disable-next-line no-console
75
+ console.log(rawJson);
76
+ }
77
+ if (flags.fail) {
78
+ const hasIssuesAboveThreshold = summary.counts.critical > 0 ||
79
+ (severity !== "critical" && summary.counts.high > 0) ||
80
+ (["low", "moderate"].includes(severity) && summary.counts.moderate > 0) ||
81
+ (severity === "low" && summary.counts.low > 0);
82
+ if (hasIssuesAboveThreshold) {
83
+ process.exitCode = 1;
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.main = main;
5
+ const audit_1 = require("./commands/audit");
6
+ const summarize_1 = require("./lib/audit/summarize");
7
+ const write_1 = require("./lib/env/write");
8
+ const backup_1 = require("./lib/safety/backup");
9
+ const log_1 = require("./lib/ui/log");
10
+ async function main(argv = process.argv.slice(2)) {
11
+ const [command, subcommand, ...rest] = argv;
12
+ try {
13
+ switch (command) {
14
+ case "audit":
15
+ await (0, audit_1.runAuditCommand)(rest);
16
+ break;
17
+ case "fix":
18
+ await (0, summarize_1.runFix)();
19
+ break;
20
+ case "env":
21
+ if (subcommand === "generate") {
22
+ await (0, write_1.runEnvGenerate)(rest);
23
+ }
24
+ else {
25
+ (0, log_1.logError)('Usage: depfix-ai env generate');
26
+ }
27
+ break;
28
+ case "onboard":
29
+ await (0, backup_1.runOnboard)();
30
+ break;
31
+ default:
32
+ (0, log_1.logInfo)("depfix-ai CLI");
33
+ (0, log_1.logInfo)("Available commands:");
34
+ (0, log_1.logInfo)(" audit");
35
+ (0, log_1.logInfo)(" fix");
36
+ (0, log_1.logInfo)(" env generate");
37
+ (0, log_1.logInfo)(" onboard");
38
+ break;
39
+ }
40
+ }
41
+ catch (err) {
42
+ (0, log_1.logError)("Unexpected error", err);
43
+ process.exitCode = 1;
44
+ }
45
+ }
46
+ if (require.main === module) {
47
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
48
+ main();
49
+ }
@@ -0,0 +1,12 @@
1
+ export interface NpmAuditResult {
2
+ pm: "npm" | string;
3
+ rawJson: string | undefined;
4
+ exitCode: number | undefined;
5
+ }
6
+ /**
7
+ * Run an npm audit in JSON mode.
8
+ *
9
+ * For v0.1.0 we only support npm; if another package manager
10
+ * is detected we return early without running anything.
11
+ */
12
+ export declare function runNpmAuditJson(cwd?: string): Promise<NpmAuditResult>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runNpmAuditJson = runNpmAuditJson;
4
+ const detect_1 = require("../pm/detect");
5
+ const run_1 = require("../pm/run");
6
+ /**
7
+ * Run an npm audit in JSON mode.
8
+ *
9
+ * For v0.1.0 we only support npm; if another package manager
10
+ * is detected we return early without running anything.
11
+ */
12
+ async function runNpmAuditJson(cwd = process.cwd()) {
13
+ const pm = (0, detect_1.detectPackageManager)(cwd);
14
+ if (pm !== "npm") {
15
+ return { pm, rawJson: undefined, exitCode: undefined };
16
+ }
17
+ const { stdout, exitCode } = await (0, run_1.runPmCommand)(pm, ["audit", "--json"], {
18
+ cwd,
19
+ });
20
+ return {
21
+ pm,
22
+ rawJson: stdout,
23
+ exitCode,
24
+ };
25
+ }
@@ -0,0 +1,29 @@
1
+ export type Severity = "low" | "moderate" | "high" | "critical";
2
+ export interface AuditSummaryOptions {
3
+ /**
4
+ * Minimum severity to include in the summary.
5
+ * Vulnerabilities below this level will be ignored.
6
+ */
7
+ minSeverity?: Severity;
8
+ }
9
+ export interface SeverityCounts {
10
+ low: number;
11
+ moderate: number;
12
+ high: number;
13
+ critical: number;
14
+ }
15
+ export interface PackageImpact {
16
+ name: string;
17
+ count: number;
18
+ }
19
+ export interface AuditSummary {
20
+ counts: SeverityCounts;
21
+ impactedPackages: PackageImpact[];
22
+ }
23
+ /**
24
+ * Normalise npm audit JSON into a simple summary that is reasonably
25
+ * robust across npm versions (v6/v7+).
26
+ */
27
+ export declare function summarizeNpmAudit(data: unknown, options?: AuditSummaryOptions): AuditSummary;
28
+ export declare function printAuditSummary(summary: AuditSummary): void;
29
+ export declare function runFix(): Promise<void>;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.summarizeNpmAudit = summarizeNpmAudit;
4
+ exports.printAuditSummary = printAuditSummary;
5
+ exports.runFix = runFix;
6
+ const log_1 = require("../ui/log");
7
+ const severityOrder = ["low", "moderate", "high", "critical"];
8
+ function severityAtLeast(a, min) {
9
+ return severityOrder.indexOf(a) >= severityOrder.indexOf(min);
10
+ }
11
+ function emptyCounts() {
12
+ return { low: 0, moderate: 0, high: 0, critical: 0 };
13
+ }
14
+ /**
15
+ * Normalise npm audit JSON into a simple summary that is reasonably
16
+ * robust across npm versions (v6/v7+).
17
+ */
18
+ function summarizeNpmAudit(data, options = {}) {
19
+ const minSeverity = options.minSeverity ?? "low";
20
+ const counts = emptyCounts();
21
+ const pkgCounts = new Map();
22
+ const anyData = data;
23
+ // Prefer the modern `vulnerabilities` shape if present.
24
+ if (anyData && typeof anyData === "object" && anyData.vulnerabilities) {
25
+ const vulns = anyData.vulnerabilities;
26
+ for (const [pkgName, vuln] of Object.entries(vulns)) {
27
+ const severity = vuln.severity;
28
+ if (!severity || !severityAtLeast(severity, minSeverity))
29
+ continue;
30
+ counts[severity] += 1;
31
+ pkgCounts.set(pkgName, (pkgCounts.get(pkgName) ?? 0) + 1);
32
+ }
33
+ }
34
+ else if (anyData && typeof anyData === "object" && anyData.advisories) {
35
+ // Legacy npm audit format with `advisories`.
36
+ const advisories = anyData.advisories;
37
+ for (const adv of Object.values(advisories)) {
38
+ const severity = adv.severity;
39
+ const moduleName = adv.module_name;
40
+ if (!severity || !moduleName || !severityAtLeast(severity, minSeverity))
41
+ continue;
42
+ counts[severity] += 1;
43
+ pkgCounts.set(moduleName, (pkgCounts.get(moduleName) ?? 0) + 1);
44
+ }
45
+ }
46
+ else if (anyData && anyData.metadata && anyData.metadata.vulnerabilities) {
47
+ // Fallback: just lift counts from metadata if available.
48
+ const metaCounts = anyData.metadata.vulnerabilities;
49
+ for (const sev of severityOrder) {
50
+ const value = metaCounts[sev];
51
+ if (typeof value === "number" && severityAtLeast(sev, minSeverity)) {
52
+ counts[sev] += value;
53
+ }
54
+ }
55
+ }
56
+ const impactedPackages = Array.from(pkgCounts.entries())
57
+ .map(([name, count]) => ({ name, count }))
58
+ .sort((a, b) => b.count - a.count)
59
+ .slice(0, 5);
60
+ return { counts, impactedPackages };
61
+ }
62
+ function printAuditSummary(summary) {
63
+ const { counts, impactedPackages } = summary;
64
+ (0, log_1.logInfo)("Vulnerability summary:");
65
+ (0, log_1.logInfo)(` low: ${counts.low}, moderate: ${counts.moderate}, high: ${counts.high}, critical: ${counts.critical}`);
66
+ if (impactedPackages.length > 0) {
67
+ (0, log_1.logInfo)("Top affected packages:");
68
+ for (const pkg of impactedPackages) {
69
+ (0, log_1.logInfo)(` ${pkg.name}: ${pkg.count} issue(s)`);
70
+ }
71
+ }
72
+ else {
73
+ (0, log_1.logInfo)("No vulnerable packages found at or above the selected severity.");
74
+ }
75
+ (0, log_1.logInfo)("What to do next:");
76
+ (0, log_1.logInfo)(" - Run `npm audit fix` to apply safe automatic fixes.");
77
+ (0, log_1.logInfo)(" - For remaining issues, review advisories and consider upgrading major versions or replacing packages.");
78
+ (0, log_1.logInfo)(" - If you cannot upgrade immediately, consider using overrides/resolutions with care and track them for cleanup.");
79
+ }
80
+ // Temporary placeholder to keep the existing `fix` command wired up.
81
+ // This can later be replaced with a real remediation flow.
82
+ async function runFix() {
83
+ (0, log_1.logInfo)("Running depfix-ai fix (not implemented yet).");
84
+ }
@@ -0,0 +1,19 @@
1
+ export type Severity = "low" | "moderate" | "high" | "critical";
2
+ export interface SeverityCounts {
3
+ low: number;
4
+ moderate: number;
5
+ high: number;
6
+ critical: number;
7
+ }
8
+ export interface PackageSummary {
9
+ name: string;
10
+ count: number;
11
+ highestSeverity: Severity;
12
+ }
13
+ export interface AuditSummary {
14
+ severityCounts: SeverityCounts;
15
+ topPackages: PackageSummary[];
16
+ }
17
+ export interface SummarizeOptions {
18
+ minSeverity?: Severity;
19
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface Config {
2
+ dryRun: boolean;
3
+ }
4
+ export declare function getConfig(): Config;
5
+ export declare function setConfig(next: Partial<Config>): void;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConfig = getConfig;
4
+ exports.setConfig = setConfig;
5
+ let currentConfig = {
6
+ dryRun: false,
7
+ };
8
+ function getConfig() {
9
+ return currentConfig;
10
+ }
11
+ function setConfig(next) {
12
+ currentConfig = { ...currentConfig, ...next };
13
+ }
@@ -0,0 +1,2 @@
1
+ import { EnvScanResult } from "./scan";
2
+ export declare function renderEnv(result: EnvScanResult): string;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderEnv = renderEnv;
4
+ const GROUPS = [
5
+ { prefix: "DB_", heading: "Database" },
6
+ { prefix: "REDIS_", heading: "Redis" },
7
+ { prefix: "AWS_", heading: "AWS" },
8
+ { prefix: "SMTP_", heading: "SMTP" },
9
+ { prefix: "NEXT_PUBLIC_", heading: "Next.js public env" },
10
+ { prefix: "VITE_", heading: "Vite env" },
11
+ ];
12
+ function renderEnv(result) {
13
+ const remaining = new Set(result.keys);
14
+ const groups = [];
15
+ for (const { prefix, heading } of GROUPS) {
16
+ const keys = result.keys.filter((k) => k.startsWith(prefix));
17
+ if (keys.length === 0)
18
+ continue;
19
+ keys.forEach((k) => remaining.delete(k));
20
+ groups.push({ heading, keys });
21
+ }
22
+ if (remaining.size > 0) {
23
+ groups.push({
24
+ heading: "Other",
25
+ keys: Array.from(remaining).sort(),
26
+ });
27
+ }
28
+ const lines = [];
29
+ for (const group of groups) {
30
+ lines.push(`# ${group.heading}`);
31
+ for (const key of group.keys) {
32
+ lines.push(`${key}=`);
33
+ }
34
+ lines.push("");
35
+ }
36
+ return lines.join("\n").trimEnd() + (lines.length ? "\n" : "");
37
+ }
@@ -0,0 +1,4 @@
1
+ export interface EnvScanResult {
2
+ keys: string[];
3
+ }
4
+ export declare function scanEnv(cwd?: string): Promise<EnvScanResult>;
@@ -0,0 +1,42 @@
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.scanEnv = scanEnv;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const fast_glob_1 = __importDefault(require("fast-glob"));
9
+ const INCLUDE_GLOBS = [
10
+ "src/**/*.{js,jsx,ts,tsx}",
11
+ "app/**/*.{js,jsx,ts,tsx}",
12
+ "server/**/*.{js,jsx,ts,tsx}",
13
+ "pages/**/*.{js,jsx,ts,tsx}",
14
+ ];
15
+ const EXCLUDE_GLOBS = ["**/node_modules/**", "**/dist/**", "**/.next/**", "**/build/**", "**/coverage/**"];
16
+ const PROCESS_ENV_REGEX = /process\.env\.([A-Z0-9_]+)/g;
17
+ const IMPORT_META_ENV_REGEX = /import\.meta\.env\.([A-Z0-9_]+)/g;
18
+ async function scanEnv(cwd = process.cwd()) {
19
+ const files = await (0, fast_glob_1.default)(INCLUDE_GLOBS, {
20
+ cwd,
21
+ ignore: EXCLUDE_GLOBS,
22
+ absolute: true,
23
+ });
24
+ const keys = new Set();
25
+ for (const file of files) {
26
+ let content;
27
+ try {
28
+ content = await promises_1.default.readFile(file, "utf8");
29
+ }
30
+ catch {
31
+ continue;
32
+ }
33
+ let match;
34
+ while ((match = PROCESS_ENV_REGEX.exec(content)) !== null) {
35
+ keys.add(match[1]);
36
+ }
37
+ while ((match = IMPORT_META_ENV_REGEX.exec(content)) !== null) {
38
+ keys.add(match[1]);
39
+ }
40
+ }
41
+ return { keys: Array.from(keys).sort() };
42
+ }
@@ -0,0 +1 @@
1
+ export declare function runEnvGenerate(argv?: string[]): Promise<void>;
@@ -0,0 +1,110 @@
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.runEnvGenerate = runEnvGenerate;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const scan_1 = require("./scan");
10
+ const render_1 = require("./render");
11
+ const log_1 = require("../ui/log");
12
+ function parseEnvArgs(argv) {
13
+ const flags = {
14
+ out: ".env.example",
15
+ create: false,
16
+ force: false,
17
+ check: false,
18
+ };
19
+ for (let i = 0; i < argv.length; i++) {
20
+ const arg = argv[i];
21
+ switch (arg) {
22
+ case "--out":
23
+ if (argv[i + 1]) {
24
+ flags.out = argv[i + 1];
25
+ i++;
26
+ }
27
+ else {
28
+ (0, log_1.logError)("Missing value for --out");
29
+ }
30
+ break;
31
+ case "--create":
32
+ flags.create = true;
33
+ break;
34
+ case "--force":
35
+ flags.force = true;
36
+ break;
37
+ case "--check":
38
+ flags.check = true;
39
+ break;
40
+ default:
41
+ (0, log_1.logError)(`Unknown flag or argument: ${arg}`);
42
+ break;
43
+ }
44
+ }
45
+ return flags;
46
+ }
47
+ async function fileExists(p) {
48
+ try {
49
+ await promises_1.default.stat(p);
50
+ return true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ function parseEnvKeysFromExample(content) {
57
+ const keys = new Set();
58
+ const lines = content.split(/\r?\n/);
59
+ for (const line of lines) {
60
+ const trimmed = line.trim();
61
+ if (!trimmed || trimmed.startsWith("#"))
62
+ continue;
63
+ const eqIndex = trimmed.indexOf("=");
64
+ if (eqIndex <= 0)
65
+ continue;
66
+ const key = trimmed.slice(0, eqIndex).trim();
67
+ if (key)
68
+ keys.add(key);
69
+ }
70
+ return keys;
71
+ }
72
+ async function runEnvGenerate(argv = []) {
73
+ const flags = parseEnvArgs(argv);
74
+ const cwd = process.cwd();
75
+ const outPath = node_path_1.default.resolve(cwd, flags.out);
76
+ const envPath = node_path_1.default.resolve(cwd, ".env");
77
+ const scanResult = await (0, scan_1.scanEnv)(cwd);
78
+ if (flags.check) {
79
+ if (!(await fileExists(outPath))) {
80
+ (0, log_1.logError)(`${flags.out} does not exist. Run 'depfix-ai env generate' to create it.`);
81
+ process.exitCode = 1;
82
+ return;
83
+ }
84
+ const existing = await promises_1.default.readFile(outPath, "utf8");
85
+ const existingKeys = parseEnvKeysFromExample(existing);
86
+ const missing = scanResult.keys.filter((k) => !existingKeys.has(k));
87
+ if (missing.length > 0) {
88
+ (0, log_1.logError)(`${flags.out} is missing the following environment variables: ${missing.join(", ")}`);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ (0, log_1.logInfo)(`${flags.out} contains all required environment variables.`);
93
+ return;
94
+ }
95
+ // Always (re)write the template example file.
96
+ const exampleContent = (0, render_1.renderEnv)(scanResult);
97
+ await promises_1.default.writeFile(outPath, exampleContent, "utf8");
98
+ (0, log_1.logInfo)(`Wrote environment template to ${outPath}`);
99
+ if (flags.create) {
100
+ const envExists = await fileExists(envPath);
101
+ if (envExists && !flags.force) {
102
+ (0, log_1.logInfo)(`.env already exists; not overwriting. Use --force to overwrite.`);
103
+ }
104
+ else {
105
+ const envContent = scanResult.keys.map((k) => `${k}=`).join("\n") + "\n";
106
+ await promises_1.default.writeFile(envPath, envContent, "utf8");
107
+ (0, log_1.logInfo)(`Wrote ${envExists ? "updated" : "new"} .env file to ${envPath}`);
108
+ }
109
+ }
110
+ }
@@ -0,0 +1 @@
1
+ export declare function gitCommitAll(message: string, cwd?: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitCommitAll = gitCommitAll;
4
+ const execa_1 = require("execa");
5
+ async function gitCommitAll(message, cwd = process.cwd()) {
6
+ await (0, execa_1.execa)("git", ["add", "-A"], { cwd, stdio: "inherit" });
7
+ await (0, execa_1.execa)("git", ["commit", "-m", message], { cwd, stdio: "inherit" });
8
+ }
@@ -0,0 +1 @@
1
+ export declare function gitStashSave(message?: string, cwd?: string): Promise<void>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitStashSave = gitStashSave;
4
+ const execa_1 = require("execa");
5
+ async function gitStashSave(message = "depfix-ai backup", cwd = process.cwd()) {
6
+ await (0, execa_1.execa)("git", ["stash", "push", "-m", message], { cwd, stdio: "inherit" });
7
+ }
@@ -0,0 +1 @@
1
+ export declare function gitStatusShort(cwd?: string): Promise<string>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitStatusShort = gitStatusShort;
4
+ const execa_1 = require("execa");
5
+ async function gitStatusShort(cwd = process.cwd()) {
6
+ const { stdout } = await (0, execa_1.execa)("git", ["status", "--short"], { cwd });
7
+ return stdout;
8
+ }
@@ -0,0 +1,2 @@
1
+ import { PackageManager } from "./types";
2
+ export declare function detectPackageManager(cwd?: string): PackageManager;
@@ -0,0 +1,25 @@
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.detectPackageManager = detectPackageManager;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function detectPackageManager(cwd = process.cwd()) {
10
+ // Priority:
11
+ // 1. pnpm (pnpm-lock.yaml)
12
+ // 2. yarn (yarn.lock)
13
+ // 3. npm (package-lock.json)
14
+ // 4. bun (bun.lockb)
15
+ if (node_fs_1.default.existsSync(node_path_1.default.join(cwd, "pnpm-lock.yaml")))
16
+ return "pnpm";
17
+ if (node_fs_1.default.existsSync(node_path_1.default.join(cwd, "yarn.lock")))
18
+ return "yarn";
19
+ if (node_fs_1.default.existsSync(node_path_1.default.join(cwd, "package-lock.json")))
20
+ return "npm";
21
+ if (node_fs_1.default.existsSync(node_path_1.default.join(cwd, "bun.lockb")))
22
+ return "bun";
23
+ // Fallback to npm if no known lockfile is present.
24
+ return "npm";
25
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const detect_1 = require("./detect");
5
+ (0, vitest_1.describe)("detectPackageManager", () => {
6
+ (0, vitest_1.it)("returns npm for this repository", () => {
7
+ const pm = (0, detect_1.detectPackageManager)();
8
+ (0, vitest_1.expect)(pm).toBe("npm");
9
+ });
10
+ });
@@ -0,0 +1,2 @@
1
+ import { PackageManager, RunOptions, RunResult } from "./types";
2
+ export declare function runPmCommand(pm: PackageManager, args: string[], options?: RunOptions): Promise<RunResult>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runPmCommand = runPmCommand;
4
+ const execa_1 = require("execa");
5
+ async function runPmCommand(pm, args, options = {}) {
6
+ const { stdout, stderr, exitCode } = await (0, execa_1.execa)(pm, args, {
7
+ cwd: options.cwd,
8
+ stdio: options.stdio ?? "pipe",
9
+ // Never throw on non‑zero exit codes; always resolve with the result.
10
+ reject: false,
11
+ });
12
+ return { stdout, stderr, exitCode };
13
+ }
@@ -0,0 +1,10 @@
1
+ export type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
2
+ export interface RunOptions {
3
+ cwd?: string;
4
+ stdio?: "inherit" | "pipe";
5
+ }
6
+ export interface RunResult {
7
+ stdout?: string;
8
+ stderr?: string;
9
+ exitCode?: number;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export declare function runOnboard(): Promise<void>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runOnboard = runOnboard;
4
+ const stash_1 = require("../git/stash");
5
+ const log_1 = require("../ui/log");
6
+ async function runOnboard() {
7
+ (0, log_1.logInfo)("Onboarding project with depfix-ai (minimal stub).");
8
+ await (0, stash_1.gitStashSave)("depfix-ai onboarding backup");
9
+ (0, log_1.logInfo)("Created git stash as a safety backup.");
10
+ }
@@ -0,0 +1,2 @@
1
+ export declare function startDryRun(): void;
2
+ export declare function endDryRun(): void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startDryRun = startDryRun;
4
+ exports.endDryRun = endDryRun;
5
+ const log_1 = require("../ui/log");
6
+ function startDryRun() {
7
+ (0, log_1.logInfo)("Starting dry run (no changes will be written).");
8
+ }
9
+ function endDryRun() {
10
+ (0, log_1.logInfo)("Finished dry run.");
11
+ }
@@ -0,0 +1,2 @@
1
+ export declare function logInfo(message: string, ...rest: unknown[]): void;
2
+ export declare function logError(message: string, ...rest: unknown[]): void;
@@ -0,0 +1,14 @@
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.logInfo = logInfo;
7
+ exports.logError = logError;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ function logInfo(message, ...rest) {
10
+ console.log(chalk_1.default.cyan("[depfix-ai]"), message, ...rest);
11
+ }
12
+ function logError(message, ...rest) {
13
+ console.error(chalk_1.default.red("[depfix-ai]"), message, ...rest);
14
+ }
@@ -0,0 +1 @@
1
+ export declare function createSpinner(text: string): import("ora").Ora;
@@ -0,0 +1,10 @@
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.createSpinner = createSpinner;
7
+ const ora_1 = __importDefault(require("ora"));
8
+ function createSpinner(text) {
9
+ return (0, ora_1.default)({ text });
10
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "depfix-ai",
3
+ "version": "0.1.1",
4
+ "scripts": {
5
+ "build": "tsc -b",
6
+ "test": "vitest run",
7
+ "prepack": "npm run build",
8
+ "version:patch": "npm version patch",
9
+ "version:minor": "npm version minor",
10
+ "version:major": "npm version major"
11
+ },
12
+ "files": [
13
+ "dist/**"
14
+ ],
15
+ "bin": {
16
+ "depfix-ai": "dist/index.js"
17
+ },
18
+ "dependencies": {
19
+ "chalk": "^5.6.2",
20
+ "execa": "^9.6.1",
21
+ "fast-glob": "^3.3.3",
22
+ "ora": "^9.3.0",
23
+ "semver": "^7.7.3"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.2.0",
27
+ "eslint": "^9.39.2",
28
+ "prettier": "^3.8.1",
29
+ "typescript": "^5.9.3",
30
+ "vitest": "^4.0.18"
31
+ }
32
+ }