@vyuhlabs/dxkit 1.5.1 → 1.6.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 +264 -0
- package/README.md +265 -352
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/analyzers/developer/detailed.d.ts +26 -0
- package/dist/analyzers/developer/detailed.d.ts.map +1 -0
- package/dist/analyzers/developer/detailed.js +193 -0
- package/dist/analyzers/developer/detailed.js.map +1 -0
- package/dist/analyzers/developer/gather.d.ts +11 -0
- package/dist/analyzers/developer/gather.d.ts.map +1 -0
- package/dist/analyzers/developer/gather.js +167 -0
- package/dist/analyzers/developer/gather.js.map +1 -0
- package/dist/analyzers/developer/index.d.ts +8 -0
- package/dist/analyzers/developer/index.d.ts.map +1 -0
- package/dist/analyzers/developer/index.js +168 -0
- package/dist/analyzers/developer/index.js.map +1 -0
- package/dist/analyzers/developer/types.d.ts +49 -0
- package/dist/analyzers/developer/types.d.ts.map +1 -0
- package/dist/analyzers/developer/types.js +6 -0
- package/dist/analyzers/developer/types.js.map +1 -0
- package/dist/analyzers/docs/shallow.d.ts +9 -0
- package/dist/analyzers/docs/shallow.d.ts.map +1 -0
- package/dist/analyzers/docs/shallow.js +8 -0
- package/dist/analyzers/docs/shallow.js.map +1 -0
- package/dist/analyzers/dx/shallow.d.ts +9 -0
- package/dist/analyzers/dx/shallow.d.ts.map +1 -0
- package/dist/analyzers/dx/shallow.js +8 -0
- package/dist/analyzers/dx/shallow.js.map +1 -0
- package/dist/analyzers/evidence.d.ts +36 -0
- package/dist/analyzers/evidence.d.ts.map +1 -0
- package/dist/analyzers/evidence.js +3 -0
- package/dist/analyzers/evidence.js.map +1 -0
- package/dist/analyzers/health/actions.d.ts +10 -0
- package/dist/analyzers/health/actions.d.ts.map +1 -0
- package/dist/analyzers/health/actions.js +284 -0
- package/dist/analyzers/health/actions.js.map +1 -0
- package/dist/analyzers/health/detailed.d.ts +26 -0
- package/dist/analyzers/health/detailed.d.ts.map +1 -0
- package/dist/analyzers/health/detailed.js +147 -0
- package/dist/analyzers/health/detailed.js.map +1 -0
- package/dist/analyzers/health.d.ts +22 -0
- package/dist/analyzers/health.d.ts.map +1 -0
- package/dist/analyzers/health.js +270 -0
- package/dist/analyzers/health.js.map +1 -0
- package/dist/analyzers/index.d.ts +3 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +6 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/maintainability/shallow.d.ts +9 -0
- package/dist/analyzers/maintainability/shallow.d.ts.map +1 -0
- package/dist/analyzers/maintainability/shallow.js +8 -0
- package/dist/analyzers/maintainability/shallow.js.map +1 -0
- package/dist/analyzers/quality/actions.d.ts +5 -0
- package/dist/analyzers/quality/actions.d.ts.map +1 -0
- package/dist/analyzers/quality/actions.js +158 -0
- package/dist/analyzers/quality/actions.js.map +1 -0
- package/dist/analyzers/quality/detailed.d.ts +17 -0
- package/dist/analyzers/quality/detailed.d.ts.map +1 -0
- package/dist/analyzers/quality/detailed.js +122 -0
- package/dist/analyzers/quality/detailed.js.map +1 -0
- package/dist/analyzers/quality/gather.d.ts +38 -0
- package/dist/analyzers/quality/gather.d.ts.map +1 -0
- package/dist/analyzers/quality/gather.js +279 -0
- package/dist/analyzers/quality/gather.js.map +1 -0
- package/dist/analyzers/quality/index.d.ts +12 -0
- package/dist/analyzers/quality/index.d.ts.map +1 -0
- package/dist/analyzers/quality/index.js +281 -0
- package/dist/analyzers/quality/index.js.map +1 -0
- package/dist/analyzers/quality/shallow.d.ts +9 -0
- package/dist/analyzers/quality/shallow.d.ts.map +1 -0
- package/dist/analyzers/quality/shallow.js +8 -0
- package/dist/analyzers/quality/shallow.js.map +1 -0
- package/dist/analyzers/quality/types.d.ts +66 -0
- package/dist/analyzers/quality/types.d.ts.map +1 -0
- package/dist/analyzers/quality/types.js +3 -0
- package/dist/analyzers/quality/types.js.map +1 -0
- package/dist/analyzers/remediation.d.ts +42 -0
- package/dist/analyzers/remediation.d.ts.map +1 -0
- package/dist/analyzers/remediation.js +28 -0
- package/dist/analyzers/remediation.js.map +1 -0
- package/dist/analyzers/scoring.d.ts +32 -0
- package/dist/analyzers/scoring.d.ts.map +1 -0
- package/dist/analyzers/scoring.js +410 -0
- package/dist/analyzers/scoring.js.map +1 -0
- package/dist/analyzers/security/actions.d.ts +7 -0
- package/dist/analyzers/security/actions.d.ts.map +1 -0
- package/dist/analyzers/security/actions.js +104 -0
- package/dist/analyzers/security/actions.js.map +1 -0
- package/dist/analyzers/security/detailed.d.ts +14 -0
- package/dist/analyzers/security/detailed.d.ts.map +1 -0
- package/dist/analyzers/security/detailed.js +124 -0
- package/dist/analyzers/security/detailed.js.map +1 -0
- package/dist/analyzers/security/gather.d.ts +12 -0
- package/dist/analyzers/security/gather.d.ts.map +1 -0
- package/dist/analyzers/security/gather.js +195 -0
- package/dist/analyzers/security/gather.js.map +1 -0
- package/dist/analyzers/security/index.d.ts +8 -0
- package/dist/analyzers/security/index.d.ts.map +1 -0
- package/dist/analyzers/security/index.js +173 -0
- package/dist/analyzers/security/index.js.map +1 -0
- package/dist/analyzers/security/scoring.d.ts +29 -0
- package/dist/analyzers/security/scoring.d.ts.map +1 -0
- package/dist/analyzers/security/scoring.js +40 -0
- package/dist/analyzers/security/scoring.js.map +1 -0
- package/dist/analyzers/security/shallow.d.ts +10 -0
- package/dist/analyzers/security/shallow.d.ts.map +1 -0
- package/dist/analyzers/security/shallow.js +8 -0
- package/dist/analyzers/security/shallow.js.map +1 -0
- package/dist/analyzers/security/types.d.ts +43 -0
- package/dist/analyzers/security/types.d.ts.map +1 -0
- package/dist/analyzers/security/types.js +6 -0
- package/dist/analyzers/security/types.js.map +1 -0
- package/dist/analyzers/tests/actions.d.ts +6 -0
- package/dist/analyzers/tests/actions.d.ts.map +1 -0
- package/dist/analyzers/tests/actions.js +80 -0
- package/dist/analyzers/tests/actions.js.map +1 -0
- package/dist/analyzers/tests/detailed.d.ts +14 -0
- package/dist/analyzers/tests/detailed.d.ts.map +1 -0
- package/dist/analyzers/tests/detailed.js +121 -0
- package/dist/analyzers/tests/detailed.js.map +1 -0
- package/dist/analyzers/tests/gather.d.ts +5 -0
- package/dist/analyzers/tests/gather.d.ts.map +1 -0
- package/dist/analyzers/tests/gather.js +270 -0
- package/dist/analyzers/tests/gather.js.map +1 -0
- package/dist/analyzers/tests/import-graph.d.ts +48 -0
- package/dist/analyzers/tests/import-graph.d.ts.map +1 -0
- package/dist/analyzers/tests/import-graph.js +231 -0
- package/dist/analyzers/tests/import-graph.js.map +1 -0
- package/dist/analyzers/tests/index.d.ts +8 -0
- package/dist/analyzers/tests/index.d.ts.map +1 -0
- package/dist/analyzers/tests/index.js +247 -0
- package/dist/analyzers/tests/index.js.map +1 -0
- package/dist/analyzers/tests/scoring.d.ts +27 -0
- package/dist/analyzers/tests/scoring.d.ts.map +1 -0
- package/dist/analyzers/tests/scoring.js +38 -0
- package/dist/analyzers/tests/scoring.js.map +1 -0
- package/dist/analyzers/tests/shallow.d.ts +9 -0
- package/dist/analyzers/tests/shallow.d.ts.map +1 -0
- package/dist/analyzers/tests/shallow.js +8 -0
- package/dist/analyzers/tests/shallow.js.map +1 -0
- package/dist/analyzers/tests/types.d.ts +49 -0
- package/dist/analyzers/tests/types.d.ts.map +1 -0
- package/dist/analyzers/tests/types.js +6 -0
- package/dist/analyzers/tests/types.js.map +1 -0
- package/dist/analyzers/tools/cloc.d.ts +8 -0
- package/dist/analyzers/tools/cloc.d.ts.map +1 -0
- package/dist/analyzers/tools/cloc.js +49 -0
- package/dist/analyzers/tools/cloc.js.map +1 -0
- package/dist/analyzers/tools/coverage.d.ts +59 -0
- package/dist/analyzers/tools/coverage.d.ts.map +1 -0
- package/dist/analyzers/tools/coverage.js +280 -0
- package/dist/analyzers/tools/coverage.js.map +1 -0
- package/dist/analyzers/tools/cvss-v4-lookup.d.ts +10 -0
- package/dist/analyzers/tools/cvss-v4-lookup.d.ts.map +1 -0
- package/dist/analyzers/tools/cvss-v4-lookup.js +284 -0
- package/dist/analyzers/tools/cvss-v4-lookup.js.map +1 -0
- package/dist/analyzers/tools/cvss-v4.d.ts +24 -0
- package/dist/analyzers/tools/cvss-v4.d.ts.map +1 -0
- package/dist/analyzers/tools/cvss-v4.js +362 -0
- package/dist/analyzers/tools/cvss-v4.js.map +1 -0
- package/dist/analyzers/tools/default-exclusions.gitignore +56 -0
- package/dist/analyzers/tools/exclusions.d.ts +70 -0
- package/dist/analyzers/tools/exclusions.d.ts.map +1 -0
- package/dist/analyzers/tools/exclusions.js +250 -0
- package/dist/analyzers/tools/exclusions.js.map +1 -0
- package/dist/analyzers/tools/generic.d.ts +4 -0
- package/dist/analyzers/tools/generic.d.ts.map +1 -0
- package/dist/analyzers/tools/generic.js +198 -0
- package/dist/analyzers/tools/generic.js.map +1 -0
- package/dist/analyzers/tools/gitleaks.d.ts +8 -0
- package/dist/analyzers/tools/gitleaks.d.ts.map +1 -0
- package/dist/analyzers/tools/gitleaks.js +58 -0
- package/dist/analyzers/tools/gitleaks.js.map +1 -0
- package/dist/analyzers/tools/graphify.d.ts +4 -0
- package/dist/analyzers/tools/graphify.d.ts.map +1 -0
- package/dist/analyzers/tools/graphify.js +222 -0
- package/dist/analyzers/tools/graphify.js.map +1 -0
- package/dist/analyzers/tools/osv.d.ts +51 -0
- package/dist/analyzers/tools/osv.d.ts.map +1 -0
- package/dist/analyzers/tools/osv.js +188 -0
- package/dist/analyzers/tools/osv.js.map +1 -0
- package/dist/analyzers/tools/parallel.d.ts +8 -0
- package/dist/analyzers/tools/parallel.d.ts.map +1 -0
- package/dist/analyzers/tools/parallel.js +195 -0
- package/dist/analyzers/tools/parallel.js.map +1 -0
- package/dist/analyzers/tools/runner.d.ts +13 -0
- package/dist/analyzers/tools/runner.d.ts.map +1 -0
- package/dist/analyzers/tools/runner.js +109 -0
- package/dist/analyzers/tools/runner.js.map +1 -0
- package/dist/analyzers/tools/suppressions.d.ts +55 -0
- package/dist/analyzers/tools/suppressions.d.ts.map +1 -0
- package/dist/analyzers/tools/suppressions.js +203 -0
- package/dist/analyzers/tools/suppressions.js.map +1 -0
- package/dist/analyzers/tools/timing.d.ts +9 -0
- package/dist/analyzers/tools/timing.d.ts.map +1 -0
- package/dist/analyzers/tools/timing.js +29 -0
- package/dist/analyzers/tools/timing.js.map +1 -0
- package/dist/analyzers/tools/tool-registry.d.ts +86 -0
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -0
- package/dist/analyzers/tools/tool-registry.js +705 -0
- package/dist/analyzers/tools/tool-registry.js.map +1 -0
- package/dist/analyzers/types.d.ts +125 -0
- package/dist/analyzers/types.d.ts.map +1 -0
- package/dist/analyzers/types.js +11 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +405 -0
- package/dist/cli.js.map +1 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +24 -15
- package/dist/detect.js.map +1 -1
- package/dist/languages/csharp.d.ts +5 -0
- package/dist/languages/csharp.d.ts.map +1 -0
- package/dist/languages/csharp.js +265 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/go.d.ts +11 -0
- package/dist/languages/go.d.ts.map +1 -0
- package/dist/languages/go.js +321 -0
- package/dist/languages/go.js.map +1 -0
- package/dist/languages/index.d.ts +6 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +18 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/python.d.ts +3 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +284 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/rust.d.ts +17 -0
- package/dist/languages/rust.d.ts.map +1 -0
- package/dist/languages/rust.js +333 -0
- package/dist/languages/rust.js.map +1 -0
- package/dist/languages/types.d.ts +38 -0
- package/dist/languages/types.d.ts.map +1 -0
- package/dist/languages/types.js +3 -0
- package/dist/languages/types.js.map +1 -0
- package/dist/languages/typescript.d.ts +15 -0
- package/dist/languages/typescript.d.ts.map +1 -0
- package/dist/languages/typescript.js +353 -0
- package/dist/languages/typescript.js.map +1 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +25 -12
- package/dist/logger.js.map +1 -1
- package/dist/project-yaml.d.ts.map +1 -1
- package/dist/project-yaml.js +1 -0
- package/dist/project-yaml.js.map +1 -1
- package/dist/tools-cli.d.ts +2 -0
- package/dist/tools-cli.d.ts.map +1 -0
- package/dist/tools-cli.js +231 -0
- package/dist/tools-cli.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
- package/templates/.claude/commands/dev-report.md +34 -4
- package/templates/.claude/commands/health.md +45 -2
- package/templates/.claude/commands/quality.md.template +38 -15
- package/templates/.claude/commands/test-gaps.md +36 -2
- package/templates/.claude/commands/vulnerabilities.md +36 -2
|
@@ -0,0 +1,705 @@
|
|
|
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.TOOL_DEFS = void 0;
|
|
37
|
+
exports.findTool = findTool;
|
|
38
|
+
exports.getInstallCommand = getInstallCommand;
|
|
39
|
+
exports.buildRequiredTools = buildRequiredTools;
|
|
40
|
+
exports.getToolDef = getToolDef;
|
|
41
|
+
exports.getSemgrepRulesets = getSemgrepRulesets;
|
|
42
|
+
exports.runRegisteredTool = runRegisteredTool;
|
|
43
|
+
exports.checkAllTools = checkAllTools;
|
|
44
|
+
/**
|
|
45
|
+
* Tool registry -- central source of truth for all analysis tools.
|
|
46
|
+
*
|
|
47
|
+
* Responsibilities:
|
|
48
|
+
* 1. Describe each tool (what it does, who needs it, how to install)
|
|
49
|
+
* 2. Detect if a tool is available (multi-path: PATH, brew, pipx, npm-g, system paths)
|
|
50
|
+
* 3. Provide install commands per platform
|
|
51
|
+
* 4. Build the `requiredTools` array for a given stack
|
|
52
|
+
*
|
|
53
|
+
* Used by:
|
|
54
|
+
* - `src/detect.ts` -- populates `DetectedStack.requiredTools`
|
|
55
|
+
* - `src/cli.ts tools` subcommand -- list status, interactive install
|
|
56
|
+
* - `src/analyzers/tools/*.ts` -- tool-specific modules call `findTool()`
|
|
57
|
+
* - `devstack` package -- reads `requiredTools` to package devcontainers
|
|
58
|
+
*/
|
|
59
|
+
const child_process_1 = require("child_process");
|
|
60
|
+
const fs = __importStar(require("fs"));
|
|
61
|
+
const os = __importStar(require("os"));
|
|
62
|
+
const path = __importStar(require("path"));
|
|
63
|
+
const languages_1 = require("../../languages");
|
|
64
|
+
/**
|
|
65
|
+
* Platform-specific system paths to probe.
|
|
66
|
+
*/
|
|
67
|
+
function getSystemPaths() {
|
|
68
|
+
const home = os.homedir();
|
|
69
|
+
const paths = [
|
|
70
|
+
'/usr/local/bin',
|
|
71
|
+
'/opt/homebrew/bin', // Apple Silicon brew
|
|
72
|
+
'/home/linuxbrew/.linuxbrew/bin', // Linux brew
|
|
73
|
+
`${home}/.local/bin`, // pipx, user pip
|
|
74
|
+
`${home}/.cargo/bin`, // rust
|
|
75
|
+
`${home}/go/bin`, // go
|
|
76
|
+
'/tmp/graphify-venv/bin', // dxkit graphify venv
|
|
77
|
+
];
|
|
78
|
+
// Include $GOPATH/bin if set
|
|
79
|
+
if (process.env.GOPATH) {
|
|
80
|
+
paths.push(path.join(process.env.GOPATH, 'bin'));
|
|
81
|
+
}
|
|
82
|
+
return paths;
|
|
83
|
+
}
|
|
84
|
+
/** Run a command with short timeout, return stdout or empty string. */
|
|
85
|
+
function quickRun(cmd) {
|
|
86
|
+
try {
|
|
87
|
+
return (0, child_process_1.execSync)(cmd, {
|
|
88
|
+
encoding: 'utf-8',
|
|
89
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
90
|
+
timeout: 5000,
|
|
91
|
+
}).trim();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Check if a binary name is available via `which`. */
|
|
98
|
+
function findInPath(binary) {
|
|
99
|
+
const result = quickRun(`which ${binary} 2>/dev/null`);
|
|
100
|
+
return result || null;
|
|
101
|
+
}
|
|
102
|
+
/** Check if brew has installed a tool (macOS/Linux). */
|
|
103
|
+
function findInBrew(binary) {
|
|
104
|
+
const brewPrefix = quickRun('brew --prefix 2>/dev/null');
|
|
105
|
+
if (!brewPrefix)
|
|
106
|
+
return null;
|
|
107
|
+
const candidate = path.join(brewPrefix, 'bin', binary);
|
|
108
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
109
|
+
}
|
|
110
|
+
/** Check if npm -g has installed a package with this binary. */
|
|
111
|
+
function findInNpmGlobal(binary) {
|
|
112
|
+
const npmBin = quickRun('npm bin -g 2>/dev/null') || quickRun('npm prefix -g 2>/dev/null');
|
|
113
|
+
if (!npmBin)
|
|
114
|
+
return null;
|
|
115
|
+
const candidates = [path.join(npmBin, binary), path.join(npmBin, 'bin', binary)];
|
|
116
|
+
for (const c of candidates) {
|
|
117
|
+
if (fs.existsSync(c))
|
|
118
|
+
return c;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/** Check if pipx has installed a tool. */
|
|
123
|
+
function findInPipx(binary) {
|
|
124
|
+
const home = os.homedir();
|
|
125
|
+
const pipxBin = path.join(home, '.local', 'bin', binary);
|
|
126
|
+
return fs.existsSync(pipxBin) ? pipxBin : null;
|
|
127
|
+
}
|
|
128
|
+
/** Check system probe paths. */
|
|
129
|
+
function findInProbePaths(binary, extraProbes = []) {
|
|
130
|
+
const allPaths = [...getSystemPaths(), ...extraProbes];
|
|
131
|
+
for (const p of allPaths) {
|
|
132
|
+
const candidate = path.join(p, binary);
|
|
133
|
+
if (fs.existsSync(candidate))
|
|
134
|
+
return candidate;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/** Check project-local node_modules/.bin/ for language tools. */
|
|
139
|
+
function findInProjectNodeModules(binary, cwd) {
|
|
140
|
+
const candidate = path.join(cwd, 'node_modules', '.bin', binary);
|
|
141
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
142
|
+
}
|
|
143
|
+
/** Check project-local node_modules for a package without a CLI binary. */
|
|
144
|
+
function findNodePackage(pkg, cwd) {
|
|
145
|
+
const candidate = path.join(cwd, 'node_modules', pkg, 'package.json');
|
|
146
|
+
return fs.existsSync(candidate) ? path.join(cwd, 'node_modules', pkg) : null;
|
|
147
|
+
}
|
|
148
|
+
/** Special-case: check if graphify Python module is importable. */
|
|
149
|
+
function findGraphifyPython(cwd) {
|
|
150
|
+
const pythonCandidates = [
|
|
151
|
+
'/tmp/graphify-venv/bin/python',
|
|
152
|
+
`${os.homedir()}/.local/bin/python3`,
|
|
153
|
+
'python3',
|
|
154
|
+
];
|
|
155
|
+
for (const py of pythonCandidates) {
|
|
156
|
+
// Resolve 'python3' via which first
|
|
157
|
+
const resolved = py === 'python3' ? findInPath('python3') : py;
|
|
158
|
+
if (!resolved || !fs.existsSync(resolved.replace('${HOME}', os.homedir())))
|
|
159
|
+
continue;
|
|
160
|
+
try {
|
|
161
|
+
const check = (0, child_process_1.execSync)(`${resolved} -c "import graphify; print('ok')" 2>/dev/null`, {
|
|
162
|
+
encoding: 'utf-8',
|
|
163
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
164
|
+
timeout: 5000,
|
|
165
|
+
cwd,
|
|
166
|
+
}).trim();
|
|
167
|
+
if (check === 'ok')
|
|
168
|
+
return resolved;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
/* try next */
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Find a tool across multiple installation methods.
|
|
178
|
+
* Returns the first matching absolute path, or null.
|
|
179
|
+
*/
|
|
180
|
+
function findTool(def, cwd) {
|
|
181
|
+
// Special case: graphify is a Python module, not a binary
|
|
182
|
+
if (def.name === 'graphify') {
|
|
183
|
+
const pyPath = findGraphifyPython(cwd || process.cwd());
|
|
184
|
+
if (pyPath) {
|
|
185
|
+
return {
|
|
186
|
+
name: def.name,
|
|
187
|
+
available: true,
|
|
188
|
+
path: pyPath,
|
|
189
|
+
version: 'importable',
|
|
190
|
+
source: pyPath.includes('/tmp/graphify-venv') ? 'probe' : 'path',
|
|
191
|
+
requirement: def,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
name: def.name,
|
|
196
|
+
available: false,
|
|
197
|
+
path: null,
|
|
198
|
+
version: null,
|
|
199
|
+
source: 'missing',
|
|
200
|
+
requirement: def,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Node packages without a CLI binary (e.g. vitest plugins):
|
|
204
|
+
if (def.nodePackage && cwd) {
|
|
205
|
+
const pkgPath = findNodePackage(def.nodePackage, cwd);
|
|
206
|
+
if (pkgPath)
|
|
207
|
+
return makeStatus(def, pkgPath, 'probe');
|
|
208
|
+
// Nothing more to check — the package has no binary.
|
|
209
|
+
return {
|
|
210
|
+
name: def.name,
|
|
211
|
+
available: false,
|
|
212
|
+
path: null,
|
|
213
|
+
version: null,
|
|
214
|
+
source: 'missing',
|
|
215
|
+
requirement: def,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
for (const binary of def.binaries) {
|
|
219
|
+
// 0. Project-local node_modules (for language tools)
|
|
220
|
+
if (cwd && def.layer === 'language' && def.for === 'node') {
|
|
221
|
+
const localResult = findInProjectNodeModules(binary, cwd);
|
|
222
|
+
if (localResult)
|
|
223
|
+
return makeStatus(def, localResult, 'probe');
|
|
224
|
+
}
|
|
225
|
+
// 1. PATH
|
|
226
|
+
const pathResult = findInPath(binary);
|
|
227
|
+
if (pathResult)
|
|
228
|
+
return makeStatus(def, pathResult, 'path');
|
|
229
|
+
// 2. brew
|
|
230
|
+
const brewResult = findInBrew(binary);
|
|
231
|
+
if (brewResult)
|
|
232
|
+
return makeStatus(def, brewResult, 'brew');
|
|
233
|
+
// 3. npm global
|
|
234
|
+
const npmResult = findInNpmGlobal(binary);
|
|
235
|
+
if (npmResult)
|
|
236
|
+
return makeStatus(def, npmResult, 'npm-g');
|
|
237
|
+
// 4. pipx
|
|
238
|
+
const pipxResult = findInPipx(binary);
|
|
239
|
+
if (pipxResult)
|
|
240
|
+
return makeStatus(def, pipxResult, 'pipx');
|
|
241
|
+
// 5. System probe paths (includes cargo/go/graphify venv)
|
|
242
|
+
const probeResult = findInProbePaths(binary, def.probePaths);
|
|
243
|
+
if (probeResult) {
|
|
244
|
+
let source = 'probe';
|
|
245
|
+
if (probeResult.includes('/.cargo/'))
|
|
246
|
+
source = 'cargo';
|
|
247
|
+
else if (probeResult.includes('/go/bin/'))
|
|
248
|
+
source = 'go';
|
|
249
|
+
return makeStatus(def, probeResult, source);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name: def.name,
|
|
254
|
+
available: false,
|
|
255
|
+
path: null,
|
|
256
|
+
version: null,
|
|
257
|
+
source: 'missing',
|
|
258
|
+
requirement: def,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function makeStatus(def, binPath, source) {
|
|
262
|
+
const version = def.versionCheck ? quickRun(def.versionCheck) : null;
|
|
263
|
+
return {
|
|
264
|
+
name: def.name,
|
|
265
|
+
available: true,
|
|
266
|
+
path: binPath,
|
|
267
|
+
version: version ? version.split('\n')[0] : null,
|
|
268
|
+
source,
|
|
269
|
+
requirement: def,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/** Get the install command for the current platform. */
|
|
273
|
+
function getInstallCommand(def) {
|
|
274
|
+
const platform = process.platform;
|
|
275
|
+
if (platform === 'darwin' && def.installCommands.macos)
|
|
276
|
+
return def.installCommands.macos;
|
|
277
|
+
if (platform === 'linux' && def.installCommands.linux)
|
|
278
|
+
return def.installCommands.linux;
|
|
279
|
+
if (platform === 'win32' && def.installCommands.windows)
|
|
280
|
+
return def.installCommands.windows;
|
|
281
|
+
// Fall back to install field from ToolRequirement
|
|
282
|
+
return def.install;
|
|
283
|
+
}
|
|
284
|
+
// =============================================================================
|
|
285
|
+
// Tool definitions
|
|
286
|
+
// =============================================================================
|
|
287
|
+
exports.TOOL_DEFS = {
|
|
288
|
+
cloc: {
|
|
289
|
+
name: 'cloc',
|
|
290
|
+
description: 'Count lines of code per language',
|
|
291
|
+
install: 'npm install -g cloc',
|
|
292
|
+
check: 'cloc --version',
|
|
293
|
+
for: 'all',
|
|
294
|
+
layer: 'universal',
|
|
295
|
+
binaries: ['cloc'],
|
|
296
|
+
versionCheck: 'cloc --version 2>/dev/null',
|
|
297
|
+
installCommands: {
|
|
298
|
+
// Create isolated npm workspace at ~/.local/share/dxkit, symlink to ~/.local/bin
|
|
299
|
+
macos: 'brew install cloc || (mkdir -p ~/.local/share/dxkit && cd ~/.local/share/dxkit && echo \'{"name":"dxkit-tools","private":true}\' > package.json && npm install --silent cloc && mkdir -p ~/.local/bin && ln -sf ~/.local/share/dxkit/node_modules/cloc/lib/cloc ~/.local/bin/cloc)',
|
|
300
|
+
linux: 'mkdir -p ~/.local/share/dxkit && cd ~/.local/share/dxkit && echo \'{"name":"dxkit-tools","private":true}\' > package.json && npm install --silent cloc && mkdir -p ~/.local/bin && ln -sf ~/.local/share/dxkit/node_modules/cloc/lib/cloc ~/.local/bin/cloc',
|
|
301
|
+
windows: 'npm install -g cloc',
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
gitleaks: {
|
|
305
|
+
name: 'gitleaks',
|
|
306
|
+
description: 'Secret scanning with 800+ patterns',
|
|
307
|
+
install: 'brew install gitleaks',
|
|
308
|
+
check: 'gitleaks version',
|
|
309
|
+
for: 'all',
|
|
310
|
+
layer: 'universal',
|
|
311
|
+
binaries: ['gitleaks'],
|
|
312
|
+
probePaths: ['/tmp'],
|
|
313
|
+
versionCheck: 'gitleaks version 2>/dev/null',
|
|
314
|
+
installCommands: {
|
|
315
|
+
macos: 'brew install gitleaks',
|
|
316
|
+
// Install to ~/.local/bin (user path, no sudo)
|
|
317
|
+
linux: 'mkdir -p ~/.local/bin && curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.24.0/gitleaks_8.24.0_linux_x64.tar.gz | tar xz -C ~/.local/bin gitleaks && chmod +x ~/.local/bin/gitleaks',
|
|
318
|
+
windows: 'scoop install gitleaks',
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
graphify: {
|
|
322
|
+
name: 'graphify',
|
|
323
|
+
description: 'Deterministic AST extraction via tree-sitter (20+ languages)',
|
|
324
|
+
install: 'pip install graphifyy',
|
|
325
|
+
check: 'python3 -c "import graphify"',
|
|
326
|
+
for: 'all',
|
|
327
|
+
layer: 'optional',
|
|
328
|
+
binaries: ['graphify'],
|
|
329
|
+
probePaths: ['/tmp/graphify-venv/bin'],
|
|
330
|
+
versionCheck: 'python3 -c "import graphify; print(\'installed\')" 2>/dev/null || /tmp/graphify-venv/bin/python -c "import graphify; print(\'installed\')" 2>/dev/null',
|
|
331
|
+
installCommands: {
|
|
332
|
+
macos: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install -q graphifyy',
|
|
333
|
+
linux: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install -q graphifyy',
|
|
334
|
+
windows: 'pip install --user graphifyy',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
jscpd: {
|
|
338
|
+
name: 'jscpd',
|
|
339
|
+
description: 'Copy-paste / duplicate code detector',
|
|
340
|
+
install: 'npm install -g jscpd',
|
|
341
|
+
check: 'jscpd --version',
|
|
342
|
+
for: 'all',
|
|
343
|
+
layer: 'universal',
|
|
344
|
+
binaries: ['jscpd'],
|
|
345
|
+
versionCheck: 'jscpd --version 2>/dev/null',
|
|
346
|
+
installCommands: {
|
|
347
|
+
macos: 'mkdir -p ~/.local/share/dxkit && cd ~/.local/share/dxkit && npm install jscpd && mkdir -p ~/.local/bin && ln -sf ~/.local/share/dxkit/node_modules/.bin/jscpd ~/.local/bin/jscpd',
|
|
348
|
+
linux: 'mkdir -p ~/.local/share/dxkit && cd ~/.local/share/dxkit && npm install jscpd && mkdir -p ~/.local/bin && ln -sf ~/.local/share/dxkit/node_modules/.bin/jscpd ~/.local/bin/jscpd',
|
|
349
|
+
windows: 'npm install -g jscpd',
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
semgrep: {
|
|
353
|
+
name: 'semgrep',
|
|
354
|
+
description: 'Static analysis security scanner (SAST)',
|
|
355
|
+
install: 'pip install semgrep',
|
|
356
|
+
check: 'semgrep --version',
|
|
357
|
+
for: 'all',
|
|
358
|
+
layer: 'universal',
|
|
359
|
+
binaries: ['semgrep'],
|
|
360
|
+
probePaths: ['/tmp/graphify-venv/bin'],
|
|
361
|
+
versionCheck: 'semgrep --version 2>/dev/null',
|
|
362
|
+
installCommands: {
|
|
363
|
+
macos: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install -q semgrep && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/semgrep ~/.local/bin/semgrep',
|
|
364
|
+
linux: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install -q semgrep && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/semgrep ~/.local/bin/semgrep',
|
|
365
|
+
windows: 'pip install --user semgrep',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
eslint: {
|
|
369
|
+
name: 'eslint',
|
|
370
|
+
description: 'JavaScript/TypeScript linting',
|
|
371
|
+
install: 'npm install --save-dev eslint',
|
|
372
|
+
check: 'npx eslint --version',
|
|
373
|
+
for: 'node',
|
|
374
|
+
layer: 'language',
|
|
375
|
+
binaries: ['eslint', 'lb-eslint'],
|
|
376
|
+
versionCheck: 'npx eslint --version 2>/dev/null',
|
|
377
|
+
installCommands: {
|
|
378
|
+
macos: 'npm install --save-dev eslint',
|
|
379
|
+
linux: 'npm install --save-dev eslint',
|
|
380
|
+
windows: 'npm install --save-dev eslint',
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
'npm-audit': {
|
|
384
|
+
name: 'npm-audit',
|
|
385
|
+
description: 'Dependency vulnerability scanning (built into npm)',
|
|
386
|
+
install: 'builtin (npm)',
|
|
387
|
+
check: 'npm audit --help',
|
|
388
|
+
for: 'node',
|
|
389
|
+
layer: 'language',
|
|
390
|
+
binaries: ['npm'],
|
|
391
|
+
versionCheck: 'npm --version 2>/dev/null',
|
|
392
|
+
installCommands: {
|
|
393
|
+
macos: 'builtin',
|
|
394
|
+
linux: 'builtin',
|
|
395
|
+
windows: 'builtin',
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
ruff: {
|
|
399
|
+
name: 'ruff',
|
|
400
|
+
description: 'Python linting and formatting',
|
|
401
|
+
install: 'pip install ruff',
|
|
402
|
+
check: 'ruff --version',
|
|
403
|
+
for: 'python',
|
|
404
|
+
layer: 'language',
|
|
405
|
+
binaries: ['ruff'],
|
|
406
|
+
versionCheck: 'ruff --version 2>/dev/null',
|
|
407
|
+
installCommands: {
|
|
408
|
+
// Use the dxkit venv (created during graphify install) for Python tools
|
|
409
|
+
macos: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install ruff && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/ruff ~/.local/bin/ruff',
|
|
410
|
+
linux: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install ruff && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/ruff ~/.local/bin/ruff',
|
|
411
|
+
windows: 'pip install --user ruff',
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
'pip-audit': {
|
|
415
|
+
name: 'pip-audit',
|
|
416
|
+
description: 'Python dependency vulnerability scanning',
|
|
417
|
+
install: 'pip install pip-audit',
|
|
418
|
+
check: 'pip-audit --version',
|
|
419
|
+
for: 'python',
|
|
420
|
+
layer: 'language',
|
|
421
|
+
binaries: ['pip-audit'],
|
|
422
|
+
versionCheck: 'pip-audit --version 2>/dev/null',
|
|
423
|
+
installCommands: {
|
|
424
|
+
macos: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install pip-audit && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/pip-audit ~/.local/bin/pip-audit',
|
|
425
|
+
linux: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install pip-audit && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/pip-audit ~/.local/bin/pip-audit',
|
|
426
|
+
windows: 'pip install --user pip-audit',
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
'golangci-lint': {
|
|
430
|
+
name: 'golangci-lint',
|
|
431
|
+
description: 'Go linting',
|
|
432
|
+
install: 'go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest',
|
|
433
|
+
check: 'golangci-lint --version',
|
|
434
|
+
for: 'go',
|
|
435
|
+
layer: 'language',
|
|
436
|
+
binaries: ['golangci-lint'],
|
|
437
|
+
versionCheck: 'golangci-lint --version 2>/dev/null',
|
|
438
|
+
installCommands: {
|
|
439
|
+
macos: 'brew install golangci-lint',
|
|
440
|
+
linux: 'go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest',
|
|
441
|
+
windows: 'go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest',
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
govulncheck: {
|
|
445
|
+
name: 'govulncheck',
|
|
446
|
+
description: 'Go vulnerability scanning',
|
|
447
|
+
install: 'go install golang.org/x/vuln/cmd/govulncheck@latest',
|
|
448
|
+
check: 'govulncheck -version',
|
|
449
|
+
for: 'go',
|
|
450
|
+
layer: 'language',
|
|
451
|
+
binaries: ['govulncheck'],
|
|
452
|
+
versionCheck: 'govulncheck -version 2>/dev/null',
|
|
453
|
+
installCommands: {
|
|
454
|
+
macos: 'go install golang.org/x/vuln/cmd/govulncheck@latest',
|
|
455
|
+
linux: 'go install golang.org/x/vuln/cmd/govulncheck@latest',
|
|
456
|
+
windows: 'go install golang.org/x/vuln/cmd/govulncheck@latest',
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
clippy: {
|
|
460
|
+
name: 'clippy',
|
|
461
|
+
description: 'Rust linting',
|
|
462
|
+
install: 'rustup component add clippy',
|
|
463
|
+
check: 'cargo clippy --version',
|
|
464
|
+
for: 'rust',
|
|
465
|
+
layer: 'language',
|
|
466
|
+
binaries: ['cargo-clippy'],
|
|
467
|
+
versionCheck: 'cargo clippy --version 2>/dev/null',
|
|
468
|
+
installCommands: {
|
|
469
|
+
macos: 'rustup component add clippy',
|
|
470
|
+
linux: 'rustup component add clippy',
|
|
471
|
+
windows: 'rustup component add clippy',
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
'cargo-audit': {
|
|
475
|
+
name: 'cargo-audit',
|
|
476
|
+
description: 'Rust dependency vulnerability scanning',
|
|
477
|
+
install: 'cargo install cargo-audit',
|
|
478
|
+
check: 'cargo audit --version',
|
|
479
|
+
for: 'rust',
|
|
480
|
+
layer: 'language',
|
|
481
|
+
binaries: ['cargo-audit'],
|
|
482
|
+
versionCheck: 'cargo audit --version 2>/dev/null',
|
|
483
|
+
installCommands: {
|
|
484
|
+
macos: 'cargo install cargo-audit',
|
|
485
|
+
linux: 'cargo install cargo-audit',
|
|
486
|
+
windows: 'cargo install cargo-audit',
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
'dotnet-format': {
|
|
490
|
+
name: 'dotnet-format',
|
|
491
|
+
description: 'C# formatting and linting',
|
|
492
|
+
install: 'builtin (dotnet SDK)',
|
|
493
|
+
check: 'dotnet format --version',
|
|
494
|
+
for: 'csharp',
|
|
495
|
+
layer: 'language',
|
|
496
|
+
binaries: ['dotnet'],
|
|
497
|
+
versionCheck: 'dotnet --version 2>/dev/null',
|
|
498
|
+
installCommands: {
|
|
499
|
+
macos: 'brew install dotnet-sdk',
|
|
500
|
+
linux: 'apt install dotnet-sdk-8.0',
|
|
501
|
+
windows: 'winget install Microsoft.DotNet.SDK.8',
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
// ── Coverage providers ──────────────────────────────────────────────────
|
|
505
|
+
'vitest-coverage': {
|
|
506
|
+
name: 'vitest-coverage',
|
|
507
|
+
description: 'Vitest V8 coverage provider (produces Istanbul-compatible JSON)',
|
|
508
|
+
install: 'npm install --save-dev @vitest/coverage-v8',
|
|
509
|
+
check: 'node -e "require(\'@vitest/coverage-v8\')"',
|
|
510
|
+
for: 'node',
|
|
511
|
+
layer: 'language',
|
|
512
|
+
binaries: [],
|
|
513
|
+
nodePackage: '@vitest/coverage-v8',
|
|
514
|
+
installCommands: {
|
|
515
|
+
// Version must match installed vitest major — auto-detect it.
|
|
516
|
+
macos: "npm install --save-dev \"@vitest/coverage-v8@$(node -e \"process.stdout.write('^'+require('vitest/package.json').version.split('.')[0])\")\"",
|
|
517
|
+
linux: "npm install --save-dev \"@vitest/coverage-v8@$(node -e \"process.stdout.write('^'+require('vitest/package.json').version.split('.')[0])\")\"",
|
|
518
|
+
windows: 'npm install --save-dev @vitest/coverage-v8',
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
'cargo-llvm-cov': {
|
|
522
|
+
name: 'cargo-llvm-cov',
|
|
523
|
+
description: 'Rust line-level coverage via LLVM instrumentation (produces lcov/cobertura)',
|
|
524
|
+
install: 'cargo install cargo-llvm-cov',
|
|
525
|
+
check: 'cargo llvm-cov --version',
|
|
526
|
+
for: 'rust',
|
|
527
|
+
layer: 'language',
|
|
528
|
+
binaries: ['cargo-llvm-cov'],
|
|
529
|
+
versionCheck: 'cargo llvm-cov --version 2>/dev/null',
|
|
530
|
+
installCommands: {
|
|
531
|
+
macos: 'cargo install cargo-llvm-cov',
|
|
532
|
+
linux: 'cargo install cargo-llvm-cov',
|
|
533
|
+
windows: 'cargo install cargo-llvm-cov',
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
'coverage-py': {
|
|
537
|
+
name: 'coverage-py',
|
|
538
|
+
description: 'Python line-level coverage (produces coverage.json)',
|
|
539
|
+
install: 'pip install coverage',
|
|
540
|
+
check: 'coverage --version',
|
|
541
|
+
for: 'python',
|
|
542
|
+
layer: 'language',
|
|
543
|
+
binaries: ['coverage'],
|
|
544
|
+
versionCheck: 'coverage --version 2>/dev/null',
|
|
545
|
+
installCommands: {
|
|
546
|
+
macos: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install coverage && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/coverage ~/.local/bin/coverage',
|
|
547
|
+
linux: 'test -d /tmp/graphify-venv || python3 -m venv /tmp/graphify-venv; /tmp/graphify-venv/bin/pip install coverage && mkdir -p ~/.local/bin && ln -sf /tmp/graphify-venv/bin/coverage ~/.local/bin/coverage',
|
|
548
|
+
windows: 'pip install --user coverage',
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
// =============================================================================
|
|
553
|
+
// Public API
|
|
554
|
+
// =============================================================================
|
|
555
|
+
/**
|
|
556
|
+
* Build the list of tools required for a given detected stack.
|
|
557
|
+
* This is the single source of truth for detect.ts's `requiredTools` field.
|
|
558
|
+
*/
|
|
559
|
+
function buildRequiredTools(languages) {
|
|
560
|
+
const names = [
|
|
561
|
+
// Universal
|
|
562
|
+
'cloc',
|
|
563
|
+
'gitleaks',
|
|
564
|
+
'jscpd',
|
|
565
|
+
'semgrep',
|
|
566
|
+
'graphify',
|
|
567
|
+
];
|
|
568
|
+
// Language-specific tools dispatched through the language registry.
|
|
569
|
+
// Maps DetectedStack keys to LanguageId (handles node/nextjs → typescript).
|
|
570
|
+
const langMap = {
|
|
571
|
+
node: 'typescript',
|
|
572
|
+
nextjs: 'typescript',
|
|
573
|
+
python: 'python',
|
|
574
|
+
go: 'go',
|
|
575
|
+
rust: 'rust',
|
|
576
|
+
csharp: 'csharp',
|
|
577
|
+
};
|
|
578
|
+
const seen = new Set();
|
|
579
|
+
for (const [key, active] of Object.entries(languages)) {
|
|
580
|
+
if (!active)
|
|
581
|
+
continue;
|
|
582
|
+
const id = langMap[key];
|
|
583
|
+
if (!id || seen.has(id))
|
|
584
|
+
continue;
|
|
585
|
+
seen.add(id);
|
|
586
|
+
const lang = (0, languages_1.getLanguage)(id);
|
|
587
|
+
if (lang)
|
|
588
|
+
names.push(...lang.tools);
|
|
589
|
+
}
|
|
590
|
+
return names.map((n) => {
|
|
591
|
+
const def = exports.TOOL_DEFS[n];
|
|
592
|
+
// Strip ToolDefinition-only fields, keep ToolRequirement fields
|
|
593
|
+
return {
|
|
594
|
+
name: def.name,
|
|
595
|
+
description: def.description,
|
|
596
|
+
install: def.install,
|
|
597
|
+
check: def.check,
|
|
598
|
+
for: def.for,
|
|
599
|
+
layer: def.layer,
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
/** Get full ToolDefinition by name. */
|
|
604
|
+
function getToolDef(name) {
|
|
605
|
+
return exports.TOOL_DEFS[name];
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Map detected languages to the corresponding semgrep ruleset IDs.
|
|
609
|
+
*
|
|
610
|
+
* Semgrep ruleset naming follows `p/<language-or-pack>`. Not every language
|
|
611
|
+
* has a ruleset (e.g., `p/go` does NOT exist; the gosec rules live under
|
|
612
|
+
* `p/gosec`). This function encodes those mappings so callers never hardcode.
|
|
613
|
+
*
|
|
614
|
+
* Adding a new language:
|
|
615
|
+
* 1. Add it to detect.ts `DetectedStack.languages`
|
|
616
|
+
* 2. Add a case here if semgrep has a ruleset for it
|
|
617
|
+
*
|
|
618
|
+
* @see https://semgrep.dev/explore for the full ruleset registry
|
|
619
|
+
*/
|
|
620
|
+
function getSemgrepRulesets(languages) {
|
|
621
|
+
// Always include the baseline audit ruleset — covers OWASP Top 10 patterns
|
|
622
|
+
// across all languages. Safe to combine with language-specific rulesets.
|
|
623
|
+
const rulesets = ['p/security-audit'];
|
|
624
|
+
// Language-specific rulesets from the language registry.
|
|
625
|
+
const langMap = {
|
|
626
|
+
node: 'typescript',
|
|
627
|
+
nextjs: 'typescript',
|
|
628
|
+
python: 'python',
|
|
629
|
+
go: 'go',
|
|
630
|
+
rust: 'rust',
|
|
631
|
+
csharp: 'csharp',
|
|
632
|
+
};
|
|
633
|
+
const seen = new Set();
|
|
634
|
+
for (const [key, active] of Object.entries(languages)) {
|
|
635
|
+
if (!active)
|
|
636
|
+
continue;
|
|
637
|
+
const id = langMap[key];
|
|
638
|
+
if (!id || seen.has(id))
|
|
639
|
+
continue;
|
|
640
|
+
seen.add(id);
|
|
641
|
+
const lang = (0, languages_1.getLanguage)(id);
|
|
642
|
+
if (lang)
|
|
643
|
+
rulesets.push(...lang.semgrepRulesets);
|
|
644
|
+
}
|
|
645
|
+
return rulesets;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Run a registered tool and return its stdout.
|
|
649
|
+
*
|
|
650
|
+
* ENFORCEMENT: This is the ONLY sanctioned way to execute an external tool
|
|
651
|
+
* in analyzer code. It verifies the tool exists in TOOL_DEFS before running.
|
|
652
|
+
* If you need to run a tool that's not registered, add it to TOOL_DEFS first.
|
|
653
|
+
*
|
|
654
|
+
* Builtins (grep, find, wc, git, node) don't go through this — they're
|
|
655
|
+
* always available and don't need detection/install. Only tools that:
|
|
656
|
+
* - Need to be discovered (multi-path detection)
|
|
657
|
+
* - Need to be installed by `vyuh-dxkit tools install`
|
|
658
|
+
* - Appear in `vyuh-dxkit tools list`
|
|
659
|
+
* must be registered and invoked via this function or findTool().
|
|
660
|
+
*
|
|
661
|
+
* @param toolName — must match a key in TOOL_DEFS
|
|
662
|
+
* @param buildCmd — function that receives the resolved binary path and returns the shell command
|
|
663
|
+
* @param cwd — working directory
|
|
664
|
+
* @param timeoutMs — command timeout
|
|
665
|
+
* @returns stdout string, or null if tool unavailable
|
|
666
|
+
*/
|
|
667
|
+
function runRegisteredTool(toolName, buildCmd, cwd, timeoutMs = 60000) {
|
|
668
|
+
const def = exports.TOOL_DEFS[toolName];
|
|
669
|
+
if (!def) {
|
|
670
|
+
throw new Error(`Tool "${toolName}" is not registered in TOOL_DEFS. ` +
|
|
671
|
+
'Add it to src/analyzers/tools/tool-registry.ts before using.');
|
|
672
|
+
}
|
|
673
|
+
const status = findTool(def, cwd);
|
|
674
|
+
if (!status.available || !status.path) {
|
|
675
|
+
return { output: null, available: false, path: null };
|
|
676
|
+
}
|
|
677
|
+
const cmd = buildCmd(status.path);
|
|
678
|
+
const result = quickRun(cmd) ||
|
|
679
|
+
(0, child_process_1.execSync)(cmd, {
|
|
680
|
+
cwd,
|
|
681
|
+
encoding: 'utf-8',
|
|
682
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
683
|
+
timeout: timeoutMs,
|
|
684
|
+
}).trim();
|
|
685
|
+
return { output: result || null, available: true, path: status.path };
|
|
686
|
+
}
|
|
687
|
+
/** Check status of all required tools for a stack. */
|
|
688
|
+
function checkAllTools(languages, cwd) {
|
|
689
|
+
const required = buildRequiredTools(languages);
|
|
690
|
+
return required.map((req) => {
|
|
691
|
+
const def = exports.TOOL_DEFS[req.name];
|
|
692
|
+
if (!def) {
|
|
693
|
+
return {
|
|
694
|
+
name: req.name,
|
|
695
|
+
available: false,
|
|
696
|
+
path: null,
|
|
697
|
+
version: null,
|
|
698
|
+
source: 'missing',
|
|
699
|
+
requirement: { ...req, binaries: [req.name], installCommands: {} },
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
return findTool(def, cwd);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=tool-registry.js.map
|