dep-brain 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## Unreleased
6
+
7
+ - Workspace-aware analysis for npm workspaces.
8
+ - Config loading and CI policy controls.
9
+ - Improved duplicate detection and unused dependency heuristics.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vijay prakash
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,197 @@
1
+ # Dependency Brain
2
+
3
+ [![npm version](https://img.shields.io/npm/v/dep-brain)](https://www.npmjs.com/package/dep-brain)
4
+ [![npm downloads](https://img.shields.io/npm/dm/dep-brain)](https://www.npmjs.com/package/dep-brain)
5
+ [![license](https://img.shields.io/npm/l/dep-brain)](LICENSE)
6
+
7
+ `dep-brain` is a CLI and library for analyzing dependency health in JavaScript and TypeScript projects.
8
+
9
+ ## Vision
10
+
11
+ `npm audit + depcheck + dedupe + intelligence = one tool`
12
+
13
+ ## What It Does
14
+
15
+ - Detect duplicate dependencies from `package-lock.json`
16
+ - Detect likely unused dependencies from source imports and scripts
17
+ - Detect outdated packages
18
+ - Highlight dependency risk signals
19
+ - Generate a simple project health score
20
+ - Output reports in human-readable or JSON format
21
+
22
+ ## Current MVP Features
23
+
24
+ - Duplicate dependency detection with lockfile instance tracking
25
+ - Unused dependency detection with runtime vs dev-tool heuristics
26
+ - Outdated dependency reporting with `major`, `minor`, and `patch` classification
27
+ - Risk analysis based on npm package metadata
28
+ - Config loading from `depbrain.config.json`
29
+ - Ignore rules for noisy dependencies and checks
30
+ - CI-friendly policy evaluation with non-zero exit codes
31
+ - Workspace-aware analysis for npm workspaces
32
+ - Console reporting
33
+ - JSON output via `--json`
34
+ - Library entrypoint for programmatic use
35
+
36
+ ## CLI Usage
37
+
38
+ ```bash
39
+ npm install -g dep-brain
40
+ dep-brain analyze
41
+
42
+ npx dep-brain analyze
43
+ npx dep-brain analyze --json
44
+ npx dep-brain analyze ./path-to-project
45
+ npx dep-brain analyze --config depbrain.config.json
46
+ npx dep-brain analyze --min-score 90 --fail-on-risks
47
+ npx dep-brain analyze ./path-to-project --fail-on-unused --json
48
+
49
+ dep-brain config
50
+ dep-brain config --config depbrain.config.json
51
+
52
+ dep-brain help
53
+ dep-brain analyze --help
54
+ ```
55
+
56
+ ## Workspaces
57
+
58
+ If the root `package.json` defines `workspaces`, `dep-brain` analyzes each workspace package and reports per-package results. Aggregated counts are still shown at the top-level summary.
59
+
60
+ ## Example Output
61
+
62
+ ```text
63
+ Project Health: 78/100
64
+ Path: /your/project
65
+ Policy: FAIL
66
+
67
+ WARN Duplicates: 2
68
+ OK Unused: 0
69
+ WARN Outdated: 3
70
+ OK Risks: 0
71
+
72
+ Duplicate dependencies:
73
+ - ansi-regex: 5.0.1, 6.0.1
74
+
75
+ Outdated dependencies:
76
+ - chalk: ^4.1.2 -> 5.4.1 [major]
77
+
78
+ Policy reasons:
79
+ - Score 78 is below minimum 90
80
+
81
+ Suggestions:
82
+ - Consider consolidating ansi-regex to one version
83
+ - Review chalk: ^4.1.2 -> 5.4.1 (major)
84
+ ```
85
+
86
+ ## JSON Output
87
+
88
+ ```bash
89
+ dep-brain analyze --json
90
+ ```
91
+
92
+ ## Config File
93
+
94
+ Create a `depbrain.config.json` file in the project root:
95
+
96
+ ```json
97
+ {
98
+ "ignore": {
99
+ "unused": ["eslint"],
100
+ "outdated": ["typescript"]
101
+ },
102
+ "policy": {
103
+ "minScore": 90,
104
+ "failOnUnused": true,
105
+ "failOnRisks": true
106
+ },
107
+ "report": {
108
+ "maxSuggestions": 3
109
+ }
110
+ }
111
+ ```
112
+
113
+ Supported sections:
114
+
115
+ - `ignore.dependencies`
116
+ - `ignore.devDependencies`
117
+ - `ignore.unused`
118
+ - `ignore.duplicates`
119
+ - `ignore.outdated`
120
+ - `ignore.risks`
121
+ - `policy.minScore`
122
+ - `policy.failOnDuplicates`
123
+ - `policy.failOnUnused`
124
+ - `policy.failOnOutdated`
125
+ - `policy.failOnRisks`
126
+ - `report.maxSuggestions`
127
+
128
+ Sample config file:
129
+
130
+ - `depbrain.config.json`
131
+ - `depbrain.config.schema.json`
132
+
133
+ ## CI Behavior
134
+
135
+ `dep-brain` now returns a non-zero exit code when configured policy checks fail.
136
+
137
+ Examples:
138
+
139
+ ```bash
140
+ dep-brain analyze --fail-on-unused
141
+ dep-brain analyze --min-score 85 --fail-on-risks
142
+ dep-brain analyze --config depbrain.config.json
143
+ ```
144
+
145
+ ## Config Debugging
146
+
147
+ Print the resolved config (after defaults and CLI overrides):
148
+
149
+ ```bash
150
+ dep-brain config
151
+ dep-brain config --config depbrain.config.json
152
+ ```
153
+
154
+ ## Development
155
+
156
+ ```bash
157
+ npm install
158
+ npm run typecheck
159
+ npm run test
160
+ npm run build
161
+ ```
162
+
163
+ ## Project Structure
164
+
165
+ ```text
166
+ src/
167
+ |-- cli.ts
168
+ |-- index.ts
169
+ |-- core/
170
+ | |-- analyzer.ts
171
+ | |-- graph-builder.ts
172
+ | `-- scorer.ts
173
+ |-- checks/
174
+ | |-- duplicate.ts
175
+ | |-- unused.ts
176
+ | |-- outdated.ts
177
+ | `-- risk.ts
178
+ |-- reporters/
179
+ | |-- console.ts
180
+ | `-- json.ts
181
+ `-- utils/
182
+ |-- file-parser.ts
183
+ |-- npm-api.ts
184
+ `-- config.ts
185
+ ```
186
+
187
+ ## Roadmap Direction
188
+
189
+ - Improve false-positive reduction for unused dependency detection
190
+ - Improve monorepo and workspace support
191
+ - Strengthen risk scoring and suggestions
192
+ - Add CI and GitHub Action support in later releases
193
+
194
+ ## Repository Notes
195
+
196
+ - Project brief: [docs/project-brief.md](./docs/project-brief.md)
197
+ - Implementation history: [docs/implementation-log.md](./docs/implementation-log.md)
@@ -0,0 +1,20 @@
1
+ {
2
+ "ignore": {
3
+ "unused": [],
4
+ "outdated": [],
5
+ "duplicates": [],
6
+ "risks": [],
7
+ "dependencies": [],
8
+ "devDependencies": []
9
+ },
10
+ "policy": {
11
+ "minScore": 85,
12
+ "failOnUnused": false,
13
+ "failOnOutdated": false,
14
+ "failOnDuplicates": false,
15
+ "failOnRisks": false
16
+ },
17
+ "report": {
18
+ "maxSuggestions": 5
19
+ }
20
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Dependency Brain Config",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {
7
+ "ignore": {
8
+ "type": "object",
9
+ "additionalProperties": false,
10
+ "properties": {
11
+ "dependencies": { "type": "array", "items": { "type": "string" } },
12
+ "devDependencies": { "type": "array", "items": { "type": "string" } },
13
+ "unused": { "type": "array", "items": { "type": "string" } },
14
+ "duplicates": { "type": "array", "items": { "type": "string" } },
15
+ "outdated": { "type": "array", "items": { "type": "string" } },
16
+ "risks": { "type": "array", "items": { "type": "string" } }
17
+ }
18
+ },
19
+ "policy": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "properties": {
23
+ "minScore": { "type": "number" },
24
+ "failOnUnused": { "type": "boolean" },
25
+ "failOnOutdated": { "type": "boolean" },
26
+ "failOnDuplicates": { "type": "boolean" },
27
+ "failOnRisks": { "type": "boolean" }
28
+ }
29
+ },
30
+ "report": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "maxSuggestions": { "type": "number" }
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,3 @@
1
+ import type { DuplicateDependency } from "../core/analyzer.js";
2
+ import type { DependencyGraph } from "../core/graph-builder.js";
3
+ export declare function findDuplicateDependencies(graph: DependencyGraph): Promise<DuplicateDependency[]>;
@@ -0,0 +1,10 @@
1
+ export async function findDuplicateDependencies(graph) {
2
+ return Object.entries(graph.lockPackages)
3
+ .map(([name, instances]) => ({
4
+ name,
5
+ versions: Array.from(new Set(instances.map((instance) => instance.version))).sort(),
6
+ instances
7
+ }))
8
+ .filter((dependency) => dependency.versions.length > 1)
9
+ .sort((left, right) => left.name.localeCompare(right.name));
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { OutdatedDependency } from "../core/analyzer.js";
2
+ import type { DependencyGraph } from "../core/graph-builder.js";
3
+ export interface OutdatedOptions {
4
+ resolveLatestVersion?: (name: string) => Promise<string | null>;
5
+ }
6
+ export declare function findOutdatedDependencies(graph: DependencyGraph, options?: OutdatedOptions): Promise<OutdatedDependency[]>;
@@ -0,0 +1,51 @@
1
+ import { getLatestVersion } from "../utils/npm-api.js";
2
+ export async function findOutdatedDependencies(graph, options = {}) {
3
+ const resolveLatestVersion = options.resolveLatestVersion ?? getLatestVersion;
4
+ const combined = {
5
+ ...graph.dependencies,
6
+ ...graph.devDependencies
7
+ };
8
+ const results = await Promise.all(Object.entries(combined).map(async ([name, current]) => {
9
+ const normalized = normalizeVersion(current);
10
+ const latest = await resolveLatestVersion(name);
11
+ if (!latest || latest === normalized) {
12
+ return null;
13
+ }
14
+ return {
15
+ name,
16
+ current,
17
+ latest,
18
+ updateType: classifyUpdateType(normalized, latest)
19
+ };
20
+ }));
21
+ return results
22
+ .filter((item) => item !== null)
23
+ .sort((left, right) => left.name.localeCompare(right.name));
24
+ }
25
+ function normalizeVersion(versionRange) {
26
+ return versionRange.trim().replace(/^[~^><=\s]+/, "");
27
+ }
28
+ function classifyUpdateType(currentVersion, latestVersion) {
29
+ const current = parseVersion(currentVersion);
30
+ const latest = parseVersion(latestVersion);
31
+ if (!current || !latest) {
32
+ return "unknown";
33
+ }
34
+ if (latest[0] !== current[0]) {
35
+ return "major";
36
+ }
37
+ if (latest[1] !== current[1]) {
38
+ return "minor";
39
+ }
40
+ if (latest[2] !== current[2]) {
41
+ return "patch";
42
+ }
43
+ return "unknown";
44
+ }
45
+ function parseVersion(version) {
46
+ const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
47
+ if (!match) {
48
+ return null;
49
+ }
50
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
51
+ }
@@ -0,0 +1,3 @@
1
+ import type { DependencyGraph } from "../core/graph-builder.js";
2
+ import type { RiskDependency } from "../core/analyzer.js";
3
+ export declare function findRiskDependencies(graph: DependencyGraph): Promise<RiskDependency[]>;
@@ -0,0 +1,31 @@
1
+ import { getPackageMetadata } from "../utils/npm-api.js";
2
+ const TWO_YEARS_IN_DAYS = 365 * 2;
3
+ export async function findRiskDependencies(graph) {
4
+ const names = Object.keys({
5
+ ...graph.dependencies,
6
+ ...graph.devDependencies
7
+ });
8
+ const results = await Promise.all(names.map(async (name) => {
9
+ const metadata = await getPackageMetadata(name);
10
+ const reasons = [];
11
+ if (!metadata) {
12
+ return null;
13
+ }
14
+ if (metadata.daysSincePublish !== null && metadata.daysSincePublish > TWO_YEARS_IN_DAYS) {
15
+ reasons.push("No release in over 2 years");
16
+ }
17
+ if (metadata.downloads !== null && metadata.downloads < 1000) {
18
+ reasons.push("Low weekly download volume");
19
+ }
20
+ if (!metadata.repository) {
21
+ reasons.push("Missing repository metadata");
22
+ }
23
+ if (reasons.length === 0) {
24
+ return null;
25
+ }
26
+ return { name, reasons };
27
+ }));
28
+ return results
29
+ .filter((item) => item !== null)
30
+ .sort((left, right) => left.name.localeCompare(right.name));
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { UnusedDependency } from "../core/analyzer.js";
2
+ import type { DependencyGraph } from "../core/graph-builder.js";
3
+ export declare function findUnusedDependencies(rootDir: string, graph: DependencyGraph): Promise<UnusedDependency[]>;
@@ -0,0 +1,120 @@
1
+ import path from "node:path";
2
+ import { collectProjectFiles, readTextFile } from "../utils/file-parser.js";
3
+ const SOURCE_FILE_PATTERN = /\.(c|m)?(t|j)sx?$/;
4
+ const CONFIG_FILE_PATTERN = /(^|[\\/])(vite|vitest|jest|eslint|prettier|rollup|webpack|babel|tsup|eslint\.config|commitlint|playwright|storybook|tailwind|postcss)\.config\.(c|m)?(t|j)s$/;
5
+ const TEST_FILE_PATTERN = /(^|[\\/])(__tests__|test|tests|spec|specs)([\\/]|$)|\.(test|spec)\.(c|m)?(t|j)sx?$/;
6
+ const RUNTIME_DIR_PATTERN = /(^|[\\/])(src|app|lib|server|client|pages|components)([\\/]|$)/;
7
+ export async function findUnusedDependencies(rootDir, graph) {
8
+ const files = await collectProjectFiles(rootDir, SOURCE_FILE_PATTERN);
9
+ const projectFiles = files.filter((filePath) => !filePath.includes(`${path.sep}node_modules${path.sep}`));
10
+ const runtimeUsed = new Set();
11
+ const devUsed = new Set();
12
+ for (const filePath of projectFiles) {
13
+ const content = await readTextFile(filePath);
14
+ const imports = extractImportedPackages(content);
15
+ const isDevOnlyFile = isDevelopmentOnlyFile(rootDir, filePath);
16
+ const target = isDevOnlyFile ? devUsed : runtimeUsed;
17
+ for (const importedPackage of imports) {
18
+ target.add(importedPackage);
19
+ if (!isDevOnlyFile) {
20
+ devUsed.add(importedPackage);
21
+ }
22
+ }
23
+ }
24
+ for (const referencedBinary of extractScriptReferences(graph.scripts)) {
25
+ devUsed.add(referencedBinary);
26
+ }
27
+ const hasTypeScriptSources = projectFiles.some((filePath) => /\.(c|m)?tsx?$/.test(filePath));
28
+ const hasTypeScriptConfig = await hasFile(rootDir, "tsconfig.json");
29
+ if (hasTypeScriptConfig) {
30
+ devUsed.add("typescript");
31
+ }
32
+ const unusedDependencies = Object.keys(graph.dependencies)
33
+ .filter((name) => !runtimeUsed.has(name))
34
+ .map((name) => ({ name, section: "dependencies" }));
35
+ const unusedDevDependencies = Object.keys(graph.devDependencies)
36
+ .filter((name) => !devUsed.has(name) && !runtimeUsed.has(name))
37
+ .filter((name) => !isImplicitlyUsedDevDependency(name, hasTypeScriptSources, hasTypeScriptConfig))
38
+ .map((name) => ({ name, section: "devDependencies" }));
39
+ return [...unusedDependencies, ...unusedDevDependencies].sort((left, right) => left.name.localeCompare(right.name));
40
+ }
41
+ function extractImportedPackages(content) {
42
+ const imports = new Set();
43
+ const patterns = [
44
+ /\bimport\s+[^"'`]*?from\s+["'`]([^"'`]+)["'`]/g,
45
+ /\bexport\s+[^"'`]*?from\s+["'`]([^"'`]+)["'`]/g,
46
+ /\bimport\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
47
+ /\brequire\(\s*["'`]([^"'`]+)["'`]\s*\)/g,
48
+ /\bimport\s+["'`]([^"'`]+)["'`]/g
49
+ ];
50
+ for (const pattern of patterns) {
51
+ for (const match of content.matchAll(pattern)) {
52
+ const dependencyName = normalizeModuleSpecifier(match[1]);
53
+ if (dependencyName) {
54
+ imports.add(dependencyName);
55
+ }
56
+ }
57
+ }
58
+ return imports;
59
+ }
60
+ function normalizeModuleSpecifier(specifier) {
61
+ if (!specifier ||
62
+ specifier.startsWith(".") ||
63
+ specifier.startsWith("/") ||
64
+ specifier.startsWith("#")) {
65
+ return null;
66
+ }
67
+ if (specifier.startsWith("@")) {
68
+ const [scope, name] = specifier.split("/");
69
+ return scope && name ? `${scope}/${name}` : null;
70
+ }
71
+ return specifier.split("/")[0] ?? null;
72
+ }
73
+ function isDevelopmentOnlyFile(rootDir, filePath) {
74
+ const relativePath = path.relative(rootDir, filePath);
75
+ return (TEST_FILE_PATTERN.test(relativePath) ||
76
+ CONFIG_FILE_PATTERN.test(relativePath) ||
77
+ (!RUNTIME_DIR_PATTERN.test(relativePath) && relativePath.includes("scripts")));
78
+ }
79
+ function extractScriptReferences(scripts) {
80
+ const references = new Set();
81
+ for (const script of Object.values(scripts)) {
82
+ for (const token of script.split(/[^@\w./-]+/).filter(Boolean)) {
83
+ const normalized = normalizeScriptToken(token);
84
+ if (normalized) {
85
+ references.add(normalized);
86
+ }
87
+ }
88
+ }
89
+ return references;
90
+ }
91
+ function normalizeScriptToken(token) {
92
+ if (!token || token.startsWith(".") || token.includes("=") || token.startsWith("node")) {
93
+ return null;
94
+ }
95
+ if (token.startsWith("@")) {
96
+ const scopedMatch = token.match(/^(@[^/]+\/[^/]+)/);
97
+ if (scopedMatch) {
98
+ return scopedMatch[1];
99
+ }
100
+ }
101
+ return token.replace(/\.cmd$/i, "");
102
+ }
103
+ function isImplicitlyUsedDevDependency(name, hasTypeScriptSources, hasTypeScriptConfig) {
104
+ if (name === "typescript" && (hasTypeScriptSources || hasTypeScriptConfig)) {
105
+ return true;
106
+ }
107
+ if (name.startsWith("@types/") && hasTypeScriptSources) {
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ async function hasFile(rootDir, fileName) {
113
+ try {
114
+ await readTextFile(path.join(rootDir, fileName));
115
+ return true;
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ import { analyzeProject } from "./core/analyzer.js";
3
+ import { renderConsoleReport } from "./reporters/console.js";
4
+ import { renderJsonReport } from "./reporters/json.js";
5
+ import { promises as fs } from "node:fs";
6
+ import path from "node:path";
7
+ async function main() {
8
+ const args = process.argv.slice(2);
9
+ const command = args[0] ?? "analyze";
10
+ const optionValues = new Map();
11
+ const flags = new Set();
12
+ const positionals = [];
13
+ for (let index = 1; index < args.length; index += 1) {
14
+ const value = args[index];
15
+ if (!value?.startsWith("--")) {
16
+ positionals.push(value);
17
+ continue;
18
+ }
19
+ const nextValue = args[index + 1];
20
+ if (nextValue && !nextValue.startsWith("--")) {
21
+ optionValues.set(value, nextValue);
22
+ index += 1;
23
+ continue;
24
+ }
25
+ flags.add(value);
26
+ }
27
+ const targetPath = positionals[0] ?? process.cwd();
28
+ const showHelp = flags.has("--help") || command === "help";
29
+ if (showHelp) {
30
+ printHelp();
31
+ return;
32
+ }
33
+ if (command !== "analyze") {
34
+ if (command === "config") {
35
+ if (!(await hasPackageJson(targetPath))) {
36
+ console.error(`No package.json found at ${targetPath}`);
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ const config = await analyzeProject({
41
+ rootDir: targetPath,
42
+ configPath: optionValues.get("--config"),
43
+ config: buildCliConfig(flags, optionValues)
44
+ });
45
+ console.log(JSON.stringify(config.config, null, 2));
46
+ return;
47
+ }
48
+ console.error(`Unknown command: ${command}`);
49
+ printHelp();
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ if (!(await hasPackageJson(targetPath))) {
54
+ console.error(`No package.json found at ${targetPath}`);
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ const cliConfig = buildCliConfig(flags, optionValues);
59
+ const result = await analyzeProject({
60
+ rootDir: targetPath,
61
+ configPath: optionValues.get("--config"),
62
+ config: cliConfig
63
+ });
64
+ console.log(flags.has("--json") ? renderJsonReport(result) : renderConsoleReport(result));
65
+ if (!result.policy.passed) {
66
+ process.exitCode = 1;
67
+ }
68
+ }
69
+ void main();
70
+ function buildCliConfig(flags, optionValues) {
71
+ const minScore = optionValues.get("--min-score");
72
+ const policy = {};
73
+ if (minScore) {
74
+ policy.minScore = Number(minScore);
75
+ }
76
+ if (flags.has("--fail-on-duplicates")) {
77
+ policy.failOnDuplicates = true;
78
+ }
79
+ if (flags.has("--fail-on-outdated")) {
80
+ policy.failOnOutdated = true;
81
+ }
82
+ if (flags.has("--fail-on-risks")) {
83
+ policy.failOnRisks = true;
84
+ }
85
+ if (flags.has("--fail-on-unused")) {
86
+ policy.failOnUnused = true;
87
+ }
88
+ return {
89
+ policy
90
+ };
91
+ }
92
+ async function hasPackageJson(targetPath) {
93
+ try {
94
+ await fs.access(path.join(targetPath, "package.json"));
95
+ return true;
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ function printHelp() {
102
+ console.log("Dependency Brain");
103
+ console.log("");
104
+ console.log("Usage:");
105
+ console.log(" dep-brain analyze [path] [--json] [--config path] [--min-score n] [--fail-on-risks] [--fail-on-outdated] [--fail-on-unused] [--fail-on-duplicates]");
106
+ console.log(" dep-brain config [path] [--config path]");
107
+ console.log(" dep-brain help");
108
+ console.log("");
109
+ console.log("Options:");
110
+ console.log(" --json Output JSON for analysis");
111
+ console.log(" --config <path> Path to depbrain.config.json");
112
+ console.log(" --min-score <n> Minimum score required to pass");
113
+ console.log(" --fail-on-risks Fail when risky dependencies exist");
114
+ console.log(" --fail-on-outdated Fail when outdated dependencies exist");
115
+ console.log(" --fail-on-unused Fail when unused dependencies exist");
116
+ console.log(" --fail-on-duplicates Fail when duplicates exist");
117
+ console.log(" --help Show this help output");
118
+ }