@mcptoolshop/venvkit 0.2.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -0
  3. package/dist/doctorLite.d.ts +75 -0
  4. package/dist/doctorLite.d.ts.map +1 -0
  5. package/dist/doctorLite.js +705 -0
  6. package/dist/doctorLite.js.map +1 -0
  7. package/dist/doctorLite.test.d.ts +2 -0
  8. package/dist/doctorLite.test.d.ts.map +1 -0
  9. package/dist/doctorLite.test.js +268 -0
  10. package/dist/doctorLite.test.js.map +1 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +6 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/integration.test.d.ts +2 -0
  16. package/dist/integration.test.d.ts.map +1 -0
  17. package/dist/integration.test.js +245 -0
  18. package/dist/integration.test.js.map +1 -0
  19. package/dist/mapRender.d.ts +105 -0
  20. package/dist/mapRender.d.ts.map +1 -0
  21. package/dist/mapRender.js +718 -0
  22. package/dist/mapRender.js.map +1 -0
  23. package/dist/mapRender.test.d.ts +2 -0
  24. package/dist/mapRender.test.d.ts.map +1 -0
  25. package/dist/mapRender.test.js +571 -0
  26. package/dist/mapRender.test.js.map +1 -0
  27. package/dist/map_cli.d.ts +3 -0
  28. package/dist/map_cli.d.ts.map +1 -0
  29. package/dist/map_cli.js +278 -0
  30. package/dist/map_cli.js.map +1 -0
  31. package/dist/map_cli.test.d.ts +2 -0
  32. package/dist/map_cli.test.d.ts.map +1 -0
  33. package/dist/map_cli.test.js +276 -0
  34. package/dist/map_cli.test.js.map +1 -0
  35. package/dist/runLog.d.ts +71 -0
  36. package/dist/runLog.d.ts.map +1 -0
  37. package/dist/runLog.js +98 -0
  38. package/dist/runLog.js.map +1 -0
  39. package/dist/runLog.test.d.ts +2 -0
  40. package/dist/runLog.test.d.ts.map +1 -0
  41. package/dist/runLog.test.js +327 -0
  42. package/dist/runLog.test.js.map +1 -0
  43. package/dist/scanEnvPaths.d.ts +18 -0
  44. package/dist/scanEnvPaths.d.ts.map +1 -0
  45. package/dist/scanEnvPaths.js +174 -0
  46. package/dist/scanEnvPaths.js.map +1 -0
  47. package/dist/scanEnvPaths.test.d.ts +2 -0
  48. package/dist/scanEnvPaths.test.d.ts.map +1 -0
  49. package/dist/scanEnvPaths.test.js +250 -0
  50. package/dist/scanEnvPaths.test.js.map +1 -0
  51. package/dist/taskCluster.d.ts +62 -0
  52. package/dist/taskCluster.d.ts.map +1 -0
  53. package/dist/taskCluster.js +180 -0
  54. package/dist/taskCluster.js.map +1 -0
  55. package/dist/taskCluster.test.d.ts +2 -0
  56. package/dist/taskCluster.test.d.ts.map +1 -0
  57. package/dist/taskCluster.test.js +375 -0
  58. package/dist/taskCluster.test.js.map +1 -0
  59. package/dist/vitest.config.d.ts +3 -0
  60. package/dist/vitest.config.d.ts.map +1 -0
  61. package/dist/vitest.config.js +8 -0
  62. package/dist/vitest.config.js.map +1 -0
  63. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 mcp-tool-shop
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,249 @@
1
+ # venvkit
2
+
3
+ [![CI](https://github.com/mcp-tool-shop/venvkit/actions/workflows/ci.yml/badge.svg)](https://github.com/mcp-tool-shop/venvkit/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **Python virtual environment diagnostic toolkit for Windows ML workflows.**
7
+
8
+ Scans your system for Python environments, diagnoses health issues (SSL, DLLs, ABI mismatches, path leakage), tracks task execution history, detects flaky tasks, and renders an ecosystem map.
9
+
10
+ ## 30-Second Quickstart
11
+
12
+ ```bash
13
+ git clone https://github.com/mcp-tool-shop/venvkit && cd venvkit
14
+ npm install && npm run build
15
+ node dist/map_cli.js --root C:\projects --httpsProbe
16
+ # Open .venvkit/venv-map.html in your browser
17
+ ```
18
+
19
+ ## Features
20
+
21
+ - **doctorLite** - Fast health check for any Python interpreter
22
+ - SSL/TLS verification
23
+ - DLL load failures (common with PyTorch/CUDA)
24
+ - ABI mismatches (ARM vs x86)
25
+ - pip sanity checks
26
+ - User-site and PYTHONPATH leakage detection
27
+
28
+ - **scanEnvPaths** - Discover all Python environments on your system
29
+ - Finds venvs, conda envs, pyenv versions, base interpreters
30
+ - Configurable depth and filtering
31
+
32
+ - **mapRender** - Visualize your Python ecosystem
33
+ - Graph JSON output for programmatic use
34
+ - Mermaid diagrams for documentation
35
+ - Base interpreter grouping with blast radius analysis
36
+ - Task routing visualization
37
+
38
+ - **runLog** - Track task execution history
39
+ - Append-only JSONL format
40
+ - Records which env ran what task
41
+ - Captures success/failure with error classification
42
+
43
+ - **taskCluster** - Aggregate task runs by signature
44
+ - Flaky task detection (inconsistent pass/fail)
45
+ - Environment-dependent flake detection
46
+ - Failure hotspot identification
47
+ - Contagion analysis (shared root causes)
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install
53
+ npm run build
54
+ ```
55
+
56
+ ## CLI Usage
57
+
58
+ ```bash
59
+ # Scan current directory and generate ecosystem map
60
+ node dist/map_cli.js
61
+
62
+ # Scan specific directories
63
+ node dist/map_cli.js --root C:\projects --root D:\ml-experiments
64
+
65
+ # Include task run history
66
+ node dist/map_cli.js --runlog .venvkit/runs.jsonl
67
+
68
+ # Output options
69
+ node dist/map_cli.js --out ./output --minScore 50 --strict --httpsProbe
70
+ ```
71
+
72
+ ### CLI Options
73
+
74
+ | Flag | Description |
75
+ |------|-------------|
76
+ | `--root, -r` | Directory to scan (can specify multiple) |
77
+ | `--out` | Output directory (default: `.venvkit`) |
78
+ | `--maxDepth` | Max directory depth to scan (default: 5) |
79
+ | `--strict` | Enable strict mode checks |
80
+ | `--httpsProbe` | Test HTTPS connectivity |
81
+ | `--minScore` | Filter envs below this health score |
82
+ | `--concurrency` | Parallel checks (default: CPU count) |
83
+ | `--runlog` | Path to task run log (JSONL) |
84
+ | `--no-tasks` | Skip task visualization |
85
+
86
+ ### Outputs
87
+
88
+ | File | Description |
89
+ |------|-------------|
90
+ | `venv-map.json` | Full graph data (nodes, edges, summary) |
91
+ | `venv-map.mmd` | Mermaid diagram source |
92
+ | `venv-map.html` | Interactive viewer |
93
+ | `reports.json` | Raw doctorLite reports |
94
+ | `insights.json` | Actionable recommendations |
95
+
96
+ ## Programmatic Usage
97
+
98
+ ```typescript
99
+ import { doctorLite, scanEnvPaths, mapRender, readRunLog } from 'venvkit';
100
+
101
+ // Check a specific Python
102
+ const report = await doctorLite({
103
+ pythonPath: 'C:\\project\\.venv\\Scripts\\python.exe',
104
+ requiredModules: ['torch', 'transformers'],
105
+ httpsProbe: true,
106
+ });
107
+
108
+ console.log(report.status); // 'good' | 'warn' | 'bad'
109
+ console.log(report.score); // 0-100
110
+ console.log(report.findings); // Array of issues
111
+
112
+ // Scan for all Python environments
113
+ const scan = await scanEnvPaths({
114
+ roots: ['C:\\projects'],
115
+ maxDepth: 5,
116
+ });
117
+
118
+ // Run doctorLite on all found environments
119
+ const reports = await Promise.all(
120
+ scan.pythonPaths.map(p => doctorLite({ pythonPath: p }))
121
+ );
122
+
123
+ // Load task execution history
124
+ const runs = await readRunLog('.venvkit/runs.jsonl');
125
+
126
+ // Generate ecosystem visualization
127
+ const { graph, mermaid, insights } = mapRender(reports, runs, {
128
+ taskMode: 'clustered', // 'none' | 'runs' | 'clustered'
129
+ includeHotEdgeLabels: true,
130
+ });
131
+ ```
132
+
133
+ ## Run Log Schema
134
+
135
+ Track task executions by appending events to a JSONL file:
136
+
137
+ ```typescript
138
+ import { appendRunLog, newRunId } from 'venvkit';
139
+
140
+ await appendRunLog('.venvkit/runs.jsonl', {
141
+ version: '1.0',
142
+ runId: newRunId(),
143
+ at: new Date().toISOString(),
144
+ task: {
145
+ name: 'train',
146
+ command: 'python train.py --epochs 10',
147
+ requirements: { packages: ['torch', 'transformers'] },
148
+ },
149
+ selected: {
150
+ pythonPath: 'C:\\project\\.venv\\Scripts\\python.exe',
151
+ score: 95,
152
+ status: 'good',
153
+ },
154
+ outcome: {
155
+ ok: true,
156
+ exitCode: 0,
157
+ durationMs: 45000,
158
+ },
159
+ });
160
+ ```
161
+
162
+ ## Task Clustering
163
+
164
+ When you have many task runs, venvkit clusters them by signature:
165
+
166
+ ```typescript
167
+ import { clusterRuns, isFlaky, getFailingEnvs } from 'venvkit';
168
+
169
+ const clusters = clusterRuns(runs);
170
+
171
+ for (const c of clusters) {
172
+ console.log(`${c.sig.name}: ${c.ok}/${c.runs} (${(c.successRate * 100).toFixed(0)}%)`);
173
+
174
+ if (isFlaky(c)) {
175
+ console.log(` WARNING: Flaky task!`);
176
+ const badEnvs = getFailingEnvs(c, 3);
177
+ console.log(` Failing most on: ${badEnvs.map(e => e.pythonPath).join(', ')}`);
178
+ }
179
+ }
180
+ ```
181
+
182
+ ## Graph Schema
183
+
184
+ The `mapRender` output follows a stable JSON schema:
185
+
186
+ ```typescript
187
+ type GraphJSONv1 = {
188
+ version: '1.0';
189
+ generatedAt: string;
190
+ host: { os: string; arch: string; hostname: string };
191
+ summary: {
192
+ envCount: number;
193
+ baseCount: number;
194
+ taskCount: number;
195
+ healthy: number;
196
+ warning: number;
197
+ broken: number;
198
+ runsPassed: number;
199
+ runsFailed: number;
200
+ topIssues: Array<{ code: string; count: number; hint: string }>;
201
+ };
202
+ nodes: GraphNode[];
203
+ edges: GraphEdge[];
204
+ };
205
+ ```
206
+
207
+ ### Node Types
208
+
209
+ | Type | Description |
210
+ |------|-------------|
211
+ | `base` | Base Python interpreter (e.g., `C:\Python311`) |
212
+ | `venv` | Virtual environment |
213
+ | `task` | Task signature (clustered runs) |
214
+
215
+ ### Edge Types
216
+
217
+ | Type | Description |
218
+ |------|-------------|
219
+ | `USES_BASE` | venv → base relationship |
220
+ | `ROUTES_TASK_TO` | task → env routing |
221
+ | `FAILED_RUN` | task → env failure (dashed in Mermaid) |
222
+
223
+ ## Finding Codes
224
+
225
+ | Code | Severity | Description |
226
+ |------|----------|-------------|
227
+ | `SSL_BROKEN` | bad | SSL module fails to import |
228
+ | `CERT_STORE_FAIL` | warn | HTTPS certificate verification fails |
229
+ | `DLL_LOAD_FAIL` | bad | Native extension DLL loading fails |
230
+ | `ABI_MISMATCH` | bad | Binary incompatibility (ARM/x86) |
231
+ | `PIP_MISSING` | warn | pip not available |
232
+ | `PIP_CHECK_FAIL` | warn | Dependency conflicts detected |
233
+ | `USER_SITE_LEAK` | warn | User site-packages enabled in venv |
234
+ | `PYTHONPATH_INJECTED` | warn | PYTHONPATH environment variable set |
235
+ | `ARCH_MISMATCH` | bad | 32-bit Python when 64-bit required |
236
+ | `PYVENV_CFG_INVALID` | warn | Broken or missing pyvenv.cfg |
237
+
238
+ ## Development
239
+
240
+ ```bash
241
+ npm install
242
+ npm run typecheck # Type check
243
+ npm run test # Run tests
244
+ npm run build # Build to dist/
245
+ ```
246
+
247
+ ## License
248
+
249
+ MIT
@@ -0,0 +1,75 @@
1
+ export type Severity = "info" | "warn" | "bad";
2
+ export type HealthStatus = "good" | "warn" | "bad" | "unknown";
3
+ export type FindingCode = "PYTHON_EXEC_MISSING" | "NOT_A_VENV" | "ARCH_MISMATCH" | "PIP_MISSING" | "PIP_POINTS_TO_OTHER_PYTHON" | "PYTHONPATH_INJECTED" | "USER_SITE_ENABLED" | "USER_SITE_LEAK" | "PIP_CHECK_FAIL" | "MULTI_VERSION_ON_PATH" | "RESOLVER_CONFLICT_HINT" | "OUTDATED_INSTALL_TOOLING" | "IMPORT_FAIL" | "DLL_LOAD_FAIL" | "ABI_MISMATCH" | "SSL_BROKEN" | "CERT_STORE_FAIL" | "SUBPROCESS_BROKEN" | "PYVENV_CFG_INVALID";
4
+ export type Finding = {
5
+ code: FindingCode;
6
+ severity: Severity;
7
+ penalty: number;
8
+ what: string;
9
+ why: string;
10
+ fix: Array<{
11
+ title: string;
12
+ steps: string[];
13
+ }>;
14
+ evidence?: Record<string, unknown>;
15
+ };
16
+ export type RunResult = {
17
+ ok: boolean;
18
+ exitCode: number | null;
19
+ stdout: string;
20
+ stderr: string;
21
+ timedOut: boolean;
22
+ };
23
+ export type CmdRunner = (cmd: string, args: string[], opts: {
24
+ env?: NodeJS.ProcessEnv;
25
+ timeoutMs: number;
26
+ }) => Promise<RunResult>;
27
+ export type DoctorLiteOptions = {
28
+ /** Python executable path (venv python or base python). Required. */
29
+ pythonPath: string;
30
+ /** Modules to import-test (task required caps). */
31
+ requiredModules?: string[];
32
+ /** If true, also do a HTTPS probe to pypi.org (can fail in corp MITM; still useful). */
33
+ httpsProbe?: boolean;
34
+ /** If task requires 64-bit python, set true. If unknown, leave undefined. */
35
+ requireX64?: boolean;
36
+ /** Timeout per subprocess (ms). */
37
+ timeoutMs?: number;
38
+ /** If true, run heavier checks (pip check, multi-version scan). */
39
+ strict?: boolean;
40
+ /** Override environment variables passed to python. */
41
+ env?: Record<string, string | undefined>;
42
+ };
43
+ export type DoctorLiteReport = {
44
+ pythonPath: string;
45
+ ranAt: string;
46
+ status: HealthStatus;
47
+ score: number;
48
+ summary: string;
49
+ facts?: FactsResult;
50
+ findings: Finding[];
51
+ };
52
+ type FactsResult = {
53
+ version: string;
54
+ version_info: number[];
55
+ executable: string;
56
+ prefix: string;
57
+ base_prefix: string | null;
58
+ bits: number;
59
+ machine: string;
60
+ os: string;
61
+ py_path: string[];
62
+ enable_user_site: boolean | null;
63
+ user_site: string;
64
+ };
65
+ /**
66
+ * Run a subprocess with timeout, capturing stdout/stderr.
67
+ */
68
+ export declare function runCmd(cmd: string, args: string[], opts: {
69
+ env?: NodeJS.ProcessEnv;
70
+ timeoutMs: number;
71
+ }): Promise<RunResult>;
72
+ export declare function doctorLite(opts: DoctorLiteOptions, runner?: CmdRunner): Promise<DoctorLiteReport>;
73
+ export declare function envIdFromPythonPath(pythonPath: string): string;
74
+ export {};
75
+ //# sourceMappingURL=doctorLite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctorLite.d.ts","sourceRoot":"","sources":["../doctorLite.ts"],"names":[],"mappings":"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"}